[PATCH 2/2] drm: Implement nonblocking mode in drm_read()
Takashi Iwai
tiwai at suse.de
Thu Dec 4 02:56:43 PST 2014
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 at suse.de>
---
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
More information about the dri-devel
mailing list