LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_modeset_lock.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 115 0.0 %
Date: 2022-03-28 15:32:58 Functions: 0 13 0.0 %
Branches: 0 74 0.0 %

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

Generated by: LCOV version 1.14