Message ID | 1391004120-687-7-git-send-email-dh.herrmann@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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
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
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 --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 */
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(-)