linux/drivers/gpu/drm/drm_modeset_lock.c
<<
>>
Prefs
   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, DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
  43 *     retry:
  44 *     foreach (lock in random_ordered_set_of_locks) {
  45 *         ret = drm_modeset_lock(lock, ctx)
  46 *         if (ret == -EDEADLK) {
  47 *             ret = drm_modeset_backoff(ctx);
  48 *             if (!ret)
  49 *                 goto retry;
  50 *         }
  51 *         if (ret)
  52 *             goto out;
  53 *     }
  54 *     ... do stuff ...
  55 *     out:
  56 *     drm_modeset_drop_locks(ctx);
  57 *     drm_modeset_acquire_fini(ctx);
  58 *
  59 * If all that is needed is a single modeset lock, then the &struct
  60 * drm_modeset_acquire_ctx is not needed and the locking can be simplified
  61 * by passing a NULL instead of ctx in the drm_modeset_lock() call or
  62 * calling  drm_modeset_lock_single_interruptible(). To unlock afterwards
  63 * call drm_modeset_unlock().
  64 *
  65 * On top of these per-object locks using &ww_mutex there's also an overall
  66 * &drm_mode_config.mutex, for protecting everything else. Mostly this means
  67 * probe state of connectors, and preventing hotplug add/removal of connectors.
  68 *
  69 * Finally there's a bunch of dedicated locks to protect drm core internal
  70 * lists and lookup data structures.
  71 */
  72
  73static DEFINE_WW_CLASS(crtc_ww_class);
  74
  75/**
  76 * drm_modeset_lock_all - take all modeset locks
  77 * @dev: DRM device
  78 *
  79 * This function takes all modeset locks, suitable where a more fine-grained
  80 * scheme isn't (yet) implemented. Locks must be dropped by calling the
  81 * drm_modeset_unlock_all() function.
  82 *
  83 * This function is deprecated. It allocates a lock acquisition context and
  84 * stores it in &drm_device.mode_config. This facilitate conversion of
  85 * existing code because it removes the need to manually deal with the
  86 * acquisition context, but it is also brittle because the context is global
  87 * and care must be taken not to nest calls. New code should use the
  88 * drm_modeset_lock_all_ctx() function and pass in the context explicitly.
  89 */
  90void drm_modeset_lock_all(struct drm_device *dev)
  91{
  92        struct drm_mode_config *config = &dev->mode_config;
  93        struct drm_modeset_acquire_ctx *ctx;
  94        int ret;
  95
  96        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL | __GFP_NOFAIL);
  97        if (WARN_ON(!ctx))
  98                return;
  99
 100        mutex_lock(&config->mutex);
 101
 102        drm_modeset_acquire_init(ctx, 0);
 103
 104retry:
 105        ret = drm_modeset_lock_all_ctx(dev, ctx);
 106        if (ret < 0) {
 107                if (ret == -EDEADLK) {
 108                        drm_modeset_backoff(ctx);
 109                        goto retry;
 110                }
 111
 112                drm_modeset_acquire_fini(ctx);
 113                kfree(ctx);
 114                return;
 115        }
 116
 117        WARN_ON(config->acquire_ctx);
 118
 119        /*
 120         * We hold the locks now, so it is safe to stash the acquisition
 121         * context for drm_modeset_unlock_all().
 122         */
 123        config->acquire_ctx = ctx;
 124
 125        drm_warn_on_modeset_not_all_locked(dev);
 126}
 127EXPORT_SYMBOL(drm_modeset_lock_all);
 128
 129/**
 130 * drm_modeset_unlock_all - drop all modeset locks
 131 * @dev: DRM device
 132 *
 133 * This function drops all modeset locks taken by a previous call to the
 134 * drm_modeset_lock_all() function.
 135 *
 136 * This function is deprecated. It uses the lock acquisition context stored
 137 * in &drm_device.mode_config. This facilitates conversion of existing
 138 * code because it removes the need to manually deal with the acquisition
 139 * context, but it is also brittle because the context is global and care must
 140 * be taken not to nest calls. New code should pass the acquisition context
 141 * directly to the drm_modeset_drop_locks() function.
 142 */
 143void drm_modeset_unlock_all(struct drm_device *dev)
 144{
 145        struct drm_mode_config *config = &dev->mode_config;
 146        struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
 147
 148        if (WARN_ON(!ctx))
 149                return;
 150
 151        config->acquire_ctx = NULL;
 152        drm_modeset_drop_locks(ctx);
 153        drm_modeset_acquire_fini(ctx);
 154
 155        kfree(ctx);
 156
 157        mutex_unlock(&dev->mode_config.mutex);
 158}
 159EXPORT_SYMBOL(drm_modeset_unlock_all);
 160
 161/**
 162 * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
 163 * @dev: device
 164 *
 165 * Useful as a debug assert.
 166 */
 167void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
 168{
 169        struct drm_crtc *crtc;
 170
 171        /* Locking is currently fubar in the panic handler. */
 172        if (oops_in_progress)
 173                return;
 174
 175        drm_for_each_crtc(crtc, dev)
 176                WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 177
 178        WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 179        WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 180}
 181EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
 182
 183/**
 184 * drm_modeset_acquire_init - initialize acquire context
 185 * @ctx: the acquire context
 186 * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE
 187 *
 188 * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags,
 189 * all calls to drm_modeset_lock() will perform an interruptible
 190 * wait.
 191 */
 192void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
 193                uint32_t flags)
 194{
 195        memset(ctx, 0, sizeof(*ctx));
 196        ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
 197        INIT_LIST_HEAD(&ctx->locked);
 198
 199        if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
 200                ctx->interruptible = true;
 201}
 202EXPORT_SYMBOL(drm_modeset_acquire_init);
 203
 204/**
 205 * drm_modeset_acquire_fini - cleanup acquire context
 206 * @ctx: the acquire context
 207 */
 208void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
 209{
 210        ww_acquire_fini(&ctx->ww_ctx);
 211}
 212EXPORT_SYMBOL(drm_modeset_acquire_fini);
 213
 214/**
 215 * drm_modeset_drop_locks - drop all locks
 216 * @ctx: the acquire context
 217 *
 218 * Drop all locks currently held against this acquire context.
 219 */
 220void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
 221{
 222        WARN_ON(ctx->contended);
 223        while (!list_empty(&ctx->locked)) {
 224                struct drm_modeset_lock *lock;
 225
 226                lock = list_first_entry(&ctx->locked,
 227                                struct drm_modeset_lock, head);
 228
 229                drm_modeset_unlock(lock);
 230        }
 231}
 232EXPORT_SYMBOL(drm_modeset_drop_locks);
 233
 234static inline int modeset_lock(struct drm_modeset_lock *lock,
 235                struct drm_modeset_acquire_ctx *ctx,
 236                bool interruptible, bool slow)
 237{
 238        int ret;
 239
 240        WARN_ON(ctx->contended);
 241
 242        if (ctx->trylock_only) {
 243                lockdep_assert_held(&ctx->ww_ctx);
 244
 245                if (!ww_mutex_trylock(&lock->mutex))
 246                        return -EBUSY;
 247                else
 248                        return 0;
 249        } else if (interruptible && slow) {
 250                ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
 251        } else if (interruptible) {
 252                ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
 253        } else if (slow) {
 254                ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
 255                ret = 0;
 256        } else {
 257                ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
 258        }
 259        if (!ret) {
 260                WARN_ON(!list_empty(&lock->head));
 261                list_add(&lock->head, &ctx->locked);
 262        } else if (ret == -EALREADY) {
 263                /* we already hold the lock.. this is fine.  For atomic
 264                 * we will need to be able to drm_modeset_lock() things
 265                 * without having to keep track of what is already locked
 266                 * or not.
 267                 */
 268                ret = 0;
 269        } else if (ret == -EDEADLK) {
 270                ctx->contended = lock;
 271        }
 272
 273        return ret;
 274}
 275
 276/**
 277 * drm_modeset_backoff - deadlock avoidance backoff
 278 * @ctx: the acquire context
 279 *
 280 * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
 281 * you must call this function to drop all currently held locks and
 282 * block until the contended lock becomes available.
 283 *
 284 * This function returns 0 on success, or -ERESTARTSYS if this context
 285 * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the
 286 * wait has been interrupted.
 287 */
 288int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
 289{
 290        struct drm_modeset_lock *contended = ctx->contended;
 291
 292        ctx->contended = NULL;
 293
 294        if (WARN_ON(!contended))
 295                return 0;
 296
 297        drm_modeset_drop_locks(ctx);
 298
 299        return modeset_lock(contended, ctx, ctx->interruptible, true);
 300}
 301EXPORT_SYMBOL(drm_modeset_backoff);
 302
 303/**
 304 * drm_modeset_lock_init - initialize lock
 305 * @lock: lock to init
 306 */
 307void drm_modeset_lock_init(struct drm_modeset_lock *lock)
 308{
 309        ww_mutex_init(&lock->mutex, &crtc_ww_class);
 310        INIT_LIST_HEAD(&lock->head);
 311}
 312EXPORT_SYMBOL(drm_modeset_lock_init);
 313
 314/**
 315 * drm_modeset_lock - take modeset lock
 316 * @lock: lock to take
 317 * @ctx: acquire ctx
 318 *
 319 * If @ctx is not NULL, then its ww acquire context is used and the
 320 * lock will be tracked by the context and can be released by calling
 321 * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
 322 * deadlock scenario has been detected and it is an error to attempt
 323 * to take any more locks without first calling drm_modeset_backoff().
 324 *
 325 * If the @ctx is not NULL and initialized with
 326 * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with
 327 * -ERESTARTSYS when interrupted.
 328 *
 329 * If @ctx is NULL then the function call behaves like a normal,
 330 * uninterruptible non-nesting mutex_lock() call.
 331 */
 332int drm_modeset_lock(struct drm_modeset_lock *lock,
 333                struct drm_modeset_acquire_ctx *ctx)
 334{
 335        if (ctx)
 336                return modeset_lock(lock, ctx, ctx->interruptible, false);
 337
 338        ww_mutex_lock(&lock->mutex, NULL);
 339        return 0;
 340}
 341EXPORT_SYMBOL(drm_modeset_lock);
 342
 343/**
 344 * drm_modeset_lock_single_interruptible - take a single modeset lock
 345 * @lock: lock to take
 346 *
 347 * This function behaves as drm_modeset_lock() with a NULL context,
 348 * but performs interruptible waits.
 349 *
 350 * This function returns 0 on success, or -ERESTARTSYS when interrupted.
 351 */
 352int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock)
 353{
 354        return ww_mutex_lock_interruptible(&lock->mutex, NULL);
 355}
 356EXPORT_SYMBOL(drm_modeset_lock_single_interruptible);
 357
 358/**
 359 * drm_modeset_unlock - drop modeset lock
 360 * @lock: lock to release
 361 */
 362void drm_modeset_unlock(struct drm_modeset_lock *lock)
 363{
 364        list_del_init(&lock->head);
 365        ww_mutex_unlock(&lock->mutex);
 366}
 367EXPORT_SYMBOL(drm_modeset_unlock);
 368
 369/**
 370 * drm_modeset_lock_all_ctx - take all modeset locks
 371 * @dev: DRM device
 372 * @ctx: lock acquisition context
 373 *
 374 * This function takes all modeset locks, suitable where a more fine-grained
 375 * scheme isn't (yet) implemented.
 376 *
 377 * Unlike drm_modeset_lock_all(), it doesn't take the &drm_mode_config.mutex
 378 * since that lock isn't required for modeset state changes. Callers which
 379 * need to grab that lock too need to do so outside of the acquire context
 380 * @ctx.
 381 *
 382 * Locks acquired with this function should be released by calling the
 383 * drm_modeset_drop_locks() function on @ctx.
 384 *
 385 * Returns: 0 on success or a negative error-code on failure.
 386 */
 387int drm_modeset_lock_all_ctx(struct drm_device *dev,
 388                             struct drm_modeset_acquire_ctx *ctx)
 389{
 390        struct drm_crtc *crtc;
 391        struct drm_plane *plane;
 392        int ret;
 393
 394        ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx);
 395        if (ret)
 396                return ret;
 397
 398        drm_for_each_crtc(crtc, dev) {
 399                ret = drm_modeset_lock(&crtc->mutex, ctx);
 400                if (ret)
 401                        return ret;
 402        }
 403
 404        drm_for_each_plane(plane, dev) {
 405                ret = drm_modeset_lock(&plane->mutex, ctx);
 406                if (ret)
 407                        return ret;
 408        }
 409
 410        return 0;
 411}
 412EXPORT_SYMBOL(drm_modeset_lock_all_ctx);
 413