summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux-core/drmP.h9
-rw-r--r--linux-core/drm_gem.c6
-rw-r--r--linux-core/drm_lock.c74
-rw-r--r--linux-core/i915_gem.c5
-rw-r--r--shared-core/i915_drv.h1
5 files changed, 77 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;
}
diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h
index fee6d436..76bc6bee 100644
--- a/shared-core/i915_drv.h
+++ b/shared-core/i915_drv.h
@@ -454,6 +454,7 @@ int i915_gem_busy_ioctl(struct drm_device *dev, void *data,
int i915_gem_init_object(struct drm_gem_object *obj);
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);
int i915_gem_flush_pwrite(struct drm_gem_object *obj,