| 1 | |
| 2 | * Copyright (C) 2014 Red Hat
|
| 3 | * Author: Rob Clark <[email protected]>
|
| 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 <linux/export.h>
|
| 25 |
|
| 26 | #include <drm/drm_atomic.h>
|
| 27 | #include <drm/drm_crtc.h>
|
| 28 | #include <drm/drm_device.h>
|
| 29 | #include <drm/drm_modeset_lock.h>
|
| 30 | #include <drm/drm_print.h>
|
| 31 |
|
| 32 | |
| 33 | * DOC: kms locking
|
| 34 | *
|
| 35 | * As KMS moves toward more fine grained locking, and atomic ioctl where
|
| 36 | * userspace can indirectly control locking order, it becomes necessary
|
| 37 | * to use &ww_mutex and acquire-contexts to avoid deadlocks. But because
|
| 38 | * the locking is more distributed around the driver code, we want a bit
|
| 39 | * of extra utility/tracking out of our acquire-ctx. This is provided
|
| 40 | * by &struct drm_modeset_lock and &struct drm_modeset_acquire_ctx.
|
| 41 | *
|
| 42 | * For basic principles of &ww_mutex, see: Documentation/locking/ww-mutex-design.rst
|
| 43 | *
|
| 44 | * The basic usage pattern is to::
|
| 45 | *
|
| 46 | * drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
|
| 47 | * retry:
|
| 48 | * foreach (lock in random_ordered_set_of_locks) {
|
| 49 | * ret = drm_modeset_lock(lock, ctx)
|
| 50 | * if (ret == -EDEADLK) {
|
| 51 | * ret = drm_modeset_backoff(ctx);
|
| 52 | * if (!ret)
|
| 53 | * goto retry;
|
| 54 | * }
|
| 55 | * if (ret)
|
| 56 | * goto out;
|
| 57 | * }
|
| 58 | * ... do stuff ...
|
| 59 | * out:
|
| 60 | * drm_modeset_drop_locks(ctx);
|
| 61 | * drm_modeset_acquire_fini(ctx);
|
| 62 | *
|
| 63 | * For convenience this control flow is implemented in
|
| 64 | * DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() for the case
|
| 65 | * where all modeset locks need to be taken through drm_modeset_lock_all_ctx().
|
| 66 | *
|
| 67 | * If all that is needed is a single modeset lock, then the &struct
|
| 68 | * drm_modeset_acquire_ctx is not needed and the locking can be simplified
|
| 69 | * by passing a NULL instead of ctx in the drm_modeset_lock() call or
|
| 70 | * calling drm_modeset_lock_single_interruptible(). To unlock afterwards
|
| 71 | * call drm_modeset_unlock().
|
| 72 | *
|
| 73 | * On top of these per-object locks using &ww_mutex there's also an overall
|
| 74 | * &drm_mode_config.mutex, for protecting everything else. Mostly this means
|
| 75 | * probe state of connectors, and preventing hotplug add/removal of connectors.
|
| 76 | *
|
| 77 | * Finally there's a bunch of dedicated locks to protect drm core internal
|
| 78 | * lists and lookup data structures.
|
| 79 |
|
| 80 |
|
| 81 | static DEFINE_WW_CLASS(crtc_ww_class);
|
| 82 |
|
| 83 | #if IS_ENABLED(CONFIG_DRM_DEBUG_MODESET_LOCK)
|
| 84 | static noinline depot_stack_handle_t __drm_stack_depot_save(void)
|
| 85 | {
|
| 86 | unsigned long entries[8];
|
| 87 | unsigned int n;
|
| 88 |
|
| 89 | n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
|
| 90 |
|
| 91 | return stack_depot_save(entries, n, GFP_NOWAIT | __GFP_NOWARN);
|
| 92 | }
|
| 93 |
|
| 94 | static void __drm_stack_depot_print(depot_stack_handle_t stack_depot)
|
| 95 | {
|
| 96 | struct drm_printer p = drm_dbg_printer(NULL, DRM_UT_KMS, "drm_modeset_lock");
|
| 97 | unsigned long *entries;
|
| 98 | unsigned int nr_entries;
|
| 99 | char *buf;
|
| 100 |
|
| 101 | buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
|
| 102 | if (!buf)
|
| 103 | return;
|
| 104 |
|
| 105 | nr_entries = stack_depot_fetch(stack_depot, &entries);
|
| 106 | stack_trace_snprint(buf, PAGE_SIZE, entries, nr_entries, 2);
|
| 107 |
|
| 108 | drm_printf(&p, "attempting to lock a contended lock without backoff:\n%s", buf);
|
| 109 |
|
| 110 | kfree(buf);
|
| 111 | }
|
| 112 |
|
| 113 | static void __drm_stack_depot_init(void)
|
| 114 | {
|
| 115 | stack_depot_init();
|
| 116 | }
|
| 117 | #else
|
| 118 | static depot_stack_handle_t __drm_stack_depot_save(void)
|
| 119 | {
|
| 120 | return 0;
|
| 121 | }
|
| 122 | static void __drm_stack_depot_print(depot_stack_handle_t stack_depot)
|
| 123 | {
|
| 124 | }
|
| 125 | static void __drm_stack_depot_init(void)
|
| 126 | {
|
| 127 | }
|
| 128 | #endif
|
| 129 |
|
| 130 | |
| 131 | * drm_modeset_lock_all - take all modeset locks
|
| 132 | * @dev: DRM device
|
| 133 | *
|
| 134 | * This function takes all modeset locks, suitable where a more fine-grained
|
| 135 | * scheme isn't (yet) implemented. Locks must be dropped by calling the
|
| 136 | * drm_modeset_unlock_all() function.
|
| 137 | *
|
| 138 | * This function is deprecated. It allocates a lock acquisition context and
|
| 139 | * stores it in &drm_device.mode_config. This facilitate conversion of
|
| 140 | * existing code because it removes the need to manually deal with the
|
| 141 | * acquisition context, but it is also brittle because the context is global
|
| 142 | * and care must be taken not to nest calls. New code should use the
|
| 143 | * drm_modeset_lock_all_ctx() function and pass in the context explicitly.
|
| 144 |
|
| 145 | void drm_modeset_lock_all(struct drm_device *dev)
|
| 146 | {
|
| 147 | struct drm_mode_config *config = &dev->mode_config;
|
| 148 | struct drm_modeset_acquire_ctx *ctx;
|
| 149 | int ret;
|
| 150 |
|
| 151 | ctx = kzalloc_obj(*ctx, GFP_KERNEL | __GFP_NOFAIL);
|
| 152 | mutex_lock(&config->mutex);
|
| 153 | drm_modeset_acquire_init(ctx, 0);
|
| 154 |
|
| 155 | retry:
|
| 156 | ret = drm_modeset_lock_all_ctx(dev, ctx);
|
| 157 | if (ret < 0) {
|
| 158 | if (ret == -EDEADLK) {
|
| 159 | drm_modeset_backoff(ctx);
|
| 160 | goto retry;
|
| 161 | }
|
| 162 |
|
| 163 | drm_modeset_acquire_fini(ctx);
|
| 164 | kfree(ctx);
|
| 165 | return;
|
| 166 | }
|
| 167 | ww_acquire_done(&ctx->ww_ctx);
|
| 168 |
|
| 169 | WARN_ON(config->acquire_ctx);
|
| 170 |
|
| 171 | |
| 172 | * We hold the locks now, so it is safe to stash the acquisition
|
| 173 | * context for drm_modeset_unlock_all().
|
| 174 |
|
| 175 | config->acquire_ctx = ctx;
|
| 176 |
|
| 177 | drm_warn_on_modeset_not_all_locked(dev);
|
| 178 | }
|
| 179 | EXPORT_SYMBOL(drm_modeset_lock_all);
|
| 180 |
|
| 181 | |
| 182 | * drm_modeset_unlock_all - drop all modeset locks
|
| 183 | * @dev: DRM device
|
| 184 | *
|
| 185 | * This function drops all modeset locks taken by a previous call to the
|
| 186 | * drm_modeset_lock_all() function.
|
| 187 | *
|
| 188 | * This function is deprecated. It uses the lock acquisition context stored
|
| 189 | * in &drm_device.mode_config. This facilitates conversion of existing
|
| 190 | * code because it removes the need to manually deal with the acquisition
|
| 191 | * context, but it is also brittle because the context is global and care must
|
| 192 | * be taken not to nest calls. New code should pass the acquisition context
|
| 193 | * directly to the drm_modeset_drop_locks() function.
|
| 194 |
|
| 195 | void drm_modeset_unlock_all(struct drm_device *dev)
|
| 196 | {
|
| 197 | struct drm_mode_config *config = &dev->mode_config;
|
| 198 | struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
|
| 199 |
|
| 200 | if (WARN_ON(!ctx))
|
| 201 | return;
|
| 202 |
|
| 203 | config->acquire_ctx = NULL;
|
| 204 | drm_modeset_drop_locks(ctx);
|
| 205 | drm_modeset_acquire_fini(ctx);
|
| 206 |
|
| 207 | kfree(ctx);
|
| 208 |
|
| 209 | mutex_unlock(&dev->mode_config.mutex);
|
| 210 | }
|
| 211 | EXPORT_SYMBOL(drm_modeset_unlock_all);
|
| 212 |
|
| 213 | |
| 214 | * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
|
| 215 | * @dev: device
|
| 216 | *
|
| 217 | * Useful as a debug assert.
|
| 218 |
|
| 219 | void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
|
| 220 | {
|
| 221 | struct drm_crtc *crtc;
|
| 222 |
|
| 223 |
|
| 224 | if (oops_in_progress)
|
| 225 | return;
|
| 226 |
|
| 227 | drm_for_each_crtc(crtc, dev)
|
| 228 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
|
| 229 |
|
| 230 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
|
| 231 | WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
| 232 | }
|
| 233 | EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
|
| 234 |
|
| 235 | |
| 236 | * drm_modeset_acquire_init - initialize acquire context
|
| 237 | * @ctx: the acquire context
|
| 238 | * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE
|
| 239 | *
|
| 240 | * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags,
|
| 241 | * all calls to drm_modeset_lock() will perform an interruptible
|
| 242 | * wait.
|
| 243 |
|
| 244 | void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
|
| 245 | uint32_t flags)
|
| 246 | {
|
| 247 | memset(ctx, 0, sizeof(*ctx));
|
| 248 | ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
|
| 249 | INIT_LIST_HEAD(&ctx->locked);
|
| 250 |
|
| 251 | if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
|
| 252 | ctx->interruptible = true;
|
| 253 | }
|
| 254 | EXPORT_SYMBOL(drm_modeset_acquire_init);
|
| 255 |
|
| 256 | |
| 257 | * drm_modeset_acquire_fini - cleanup acquire context
|
| 258 | * @ctx: the acquire context
|
| 259 |
|
| 260 | void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
|
| 261 | {
|
| 262 | ww_acquire_fini(&ctx->ww_ctx);
|
| 263 | }
|
| 264 | EXPORT_SYMBOL(drm_modeset_acquire_fini);
|
| 265 |
|
| 266 | |
| 267 | * drm_modeset_drop_locks - drop all locks
|
| 268 | * @ctx: the acquire context
|
| 269 | *
|
| 270 | * Drop all locks currently held against this acquire context.
|
| 271 |
|
| 272 | void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
|
| 273 | {
|
| 274 | if (WARN_ON(ctx->contended))
|
| 275 | __drm_stack_depot_print(ctx->stack_depot);
|
| 276 |
|
| 277 | while (!list_empty(&ctx->locked)) {
|
| 278 | struct drm_modeset_lock *lock;
|
| 279 |
|
| 280 | lock = list_first_entry(&ctx->locked,
|
| 281 | struct drm_modeset_lock, head);
|
| 282 |
|
| 283 | drm_modeset_unlock(lock);
|
| 284 | }
|
| 285 | }
|
| 286 | EXPORT_SYMBOL(drm_modeset_drop_locks);
|
| 287 |
|
| 288 | static inline int modeset_lock(struct drm_modeset_lock *lock,
|
| 289 | struct drm_modeset_acquire_ctx *ctx,
|
| 290 | bool interruptible, bool slow)
|
| 291 | {
|
| 292 | int ret;
|
| 293 |
|
| 294 | if (WARN_ON(ctx->contended))
|
| 295 | __drm_stack_depot_print(ctx->stack_depot);
|
| 296 |
|
| 297 | if (ctx->trylock_only) {
|
| 298 | lockdep_assert_held(&ctx->ww_ctx);
|
| 299 |
|
| 300 | if (!ww_mutex_trylock(&lock->mutex, NULL))
|
| 301 | return -EBUSY;
|
| 302 | else
|
| 303 | return 0;
|
| 304 | } else if (interruptible && slow) {
|
| 305 | ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
|
| 306 | } else if (interruptible) {
|
| 307 | ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
|
| 308 | } else if (slow) {
|
| 309 | ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
|
| 310 | ret = 0;
|
| 311 | } else {
|
| 312 | ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
|
| 313 | }
|
| 314 | if (!ret) {
|
| 315 | WARN_ON(!list_empty(&lock->head));
|
| 316 | list_add(&lock->head, &ctx->locked);
|
| 317 | } else if (ret == -EALREADY) {
|
| 318 | |
| 319 | * we will need to be able to drm_modeset_lock() things
|
| 320 | * without having to keep track of what is already locked
|
| 321 | * or not.
|
| 322 |
|
| 323 | ret = 0;
|
| 324 | } else if (ret == -EDEADLK) {
|
| 325 | ctx->contended = lock;
|
| 326 | ctx->stack_depot = __drm_stack_depot_save();
|
| 327 | }
|
| 328 |
|
| 329 | return ret;
|
| 330 | }
|
| 331 |
|
| 332 | |
| 333 | * drm_modeset_backoff - deadlock avoidance backoff
|
| 334 | * @ctx: the acquire context
|
| 335 | *
|
| 336 | * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
|
| 337 | * you must call this function to drop all currently held locks and
|
| 338 | * block until the contended lock becomes available.
|
| 339 | *
|
| 340 | * This function returns 0 on success, or -ERESTARTSYS if this context
|
| 341 | * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the
|
| 342 | * wait has been interrupted.
|
| 343 |
|
| 344 | int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
|
| 345 | {
|
| 346 | struct drm_modeset_lock *contended = ctx->contended;
|
| 347 |
|
| 348 | ctx->contended = NULL;
|
| 349 | ctx->stack_depot = 0;
|
| 350 |
|
| 351 | if (WARN_ON(!contended))
|
| 352 | return 0;
|
| 353 |
|
| 354 | drm_modeset_drop_locks(ctx);
|
| 355 |
|
| 356 | return modeset_lock(contended, ctx, ctx->interruptible, true);
|
| 357 | }
|
| 358 | EXPORT_SYMBOL(drm_modeset_backoff);
|
| 359 |
|
| 360 | |
| 361 | * drm_modeset_lock_init - initialize lock
|
| 362 | * @lock: lock to init
|
| 363 |
|
| 364 | void drm_modeset_lock_init(struct drm_modeset_lock *lock)
|
| 365 | {
|
| 366 | ww_mutex_init(&lock->mutex, &crtc_ww_class);
|
| 367 | INIT_LIST_HEAD(&lock->head);
|
| 368 | __drm_stack_depot_init();
|
| 369 | }
|
| 370 | EXPORT_SYMBOL(drm_modeset_lock_init);
|
| 371 |
|
| 372 | |
| 373 | * drm_modeset_lock - take modeset lock
|
| 374 | * @lock: lock to take
|
| 375 | * @ctx: acquire ctx
|
| 376 | *
|
| 377 | * If @ctx is not NULL, then its ww acquire context is used and the
|
| 378 | * lock will be tracked by the context and can be released by calling
|
| 379 | * drm_modeset_drop_locks(). If -EDEADLK is returned, this means a
|
| 380 | * deadlock scenario has been detected and it is an error to attempt
|
| 381 | * to take any more locks without first calling drm_modeset_backoff().
|
| 382 | *
|
| 383 | * If the @ctx is not NULL and initialized with
|
| 384 | * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with
|
| 385 | * -ERESTARTSYS when interrupted.
|
| 386 | *
|
| 387 | * If @ctx is NULL then the function call behaves like a normal,
|
| 388 | * uninterruptible non-nesting mutex_lock() call.
|
| 389 |
|
| 390 | int drm_modeset_lock(struct drm_modeset_lock *lock,
|
| 391 | struct drm_modeset_acquire_ctx *ctx)
|
| 392 | {
|
| 393 | if (ctx)
|
| 394 | return modeset_lock(lock, ctx, ctx->interruptible, false);
|
| 395 |
|
| 396 | ww_mutex_lock(&lock->mutex, NULL);
|
| 397 | return 0;
|
| 398 | }
|
| 399 | EXPORT_SYMBOL(drm_modeset_lock);
|
| 400 |
|
| 401 | |
| 402 | * drm_modeset_lock_single_interruptible - take a single modeset lock
|
| 403 | * @lock: lock to take
|
| 404 | *
|
| 405 | * This function behaves as drm_modeset_lock() with a NULL context,
|
| 406 | * but performs interruptible waits.
|
| 407 | *
|
| 408 | * This function returns 0 on success, or -ERESTARTSYS when interrupted.
|
| 409 |
|
| 410 | int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock)
|
| 411 | {
|
| 412 | return ww_mutex_lock_interruptible(&lock->mutex, NULL);
|
| 413 | }
|
| 414 | EXPORT_SYMBOL(drm_modeset_lock_single_interruptible);
|
| 415 |
|
| 416 | |
| 417 | * drm_modeset_unlock - drop modeset lock
|
| 418 | * @lock: lock to release
|
| 419 |
|
| 420 | void drm_modeset_unlock(struct drm_modeset_lock *lock)
|
| 421 | {
|
| 422 | list_del_init(&lock->head);
|
| 423 | ww_mutex_unlock(&lock->mutex);
|
| 424 | }
|
| 425 | EXPORT_SYMBOL(drm_modeset_unlock);
|
| 426 |
|
| 427 | |
| 428 | * drm_modeset_lock_all_ctx - take all modeset locks
|
| 429 | * @dev: DRM device
|
| 430 | * @ctx: lock acquisition context
|
| 431 | *
|
| 432 | * This function takes all modeset locks, suitable where a more fine-grained
|
| 433 | * scheme isn't (yet) implemented.
|
| 434 | *
|
| 435 | * Unlike drm_modeset_lock_all(), it doesn't take the &drm_mode_config.mutex
|
| 436 | * since that lock isn't required for modeset state changes. Callers which
|
| 437 | * need to grab that lock too need to do so outside of the acquire context
|
| 438 | * @ctx.
|
| 439 | *
|
| 440 | * Locks acquired with this function should be released by calling the
|
| 441 | * drm_modeset_drop_locks() function on @ctx.
|
| 442 | *
|
| 443 | * See also: DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END()
|
| 444 | *
|
| 445 | * Returns: 0 on success or a negative error-code on failure.
|
| 446 |
|
| 447 | int drm_modeset_lock_all_ctx(struct drm_device *dev,
|
| 448 | struct drm_modeset_acquire_ctx *ctx)
|
| 449 | {
|
| 450 | struct drm_private_obj *privobj;
|
| 451 | struct drm_crtc *crtc;
|
| 452 | struct drm_plane *plane;
|
| 453 | int ret;
|
| 454 |
|
| 455 | ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx);
|
| 456 | if (ret)
|
| 457 | return ret;
|
| 458 |
|
| 459 | drm_for_each_crtc(crtc, dev) {
|
| 460 | ret = drm_modeset_lock(&crtc->mutex, ctx);
|
| 461 | if (ret)
|
| 462 | return ret;
|
| 463 | }
|
| 464 |
|
| 465 | drm_for_each_plane(plane, dev) {
|
| 466 | ret = drm_modeset_lock(&plane->mutex, ctx);
|
| 467 | if (ret)
|
| 468 | return ret;
|
| 469 | }
|
| 470 |
|
| 471 | drm_for_each_privobj(privobj, dev) {
|
| 472 | ret = drm_modeset_lock(&privobj->lock, ctx);
|
| 473 | if (ret)
|
| 474 | return ret;
|
| 475 | }
|
| 476 |
|
| 477 | return 0;
|
| 478 | }
|
| 479 | EXPORT_SYMBOL(drm_modeset_lock_all_ctx);
|