drm: Protect the master management with a drm_device::master_mutex v3
[linux-2.6-block.git] / drivers / gpu / drm / drm_fops.c
index c7792b1d1773942e4edd70ffb290cea5f69ecdea..a0ce39c96f8e9c7c8bb320fb00850d8f4eec09bd 100644 (file)
@@ -231,12 +231,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
 
        /* if there is no current master make this fd it, but do not create
         * any master object for render clients */
-       mutex_lock(&dev->struct_mutex);
+       mutex_lock(&dev->master_mutex);
        if (drm_is_primary_client(priv) && !priv->minor->master) {
                /* create a new master */
                priv->minor->master = drm_master_create(priv->minor);
                if (!priv->minor->master) {
-                       mutex_unlock(&dev->struct_mutex);
                        ret = -ENOMEM;
                        goto out_close;
                }
@@ -244,29 +243,23 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
                priv->is_master = 1;
                /* take another reference for the copy in the local file priv */
                priv->master = drm_master_get(priv->minor->master);
-
                priv->authenticated = 1;
 
-               mutex_unlock(&dev->struct_mutex);
                if (dev->driver->master_create) {
                        ret = dev->driver->master_create(dev, priv->master);
                        if (ret) {
-                               mutex_lock(&dev->struct_mutex);
                                /* drop both references if this fails */
                                drm_master_put(&priv->minor->master);
                                drm_master_put(&priv->master);
-                               mutex_unlock(&dev->struct_mutex);
                                goto out_close;
                        }
                }
-               mutex_lock(&dev->struct_mutex);
                if (dev->driver->master_set) {
                        ret = dev->driver->master_set(dev, priv, true);
                        if (ret) {
                                /* drop both references if this fails */
                                drm_master_put(&priv->minor->master);
                                drm_master_put(&priv->master);
-                               mutex_unlock(&dev->struct_mutex);
                                goto out_close;
                        }
                }
@@ -274,7 +267,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
                /* get a reference to the master */
                priv->master = drm_master_get(priv->minor->master);
        }
-       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev->master_mutex);
 
        mutex_lock(&dev->struct_mutex);
        list_add(&priv->lhead, &dev->filelist);
@@ -302,6 +295,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
        return 0;
 
 out_close:
+       mutex_unlock(&dev->master_mutex);
        if (dev->driver->postclose)
                dev->driver->postclose(dev, priv);
 out_prime_destroy:
@@ -489,11 +483,13 @@ int drm_release(struct inode *inode, struct file *filp)
        }
        mutex_unlock(&dev->ctxlist_mutex);
 
-       mutex_lock(&dev->struct_mutex);
+       mutex_lock(&dev->master_mutex);
 
        if (file_priv->is_master) {
                struct drm_master *master = file_priv->master;
                struct drm_file *temp;
+
+               mutex_lock(&dev->struct_mutex);
                list_for_each_entry(temp, &dev->filelist, lhead) {
                        if ((temp->master == file_priv->master) &&
                            (temp != file_priv))
@@ -512,6 +508,7 @@ int drm_release(struct inode *inode, struct file *filp)
                        master->lock.file_priv = NULL;
                        wake_up_interruptible_all(&master->lock.lock_queue);
                }
+               mutex_unlock(&dev->struct_mutex);
 
                if (file_priv->minor->master == file_priv->master) {
                        /* drop the reference held my the minor */
@@ -521,10 +518,13 @@ int drm_release(struct inode *inode, struct file *filp)
                }
        }
 
-       /* drop the reference held my the file priv */
+       /* drop the master reference held by the file priv */
        if (file_priv->master)
                drm_master_put(&file_priv->master);
        file_priv->is_master = 0;
+       mutex_unlock(&dev->master_mutex);
+
+       mutex_lock(&dev->struct_mutex);
        list_del(&file_priv->lhead);
        mutex_unlock(&dev->struct_mutex);