diff options
Diffstat (limited to 'drivers/gpu/drm/drm_file.c')
-rw-r--r-- | drivers/gpu/drm/drm_file.c | 90 |
1 files changed, 83 insertions, 7 deletions
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 92d16724f949..c4c704e01961 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -51,6 +51,37 @@ /* 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); @@ -456,6 +498,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 * @buffer: userspace destination pointer for the read |