Subversion Repositories Kolibri OS

Rev

Rev 5271 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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 drm_modeset_lock / drm_modeset_acquire_ctx.
  37.  *
  38.  * For basic principles of ww_mutex, see: Documentation/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.  *
  52.  *     ... do stuff ...
  53.  *
  54.  *     drm_modeset_drop_locks(&ctx);
  55.  *     drm_modeset_acquire_fini(&ctx);
  56.  */
  57.  
  58.  
  59. /**
  60.  * drm_modeset_acquire_init - initialize acquire context
  61.  * @ctx: the acquire context
  62.  * @flags: for future
  63.  */
  64. void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
  65.                 uint32_t flags)
  66. {
  67.         memset(ctx, 0, sizeof(*ctx));
  68.         ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
  69.         INIT_LIST_HEAD(&ctx->locked);
  70. }
  71. EXPORT_SYMBOL(drm_modeset_acquire_init);
  72.  
  73. /**
  74.  * drm_modeset_acquire_fini - cleanup acquire context
  75.  * @ctx: the acquire context
  76.  */
  77. void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
  78. {
  79.         ww_acquire_fini(&ctx->ww_ctx);
  80. }
  81. EXPORT_SYMBOL(drm_modeset_acquire_fini);
  82.  
  83. /**
  84.  * drm_modeset_drop_locks - drop all locks
  85.  * @ctx: the acquire context
  86.  *
  87.  * Drop all locks currently held against this acquire context.
  88.  */
  89. void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
  90. {
  91.         WARN_ON(ctx->contended);
  92.         while (!list_empty(&ctx->locked)) {
  93.                 struct drm_modeset_lock *lock;
  94.  
  95.                 lock = list_first_entry(&ctx->locked,
  96.                                 struct drm_modeset_lock, head);
  97.  
  98.                 drm_modeset_unlock(lock);
  99.         }
  100. }
  101. EXPORT_SYMBOL(drm_modeset_drop_locks);
  102.  
  103. static inline int modeset_lock(struct drm_modeset_lock *lock,
  104.                 struct drm_modeset_acquire_ctx *ctx,
  105.                 bool interruptible, bool slow)
  106. {
  107.         int ret;
  108.  
  109.         WARN_ON(ctx->contended);
  110.  
  111.         if (interruptible && slow) {
  112.                 ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
  113.         } else if (interruptible) {
  114.                 ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
  115.         } else if (slow) {
  116.                 ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
  117.                 ret = 0;
  118.         } else {
  119.                 ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
  120.         }
  121.         if (!ret) {
  122.                 WARN_ON(!list_empty(&lock->head));
  123.                 list_add(&lock->head, &ctx->locked);
  124.         } else if (ret == -EALREADY) {
  125.                 /* we already hold the lock.. this is fine.  For atomic
  126.                  * we will need to be able to drm_modeset_lock() things
  127.                  * without having to keep track of what is already locked
  128.                  * or not.
  129.                  */
  130.                 ret = 0;
  131.         } else if (ret == -EDEADLK) {
  132.                 ctx->contended = lock;
  133.         }
  134.  
  135.         return ret;
  136. }
  137.  
  138. static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
  139.                 bool interruptible)
  140. {
  141.         struct drm_modeset_lock *contended = ctx->contended;
  142.  
  143.         ctx->contended = NULL;
  144.  
  145.         if (WARN_ON(!contended))
  146.                 return 0;
  147.  
  148.         drm_modeset_drop_locks(ctx);
  149.  
  150.         return modeset_lock(contended, ctx, interruptible, true);
  151. }
  152.  
  153. /**
  154.  * drm_modeset_backoff - deadlock avoidance backoff
  155.  * @ctx: the acquire context
  156.  *
  157.  * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
  158.  * you must call this function to drop all currently held locks and
  159.  * block until the contended lock becomes available.
  160.  */
  161. void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
  162. {
  163.         modeset_backoff(ctx, false);
  164. }
  165. EXPORT_SYMBOL(drm_modeset_backoff);
  166.  
  167. /**
  168.  * drm_modeset_backoff_interruptible - deadlock avoidance backoff
  169.  * @ctx: the acquire context
  170.  *
  171.  * Interruptible version of drm_modeset_backoff()
  172.  */
  173. int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
  174. {
  175.         return modeset_backoff(ctx, true);
  176. }
  177. EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
  178.  
  179. /**
  180.  * drm_modeset_lock - take modeset lock
  181.  * @lock: lock to take
  182.  * @ctx: acquire ctx
  183.  *
  184.  * If ctx is not NULL, then its ww acquire context is used and the
  185.  * lock will be tracked by the context and can be released by calling
  186.  * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
  187.  * deadlock scenario has been detected and it is an error to attempt
  188.  * to take any more locks without first calling drm_modeset_backoff().
  189.  */
  190. int drm_modeset_lock(struct drm_modeset_lock *lock,
  191.                 struct drm_modeset_acquire_ctx *ctx)
  192. {
  193.         if (ctx)
  194.                 return modeset_lock(lock, ctx, false, false);
  195.  
  196.         ww_mutex_lock(&lock->mutex, NULL);
  197.         return 0;
  198. }
  199. EXPORT_SYMBOL(drm_modeset_lock);
  200.  
  201. /**
  202.  * drm_modeset_lock_interruptible - take modeset lock
  203.  * @lock: lock to take
  204.  * @ctx: acquire ctx
  205.  *
  206.  * Interruptible version of drm_modeset_lock()
  207.  */
  208. int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
  209.                 struct drm_modeset_acquire_ctx *ctx)
  210. {
  211.         if (ctx)
  212.                 return modeset_lock(lock, ctx, true, false);
  213.  
  214.         return ww_mutex_lock_interruptible(&lock->mutex, NULL);
  215. }
  216. EXPORT_SYMBOL(drm_modeset_lock_interruptible);
  217.  
  218. /**
  219.  * drm_modeset_unlock - drop modeset lock
  220.  * @lock: lock to release
  221.  */
  222. void drm_modeset_unlock(struct drm_modeset_lock *lock)
  223. {
  224.         list_del_init(&lock->head);
  225.         ww_mutex_unlock(&lock->mutex);
  226. }
  227. EXPORT_SYMBOL(drm_modeset_unlock);
  228.  
  229. /* Temporary.. until we have sufficiently fine grained locking, there
  230.  * are a couple scenarios where it is convenient to grab all crtc locks.
  231.  * It is planned to remove this:
  232.  */
  233. int drm_modeset_lock_all_crtcs(struct drm_device *dev,
  234.                 struct drm_modeset_acquire_ctx *ctx)
  235. {
  236.         struct drm_mode_config *config = &dev->mode_config;
  237.         struct drm_crtc *crtc;
  238.         int ret = 0;
  239.  
  240.         list_for_each_entry(crtc, &config->crtc_list, head) {
  241.                 ret = drm_modeset_lock(&crtc->mutex, ctx);
  242.                 if (ret)
  243.                         return ret;
  244.         }
  245.  
  246.         return 0;
  247. }
  248. EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
  249.