diff options
Diffstat (limited to 'linux-core')
-rw-r--r-- | linux-core/drmP.h | 9 | ||||
-rw-r--r-- | linux-core/drm_gem.c | 6 | ||||
-rw-r--r-- | linux-core/drm_lock.c | 74 | ||||
-rw-r--r-- | linux-core/i915_gem.c | 5 |
4 files changed, 76 insertions, 18 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h index fc7043d7..8246f44a 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -474,6 +474,11 @@ struct drm_lock_data { uint32_t kernel_waiters; uint32_t user_waiters; int idle_has_lock; + /** + * Boolean signaling that the lock is held on behalf of the + * file_priv client by the kernel in an ioctl handler. + */ + int kernel_held; }; /** @@ -778,6 +783,7 @@ struct drm_driver { * Driver-specific callback to set memory domains from userspace */ int (*gem_set_domain) (struct drm_gem_object *obj, + struct drm_file *file_priv, uint32_t read_domains, uint32_t write_domain); @@ -1178,6 +1184,9 @@ extern int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); extern int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context); extern void drm_idlelock_take(struct drm_lock_data *lock_data); extern void drm_idlelock_release(struct drm_lock_data *lock_data); +extern int drm_client_lock_take(struct drm_device *dev, + struct drm_file *file_priv); +extern void drm_client_lock_release(struct drm_device *dev); /* * These are exported to drivers so that they can implement fencing using diff --git a/linux-core/drm_gem.c b/linux-core/drm_gem.c index fb175d7d..b726e598 100644 --- a/linux-core/drm_gem.c +++ b/linux-core/drm_gem.c @@ -291,7 +291,7 @@ drm_gem_pread_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); if (dev->driver->gem_set_domain) { - ret = dev->driver->gem_set_domain(obj, + ret = dev->driver->gem_set_domain(obj, file_priv, DRM_GEM_DOMAIN_CPU, 0); if (ret) { @@ -384,7 +384,7 @@ drm_gem_pwrite_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); if (dev->driver->gem_set_domain) { - ret = dev->driver->gem_set_domain(obj, + ret = dev->driver->gem_set_domain(obj, file_priv, DRM_GEM_DOMAIN_CPU, DRM_GEM_DOMAIN_CPU); if (ret) { @@ -530,7 +530,7 @@ drm_gem_set_domain_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); if (dev->driver->gem_set_domain) { - ret = dev->driver->gem_set_domain(obj, + ret = dev->driver->gem_set_domain(obj, file_priv, args->read_domains, args->write_domain); } else { diff --git a/linux-core/drm_lock.c b/linux-core/drm_lock.c index db1d646b..ad7c1679 100644 --- a/linux-core/drm_lock.c +++ b/linux-core/drm_lock.c @@ -215,22 +215,16 @@ int drm_lock_take(struct drm_lock_data *lock_data, } while (prev != old); spin_unlock_irqrestore(&lock_data->spinlock, irqflags); - if (_DRM_LOCKING_CONTEXT(old) == context) { - if (old & _DRM_LOCK_HELD) { - if (context != DRM_KERNEL_CONTEXT) { - DRM_ERROR("%d holds heavyweight lock\n", - context); - } - return 0; + /* Warn on recursive locking of user contexts. */ + if (_DRM_LOCKING_CONTEXT(old) == context && _DRM_LOCK_IS_HELD(old)) { + if (context != DRM_KERNEL_CONTEXT) { + DRM_ERROR("%d holds heavyweight lock\n", + context); } + return 0; } - if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) { - /* Have lock */ - - return 1; - } - return 0; + return !_DRM_LOCK_IS_HELD(old); } /** @@ -386,6 +380,60 @@ void drm_idlelock_release(struct drm_lock_data *lock_data) } EXPORT_SYMBOL(drm_idlelock_release); +/** + * Takes the lock on behalf of the client if needed, using the kernel context. + * + * This allows us to hide the hardware lock when it's required for protection + * of data structures (such as command ringbuffer) shared with the X Server, and + * a way for us to transition to lockless for those requests when the X Server + * stops accessing the ringbuffer directly, without having to update the + * other userland clients. + */ +int drm_client_lock_take(struct drm_device *dev, struct drm_file *file_priv) +{ + int ret; + unsigned long irqflags; + + /* If the client has the lock, we're already done. */ + if (drm_i_have_hw_lock(dev, file_priv)) + return 0; + + /* Client doesn't hold the lock. Block taking the lock with the kernel + * context on behalf of the client, and return whether we were + * successful. + */ + spin_lock_irqsave(&dev->lock.spinlock, irqflags); + dev->lock.user_waiters++; + spin_unlock_irqrestore(&dev->lock.spinlock, irqflags); + ret = wait_event_interruptible(dev->lock.lock_queue, + drm_lock_take(&dev->lock, + DRM_KERNEL_CONTEXT)); + spin_lock_irqsave(&dev->lock.spinlock, irqflags); + dev->lock.user_waiters--; + if (ret != 0) { + spin_unlock_irqrestore(&dev->lock.spinlock, irqflags); + return ret; + } else { + dev->lock.file_priv = file_priv; + dev->lock.lock_time = jiffies; + dev->lock.kernel_held = 1; + file_priv->lock_count++; + spin_unlock_irqrestore(&dev->lock.spinlock, irqflags); + return 0; + } +} +EXPORT_SYMBOL(drm_client_lock_take); + +void drm_client_lock_release(struct drm_device *dev) +{ + if (dev->lock.kernel_held) { + dev->lock.kernel_held = 0; + dev->lock.file_priv = NULL; + drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT); + } +} +EXPORT_SYMBOL(drm_client_lock_release); + int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv) { diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 5eeabdaa..ebe51539 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -1522,6 +1522,7 @@ void i915_gem_free_object(struct drm_gem_object *obj) int i915_gem_set_domain(struct drm_gem_object *obj, + struct drm_file *file_priv, uint32_t read_domains, uint32_t write_domain) { @@ -1529,11 +1530,11 @@ i915_gem_set_domain(struct drm_gem_object *obj, BUG_ON(!mutex_is_locked(&dev->struct_mutex)); - drm_idlelock_take (&dev->lock); + drm_client_lock_take(dev, file_priv); i915_kernel_lost_context(dev); i915_gem_object_set_domain(obj, read_domains, write_domain); i915_gem_dev_set_domain(obj->dev); - drm_idlelock_release (&dev->lock); + drm_client_lock_release(dev); return 0; } |