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