diff options
author | Thomas Hellstrom <thellstrom@vmware.com> | 2009-03-02 11:10:56 +0100 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2009-03-03 09:50:20 +1000 |
commit | fda714c29cdf360464059044b221450decb4b913 (patch) | |
tree | dc985f3c861d9490a1b5ffe82583497b6fe64236 | |
parent | 171901d15deeef61aa8e1b0d0772404f39691b73 (diff) |
drm: Avoid client deadlocks when the master disappears.
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>
-rw-r--r-- | drivers/gpu/drm/drm_fops.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_lock.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_stub.c | 8 |
3 files changed, 15 insertions, 8 deletions
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 6c020fe5431..f52663ebe01 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -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); diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index b03586fdabb..e2f70a516c3 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -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; } diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 7b251dd7081..096e2a37446 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -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); } |