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