drm: add minor-lookup/release helpers

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>
This commit is contained in:
David Herrmann 2014-01-29 10:49:19 +01:00
parent 099d1c290e
commit 1616c525b9
3 changed files with 70 additions and 23 deletions

View File

@ -79,23 +79,22 @@ static int drm_setup(struct drm_device * dev)
*/ */
int drm_open(struct inode *inode, struct file *filp) int drm_open(struct inode *inode, struct file *filp)
{ {
struct drm_device *dev = NULL; struct drm_device *dev;
int minor_id = iminor(inode);
struct drm_minor *minor; struct drm_minor *minor;
int retcode = 0; int retcode;
int need_setup = 0; int need_setup = 0;
struct address_space *old_mapping; struct address_space *old_mapping;
struct address_space *old_imapping; struct address_space *old_imapping;
minor = idr_find(&drm_minors_idr, minor_id); minor = drm_minor_acquire(iminor(inode));
if (!minor) if (IS_ERR(minor))
return -ENODEV; return PTR_ERR(minor);
if (!(dev = minor->dev)) dev = minor->dev;
return -ENODEV; if (drm_device_is_unplugged(dev)) {
retcode = -ENODEV;
if (drm_device_is_unplugged(dev)) goto err_release;
return -ENODEV; }
if (!dev->open_count++) if (!dev->open_count++)
need_setup = 1; need_setup = 1;
@ -128,6 +127,8 @@ err_undo:
dev->dev_mapping = old_mapping; dev->dev_mapping = old_mapping;
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
dev->open_count--; dev->open_count--;
err_release:
drm_minor_release(minor);
return retcode; return retcode;
} }
EXPORT_SYMBOL(drm_open); EXPORT_SYMBOL(drm_open);
@ -143,33 +144,33 @@ EXPORT_SYMBOL(drm_open);
*/ */
int drm_stub_open(struct inode *inode, struct file *filp) int drm_stub_open(struct inode *inode, struct file *filp)
{ {
struct drm_device *dev = NULL; struct drm_device *dev;
struct drm_minor *minor; struct drm_minor *minor;
int minor_id = iminor(inode);
int err = -ENODEV; int err = -ENODEV;
const struct file_operations *new_fops; const struct file_operations *new_fops;
DRM_DEBUG("\n"); DRM_DEBUG("\n");
mutex_lock(&drm_global_mutex); mutex_lock(&drm_global_mutex);
minor = idr_find(&drm_minors_idr, minor_id); minor = drm_minor_acquire(iminor(inode));
if (!minor) if (IS_ERR(minor))
goto out; goto out_unlock;
if (!(dev = minor->dev))
goto out;
dev = minor->dev;
if (drm_device_is_unplugged(dev)) if (drm_device_is_unplugged(dev))
goto out; goto out_release;
new_fops = fops_get(dev->driver->fops); new_fops = fops_get(dev->driver->fops);
if (!new_fops) if (!new_fops)
goto out; goto out_release;
replace_fops(filp, new_fops); replace_fops(filp, new_fops);
if (filp->f_op->open) if (filp->f_op->open)
err = filp->f_op->open(inode, filp); err = filp->f_op->open(inode, filp);
out:
out_release:
drm_minor_release(minor);
out_unlock:
mutex_unlock(&drm_global_mutex); mutex_unlock(&drm_global_mutex);
return err; return err;
} }
@ -453,7 +454,8 @@ int drm_lastclose(struct drm_device * dev)
int drm_release(struct inode *inode, struct file *filp) int drm_release(struct inode *inode, struct file *filp)
{ {
struct drm_file *file_priv = filp->private_data; 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; int retcode = 0;
mutex_lock(&drm_global_mutex); mutex_lock(&drm_global_mutex);
@ -575,6 +577,8 @@ int drm_release(struct inode *inode, struct file *filp)
} }
mutex_unlock(&drm_global_mutex); mutex_unlock(&drm_global_mutex);
drm_minor_release(minor);
return retcode; return retcode;
} }
EXPORT_SYMBOL(drm_release); EXPORT_SYMBOL(drm_release);

View File

@ -355,6 +355,45 @@ static void drm_unplug_minor(struct drm_minor *minor)
idr_remove(&drm_minors_idr, minor->index); idr_remove(&drm_minors_idr, minor->index);
} }
/**
* 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 * drm_put_minor - Destroy DRM minor
* @minor: Minor to destroy * @minor: Minor to destroy

View File

@ -1672,6 +1672,10 @@ void drm_dev_ref(struct drm_device *dev);
void drm_dev_unref(struct drm_device *dev); void drm_dev_unref(struct drm_device *dev);
int drm_dev_register(struct drm_device *dev, unsigned long flags); int drm_dev_register(struct drm_device *dev, unsigned long flags);
void drm_dev_unregister(struct drm_device *dev); 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 */ /* PCI section */