drm: Serialise multiple event readers
authorChris Wilson <chris@chris-wilson.co.uk>
Wed, 25 Nov 2015 14:39:03 +0000 (14:39 +0000)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 26 Nov 2015 14:21:27 +0000 (15:21 +0100)
The previous patch reintroduced a race condition whereby a failure in
one reader may allow a second reader to see out-of-order events.
Introduce a mutex to serialise readers so that an event is completed in
its entirety before another reader may process an event. The two readers
may race against each other, but the events each retrieves are in the
correct order.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Thomas Hellstrom <thellstrom@vmware.com>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1448462343-2072-2-git-send-email-chris@chris-wilson.co.uk
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/drm_fops.c
include/drm/drmP.h

index eb8702d39e7d6be697b7f67b6bf5517f70118f2e..81df9ae95e2e1eb5fff09eece4613cdec9c03b3b 100644 (file)
@@ -172,6 +172,8 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
        init_waitqueue_head(&priv->event_wait);
        priv->event_space = 4096; /* set aside 4k for event buffer */
 
+       mutex_init(&priv->event_read_lock);
+
        if (drm_core_check_feature(dev, DRIVER_GEM))
                drm_gem_open(dev, priv);
 
@@ -483,11 +485,15 @@ ssize_t drm_read(struct file *filp, char __user *buffer,
 {
        struct drm_file *file_priv = filp->private_data;
        struct drm_device *dev = file_priv->minor->dev;
-       ssize_t ret = 0;
+       ssize_t ret;
 
        if (!access_ok(VERIFY_WRITE, buffer, count))
                return -EFAULT;
 
+       ret = mutex_lock_interruptible(&file_priv->event_read_lock);
+       if (ret)
+               return ret;
+
        for (;;) {
                struct drm_pending_event *e = NULL;
 
@@ -509,12 +515,13 @@ ssize_t drm_read(struct file *filp, char __user *buffer,
                                break;
                        }
 
+                       mutex_unlock(&file_priv->event_read_lock);
                        ret = wait_event_interruptible(file_priv->event_wait,
                                                       !list_empty(&file_priv->event_list));
-                       if (ret < 0)
-                               break;
-
-                       ret = 0;
+                       if (ret >= 0)
+                               ret = mutex_lock_interruptible(&file_priv->event_read_lock);
+                       if (ret)
+                               return ret;
                } else {
                        unsigned length = e->event->length;
 
@@ -537,6 +544,7 @@ put_back_event:
                        e->destroy(e);
                }
        }
+       mutex_unlock(&file_priv->event_read_lock);
 
        return ret;
 }
index 30d4a5a495e25661c9d1e2498b26dadf40ad9a17..8e1df1f7057ccebe091d51e457df18576472824e 100644 (file)
@@ -344,6 +344,8 @@ struct drm_file {
        struct list_head event_list;
        int event_space;
 
+       struct mutex event_read_lock;
+
        struct drm_prime_file_private prime;
 };