xref: /linux/drivers/gpu/drm/xe/xe_validation.c (revision c0d6f52f9b62479d61f8cd4faf9fb2f8bce6e301)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2024 Intel Corporation
4  */
5 #include <drm/drm_exec.h>
6 #include <drm/drm_gem.h>
7 #include <drm/drm_gpuvm.h>
8 
9 #include "xe_assert.h"
10 #include "xe_validation.h"
11 
12 #ifdef CONFIG_DRM_XE_DEBUG
13 /**
14  * xe_validation_assert_exec() - Assert that the drm_exec pointer is suitable
15  * for validation.
16  * @xe: Pointer to the xe device.
17  * @exec: The drm_exec pointer to check.
18  * @obj: Pointer to the object subject to validation.
19  *
20  * NULL exec pointers are not allowed.
21  * For XE_VALIDATION_UNIMPLEMENTED, no checking.
22  * For XE_VLIDATION_OPT_OUT, check that the caller is a kunit test
23  * For XE_VALIDATION_UNSUPPORTED, check that the object subject to
24  * validation is a dma-buf, for which support for ww locking is
25  * not in place in the dma-buf layer.
26  */
27 void xe_validation_assert_exec(const struct xe_device *xe,
28 			       const struct drm_exec *exec,
29 			       const struct drm_gem_object *obj)
30 {
31 	xe_assert(xe, exec);
32 	if (IS_ERR(exec)) {
33 		switch (PTR_ERR(exec)) {
34 		case __XE_VAL_UNIMPLEMENTED:
35 			break;
36 		case __XE_VAL_UNSUPPORTED:
37 			xe_assert(xe, !!obj->dma_buf);
38 			break;
39 #if IS_ENABLED(CONFIG_KUNIT)
40 		case __XE_VAL_OPT_OUT:
41 			xe_assert(xe, current->kunit_test);
42 			break;
43 #endif
44 		default:
45 			xe_assert(xe, false);
46 		}
47 	}
48 }
49 #endif
50 
51 static int xe_validation_lock(struct xe_validation_ctx *ctx)
52 {
53 	struct xe_validation_device *val = ctx->val;
54 	int ret = 0;
55 
56 	if (ctx->val_flags.interruptible) {
57 		if (ctx->request_exclusive)
58 			ret = down_write_killable(&val->lock);
59 		else
60 			ret = down_read_interruptible(&val->lock);
61 	} else {
62 		if (ctx->request_exclusive)
63 			down_write(&val->lock);
64 		else
65 			down_read(&val->lock);
66 	}
67 
68 	if (!ret) {
69 		ctx->lock_held = true;
70 		ctx->lock_held_exclusive = ctx->request_exclusive;
71 	}
72 
73 	return ret;
74 }
75 
76 static int xe_validation_trylock(struct xe_validation_ctx *ctx)
77 {
78 	struct xe_validation_device *val = ctx->val;
79 	bool locked;
80 
81 	if (ctx->request_exclusive)
82 		locked = down_write_trylock(&val->lock);
83 	else
84 		locked = down_read_trylock(&val->lock);
85 
86 	if (locked) {
87 		ctx->lock_held = true;
88 		ctx->lock_held_exclusive = ctx->request_exclusive;
89 	}
90 
91 	return locked ? 0 : -EWOULDBLOCK;
92 }
93 
94 static void xe_validation_unlock(struct xe_validation_ctx *ctx)
95 {
96 	if (!ctx->lock_held)
97 		return;
98 
99 	if (ctx->lock_held_exclusive)
100 		up_write(&ctx->val->lock);
101 	else
102 		up_read(&ctx->val->lock);
103 
104 	ctx->lock_held = false;
105 }
106 
107 /**
108  * xe_validation_ctx_init() - Initialize an xe_validation_ctx
109  * @ctx: The xe_validation_ctx to initialize.
110  * @val: The xe_validation_device representing the validation domain.
111  * @exec: The struct drm_exec to use for the transaction. May be NULL.
112  * @flags: The flags to use for initialization.
113  *
114  * Initialize and lock a an xe_validation transaction using the validation domain
115  * represented by @val. Also initialize the drm_exec object forwarding parts of
116  * @flags to the drm_exec initialization. The @flags.exclusive flag should
117  * typically be set to false to avoid locking out other validators from the
118  * domain until an OOM is hit. For testing- or final attempt purposes it can,
119  * however, be set to true.
120  *
121  * Return: %0 on success, %-EINTR if interruptible initial locking failed with a
122  * signal pending. If @flags.no_block is set to true, a failed trylock
123  * returns %-EWOULDBLOCK.
124  */
125 int xe_validation_ctx_init(struct xe_validation_ctx *ctx, struct xe_validation_device *val,
126 			   struct drm_exec *exec, const struct xe_val_flags flags)
127 {
128 	int ret;
129 
130 	ctx->exec = exec;
131 	ctx->val = val;
132 	ctx->lock_held = false;
133 	ctx->lock_held_exclusive = false;
134 	ctx->request_exclusive = flags.exclusive;
135 	ctx->val_flags = flags;
136 	ctx->exec_flags = 0;
137 	ctx->nr = 0;
138 
139 	if (flags.no_block)
140 		ret = xe_validation_trylock(ctx);
141 	else
142 		ret = xe_validation_lock(ctx);
143 	if (ret)
144 		return ret;
145 
146 	if (exec) {
147 		if (flags.interruptible)
148 			ctx->exec_flags |= DRM_EXEC_INTERRUPTIBLE_WAIT;
149 		if (flags.exec_ignore_duplicates)
150 			ctx->exec_flags |= DRM_EXEC_IGNORE_DUPLICATES;
151 		drm_exec_init(exec, ctx->exec_flags, ctx->nr);
152 	}
153 
154 	return 0;
155 }
156 
157 #ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH
158 /*
159  * This abuses both drm_exec and ww_mutex internals and should be
160  * replaced by checking for -EDEADLK when we can make TTM
161  * stop converting -EDEADLK to -ENOMEM.
162  * An alternative is to not have exhaustive eviction with
163  * CONFIG_DEBUG_WW_MUTEX_SLOWPATH until that happens.
164  */
165 static bool xe_validation_contention_injected(struct drm_exec *exec)
166 {
167 	return !!exec->ticket.contending_lock;
168 }
169 
170 #else
171 
172 static bool xe_validation_contention_injected(struct drm_exec *exec)
173 {
174 	return false;
175 }
176 
177 #endif
178 
179 static bool __xe_validation_should_retry(struct xe_validation_ctx *ctx, int ret)
180 {
181 	if (ret == -ENOMEM &&
182 	    ((ctx->request_exclusive &&
183 	      xe_validation_contention_injected(ctx->exec)) ||
184 	     !ctx->request_exclusive)) {
185 		ctx->request_exclusive = true;
186 		return true;
187 	}
188 
189 	return false;
190 }
191 
192 /**
193  * xe_validation_exec_lock() - Perform drm_gpuvm_exec_lock within a validation
194  * transaction.
195  * @ctx: An uninitialized xe_validation_ctx.
196  * @vm_exec: An initialized struct vm_exec.
197  * @val: The validation domain.
198  *
199  * The drm_gpuvm_exec_lock() function internally initializes its drm_exec
200  * transaction and therefore doesn't lend itself very well to be using
201  * xe_validation_ctx_init(). Provide a helper that takes an uninitialized
202  * xe_validation_ctx and calls drm_gpuvm_exec_lock() with OOM retry.
203  *
204  * Return: %0 on success, negative error code on failure.
205  */
206 int xe_validation_exec_lock(struct xe_validation_ctx *ctx,
207 			    struct drm_gpuvm_exec *vm_exec,
208 			    struct xe_validation_device *val)
209 {
210 	int ret;
211 
212 	memset(ctx, 0, sizeof(*ctx));
213 	ctx->exec = &vm_exec->exec;
214 	ctx->exec_flags = vm_exec->flags;
215 	ctx->val = val;
216 	if (ctx->exec_flags & DRM_EXEC_INTERRUPTIBLE_WAIT)
217 		ctx->val_flags.interruptible = 1;
218 	if (ctx->exec_flags & DRM_EXEC_IGNORE_DUPLICATES)
219 		ctx->val_flags.exec_ignore_duplicates = 1;
220 retry:
221 	ret = xe_validation_lock(ctx);
222 	if (ret)
223 		return ret;
224 
225 	ret = drm_gpuvm_exec_lock(vm_exec);
226 	if (ret) {
227 		xe_validation_unlock(ctx);
228 		if (__xe_validation_should_retry(ctx, ret))
229 			goto retry;
230 	}
231 
232 	return ret;
233 }
234 
235 /**
236  * xe_validation_ctx_fini() - Finalize a validation transaction
237  * @ctx: The Validation transaction to finalize.
238  *
239  * Finalize a validation transaction and its related drm_exec transaction.
240  */
241 void xe_validation_ctx_fini(struct xe_validation_ctx *ctx)
242 {
243 	if (ctx->exec)
244 		drm_exec_fini(ctx->exec);
245 	xe_validation_unlock(ctx);
246 }
247 
248 /**
249  * xe_validation_should_retry() - Determine if a validation transaction should retry
250  * @ctx: The validation transaction.
251  * @ret: Pointer to a return value variable.
252  *
253  * Determines whether a validation transaction should retry based on the
254  * internal transaction state and the return value pointed to by @ret.
255  * If a validation should be retried, the transaction is prepared for that,
256  * and the validation locked might be re-locked in exclusive mode, and *@ret
257  * is set to %0. If the re-locking errors, typically due to interruptible
258  * locking with signal pending, *@ret is instead set to -EINTR and the
259  * function returns %false.
260  *
261  * Return: %true if validation should be retried, %false otherwise.
262  */
263 bool xe_validation_should_retry(struct xe_validation_ctx *ctx, int *ret)
264 {
265 	if (__xe_validation_should_retry(ctx, *ret)) {
266 		drm_exec_fini(ctx->exec);
267 		*ret = 0;
268 		if (ctx->request_exclusive != ctx->lock_held_exclusive) {
269 			xe_validation_unlock(ctx);
270 			*ret = xe_validation_lock(ctx);
271 		}
272 		drm_exec_init(ctx->exec, ctx->exec_flags, ctx->nr);
273 		return !*ret;
274 	}
275 
276 	return false;
277 }
278