On Fri, Jan 24, 2020 at 12:56:26PM +0000, Chris Wilson wrote:
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: Chris Wilson chris@chris-wilson.co.uk Cc: Thomas Hellström (VMware) thomas_os@shipmail.org Acked-by: Thomas Hellström (VMware) thomas_os@shipmail.org
drivers/gpu/drm/drm_file.c | 36 ++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/i915_drv.c | 2 +- include/drm/drm_file.h | 1 + 3 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 92d16724f949..e25306c49cc6 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -220,7 +220,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);
READ_ONCE(dev->open_count));
if (drm_core_check_feature(dev, DRIVER_LEGACY) && dev->driver->preclose)
@@ -455,6 +455,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);
- mutex_lock(&drm_global_mutex);
- if (!--dev->open_count)
drm_lastclose(dev);
- mutex_unlock(&drm_global_mutex);
btw my rough idea for lastclose is that we're just going to make it racy, and then use the master lock and drm_client infrastructure to handle fights between fbcon and a restarted compositor. That's already how that's handled everywhere else. We might need to cut over drivers to the generic fbcon stuff (or at least steal parts of the drm_client stuff I think), but not sure. -Daniel
- drm_minor_release(minor);
- return 0;
+} +EXPORT_SYMBOL(drm_release_noglobal);
/**
- drm_read - read method for DRM file
- @filp: file pointer
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index e9b42e962032..5a5846d892f4 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -2673,7 +2673,7 @@ const struct dev_pm_ops i915_pm_ops = { static const struct file_operations i915_driver_fops = { .owner = THIS_MODULE, .open = drm_open,
- .release = drm_release,
- .release = drm_release_noglobal, .unlocked_ioctl = drm_ioctl, .mmap = i915_gem_mmap, .poll = drm_poll,
diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 8b099b347817..19df8028a6c4 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -374,6 +374,7 @@ int drm_open(struct inode *inode, struct file *filp); ssize_t drm_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset); 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); int drm_event_reserve_init_locked(struct drm_device *dev, struct drm_file *file_priv, -- 2.25.0