drm: Avoid client deadlocks when the master disappears.
authorThomas Hellstrom <thellstrom@vmware.com>
Mon, 2 Mar 2009 10:10:56 +0000 (11:10 +0100)
committerDave Airlie <airlied@linux.ie>
Mon, 2 Mar 2009 23:50:20 +0000 (09:50 +1000)
This is done by
1) Wake up lock waiters when we close the master file descriptor.
   Not when the master structure is removed, since the latter
   requires the waiters themselves to release the refcount on the
   master structure -> Deadlock.
2) Send a SIGTERM to all clients waiting for the lock.
   Normally these clients will get a SIGPIPE when the X server dies,
   but clients may also spin trying to grab the DRM lock, without
   getting any sort of notification.

Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Dave Airlie <airlied@linux.ie>
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_lock.c
drivers/gpu/drm/drm_stub.c

index 6c020fe5431ca5eea366e60fd9560f5096e6da58..f52663ebe016f5ea194b5aa2a403881ebb2e0448 100644 (file)
@@ -484,6 +484,7 @@ int drm_release(struct inode *inode, struct file *filp)
        mutex_lock(&dev->struct_mutex);
 
        if (file_priv->is_master) {
+               struct drm_master *master = file_priv->master;
                struct drm_file *temp;
                list_for_each_entry(temp, &dev->filelist, lhead) {
                        if ((temp->master == file_priv->master) &&
@@ -491,6 +492,19 @@ int drm_release(struct inode *inode, struct file *filp)
                                temp->authenticated = 0;
                }
 
+               /**
+                * Since the master is disappearing, so is the
+                * possibility to lock.
+                */
+
+               if (master->lock.hw_lock) {
+                       if (dev->sigdata.lock == master->lock.hw_lock)
+                               dev->sigdata.lock = NULL;
+                       master->lock.hw_lock = NULL;
+                       master->lock.file_priv = NULL;
+                       wake_up_interruptible_all(&master->lock.lock_queue);
+               }
+
                if (file_priv->minor->master == file_priv->master) {
                        /* drop the reference held my the minor */
                        drm_master_put(&file_priv->minor->master);
index b03586fdabbd8eb8d709c8b0be2a8f30ae4fa297..e2f70a516c34412112078131203309c3656622d7 100644 (file)
@@ -80,6 +80,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
                __set_current_state(TASK_INTERRUPTIBLE);
                if (!master->lock.hw_lock) {
                        /* Device has been unregistered */
+                       send_sig(SIGTERM, current, 0);
                        ret = -EINTR;
                        break;
                }
index 7b251dd708115b5a18914cd86ed43a613bcb0aa5..096e2a37446d3e8fb333f45c342bc2a975607b34 100644 (file)
@@ -146,14 +146,6 @@ static void drm_master_destroy(struct kref *kref)
 
        drm_ht_remove(&master->magiclist);
 
-       if (master->lock.hw_lock) {
-               if (dev->sigdata.lock == master->lock.hw_lock)
-                       dev->sigdata.lock = NULL;
-               master->lock.hw_lock = NULL;
-               master->lock.file_priv = NULL;
-               wake_up_interruptible_all(&master->lock.lock_queue);
-       }
-
        drm_free(master, sizeof(*master), DRM_MEM_DRIVER);
 }