18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2014 Red Hat 38c2ecf20Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 68c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 78c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 88c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 98c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 108c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 138c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 198c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 208c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 218c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#ifndef DRM_MODESET_LOCK_H_ 258c2ecf20Sopenharmony_ci#define DRM_MODESET_LOCK_H_ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/ww_mutex.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct drm_modeset_lock; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * struct drm_modeset_acquire_ctx - locking context (see ww_acquire_ctx) 338c2ecf20Sopenharmony_ci * @ww_ctx: base acquire ctx 348c2ecf20Sopenharmony_ci * @contended: used internally for -EDEADLK handling 358c2ecf20Sopenharmony_ci * @locked: list of held locks 368c2ecf20Sopenharmony_ci * @trylock_only: trylock mode used in atomic contexts/panic notifiers 378c2ecf20Sopenharmony_ci * @interruptible: whether interruptible locking should be used. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * Each thread competing for a set of locks must use one acquire 408c2ecf20Sopenharmony_ci * ctx. And if any lock fxn returns -EDEADLK, it must backoff and 418c2ecf20Sopenharmony_ci * retry. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cistruct drm_modeset_acquire_ctx { 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci struct ww_acquire_ctx ww_ctx; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * Contended lock: if a lock is contended you should only call 498c2ecf20Sopenharmony_ci * drm_modeset_backoff() which drops locks and slow-locks the 508c2ecf20Sopenharmony_ci * contended lock. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci struct drm_modeset_lock *contended; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * list of held locks (drm_modeset_lock) 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci struct list_head locked; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * Trylock mode, use only for panic handlers! 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci bool trylock_only; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* Perform interruptible waits on this context. */ 658c2ecf20Sopenharmony_ci bool interruptible; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/** 698c2ecf20Sopenharmony_ci * struct drm_modeset_lock - used for locking modeset resources. 708c2ecf20Sopenharmony_ci * @mutex: resource locking 718c2ecf20Sopenharmony_ci * @head: used to hold its place on &drm_atomi_state.locked list when 728c2ecf20Sopenharmony_ci * part of an atomic update 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * Used for locking CRTCs and other modeset resources. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cistruct drm_modeset_lock { 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * modeset lock 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci struct ww_mutex mutex; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* 838c2ecf20Sopenharmony_ci * Resources that are locked as part of an atomic update are added 848c2ecf20Sopenharmony_ci * to a list (so we know what to unlock at the end). 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci struct list_head head; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define DRM_MODESET_ACQUIRE_INTERRUPTIBLE BIT(0) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_civoid drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, 928c2ecf20Sopenharmony_ci uint32_t flags); 938c2ecf20Sopenharmony_civoid drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx); 948c2ecf20Sopenharmony_civoid drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx); 958c2ecf20Sopenharmony_ciint drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_civoid drm_modeset_lock_init(struct drm_modeset_lock *lock); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/** 1008c2ecf20Sopenharmony_ci * drm_modeset_lock_fini - cleanup lock 1018c2ecf20Sopenharmony_ci * @lock: lock to cleanup 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&lock->head)); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/** 1098c2ecf20Sopenharmony_ci * drm_modeset_is_locked - equivalent to mutex_is_locked() 1108c2ecf20Sopenharmony_ci * @lock: lock to check 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_cistatic inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci return ww_mutex_is_locked(&lock->mutex); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/** 1188c2ecf20Sopenharmony_ci * drm_modeset_lock_assert_held - equivalent to lockdep_assert_held() 1198c2ecf20Sopenharmony_ci * @lock: lock to check 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic inline void drm_modeset_lock_assert_held(struct drm_modeset_lock *lock) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci lockdep_assert_held(&lock->mutex.base); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciint drm_modeset_lock(struct drm_modeset_lock *lock, 1278c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx); 1288c2ecf20Sopenharmony_ciint __must_check drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock); 1298c2ecf20Sopenharmony_civoid drm_modeset_unlock(struct drm_modeset_lock *lock); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistruct drm_device; 1328c2ecf20Sopenharmony_cistruct drm_crtc; 1338c2ecf20Sopenharmony_cistruct drm_plane; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_civoid drm_modeset_lock_all(struct drm_device *dev); 1368c2ecf20Sopenharmony_civoid drm_modeset_unlock_all(struct drm_device *dev); 1378c2ecf20Sopenharmony_civoid drm_warn_on_modeset_not_all_locked(struct drm_device *dev); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciint drm_modeset_lock_all_ctx(struct drm_device *dev, 1408c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/** 1438c2ecf20Sopenharmony_ci * DRM_MODESET_LOCK_ALL_BEGIN - Helper to acquire modeset locks 1448c2ecf20Sopenharmony_ci * @dev: drm device 1458c2ecf20Sopenharmony_ci * @ctx: local modeset acquire context, will be dereferenced 1468c2ecf20Sopenharmony_ci * @flags: DRM_MODESET_ACQUIRE_* flags to pass to drm_modeset_acquire_init() 1478c2ecf20Sopenharmony_ci * @ret: local ret/err/etc variable to track error status 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * Use these macros to simplify grabbing all modeset locks using a local 1508c2ecf20Sopenharmony_ci * context. This has the advantage of reducing boilerplate, but also properly 1518c2ecf20Sopenharmony_ci * checking return values where appropriate. 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * Any code run between BEGIN and END will be holding the modeset locks. 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * This must be paired with DRM_MODESET_LOCK_ALL_END(). We will jump back and 1568c2ecf20Sopenharmony_ci * forth between the labels on deadlock and error conditions. 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Drivers can acquire additional modeset locks. If any lock acquisition 1598c2ecf20Sopenharmony_ci * fails, the control flow needs to jump to DRM_MODESET_LOCK_ALL_END() with 1608c2ecf20Sopenharmony_ci * the @ret parameter containing the return value of drm_modeset_lock(). 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * Returns: 1638c2ecf20Sopenharmony_ci * The only possible value of ret immediately after DRM_MODESET_LOCK_ALL_BEGIN() 1648c2ecf20Sopenharmony_ci * is 0, so no error checking is necessary 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci#define DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, flags, ret) \ 1678c2ecf20Sopenharmony_ci if (!drm_drv_uses_atomic_modeset(dev)) \ 1688c2ecf20Sopenharmony_ci mutex_lock(&dev->mode_config.mutex); \ 1698c2ecf20Sopenharmony_ci drm_modeset_acquire_init(&ctx, flags); \ 1708c2ecf20Sopenharmony_cimodeset_lock_retry: \ 1718c2ecf20Sopenharmony_ci ret = drm_modeset_lock_all_ctx(dev, &ctx); \ 1728c2ecf20Sopenharmony_ci if (ret) \ 1738c2ecf20Sopenharmony_ci goto modeset_lock_fail; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * DRM_MODESET_LOCK_ALL_END - Helper to release and cleanup modeset locks 1778c2ecf20Sopenharmony_ci * @dev: drm device 1788c2ecf20Sopenharmony_ci * @ctx: local modeset acquire context, will be dereferenced 1798c2ecf20Sopenharmony_ci * @ret: local ret/err/etc variable to track error status 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * The other side of DRM_MODESET_LOCK_ALL_BEGIN(). It will bounce back to BEGIN 1828c2ecf20Sopenharmony_ci * if ret is -EDEADLK. 1838c2ecf20Sopenharmony_ci * 1848c2ecf20Sopenharmony_ci * It's important that you use the same ret variable for begin and end so 1858c2ecf20Sopenharmony_ci * deadlock conditions are properly handled. 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * Returns: 1888c2ecf20Sopenharmony_ci * ret will be untouched unless it is -EDEADLK on entry. That means that if you 1898c2ecf20Sopenharmony_ci * successfully acquire the locks, ret will be whatever your code sets it to. If 1908c2ecf20Sopenharmony_ci * there is a deadlock or other failure with acquire or backoff, ret will be set 1918c2ecf20Sopenharmony_ci * to that failure. In both of these cases the code between BEGIN/END will not 1928c2ecf20Sopenharmony_ci * be run, so the failure will reflect the inability to grab the locks. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci#define DRM_MODESET_LOCK_ALL_END(dev, ctx, ret) \ 1958c2ecf20Sopenharmony_cimodeset_lock_fail: \ 1968c2ecf20Sopenharmony_ci if (ret == -EDEADLK) { \ 1978c2ecf20Sopenharmony_ci ret = drm_modeset_backoff(&ctx); \ 1988c2ecf20Sopenharmony_ci if (!ret) \ 1998c2ecf20Sopenharmony_ci goto modeset_lock_retry; \ 2008c2ecf20Sopenharmony_ci } \ 2018c2ecf20Sopenharmony_ci drm_modeset_drop_locks(&ctx); \ 2028c2ecf20Sopenharmony_ci drm_modeset_acquire_fini(&ctx); \ 2038c2ecf20Sopenharmony_ci if (!drm_drv_uses_atomic_modeset(dev)) \ 2048c2ecf20Sopenharmony_ci mutex_unlock(&dev->mode_config.mutex); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#endif /* DRM_MODESET_LOCK_H_ */ 207