[PATCH RESEND] drm: fix drm_read() returning 0

David Herrmann dh.herrmann at googlemail.com
Sun Jun 24 06:42:26 PDT 2012


Imagine two threads read()'ing on the drm file and both are asleep waiting
for events in drm_read(). If a single event occurs, both threads are woken
up and start fetching the event. One thread will get it and return, the
other thread will notice that there is no further event and return 0 to
user-space.

We can avoid this by waiting for events until we got at least one event or
an error occurred.

Signed-off-by: David Herrmann <dh.herrmann at googlemail.com>
---
The patch might look a bit scary but it adds only a single do { } while(!total);
around the whole block.

 drivers/gpu/drm/drm_fops.c |   33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 123de28..6e7d349 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -635,25 +635,26 @@ ssize_t drm_read(struct file *filp, char __user *buffer,
 {
 	struct drm_file *file_priv = filp->private_data;
 	struct drm_pending_event *e;
-	size_t total;
+	size_t total = 0;
 	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)) {
-		if (copy_to_user(buffer + total,
-				 e->event, e->event->length)) {
-			total = -EFAULT;
-			break;
-		}
+	do {
+		ret = wait_event_interruptible(file_priv->event_wait,
+					       !list_empty(&file_priv->event_list));
+		if (ret < 0)
+			return ret;
 
-		total += e->event->length;
-		e->destroy(e);
-	}
+		while (drm_dequeue_event(file_priv, total, count, &e)) {
+			if (copy_to_user(buffer + total,
+					 e->event, e->event->length)) {
+				total = -EFAULT;
+				break;
+			}
+
+			total += e->event->length;
+			e->destroy(e);
+		}
+	} while (!total);
 
 	return total;
 }
-- 
1.7.10.4



More information about the dri-devel mailing list