Commit 7a2c65dd authored by Chris Wilson's avatar Chris Wilson

drm: Release filp before global lock

The file is not part of the global drm resource and can be released
prior to take the global mutex to drop the open_count (and potentially
close) the drm device. As the global mutex is indeed global, not only
within the device but across devices, a slow file release mechanism can
bottleneck the entire system.

However, inside drm_close_helper() there are a number of dev->driver
callbacks that take the drm_device as the first parameter... Worryingly
some of those callbacks may be (implicitly) depending on the global
mutex.

v2: Drop the debug message for the open-count, it's included with the
drm_file_free() debug message -- and for good measure make that up as
reading outside of the mutex.

v3: Separate the calling of the filp cleanup outside of
drm_global_mutex into a new drm_release_noglobal() hook, so that we can
phase the transition. drm/savage relies on the global mutex, and there
may be more, so be cautious.
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Thomas Hellström (VMware) <thomas_os@shipmail.org>
Reviewed-by: default avatarThomas Hellström (VMware) <thomas_os@shipmail.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20200124125627.125042-1-chris@chris-wilson.co.uk
parent c2d4290b
...@@ -220,7 +220,7 @@ void drm_file_free(struct drm_file *file) ...@@ -220,7 +220,7 @@ void drm_file_free(struct drm_file *file)
DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
task_pid_nr(current), task_pid_nr(current),
(long)old_encode_dev(file->minor->kdev->devt), (long)old_encode_dev(file->minor->kdev->devt),
dev->open_count); READ_ONCE(dev->open_count));
if (drm_core_check_feature(dev, DRIVER_LEGACY) && if (drm_core_check_feature(dev, DRIVER_LEGACY) &&
dev->driver->preclose) dev->driver->preclose)
...@@ -455,6 +455,40 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -455,6 +455,40 @@ int drm_release(struct inode *inode, struct file *filp)
} }
EXPORT_SYMBOL(drm_release); 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);
mutex_lock(&drm_global_mutex);
if (!--dev->open_count)
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 * drm_read - read method for DRM file
* @filp: file pointer * @filp: file pointer
......
...@@ -2663,7 +2663,7 @@ const struct dev_pm_ops i915_pm_ops = { ...@@ -2663,7 +2663,7 @@ const struct dev_pm_ops i915_pm_ops = {
static const struct file_operations i915_driver_fops = { static const struct file_operations i915_driver_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = drm_open, .open = drm_open,
.release = drm_release, .release = drm_release_noglobal,
.unlocked_ioctl = drm_ioctl, .unlocked_ioctl = drm_ioctl,
.mmap = i915_gem_mmap, .mmap = i915_gem_mmap,
.poll = drm_poll, .poll = drm_poll,
......
...@@ -374,6 +374,7 @@ int drm_open(struct inode *inode, struct file *filp); ...@@ -374,6 +374,7 @@ int drm_open(struct inode *inode, struct file *filp);
ssize_t drm_read(struct file *filp, char __user *buffer, ssize_t drm_read(struct file *filp, char __user *buffer,
size_t count, loff_t *offset); size_t count, loff_t *offset);
int drm_release(struct inode *inode, struct file *filp); int drm_release(struct inode *inode, struct file *filp);
int drm_release_noglobal(struct inode *inode, struct file *filp);
__poll_t drm_poll(struct file *filp, struct poll_table_struct *wait); __poll_t drm_poll(struct file *filp, struct poll_table_struct *wait);
int drm_event_reserve_init_locked(struct drm_device *dev, int drm_event_reserve_init_locked(struct drm_device *dev,
struct drm_file *file_priv, struct drm_file *file_priv,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment