151fd371bSRob Clark /*
251fd371bSRob Clark  * Copyright (C) 2014 Red Hat
351fd371bSRob Clark  * Author: Rob Clark <robdclark@gmail.com>
451fd371bSRob Clark  *
551fd371bSRob Clark  * Permission is hereby granted, free of charge, to any person obtaining a
651fd371bSRob Clark  * copy of this software and associated documentation files (the "Software"),
751fd371bSRob Clark  * to deal in the Software without restriction, including without limitation
851fd371bSRob Clark  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
951fd371bSRob Clark  * and/or sell copies of the Software, and to permit persons to whom the
1051fd371bSRob Clark  * Software is furnished to do so, subject to the following conditions:
1151fd371bSRob Clark  *
1251fd371bSRob Clark  * The above copyright notice and this permission notice shall be included in
1351fd371bSRob Clark  * all copies or substantial portions of the Software.
1451fd371bSRob Clark  *
1551fd371bSRob Clark  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1651fd371bSRob Clark  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1751fd371bSRob Clark  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1851fd371bSRob Clark  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1951fd371bSRob Clark  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2051fd371bSRob Clark  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2151fd371bSRob Clark  * OTHER DEALINGS IN THE SOFTWARE.
2251fd371bSRob Clark  */
2351fd371bSRob Clark 
2451fd371bSRob Clark #include <drm/drmP.h>
2551fd371bSRob Clark #include <drm/drm_crtc.h>
2651fd371bSRob Clark #include <drm/drm_modeset_lock.h>
2751fd371bSRob Clark 
2851fd371bSRob Clark /**
2951fd371bSRob Clark  * DOC: kms locking
3051fd371bSRob Clark  *
3151fd371bSRob Clark  * As KMS moves toward more fine grained locking, and atomic ioctl where
3251fd371bSRob Clark  * userspace can indirectly control locking order, it becomes necessary
3351fd371bSRob Clark  * to use ww_mutex and acquire-contexts to avoid deadlocks.  But because
3451fd371bSRob Clark  * the locking is more distributed around the driver code, we want a bit
3551fd371bSRob Clark  * of extra utility/tracking out of our acquire-ctx.  This is provided
3651fd371bSRob Clark  * by drm_modeset_lock / drm_modeset_acquire_ctx.
3751fd371bSRob Clark  *
3851fd371bSRob Clark  * For basic principles of ww_mutex, see: Documentation/ww-mutex-design.txt
3951fd371bSRob Clark  *
4051fd371bSRob Clark  * The basic usage pattern is to:
4151fd371bSRob Clark  *
4251fd371bSRob Clark  *     drm_modeset_acquire_init(&ctx)
4351fd371bSRob Clark  *   retry:
4451fd371bSRob Clark  *     foreach (lock in random_ordered_set_of_locks) {
4551fd371bSRob Clark  *       ret = drm_modeset_lock(lock, &ctx)
4651fd371bSRob Clark  *       if (ret == -EDEADLK) {
4751fd371bSRob Clark  *          drm_modeset_backoff(&ctx);
4851fd371bSRob Clark  *          goto retry;
4951fd371bSRob Clark  *       }
5051fd371bSRob Clark  *     }
5151fd371bSRob Clark  *
5251fd371bSRob Clark  *     ... do stuff ...
5351fd371bSRob Clark  *
5451fd371bSRob Clark  *     drm_modeset_drop_locks(&ctx);
5551fd371bSRob Clark  *     drm_modeset_acquire_fini(&ctx);
5651fd371bSRob Clark  */
5751fd371bSRob Clark 
5851fd371bSRob Clark 
5951fd371bSRob Clark /**
60cb597bb3SDaniel Vetter  * __drm_modeset_lock_all - internal helper to grab all modeset locks
61cb597bb3SDaniel Vetter  * @dev: DRM device
62cb597bb3SDaniel Vetter  * @trylock: trylock mode for atomic contexts
63a6a8bb84SDaniel Vetter  *
64cb597bb3SDaniel Vetter  * This is a special version of drm_modeset_lock_all() which can also be used in
65cb597bb3SDaniel Vetter  * atomic contexts. Then @trylock must be set to true.
66cb597bb3SDaniel Vetter  *
67cb597bb3SDaniel Vetter  * Returns:
68cb597bb3SDaniel Vetter  * 0 on success or negative error code on failure.
69a6a8bb84SDaniel Vetter  */
70cb597bb3SDaniel Vetter int __drm_modeset_lock_all(struct drm_device *dev,
71cb597bb3SDaniel Vetter 			   bool trylock)
72a6a8bb84SDaniel Vetter {
73a6a8bb84SDaniel Vetter 	struct drm_mode_config *config = &dev->mode_config;
74a6a8bb84SDaniel Vetter 	struct drm_modeset_acquire_ctx *ctx;
75a6a8bb84SDaniel Vetter 	int ret;
76a6a8bb84SDaniel Vetter 
77cb597bb3SDaniel Vetter 	ctx = kzalloc(sizeof(*ctx),
78cb597bb3SDaniel Vetter 		      trylock ? GFP_ATOMIC : GFP_KERNEL);
79cb597bb3SDaniel Vetter 	if (!ctx)
80cb597bb3SDaniel Vetter 		return -ENOMEM;
81a6a8bb84SDaniel Vetter 
82cb597bb3SDaniel Vetter 	if (trylock) {
83cb597bb3SDaniel Vetter 		if (!mutex_trylock(&config->mutex))
84cb597bb3SDaniel Vetter 			return -EBUSY;
85cb597bb3SDaniel Vetter 	} else {
86a6a8bb84SDaniel Vetter 		mutex_lock(&config->mutex);
87cb597bb3SDaniel Vetter 	}
88a6a8bb84SDaniel Vetter 
89a6a8bb84SDaniel Vetter 	drm_modeset_acquire_init(ctx, 0);
90cb597bb3SDaniel Vetter 	ctx->trylock_only = trylock;
91a6a8bb84SDaniel Vetter 
92a6a8bb84SDaniel Vetter retry:
93a6a8bb84SDaniel Vetter 	ret = drm_modeset_lock(&config->connection_mutex, ctx);
94a6a8bb84SDaniel Vetter 	if (ret)
95a6a8bb84SDaniel Vetter 		goto fail;
96a6a8bb84SDaniel Vetter 	ret = drm_modeset_lock_all_crtcs(dev, ctx);
97a6a8bb84SDaniel Vetter 	if (ret)
98a6a8bb84SDaniel Vetter 		goto fail;
99a6a8bb84SDaniel Vetter 
100a6a8bb84SDaniel Vetter 	WARN_ON(config->acquire_ctx);
101a6a8bb84SDaniel Vetter 
102a6a8bb84SDaniel Vetter 	/* now we hold the locks, so now that it is safe, stash the
103a6a8bb84SDaniel Vetter 	 * ctx for drm_modeset_unlock_all():
104a6a8bb84SDaniel Vetter 	 */
105a6a8bb84SDaniel Vetter 	config->acquire_ctx = ctx;
106a6a8bb84SDaniel Vetter 
107a6a8bb84SDaniel Vetter 	drm_warn_on_modeset_not_all_locked(dev);
108a6a8bb84SDaniel Vetter 
109cb597bb3SDaniel Vetter 	return 0;
110a6a8bb84SDaniel Vetter 
111a6a8bb84SDaniel Vetter fail:
112a6a8bb84SDaniel Vetter 	if (ret == -EDEADLK) {
113a6a8bb84SDaniel Vetter 		drm_modeset_backoff(ctx);
114a6a8bb84SDaniel Vetter 		goto retry;
115a6a8bb84SDaniel Vetter 	}
116cb597bb3SDaniel Vetter 
117cb597bb3SDaniel Vetter 	return ret;
118cb597bb3SDaniel Vetter }
119cb597bb3SDaniel Vetter EXPORT_SYMBOL(__drm_modeset_lock_all);
120cb597bb3SDaniel Vetter 
121cb597bb3SDaniel Vetter /**
122cb597bb3SDaniel Vetter  * drm_modeset_lock_all - take all modeset locks
123cb597bb3SDaniel Vetter  * @dev: drm device
124cb597bb3SDaniel Vetter  *
125cb597bb3SDaniel Vetter  * This function takes all modeset locks, suitable where a more fine-grained
126cb597bb3SDaniel Vetter  * scheme isn't (yet) implemented. Locks must be dropped with
127cb597bb3SDaniel Vetter  * drm_modeset_unlock_all.
128cb597bb3SDaniel Vetter  */
129cb597bb3SDaniel Vetter void drm_modeset_lock_all(struct drm_device *dev)
130cb597bb3SDaniel Vetter {
131cb597bb3SDaniel Vetter 	WARN_ON(__drm_modeset_lock_all(dev, false) != 0);
132a6a8bb84SDaniel Vetter }
133a6a8bb84SDaniel Vetter EXPORT_SYMBOL(drm_modeset_lock_all);
134a6a8bb84SDaniel Vetter 
135a6a8bb84SDaniel Vetter /**
136a6a8bb84SDaniel Vetter  * drm_modeset_unlock_all - drop all modeset locks
137a6a8bb84SDaniel Vetter  * @dev: device
138a6a8bb84SDaniel Vetter  *
139a6a8bb84SDaniel Vetter  * This function drop all modeset locks taken by drm_modeset_lock_all.
140a6a8bb84SDaniel Vetter  */
141a6a8bb84SDaniel Vetter void drm_modeset_unlock_all(struct drm_device *dev)
142a6a8bb84SDaniel Vetter {
143a6a8bb84SDaniel Vetter 	struct drm_mode_config *config = &dev->mode_config;
144a6a8bb84SDaniel Vetter 	struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
145a6a8bb84SDaniel Vetter 
146a6a8bb84SDaniel Vetter 	if (WARN_ON(!ctx))
147a6a8bb84SDaniel Vetter 		return;
148a6a8bb84SDaniel Vetter 
149a6a8bb84SDaniel Vetter 	config->acquire_ctx = NULL;
150a6a8bb84SDaniel Vetter 	drm_modeset_drop_locks(ctx);
151a6a8bb84SDaniel Vetter 	drm_modeset_acquire_fini(ctx);
152a6a8bb84SDaniel Vetter 
153a6a8bb84SDaniel Vetter 	kfree(ctx);
154a6a8bb84SDaniel Vetter 
155a6a8bb84SDaniel Vetter 	mutex_unlock(&dev->mode_config.mutex);
156a6a8bb84SDaniel Vetter }
157a6a8bb84SDaniel Vetter EXPORT_SYMBOL(drm_modeset_unlock_all);
158a6a8bb84SDaniel Vetter 
159a6a8bb84SDaniel Vetter /**
160d059f652SDaniel Vetter  * drm_modeset_lock_crtc - lock crtc with hidden acquire ctx
161d059f652SDaniel Vetter  * @crtc: drm crtc
162d059f652SDaniel Vetter  *
163d059f652SDaniel Vetter  * This function locks the given crtc using a hidden acquire context. This is
164d059f652SDaniel Vetter  * necessary so that drivers internally using the atomic interfaces can grab
165d059f652SDaniel Vetter  * further locks with the lock acquire context.
166d059f652SDaniel Vetter  */
167d059f652SDaniel Vetter void drm_modeset_lock_crtc(struct drm_crtc *crtc)
168d059f652SDaniel Vetter {
169d059f652SDaniel Vetter 	struct drm_modeset_acquire_ctx *ctx;
170d059f652SDaniel Vetter 	int ret;
171d059f652SDaniel Vetter 
172d059f652SDaniel Vetter 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
173d059f652SDaniel Vetter 	if (WARN_ON(!ctx))
174d059f652SDaniel Vetter 		return;
175d059f652SDaniel Vetter 
176d059f652SDaniel Vetter 	drm_modeset_acquire_init(ctx, 0);
177d059f652SDaniel Vetter 
178d059f652SDaniel Vetter retry:
179d059f652SDaniel Vetter 	ret = drm_modeset_lock(&crtc->mutex, ctx);
180d059f652SDaniel Vetter 	if (ret)
181d059f652SDaniel Vetter 		goto fail;
182d059f652SDaniel Vetter 
183d059f652SDaniel Vetter 	WARN_ON(crtc->acquire_ctx);
184d059f652SDaniel Vetter 
185d059f652SDaniel Vetter 	/* now we hold the locks, so now that it is safe, stash the
186d059f652SDaniel Vetter 	 * ctx for drm_modeset_unlock_crtc():
187d059f652SDaniel Vetter 	 */
188d059f652SDaniel Vetter 	crtc->acquire_ctx = ctx;
189d059f652SDaniel Vetter 
190d059f652SDaniel Vetter 	return;
191d059f652SDaniel Vetter 
192d059f652SDaniel Vetter fail:
193d059f652SDaniel Vetter 	if (ret == -EDEADLK) {
194d059f652SDaniel Vetter 		drm_modeset_backoff(ctx);
195d059f652SDaniel Vetter 		goto retry;
196d059f652SDaniel Vetter 	}
197d059f652SDaniel Vetter }
198d059f652SDaniel Vetter EXPORT_SYMBOL(drm_modeset_lock_crtc);
199d059f652SDaniel Vetter 
200d059f652SDaniel Vetter /**
201d059f652SDaniel Vetter  * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls
202295ee853SDaniel Vetter  * @crtc: drm crtc
203d059f652SDaniel Vetter  *
204d059f652SDaniel Vetter  * Legacy ioctl operations like cursor updates or page flips only have per-crtc
205d059f652SDaniel Vetter  * locking, and store the acquire ctx in the corresponding crtc. All other
206d059f652SDaniel Vetter  * legacy operations take all locks and use a global acquire context. This
207d059f652SDaniel Vetter  * function grabs the right one.
208d059f652SDaniel Vetter  */
209d059f652SDaniel Vetter struct drm_modeset_acquire_ctx *
210d059f652SDaniel Vetter drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc)
211d059f652SDaniel Vetter {
212d059f652SDaniel Vetter 	if (crtc->acquire_ctx)
213d059f652SDaniel Vetter 		return crtc->acquire_ctx;
214d059f652SDaniel Vetter 
215d059f652SDaniel Vetter 	WARN_ON(!crtc->dev->mode_config.acquire_ctx);
216d059f652SDaniel Vetter 
217d059f652SDaniel Vetter 	return crtc->dev->mode_config.acquire_ctx;
218d059f652SDaniel Vetter }
219d059f652SDaniel Vetter EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx);
220d059f652SDaniel Vetter 
221d059f652SDaniel Vetter /**
222d059f652SDaniel Vetter  * drm_modeset_unlock_crtc - drop crtc lock
223d059f652SDaniel Vetter  * @crtc: drm crtc
224d059f652SDaniel Vetter  *
225d059f652SDaniel Vetter  * This drops the crtc lock acquire with drm_modeset_lock_crtc() and all other
226d059f652SDaniel Vetter  * locks acquired through the hidden context.
227d059f652SDaniel Vetter  */
228d059f652SDaniel Vetter void drm_modeset_unlock_crtc(struct drm_crtc *crtc)
229d059f652SDaniel Vetter {
230d059f652SDaniel Vetter 	struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx;
231d059f652SDaniel Vetter 
232d059f652SDaniel Vetter 	if (WARN_ON(!ctx))
233d059f652SDaniel Vetter 		return;
234d059f652SDaniel Vetter 
235d059f652SDaniel Vetter 	crtc->acquire_ctx = NULL;
236d059f652SDaniel Vetter 	drm_modeset_drop_locks(ctx);
237d059f652SDaniel Vetter 	drm_modeset_acquire_fini(ctx);
238d059f652SDaniel Vetter 
239d059f652SDaniel Vetter 	kfree(ctx);
240d059f652SDaniel Vetter }
241d059f652SDaniel Vetter EXPORT_SYMBOL(drm_modeset_unlock_crtc);
242d059f652SDaniel Vetter 
243d059f652SDaniel Vetter /**
244a6a8bb84SDaniel Vetter  * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
245a6a8bb84SDaniel Vetter  * @dev: device
246a6a8bb84SDaniel Vetter  *
247a6a8bb84SDaniel Vetter  * Useful as a debug assert.
248a6a8bb84SDaniel Vetter  */
249a6a8bb84SDaniel Vetter void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
250a6a8bb84SDaniel Vetter {
251a6a8bb84SDaniel Vetter 	struct drm_crtc *crtc;
252a6a8bb84SDaniel Vetter 
253a6a8bb84SDaniel Vetter 	/* Locking is currently fubar in the panic handler. */
254a6a8bb84SDaniel Vetter 	if (oops_in_progress)
255a6a8bb84SDaniel Vetter 		return;
256a6a8bb84SDaniel Vetter 
257a6a8bb84SDaniel Vetter 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
258a6a8bb84SDaniel Vetter 		WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
259a6a8bb84SDaniel Vetter 
260a6a8bb84SDaniel Vetter 	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
261a6a8bb84SDaniel Vetter 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
262a6a8bb84SDaniel Vetter }
263a6a8bb84SDaniel Vetter EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
264a6a8bb84SDaniel Vetter 
265a6a8bb84SDaniel Vetter /**
26651fd371bSRob Clark  * drm_modeset_acquire_init - initialize acquire context
26751fd371bSRob Clark  * @ctx: the acquire context
26851fd371bSRob Clark  * @flags: for future
26951fd371bSRob Clark  */
27051fd371bSRob Clark void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
27151fd371bSRob Clark 		uint32_t flags)
27251fd371bSRob Clark {
273fb54918aSRob Clark 	memset(ctx, 0, sizeof(*ctx));
27451fd371bSRob Clark 	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
27551fd371bSRob Clark 	INIT_LIST_HEAD(&ctx->locked);
27651fd371bSRob Clark }
27751fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_acquire_init);
27851fd371bSRob Clark 
27951fd371bSRob Clark /**
28051fd371bSRob Clark  * drm_modeset_acquire_fini - cleanup acquire context
28151fd371bSRob Clark  * @ctx: the acquire context
28251fd371bSRob Clark  */
28351fd371bSRob Clark void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
28451fd371bSRob Clark {
28551fd371bSRob Clark 	ww_acquire_fini(&ctx->ww_ctx);
28651fd371bSRob Clark }
28751fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_acquire_fini);
28851fd371bSRob Clark 
28951fd371bSRob Clark /**
29051fd371bSRob Clark  * drm_modeset_drop_locks - drop all locks
29151fd371bSRob Clark  * @ctx: the acquire context
29251fd371bSRob Clark  *
29351fd371bSRob Clark  * Drop all locks currently held against this acquire context.
29451fd371bSRob Clark  */
29551fd371bSRob Clark void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
29651fd371bSRob Clark {
29751fd371bSRob Clark 	WARN_ON(ctx->contended);
29851fd371bSRob Clark 	while (!list_empty(&ctx->locked)) {
29951fd371bSRob Clark 		struct drm_modeset_lock *lock;
30051fd371bSRob Clark 
30151fd371bSRob Clark 		lock = list_first_entry(&ctx->locked,
30251fd371bSRob Clark 				struct drm_modeset_lock, head);
30351fd371bSRob Clark 
30451fd371bSRob Clark 		drm_modeset_unlock(lock);
30551fd371bSRob Clark 	}
30651fd371bSRob Clark }
30751fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_drop_locks);
30851fd371bSRob Clark 
30951fd371bSRob Clark static inline int modeset_lock(struct drm_modeset_lock *lock,
31051fd371bSRob Clark 		struct drm_modeset_acquire_ctx *ctx,
31151fd371bSRob Clark 		bool interruptible, bool slow)
31251fd371bSRob Clark {
31351fd371bSRob Clark 	int ret;
31451fd371bSRob Clark 
31551fd371bSRob Clark 	WARN_ON(ctx->contended);
31651fd371bSRob Clark 
317cb597bb3SDaniel Vetter 	if (ctx->trylock_only) {
318cb597bb3SDaniel Vetter 		if (!ww_mutex_trylock(&lock->mutex))
319cb597bb3SDaniel Vetter 			return -EBUSY;
320cb597bb3SDaniel Vetter 		else
321cb597bb3SDaniel Vetter 			return 0;
322cb597bb3SDaniel Vetter 	} else if (interruptible && slow) {
32351fd371bSRob Clark 		ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
32451fd371bSRob Clark 	} else if (interruptible) {
32551fd371bSRob Clark 		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
32651fd371bSRob Clark 	} else if (slow) {
32751fd371bSRob Clark 		ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
32851fd371bSRob Clark 		ret = 0;
32951fd371bSRob Clark 	} else {
33051fd371bSRob Clark 		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
33151fd371bSRob Clark 	}
33251fd371bSRob Clark 	if (!ret) {
33351fd371bSRob Clark 		WARN_ON(!list_empty(&lock->head));
33451fd371bSRob Clark 		list_add(&lock->head, &ctx->locked);
33551fd371bSRob Clark 	} else if (ret == -EALREADY) {
33651fd371bSRob Clark 		/* we already hold the lock.. this is fine.  For atomic
33751fd371bSRob Clark 		 * we will need to be able to drm_modeset_lock() things
33851fd371bSRob Clark 		 * without having to keep track of what is already locked
33951fd371bSRob Clark 		 * or not.
34051fd371bSRob Clark 		 */
34151fd371bSRob Clark 		ret = 0;
34251fd371bSRob Clark 	} else if (ret == -EDEADLK) {
34351fd371bSRob Clark 		ctx->contended = lock;
34451fd371bSRob Clark 	}
34551fd371bSRob Clark 
34651fd371bSRob Clark 	return ret;
34751fd371bSRob Clark }
34851fd371bSRob Clark 
34951fd371bSRob Clark static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
35051fd371bSRob Clark 		bool interruptible)
35151fd371bSRob Clark {
35251fd371bSRob Clark 	struct drm_modeset_lock *contended = ctx->contended;
35351fd371bSRob Clark 
35451fd371bSRob Clark 	ctx->contended = NULL;
35551fd371bSRob Clark 
35651fd371bSRob Clark 	if (WARN_ON(!contended))
35751fd371bSRob Clark 		return 0;
35851fd371bSRob Clark 
35951fd371bSRob Clark 	drm_modeset_drop_locks(ctx);
36051fd371bSRob Clark 
36151fd371bSRob Clark 	return modeset_lock(contended, ctx, interruptible, true);
36251fd371bSRob Clark }
36351fd371bSRob Clark 
36451fd371bSRob Clark /**
36551fd371bSRob Clark  * drm_modeset_backoff - deadlock avoidance backoff
36651fd371bSRob Clark  * @ctx: the acquire context
36751fd371bSRob Clark  *
36851fd371bSRob Clark  * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
36951fd371bSRob Clark  * you must call this function to drop all currently held locks and
37051fd371bSRob Clark  * block until the contended lock becomes available.
37151fd371bSRob Clark  */
37251fd371bSRob Clark void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
37351fd371bSRob Clark {
37451fd371bSRob Clark 	modeset_backoff(ctx, false);
37551fd371bSRob Clark }
37651fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_backoff);
37751fd371bSRob Clark 
37851fd371bSRob Clark /**
37951fd371bSRob Clark  * drm_modeset_backoff_interruptible - deadlock avoidance backoff
38051fd371bSRob Clark  * @ctx: the acquire context
38151fd371bSRob Clark  *
38251fd371bSRob Clark  * Interruptible version of drm_modeset_backoff()
38351fd371bSRob Clark  */
38451fd371bSRob Clark int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
38551fd371bSRob Clark {
38651fd371bSRob Clark 	return modeset_backoff(ctx, true);
38751fd371bSRob Clark }
38851fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
38951fd371bSRob Clark 
39051fd371bSRob Clark /**
39151fd371bSRob Clark  * drm_modeset_lock - take modeset lock
39251fd371bSRob Clark  * @lock: lock to take
39351fd371bSRob Clark  * @ctx: acquire ctx
39451fd371bSRob Clark  *
39551fd371bSRob Clark  * If ctx is not NULL, then its ww acquire context is used and the
39651fd371bSRob Clark  * lock will be tracked by the context and can be released by calling
39751fd371bSRob Clark  * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
39851fd371bSRob Clark  * deadlock scenario has been detected and it is an error to attempt
39951fd371bSRob Clark  * to take any more locks without first calling drm_modeset_backoff().
40051fd371bSRob Clark  */
40151fd371bSRob Clark int drm_modeset_lock(struct drm_modeset_lock *lock,
40251fd371bSRob Clark 		struct drm_modeset_acquire_ctx *ctx)
40351fd371bSRob Clark {
40451fd371bSRob Clark 	if (ctx)
40551fd371bSRob Clark 		return modeset_lock(lock, ctx, false, false);
40651fd371bSRob Clark 
40751fd371bSRob Clark 	ww_mutex_lock(&lock->mutex, NULL);
40851fd371bSRob Clark 	return 0;
40951fd371bSRob Clark }
41051fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_lock);
41151fd371bSRob Clark 
41251fd371bSRob Clark /**
41351fd371bSRob Clark  * drm_modeset_lock_interruptible - take modeset lock
41451fd371bSRob Clark  * @lock: lock to take
41551fd371bSRob Clark  * @ctx: acquire ctx
41651fd371bSRob Clark  *
41751fd371bSRob Clark  * Interruptible version of drm_modeset_lock()
41851fd371bSRob Clark  */
41951fd371bSRob Clark int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
42051fd371bSRob Clark 		struct drm_modeset_acquire_ctx *ctx)
42151fd371bSRob Clark {
42251fd371bSRob Clark 	if (ctx)
42351fd371bSRob Clark 		return modeset_lock(lock, ctx, true, false);
42451fd371bSRob Clark 
42551fd371bSRob Clark 	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
42651fd371bSRob Clark }
42751fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_lock_interruptible);
42851fd371bSRob Clark 
42951fd371bSRob Clark /**
43051fd371bSRob Clark  * drm_modeset_unlock - drop modeset lock
43151fd371bSRob Clark  * @lock: lock to release
43251fd371bSRob Clark  */
43351fd371bSRob Clark void drm_modeset_unlock(struct drm_modeset_lock *lock)
43451fd371bSRob Clark {
43551fd371bSRob Clark 	list_del_init(&lock->head);
43651fd371bSRob Clark 	ww_mutex_unlock(&lock->mutex);
43751fd371bSRob Clark }
43851fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_unlock);
43951fd371bSRob Clark 
44051fd371bSRob Clark /* Temporary.. until we have sufficiently fine grained locking, there
44151fd371bSRob Clark  * are a couple scenarios where it is convenient to grab all crtc locks.
44251fd371bSRob Clark  * It is planned to remove this:
44351fd371bSRob Clark  */
44451fd371bSRob Clark int drm_modeset_lock_all_crtcs(struct drm_device *dev,
44551fd371bSRob Clark 		struct drm_modeset_acquire_ctx *ctx)
44651fd371bSRob Clark {
44751fd371bSRob Clark 	struct drm_mode_config *config = &dev->mode_config;
44851fd371bSRob Clark 	struct drm_crtc *crtc;
44951fd371bSRob Clark 	int ret = 0;
45051fd371bSRob Clark 
45151fd371bSRob Clark 	list_for_each_entry(crtc, &config->crtc_list, head) {
45251fd371bSRob Clark 		ret = drm_modeset_lock(&crtc->mutex, ctx);
45351fd371bSRob Clark 		if (ret)
45451fd371bSRob Clark 			return ret;
45551fd371bSRob Clark 	}
45651fd371bSRob Clark 
45751fd371bSRob Clark 	return 0;
45851fd371bSRob Clark }
45951fd371bSRob Clark EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
460