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#ifndef DRM_MODESET_LOCK_H_ 2562306a36Sopenharmony_ci#define DRM_MODESET_LOCK_H_ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/types.h> /* stackdepot.h is not self-contained */ 2862306a36Sopenharmony_ci#include <linux/stackdepot.h> 2962306a36Sopenharmony_ci#include <linux/ww_mutex.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct drm_modeset_lock; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/** 3462306a36Sopenharmony_ci * struct drm_modeset_acquire_ctx - locking context (see ww_acquire_ctx) 3562306a36Sopenharmony_ci * @ww_ctx: base acquire ctx 3662306a36Sopenharmony_ci * @contended: used internally for -EDEADLK handling 3762306a36Sopenharmony_ci * @stack_depot: used internally for contention debugging 3862306a36Sopenharmony_ci * @locked: list of held locks 3962306a36Sopenharmony_ci * @trylock_only: trylock mode used in atomic contexts/panic notifiers 4062306a36Sopenharmony_ci * @interruptible: whether interruptible locking should be used. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Each thread competing for a set of locks must use one acquire 4362306a36Sopenharmony_ci * ctx. And if any lock fxn returns -EDEADLK, it must backoff and 4462306a36Sopenharmony_ci * retry. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistruct drm_modeset_acquire_ctx { 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci struct ww_acquire_ctx ww_ctx; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* 5162306a36Sopenharmony_ci * Contended lock: if a lock is contended you should only call 5262306a36Sopenharmony_ci * drm_modeset_backoff() which drops locks and slow-locks the 5362306a36Sopenharmony_ci * contended lock. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci struct drm_modeset_lock *contended; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * Stack depot for debugging when a contended lock was not backed off 5962306a36Sopenharmony_ci * from. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci depot_stack_handle_t stack_depot; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* 6462306a36Sopenharmony_ci * list of held locks (drm_modeset_lock) 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci struct list_head locked; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Trylock mode, use only for panic handlers! 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci bool trylock_only; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Perform interruptible waits on this context. */ 7462306a36Sopenharmony_ci bool interruptible; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/** 7862306a36Sopenharmony_ci * struct drm_modeset_lock - used for locking modeset resources. 7962306a36Sopenharmony_ci * @mutex: resource locking 8062306a36Sopenharmony_ci * @head: used to hold its place on &drm_atomi_state.locked list when 8162306a36Sopenharmony_ci * part of an atomic update 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * Used for locking CRTCs and other modeset resources. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistruct drm_modeset_lock { 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * modeset lock 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci struct ww_mutex mutex; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * Resources that are locked as part of an atomic update are added 9362306a36Sopenharmony_ci * to a list (so we know what to unlock at the end). 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci struct list_head head; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define DRM_MODESET_ACQUIRE_INTERRUPTIBLE BIT(0) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_civoid drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, 10162306a36Sopenharmony_ci uint32_t flags); 10262306a36Sopenharmony_civoid drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx); 10362306a36Sopenharmony_civoid drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx); 10462306a36Sopenharmony_ciint drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid drm_modeset_lock_init(struct drm_modeset_lock *lock); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/** 10962306a36Sopenharmony_ci * drm_modeset_lock_fini - cleanup lock 11062306a36Sopenharmony_ci * @lock: lock to cleanup 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_cistatic inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci WARN_ON(!list_empty(&lock->head)); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/** 11862306a36Sopenharmony_ci * drm_modeset_is_locked - equivalent to mutex_is_locked() 11962306a36Sopenharmony_ci * @lock: lock to check 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistatic inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci return ww_mutex_is_locked(&lock->mutex); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/** 12762306a36Sopenharmony_ci * drm_modeset_lock_assert_held - equivalent to lockdep_assert_held() 12862306a36Sopenharmony_ci * @lock: lock to check 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic inline void drm_modeset_lock_assert_held(struct drm_modeset_lock *lock) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci lockdep_assert_held(&lock->mutex.base); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciint drm_modeset_lock(struct drm_modeset_lock *lock, 13662306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx); 13762306a36Sopenharmony_ciint __must_check drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock); 13862306a36Sopenharmony_civoid drm_modeset_unlock(struct drm_modeset_lock *lock); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct drm_device; 14162306a36Sopenharmony_cistruct drm_crtc; 14262306a36Sopenharmony_cistruct drm_plane; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid drm_modeset_lock_all(struct drm_device *dev); 14562306a36Sopenharmony_civoid drm_modeset_unlock_all(struct drm_device *dev); 14662306a36Sopenharmony_civoid drm_warn_on_modeset_not_all_locked(struct drm_device *dev); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint drm_modeset_lock_all_ctx(struct drm_device *dev, 14962306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/** 15262306a36Sopenharmony_ci * DRM_MODESET_LOCK_ALL_BEGIN - Helper to acquire modeset locks 15362306a36Sopenharmony_ci * @dev: drm device 15462306a36Sopenharmony_ci * @ctx: local modeset acquire context, will be dereferenced 15562306a36Sopenharmony_ci * @flags: DRM_MODESET_ACQUIRE_* flags to pass to drm_modeset_acquire_init() 15662306a36Sopenharmony_ci * @ret: local ret/err/etc variable to track error status 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Use these macros to simplify grabbing all modeset locks using a local 15962306a36Sopenharmony_ci * context. This has the advantage of reducing boilerplate, but also properly 16062306a36Sopenharmony_ci * checking return values where appropriate. 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Any code run between BEGIN and END will be holding the modeset locks. 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * This must be paired with DRM_MODESET_LOCK_ALL_END(). We will jump back and 16562306a36Sopenharmony_ci * forth between the labels on deadlock and error conditions. 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * Drivers can acquire additional modeset locks. If any lock acquisition 16862306a36Sopenharmony_ci * fails, the control flow needs to jump to DRM_MODESET_LOCK_ALL_END() with 16962306a36Sopenharmony_ci * the @ret parameter containing the return value of drm_modeset_lock(). 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * Returns: 17262306a36Sopenharmony_ci * The only possible value of ret immediately after DRM_MODESET_LOCK_ALL_BEGIN() 17362306a36Sopenharmony_ci * is 0, so no error checking is necessary 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci#define DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, flags, ret) \ 17662306a36Sopenharmony_ci if (!drm_drv_uses_atomic_modeset(dev)) \ 17762306a36Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); \ 17862306a36Sopenharmony_ci drm_modeset_acquire_init(&ctx, flags); \ 17962306a36Sopenharmony_cimodeset_lock_retry: \ 18062306a36Sopenharmony_ci ret = drm_modeset_lock_all_ctx(dev, &ctx); \ 18162306a36Sopenharmony_ci if (ret) \ 18262306a36Sopenharmony_ci goto modeset_lock_fail; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/** 18562306a36Sopenharmony_ci * DRM_MODESET_LOCK_ALL_END - Helper to release and cleanup modeset locks 18662306a36Sopenharmony_ci * @dev: drm device 18762306a36Sopenharmony_ci * @ctx: local modeset acquire context, will be dereferenced 18862306a36Sopenharmony_ci * @ret: local ret/err/etc variable to track error status 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * The other side of DRM_MODESET_LOCK_ALL_BEGIN(). It will bounce back to BEGIN 19162306a36Sopenharmony_ci * if ret is -EDEADLK. 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * It's important that you use the same ret variable for begin and end so 19462306a36Sopenharmony_ci * deadlock conditions are properly handled. 19562306a36Sopenharmony_ci * 19662306a36Sopenharmony_ci * Returns: 19762306a36Sopenharmony_ci * ret will be untouched unless it is -EDEADLK on entry. That means that if you 19862306a36Sopenharmony_ci * successfully acquire the locks, ret will be whatever your code sets it to. If 19962306a36Sopenharmony_ci * there is a deadlock or other failure with acquire or backoff, ret will be set 20062306a36Sopenharmony_ci * to that failure. In both of these cases the code between BEGIN/END will not 20162306a36Sopenharmony_ci * be run, so the failure will reflect the inability to grab the locks. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci#define DRM_MODESET_LOCK_ALL_END(dev, ctx, ret) \ 20462306a36Sopenharmony_cimodeset_lock_fail: \ 20562306a36Sopenharmony_ci if (ret == -EDEADLK) { \ 20662306a36Sopenharmony_ci ret = drm_modeset_backoff(&ctx); \ 20762306a36Sopenharmony_ci if (!ret) \ 20862306a36Sopenharmony_ci goto modeset_lock_retry; \ 20962306a36Sopenharmony_ci } \ 21062306a36Sopenharmony_ci drm_modeset_drop_locks(&ctx); \ 21162306a36Sopenharmony_ci drm_modeset_acquire_fini(&ctx); \ 21262306a36Sopenharmony_ci if (!drm_drv_uses_atomic_modeset(dev)) \ 21362306a36Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci#endif /* DRM_MODESET_LOCK_H_ */ 216