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

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
Loading
Loading
Loading
Loading
+35 −1
Original line number Diff line number Diff line
@@ -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);

	drm_minor_release(minor);

	return 0;
}
EXPORT_SYMBOL(drm_release_noglobal);

/**
 * drm_read - read method for DRM file
 * @filp: file pointer
+1 −1
Original line number Diff line number Diff line
@@ -2663,7 +2663,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,
+1 −0
Original line number Diff line number Diff line
@@ -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,