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;  } | 
