LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_exec.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 64 93 68.8 %
Date: 2023-07-19 18:55:55 Functions: 10 10 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0 OR MIT
       2             : 
       3             : #include <drm/drm_exec.h>
       4             : #include <drm/drm_gem.h>
       5             : #include <linux/dma-resv.h>
       6             : 
       7             : /**
       8             :  * DOC: Overview
       9             :  *
      10             :  * This component mainly abstracts the retry loop necessary for locking
      11             :  * multiple GEM objects while preparing hardware operations (e.g. command
      12             :  * submissions, page table updates etc..).
      13             :  *
      14             :  * If a contention is detected while locking a GEM object the cleanup procedure
      15             :  * unlocks all previously locked GEM objects and locks the contended one first
      16             :  * before locking any further objects.
      17             :  *
      18             :  * After an object is locked fences slots can optionally be reserved on the
      19             :  * dma_resv object inside the GEM object.
      20             :  *
      21             :  * A typical usage pattern should look like this::
      22             :  *
      23             :  *      struct drm_gem_object *obj;
      24             :  *      struct drm_exec exec;
      25             :  *      unsigned long index;
      26             :  *      int ret;
      27             :  *
      28             :  *      drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
      29             :  *      drm_exec_until_all_locked(&exec) {
      30             :  *              ret = drm_exec_prepare_obj(&exec, boA, 1);
      31             :  *              drm_exec_retry_on_contention(&exec);
      32             :  *              if (ret)
      33             :  *                      goto error;
      34             :  *
      35             :  *              ret = drm_exec_prepare_obj(&exec, boB, 1);
      36             :  *              drm_exec_retry_on_contention(&exec);
      37             :  *              if (ret)
      38             :  *                      goto error;
      39             :  *      }
      40             :  *
      41             :  *      drm_exec_for_each_locked_object(&exec, index, obj) {
      42             :  *              dma_resv_add_fence(obj->resv, fence, DMA_RESV_USAGE_READ);
      43             :  *              ...
      44             :  *      }
      45             :  *      drm_exec_fini(&exec);
      46             :  *
      47             :  * See struct dma_exec for more details.
      48             :  */
      49             : 
      50             : /* Dummy value used to initially enter the retry loop */
      51             : #define DRM_EXEC_DUMMY ((void *)~0)
      52             : 
      53             : /* Unlock all objects and drop references */
      54           6 : static void drm_exec_unlock_all(struct drm_exec *exec)
      55             : {
      56             :         struct drm_gem_object *obj;
      57             :         unsigned long index;
      58             : 
      59          11 :         drm_exec_for_each_locked_object(exec, index, obj) {
      60          10 :                 dma_resv_unlock(obj->resv);
      61           5 :                 drm_gem_object_put(obj);
      62             :         }
      63             : 
      64          12 :         drm_gem_object_put(exec->prelocked);
      65           6 :         exec->prelocked = NULL;
      66           6 : }
      67             : 
      68             : /**
      69             :  * drm_exec_init - initialize a drm_exec object
      70             :  * @exec: the drm_exec object to initialize
      71             :  * @flags: controls locking behavior, see DRM_EXEC_* defines
      72             :  *
      73             :  * Initialize the object and make sure that we can track locked objects.
      74             :  */
      75           6 : void drm_exec_init(struct drm_exec *exec, uint32_t flags)
      76             : {
      77           6 :         exec->flags = flags;
      78           6 :         exec->objects = kmalloc(PAGE_SIZE, GFP_KERNEL);
      79             : 
      80             :         /* If allocation here fails, just delay that till the first use */
      81           6 :         exec->max_objects = exec->objects ? PAGE_SIZE / sizeof(void *) : 0;
      82           6 :         exec->num_objects = 0;
      83           6 :         exec->contended = DRM_EXEC_DUMMY;
      84           6 :         exec->prelocked = NULL;
      85           6 : }
      86             : EXPORT_SYMBOL(drm_exec_init);
      87             : 
      88             : /**
      89             :  * drm_exec_fini - finalize a drm_exec object
      90             :  * @exec: the drm_exec object to finalize
      91             :  *
      92             :  * Unlock all locked objects, drop the references to objects and free all memory
      93             :  * used for tracking the state.
      94             :  */
      95           6 : void drm_exec_fini(struct drm_exec *exec)
      96             : {
      97           6 :         drm_exec_unlock_all(exec);
      98           6 :         kvfree(exec->objects);
      99           6 :         if (exec->contended != DRM_EXEC_DUMMY) {
     100          10 :                 drm_gem_object_put(exec->contended);
     101           5 :                 ww_acquire_fini(&exec->ticket);
     102             :         }
     103           6 : }
     104             : EXPORT_SYMBOL(drm_exec_fini);
     105             : 
     106             : /**
     107             :  * drm_exec_cleanup - cleanup when contention is detected
     108             :  * @exec: the drm_exec object to cleanup
     109             :  *
     110             :  * Cleanup the current state and return true if we should stay inside the retry
     111             :  * loop, false if there wasn't any contention detected and we can keep the
     112             :  * objects locked.
     113             :  */
     114          10 : bool drm_exec_cleanup(struct drm_exec *exec)
     115             : {
     116          10 :         if (likely(!exec->contended)) {
     117             :                 ww_acquire_done(&exec->ticket);
     118             :                 return false;
     119             :         }
     120             : 
     121           5 :         if (likely(exec->contended == DRM_EXEC_DUMMY)) {
     122           5 :                 exec->contended = NULL;
     123          10 :                 ww_acquire_init(&exec->ticket, &reservation_ww_class);
     124           5 :                 return true;
     125             :         }
     126             : 
     127           0 :         drm_exec_unlock_all(exec);
     128           0 :         exec->num_objects = 0;
     129           0 :         return true;
     130             : }
     131             : EXPORT_SYMBOL(drm_exec_cleanup);
     132             : 
     133             : /* Track the locked object in the array */
     134           7 : static int drm_exec_obj_locked(struct drm_exec *exec,
     135             :                                struct drm_gem_object *obj)
     136             : {
     137           7 :         if (unlikely(exec->num_objects == exec->max_objects)) {
     138           0 :                 size_t size = exec->max_objects * sizeof(void *);
     139             :                 void *tmp;
     140             : 
     141           0 :                 tmp = kvrealloc(exec->objects, size, size + PAGE_SIZE,
     142             :                                 GFP_KERNEL);
     143           0 :                 if (!tmp)
     144             :                         return -ENOMEM;
     145             : 
     146           0 :                 exec->objects = tmp;
     147           0 :                 exec->max_objects += PAGE_SIZE / sizeof(void *);
     148             :         }
     149           7 :         drm_gem_object_get(obj);
     150           7 :         exec->objects[exec->num_objects++] = obj;
     151             : 
     152           7 :         return 0;
     153             : }
     154             : 
     155             : /* Make sure the contended object is locked first */
     156           8 : static int drm_exec_lock_contended(struct drm_exec *exec)
     157             : {
     158           8 :         struct drm_gem_object *obj = exec->contended;
     159             :         int ret;
     160             : 
     161           8 :         if (likely(!obj))
     162             :                 return 0;
     163             : 
     164             :         /* Always cleanup the contention so that error handling can kick in */
     165           0 :         exec->contended = NULL;
     166           0 :         if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT) {
     167           0 :                 ret = dma_resv_lock_slow_interruptible(obj->resv,
     168             :                                                        &exec->ticket);
     169           0 :                 if (unlikely(ret))
     170             :                         goto error_dropref;
     171             :         } else {
     172           0 :                 dma_resv_lock_slow(obj->resv, &exec->ticket);
     173             :         }
     174             : 
     175           0 :         ret = drm_exec_obj_locked(exec, obj);
     176           0 :         if (unlikely(ret))
     177             :                 goto error_unlock;
     178             : 
     179           0 :         exec->prelocked = obj;
     180           0 :         return 0;
     181             : 
     182             : error_unlock:
     183           0 :         dma_resv_unlock(obj->resv);
     184             : 
     185             : error_dropref:
     186             :         drm_gem_object_put(obj);
     187             :         return ret;
     188             : }
     189             : 
     190             : /**
     191             :  * drm_exec_lock_obj - lock a GEM object for use
     192             :  * @exec: the drm_exec object with the state
     193             :  * @obj: the GEM object to lock
     194             :  *
     195             :  * Lock a GEM object for use and grab a reference to it.
     196             :  *
     197             :  * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
     198             :  * already locked (can be suppressed by setting the DRM_EXEC_IGNORE_DUPLICATES
     199             :  * flag), -ENOMEM when memory allocation failed and zero for success.
     200             :  */
     201           8 : int drm_exec_lock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
     202             : {
     203             :         int ret;
     204             : 
     205           8 :         ret = drm_exec_lock_contended(exec);
     206           8 :         if (unlikely(ret))
     207             :                 return ret;
     208             : 
     209           8 :         if (exec->prelocked == obj) {
     210           0 :                 drm_gem_object_put(exec->prelocked);
     211           0 :                 exec->prelocked = NULL;
     212           0 :                 return 0;
     213             :         }
     214             : 
     215           8 :         if (exec->flags & DRM_EXEC_INTERRUPTIBLE_WAIT)
     216          12 :                 ret = dma_resv_lock_interruptible(obj->resv, &exec->ticket);
     217             :         else
     218           4 :                 ret = dma_resv_lock(obj->resv, &exec->ticket);
     219             : 
     220           8 :         if (unlikely(ret == -EDEADLK)) {
     221           0 :                 drm_gem_object_get(obj);
     222           0 :                 exec->contended = obj;
     223           0 :                 return -EDEADLK;
     224             :         }
     225             : 
     226           9 :         if (unlikely(ret == -EALREADY) &&
     227           1 :             exec->flags & DRM_EXEC_IGNORE_DUPLICATES)
     228             :                 return 0;
     229             : 
     230           7 :         if (unlikely(ret))
     231             :                 return ret;
     232             : 
     233           7 :         ret = drm_exec_obj_locked(exec, obj);
     234           7 :         if (ret)
     235             :                 goto error_unlock;
     236             : 
     237             :         return 0;
     238             : 
     239             : error_unlock:
     240           0 :         dma_resv_unlock(obj->resv);
     241           0 :         return ret;
     242             : }
     243             : EXPORT_SYMBOL(drm_exec_lock_obj);
     244             : 
     245             : /**
     246             :  * drm_exec_unlock_obj - unlock a GEM object in this exec context
     247             :  * @exec: the drm_exec object with the state
     248             :  * @obj: the GEM object to unlock
     249             :  *
     250             :  * Unlock the GEM object and remove it from the collection of locked objects.
     251             :  * Should only be used to unlock the most recently locked objects. It's not time
     252             :  * efficient to unlock objects locked long ago.
     253             :  */
     254           2 : void drm_exec_unlock_obj(struct drm_exec *exec, struct drm_gem_object *obj)
     255             : {
     256             :         unsigned int i;
     257             : 
     258           4 :         for (i = exec->num_objects; i--;) {
     259           2 :                 if (exec->objects[i] == obj) {
     260           4 :                         dma_resv_unlock(obj->resv);
     261           2 :                         for (++i; i < exec->num_objects; ++i)
     262           0 :                                 exec->objects[i - 1] = exec->objects[i];
     263           2 :                         --exec->num_objects;
     264             :                         drm_gem_object_put(obj);
     265             :                         return;
     266             :                 }
     267             : 
     268             :         }
     269             : }
     270             : EXPORT_SYMBOL(drm_exec_unlock_obj);
     271             : 
     272             : /**
     273             :  * drm_exec_prepare_obj - prepare a GEM object for use
     274             :  * @exec: the drm_exec object with the state
     275             :  * @obj: the GEM object to prepare
     276             :  * @num_fences: how many fences to reserve
     277             :  *
     278             :  * Prepare a GEM object for use by locking it and reserving fence slots.
     279             :  *
     280             :  * Returns: -EDEADLK if a contention is detected, -EALREADY when object is
     281             :  * already locked, -ENOMEM when memory allocation failed and zero for success.
     282             :  */
     283           3 : int drm_exec_prepare_obj(struct drm_exec *exec, struct drm_gem_object *obj,
     284             :                          unsigned int num_fences)
     285             : {
     286             :         int ret;
     287             : 
     288           3 :         ret = drm_exec_lock_obj(exec, obj);
     289           3 :         if (ret)
     290             :                 return ret;
     291             : 
     292           3 :         ret = dma_resv_reserve_fences(obj->resv, num_fences);
     293           3 :         if (ret) {
     294           0 :                 drm_exec_unlock_obj(exec, obj);
     295           0 :                 return ret;
     296             :         }
     297             : 
     298             :         return 0;
     299             : }
     300             : EXPORT_SYMBOL(drm_exec_prepare_obj);
     301             : 
     302             : /**
     303             :  * drm_exec_prepare_array - helper to prepare an array of objects
     304             :  * @exec: the drm_exec object with the state
     305             :  * @objects: array of GEM object to prepare
     306             :  * @num_objects: number of GEM objects in the array
     307             :  * @num_fences: number of fences to reserve on each GEM object
     308             :  *
     309             :  * Prepares all GEM objects in an array, aborts on first error.
     310             :  * Reserves @num_fences on each GEM object after locking it.
     311             :  *
     312             :  * Returns: -EDEADLOCK on contention, -EALREADY when object is already locked,
     313             :  * -ENOMEM when memory allocation failed and zero for success.
     314             :  */
     315           1 : int drm_exec_prepare_array(struct drm_exec *exec,
     316             :                            struct drm_gem_object **objects,
     317             :                            unsigned int num_objects,
     318             :                            unsigned int num_fences)
     319             : {
     320             :         int ret;
     321             : 
     322           3 :         for (unsigned int i = 0; i < num_objects; ++i) {
     323           2 :                 ret = drm_exec_prepare_obj(exec, objects[i], num_fences);
     324           2 :                 if (unlikely(ret))
     325             :                         return ret;
     326             :         }
     327             : 
     328             :         return 0;
     329             : }
     330             : EXPORT_SYMBOL(drm_exec_prepare_array);
     331             : 
     332             : MODULE_DESCRIPTION("DRM execution context");
     333             : MODULE_LICENSE("Dual MIT/GPL");

Generated by: LCOV version 1.14