drm: add minor-lookup/release helpers
authorDavid Herrmann <dh.herrmann@gmail.com>
Wed, 29 Jan 2014 09:49:19 +0000 (10:49 +0100)
committerSimon Horman <horms+renesas@verge.net.au>
Thu, 11 Dec 2014 01:19:49 +0000 (10:19 +0900)
Instead of accessing drm_minors_idr directly, this adds a small helper to
hide the internals. This will help us later to remove the drm_global_mutex
requirement for minor-lookup.

Furthermore, this also makes sure that minor->dev is always valid and
takes a reference-count to the device as long as the minor is used in an
open-file. This way, "struct file*"->private_data->dev is guaranteed to be
valid (which it has to, as we cannot reset it).

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
(cherry picked from commit 1616c525b98deb34b8f4b02eccf0ae3a1310fa27)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_stub.c
include/drm/drmP.h

index 7f2af9a..c7881ad 100644 (file)
@@ -79,23 +79,22 @@ static int drm_setup(struct drm_device * dev)
  */
 int drm_open(struct inode *inode, struct file *filp)
 {
-       struct drm_device *dev = NULL;
-       int minor_id = iminor(inode);
+       struct drm_device *dev;
        struct drm_minor *minor;
-       int retcode = 0;
+       int retcode;
        int need_setup = 0;
        struct address_space *old_mapping;
        struct address_space *old_imapping;
 
-       minor = idr_find(&drm_minors_idr, minor_id);
-       if (!minor)
-               return -ENODEV;
+       minor = drm_minor_acquire(iminor(inode));
+       if (IS_ERR(minor))
+               return PTR_ERR(minor);
 
-       if (!(dev = minor->dev))
-               return -ENODEV;
-
-       if (drm_device_is_unplugged(dev))
-               return -ENODEV;
+       dev = minor->dev;
+       if (drm_device_is_unplugged(dev)) {
+               retcode = -ENODEV;
+               goto err_release;
+       }
 
        if (!dev->open_count++)
                need_setup = 1;
@@ -128,6 +127,8 @@ err_undo:
        dev->dev_mapping = old_mapping;
        mutex_unlock(&dev->struct_mutex);
        dev->open_count--;
+err_release:
+       drm_minor_release(minor);
        return retcode;
 }
 EXPORT_SYMBOL(drm_open);
@@ -143,33 +144,33 @@ EXPORT_SYMBOL(drm_open);
  */
 int drm_stub_open(struct inode *inode, struct file *filp)
 {
-       struct drm_device *dev = NULL;
+       struct drm_device *dev;
        struct drm_minor *minor;
-       int minor_id = iminor(inode);
        int err = -ENODEV;
        const struct file_operations *new_fops;
 
        DRM_DEBUG("\n");
 
        mutex_lock(&drm_global_mutex);
-       minor = idr_find(&drm_minors_idr, minor_id);
-       if (!minor)
-               goto out;
-
-       if (!(dev = minor->dev))
-               goto out;
+       minor = drm_minor_acquire(iminor(inode));
+       if (IS_ERR(minor))
+               goto out_unlock;
 
+       dev = minor->dev;
        if (drm_device_is_unplugged(dev))
-               goto out;
+               goto out_release;
 
        new_fops = fops_get(dev->driver->fops);
        if (!new_fops)
-               goto out;
+               goto out_release;
 
        replace_fops(filp, new_fops);
        if (filp->f_op->open)
                err = filp->f_op->open(inode, filp);
-out:
+
+out_release:
+       drm_minor_release(minor);
+out_unlock:
        mutex_unlock(&drm_global_mutex);
        return err;
 }
@@ -458,7 +459,8 @@ int drm_lastclose(struct drm_device * dev)
 int drm_release(struct inode *inode, struct file *filp)
 {
        struct drm_file *file_priv = filp->private_data;
-       struct drm_device *dev = file_priv->minor->dev;
+       struct drm_minor *minor = file_priv->minor;
+       struct drm_device *dev = minor->dev;
        int retcode = 0;
 
        mutex_lock(&drm_global_mutex);
@@ -580,6 +582,8 @@ int drm_release(struct inode *inode, struct file *filp)
        }
        mutex_unlock(&drm_global_mutex);
 
+       drm_minor_release(minor);
+
        return retcode;
 }
 EXPORT_SYMBOL(drm_release);
index 98a33c5..5b60d8a 100644 (file)
@@ -356,6 +356,45 @@ static void drm_unplug_minor(struct drm_minor *minor)
 }
 
 /**
+ * drm_minor_acquire - Acquire a DRM minor
+ * @minor_id: Minor ID of the DRM-minor
+ *
+ * Looks up the given minor-ID and returns the respective DRM-minor object. The
+ * refence-count of the underlying device is increased so you must release this
+ * object with drm_minor_release().
+ *
+ * As long as you hold this minor, it is guaranteed that the object and the
+ * minor->dev pointer will stay valid! However, the device may get unplugged and
+ * unregistered while you hold the minor.
+ *
+ * Returns:
+ * Pointer to minor-object with increased device-refcount, or PTR_ERR on
+ * failure.
+ */
+struct drm_minor *drm_minor_acquire(unsigned int minor_id)
+{
+       struct drm_minor *minor;
+
+       minor = idr_find(&drm_minors_idr, minor_id);
+       if (!minor)
+               return ERR_PTR(-ENODEV);
+
+       drm_dev_ref(minor->dev);
+       return minor;
+}
+
+/**
+ * drm_minor_release - Release DRM minor
+ * @minor: Pointer to DRM minor object
+ *
+ * Release a minor that was previously acquired via drm_minor_acquire().
+ */
+void drm_minor_release(struct drm_minor *minor)
+{
+       drm_dev_unref(minor->dev);
+}
+
+/**
  * drm_put_minor - Destroy DRM minor
  * @minor: Minor to destroy
  *
index 04a7f31..39a3169 100644 (file)
@@ -1664,6 +1664,10 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
 void drm_dev_free(struct drm_device *dev);
 int drm_dev_register(struct drm_device *dev, unsigned long flags);
 void drm_dev_unregister(struct drm_device *dev);
+
+struct drm_minor *drm_minor_acquire(unsigned int minor_id);
+void drm_minor_release(struct drm_minor *minor);
+
 /*@}*/
 
 /* PCI section */