projects/linux·blob master · refs

277 lines · 7.4 KiB

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 */
27void 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
51static 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
76static 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
94static 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 */
125int 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 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 */
165static bool xe_validation_contention_injected(struct drm_exec *exec)
166{
167 return !!drm_exec_ticket(exec)->contending_lock;
168}
169
170#else
171
172static bool xe_validation_contention_injected(struct drm_exec *exec)
173{
174 return false;
175}
176
177#endif
178
179static 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 */
206int 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;
220retry:
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 */
241void 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 */
263bool 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}