162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (C) 2014 Red Hat 362306a36Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 662306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 762306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 862306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 962306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1062306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1362306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1962306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2062306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2162306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <drm/drm_atomic.h> 2562306a36Sopenharmony_ci#include <drm/drm_crtc.h> 2662306a36Sopenharmony_ci#include <drm/drm_device.h> 2762306a36Sopenharmony_ci#include <drm/drm_modeset_lock.h> 2862306a36Sopenharmony_ci#include <drm/drm_print.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * DOC: kms locking 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * As KMS moves toward more fine grained locking, and atomic ioctl where 3462306a36Sopenharmony_ci * userspace can indirectly control locking order, it becomes necessary 3562306a36Sopenharmony_ci * to use &ww_mutex and acquire-contexts to avoid deadlocks. But because 3662306a36Sopenharmony_ci * the locking is more distributed around the driver code, we want a bit 3762306a36Sopenharmony_ci * of extra utility/tracking out of our acquire-ctx. This is provided 3862306a36Sopenharmony_ci * by &struct drm_modeset_lock and &struct drm_modeset_acquire_ctx. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * For basic principles of &ww_mutex, see: Documentation/locking/ww-mutex-design.rst 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * The basic usage pattern is to:: 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE) 4562306a36Sopenharmony_ci * retry: 4662306a36Sopenharmony_ci * foreach (lock in random_ordered_set_of_locks) { 4762306a36Sopenharmony_ci * ret = drm_modeset_lock(lock, ctx) 4862306a36Sopenharmony_ci * if (ret == -EDEADLK) { 4962306a36Sopenharmony_ci * ret = drm_modeset_backoff(ctx); 5062306a36Sopenharmony_ci * if (!ret) 5162306a36Sopenharmony_ci * goto retry; 5262306a36Sopenharmony_ci * } 5362306a36Sopenharmony_ci * if (ret) 5462306a36Sopenharmony_ci * goto out; 5562306a36Sopenharmony_ci * } 5662306a36Sopenharmony_ci * ... do stuff ... 5762306a36Sopenharmony_ci * out: 5862306a36Sopenharmony_ci * drm_modeset_drop_locks(ctx); 5962306a36Sopenharmony_ci * drm_modeset_acquire_fini(ctx); 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * For convenience this control flow is implemented in 6262306a36Sopenharmony_ci * DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() for the case 6362306a36Sopenharmony_ci * where all modeset locks need to be taken through drm_modeset_lock_all_ctx(). 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * If all that is needed is a single modeset lock, then the &struct 6662306a36Sopenharmony_ci * drm_modeset_acquire_ctx is not needed and the locking can be simplified 6762306a36Sopenharmony_ci * by passing a NULL instead of ctx in the drm_modeset_lock() call or 6862306a36Sopenharmony_ci * calling drm_modeset_lock_single_interruptible(). To unlock afterwards 6962306a36Sopenharmony_ci * call drm_modeset_unlock(). 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * On top of these per-object locks using &ww_mutex there's also an overall 7262306a36Sopenharmony_ci * &drm_mode_config.mutex, for protecting everything else. Mostly this means 7362306a36Sopenharmony_ci * probe state of connectors, and preventing hotplug add/removal of connectors. 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * Finally there's a bunch of dedicated locks to protect drm core internal 7662306a36Sopenharmony_ci * lists and lookup data structures. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic DEFINE_WW_CLASS(crtc_ww_class); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM_DEBUG_MODESET_LOCK) 8262306a36Sopenharmony_cistatic noinline depot_stack_handle_t __drm_stack_depot_save(void) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci unsigned long entries[8]; 8562306a36Sopenharmony_ci unsigned int n; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci n = stack_trace_save(entries, ARRAY_SIZE(entries), 1); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return stack_depot_save(entries, n, GFP_NOWAIT | __GFP_NOWARN); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void __drm_stack_depot_print(depot_stack_handle_t stack_depot) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct drm_printer p = drm_debug_printer("drm_modeset_lock"); 9562306a36Sopenharmony_ci unsigned long *entries; 9662306a36Sopenharmony_ci unsigned int nr_entries; 9762306a36Sopenharmony_ci char *buf; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN); 10062306a36Sopenharmony_ci if (!buf) 10162306a36Sopenharmony_ci return; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci nr_entries = stack_depot_fetch(stack_depot, &entries); 10462306a36Sopenharmony_ci stack_trace_snprint(buf, PAGE_SIZE, entries, nr_entries, 2); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci drm_printf(&p, "attempting to lock a contended lock without backoff:\n%s", buf); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci kfree(buf); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void __drm_stack_depot_init(void) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci stack_depot_init(); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci#else /* CONFIG_DRM_DEBUG_MODESET_LOCK */ 11662306a36Sopenharmony_cistatic depot_stack_handle_t __drm_stack_depot_save(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_cistatic void __drm_stack_depot_print(depot_stack_handle_t stack_depot) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_cistatic void __drm_stack_depot_init(void) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci#endif /* CONFIG_DRM_DEBUG_MODESET_LOCK */ 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * drm_modeset_lock_all - take all modeset locks 13062306a36Sopenharmony_ci * @dev: DRM device 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * This function takes all modeset locks, suitable where a more fine-grained 13362306a36Sopenharmony_ci * scheme isn't (yet) implemented. Locks must be dropped by calling the 13462306a36Sopenharmony_ci * drm_modeset_unlock_all() function. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * This function is deprecated. It allocates a lock acquisition context and 13762306a36Sopenharmony_ci * stores it in &drm_device.mode_config. This facilitate conversion of 13862306a36Sopenharmony_ci * existing code because it removes the need to manually deal with the 13962306a36Sopenharmony_ci * acquisition context, but it is also brittle because the context is global 14062306a36Sopenharmony_ci * and care must be taken not to nest calls. New code should use the 14162306a36Sopenharmony_ci * drm_modeset_lock_all_ctx() function and pass in the context explicitly. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_civoid drm_modeset_lock_all(struct drm_device *dev) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct drm_mode_config *config = &dev->mode_config; 14662306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx; 14762306a36Sopenharmony_ci int ret; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL | __GFP_NOFAIL); 15062306a36Sopenharmony_ci if (WARN_ON(!ctx)) 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci mutex_lock(&config->mutex); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci drm_modeset_acquire_init(ctx, 0); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ciretry: 15862306a36Sopenharmony_ci ret = drm_modeset_lock_all_ctx(dev, ctx); 15962306a36Sopenharmony_ci if (ret < 0) { 16062306a36Sopenharmony_ci if (ret == -EDEADLK) { 16162306a36Sopenharmony_ci drm_modeset_backoff(ctx); 16262306a36Sopenharmony_ci goto retry; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci drm_modeset_acquire_fini(ctx); 16662306a36Sopenharmony_ci kfree(ctx); 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci ww_acquire_done(&ctx->ww_ctx); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci WARN_ON(config->acquire_ctx); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * We hold the locks now, so it is safe to stash the acquisition 17562306a36Sopenharmony_ci * context for drm_modeset_unlock_all(). 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci config->acquire_ctx = ctx; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci drm_warn_on_modeset_not_all_locked(dev); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_lock_all); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/** 18462306a36Sopenharmony_ci * drm_modeset_unlock_all - drop all modeset locks 18562306a36Sopenharmony_ci * @dev: DRM device 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * This function drops all modeset locks taken by a previous call to the 18862306a36Sopenharmony_ci * drm_modeset_lock_all() function. 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * This function is deprecated. It uses the lock acquisition context stored 19162306a36Sopenharmony_ci * in &drm_device.mode_config. This facilitates conversion of existing 19262306a36Sopenharmony_ci * code because it removes the need to manually deal with the acquisition 19362306a36Sopenharmony_ci * context, but it is also brittle because the context is global and care must 19462306a36Sopenharmony_ci * be taken not to nest calls. New code should pass the acquisition context 19562306a36Sopenharmony_ci * directly to the drm_modeset_drop_locks() function. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_civoid drm_modeset_unlock_all(struct drm_device *dev) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct drm_mode_config *config = &dev->mode_config; 20062306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (WARN_ON(!ctx)) 20362306a36Sopenharmony_ci return; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci config->acquire_ctx = NULL; 20662306a36Sopenharmony_ci drm_modeset_drop_locks(ctx); 20762306a36Sopenharmony_ci drm_modeset_acquire_fini(ctx); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci kfree(ctx); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_unlock_all); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/** 21662306a36Sopenharmony_ci * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked 21762306a36Sopenharmony_ci * @dev: device 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * Useful as a debug assert. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_civoid drm_warn_on_modeset_not_all_locked(struct drm_device *dev) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct drm_crtc *crtc; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Locking is currently fubar in the panic handler. */ 22662306a36Sopenharmony_ci if (oops_in_progress) 22762306a36Sopenharmony_ci return; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) 23062306a36Sopenharmony_ci WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 23362306a36Sopenharmony_ci WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/** 23862306a36Sopenharmony_ci * drm_modeset_acquire_init - initialize acquire context 23962306a36Sopenharmony_ci * @ctx: the acquire context 24062306a36Sopenharmony_ci * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE 24162306a36Sopenharmony_ci * 24262306a36Sopenharmony_ci * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags, 24362306a36Sopenharmony_ci * all calls to drm_modeset_lock() will perform an interruptible 24462306a36Sopenharmony_ci * wait. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_civoid drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, 24762306a36Sopenharmony_ci uint32_t flags) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 25062306a36Sopenharmony_ci ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); 25162306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->locked); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE) 25462306a36Sopenharmony_ci ctx->interruptible = true; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_acquire_init); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/** 25962306a36Sopenharmony_ci * drm_modeset_acquire_fini - cleanup acquire context 26062306a36Sopenharmony_ci * @ctx: the acquire context 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_civoid drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci ww_acquire_fini(&ctx->ww_ctx); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_acquire_fini); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/** 26962306a36Sopenharmony_ci * drm_modeset_drop_locks - drop all locks 27062306a36Sopenharmony_ci * @ctx: the acquire context 27162306a36Sopenharmony_ci * 27262306a36Sopenharmony_ci * Drop all locks currently held against this acquire context. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_civoid drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci if (WARN_ON(ctx->contended)) 27762306a36Sopenharmony_ci __drm_stack_depot_print(ctx->stack_depot); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci while (!list_empty(&ctx->locked)) { 28062306a36Sopenharmony_ci struct drm_modeset_lock *lock; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci lock = list_first_entry(&ctx->locked, 28362306a36Sopenharmony_ci struct drm_modeset_lock, head); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci drm_modeset_unlock(lock); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_drop_locks); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic inline int modeset_lock(struct drm_modeset_lock *lock, 29162306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx, 29262306a36Sopenharmony_ci bool interruptible, bool slow) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci int ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (WARN_ON(ctx->contended)) 29762306a36Sopenharmony_ci __drm_stack_depot_print(ctx->stack_depot); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (ctx->trylock_only) { 30062306a36Sopenharmony_ci lockdep_assert_held(&ctx->ww_ctx); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!ww_mutex_trylock(&lock->mutex, NULL)) 30362306a36Sopenharmony_ci return -EBUSY; 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci } else if (interruptible && slow) { 30762306a36Sopenharmony_ci ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx); 30862306a36Sopenharmony_ci } else if (interruptible) { 30962306a36Sopenharmony_ci ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); 31062306a36Sopenharmony_ci } else if (slow) { 31162306a36Sopenharmony_ci ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx); 31262306a36Sopenharmony_ci ret = 0; 31362306a36Sopenharmony_ci } else { 31462306a36Sopenharmony_ci ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci if (!ret) { 31762306a36Sopenharmony_ci WARN_ON(!list_empty(&lock->head)); 31862306a36Sopenharmony_ci list_add(&lock->head, &ctx->locked); 31962306a36Sopenharmony_ci } else if (ret == -EALREADY) { 32062306a36Sopenharmony_ci /* we already hold the lock.. this is fine. For atomic 32162306a36Sopenharmony_ci * we will need to be able to drm_modeset_lock() things 32262306a36Sopenharmony_ci * without having to keep track of what is already locked 32362306a36Sopenharmony_ci * or not. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci ret = 0; 32662306a36Sopenharmony_ci } else if (ret == -EDEADLK) { 32762306a36Sopenharmony_ci ctx->contended = lock; 32862306a36Sopenharmony_ci ctx->stack_depot = __drm_stack_depot_save(); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return ret; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/** 33562306a36Sopenharmony_ci * drm_modeset_backoff - deadlock avoidance backoff 33662306a36Sopenharmony_ci * @ctx: the acquire context 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK), 33962306a36Sopenharmony_ci * you must call this function to drop all currently held locks and 34062306a36Sopenharmony_ci * block until the contended lock becomes available. 34162306a36Sopenharmony_ci * 34262306a36Sopenharmony_ci * This function returns 0 on success, or -ERESTARTSYS if this context 34362306a36Sopenharmony_ci * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the 34462306a36Sopenharmony_ci * wait has been interrupted. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ciint drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct drm_modeset_lock *contended = ctx->contended; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci ctx->contended = NULL; 35162306a36Sopenharmony_ci ctx->stack_depot = 0; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (WARN_ON(!contended)) 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci drm_modeset_drop_locks(ctx); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return modeset_lock(contended, ctx, ctx->interruptible, true); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_backoff); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/** 36362306a36Sopenharmony_ci * drm_modeset_lock_init - initialize lock 36462306a36Sopenharmony_ci * @lock: lock to init 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_civoid drm_modeset_lock_init(struct drm_modeset_lock *lock) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci ww_mutex_init(&lock->mutex, &crtc_ww_class); 36962306a36Sopenharmony_ci INIT_LIST_HEAD(&lock->head); 37062306a36Sopenharmony_ci __drm_stack_depot_init(); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_lock_init); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/** 37562306a36Sopenharmony_ci * drm_modeset_lock - take modeset lock 37662306a36Sopenharmony_ci * @lock: lock to take 37762306a36Sopenharmony_ci * @ctx: acquire ctx 37862306a36Sopenharmony_ci * 37962306a36Sopenharmony_ci * If @ctx is not NULL, then its ww acquire context is used and the 38062306a36Sopenharmony_ci * lock will be tracked by the context and can be released by calling 38162306a36Sopenharmony_ci * drm_modeset_drop_locks(). If -EDEADLK is returned, this means a 38262306a36Sopenharmony_ci * deadlock scenario has been detected and it is an error to attempt 38362306a36Sopenharmony_ci * to take any more locks without first calling drm_modeset_backoff(). 38462306a36Sopenharmony_ci * 38562306a36Sopenharmony_ci * If the @ctx is not NULL and initialized with 38662306a36Sopenharmony_ci * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with 38762306a36Sopenharmony_ci * -ERESTARTSYS when interrupted. 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * If @ctx is NULL then the function call behaves like a normal, 39062306a36Sopenharmony_ci * uninterruptible non-nesting mutex_lock() call. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ciint drm_modeset_lock(struct drm_modeset_lock *lock, 39362306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci if (ctx) 39662306a36Sopenharmony_ci return modeset_lock(lock, ctx, ctx->interruptible, false); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ww_mutex_lock(&lock->mutex, NULL); 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_lock); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/** 40462306a36Sopenharmony_ci * drm_modeset_lock_single_interruptible - take a single modeset lock 40562306a36Sopenharmony_ci * @lock: lock to take 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * This function behaves as drm_modeset_lock() with a NULL context, 40862306a36Sopenharmony_ci * but performs interruptible waits. 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * This function returns 0 on success, or -ERESTARTSYS when interrupted. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ciint drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci return ww_mutex_lock_interruptible(&lock->mutex, NULL); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_lock_single_interruptible); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/** 41962306a36Sopenharmony_ci * drm_modeset_unlock - drop modeset lock 42062306a36Sopenharmony_ci * @lock: lock to release 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_civoid drm_modeset_unlock(struct drm_modeset_lock *lock) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci list_del_init(&lock->head); 42562306a36Sopenharmony_ci ww_mutex_unlock(&lock->mutex); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_unlock); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * drm_modeset_lock_all_ctx - take all modeset locks 43162306a36Sopenharmony_ci * @dev: DRM device 43262306a36Sopenharmony_ci * @ctx: lock acquisition context 43362306a36Sopenharmony_ci * 43462306a36Sopenharmony_ci * This function takes all modeset locks, suitable where a more fine-grained 43562306a36Sopenharmony_ci * scheme isn't (yet) implemented. 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Unlike drm_modeset_lock_all(), it doesn't take the &drm_mode_config.mutex 43862306a36Sopenharmony_ci * since that lock isn't required for modeset state changes. Callers which 43962306a36Sopenharmony_ci * need to grab that lock too need to do so outside of the acquire context 44062306a36Sopenharmony_ci * @ctx. 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * Locks acquired with this function should be released by calling the 44362306a36Sopenharmony_ci * drm_modeset_drop_locks() function on @ctx. 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * See also: DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * Returns: 0 on success or a negative error-code on failure. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ciint drm_modeset_lock_all_ctx(struct drm_device *dev, 45062306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct drm_private_obj *privobj; 45362306a36Sopenharmony_ci struct drm_crtc *crtc; 45462306a36Sopenharmony_ci struct drm_plane *plane; 45562306a36Sopenharmony_ci int ret; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); 45862306a36Sopenharmony_ci if (ret) 45962306a36Sopenharmony_ci return ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 46262306a36Sopenharmony_ci ret = drm_modeset_lock(&crtc->mutex, ctx); 46362306a36Sopenharmony_ci if (ret) 46462306a36Sopenharmony_ci return ret; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci drm_for_each_plane(plane, dev) { 46862306a36Sopenharmony_ci ret = drm_modeset_lock(&plane->mutex, ctx); 46962306a36Sopenharmony_ci if (ret) 47062306a36Sopenharmony_ci return ret; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci drm_for_each_privobj(privobj, dev) { 47462306a36Sopenharmony_ci ret = drm_modeset_lock(&privobj->lock, ctx); 47562306a36Sopenharmony_ci if (ret) 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_modeset_lock_all_ctx); 482