[PATCH 1/2] drm: convert crtc and connection_mutex to ww_mutex (v4)

Daniel Vetter daniel at ffwll.ch
Wed Jun 4 07:47:28 PDT 2014


On Wed, Jun 04, 2014 at 10:27:25AM -0400, Rob Clark wrote:
> For atomic, it will be quite necessary to not need to care so much
> about locking order.  And 'state' gives us a convenient place to stash a
> ww_ctx for any sort of update that needs to grab multiple crtc locks.
> 
> Because we will want to eventually make locking even more fine grained
> (giving locks to planes, connectors, etc), split out drm_modeset_lock
> and drm_modeset_acquire_ctx to track acquired locks.
> 
> Atomic will use this to keep track of which locks have been acquired
> in a transaction.
> 
> v1: original
> v2: remove a few things not needed until atomic, for now
> v3: update for v3 of connection_mutex patch..
> v4: squash in docbook
> 
> Signed-off-by: Rob Clark <robdclark at gmail.com>

Ok, only checked the kerneldoc now and found a few nitpicks. With those
fixed and presuming no bugs added to the code compared to last version
this is Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch>
> ---
>  Documentation/DocBook/drm.tmpl        |   6 +
>  drivers/gpu/drm/Makefile              |   3 +-
>  drivers/gpu/drm/drm_crtc.c            |  85 +++++++++---
>  drivers/gpu/drm/drm_crtc_helper.c     |   3 +-
>  drivers/gpu/drm/drm_fb_helper.c       |   4 +
>  drivers/gpu/drm/drm_modeset_lock.c    | 247 ++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_plane_helper.c    |   2 +-
>  drivers/gpu/drm/i915/intel_crt.c      |   5 +-
>  drivers/gpu/drm/i915/intel_display.c  |  56 +++++---
>  drivers/gpu/drm/i915/intel_dp.c       |  14 +-
>  drivers/gpu/drm/i915/intel_drv.h      |   6 +-
>  drivers/gpu/drm/i915/intel_opregion.c |   4 +-
>  drivers/gpu/drm/i915/intel_overlay.c  |   4 +-
>  drivers/gpu/drm/i915/intel_panel.c    |   8 +-
>  drivers/gpu/drm/i915/intel_sprite.c   |   2 +-
>  drivers/gpu/drm/i915/intel_tv.c       |   5 +-
>  drivers/gpu/drm/omapdrm/omap_crtc.c   |  10 +-
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c   |   8 +-
>  include/drm/drmP.h                    |   5 -
>  include/drm/drm_crtc.h                |  15 ++-
>  include/drm/drm_modeset_lock.h        | 123 +++++++++++++++++
>  21 files changed, 528 insertions(+), 87 deletions(-)
>  create mode 100644 drivers/gpu/drm/drm_modeset_lock.c
>  create mode 100644 include/drm/drm_modeset_lock.h
> 
> diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl
> index 00f1c25..efef637 100644
> --- a/Documentation/DocBook/drm.tmpl
> +++ b/Documentation/DocBook/drm.tmpl
> @@ -1791,6 +1791,12 @@ void intel_crt_init(struct drm_device *dev)
>        <title>KMS API Functions</title>
>  !Edrivers/gpu/drm/drm_crtc.c
>      </sect2>
> +    <sect2>
> +      <title>KMS Locking</title>
> +!Pdrivers/gpu/drm/drm_modeset_lock.c kms locking
> +!Iinclude/drm/drm_modeset_lock.h
> +!Edrivers/gpu/drm/drm_modeset_lock.c
> +    </sect2>
>    </sect1>
>  
>    <!-- Internals: kms helper functions -->
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 863db84..dd2ba42 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -13,7 +13,8 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
>  		drm_crtc.o drm_modes.o drm_edid.o \
>  		drm_info.o drm_debugfs.o drm_encoder_slave.o \
>  		drm_trace_points.o drm_global.o drm_prime.o \
> -		drm_rect.o drm_vma_manager.o drm_flip_work.o
> +		drm_rect.o drm_vma_manager.o drm_flip_work.o \
> +		drm_modeset_lock.o
>  
>  drm-$(CONFIG_COMPAT) += drm_ioc32.o
>  drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index f3b98d4..43735f3 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -37,6 +37,7 @@
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_edid.h>
>  #include <drm/drm_fourcc.h>
> +#include <drm/drm_modeset_lock.h>
>  
>  #include "drm_crtc_internal.h"
>  
> @@ -50,14 +51,42 @@
>   */
>  void drm_modeset_lock_all(struct drm_device *dev)
>  {
> -	struct drm_crtc *crtc;
> +	struct drm_mode_config *config = &dev->mode_config;
> +	struct drm_modeset_acquire_ctx *ctx;
> +	int ret;
>  
> -	mutex_lock(&dev->mode_config.mutex);
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (WARN_ON(!ctx))
> +		return;
>  
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	mutex_lock(&config->mutex);
>  
> -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> -		mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
> +	drm_modeset_acquire_init(ctx, 0);
> +
> +retry:
> +	ret = drm_modeset_lock(&config->connection_mutex, ctx);
> +	if (ret)
> +		goto fail;
> +	ret = drm_modeset_lock_all_crtcs(dev, ctx);
> +	if (ret)
> +		goto fail;
> +
> +	WARN_ON(config->acquire_ctx);
> +
> +	/* now we hold the locks, so now that it is safe, stash the
> +	 * ctx for drm_modeset_unlock_all():
> +	 */
> +	config->acquire_ctx = ctx;
> +
> +	drm_warn_on_modeset_not_all_locked(dev);
> +
> +	return;
> +
> +fail:
> +	if (ret == -EDEADLK) {
> +		drm_modeset_backoff(ctx);
> +		goto retry;
> +	}
>  }
>  EXPORT_SYMBOL(drm_modeset_lock_all);
>  
> @@ -69,12 +98,17 @@ EXPORT_SYMBOL(drm_modeset_lock_all);
>   */
>  void drm_modeset_unlock_all(struct drm_device *dev)
>  {
> -	struct drm_crtc *crtc;
> +	struct drm_mode_config *config = &dev->mode_config;
> +	struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
>  
> -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> -		mutex_unlock(&crtc->mutex);
> +	if (WARN_ON(!ctx))
> +		return;
> +
> +	config->acquire_ctx = NULL;
> +	drm_modeset_drop_locks(ctx);
> +	drm_modeset_acquire_fini(ctx);
>  
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +	kfree(ctx);
>  
>  	mutex_unlock(&dev->mode_config.mutex);
>  }
> @@ -95,9 +129,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
>  		return;
>  
>  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> -		WARN_ON(!mutex_is_locked(&crtc->mutex));
> +		WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
>  
> -	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
>  }
>  EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
> @@ -671,6 +705,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  }
>  EXPORT_SYMBOL(drm_framebuffer_remove);
>  
> +DEFINE_WW_CLASS(crtc_ww_class);
> +
>  /**
>   * drm_crtc_init_with_planes - Initialise a new CRTC object with
>   *    specified primary and cursor planes.
> @@ -690,6 +726,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>  			      void *cursor,
>  			      const struct drm_crtc_funcs *funcs)
>  {
> +	struct drm_mode_config *config = &dev->mode_config;
>  	int ret;
>  
>  	crtc->dev = dev;
> @@ -697,8 +734,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>  	crtc->invert_dimensions = false;
>  
>  	drm_modeset_lock_all(dev);
> -	mutex_init(&crtc->mutex);
> -	mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
> +	drm_modeset_lock_init(&crtc->mutex);
> +	/* dropped by _unlock_all(): */
> +	drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
>  
>  	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
>  	if (ret)
> @@ -706,8 +744,8 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>  
>  	crtc->base.properties = &crtc->properties;
>  
> -	list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
> -	dev->mode_config.num_crtc++;
> +	list_add_tail(&crtc->head, &config->crtc_list);
> +	config->num_crtc++;
>  
>  	crtc->primary = primary;
>  	if (primary)
> @@ -735,6 +773,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
>  	kfree(crtc->gamma_store);
>  	crtc->gamma_store = NULL;
>  
> +	drm_modeset_lock_fini(&crtc->mutex);
> +
>  	drm_mode_object_put(dev, &crtc->base);
>  	list_del(&crtc->head);
>  	dev->mode_config.num_crtc--;
> @@ -1798,7 +1838,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>  	DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
>  
>  	mutex_lock(&dev->mode_config.mutex);
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  
>  	connector = drm_connector_find(dev, out_resp->connector_id);
>  	if (!connector) {
> @@ -1897,7 +1937,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>  	out_resp->count_encoders = encoders_count;
>  
>  out:
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  	mutex_unlock(&dev->mode_config.mutex);
>  
>  	return ret;
> @@ -2481,7 +2521,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
>  		return -ENOENT;
>  	}
>  
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  	if (req->flags & DRM_MODE_CURSOR_BO) {
>  		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
>  			ret = -ENXIO;
> @@ -2505,7 +2545,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
>  		}
>  	}
>  out:
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  
>  	return ret;
>  
> @@ -4198,7 +4238,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>  	if (!crtc)
>  		return -ENOENT;
>  
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  	if (crtc->primary->fb == NULL) {
>  		/* The framebuffer is currently unbound, presumably
>  		 * due to a hotplug event, that userspace has not
> @@ -4282,7 +4322,7 @@ out:
>  		drm_framebuffer_unreference(fb);
>  	if (old_fb)
>  		drm_framebuffer_unreference(old_fb);
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  
>  	return ret;
>  }
> @@ -4647,7 +4687,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
>  void drm_mode_config_init(struct drm_device *dev)
>  {
>  	mutex_init(&dev->mode_config.mutex);
> -	mutex_init(&dev->mode_config.connection_mutex);
> +	drm_modeset_lock_init(&dev->mode_config.connection_mutex);
>  	mutex_init(&dev->mode_config.idr_mutex);
>  	mutex_init(&dev->mode_config.fb_lock);
>  	INIT_LIST_HEAD(&dev->mode_config.fb_list);
> @@ -4747,5 +4787,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
>  	}
>  
>  	idr_destroy(&dev->mode_config.crtc_idr);
> +	drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
>  }
>  EXPORT_SYMBOL(drm_mode_config_cleanup);
> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
> index b55d27c..eb1c062 100644
> --- a/drivers/gpu/drm/drm_crtc_helper.c
> +++ b/drivers/gpu/drm/drm_crtc_helper.c
> @@ -89,8 +89,7 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
>  	struct drm_device *dev = encoder->dev;
>  
>  	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
> -	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> -
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>  		if (connector->encoder == encoder)
>  			return true;
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 1ddc174..43329ce 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -331,6 +331,10 @@ static bool drm_fb_helper_force_kernel_mode(void)
>  		if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
>  			continue;
>  
> +		/* NOTE: we use lockless flag below to avoid grabbing other
> +		 * modeset locks.  So just trylock the underlying mutex
> +		 * directly:
> +		 */
>  		if (!mutex_trylock(&dev->mode_config.mutex)) {
>  			error = true;
>  			continue;
> diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
> new file mode 100644
> index 0000000..19b4e91
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_modeset_lock.c
> @@ -0,0 +1,247 @@
> +/*
> + * Copyright (C) 2014 Red Hat
> + * Author: Rob Clark <robdclark at gmail.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_modeset_lock.h>
> +
> +/**
> + * DOC: kms locking
> + *
> + * As KMS moves toward more fine grained locking, and atomic ioctl where
> + * userspace can indirectly control locking order, it becomes necessary
> + * to use ww_mutex and acquire-contexts to avoid deadlock.  But because

s/deadlock/deadlocks/.

> + * the locking is more distributed around the driver code, we want a bit
> + * of extra utility/tracking out of our acquire-ctx.  This is provided
> + * by drm_modeset_lock / drm_modeset_acquire_ctx.
> + *
> + * For basic principles of ww_mutex, see: Documentation/ww-mutex-design.txt
> + *
> + * The basic usage pattern is to:
> + *
> + *     drm_modeset_acquire_init(&ctx)
> + *   retry:
> + *     foreach (lock in random_ordered_set_of_locks) {
> + *       ret = drm_modeset_lock(lock, &ctx)
> + *       if (ret == -EDEADLK) {
> + *          drm_modeset_backoff(&ctx);
> + *          goto retry;
> + *       }
> + *     }
> + *
> + *     ... do stuff ...
> + *
> + *     drm_modeset_drop_locks(&ctx);
> + *     drm_modeset_acquire_fini(&ctx);
> + */
> +
> +
> +/**
> + * drm_modeset_acquire_init - initialize acquire context
> + * @ctx: the acquire context
> + * @flags: for future
> + */
> +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
> +		uint32_t flags)
> +{
> +	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
> +	INIT_LIST_HEAD(&ctx->locked);
> +}
> +EXPORT_SYMBOL(drm_modeset_acquire_init);
> +
> +/**
> + * drm_modeset_acquire_fini - cleanup acquire context
> + * @ctx: the acquire context
> + */
> +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
> +{
> +	ww_acquire_fini(&ctx->ww_ctx);
> +}
> +EXPORT_SYMBOL(drm_modeset_acquire_fini);
> +
> +/**
> + * drm_modeset_drop_locks - drop all locks
> + * @ctx: the acquire context
> + *
> + * Drop all locks currently held against this acquire context

Missing full stop after "context".

> + */
> +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
> +{
> +	WARN_ON(ctx->contended);
> +	while (!list_empty(&ctx->locked)) {
> +		struct drm_modeset_lock *lock;
> +
> +		lock = list_first_entry(&ctx->locked,
> +				struct drm_modeset_lock, head);
> +
> +		drm_modeset_unlock(lock);
> +	}
> +}
> +EXPORT_SYMBOL(drm_modeset_drop_locks);
> +
> +static inline int modeset_lock(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx,
> +		bool interruptible, bool slow)
> +{
> +	int ret;
> +
> +	WARN_ON(ctx->contended);
> +
> +	if (interruptible && slow) {
> +		ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
> +	} else if (interruptible) {
> +		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
> +	} else if (slow) {
> +		ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
> +		ret = 0;
> +	} else {
> +		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
> +	}
> +	if (!ret) {
> +		WARN_ON(!list_empty(&lock->head));
> +		list_add(&lock->head, &ctx->locked);
> +	} else if (ret == -EALREADY) {
> +		/* we already hold the lock.. this is fine.  For atomic
> +		 * we will need to be able to drm_modeset_lock() things
> +		 * without having to keep track of what is already locked
> +		 * or not.
> +		 */
> +		ret = 0;
> +	} else if (ret == -EDEADLK) {
> +		ctx->contended = lock;
> +	}
> +
> +	return ret;
> +}
> +
> +static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
> +		bool interruptible)
> +{
> +	struct drm_modeset_lock *contended = ctx->contended;
> +
> +	ctx->contended = NULL;
> +
> +	if (WARN_ON(!contended))
> +		return 0;
> +
> +	drm_modeset_drop_locks(ctx);
> +
> +	return modeset_lock(contended, ctx, interruptible, true);
> +}
> +
> +/**
> + * drm_modeset_backoff - deadlock avoidance backoff
> + * @ctx: the acquire context
> + *
> + * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),

s/If deadlock/If a deadlock/

> + * you must call this function to drop all currently held locks and
> + * block until the contended lock to becomes available.

s/to//

> + */
> +void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
> +{
> +	modeset_backoff(ctx, false);
> +}
> +EXPORT_SYMBOL(drm_modeset_backoff);
> +
> +/**
> + * drm_modeset_backoff_interruptible - deadlock avoidance backoff
> + * @ctx: the acquire context
> + *
> + * Interruptible version of drm_modeset_backoff()
> + */
> +int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
> +{
> +	return modeset_backoff(ctx, true);
> +}
> +EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
> +
> +/**
> + * drm_modeset_lock - take modeset lock
> + * @lock: lock to take
> + * @ctx: acquire ctx
> + *
> + * If ctx is not NULL, then it's acquire context is used and the lock

s/it's/its/

and maybe s/acquire context/ww acquire context/ since otherwise it's
unclear whether it's the drm lock context or the ww one we're talking
about here.

> + * will be tracked by the context and can be released by calling
> + * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
> + * deadlock scenario has been detected and it is an error to attempt
> + * to take any more locks without first calling drm_modeset_backoff().
> + */
> +int drm_modeset_lock(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx)
> +{
> +	if (ctx)
> +		return modeset_lock(lock, ctx, false, false);
> +
> +	ww_mutex_lock(&lock->mutex, NULL);
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_modeset_lock);
> +
> +/**
> + * drm_modeset_lock_interruptible - take modeset lock
> + * @lock: lock to take
> + * @ctx: acquire ctx
> + *
> + * Interruptible version of drm_modeset_lock()
> + */
> +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx)
> +{
> +	if (ctx)
> +		return modeset_lock(lock, ctx, true, false);
> +
> +	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
> +}
> +EXPORT_SYMBOL(drm_modeset_lock_interruptible);
> +
> +/**
> + * drm_modeset_unlock - drop modeset lock
> + * @lock: lock to release
> + */
> +void drm_modeset_unlock(struct drm_modeset_lock *lock)
> +{
> +	list_del_init(&lock->head);
> +	ww_mutex_unlock(&lock->mutex);
> +}
> +EXPORT_SYMBOL(drm_modeset_unlock);
> +
> +/* Temporary.. until we have sufficiently fine grained locking, there
> + * are a couple scenarios where it is convenient to grab all crtc locks.
> + * It is planned to remove this:
> + */
> +int drm_modeset_lock_all_crtcs(struct drm_device *dev,
> +		struct drm_modeset_acquire_ctx *ctx)
> +{
> +	struct drm_mode_config *config = &dev->mode_config;
> +	struct drm_crtc *crtc;
> +	int ret = 0;
> +
> +	list_for_each_entry(crtc, &config->crtc_list, head) {
> +		ret = drm_modeset_lock(&crtc->mutex, ctx);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
> index f3e0a23..1b15643 100644
> --- a/drivers/gpu/drm/drm_plane_helper.c
> +++ b/drivers/gpu/drm/drm_plane_helper.c
> @@ -60,7 +60,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
>  	 * need to grab the connection_mutex here to be able to make these
>  	 * checks.
>  	 */
> -	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  
>  	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>  		if (connector->encoder && connector->encoder->crtc == crtc) {
> diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
> index 1fc91df5..5a045d3 100644
> --- a/drivers/gpu/drm/i915/intel_crt.c
> +++ b/drivers/gpu/drm/i915/intel_crt.c
> @@ -630,6 +630,7 @@ intel_crt_detect(struct drm_connector *connector, bool force)
>  	enum intel_display_power_domain power_domain;
>  	enum drm_connector_status status;
>  	struct intel_load_detect_pipe tmp;
> +	struct drm_modeset_acquire_ctx ctx;
>  
>  	intel_runtime_pm_get(dev_priv);
>  
> @@ -673,12 +674,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
>  	}
>  
>  	/* for pre-945g platforms use load detect */
> -	if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
> +	if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) {
>  		if (intel_crt_detect_ddc(connector))
>  			status = connector_status_connected;
>  		else
>  			status = intel_crt_load_detect(crt);
> -		intel_release_load_detect_pipe(connector, &tmp);
> +		intel_release_load_detect_pipe(connector, &tmp, &ctx);
>  	} else
>  		status = connector_status_unknown;
>  
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index c2976ad..1ce4ad4 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -2576,7 +2576,7 @@ void intel_display_handle_reset(struct drm_device *dev)
>  	for_each_crtc(dev, crtc) {
>  		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>  
> -		mutex_lock(&crtc->mutex);
> +		drm_modeset_lock(&crtc->mutex, NULL);
>  		/*
>  		 * FIXME: Once we have proper support for primary planes (and
>  		 * disabling them without disabling the entire crtc) allow again
> @@ -2587,7 +2587,7 @@ void intel_display_handle_reset(struct drm_device *dev)
>  							       crtc->primary->fb,
>  							       crtc->x,
>  							       crtc->y);
> -		mutex_unlock(&crtc->mutex);
> +		drm_modeset_unlock(&crtc->mutex);
>  	}
>  }
>  
> @@ -8307,7 +8307,8 @@ mode_fits_in_fbdev(struct drm_device *dev,
>  
>  bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  				struct drm_display_mode *mode,
> -				struct intel_load_detect_pipe *old)
> +				struct intel_load_detect_pipe *old,
> +				struct drm_modeset_acquire_ctx *ctx)
>  {
>  	struct intel_crtc *intel_crtc;
>  	struct intel_encoder *intel_encoder =
> @@ -8317,13 +8318,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  	struct drm_crtc *crtc = NULL;
>  	struct drm_device *dev = encoder->dev;
>  	struct drm_framebuffer *fb;
> -	int i = -1;
> +	struct drm_mode_config *config = &dev->mode_config;
> +	int ret, i = -1;
>  
>  	DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
>  		      connector->base.id, connector->name,
>  		      encoder->base.id, encoder->name);
>  
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	drm_modeset_acquire_init(ctx, 0);
> +
> +retry:
> +	ret = drm_modeset_lock(&config->connection_mutex, ctx);
> +	if (ret)
> +		goto fail_unlock;
>  
>  	/*
>  	 * Algorithm gets a little messy:
> @@ -8339,7 +8346,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  	if (encoder->crtc) {
>  		crtc = encoder->crtc;
>  
> -		mutex_lock(&crtc->mutex);
> +		ret = drm_modeset_lock(&crtc->mutex, ctx);
> +		if (ret)
> +			goto fail_unlock;
>  
>  		old->dpms_mode = connector->dpms;
>  		old->load_detect_temp = false;
> @@ -8367,10 +8376,12 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  	 */
>  	if (!crtc) {
>  		DRM_DEBUG_KMS("no pipe available for load-detect\n");
> -		goto fail_unlock_connector;
> +		goto fail_unlock;
>  	}
>  
> -	mutex_lock(&crtc->mutex);
> +	ret = drm_modeset_lock(&crtc->mutex, ctx);
> +	if (ret)
> +		goto fail_unlock;
>  	intel_encoder->new_crtc = to_intel_crtc(crtc);
>  	to_intel_connector(connector)->new_encoder = intel_encoder;
>  
> @@ -8420,15 +8431,21 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  		intel_crtc->new_config = &intel_crtc->config;
>  	else
>  		intel_crtc->new_config = NULL;
> -	mutex_unlock(&crtc->mutex);
> -fail_unlock_connector:
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +fail_unlock:
> +	if (ret == -EDEADLK) {
> +		drm_modeset_backoff(ctx);
> +		goto retry;
> +	}
> +
> +	drm_modeset_drop_locks(ctx);
> +	drm_modeset_acquire_fini(ctx);
>  
>  	return false;
>  }
>  
>  void intel_release_load_detect_pipe(struct drm_connector *connector,
> -				    struct intel_load_detect_pipe *old)
> +				    struct intel_load_detect_pipe *old,
> +				    struct drm_modeset_acquire_ctx *ctx)
>  {
>  	struct intel_encoder *intel_encoder =
>  		intel_attached_encoder(connector);
> @@ -8452,8 +8469,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
>  			drm_framebuffer_unreference(old->release_fb);
>  		}
>  
> -		mutex_unlock(&crtc->mutex);
> -		mutex_unlock(&connector->dev->mode_config.connection_mutex);
> +		goto unlock;
>  		return;
>  	}
>  
> @@ -8461,8 +8477,9 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
>  	if (old->dpms_mode != DRM_MODE_DPMS_ON)
>  		connector->funcs->dpms(connector, old->dpms_mode);
>  
> -	mutex_unlock(&crtc->mutex);
> -	mutex_unlock(&connector->dev->mode_config.connection_mutex);
> +unlock:
> +	drm_modeset_drop_locks(ctx);
> +	drm_modeset_acquire_fini(ctx);
>  }
>  
>  static int i9xx_pll_refclk(struct drm_device *dev,
> @@ -10995,7 +11012,7 @@ enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
>  	struct drm_encoder *encoder = connector->base.encoder;
>  	struct drm_device *dev = connector->base.dev;
>  
> -	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  
>  	if (!encoder)
>  		return INVALID_PIPE;
> @@ -11805,6 +11822,7 @@ static void intel_enable_pipe_a(struct drm_device *dev)
>  	struct intel_connector *connector;
>  	struct drm_connector *crt = NULL;
>  	struct intel_load_detect_pipe load_detect_temp;
> +	struct drm_modeset_acquire_ctx ctx;
>  
>  	/* We can't just switch on the pipe A, we need to set things up with a
>  	 * proper mode and output configuration. As a gross hack, enable pipe A
> @@ -11821,8 +11839,8 @@ static void intel_enable_pipe_a(struct drm_device *dev)
>  	if (!crt)
>  		return;
>  
> -	if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp))
> -		intel_release_load_detect_pipe(crt, &load_detect_temp);
> +	if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx))
> +		intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx);
>  
>  
>  }
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index c4d8839..58096fa 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -1154,7 +1154,7 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
>  	u32 pp;
>  	u32 pp_stat_reg, pp_ctrl_reg;
>  
> -	WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  
>  	if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) {
>  		struct intel_digital_port *intel_dig_port =
> @@ -1191,9 +1191,9 @@ static void edp_panel_vdd_work(struct work_struct *__work)
>  						 struct intel_dp, panel_vdd_work);
>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>  
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  	edp_panel_vdd_off_sync(intel_dp);
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  }
>  
>  static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
> @@ -3666,9 +3666,9 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
>  	drm_encoder_cleanup(encoder);
>  	if (is_edp(intel_dp)) {
>  		cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
> -		mutex_lock(&dev->mode_config.connection_mutex);
> +		drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  		edp_panel_vdd_off_sync(intel_dp);
> -		mutex_unlock(&dev->mode_config.connection_mutex);
> +		drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  	}
>  	kfree(intel_dig_port);
>  }
> @@ -4247,9 +4247,9 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
>  		drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
>  		if (is_edp(intel_dp)) {
>  			cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
> -			mutex_lock(&dev->mode_config.connection_mutex);
> +			drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  			edp_panel_vdd_off_sync(intel_dp);
> -			mutex_unlock(&dev->mode_config.connection_mutex);
> +			drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  		}
>  		drm_sysfs_connector_remove(connector);
>  		drm_connector_cleanup(connector);
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index f1d5897..0de0498 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -748,9 +748,11 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
>  			 struct intel_digital_port *dport);
>  bool intel_get_load_detect_pipe(struct drm_connector *connector,
>  				struct drm_display_mode *mode,
> -				struct intel_load_detect_pipe *old);
> +				struct intel_load_detect_pipe *old,
> +				struct drm_modeset_acquire_ctx *ctx);
>  void intel_release_load_detect_pipe(struct drm_connector *connector,
> -				    struct intel_load_detect_pipe *old);
> +				    struct intel_load_detect_pipe *old,
> +				    struct drm_modeset_acquire_ctx *ctx);
>  int intel_pin_and_fence_fb_obj(struct drm_device *dev,
>  			       struct drm_i915_gem_object *obj,
>  			       struct intel_engine_cs *pipelined);
> diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
> index b812e9d..2e2c71f 100644
> --- a/drivers/gpu/drm/i915/intel_opregion.c
> +++ b/drivers/gpu/drm/i915/intel_opregion.c
> @@ -410,7 +410,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
>  	if (bclp > 255)
>  		return ASLC_BACKLIGHT_FAILED;
>  
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  
>  	/*
>  	 * Update backlight on all connectors that support backlight (usually
> @@ -421,7 +421,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
>  		intel_panel_set_backlight(intel_connector, bclp, 255);
>  	iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv);
>  
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  
>  
>  	return 0;
> diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
> index e97ea33..0396d13 100644
> --- a/drivers/gpu/drm/i915/intel_overlay.c
> +++ b/drivers/gpu/drm/i915/intel_overlay.c
> @@ -688,7 +688,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
>  	u32 swidth, swidthsw, sheight, ostride;
>  
>  	BUG_ON(!mutex_is_locked(&dev->struct_mutex));
> -	BUG_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +	BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  	BUG_ON(!overlay);
>  
>  	ret = intel_overlay_release_old_vid(overlay);
> @@ -793,7 +793,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
>  	int ret;
>  
>  	BUG_ON(!mutex_is_locked(&dev->struct_mutex));
> -	BUG_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +	BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>  
>  	ret = intel_overlay_recover_from_interrupt(overlay);
>  	if (ret != 0)
> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
> index d4d4156..2e1338a 100644
> --- a/drivers/gpu/drm/i915/intel_panel.c
> +++ b/drivers/gpu/drm/i915/intel_panel.c
> @@ -876,12 +876,12 @@ static int intel_backlight_device_update_status(struct backlight_device *bd)
>  	struct intel_connector *connector = bl_get_data(bd);
>  	struct drm_device *dev = connector->base.dev;
>  
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  	DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n",
>  		      bd->props.brightness, bd->props.max_brightness);
>  	intel_panel_set_backlight(connector, bd->props.brightness,
>  				  bd->props.max_brightness);
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  	return 0;
>  }
>  
> @@ -893,9 +893,9 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd)
>  	int ret;
>  
>  	intel_runtime_pm_get(dev_priv);
> -	mutex_lock(&dev->mode_config.connection_mutex);
> +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>  	ret = intel_panel_get_backlight(connector);
> -	mutex_unlock(&dev->mode_config.connection_mutex);
> +	drm_modeset_unlock(&dev->mode_config.connection_mutex);
>  	intel_runtime_pm_put(dev_priv);
>  
>  	return ret;
> diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
> index d6acd6b..1b66ddc 100644
> --- a/drivers/gpu/drm/i915/intel_sprite.c
> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> @@ -55,7 +55,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl
>  	int scanline, min, max, vblank_start;
>  	DEFINE_WAIT(wait);
>  
> -	WARN_ON(!mutex_is_locked(&crtc->base.mutex));
> +	WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
>  
>  	vblank_start = mode->crtc_vblank_start;
>  	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
> index 25850a8..67c6c9a 100644
> --- a/drivers/gpu/drm/i915/intel_tv.c
> +++ b/drivers/gpu/drm/i915/intel_tv.c
> @@ -1321,10 +1321,11 @@ intel_tv_detect(struct drm_connector *connector, bool force)
>  
>  	if (force) {
>  		struct intel_load_detect_pipe tmp;
> +		struct drm_modeset_acquire_ctx ctx;
>  
> -		if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
> +		if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) {
>  			type = intel_tv_detect_type(intel_tv, connector);
> -			intel_release_load_detect_pipe(connector, &tmp);
> +			intel_release_load_detect_pipe(connector, &tmp, &ctx);
>  		} else
>  			return connector_status_unknown;
>  	} else
> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
> index e3c47a8..2d28dc3 100644
> --- a/drivers/gpu/drm/omapdrm/omap_crtc.c
> +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
> @@ -319,13 +319,13 @@ static void page_flip_worker(struct work_struct *work)
>  	struct drm_display_mode *mode = &crtc->mode;
>  	struct drm_gem_object *bo;
>  
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  	omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
>  			0, 0, mode->hdisplay, mode->vdisplay,
>  			crtc->x << 16, crtc->y << 16,
>  			mode->hdisplay << 16, mode->vdisplay << 16,
>  			vblank_cb, crtc);
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  
>  	bo = omap_framebuffer_bo(crtc->primary->fb, 0);
>  	drm_gem_object_unreference_unlocked(bo);
> @@ -465,7 +465,7 @@ static void apply_worker(struct work_struct *work)
>  	 * the callbacks and list modification all serialized
>  	 * with respect to modesetting ioctls from userspace.
>  	 */
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  	dispc_runtime_get();
>  
>  	/*
> @@ -510,7 +510,7 @@ static void apply_worker(struct work_struct *work)
>  
>  out:
>  	dispc_runtime_put();
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  }
>  
>  int omap_crtc_apply(struct drm_crtc *crtc,
> @@ -518,7 +518,7 @@ int omap_crtc_apply(struct drm_crtc *crtc,
>  {
>  	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>  
> -	WARN_ON(!mutex_is_locked(&crtc->mutex));
> +	WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
>  
>  	/* no need to queue it again if it is already queued: */
>  	if (apply->queued)
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> index e7199b4..8f3edc4 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> @@ -187,7 +187,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
>  	 * can do this since the caller in the drm core doesn't check anything
>  	 * which is protected by any looks.
>  	 */
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  	drm_modeset_lock_all(dev_priv->dev);
>  
>  	/* A lot of the code assumes this */
> @@ -252,7 +252,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
>  	ret = 0;
>  out:
>  	drm_modeset_unlock_all(dev_priv->dev);
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  
>  	return ret;
>  }
> @@ -273,7 +273,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
>  	 * can do this since the caller in the drm core doesn't check anything
>  	 * which is protected by any looks.
>  	 */
> -	mutex_unlock(&crtc->mutex);
> +	drm_modeset_unlock(&crtc->mutex);
>  	drm_modeset_lock_all(dev_priv->dev);
>  
>  	vmw_cursor_update_position(dev_priv, shown,
> @@ -281,7 +281,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
>  				   du->cursor_y + du->hotspot_y);
>  
>  	drm_modeset_unlock_all(dev_priv->dev);
> -	mutex_lock(&crtc->mutex);
> +	drm_modeset_lock(&crtc->mutex, NULL);
>  
>  	return 0;
>  }
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 76ccaab..475ca5c 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -1186,11 +1186,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev)
>  	return ret;
>  }
>  
> -static inline bool drm_modeset_is_locked(struct drm_device *dev)
> -{
> -	return mutex_is_locked(&dev->mode_config.mutex);
> -}
> -
>  static inline bool drm_is_render_client(const struct drm_file *file_priv)
>  {
>  	return file_priv->minor->type == DRM_MINOR_RENDER;
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 6c295df..a7fac56 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -33,6 +33,7 @@
>  #include <linux/hdmi.h>
>  #include <drm/drm_mode.h>
>  #include <drm/drm_fourcc.h>
> +#include <drm/drm_modeset_lock.h>
>  
>  struct drm_device;
>  struct drm_mode_set;
> @@ -205,6 +206,10 @@ struct drm_property {
>  	struct list_head enum_blob_list;
>  };
>  
> +void drm_modeset_lock_all(struct drm_device *dev);
> +void drm_modeset_unlock_all(struct drm_device *dev);
> +void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
> +
>  struct drm_crtc;
>  struct drm_connector;
>  struct drm_encoder;
> @@ -280,6 +285,7 @@ struct drm_crtc_funcs {
>   * drm_crtc - central CRTC control structure
>   * @dev: parent DRM device
>   * @head: list management
> + * @mutex: per-CRTC locking
>   * @base: base KMS object for ID tracking etc.
>   * @primary: primary plane for this CRTC
>   * @cursor: cursor plane for this CRTC
> @@ -314,7 +320,7 @@ struct drm_crtc {
>  	 * state, ...) and a write lock for everything which can be update
>  	 * without a full modeset (fb, cursor data, ...)
>  	 */
> -	struct mutex mutex;
> +	struct drm_modeset_lock mutex;
>  
>  	struct drm_mode_object base;
>  
> @@ -738,7 +744,8 @@ struct drm_mode_group {
>   */
>  struct drm_mode_config {
>  	struct mutex mutex; /* protects configuration (mode lists etc.) */
> -	struct mutex connection_mutex; /* protects connector->encoder and encoder->crtc links */
> +	struct drm_modeset_lock connection_mutex; /* protects connector->encoder and encoder->crtc links */
> +	struct drm_modeset_acquire_ctx *acquire_ctx; /* for legacy _lock_all() / _unlock_all() */
>  	struct mutex idr_mutex; /* for IDR management */
>  	struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
>  	/* this is limited to one for now */
> @@ -839,10 +846,6 @@ struct drm_prop_enum_list {
>  	char *name;
>  };
>  
> -extern void drm_modeset_lock_all(struct drm_device *dev);
> -extern void drm_modeset_unlock_all(struct drm_device *dev);
> -extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
> -
>  extern int drm_crtc_init_with_planes(struct drm_device *dev,
>  				     struct drm_crtc *crtc,
>  				     struct drm_plane *primary,
> diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
> new file mode 100644
> index 0000000..fbd9f23
> --- /dev/null
> +++ b/include/drm/drm_modeset_lock.h
> @@ -0,0 +1,123 @@
> +/*
> + * Copyright (C) 2014 Red Hat
> + * Author: Rob Clark <robdclark at gmail.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#ifndef DRM_MODESET_LOCK_H_
> +#define DRM_MODESET_LOCK_H_
> +
> +#include <linux/ww_mutex.h>
> +
> +struct drm_modeset_lock;
> +
> +/**
> + * drm_modeset_acquire_ctx - locking context (see ww_acquire_ctx)
> + * @ww_ctx: base acquire ctx
> + * @contended: used internally for -EDEADLK handling
> + * @locked: list of held locks
> + *
> + * Each thread competing for a set of locks must use one acquire
> + * ctx.  And if any lock fxn returns -EDEADLK, it must backoff and
> + * retry.
> + */
> +struct drm_modeset_acquire_ctx {
> +
> +	struct ww_acquire_ctx ww_ctx;
> +
> +	/**
> +	 * Contended lock: if a lock is contended you should only call
> +	 * drm_modeset_backoff() which drops locks and slow-locks the
> +	 * contended lock.
> +	 */
> +	struct drm_modeset_lock *contended;
> +
> +	/**
> +	 * list of held locks (drm_modeset_lock)
> +	 */
> +	struct list_head locked;
> +};
> +
> +/**
> + * drm_modeset_lock - used for locking modeset resources.
> + * @mutex: resource locking
> + * @head: used to hold it's place on state->locked list when
> + *    part of an atomic update
> + *
> + * Used for locking CRTCs and other modeset resources.
> + */
> +struct drm_modeset_lock {
> +	/**
> +	 * modeset lock
> +	 */
> +	struct ww_mutex mutex;
> +
> +	/**
> +	 * Resources that are locked as part of an atomic update are added
> +	 * to a list (so we know what to unlock at the end).
> +	 */
> +	struct list_head head;
> +};
> +
> +extern struct ww_class crtc_ww_class;
> +
> +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
> +		uint32_t flags);
> +void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
> +void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
> +void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
> +int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx);
> +
> +/**
> + * drm_modeset_lock_init - initialize lock

Won't kerneldoc complain about the lack of @lock: here? More below of the
same.

> + */
> +static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock)
> +{
> +	ww_mutex_init(&lock->mutex, &crtc_ww_class);
> +	INIT_LIST_HEAD(&lock->head);
> +}
> +
> +/**
> + * drm_modeset_lock_fini - cleanup lock
> + */
> +static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
> +{
> +	WARN_ON(!list_empty(&lock->head));
> +}
> +
> +/**
> + * drm_modeset_is_locked - equivalent to mutex_is_locked()
> + */
> +static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock)
> +{
> +	return ww_mutex_is_locked(&lock->mutex);
> +}
> +
> +int drm_modeset_lock(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx);
> +int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
> +		struct drm_modeset_acquire_ctx *ctx);
> +void drm_modeset_unlock(struct drm_modeset_lock *lock);
> +
> +struct drm_device;
> +int drm_modeset_lock_all_crtcs(struct drm_device *dev,
> +		struct drm_modeset_acquire_ctx *ctx);
> +
> +#endif /* DRM_MODESET_LOCK_H_ */
> -- 
> 1.9.3
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch


More information about the dri-devel mailing list