@@ -876,6 +876,31 @@ __drm_gpuvm_bo_list_add(struct drm_gpuvm *gpuvm, spinlock_t *lock,
876876 cond_spin_unlock (lock , !!lock );
877877}
878878
879+ /**
880+ * drm_gpuvm_bo_is_zombie() - check whether this vm_bo is scheduled for cleanup
881+ * @vm_bo: the &drm_gpuvm_bo
882+ *
883+ * When a vm_bo is scheduled for cleanup using the bo_defer list, it is not
884+ * immediately removed from the evict and extobj lists. Therefore, anyone
885+ * iterating these lists should skip entries that are being destroyed.
886+ *
887+ * Checking the refcount without incrementing it is okay as long as the lock
888+ * protecting the evict/extobj list is held for as long as you are using the
889+ * vm_bo, because even if the refcount hits zero while you are using it, freeing
890+ * the vm_bo requires taking the list's lock.
891+ *
892+ * Zombie entries can be observed on the evict and extobj lists regardless of
893+ * whether DRM_GPUVM_RESV_PROTECTED is used, but they remain on the lists for a
894+ * longer time when the resv lock is used because we can't take the resv lock
895+ * during run_job() in immediate mode, meaning that they need to remain on the
896+ * lists until drm_gpuvm_bo_deferred_cleanup() is called.
897+ */
898+ static bool
899+ drm_gpuvm_bo_is_zombie (struct drm_gpuvm_bo * vm_bo )
900+ {
901+ return !kref_read (& vm_bo -> kref );
902+ }
903+
879904/**
880905 * drm_gpuvm_bo_list_add() - insert a vm_bo into the given list
881906 * @__vm_bo: the &drm_gpuvm_bo
@@ -1081,6 +1106,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
10811106 INIT_LIST_HEAD (& gpuvm -> evict .list );
10821107 spin_lock_init (& gpuvm -> evict .lock );
10831108
1109+ init_llist_head (& gpuvm -> bo_defer );
1110+
10841111 kref_init (& gpuvm -> kref );
10851112
10861113 gpuvm -> name = name ? name : "unknown" ;
@@ -1122,6 +1149,8 @@ drm_gpuvm_fini(struct drm_gpuvm *gpuvm)
11221149 "Extobj list should be empty.\n" );
11231150 drm_WARN (gpuvm -> drm , !list_empty (& gpuvm -> evict .list ),
11241151 "Evict list should be empty.\n" );
1152+ drm_WARN (gpuvm -> drm , !llist_empty (& gpuvm -> bo_defer ),
1153+ "VM BO cleanup list should be empty.\n" );
11251154
11261155 drm_gem_object_put (gpuvm -> r_obj );
11271156}
@@ -1217,6 +1246,9 @@ drm_gpuvm_prepare_objects_locked(struct drm_gpuvm *gpuvm,
12171246
12181247 drm_gpuvm_resv_assert_held (gpuvm );
12191248 list_for_each_entry (vm_bo , & gpuvm -> extobj .list , list .entry .extobj ) {
1249+ if (drm_gpuvm_bo_is_zombie (vm_bo ))
1250+ continue ;
1251+
12201252 ret = exec_prepare_obj (exec , vm_bo -> obj , num_fences );
12211253 if (ret )
12221254 break ;
@@ -1460,6 +1492,9 @@ drm_gpuvm_validate_locked(struct drm_gpuvm *gpuvm, struct drm_exec *exec)
14601492
14611493 list_for_each_entry_safe (vm_bo , next , & gpuvm -> evict .list ,
14621494 list .entry .evict ) {
1495+ if (drm_gpuvm_bo_is_zombie (vm_bo ))
1496+ continue ;
1497+
14631498 ret = ops -> vm_bo_validate (vm_bo , exec );
14641499 if (ret )
14651500 break ;
@@ -1560,6 +1595,7 @@ drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,
15601595
15611596 INIT_LIST_HEAD (& vm_bo -> list .entry .extobj );
15621597 INIT_LIST_HEAD (& vm_bo -> list .entry .evict );
1598+ init_llist_node (& vm_bo -> list .entry .bo_defer );
15631599
15641600 return vm_bo ;
15651601}
@@ -1621,6 +1657,126 @@ drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo)
16211657}
16221658EXPORT_SYMBOL_GPL (drm_gpuvm_bo_put );
16231659
1660+ /*
1661+ * drm_gpuvm_bo_into_zombie() - called when the vm_bo becomes a zombie due to
1662+ * deferred cleanup
1663+ *
1664+ * If deferred cleanup is used, then this must be called right after the vm_bo
1665+ * refcount drops to zero. Must be called with GEM mutex held. After releasing
1666+ * the GEM mutex, drm_gpuvm_bo_defer_zombie_cleanup() must be called.
1667+ */
1668+ static void
1669+ drm_gpuvm_bo_into_zombie (struct kref * kref )
1670+ {
1671+ struct drm_gpuvm_bo * vm_bo = container_of (kref , struct drm_gpuvm_bo ,
1672+ kref );
1673+
1674+ if (!drm_gpuvm_resv_protected (vm_bo -> vm )) {
1675+ drm_gpuvm_bo_list_del (vm_bo , extobj , true);
1676+ drm_gpuvm_bo_list_del (vm_bo , evict , true);
1677+ }
1678+
1679+ list_del (& vm_bo -> list .entry .gem );
1680+ }
1681+
1682+ /*
1683+ * drm_gpuvm_bo_defer_zombie_cleanup() - adds a new zombie vm_bo to the
1684+ * bo_defer list
1685+ *
1686+ * Called after drm_gpuvm_bo_into_zombie(). GEM mutex must not be held.
1687+ *
1688+ * It's important that the GEM stays alive for the duration in which we hold
1689+ * the mutex, but the instant we add the vm_bo to bo_defer, another thread
1690+ * might call drm_gpuvm_bo_deferred_cleanup() and put the GEM. Therefore, to
1691+ * avoid kfreeing a mutex we are holding, the GEM mutex must be released
1692+ * *before* calling this function.
1693+ */
1694+ static void
1695+ drm_gpuvm_bo_defer_zombie_cleanup (struct drm_gpuvm_bo * vm_bo )
1696+ {
1697+ llist_add (& vm_bo -> list .entry .bo_defer , & vm_bo -> vm -> bo_defer );
1698+ }
1699+
1700+ static void
1701+ drm_gpuvm_bo_defer_free (struct kref * kref )
1702+ {
1703+ struct drm_gpuvm_bo * vm_bo = container_of (kref , struct drm_gpuvm_bo ,
1704+ kref );
1705+
1706+ drm_gpuvm_bo_into_zombie (kref );
1707+ mutex_unlock (& vm_bo -> obj -> gpuva .lock );
1708+ drm_gpuvm_bo_defer_zombie_cleanup (vm_bo );
1709+ }
1710+
1711+ /**
1712+ * drm_gpuvm_bo_put_deferred() - drop a struct drm_gpuvm_bo reference with
1713+ * deferred cleanup
1714+ * @vm_bo: the &drm_gpuvm_bo to release the reference of
1715+ *
1716+ * This releases a reference to @vm_bo.
1717+ *
1718+ * This might take and release the GEMs GPUVA lock. You should call
1719+ * drm_gpuvm_bo_deferred_cleanup() later to complete the cleanup process.
1720+ *
1721+ * Returns: true if vm_bo is being destroyed, false otherwise.
1722+ */
1723+ bool
1724+ drm_gpuvm_bo_put_deferred (struct drm_gpuvm_bo * vm_bo )
1725+ {
1726+ if (!vm_bo )
1727+ return false;
1728+
1729+ drm_WARN_ON (vm_bo -> vm -> drm , !drm_gpuvm_immediate_mode (vm_bo -> vm ));
1730+
1731+ return !!kref_put_mutex (& vm_bo -> kref ,
1732+ drm_gpuvm_bo_defer_free ,
1733+ & vm_bo -> obj -> gpuva .lock );
1734+ }
1735+ EXPORT_SYMBOL_GPL (drm_gpuvm_bo_put_deferred );
1736+
1737+ /**
1738+ * drm_gpuvm_bo_deferred_cleanup() - clean up BOs in the deferred list
1739+ * deferred cleanup
1740+ * @gpuvm: the VM to clean up
1741+ *
1742+ * Cleans up &drm_gpuvm_bo instances in the deferred cleanup list.
1743+ */
1744+ void
1745+ drm_gpuvm_bo_deferred_cleanup (struct drm_gpuvm * gpuvm )
1746+ {
1747+ const struct drm_gpuvm_ops * ops = gpuvm -> ops ;
1748+ struct drm_gpuvm_bo * vm_bo ;
1749+ struct drm_gem_object * obj ;
1750+ struct llist_node * bo_defer ;
1751+
1752+ bo_defer = llist_del_all (& gpuvm -> bo_defer );
1753+ if (!bo_defer )
1754+ return ;
1755+
1756+ if (drm_gpuvm_resv_protected (gpuvm )) {
1757+ dma_resv_lock (drm_gpuvm_resv (gpuvm ), NULL );
1758+ llist_for_each_entry (vm_bo , bo_defer , list .entry .bo_defer ) {
1759+ drm_gpuvm_bo_list_del (vm_bo , extobj , false);
1760+ drm_gpuvm_bo_list_del (vm_bo , evict , false);
1761+ }
1762+ dma_resv_unlock (drm_gpuvm_resv (gpuvm ));
1763+ }
1764+
1765+ while (bo_defer ) {
1766+ vm_bo = llist_entry (bo_defer , struct drm_gpuvm_bo , list .entry .bo_defer );
1767+ bo_defer = bo_defer -> next ;
1768+ obj = vm_bo -> obj ;
1769+ if (ops && ops -> vm_bo_free )
1770+ ops -> vm_bo_free (vm_bo );
1771+ else
1772+ kfree (vm_bo );
1773+
1774+ drm_gpuvm_put (gpuvm );
1775+ drm_gem_object_put (obj );
1776+ }
1777+ }
1778+ EXPORT_SYMBOL_GPL (drm_gpuvm_bo_deferred_cleanup );
1779+
16241780static struct drm_gpuvm_bo *
16251781__drm_gpuvm_bo_find (struct drm_gpuvm * gpuvm ,
16261782 struct drm_gem_object * obj )
@@ -1948,6 +2104,40 @@ drm_gpuva_unlink(struct drm_gpuva *va)
19482104}
19492105EXPORT_SYMBOL_GPL (drm_gpuva_unlink );
19502106
2107+ /**
2108+ * drm_gpuva_unlink_defer() - unlink a &drm_gpuva with deferred vm_bo cleanup
2109+ * @va: the &drm_gpuva to unlink
2110+ *
2111+ * Similar to drm_gpuva_unlink(), but uses drm_gpuvm_bo_put_deferred() and takes
2112+ * the lock for the caller.
2113+ */
2114+ void
2115+ drm_gpuva_unlink_defer (struct drm_gpuva * va )
2116+ {
2117+ struct drm_gem_object * obj = va -> gem .obj ;
2118+ struct drm_gpuvm_bo * vm_bo = va -> vm_bo ;
2119+ bool should_defer_bo ;
2120+
2121+ if (unlikely (!obj ))
2122+ return ;
2123+
2124+ drm_WARN_ON (vm_bo -> vm -> drm , !drm_gpuvm_immediate_mode (vm_bo -> vm ));
2125+
2126+ mutex_lock (& obj -> gpuva .lock );
2127+ list_del_init (& va -> gem .entry );
2128+
2129+ /*
2130+ * This is drm_gpuvm_bo_put_deferred() except we already hold the mutex.
2131+ */
2132+ should_defer_bo = kref_put (& vm_bo -> kref , drm_gpuvm_bo_into_zombie );
2133+ mutex_unlock (& obj -> gpuva .lock );
2134+ if (should_defer_bo )
2135+ drm_gpuvm_bo_defer_zombie_cleanup (vm_bo );
2136+
2137+ va -> vm_bo = NULL ;
2138+ }
2139+ EXPORT_SYMBOL_GPL (drm_gpuva_unlink_defer );
2140+
19512141/**
19522142 * drm_gpuva_find_first() - find the first &drm_gpuva in the given range
19532143 * @gpuvm: the &drm_gpuvm to search in
0 commit comments