drm_read() always blocks the read until an event comes up even if the file is opened in nonblocking mode. Also, there is a potential race between the wake_event_interruptible() call and the actual event retrieval in drm_dequeue_event(), and it may result in zero event read even in the blocking mode. This patch fixes these issues. The fixes are two folds: - Do drm_dequeue_event() at first, then call wait_event_interruptible() in a loop only if no event is pending. It's safe to call drm_dequeue_event() at first since it returns immediately if no event is found, and the function is protected properly by event_lock. - Add a f_flags check when drm_dequeue_event() fails and returns immediately in nonblocking mode. This is evaluated only at the first read. Note: originally this issue comes up while debugging the hangup of X at switching VT quickly. For example, running the below on KDE results in the stall of X: for i in $(seq 1 100); do chvt 1; chvt 7; done This was reproduced on various Intel machines. I took a stack trace of the hanging process, and it points to the blockage in drm_read(). This patch "fixes" such a bug. A drawback is that now it becomes more difficult to find out such a buggy code in the caller side. Signed-off-by: Takashi Iwai <tiwai@xxxxxxx> --- drivers/gpu/drm/drm_fops.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index a82dc28d54f3..04833eaf51b3 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -515,13 +515,20 @@ ssize_t drm_read(struct file *filp, char __user *buffer, size_t total; ssize_t ret; - ret = wait_event_interruptible(file_priv->event_wait, - !list_empty(&file_priv->event_list)); - if (ret < 0) - return ret; - total = 0; - while (drm_dequeue_event(file_priv, total, count, &e)) { + for (;;) { + if (!drm_dequeue_event(file_priv, total, count, &e)) { + if (total) + break; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(file_priv->event_wait, + !list_empty(&file_priv->event_list)); + if (ret < 0) + return ret; + continue; + } + if (copy_to_user(buffer + total, e->event, e->event->length)) { total = -EFAULT; -- 2.1.3 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel