1 /* 2 * Copyright (C) 2014 Red Hat 3 * Author: Rob Clark <robdclark@gmail.com> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 * OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 24 #include <drm/drmP.h> 25 #include <drm/drm_crtc.h> 26 #include <drm/drm_modeset_lock.h> 27 28 /** 29 * DOC: kms locking 30 * 31 * As KMS moves toward more fine grained locking, and atomic ioctl where 32 * userspace can indirectly control locking order, it becomes necessary 33 * to use &ww_mutex and acquire-contexts to avoid deadlocks. But because 34 * the locking is more distributed around the driver code, we want a bit 35 * of extra utility/tracking out of our acquire-ctx. This is provided 36 * by &struct drm_modeset_lock and &struct drm_modeset_acquire_ctx. 37 * 38 * For basic principles of &ww_mutex, see: Documentation/locking/ww-mutex-design.txt 39 * 40 * The basic usage pattern is to:: 41 * 42 * drm_modeset_acquire_init(&ctx) 43 * retry: 44 * foreach (lock in random_ordered_set_of_locks) { 45 * ret = drm_modeset_lock(lock, &ctx) 46 * if (ret == -EDEADLK) { 47 * drm_modeset_backoff(&ctx); 48 * goto retry; 49 * } 50 * } 51 * ... do stuff ... 52 * drm_modeset_drop_locks(&ctx); 53 * drm_modeset_acquire_fini(&ctx); 54 * 55 * On top of of these per-object locks using &ww_mutex there's also an overall 56 * &drm_mode_config.mutex, for protecting everything else. Mostly this means 57 * probe state of connectors, and preventing hotplug add/removal of connectors. 58 * 59 * Finally there's a bunch of dedicated locks to protect drm core internal 60 * lists and lookup data structures. 61 */ 62 63 static DEFINE_WW_CLASS(crtc_ww_class); 64 65 /** 66 * drm_modeset_lock_all - take all modeset locks 67 * @dev: DRM device 68 * 69 * This function takes all modeset locks, suitable where a more fine-grained 70 * scheme isn't (yet) implemented. Locks must be dropped by calling the 71 * drm_modeset_unlock_all() function. 72 * 73 * This function is deprecated. It allocates a lock acquisition context and 74 * stores it in &drm_device.mode_config. This facilitate conversion of 75 * existing code because it removes the need to manually deal with the 76 * acquisition context, but it is also brittle because the context is global 77 * and care must be taken not to nest calls. New code should use the 78 * drm_modeset_lock_all_ctx() function and pass in the context explicitly. 79 */ 80 void drm_modeset_lock_all(struct drm_device *dev) 81 { 82 struct drm_mode_config *config = &dev->mode_config; 83 struct drm_modeset_acquire_ctx *ctx; 84 int ret; 85 86 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 87 if (WARN_ON(!ctx)) 88 return; 89 90 mutex_lock(&config->mutex); 91 92 drm_modeset_acquire_init(ctx, 0); 93 94 retry: 95 ret = drm_modeset_lock_all_ctx(dev, ctx); 96 if (ret < 0) { 97 if (ret == -EDEADLK) { 98 drm_modeset_backoff(ctx); 99 goto retry; 100 } 101 102 drm_modeset_acquire_fini(ctx); 103 kfree(ctx); 104 return; 105 } 106 107 WARN_ON(config->acquire_ctx); 108 109 /* 110 * We hold the locks now, so it is safe to stash the acquisition 111 * context for drm_modeset_unlock_all(). 112 */ 113 config->acquire_ctx = ctx; 114 115 drm_warn_on_modeset_not_all_locked(dev); 116 } 117 EXPORT_SYMBOL(drm_modeset_lock_all); 118 119 /** 120 * drm_modeset_unlock_all - drop all modeset locks 121 * @dev: DRM device 122 * 123 * This function drops all modeset locks taken by a previous call to the 124 * drm_modeset_lock_all() function. 125 * 126 * This function is deprecated. It uses the lock acquisition context stored 127 * in &drm_device.mode_config. This facilitates conversion of existing 128 * code because it removes the need to manually deal with the acquisition 129 * context, but it is also brittle because the context is global and care must 130 * be taken not to nest calls. New code should pass the acquisition context 131 * directly to the drm_modeset_drop_locks() function. 132 */ 133 void drm_modeset_unlock_all(struct drm_device *dev) 134 { 135 struct drm_mode_config *config = &dev->mode_config; 136 struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; 137 138 if (WARN_ON(!ctx)) 139 return; 140 141 config->acquire_ctx = NULL; 142 drm_modeset_drop_locks(ctx); 143 drm_modeset_acquire_fini(ctx); 144 145 kfree(ctx); 146 147 mutex_unlock(&dev->mode_config.mutex); 148 } 149 EXPORT_SYMBOL(drm_modeset_unlock_all); 150 151 /** 152 * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked 153 * @dev: device 154 * 155 * Useful as a debug assert. 156 */ 157 void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) 158 { 159 struct drm_crtc *crtc; 160 161 /* Locking is currently fubar in the panic handler. */ 162 if (oops_in_progress) 163 return; 164 165 drm_for_each_crtc(crtc, dev) 166 WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); 167 168 WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 169 WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 170 } 171 EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); 172 173 /** 174 * drm_modeset_acquire_init - initialize acquire context 175 * @ctx: the acquire context 176 * @flags: for future 177 */ 178 void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, 179 uint32_t flags) 180 { 181 memset(ctx, 0, sizeof(*ctx)); 182 ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); 183 INIT_LIST_HEAD(&ctx->locked); 184 } 185 EXPORT_SYMBOL(drm_modeset_acquire_init); 186 187 /** 188 * drm_modeset_acquire_fini - cleanup acquire context 189 * @ctx: the acquire context 190 */ 191 void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx) 192 { 193 ww_acquire_fini(&ctx->ww_ctx); 194 } 195 EXPORT_SYMBOL(drm_modeset_acquire_fini); 196 197 /** 198 * drm_modeset_drop_locks - drop all locks 199 * @ctx: the acquire context 200 * 201 * Drop all locks currently held against this acquire context. 202 */ 203 void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) 204 { 205 WARN_ON(ctx->contended); 206 while (!list_empty(&ctx->locked)) { 207 struct drm_modeset_lock *lock; 208 209 lock = list_first_entry(&ctx->locked, 210 struct drm_modeset_lock, head); 211 212 drm_modeset_unlock(lock); 213 } 214 } 215 EXPORT_SYMBOL(drm_modeset_drop_locks); 216 217 static inline int modeset_lock(struct drm_modeset_lock *lock, 218 struct drm_modeset_acquire_ctx *ctx, 219 bool interruptible, bool slow) 220 { 221 int ret; 222 223 WARN_ON(ctx->contended); 224 225 if (ctx->trylock_only) { 226 lockdep_assert_held(&ctx->ww_ctx); 227 228 if (!ww_mutex_trylock(&lock->mutex)) 229 return -EBUSY; 230 else 231 return 0; 232 } else if (interruptible && slow) { 233 ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx); 234 } else if (interruptible) { 235 ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); 236 } else if (slow) { 237 ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx); 238 ret = 0; 239 } else { 240 ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx); 241 } 242 if (!ret) { 243 WARN_ON(!list_empty(&lock->head)); 244 list_add(&lock->head, &ctx->locked); 245 } else if (ret == -EALREADY) { 246 /* we already hold the lock.. this is fine. For atomic 247 * we will need to be able to drm_modeset_lock() things 248 * without having to keep track of what is already locked 249 * or not. 250 */ 251 ret = 0; 252 } else if (ret == -EDEADLK) { 253 ctx->contended = lock; 254 } 255 256 return ret; 257 } 258 259 static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx, 260 bool interruptible) 261 { 262 struct drm_modeset_lock *contended = ctx->contended; 263 264 ctx->contended = NULL; 265 266 if (WARN_ON(!contended)) 267 return 0; 268 269 drm_modeset_drop_locks(ctx); 270 271 return modeset_lock(contended, ctx, interruptible, true); 272 } 273 274 /** 275 * drm_modeset_backoff - deadlock avoidance backoff 276 * @ctx: the acquire context 277 * 278 * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK), 279 * you must call this function to drop all currently held locks and 280 * block until the contended lock becomes available. 281 */ 282 void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) 283 { 284 modeset_backoff(ctx, false); 285 } 286 EXPORT_SYMBOL(drm_modeset_backoff); 287 288 /** 289 * drm_modeset_backoff_interruptible - deadlock avoidance backoff 290 * @ctx: the acquire context 291 * 292 * Interruptible version of drm_modeset_backoff() 293 */ 294 int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx) 295 { 296 return modeset_backoff(ctx, true); 297 } 298 EXPORT_SYMBOL(drm_modeset_backoff_interruptible); 299 300 /** 301 * drm_modeset_lock_init - initialize lock 302 * @lock: lock to init 303 */ 304 void drm_modeset_lock_init(struct drm_modeset_lock *lock) 305 { 306 ww_mutex_init(&lock->mutex, &crtc_ww_class); 307 INIT_LIST_HEAD(&lock->head); 308 } 309 EXPORT_SYMBOL(drm_modeset_lock_init); 310 311 /** 312 * drm_modeset_lock - take modeset lock 313 * @lock: lock to take 314 * @ctx: acquire ctx 315 * 316 * If ctx is not NULL, then its ww acquire context is used and the 317 * lock will be tracked by the context and can be released by calling 318 * drm_modeset_drop_locks(). If -EDEADLK is returned, this means a 319 * deadlock scenario has been detected and it is an error to attempt 320 * to take any more locks without first calling drm_modeset_backoff(). 321 */ 322 int drm_modeset_lock(struct drm_modeset_lock *lock, 323 struct drm_modeset_acquire_ctx *ctx) 324 { 325 if (ctx) 326 return modeset_lock(lock, ctx, false, false); 327 328 ww_mutex_lock(&lock->mutex, NULL); 329 return 0; 330 } 331 EXPORT_SYMBOL(drm_modeset_lock); 332 333 /** 334 * drm_modeset_lock_interruptible - take modeset lock 335 * @lock: lock to take 336 * @ctx: acquire ctx 337 * 338 * Interruptible version of drm_modeset_lock() 339 */ 340 int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, 341 struct drm_modeset_acquire_ctx *ctx) 342 { 343 if (ctx) 344 return modeset_lock(lock, ctx, true, false); 345 346 return ww_mutex_lock_interruptible(&lock->mutex, NULL); 347 } 348 EXPORT_SYMBOL(drm_modeset_lock_interruptible); 349 350 /** 351 * drm_modeset_unlock - drop modeset lock 352 * @lock: lock to release 353 */ 354 void drm_modeset_unlock(struct drm_modeset_lock *lock) 355 { 356 list_del_init(&lock->head); 357 ww_mutex_unlock(&lock->mutex); 358 } 359 EXPORT_SYMBOL(drm_modeset_unlock); 360 361 /** 362 * drm_modeset_lock_all_ctx - take all modeset locks 363 * @dev: DRM device 364 * @ctx: lock acquisition context 365 * 366 * This function takes all modeset locks, suitable where a more fine-grained 367 * scheme isn't (yet) implemented. 368 * 369 * Unlike drm_modeset_lock_all(), it doesn't take the &drm_mode_config.mutex 370 * since that lock isn't required for modeset state changes. Callers which 371 * need to grab that lock too need to do so outside of the acquire context 372 * @ctx. 373 * 374 * Locks acquired with this function should be released by calling the 375 * drm_modeset_drop_locks() function on @ctx. 376 * 377 * Returns: 0 on success or a negative error-code on failure. 378 */ 379 int drm_modeset_lock_all_ctx(struct drm_device *dev, 380 struct drm_modeset_acquire_ctx *ctx) 381 { 382 struct drm_crtc *crtc; 383 struct drm_plane *plane; 384 int ret; 385 386 ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); 387 if (ret) 388 return ret; 389 390 drm_for_each_crtc(crtc, dev) { 391 ret = drm_modeset_lock(&crtc->mutex, ctx); 392 if (ret) 393 return ret; 394 } 395 396 drm_for_each_plane(plane, dev) { 397 ret = drm_modeset_lock(&plane->mutex, ctx); 398 if (ret) 399 return ret; 400 } 401 402 return 0; 403 } 404 EXPORT_SYMBOL(drm_modeset_lock_all_ctx); 405