drm/amdgpu: implement more ib pools (v2)
[linux-2.6-microblaze.git] / drivers / gpu / drm / drm_file.c
index 92d1672..c4c704e 100644 (file)
 /* from BKL pushdown */
 DEFINE_MUTEX(drm_global_mutex);
 
+bool drm_dev_needs_global_mutex(struct drm_device *dev)
+{
+       /*
+        * Legacy drivers rely on all kinds of BKL locking semantics, don't
+        * bother. They also still need BKL locking for their ioctls, so better
+        * safe than sorry.
+        */
+       if (drm_core_check_feature(dev, DRIVER_LEGACY))
+               return true;
+
+       /*
+        * The deprecated ->load callback must be called after the driver is
+        * already registered. This means such drivers rely on the BKL to make
+        * sure an open can't proceed until the driver is actually fully set up.
+        * Similar hilarity holds for the unload callback.
+        */
+       if (dev->driver->load || dev->driver->unload)
+               return true;
+
+       /*
+        * Drivers with the lastclose callback assume that it's synchronized
+        * against concurrent opens, which again needs the BKL. The proper fix
+        * is to use the drm_client infrastructure with proper locking for each
+        * client.
+        */
+       if (dev->driver->lastclose)
+               return true;
+
+       return false;
+}
+
 /**
  * DOC: file operations
  *
@@ -220,7 +251,7 @@ void drm_file_free(struct drm_file *file)
        DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
                  task_pid_nr(current),
                  (long)old_encode_dev(file->minor->kdev->devt),
-                 dev->open_count);
+                 atomic_read(&dev->open_count));
 
        if (drm_core_check_feature(dev, DRIVER_LEGACY) &&
            dev->driver->preclose)
@@ -379,7 +410,10 @@ int drm_open(struct inode *inode, struct file *filp)
                return PTR_ERR(minor);
 
        dev = minor->dev;
-       if (!dev->open_count++)
+       if (drm_dev_needs_global_mutex(dev))
+               mutex_lock(&drm_global_mutex);
+
+       if (!atomic_fetch_inc(&dev->open_count))
                need_setup = 1;
 
        /* share address_space across all char-devs of a single device */
@@ -395,10 +429,16 @@ int drm_open(struct inode *inode, struct file *filp)
                        goto err_undo;
                }
        }
+
+       if (drm_dev_needs_global_mutex(dev))
+               mutex_unlock(&drm_global_mutex);
+
        return 0;
 
 err_undo:
-       dev->open_count--;
+       atomic_dec(&dev->open_count);
+       if (drm_dev_needs_global_mutex(dev))
+               mutex_unlock(&drm_global_mutex);
        drm_minor_release(minor);
        return retcode;
 }
@@ -438,16 +478,18 @@ int drm_release(struct inode *inode, struct file *filp)
        struct drm_minor *minor = file_priv->minor;
        struct drm_device *dev = minor->dev;
 
-       mutex_lock(&drm_global_mutex);
+       if (drm_dev_needs_global_mutex(dev))
+               mutex_lock(&drm_global_mutex);
 
-       DRM_DEBUG("open_count = %d\n", dev->open_count);
+       DRM_DEBUG("open_count = %d\n", atomic_read(&dev->open_count));
 
        drm_close_helper(filp);
 
-       if (!--dev->open_count)
+       if (atomic_dec_and_test(&dev->open_count))
                drm_lastclose(dev);
 
-       mutex_unlock(&drm_global_mutex);
+       if (drm_dev_needs_global_mutex(dev))
+               mutex_unlock(&drm_global_mutex);
 
        drm_minor_release(minor);
 
@@ -455,6 +497,40 @@ int drm_release(struct inode *inode, struct file *filp)
 }
 EXPORT_SYMBOL(drm_release);
 
+/**
+ * drm_release_noglobal - release method for DRM file
+ * @inode: device inode
+ * @filp: file pointer.
+ *
+ * This function may be used by drivers as their &file_operations.release
+ * method. It frees any resources associated with the open file prior to taking
+ * the drm_global_mutex, which then calls the &drm_driver.postclose driver
+ * callback. If this is the last open file for the DRM device also proceeds to
+ * call the &drm_driver.lastclose driver callback.
+ *
+ * RETURNS:
+ *
+ * Always succeeds and returns 0.
+ */
+int drm_release_noglobal(struct inode *inode, struct file *filp)
+{
+       struct drm_file *file_priv = filp->private_data;
+       struct drm_minor *minor = file_priv->minor;
+       struct drm_device *dev = minor->dev;
+
+       drm_close_helper(filp);
+
+       if (atomic_dec_and_mutex_lock(&dev->open_count, &drm_global_mutex)) {
+               drm_lastclose(dev);
+               mutex_unlock(&drm_global_mutex);
+       }
+
+       drm_minor_release(minor);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_release_noglobal);
+
 /**
  * drm_read - read method for DRM file
  * @filp: file pointer