@@ -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,26 @@ 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)
+ new_max = old->num_fences + old->reserved_fences + num_fences;
+ if (new_max <= old->max_fences) {
+ old->reserved_fences += num_fences;
return 0;
- max = max(old->num_fences + num_fences, old->max_fences * 2);
+ }
+ new_max = max(new_max, old->max_fences * 2);
} else {
- max = max(4ul, roundup_pow_of_two(num_fences));
+ new_max = max(4ul, roundup_pow_of_two(num_fences));
}
- 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 +211,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 +222,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 + (old ? old->reserved_fences : 0);
/*
* We are not changing the effective set of fences here so can
@@ -231,7 +238,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 +251,29 @@ 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 */
- if (fences)
- fences->max_fences = fences->num_fences;
-}
-EXPORT_SYMBOL(dma_resv_reset_max_fences);
+ /* reset fence slot reservation */
+ if (fences) {
+#ifdef CONFIG_DEBUG_MUTEXES
+ WARN(fences->reserved_fences, "reserved too many fence slots");
#endif
+ fences->reserved_fences = 0;
+ }
+}
+EXPORT_SYMBOL(dma_resv_reset_reserved_fences);
/**
* dma_resv_add_fence - Add a fence to the dma_resv obj
@@ -294,8 +303,12 @@ void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence,
WARN_ON(dma_fence_is_container(fence));
fobj = dma_resv_fences_list(obj);
- count = fobj->num_fences;
+ /* dma_resv_reserve_fences() has not been called */
+ BUG_ON(!fobj->reserved_fences);
+ fobj->reserved_fences -= 1;
+
+ count = fobj->num_fences;
for (i = 0; i < count; ++i) {
enum dma_resv_usage old_usage;
@@ -308,8 +321,6 @@ void dma_resv_add_fence(struct dma_resv *obj, struct dma_fence *fence,
return;
}
}
-
- BUG_ON(fobj->num_fences >= fobj->max_fences);
count++;
dma_resv_list_set(fobj, i, fence, usage);
@@ -531,7 +542,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);
@@ -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);
}
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, the side-effect being reserve check are now stricter and need to be enabled all the time. This fixes issue where ttm_bo_mem_space's reserve is ignored in various amdgpu ioctl paths, and was causing fence lost leading to soft-lockup when VRAM is exhausted. 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, 35 insertions(+), 29 deletions(-)