xref: /dflybsd-src/sys/dev/drm/drm_modeset_lock.c (revision fae36b11104b8205e65c6683845a4be9902ff63f)
1ba55f2f5SFrançois Tigeot /*
2ba55f2f5SFrançois Tigeot  * Copyright (C) 2014 Red Hat
3ba55f2f5SFrançois Tigeot  * Author: Rob Clark <robdclark@gmail.com>
4ba55f2f5SFrançois Tigeot  *
5ba55f2f5SFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
6ba55f2f5SFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
7ba55f2f5SFrançois Tigeot  * to deal in the Software without restriction, including without limitation
8ba55f2f5SFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9ba55f2f5SFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
10ba55f2f5SFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
11ba55f2f5SFrançois Tigeot  *
12ba55f2f5SFrançois Tigeot  * The above copyright notice and this permission notice shall be included in
13ba55f2f5SFrançois Tigeot  * all copies or substantial portions of the Software.
14ba55f2f5SFrançois Tigeot  *
15ba55f2f5SFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16ba55f2f5SFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17ba55f2f5SFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18ba55f2f5SFrançois Tigeot  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19ba55f2f5SFrançois Tigeot  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20ba55f2f5SFrançois Tigeot  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21ba55f2f5SFrançois Tigeot  * OTHER DEALINGS IN THE SOFTWARE.
22ba55f2f5SFrançois Tigeot  */
23ba55f2f5SFrançois Tigeot 
24ba55f2f5SFrançois Tigeot #include <drm/drmP.h>
25ba55f2f5SFrançois Tigeot #include <drm/drm_crtc.h>
26ba55f2f5SFrançois Tigeot #include <drm/drm_modeset_lock.h>
27ba55f2f5SFrançois Tigeot 
28ba55f2f5SFrançois Tigeot /**
29ba55f2f5SFrançois Tigeot  * DOC: kms locking
30ba55f2f5SFrançois Tigeot  *
31ba55f2f5SFrançois Tigeot  * As KMS moves toward more fine grained locking, and atomic ioctl where
32ba55f2f5SFrançois Tigeot  * userspace can indirectly control locking order, it becomes necessary
331dedbd3bSFrançois Tigeot  * to use &ww_mutex and acquire-contexts to avoid deadlocks.  But because
34ba55f2f5SFrançois Tigeot  * the locking is more distributed around the driver code, we want a bit
35ba55f2f5SFrançois Tigeot  * of extra utility/tracking out of our acquire-ctx.  This is provided
36a85cb24fSFrançois Tigeot  * by &struct drm_modeset_lock and &struct drm_modeset_acquire_ctx.
37ba55f2f5SFrançois Tigeot  *
381dedbd3bSFrançois Tigeot  * For basic principles of &ww_mutex, see: Documentation/locking/ww-mutex-design.txt
39ba55f2f5SFrançois Tigeot  *
401dedbd3bSFrançois Tigeot  * The basic usage pattern is to::
41ba55f2f5SFrançois Tigeot  *
423f2dd94aSFrançois Tigeot  *     drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
43ba55f2f5SFrançois Tigeot  *     retry:
44ba55f2f5SFrançois Tigeot  *     foreach (lock in random_ordered_set_of_locks) {
453f2dd94aSFrançois Tigeot  *         ret = drm_modeset_lock(lock, ctx)
46ba55f2f5SFrançois Tigeot  *         if (ret == -EDEADLK) {
473f2dd94aSFrançois Tigeot  *             ret = drm_modeset_backoff(ctx);
483f2dd94aSFrançois Tigeot  *             if (!ret)
49ba55f2f5SFrançois Tigeot  *                 goto retry;
50ba55f2f5SFrançois Tigeot  *         }
513f2dd94aSFrançois Tigeot  *         if (ret)
523f2dd94aSFrançois Tigeot  *             goto out;
53ba55f2f5SFrançois Tigeot  *     }
54ba55f2f5SFrançois Tigeot  *     ... do stuff ...
553f2dd94aSFrançois Tigeot  *     out:
563f2dd94aSFrançois Tigeot  *     drm_modeset_drop_locks(ctx);
573f2dd94aSFrançois Tigeot  *     drm_modeset_acquire_fini(ctx);
581dedbd3bSFrançois Tigeot  *
593f2dd94aSFrançois Tigeot  * If all that is needed is a single modeset lock, then the &struct
603f2dd94aSFrançois Tigeot  * drm_modeset_acquire_ctx is not needed and the locking can be simplified
613f2dd94aSFrançois Tigeot  * by passing a NULL instead of ctx in the drm_modeset_lock() call or
623f2dd94aSFrançois Tigeot  * calling  drm_modeset_lock_single_interruptible(). To unlock afterwards
633f2dd94aSFrançois Tigeot  * call drm_modeset_unlock().
643f2dd94aSFrançois Tigeot  *
653f2dd94aSFrançois Tigeot  * On top of these per-object locks using &ww_mutex there's also an overall
66a85cb24fSFrançois Tigeot  * &drm_mode_config.mutex, for protecting everything else. Mostly this means
671dedbd3bSFrançois Tigeot  * probe state of connectors, and preventing hotplug add/removal of connectors.
681dedbd3bSFrançois Tigeot  *
691dedbd3bSFrançois Tigeot  * Finally there's a bunch of dedicated locks to protect drm core internal
701dedbd3bSFrançois Tigeot  * lists and lookup data structures.
71ba55f2f5SFrançois Tigeot  */
72ba55f2f5SFrançois Tigeot 
734be47400SFrançois Tigeot static DEFINE_WW_CLASS(crtc_ww_class);
744be47400SFrançois Tigeot 
75ba55f2f5SFrançois Tigeot /**
76a05eeebfSFrançois Tigeot  * drm_modeset_lock_all - take all modeset locks
77aee94f86SFrançois Tigeot  * @dev: DRM device
781b13d190SFrançois Tigeot  *
79a05eeebfSFrançois Tigeot  * This function takes all modeset locks, suitable where a more fine-grained
80aee94f86SFrançois Tigeot  * scheme isn't (yet) implemented. Locks must be dropped by calling the
81aee94f86SFrançois Tigeot  * drm_modeset_unlock_all() function.
82aee94f86SFrançois Tigeot  *
83aee94f86SFrançois Tigeot  * This function is deprecated. It allocates a lock acquisition context and
84a85cb24fSFrançois Tigeot  * stores it in &drm_device.mode_config. This facilitate conversion of
85aee94f86SFrançois Tigeot  * existing code because it removes the need to manually deal with the
86aee94f86SFrançois Tigeot  * acquisition context, but it is also brittle because the context is global
87aee94f86SFrançois Tigeot  * and care must be taken not to nest calls. New code should use the
88aee94f86SFrançois Tigeot  * drm_modeset_lock_all_ctx() function and pass in the context explicitly.
891b13d190SFrançois Tigeot  */
drm_modeset_lock_all(struct drm_device * dev)90a05eeebfSFrançois Tigeot void drm_modeset_lock_all(struct drm_device *dev)
911b13d190SFrançois Tigeot {
921b13d190SFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
931b13d190SFrançois Tigeot 	struct drm_modeset_acquire_ctx *ctx;
941b13d190SFrançois Tigeot 	int ret;
951b13d190SFrançois Tigeot 
963f2dd94aSFrançois Tigeot 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL | __GFP_NOFAIL);
97a05eeebfSFrançois Tigeot 	if (WARN_ON(!ctx))
98a05eeebfSFrançois Tigeot 		return;
991b13d190SFrançois Tigeot 
1001b13d190SFrançois Tigeot 	mutex_lock(&config->mutex);
1011b13d190SFrançois Tigeot 
1021b13d190SFrançois Tigeot 	drm_modeset_acquire_init(ctx, 0);
1031b13d190SFrançois Tigeot 
1041b13d190SFrançois Tigeot retry:
105aee94f86SFrançois Tigeot 	ret = drm_modeset_lock_all_ctx(dev, ctx);
106aee94f86SFrançois Tigeot 	if (ret < 0) {
1071b13d190SFrançois Tigeot 		if (ret == -EDEADLK) {
1081b13d190SFrançois Tigeot 			drm_modeset_backoff(ctx);
1091b13d190SFrançois Tigeot 			goto retry;
1101b13d190SFrançois Tigeot 		}
1112c9916cdSFrançois Tigeot 
112aee94f86SFrançois Tigeot 		drm_modeset_acquire_fini(ctx);
11319c468b4SFrançois Tigeot 		kfree(ctx);
114aee94f86SFrançois Tigeot 		return;
115aee94f86SFrançois Tigeot 	}
116aee94f86SFrançois Tigeot 
117aee94f86SFrançois Tigeot 	WARN_ON(config->acquire_ctx);
118aee94f86SFrançois Tigeot 
119aee94f86SFrançois Tigeot 	/*
120aee94f86SFrançois Tigeot 	 * We hold the locks now, so it is safe to stash the acquisition
121aee94f86SFrançois Tigeot 	 * context for drm_modeset_unlock_all().
122aee94f86SFrançois Tigeot 	 */
123aee94f86SFrançois Tigeot 	config->acquire_ctx = ctx;
124aee94f86SFrançois Tigeot 
125aee94f86SFrançois Tigeot 	drm_warn_on_modeset_not_all_locked(dev);
1261b13d190SFrançois Tigeot }
1271b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock_all);
1281b13d190SFrançois Tigeot 
1291b13d190SFrançois Tigeot /**
1301b13d190SFrançois Tigeot  * drm_modeset_unlock_all - drop all modeset locks
131aee94f86SFrançois Tigeot  * @dev: DRM device
1321b13d190SFrançois Tigeot  *
133aee94f86SFrançois Tigeot  * This function drops all modeset locks taken by a previous call to the
134aee94f86SFrançois Tigeot  * drm_modeset_lock_all() function.
135aee94f86SFrançois Tigeot  *
136aee94f86SFrançois Tigeot  * This function is deprecated. It uses the lock acquisition context stored
137a85cb24fSFrançois Tigeot  * in &drm_device.mode_config. This facilitates conversion of existing
138aee94f86SFrançois Tigeot  * code because it removes the need to manually deal with the acquisition
139aee94f86SFrançois Tigeot  * context, but it is also brittle because the context is global and care must
140aee94f86SFrançois Tigeot  * be taken not to nest calls. New code should pass the acquisition context
141aee94f86SFrançois Tigeot  * directly to the drm_modeset_drop_locks() function.
1421b13d190SFrançois Tigeot  */
drm_modeset_unlock_all(struct drm_device * dev)1431b13d190SFrançois Tigeot void drm_modeset_unlock_all(struct drm_device *dev)
1441b13d190SFrançois Tigeot {
1451b13d190SFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
1461b13d190SFrançois Tigeot 	struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
1471b13d190SFrançois Tigeot 
1481b13d190SFrançois Tigeot 	if (WARN_ON(!ctx))
1491b13d190SFrançois Tigeot 		return;
1501b13d190SFrançois Tigeot 
1511b13d190SFrançois Tigeot 	config->acquire_ctx = NULL;
1521b13d190SFrançois Tigeot 	drm_modeset_drop_locks(ctx);
1531b13d190SFrançois Tigeot 	drm_modeset_acquire_fini(ctx);
1541b13d190SFrançois Tigeot 
1551b13d190SFrançois Tigeot 	kfree(ctx);
1561b13d190SFrançois Tigeot 
1571b13d190SFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
1581b13d190SFrançois Tigeot }
1591b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_unlock_all);
1601b13d190SFrançois Tigeot 
1611b13d190SFrançois Tigeot /**
1621b13d190SFrançois Tigeot  * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
1631b13d190SFrançois Tigeot  * @dev: device
1641b13d190SFrançois Tigeot  *
1651b13d190SFrançois Tigeot  * Useful as a debug assert.
1661b13d190SFrançois Tigeot  */
drm_warn_on_modeset_not_all_locked(struct drm_device * dev)1671b13d190SFrançois Tigeot void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
1681b13d190SFrançois Tigeot {
1691b13d190SFrançois Tigeot 	struct drm_crtc *crtc;
1701b13d190SFrançois Tigeot 
1711b13d190SFrançois Tigeot 	/* Locking is currently fubar in the panic handler. */
172a85cb24fSFrançois Tigeot #ifdef __DragonFly__
173a85cb24fSFrançois Tigeot 	if (panicstr)
174a85cb24fSFrançois Tigeot 		return;
175a85cb24fSFrançois Tigeot #else
1761b13d190SFrançois Tigeot 	if (oops_in_progress)
1771b13d190SFrançois Tigeot 		return;
178a85cb24fSFrançois Tigeot #endif
1791b13d190SFrançois Tigeot 
180a05eeebfSFrançois Tigeot 	drm_for_each_crtc(crtc, dev)
1811b13d190SFrançois Tigeot 		WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
1821b13d190SFrançois Tigeot 
1831b13d190SFrançois Tigeot 	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
1841b13d190SFrançois Tigeot 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
1851b13d190SFrançois Tigeot }
1861b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
1871b13d190SFrançois Tigeot 
1881b13d190SFrançois Tigeot /**
189ba55f2f5SFrançois Tigeot  * drm_modeset_acquire_init - initialize acquire context
190ba55f2f5SFrançois Tigeot  * @ctx: the acquire context
1913f2dd94aSFrançois Tigeot  * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE
1923f2dd94aSFrançois Tigeot  *
1933f2dd94aSFrançois Tigeot  * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags,
1943f2dd94aSFrançois Tigeot  * all calls to drm_modeset_lock() will perform an interruptible
1953f2dd94aSFrançois Tigeot  * wait.
196ba55f2f5SFrançois Tigeot  */
drm_modeset_acquire_init(struct drm_modeset_acquire_ctx * ctx,uint32_t flags)197ba55f2f5SFrançois Tigeot void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
198ba55f2f5SFrançois Tigeot 		uint32_t flags)
199ba55f2f5SFrançois Tigeot {
200ba55f2f5SFrançois Tigeot 	memset(ctx, 0, sizeof(*ctx));
201ba55f2f5SFrançois Tigeot 	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
202ba55f2f5SFrançois Tigeot 	INIT_LIST_HEAD(&ctx->locked);
2033f2dd94aSFrançois Tigeot 
2043f2dd94aSFrançois Tigeot 	if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
2053f2dd94aSFrançois Tigeot 		ctx->interruptible = true;
206ba55f2f5SFrançois Tigeot }
207ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_acquire_init);
208ba55f2f5SFrançois Tigeot 
209ba55f2f5SFrançois Tigeot /**
210ba55f2f5SFrançois Tigeot  * drm_modeset_acquire_fini - cleanup acquire context
211ba55f2f5SFrançois Tigeot  * @ctx: the acquire context
212ba55f2f5SFrançois Tigeot  */
drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx * ctx)213ba55f2f5SFrançois Tigeot void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
214ba55f2f5SFrançois Tigeot {
215ba55f2f5SFrançois Tigeot 	ww_acquire_fini(&ctx->ww_ctx);
216ba55f2f5SFrançois Tigeot }
217ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_acquire_fini);
218ba55f2f5SFrançois Tigeot 
219ba55f2f5SFrançois Tigeot /**
220ba55f2f5SFrançois Tigeot  * drm_modeset_drop_locks - drop all locks
221ba55f2f5SFrançois Tigeot  * @ctx: the acquire context
222ba55f2f5SFrançois Tigeot  *
223ba55f2f5SFrançois Tigeot  * Drop all locks currently held against this acquire context.
224ba55f2f5SFrançois Tigeot  */
drm_modeset_drop_locks(struct drm_modeset_acquire_ctx * ctx)225ba55f2f5SFrançois Tigeot void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
226ba55f2f5SFrançois Tigeot {
227ba55f2f5SFrançois Tigeot 	WARN_ON(ctx->contended);
228ba55f2f5SFrançois Tigeot 	while (!list_empty(&ctx->locked)) {
2294a1f0d75SFrançois Tigeot 		struct drm_modeset_lock_info *info;
230ba55f2f5SFrançois Tigeot 
2314a1f0d75SFrançois Tigeot 		info = list_first_entry(&ctx->locked,
2324a1f0d75SFrançois Tigeot 				struct drm_modeset_lock_info, ctx_entry);
233ba55f2f5SFrançois Tigeot 
2344a1f0d75SFrançois Tigeot 		drm_modeset_unlock(info->lock);
235ba55f2f5SFrançois Tigeot 	}
236ba55f2f5SFrançois Tigeot }
237ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_drop_locks);
238ba55f2f5SFrançois Tigeot 
modeset_lock(struct drm_modeset_lock * lock,struct drm_modeset_acquire_ctx * ctx,bool interruptible,bool slow)239ba55f2f5SFrançois Tigeot static inline int modeset_lock(struct drm_modeset_lock *lock,
240ba55f2f5SFrançois Tigeot 		struct drm_modeset_acquire_ctx *ctx,
241ba55f2f5SFrançois Tigeot 		bool interruptible, bool slow)
242ba55f2f5SFrançois Tigeot {
243ba55f2f5SFrançois Tigeot 	int ret;
244ba55f2f5SFrançois Tigeot 
245ba55f2f5SFrançois Tigeot 	WARN_ON(ctx->contended);
246ba55f2f5SFrançois Tigeot 
2472c9916cdSFrançois Tigeot 	if (ctx->trylock_only) {
248352ff8bdSFrançois Tigeot #if 0
249352ff8bdSFrançois Tigeot 		lockdep_assert_held(&ctx->ww_ctx);
250352ff8bdSFrançois Tigeot #endif
251352ff8bdSFrançois Tigeot 
2522c9916cdSFrançois Tigeot 		if (!ww_mutex_trylock(&lock->mutex))
2532c9916cdSFrançois Tigeot 			return -EBUSY;
2542c9916cdSFrançois Tigeot 		else
2552c9916cdSFrançois Tigeot 			return 0;
2562c9916cdSFrançois Tigeot 	} else if (interruptible && slow) {
257ba55f2f5SFrançois Tigeot 		ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
258ba55f2f5SFrançois Tigeot 	} else if (interruptible) {
259ba55f2f5SFrançois Tigeot 		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
260ba55f2f5SFrançois Tigeot 	} else if (slow) {
261*fae36b11SMatthew Dillon 		ret = ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
262*fae36b11SMatthew Dillon 		if (ret)
263*fae36b11SMatthew Dillon 		    kprintf("DRM: Warning: modeset_lock SLOW failed %d\n", ret);
264*fae36b11SMatthew Dillon 		// ret = 0;
265ba55f2f5SFrançois Tigeot 	} else {
266ba55f2f5SFrançois Tigeot 		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
267ba55f2f5SFrançois Tigeot 	}
2684a1f0d75SFrançois Tigeot 	if (ret == -EALREADY) {
269ba55f2f5SFrançois Tigeot 		/* we already hold the lock.. this is fine.  For atomic
270ba55f2f5SFrançois Tigeot 		 * we will need to be able to drm_modeset_lock() things
271ba55f2f5SFrançois Tigeot 		 * without having to keep track of what is already locked
272ba55f2f5SFrançois Tigeot 		 * or not.
273ba55f2f5SFrançois Tigeot 		 */
274ba55f2f5SFrançois Tigeot 		ret = 0;
275ba55f2f5SFrançois Tigeot 	} else if (ret == -EDEADLK) {
276ba55f2f5SFrançois Tigeot 		ctx->contended = lock;
277ba55f2f5SFrançois Tigeot 	}
2784a1f0d75SFrançois Tigeot 	if (ret == 0) {
2794a1f0d75SFrançois Tigeot 		struct drm_modeset_lock_info *info;
2804a1f0d75SFrançois Tigeot 
2814a1f0d75SFrançois Tigeot 		info = kzalloc(sizeof(*info), GFP_KERNEL);
2824a1f0d75SFrançois Tigeot 		INIT_LIST_HEAD(&info->ctx_entry);
2834a1f0d75SFrançois Tigeot 		INIT_LIST_HEAD(&info->lock_entry);
2844a1f0d75SFrançois Tigeot 		info->lock = lock;
2854a1f0d75SFrançois Tigeot 		info->ctx = ctx;
2864a1f0d75SFrançois Tigeot 		list_add(&info->ctx_entry, &ctx->locked);
2874be47400SFrançois Tigeot 		list_add(&info->lock_entry, &lock->head);
2884a1f0d75SFrançois Tigeot 	}
289ba55f2f5SFrançois Tigeot 
290ba55f2f5SFrançois Tigeot 	return ret;
291ba55f2f5SFrançois Tigeot }
292ba55f2f5SFrançois Tigeot 
2933f2dd94aSFrançois Tigeot /**
2943f2dd94aSFrançois Tigeot  * drm_modeset_backoff - deadlock avoidance backoff
2953f2dd94aSFrançois Tigeot  * @ctx: the acquire context
2963f2dd94aSFrançois Tigeot  *
2973f2dd94aSFrançois Tigeot  * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
2983f2dd94aSFrançois Tigeot  * you must call this function to drop all currently held locks and
2993f2dd94aSFrançois Tigeot  * block until the contended lock becomes available.
3003f2dd94aSFrançois Tigeot  *
3013f2dd94aSFrançois Tigeot  * This function returns 0 on success, or -ERESTARTSYS if this context
3023f2dd94aSFrançois Tigeot  * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the
3033f2dd94aSFrançois Tigeot  * wait has been interrupted.
3043f2dd94aSFrançois Tigeot  */
drm_modeset_backoff(struct drm_modeset_acquire_ctx * ctx)3053f2dd94aSFrançois Tigeot int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
306ba55f2f5SFrançois Tigeot {
307ba55f2f5SFrançois Tigeot 	struct drm_modeset_lock *contended = ctx->contended;
308ba55f2f5SFrançois Tigeot 
309ba55f2f5SFrançois Tigeot 	ctx->contended = NULL;
310ba55f2f5SFrançois Tigeot 
311ba55f2f5SFrançois Tigeot 	if (WARN_ON(!contended))
312ba55f2f5SFrançois Tigeot 		return 0;
313ba55f2f5SFrançois Tigeot 
314ba55f2f5SFrançois Tigeot 	drm_modeset_drop_locks(ctx);
315ba55f2f5SFrançois Tigeot 
3163f2dd94aSFrançois Tigeot 	return modeset_lock(contended, ctx, ctx->interruptible, true);
317ba55f2f5SFrançois Tigeot }
318ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_backoff);
319ba55f2f5SFrançois Tigeot 
320ba55f2f5SFrançois Tigeot /**
3214be47400SFrançois Tigeot  * drm_modeset_lock_init - initialize lock
3224be47400SFrançois Tigeot  * @lock: lock to init
3234be47400SFrançois Tigeot  */
drm_modeset_lock_init(struct drm_modeset_lock * lock)3244be47400SFrançois Tigeot void drm_modeset_lock_init(struct drm_modeset_lock *lock)
3254be47400SFrançois Tigeot {
3264be47400SFrançois Tigeot 	ww_mutex_init(&lock->mutex, &crtc_ww_class);
3274be47400SFrançois Tigeot 	INIT_LIST_HEAD(&lock->head);
3284be47400SFrançois Tigeot }
3294be47400SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock_init);
3304be47400SFrançois Tigeot 
3314be47400SFrançois Tigeot /**
332ba55f2f5SFrançois Tigeot  * drm_modeset_lock - take modeset lock
333ba55f2f5SFrançois Tigeot  * @lock: lock to take
334ba55f2f5SFrançois Tigeot  * @ctx: acquire ctx
335ba55f2f5SFrançois Tigeot  *
3363f2dd94aSFrançois Tigeot  * If @ctx is not NULL, then its ww acquire context is used and the
337ba55f2f5SFrançois Tigeot  * lock will be tracked by the context and can be released by calling
338ba55f2f5SFrançois Tigeot  * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
339ba55f2f5SFrançois Tigeot  * deadlock scenario has been detected and it is an error to attempt
340ba55f2f5SFrançois Tigeot  * to take any more locks without first calling drm_modeset_backoff().
3413f2dd94aSFrançois Tigeot  *
3423f2dd94aSFrançois Tigeot  * If the @ctx is not NULL and initialized with
3433f2dd94aSFrançois Tigeot  * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with
3443f2dd94aSFrançois Tigeot  * -ERESTARTSYS when interrupted.
3453f2dd94aSFrançois Tigeot  *
3463f2dd94aSFrançois Tigeot  * If @ctx is NULL then the function call behaves like a normal,
3473f2dd94aSFrançois Tigeot  * uninterruptible non-nesting mutex_lock() call.
348ba55f2f5SFrançois Tigeot  */
drm_modeset_lock(struct drm_modeset_lock * lock,struct drm_modeset_acquire_ctx * ctx)349ba55f2f5SFrançois Tigeot int drm_modeset_lock(struct drm_modeset_lock *lock,
350ba55f2f5SFrançois Tigeot 		struct drm_modeset_acquire_ctx *ctx)
351ba55f2f5SFrançois Tigeot {
352ba55f2f5SFrançois Tigeot 	if (ctx)
3533f2dd94aSFrançois Tigeot 		return modeset_lock(lock, ctx, ctx->interruptible, false);
354ba55f2f5SFrançois Tigeot 
355ba55f2f5SFrançois Tigeot 	ww_mutex_lock(&lock->mutex, NULL);
356ba55f2f5SFrançois Tigeot 	return 0;
357ba55f2f5SFrançois Tigeot }
358ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock);
359ba55f2f5SFrançois Tigeot 
360ba55f2f5SFrançois Tigeot /**
3613f2dd94aSFrançois Tigeot  * drm_modeset_lock_single_interruptible - take a single modeset lock
362ba55f2f5SFrançois Tigeot  * @lock: lock to take
363ba55f2f5SFrançois Tigeot  *
3643f2dd94aSFrançois Tigeot  * This function behaves as drm_modeset_lock() with a NULL context,
3653f2dd94aSFrançois Tigeot  * but performs interruptible waits.
3663f2dd94aSFrançois Tigeot  *
3673f2dd94aSFrançois Tigeot  * This function returns 0 on success, or -ERESTARTSYS when interrupted.
368ba55f2f5SFrançois Tigeot  */
drm_modeset_lock_single_interruptible(struct drm_modeset_lock * lock)3693f2dd94aSFrançois Tigeot int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock)
370ba55f2f5SFrançois Tigeot {
371ba55f2f5SFrançois Tigeot 	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
372ba55f2f5SFrançois Tigeot }
3733f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock_single_interruptible);
374ba55f2f5SFrançois Tigeot 
375ba55f2f5SFrançois Tigeot /**
376ba55f2f5SFrançois Tigeot  * drm_modeset_unlock - drop modeset lock
377ba55f2f5SFrançois Tigeot  * @lock: lock to release
378ba55f2f5SFrançois Tigeot  */
drm_modeset_unlock(struct drm_modeset_lock * lock)379ba55f2f5SFrançois Tigeot void drm_modeset_unlock(struct drm_modeset_lock *lock)
380ba55f2f5SFrançois Tigeot {
3814a1f0d75SFrançois Tigeot 	struct drm_modeset_lock_info *info;
3824a1f0d75SFrançois Tigeot 
3834a1f0d75SFrançois Tigeot 	/* undo in reverse order */
3844be47400SFrançois Tigeot 	if (!list_empty(&lock->head)) {
3854be47400SFrançois Tigeot 		info = list_last_entry(&lock->head,
3864a1f0d75SFrançois Tigeot 				struct drm_modeset_lock_info, lock_entry);
3874a1f0d75SFrançois Tigeot 		list_del_init(&info->lock_entry);
3884a1f0d75SFrançois Tigeot 		if (info->ctx)
3894a1f0d75SFrançois Tigeot 			list_del_init(&info->ctx_entry);
3904a1f0d75SFrançois Tigeot 		kfree(info);
3914a1f0d75SFrançois Tigeot 	}
392ba55f2f5SFrançois Tigeot 	ww_mutex_unlock(&lock->mutex);
393ba55f2f5SFrançois Tigeot }
394ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_unlock);
395ba55f2f5SFrançois Tigeot 
396aee94f86SFrançois Tigeot /**
397aee94f86SFrançois Tigeot  * drm_modeset_lock_all_ctx - take all modeset locks
398aee94f86SFrançois Tigeot  * @dev: DRM device
399aee94f86SFrançois Tigeot  * @ctx: lock acquisition context
400aee94f86SFrançois Tigeot  *
401aee94f86SFrançois Tigeot  * This function takes all modeset locks, suitable where a more fine-grained
402aee94f86SFrançois Tigeot  * scheme isn't (yet) implemented.
403aee94f86SFrançois Tigeot  *
404a85cb24fSFrançois Tigeot  * Unlike drm_modeset_lock_all(), it doesn't take the &drm_mode_config.mutex
405aee94f86SFrançois Tigeot  * since that lock isn't required for modeset state changes. Callers which
406aee94f86SFrançois Tigeot  * need to grab that lock too need to do so outside of the acquire context
407aee94f86SFrançois Tigeot  * @ctx.
408aee94f86SFrançois Tigeot  *
409aee94f86SFrançois Tigeot  * Locks acquired with this function should be released by calling the
410aee94f86SFrançois Tigeot  * drm_modeset_drop_locks() function on @ctx.
411aee94f86SFrançois Tigeot  *
412aee94f86SFrançois Tigeot  * Returns: 0 on success or a negative error-code on failure.
413aee94f86SFrançois Tigeot  */
drm_modeset_lock_all_ctx(struct drm_device * dev,struct drm_modeset_acquire_ctx * ctx)414aee94f86SFrançois Tigeot int drm_modeset_lock_all_ctx(struct drm_device *dev,
415ba55f2f5SFrançois Tigeot 			     struct drm_modeset_acquire_ctx *ctx)
416ba55f2f5SFrançois Tigeot {
417ba55f2f5SFrançois Tigeot 	struct drm_crtc *crtc;
4182c9916cdSFrançois Tigeot 	struct drm_plane *plane;
419aee94f86SFrançois Tigeot 	int ret;
420aee94f86SFrançois Tigeot 
421aee94f86SFrançois Tigeot 	ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx);
422aee94f86SFrançois Tigeot 	if (ret)
423aee94f86SFrançois Tigeot 		return ret;
424ba55f2f5SFrançois Tigeot 
425a05eeebfSFrançois Tigeot 	drm_for_each_crtc(crtc, dev) {
426ba55f2f5SFrançois Tigeot 		ret = drm_modeset_lock(&crtc->mutex, ctx);
427ba55f2f5SFrançois Tigeot 		if (ret)
428ba55f2f5SFrançois Tigeot 			return ret;
429ba55f2f5SFrançois Tigeot 	}
430ba55f2f5SFrançois Tigeot 
431a05eeebfSFrançois Tigeot 	drm_for_each_plane(plane, dev) {
4322c9916cdSFrançois Tigeot 		ret = drm_modeset_lock(&plane->mutex, ctx);
4332c9916cdSFrançois Tigeot 		if (ret)
4342c9916cdSFrançois Tigeot 			return ret;
4352c9916cdSFrançois Tigeot 	}
4362c9916cdSFrançois Tigeot 
437ba55f2f5SFrançois Tigeot 	return 0;
438ba55f2f5SFrançois Tigeot }
439aee94f86SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock_all_ctx);
440