diff mbox series

[v2] dma-buf: allow nested dma_resv_reserve_fences

Message ID 20230623200113.62051-1-Yunxiang.Li@amd.com (mailing list archive)
State New, archived
Headers show
Series [v2] dma-buf: allow nested dma_resv_reserve_fences | expand

Commit Message

Yunxiang Li June 23, 2023, 7:59 p.m. UTC
Calling dma_resv_reserve_fences a second time would not reserve memory
correctly, this is quite unintuitive and the current debug build option
cannot catch this error reliably because of the slack in max_fences.

Rework the function to allow multiple invocations, also make reserve
check stricter. This fixes issue where ttm_bo_mem_space's reserve is
ignored in various amdgpu ioctl paths, and was causing soft-lockup when
VRAM is exhausted.

v2: try to avoid memory corruption if possible.

Signed-off-by: Yunxiang Li <Yunxiang.Li@amd.com>
---
 drivers/dma-buf/dma-resv.c | 56 ++++++++++++++++++++++++--------------
 include/linux/dma-resv.h   |  8 ++----
 2 files changed, 38 insertions(+), 26 deletions(-)

Comments

kernel test robot June 23, 2023, 9:20 p.m. UTC | #1
Hi Yunxiang,

kernel test robot noticed the following build errors:



url:    https://github.com/intel-lab-lkp/linux/commits/UPDATE-20230624-040209/Yunxiang-Li/drm-amdgpu-fix-missing-fence-reserve-in-amdgpu_vm_sdma_commit/20230622-002915
base:   the 2th patch of https://lore.kernel.org/r/20230621162652.10875-3-Yunxiang.Li%40amd.com
patch link:    https://lore.kernel.org/r/20230623200113.62051-1-Yunxiang.Li%40amd.com
patch subject: [PATCH v2] dma-buf: allow nested dma_resv_reserve_fences
config: m68k-randconfig-r002-20230622 (https://download.01.org/0day-ci/archive/20230624/202306240538.Mz3kjtT7-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 12.3.0
reproduce: (https://download.01.org/0day-ci/archive/20230624/202306240538.Mz3kjtT7-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202306240538.Mz3kjtT7-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/dma-buf/dma-resv.c: In function 'dma_resv_add_fence':
>> drivers/dma-buf/dma-resv.c:326: error: unterminated #else
     326 | #ifdef CONFIG_DEBUG_MUTEXES
         | 
   drivers/dma-buf/dma-resv.c:327:9: note: '-Wmisleading-indentation' is disabled from this point onwards, since column-tracking was disabled due to the size of the code/headers
     327 |         else
         |         ^~~~
   drivers/dma-buf/dma-resv.c:327:9: note: adding '-flarge-source-files' will allow for more column-tracking support, at the expense of compilation time and memory
>> drivers/dma-buf/dma-resv.c:328:17: error: expected declaration or statement at end of input
     328 |                 WARN_ON(1); // missing fence slot allocation
         |                 ^~~~~~~


vim +326 drivers/dma-buf/dma-resv.c

   274	
   275	/**
   276	 * dma_resv_add_fence - Add a fence to the dma_resv obj
   277	 * @obj: the reservation object
   278	 * @fence: the fence to add
   279	 * @usage: how the fence is used, see enum dma_resv_usage
   280	 *
   281	 * Add a fence to a slot, @obj must be locked with dma_resv_lock(), and
   282	 * dma_resv_reserve_fences() has been called.
   283	 *
   284	 * See also &dma_resv.fence for a discussion of the semantics.
   285	 */
   286	void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence,
   287				enum dma_resv_usage usage)
   288	{
   289		struct dma_resv_list *fobj;
   290		struct dma_fence *old;
   291		unsigned int i, count;
   292	
   293		dma_fence_get(fence);
   294	
   295		dma_resv_assert_held(obj);
   296	
   297		/* Drivers should not add containers here, instead add each fence
   298		 * individually.
   299		 */
   300		WARN_ON(dma_fence_is_container(fence));
   301	
   302	retry:
   303		fobj = dma_resv_fences_list(obj);
   304		count = fobj->num_fences;
   305	
   306		for (i = 0; i < count; ++i) {
   307			enum dma_resv_usage old_usage;
   308	
   309			dma_resv_list_entry(fobj, i, obj, &old, &old_usage);
   310			if ((old->context == fence->context && old_usage >= usage &&
   311			     dma_fence_is_later(fence, old)) ||
   312			    dma_fence_is_signaled(old)) {
   313				dma_resv_list_set(fobj, i, fence, usage);
   314				dma_fence_put(old);
   315				return;
   316			}
   317		}
   318	
   319		if (WARN_ON(fobj->num_fences == fobj->max_fences)) {
   320			// try our best to avoid memory corruption
   321			dma_resv_reserve_fences(obj, 1);
   322			goto retry;
   323		}
   324		if (fobj->reserved_fences)
   325			fobj->reserved_fences -= 1;
 > 326	#ifdef CONFIG_DEBUG_MUTEXES
   327		else
 > 328			WARN_ON(1); // missing fence slot allocation
   329	#else
   330		count++;
   331	
   332		dma_resv_list_set(fobj, i, fence, usage);
   333		/* pointer update must be visible before we extend the num_fences */
   334		smp_store_mb(fobj->num_fences, count);
   335	}
   336	EXPORT_SYMBOL(dma_resv_add_fence);
   337
kernel test robot June 23, 2023, 9:41 p.m. UTC | #2
Hi Yunxiang,

kernel test robot noticed the following build errors:



url:    https://github.com/intel-lab-lkp/linux/commits/UPDATE-20230624-040209/Yunxiang-Li/drm-amdgpu-fix-missing-fence-reserve-in-amdgpu_vm_sdma_commit/20230622-002915
base:   the 2th patch of https://lore.kernel.org/r/20230621162652.10875-3-Yunxiang.Li%40amd.com
patch link:    https://lore.kernel.org/r/20230623200113.62051-1-Yunxiang.Li%40amd.com
patch subject: [PATCH v2] dma-buf: allow nested dma_resv_reserve_fences
config: s390-randconfig-r016-20230621 (https://download.01.org/0day-ci/archive/20230624/202306240524.WBCHWUlJ-lkp@intel.com/config)
compiler: clang version 15.0.7 (https://github.com/llvm/llvm-project.git 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a)
reproduce: (https://download.01.org/0day-ci/archive/20230624/202306240524.WBCHWUlJ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202306240524.WBCHWUlJ-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/dma-buf/dma-resv.c:326:2: error: unterminated conditional directive
   #ifdef CONFIG_DEBUG_MUTEXES
    ^
>> drivers/dma-buf/dma-resv.c:817:7: error: expected '}'
   #endif
         ^
   drivers/dma-buf/dma-resv.c:288:1: note: to match this '{'
   {
   ^
   2 errors generated.


vim +326 drivers/dma-buf/dma-resv.c

   274	
   275	/**
   276	 * dma_resv_add_fence - Add a fence to the dma_resv obj
   277	 * @obj: the reservation object
   278	 * @fence: the fence to add
   279	 * @usage: how the fence is used, see enum dma_resv_usage
   280	 *
   281	 * Add a fence to a slot, @obj must be locked with dma_resv_lock(), and
   282	 * dma_resv_reserve_fences() has been called.
   283	 *
   284	 * See also &dma_resv.fence for a discussion of the semantics.
   285	 */
   286	void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence,
   287				enum dma_resv_usage usage)
   288	{
   289		struct dma_resv_list *fobj;
   290		struct dma_fence *old;
   291		unsigned int i, count;
   292	
   293		dma_fence_get(fence);
   294	
   295		dma_resv_assert_held(obj);
   296	
   297		/* Drivers should not add containers here, instead add each fence
   298		 * individually.
   299		 */
   300		WARN_ON(dma_fence_is_container(fence));
   301	
   302	retry:
   303		fobj = dma_resv_fences_list(obj);
   304		count = fobj->num_fences;
   305	
   306		for (i = 0; i < count; ++i) {
   307			enum dma_resv_usage old_usage;
   308	
   309			dma_resv_list_entry(fobj, i, obj, &old, &old_usage);
   310			if ((old->context == fence->context && old_usage >= usage &&
   311			     dma_fence_is_later(fence, old)) ||
   312			    dma_fence_is_signaled(old)) {
   313				dma_resv_list_set(fobj, i, fence, usage);
   314				dma_fence_put(old);
   315				return;
   316			}
   317		}
   318	
   319		if (WARN_ON(fobj->num_fences == fobj->max_fences)) {
   320			// try our best to avoid memory corruption
   321			dma_resv_reserve_fences(obj, 1);
   322			goto retry;
   323		}
   324		if (fobj->reserved_fences)
   325			fobj->reserved_fences -= 1;
 > 326	#ifdef CONFIG_DEBUG_MUTEXES
   327		else
   328			WARN_ON(1); // missing fence slot allocation
   329	#else
   330		count++;
   331	
   332		dma_resv_list_set(fobj, i, fence, usage);
   333		/* pointer update must be visible before we extend the num_fences */
   334		smp_store_mb(fobj->num_fences, count);
   335	}
   336	EXPORT_SYMBOL(dma_resv_add_fence);
   337
kernel test robot June 23, 2023, 9:41 p.m. UTC | #3
Hi Yunxiang,

kernel test robot noticed the following build errors:



url:    https://github.com/intel-lab-lkp/linux/commits/UPDATE-20230624-040209/Yunxiang-Li/drm-amdgpu-fix-missing-fence-reserve-in-amdgpu_vm_sdma_commit/20230622-002915
base:   the 2th patch of https://lore.kernel.org/r/20230621162652.10875-3-Yunxiang.Li%40amd.com
patch link:    https://lore.kernel.org/r/20230623200113.62051-1-Yunxiang.Li%40amd.com
patch subject: [PATCH v2] dma-buf: allow nested dma_resv_reserve_fences
config: arc-randconfig-r006-20230622 (https://download.01.org/0day-ci/archive/20230624/202306240508.nAff52YL-lkp@intel.com/config)
compiler: arceb-elf-gcc (GCC) 12.3.0
reproduce: (https://download.01.org/0day-ci/archive/20230624/202306240508.nAff52YL-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202306240508.nAff52YL-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/dma-buf/dma-resv.c: In function 'dma_resv_add_fence':
   drivers/dma-buf/dma-resv.c:326: error: unterminated #else
     326 | #ifdef CONFIG_DEBUG_MUTEXES
         | 
   drivers/dma-buf/dma-resv.c:327:9: note: '-Wmisleading-indentation' is disabled from this point onwards, since column-tracking was disabled due to the size of the code/headers
     327 |         else
         |         ^~~~
   drivers/dma-buf/dma-resv.c:327:9: note: adding '-flarge-source-files' will allow for more column-tracking support, at the expense of compilation time and memory
   In file included from include/linux/build_bug.h:5,
                    from include/linux/container_of.h:5,
                    from include/linux/list.h:5,
                    from include/linux/mutex.h:15,
                    from include/linux/ww_mutex.h:20,
                    from include/linux/dma-resv.h:42,
                    from drivers/dma-buf/dma-resv.c:36:
>> include/linux/compiler.h:24:39: error: expected declaration or statement at end of input
      24 |                         static struct ftrace_likely_data                \
         |                                       ^~~~~~~~~~~~~~~~~~
   include/linux/compiler.h:47:26: note: in expansion of macro '__branch_check__'
      47 | #  define unlikely(x)   (__branch_check__(x, 0, __builtin_constant_p(x)))
         |                          ^~~~~~~~~~~~~~~~
   include/asm-generic/bug.h:125:9: note: in expansion of macro 'unlikely'
     125 |         unlikely(__ret_warn_on);                                        \
         |         ^~~~~~~~
   drivers/dma-buf/dma-resv.c:328:17: note: in expansion of macro 'WARN_ON'
     328 |                 WARN_ON(1); // missing fence slot allocation
         |                 ^~~~~~~


vim +24 include/linux/compiler.h

1f0d69a9fc815d Steven Rostedt          2008-11-12  21  
d45ae1f7041ac5 Steven Rostedt (VMware  2017-01-17  22) #define __branch_check__(x, expect, is_constant) ({			\
2026d35741f2c3 Mikulas Patocka         2018-05-30  23  			long ______r;					\
134e6a034cb004 Steven Rostedt (VMware  2017-01-19 @24) 			static struct ftrace_likely_data		\
e04462fb82f8dd Miguel Ojeda            2018-09-03  25  				__aligned(4)				\
33def8498fdde1 Joe Perches             2020-10-21  26  				__section("_ftrace_annotated_branch")	\
1f0d69a9fc815d Steven Rostedt          2008-11-12  27  				______f = {				\
134e6a034cb004 Steven Rostedt (VMware  2017-01-19  28) 				.data.func = __func__,			\
134e6a034cb004 Steven Rostedt (VMware  2017-01-19  29) 				.data.file = __FILE__,			\
134e6a034cb004 Steven Rostedt (VMware  2017-01-19  30) 				.data.line = __LINE__,			\
1f0d69a9fc815d Steven Rostedt          2008-11-12  31  			};						\
d45ae1f7041ac5 Steven Rostedt (VMware  2017-01-17  32) 			______r = __builtin_expect(!!(x), expect);	\
d45ae1f7041ac5 Steven Rostedt (VMware  2017-01-17  33) 			ftrace_likely_update(&______f, ______r,		\
d45ae1f7041ac5 Steven Rostedt (VMware  2017-01-17  34) 					     expect, is_constant);	\
1f0d69a9fc815d Steven Rostedt          2008-11-12  35  			______r;					\
1f0d69a9fc815d Steven Rostedt          2008-11-12  36  		})
1f0d69a9fc815d Steven Rostedt          2008-11-12  37
diff mbox series

Patch

diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c
index b6f71eb00866..d9a1831ae7f9 100644
--- a/drivers/dma-buf/dma-resv.c
+++ b/drivers/dma-buf/dma-resv.c
@@ -62,7 +62,7 @@  EXPORT_SYMBOL(reservation_ww_class);
 
 struct dma_resv_list {
 	struct rcu_head rcu;
-	u32 num_fences, max_fences;
+	u32 num_fences, reserved_fences, max_fences;
 	struct dma_fence __rcu *table[];
 };
 
@@ -107,6 +107,8 @@  static struct dma_resv_list *dma_resv_list_alloc(unsigned int max_fences)
 	if (!list)
 		return NULL;
 
+	list->num_fences = 0;
+	list->reserved_fences = 0;
 	/* Given the resulting bucket size, recalculated max_fences. */
 	list->max_fences = (size - offsetof(typeof(*list), table)) /
 		sizeof(*list->table);
@@ -173,7 +175,7 @@  static inline struct dma_resv_list *dma_resv_fences_list(struct dma_resv *obj)
  * locked through dma_resv_lock().
  *
  * Note that the preallocated slots need to be re-reserved if @obj is unlocked
- * at any time before calling dma_resv_add_fence(). This is validated when
+ * at any time before calling dma_resv_add_fence(). This produces a warning when
  * CONFIG_DEBUG_MUTEXES is enabled.
  *
  * RETURNS
@@ -182,22 +184,27 @@  static inline struct dma_resv_list *dma_resv_fences_list(struct dma_resv *obj)
 int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)
 {
 	struct dma_resv_list *old, *new;
-	unsigned int i, j, k, max;
+	unsigned int i, j, k, new_max;
 
 	dma_resv_assert_held(obj);
 
 	old = dma_resv_fences_list(obj);
 	if (old && old->max_fences) {
-		if ((old->num_fences + num_fences) <= old->max_fences)
+		num_fences += old->reserved_fences;
+		if ((old->num_fences + num_fences) <= old->max_fences) {
+			old->reserved_fences = num_fences;
 			return 0;
-		max = max(old->num_fences + num_fences, old->max_fences * 2);
+		}
+		new_max =
+			max(old->num_fences + num_fences, old->max_fences * 2);
 	} else {
-		max = max(4ul, roundup_pow_of_two(num_fences));
+		new_max = max(num_fences, 4u);
 	}
 
-	new = dma_resv_list_alloc(max);
+	new = dma_resv_list_alloc(new_max);
 	if (!new)
 		return -ENOMEM;
+	new_max = new->max_fences;
 
 	/*
 	 * no need to bump fence refcounts, rcu_read access
@@ -205,7 +212,7 @@  int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)
 	 * references from the old struct are carried over to
 	 * the new.
 	 */
-	for (i = 0, j = 0, k = max; i < (old ? old->num_fences : 0); ++i) {
+	for (i = 0, j = 0, k = new_max; i < (old ? old->num_fences : 0); ++i) {
 		enum dma_resv_usage usage;
 		struct dma_fence *fence;
 
@@ -216,6 +223,7 @@  int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)
 			dma_resv_list_set(new, j++, fence, usage);
 	}
 	new->num_fences = j;
+	new->reserved_fences = num_fences;
 
 	/*
 	 * We are not changing the effective set of fences here so can
@@ -231,7 +239,7 @@  int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)
 		return 0;
 
 	/* Drop the references to the signaled fences */
-	for (i = k; i < max; ++i) {
+	for (i = k; i < new_max; ++i) {
 		struct dma_fence *fence;
 
 		fence = rcu_dereference_protected(new->table[i],
@@ -244,27 +252,25 @@  int dma_resv_reserve_fences(struct dma_resv *obj, unsigned int num_fences)
 }
 EXPORT_SYMBOL(dma_resv_reserve_fences);
 
-#ifdef CONFIG_DEBUG_MUTEXES
 /**
- * dma_resv_reset_max_fences - reset fences for debugging
+ * dma_resv_reset_reserved_fences - reset fence reservation
  * @obj: the dma_resv object to reset
  *
- * Reset the number of pre-reserved fence slots to test that drivers do
+ * Reset the number of pre-reserved fence slots to make sure that drivers do
  * correct slot allocation using dma_resv_reserve_fences(). See also
- * &dma_resv_list.max_fences.
+ * &dma_resv_list.reserved_fences.
  */
-void dma_resv_reset_max_fences(struct dma_resv *obj)
+void dma_resv_reset_reserved_fences(struct dma_resv *obj)
 {
 	struct dma_resv_list *fences = dma_resv_fences_list(obj);
 
 	dma_resv_assert_held(obj);
 
-	/* Test fence slot reservation */
+	/* reset fence slot reservation */
 	if (fences)
-		fences->max_fences = fences->num_fences;
+		fences->reserved_fences = 0;
 }
-EXPORT_SYMBOL(dma_resv_reset_max_fences);
-#endif
+EXPORT_SYMBOL(dma_resv_reset_reserved_fences);
 
 /**
  * dma_resv_add_fence - Add a fence to the dma_resv obj
@@ -293,6 +299,7 @@  void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence,
 	 */
 	WARN_ON(dma_fence_is_container(fence));
 
+retry:
 	fobj = dma_resv_fences_list(obj);
 	count = fobj->num_fences;
 
@@ -309,7 +316,17 @@  void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence,
 		}
 	}
 
-	BUG_ON(fobj->num_fences >= fobj->max_fences);
+	if (WARN_ON(fobj->num_fences == fobj->max_fences)) {
+		// try our best to avoid memory corruption
+		dma_resv_reserve_fences(obj, 1);
+		goto retry;
+	}
+	if (fobj->reserved_fences)
+		fobj->reserved_fences -= 1;
+#ifdef CONFIG_DEBUG_MUTEXES
+	else
+		WARN_ON(1); // missing fence slot allocation
+#else
 	count++;
 
 	dma_resv_list_set(fobj, i, fence, usage);
@@ -531,7 +548,6 @@  int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src)
 				dma_resv_iter_end(&cursor);
 				return -ENOMEM;
 			}
-			list->num_fences = 0;
 		}
 
 		dma_fence_get(f);
diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
index 8d0e34dad446..9b2d76484ff4 100644
--- a/include/linux/dma-resv.h
+++ b/include/linux/dma-resv.h
@@ -311,11 +311,7 @@  static inline bool dma_resv_iter_is_restarted(struct dma_resv_iter *cursor)
 #define dma_resv_held(obj) lockdep_is_held(&(obj)->lock.base)
 #define dma_resv_assert_held(obj) lockdep_assert_held(&(obj)->lock.base)
 
-#ifdef CONFIG_DEBUG_MUTEXES
-void dma_resv_reset_max_fences(struct dma_resv *obj);
-#else
-static inline void dma_resv_reset_max_fences(struct dma_resv *obj) {}
-#endif
+void dma_resv_reset_reserved_fences(struct dma_resv *obj);
 
 /**
  * dma_resv_lock - lock the reservation object
@@ -460,7 +456,7 @@  static inline struct ww_acquire_ctx *dma_resv_locking_ctx(struct dma_resv *obj)
  */
 static inline void dma_resv_unlock(struct dma_resv *obj)
 {
-	dma_resv_reset_max_fences(obj);
+	dma_resv_reset_reserved_fences(obj);
 	ww_mutex_unlock(&obj->lock);
 }