diff mbox

[06/13] drm: add minor-lookup/release helpers

Message ID 1391004120-687-7-git-send-email-dh.herrmann@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

David Herrmann Jan. 29, 2014, 2:01 p.m. UTC
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>
---
 drivers/gpu/drm/drm_fops.c | 50 +++++++++++++++++++++++++---------------------
 drivers/gpu/drm/drm_stub.c | 39 ++++++++++++++++++++++++++++++++++++
 include/drm/drmP.h         |  4 ++++
 3 files changed, 70 insertions(+), 23 deletions(-)

Comments

Thierry Reding Feb. 21, 2014, 7:09 a.m. UTC | #1
On Wed, Jan 29, 2014 at 03:01:53PM +0100, David Herrmann wrote:
[...]
> diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
> index c51333e..d3232b6 100644
> --- a/drivers/gpu/drm/drm_stub.c
> +++ b/drivers/gpu/drm/drm_stub.c
> @@ -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);

Is it possible that somebody would drop the last reference on the device
right between the idr_find() call and drm_dev_ref()? In which case both
the device and the minor will have become invalid when drm_dev_ref() is
called.

Thierry
David Herrmann Feb. 21, 2014, 7:34 a.m. UTC | #2
Hi

On Fri, Feb 21, 2014 at 8:09 AM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> On Wed, Jan 29, 2014 at 03:01:53PM +0100, David Herrmann wrote:
> [...]
>> diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
>> index c51333e..d3232b6 100644
>> --- a/drivers/gpu/drm/drm_stub.c
>> +++ b/drivers/gpu/drm/drm_stub.c
>> @@ -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);
>
> Is it possible that somebody would drop the last reference on the device
> right between the idr_find() call and drm_dev_ref()? In which case both
> the device and the minor will have become invalid when drm_dev_ref() is
> called.

No, it's locked via the drm_global_mutex. And later I introduce a
spinlock here that protects this in case we remove the global lock.

Thanks
David
Thierry Reding Feb. 21, 2014, 7:37 a.m. UTC | #3
On Fri, Feb 21, 2014 at 08:34:15AM +0100, David Herrmann wrote:
> Hi
> 
> On Fri, Feb 21, 2014 at 8:09 AM, Thierry Reding
> <thierry.reding@gmail.com> wrote:
> > On Wed, Jan 29, 2014 at 03:01:53PM +0100, David Herrmann wrote:
> > [...]
> >> diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
> >> index c51333e..d3232b6 100644
> >> --- a/drivers/gpu/drm/drm_stub.c
> >> +++ b/drivers/gpu/drm/drm_stub.c
> >> @@ -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);
> >
> > Is it possible that somebody would drop the last reference on the device
> > right between the idr_find() call and drm_dev_ref()? In which case both
> > the device and the minor will have become invalid when drm_dev_ref() is
> > called.
> 
> No, it's locked via the drm_global_mutex. And later I introduce a
> spinlock here that protects this in case we remove the global lock.

Indeed. Thanks for clarifying.

Thierry
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 6466cb5..7947819 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -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;
-
-	if (!(dev = minor->dev))
-		return -ENODEV;
+	minor = drm_minor_acquire(iminor(inode));
+	if (IS_ERR(minor))
+		return PTR_ERR(minor);
 
-	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;
 }
@@ -453,7 +454,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);
@@ -575,6 +577,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);
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index c51333e..d3232b6 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -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
  *
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 2c58e94..3c02cad 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1669,6 +1669,10 @@  void drm_dev_ref(struct drm_device *dev);
 void drm_dev_unref(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 */