]>
Commit | Line | Data |
---|---|---|
dd08ebf6 MB |
1 | // SPDX-License-Identifier: MIT |
2 | /* | |
3 | * Copyright © 2021 Intel Corporation | |
4 | */ | |
5 | ||
c22a4ed0 | 6 | #include "xe_exec_queue.h" |
dd08ebf6 | 7 | |
ea9f879d LDM |
8 | #include <linux/nospec.h> |
9 | ||
dd08ebf6 MB |
10 | #include <drm/drm_device.h> |
11 | #include <drm/drm_file.h> | |
12 | #include <drm/xe_drm.h> | |
dd08ebf6 MB |
13 | |
14 | #include "xe_device.h" | |
15 | #include "xe_gt.h" | |
d2776564 | 16 | #include "xe_hw_engine_class_sysfs.h" |
7c51050b | 17 | #include "xe_hw_fence.h" |
dd08ebf6 MB |
18 | #include "xe_lrc.h" |
19 | #include "xe_macros.h" | |
20 | #include "xe_migrate.h" | |
21 | #include "xe_pm.h" | |
8ae8a2e8 | 22 | #include "xe_ring_ops_types.h" |
dd08ebf6 MB |
23 | #include "xe_trace.h" |
24 | #include "xe_vm.h" | |
25 | ||
d2776564 TU |
26 | enum xe_exec_queue_sched_prop { |
27 | XE_EXEC_QUEUE_JOB_TIMEOUT = 0, | |
28 | XE_EXEC_QUEUE_TIMESLICE = 1, | |
29 | XE_EXEC_QUEUE_PREEMPT_TIMEOUT = 2, | |
30 | XE_EXEC_QUEUE_SCHED_PROP_MAX = 3, | |
31 | }; | |
32 | ||
25ce7c50 BW |
33 | static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q, |
34 | u64 extensions, int ext_number, bool create); | |
35 | ||
6e144a7d BW |
36 | static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe, |
37 | struct xe_vm *vm, | |
38 | u32 logical_mask, | |
39 | u16 width, struct xe_hw_engine *hwe, | |
25ce7c50 | 40 | u32 flags, u64 extensions) |
dd08ebf6 | 41 | { |
9b9529ce | 42 | struct xe_exec_queue *q; |
dd08ebf6 | 43 | struct xe_gt *gt = hwe->gt; |
25ce7c50 | 44 | int err; |
dd08ebf6 | 45 | |
923e4238 DCS |
46 | /* only kernel queues can be permanent */ |
47 | XE_WARN_ON((flags & EXEC_QUEUE_FLAG_PERMANENT) && !(flags & EXEC_QUEUE_FLAG_KERNEL)); | |
48 | ||
a7a3d736 | 49 | q = kzalloc(struct_size(q, lrc, width), GFP_KERNEL); |
9b9529ce | 50 | if (!q) |
dd08ebf6 MB |
51 | return ERR_PTR(-ENOMEM); |
52 | ||
9b9529ce FD |
53 | kref_init(&q->refcount); |
54 | q->flags = flags; | |
55 | q->hwe = hwe; | |
56 | q->gt = gt; | |
9b9529ce FD |
57 | q->class = hwe->class; |
58 | q->width = width; | |
59 | q->logical_mask = logical_mask; | |
60 | q->fence_irq = >->fence_irq[hwe->class]; | |
61 | q->ring_ops = gt->ring_ops[hwe->class]; | |
62 | q->ops = gt->exec_queue_ops; | |
9b9529ce FD |
63 | INIT_LIST_HEAD(&q->compute.link); |
64 | INIT_LIST_HEAD(&q->multi_gt_link); | |
dd08ebf6 | 65 | |
eef55700 TU |
66 | q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us; |
67 | q->sched_props.preempt_timeout_us = | |
68 | hwe->eclass->sched_props.preempt_timeout_us; | |
6ae24344 BW |
69 | q->sched_props.job_timeout_ms = |
70 | hwe->eclass->sched_props.job_timeout_ms; | |
a8004af3 BW |
71 | if (q->flags & EXEC_QUEUE_FLAG_KERNEL && |
72 | q->flags & EXEC_QUEUE_FLAG_HIGH_PRIORITY) | |
73 | q->sched_props.priority = XE_EXEC_QUEUE_PRIORITY_KERNEL; | |
74 | else | |
75 | q->sched_props.priority = XE_EXEC_QUEUE_PRIORITY_NORMAL; | |
dd08ebf6 | 76 | |
25ce7c50 BW |
77 | if (extensions) { |
78 | /* | |
79 | * may set q->usm, must come before xe_lrc_init(), | |
80 | * may overwrite q->sched_props, must come before q->ops->init() | |
81 | */ | |
82 | err = exec_queue_user_extensions(xe, q, extensions, 0, true); | |
83 | if (err) { | |
84 | kfree(q); | |
85 | return ERR_PTR(err); | |
86 | } | |
87 | } | |
88 | ||
89 | if (vm) | |
90 | q->vm = xe_vm_get(vm); | |
91 | ||
9b9529ce FD |
92 | if (xe_exec_queue_is_parallel(q)) { |
93 | q->parallel.composite_fence_ctx = dma_fence_context_alloc(1); | |
94 | q->parallel.composite_fence_seqno = XE_FENCE_INITIAL_SEQNO; | |
dd08ebf6 | 95 | } |
dd08ebf6 | 96 | |
6e144a7d BW |
97 | return q; |
98 | } | |
99 | ||
100 | static void __xe_exec_queue_free(struct xe_exec_queue *q) | |
101 | { | |
102 | if (q->vm) | |
103 | xe_vm_put(q->vm); | |
104 | kfree(q); | |
105 | } | |
106 | ||
107 | static int __xe_exec_queue_init(struct xe_exec_queue *q) | |
108 | { | |
109 | struct xe_device *xe = gt_to_xe(q->gt); | |
110 | int i, err; | |
111 | ||
112 | for (i = 0; i < q->width; ++i) { | |
113 | err = xe_lrc_init(q->lrc + i, q->hwe, q, q->vm, SZ_16K); | |
dd08ebf6 MB |
114 | if (err) |
115 | goto err_lrc; | |
116 | } | |
117 | ||
9b9529ce | 118 | err = q->ops->init(q); |
dd08ebf6 MB |
119 | if (err) |
120 | goto err_lrc; | |
121 | ||
6aa26f6e MA |
122 | /* |
123 | * Normally the user vm holds an rpm ref to keep the device | |
124 | * awake, and the context holds a ref for the vm, however for | |
fcf98d68 MA |
125 | * some engines we use the kernels migrate vm underneath which offers no |
126 | * such rpm ref, or we lack a vm. Make sure we keep a ref here, so we | |
127 | * can perform GuC CT actions when needed. Caller is expected to have | |
128 | * already grabbed the rpm ref outside any sensitive locks. | |
6aa26f6e | 129 | */ |
6e144a7d | 130 | if (!(q->flags & EXEC_QUEUE_FLAG_PERMANENT) && (q->flags & EXEC_QUEUE_FLAG_VM || !q->vm)) |
6aa26f6e MA |
131 | drm_WARN_ON(&xe->drm, !xe_device_mem_access_get_if_ongoing(xe)); |
132 | ||
6e144a7d | 133 | return 0; |
dd08ebf6 MB |
134 | |
135 | err_lrc: | |
136 | for (i = i - 1; i >= 0; --i) | |
9b9529ce | 137 | xe_lrc_finish(q->lrc + i); |
6e144a7d | 138 | return err; |
dd08ebf6 MB |
139 | } |
140 | ||
9b9529ce FD |
141 | struct xe_exec_queue *xe_exec_queue_create(struct xe_device *xe, struct xe_vm *vm, |
142 | u32 logical_mask, u16 width, | |
25ce7c50 BW |
143 | struct xe_hw_engine *hwe, u32 flags, |
144 | u64 extensions) | |
dd08ebf6 | 145 | { |
9b9529ce | 146 | struct xe_exec_queue *q; |
dd08ebf6 MB |
147 | int err; |
148 | ||
25ce7c50 BW |
149 | q = __xe_exec_queue_alloc(xe, vm, logical_mask, width, hwe, flags, |
150 | extensions); | |
6e144a7d BW |
151 | if (IS_ERR(q)) |
152 | return q; | |
153 | ||
dd08ebf6 | 154 | if (vm) { |
d00e9cc2 | 155 | err = xe_vm_lock(vm, true); |
dd08ebf6 | 156 | if (err) |
6e144a7d | 157 | goto err_post_alloc; |
dd08ebf6 | 158 | } |
6e144a7d BW |
159 | |
160 | err = __xe_exec_queue_init(q); | |
dd08ebf6 | 161 | if (vm) |
d00e9cc2 | 162 | xe_vm_unlock(vm); |
6e144a7d BW |
163 | if (err) |
164 | goto err_post_alloc; | |
dd08ebf6 | 165 | |
9b9529ce | 166 | return q; |
6e144a7d BW |
167 | |
168 | err_post_alloc: | |
169 | __xe_exec_queue_free(q); | |
170 | return ERR_PTR(err); | |
dd08ebf6 MB |
171 | } |
172 | ||
9b9529ce FD |
173 | struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe_gt *gt, |
174 | struct xe_vm *vm, | |
175 | enum xe_engine_class class, u32 flags) | |
dd08ebf6 MB |
176 | { |
177 | struct xe_hw_engine *hwe, *hwe0 = NULL; | |
178 | enum xe_hw_engine_id id; | |
179 | u32 logical_mask = 0; | |
180 | ||
181 | for_each_hw_engine(hwe, gt, id) { | |
182 | if (xe_hw_engine_is_reserved(hwe)) | |
183 | continue; | |
184 | ||
185 | if (hwe->class == class) { | |
186 | logical_mask |= BIT(hwe->logical_instance); | |
187 | if (!hwe0) | |
188 | hwe0 = hwe; | |
189 | } | |
190 | } | |
191 | ||
192 | if (!logical_mask) | |
193 | return ERR_PTR(-ENODEV); | |
194 | ||
25ce7c50 | 195 | return xe_exec_queue_create(xe, vm, logical_mask, 1, hwe0, flags, 0); |
dd08ebf6 MB |
196 | } |
197 | ||
9b9529ce | 198 | void xe_exec_queue_destroy(struct kref *ref) |
dd08ebf6 | 199 | { |
9b9529ce FD |
200 | struct xe_exec_queue *q = container_of(ref, struct xe_exec_queue, refcount); |
201 | struct xe_exec_queue *eq, *next; | |
dd08ebf6 | 202 | |
e669f10c | 203 | xe_exec_queue_last_fence_put_unlocked(q); |
9b9529ce FD |
204 | if (!(q->flags & EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD)) { |
205 | list_for_each_entry_safe(eq, next, &q->multi_gt_list, | |
dd08ebf6 | 206 | multi_gt_link) |
9b9529ce | 207 | xe_exec_queue_put(eq); |
dd08ebf6 MB |
208 | } |
209 | ||
9b9529ce | 210 | q->ops->fini(q); |
dd08ebf6 MB |
211 | } |
212 | ||
9b9529ce | 213 | void xe_exec_queue_fini(struct xe_exec_queue *q) |
dd08ebf6 MB |
214 | { |
215 | int i; | |
216 | ||
9b9529ce FD |
217 | for (i = 0; i < q->width; ++i) |
218 | xe_lrc_finish(q->lrc + i); | |
fcf98d68 MA |
219 | if (!(q->flags & EXEC_QUEUE_FLAG_PERMANENT) && (q->flags & EXEC_QUEUE_FLAG_VM || !q->vm)) |
220 | xe_device_mem_access_put(gt_to_xe(q->gt)); | |
6e144a7d | 221 | __xe_exec_queue_free(q); |
dd08ebf6 MB |
222 | } |
223 | ||
0b1d1473 DCS |
224 | void xe_exec_queue_assign_name(struct xe_exec_queue *q, u32 instance) |
225 | { | |
226 | switch (q->class) { | |
227 | case XE_ENGINE_CLASS_RENDER: | |
228 | sprintf(q->name, "rcs%d", instance); | |
229 | break; | |
230 | case XE_ENGINE_CLASS_VIDEO_DECODE: | |
231 | sprintf(q->name, "vcs%d", instance); | |
232 | break; | |
233 | case XE_ENGINE_CLASS_VIDEO_ENHANCE: | |
234 | sprintf(q->name, "vecs%d", instance); | |
235 | break; | |
236 | case XE_ENGINE_CLASS_COPY: | |
237 | sprintf(q->name, "bcs%d", instance); | |
238 | break; | |
239 | case XE_ENGINE_CLASS_COMPUTE: | |
240 | sprintf(q->name, "ccs%d", instance); | |
241 | break; | |
29654910 DCS |
242 | case XE_ENGINE_CLASS_OTHER: |
243 | sprintf(q->name, "gsccs%d", instance); | |
244 | break; | |
0b1d1473 DCS |
245 | default: |
246 | XE_WARN_ON(q->class); | |
247 | } | |
248 | } | |
249 | ||
9b9529ce | 250 | struct xe_exec_queue *xe_exec_queue_lookup(struct xe_file *xef, u32 id) |
dd08ebf6 | 251 | { |
9b9529ce | 252 | struct xe_exec_queue *q; |
dd08ebf6 | 253 | |
9b9529ce FD |
254 | mutex_lock(&xef->exec_queue.lock); |
255 | q = xa_load(&xef->exec_queue.xa, id); | |
256 | if (q) | |
257 | xe_exec_queue_get(q); | |
258 | mutex_unlock(&xef->exec_queue.lock); | |
dd08ebf6 | 259 | |
9b9529ce | 260 | return q; |
dd08ebf6 MB |
261 | } |
262 | ||
9b9529ce FD |
263 | enum xe_exec_queue_priority |
264 | xe_exec_queue_device_get_max_priority(struct xe_device *xe) | |
ef5e3c2f | 265 | { |
9b9529ce FD |
266 | return capable(CAP_SYS_NICE) ? XE_EXEC_QUEUE_PRIORITY_HIGH : |
267 | XE_EXEC_QUEUE_PRIORITY_NORMAL; | |
ef5e3c2f JRS |
268 | } |
269 | ||
9b9529ce FD |
270 | static int exec_queue_set_priority(struct xe_device *xe, struct xe_exec_queue *q, |
271 | u64 value, bool create) | |
dd08ebf6 | 272 | { |
9b9529ce | 273 | if (XE_IOCTL_DBG(xe, value > XE_EXEC_QUEUE_PRIORITY_HIGH)) |
dd08ebf6 MB |
274 | return -EINVAL; |
275 | ||
9b9529ce | 276 | if (XE_IOCTL_DBG(xe, value > xe_exec_queue_device_get_max_priority(xe))) |
dd08ebf6 MB |
277 | return -EPERM; |
278 | ||
25ce7c50 BW |
279 | if (!create) |
280 | return q->ops->set_priority(q, value); | |
281 | ||
282 | q->sched_props.priority = value; | |
283 | return 0; | |
dd08ebf6 MB |
284 | } |
285 | ||
d2776564 TU |
286 | static bool xe_exec_queue_enforce_schedule_limit(void) |
287 | { | |
288 | #if IS_ENABLED(CONFIG_DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT) | |
289 | return true; | |
290 | #else | |
291 | return !capable(CAP_SYS_NICE); | |
292 | #endif | |
293 | } | |
294 | ||
295 | static void | |
296 | xe_exec_queue_get_prop_minmax(struct xe_hw_engine_class_intf *eclass, | |
297 | enum xe_exec_queue_sched_prop prop, | |
298 | u32 *min, u32 *max) | |
299 | { | |
300 | switch (prop) { | |
301 | case XE_EXEC_QUEUE_JOB_TIMEOUT: | |
302 | *min = eclass->sched_props.job_timeout_min; | |
303 | *max = eclass->sched_props.job_timeout_max; | |
304 | break; | |
305 | case XE_EXEC_QUEUE_TIMESLICE: | |
306 | *min = eclass->sched_props.timeslice_min; | |
307 | *max = eclass->sched_props.timeslice_max; | |
308 | break; | |
309 | case XE_EXEC_QUEUE_PREEMPT_TIMEOUT: | |
310 | *min = eclass->sched_props.preempt_timeout_min; | |
311 | *max = eclass->sched_props.preempt_timeout_max; | |
312 | break; | |
313 | default: | |
314 | break; | |
315 | } | |
316 | #if IS_ENABLED(CONFIG_DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT) | |
317 | if (capable(CAP_SYS_NICE)) { | |
318 | switch (prop) { | |
319 | case XE_EXEC_QUEUE_JOB_TIMEOUT: | |
320 | *min = XE_HW_ENGINE_JOB_TIMEOUT_MIN; | |
321 | *max = XE_HW_ENGINE_JOB_TIMEOUT_MAX; | |
322 | break; | |
323 | case XE_EXEC_QUEUE_TIMESLICE: | |
324 | *min = XE_HW_ENGINE_TIMESLICE_MIN; | |
325 | *max = XE_HW_ENGINE_TIMESLICE_MAX; | |
326 | break; | |
327 | case XE_EXEC_QUEUE_PREEMPT_TIMEOUT: | |
328 | *min = XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN; | |
329 | *max = XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX; | |
330 | break; | |
331 | default: | |
332 | break; | |
333 | } | |
334 | } | |
335 | #endif | |
336 | } | |
337 | ||
9b9529ce FD |
338 | static int exec_queue_set_timeslice(struct xe_device *xe, struct xe_exec_queue *q, |
339 | u64 value, bool create) | |
dd08ebf6 | 340 | { |
d2776564 TU |
341 | u32 min = 0, max = 0; |
342 | ||
343 | xe_exec_queue_get_prop_minmax(q->hwe->eclass, | |
344 | XE_EXEC_QUEUE_TIMESLICE, &min, &max); | |
345 | ||
346 | if (xe_exec_queue_enforce_schedule_limit() && | |
347 | !xe_hw_engine_timeout_in_range(value, min, max)) | |
348 | return -EINVAL; | |
dd08ebf6 | 349 | |
25ce7c50 BW |
350 | if (!create) |
351 | return q->ops->set_timeslice(q, value); | |
352 | ||
353 | q->sched_props.timeslice_us = value; | |
354 | return 0; | |
dd08ebf6 MB |
355 | } |
356 | ||
9b9529ce FD |
357 | typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe, |
358 | struct xe_exec_queue *q, | |
359 | u64 value, bool create); | |
360 | ||
361 | static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = { | |
d5dc73db FD |
362 | [DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY] = exec_queue_set_priority, |
363 | [DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE] = exec_queue_set_timeslice, | |
dd08ebf6 MB |
364 | }; |
365 | ||
9b9529ce FD |
366 | static int exec_queue_user_ext_set_property(struct xe_device *xe, |
367 | struct xe_exec_queue *q, | |
368 | u64 extension, | |
369 | bool create) | |
dd08ebf6 MB |
370 | { |
371 | u64 __user *address = u64_to_user_ptr(extension); | |
5dc079d1 | 372 | struct drm_xe_ext_set_property ext; |
dd08ebf6 MB |
373 | int err; |
374 | u32 idx; | |
375 | ||
376 | err = __copy_from_user(&ext, address, sizeof(ext)); | |
b8c1ba83 | 377 | if (XE_IOCTL_DBG(xe, err)) |
dd08ebf6 MB |
378 | return -EFAULT; |
379 | ||
b8c1ba83 | 380 | if (XE_IOCTL_DBG(xe, ext.property >= |
9b9529ce | 381 | ARRAY_SIZE(exec_queue_set_property_funcs)) || |
84a1ed5e FD |
382 | XE_IOCTL_DBG(xe, ext.pad) || |
383 | XE_IOCTL_DBG(xe, ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY && | |
384 | ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE)) | |
dd08ebf6 MB |
385 | return -EINVAL; |
386 | ||
9b9529ce | 387 | idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs)); |
f1a9abc0 TH |
388 | if (!exec_queue_set_property_funcs[idx]) |
389 | return -EINVAL; | |
390 | ||
9b9529ce | 391 | return exec_queue_set_property_funcs[idx](xe, q, ext.value, create); |
dd08ebf6 MB |
392 | } |
393 | ||
9b9529ce FD |
394 | typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe, |
395 | struct xe_exec_queue *q, | |
396 | u64 extension, | |
397 | bool create); | |
dd08ebf6 | 398 | |
9b9529ce | 399 | static const xe_exec_queue_set_property_fn exec_queue_user_extension_funcs[] = { |
d5dc73db | 400 | [DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property, |
dd08ebf6 MB |
401 | }; |
402 | ||
403 | #define MAX_USER_EXTENSIONS 16 | |
9b9529ce FD |
404 | static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q, |
405 | u64 extensions, int ext_number, bool create) | |
dd08ebf6 MB |
406 | { |
407 | u64 __user *address = u64_to_user_ptr(extensions); | |
7e9337c2 | 408 | struct drm_xe_user_extension ext; |
dd08ebf6 MB |
409 | int err; |
410 | u32 idx; | |
411 | ||
b8c1ba83 | 412 | if (XE_IOCTL_DBG(xe, ext_number >= MAX_USER_EXTENSIONS)) |
dd08ebf6 MB |
413 | return -E2BIG; |
414 | ||
415 | err = __copy_from_user(&ext, address, sizeof(ext)); | |
b8c1ba83 | 416 | if (XE_IOCTL_DBG(xe, err)) |
dd08ebf6 MB |
417 | return -EFAULT; |
418 | ||
b8c1ba83 FD |
419 | if (XE_IOCTL_DBG(xe, ext.pad) || |
420 | XE_IOCTL_DBG(xe, ext.name >= | |
9b9529ce | 421 | ARRAY_SIZE(exec_queue_user_extension_funcs))) |
dd08ebf6 MB |
422 | return -EINVAL; |
423 | ||
424 | idx = array_index_nospec(ext.name, | |
9b9529ce FD |
425 | ARRAY_SIZE(exec_queue_user_extension_funcs)); |
426 | err = exec_queue_user_extension_funcs[idx](xe, q, extensions, create); | |
b8c1ba83 | 427 | if (XE_IOCTL_DBG(xe, err)) |
dd08ebf6 MB |
428 | return err; |
429 | ||
430 | if (ext.next_extension) | |
9b9529ce | 431 | return exec_queue_user_extensions(xe, q, ext.next_extension, |
dd08ebf6 MB |
432 | ++ext_number, create); |
433 | ||
434 | return 0; | |
435 | } | |
436 | ||
437 | static const enum xe_engine_class user_to_xe_engine_class[] = { | |
438 | [DRM_XE_ENGINE_CLASS_RENDER] = XE_ENGINE_CLASS_RENDER, | |
439 | [DRM_XE_ENGINE_CLASS_COPY] = XE_ENGINE_CLASS_COPY, | |
440 | [DRM_XE_ENGINE_CLASS_VIDEO_DECODE] = XE_ENGINE_CLASS_VIDEO_DECODE, | |
441 | [DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE] = XE_ENGINE_CLASS_VIDEO_ENHANCE, | |
442 | [DRM_XE_ENGINE_CLASS_COMPUTE] = XE_ENGINE_CLASS_COMPUTE, | |
443 | }; | |
444 | ||
445 | static struct xe_hw_engine * | |
446 | find_hw_engine(struct xe_device *xe, | |
447 | struct drm_xe_engine_class_instance eci) | |
448 | { | |
449 | u32 idx; | |
450 | ||
451 | if (eci.engine_class > ARRAY_SIZE(user_to_xe_engine_class)) | |
452 | return NULL; | |
453 | ||
2a6d871b | 454 | if (eci.gt_id >= xe->info.gt_count) |
dd08ebf6 MB |
455 | return NULL; |
456 | ||
457 | idx = array_index_nospec(eci.engine_class, | |
458 | ARRAY_SIZE(user_to_xe_engine_class)); | |
459 | ||
460 | return xe_gt_hw_engine(xe_device_get_gt(xe, eci.gt_id), | |
461 | user_to_xe_engine_class[idx], | |
462 | eci.engine_instance, true); | |
463 | } | |
464 | ||
9b9529ce FD |
465 | static u32 bind_exec_queue_logical_mask(struct xe_device *xe, struct xe_gt *gt, |
466 | struct drm_xe_engine_class_instance *eci, | |
467 | u16 width, u16 num_placements) | |
dd08ebf6 MB |
468 | { |
469 | struct xe_hw_engine *hwe; | |
470 | enum xe_hw_engine_id id; | |
471 | u32 logical_mask = 0; | |
472 | ||
b8c1ba83 | 473 | if (XE_IOCTL_DBG(xe, width != 1)) |
dd08ebf6 | 474 | return 0; |
b8c1ba83 | 475 | if (XE_IOCTL_DBG(xe, num_placements != 1)) |
dd08ebf6 | 476 | return 0; |
b8c1ba83 | 477 | if (XE_IOCTL_DBG(xe, eci[0].engine_instance != 0)) |
dd08ebf6 MB |
478 | return 0; |
479 | ||
480 | eci[0].engine_class = DRM_XE_ENGINE_CLASS_COPY; | |
481 | ||
482 | for_each_hw_engine(hwe, gt, id) { | |
483 | if (xe_hw_engine_is_reserved(hwe)) | |
484 | continue; | |
485 | ||
486 | if (hwe->class == | |
487 | user_to_xe_engine_class[DRM_XE_ENGINE_CLASS_COPY]) | |
488 | logical_mask |= BIT(hwe->logical_instance); | |
489 | } | |
490 | ||
491 | return logical_mask; | |
492 | } | |
493 | ||
494 | static u32 calc_validate_logical_mask(struct xe_device *xe, struct xe_gt *gt, | |
495 | struct drm_xe_engine_class_instance *eci, | |
496 | u16 width, u16 num_placements) | |
497 | { | |
498 | int len = width * num_placements; | |
499 | int i, j, n; | |
500 | u16 class; | |
501 | u16 gt_id; | |
502 | u32 return_mask = 0, prev_mask; | |
503 | ||
c4991ee0 | 504 | if (XE_IOCTL_DBG(xe, !xe_device_uc_enabled(xe) && |
dd08ebf6 MB |
505 | len > 1)) |
506 | return 0; | |
507 | ||
508 | for (i = 0; i < width; ++i) { | |
509 | u32 current_mask = 0; | |
510 | ||
511 | for (j = 0; j < num_placements; ++j) { | |
512 | struct xe_hw_engine *hwe; | |
513 | ||
514 | n = j * width + i; | |
515 | ||
516 | hwe = find_hw_engine(xe, eci[n]); | |
b8c1ba83 | 517 | if (XE_IOCTL_DBG(xe, !hwe)) |
dd08ebf6 MB |
518 | return 0; |
519 | ||
b8c1ba83 | 520 | if (XE_IOCTL_DBG(xe, xe_hw_engine_is_reserved(hwe))) |
dd08ebf6 MB |
521 | return 0; |
522 | ||
b8c1ba83 FD |
523 | if (XE_IOCTL_DBG(xe, n && eci[n].gt_id != gt_id) || |
524 | XE_IOCTL_DBG(xe, n && eci[n].engine_class != class)) | |
dd08ebf6 MB |
525 | return 0; |
526 | ||
527 | class = eci[n].engine_class; | |
528 | gt_id = eci[n].gt_id; | |
529 | ||
530 | if (width == 1 || !i) | |
531 | return_mask |= BIT(eci[n].engine_instance); | |
532 | current_mask |= BIT(eci[n].engine_instance); | |
533 | } | |
534 | ||
535 | /* Parallel submissions must be logically contiguous */ | |
b8c1ba83 | 536 | if (i && XE_IOCTL_DBG(xe, current_mask != prev_mask << 1)) |
dd08ebf6 MB |
537 | return 0; |
538 | ||
539 | prev_mask = current_mask; | |
540 | } | |
541 | ||
542 | return return_mask; | |
543 | } | |
544 | ||
9b9529ce FD |
545 | int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, |
546 | struct drm_file *file) | |
dd08ebf6 MB |
547 | { |
548 | struct xe_device *xe = to_xe_device(dev); | |
549 | struct xe_file *xef = to_xe_file(file); | |
9b9529ce | 550 | struct drm_xe_exec_queue_create *args = data; |
dd08ebf6 MB |
551 | struct drm_xe_engine_class_instance eci[XE_HW_ENGINE_MAX_INSTANCE]; |
552 | struct drm_xe_engine_class_instance __user *user_eci = | |
553 | u64_to_user_ptr(args->instances); | |
554 | struct xe_hw_engine *hwe; | |
555 | struct xe_vm *vm, *migrate_vm; | |
556 | struct xe_gt *gt; | |
9b9529ce | 557 | struct xe_exec_queue *q = NULL; |
dd08ebf6 MB |
558 | u32 logical_mask; |
559 | u32 id; | |
7f38e1e1 | 560 | u32 len; |
dd08ebf6 MB |
561 | int err; |
562 | ||
b8c1ba83 FD |
563 | if (XE_IOCTL_DBG(xe, args->flags) || |
564 | XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1])) | |
dd08ebf6 MB |
565 | return -EINVAL; |
566 | ||
567 | len = args->width * args->num_placements; | |
b8c1ba83 | 568 | if (XE_IOCTL_DBG(xe, !len || len > XE_HW_ENGINE_MAX_INSTANCE)) |
dd08ebf6 MB |
569 | return -EINVAL; |
570 | ||
571 | err = __copy_from_user(eci, user_eci, | |
572 | sizeof(struct drm_xe_engine_class_instance) * | |
573 | len); | |
b8c1ba83 | 574 | if (XE_IOCTL_DBG(xe, err)) |
dd08ebf6 MB |
575 | return -EFAULT; |
576 | ||
2a6d871b | 577 | if (XE_IOCTL_DBG(xe, eci[0].gt_id >= xe->info.gt_count)) |
763931d2 | 578 | return -EINVAL; |
dd08ebf6 | 579 | |
d3d76739 | 580 | if (eci[0].engine_class == DRM_XE_ENGINE_CLASS_VM_BIND) { |
dd08ebf6 | 581 | for_each_gt(gt, xe, id) { |
9b9529ce | 582 | struct xe_exec_queue *new; |
25ce7c50 | 583 | u32 flags; |
dd08ebf6 MB |
584 | |
585 | if (xe_gt_is_media_type(gt)) | |
586 | continue; | |
587 | ||
588 | eci[0].gt_id = gt->info.id; | |
9b9529ce FD |
589 | logical_mask = bind_exec_queue_logical_mask(xe, gt, eci, |
590 | args->width, | |
591 | args->num_placements); | |
b8c1ba83 | 592 | if (XE_IOCTL_DBG(xe, !logical_mask)) |
67f2f0d7 | 593 | return -EINVAL; |
dd08ebf6 MB |
594 | |
595 | hwe = find_hw_engine(xe, eci[0]); | |
b8c1ba83 | 596 | if (XE_IOCTL_DBG(xe, !hwe)) |
67f2f0d7 | 597 | return -EINVAL; |
dd08ebf6 | 598 | |
6aa26f6e MA |
599 | /* The migration vm doesn't hold rpm ref */ |
600 | xe_device_mem_access_get(xe); | |
601 | ||
f1a9abc0 | 602 | flags = EXEC_QUEUE_FLAG_VM | (id ? EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD : 0); |
25ce7c50 | 603 | |
08dea767 | 604 | migrate_vm = xe_migrate_get_vm(gt_to_tile(gt)->migrate); |
9b9529ce | 605 | new = xe_exec_queue_create(xe, migrate_vm, logical_mask, |
25ce7c50 BW |
606 | args->width, hwe, flags, |
607 | args->extensions); | |
6aa26f6e MA |
608 | |
609 | xe_device_mem_access_put(xe); /* now held by engine */ | |
610 | ||
dd08ebf6 MB |
611 | xe_vm_put(migrate_vm); |
612 | if (IS_ERR(new)) { | |
613 | err = PTR_ERR(new); | |
9b9529ce FD |
614 | if (q) |
615 | goto put_exec_queue; | |
67f2f0d7 | 616 | return err; |
dd08ebf6 MB |
617 | } |
618 | if (id == 0) | |
9b9529ce | 619 | q = new; |
dd08ebf6 MB |
620 | else |
621 | list_add_tail(&new->multi_gt_list, | |
9b9529ce | 622 | &q->multi_gt_link); |
dd08ebf6 MB |
623 | } |
624 | } else { | |
625 | gt = xe_device_get_gt(xe, eci[0].gt_id); | |
626 | logical_mask = calc_validate_logical_mask(xe, gt, eci, | |
627 | args->width, | |
628 | args->num_placements); | |
b8c1ba83 | 629 | if (XE_IOCTL_DBG(xe, !logical_mask)) |
67f2f0d7 | 630 | return -EINVAL; |
dd08ebf6 MB |
631 | |
632 | hwe = find_hw_engine(xe, eci[0]); | |
b8c1ba83 | 633 | if (XE_IOCTL_DBG(xe, !hwe)) |
67f2f0d7 | 634 | return -EINVAL; |
dd08ebf6 MB |
635 | |
636 | vm = xe_vm_lookup(xef, args->vm_id); | |
b8c1ba83 | 637 | if (XE_IOCTL_DBG(xe, !vm)) |
67f2f0d7 | 638 | return -ENOENT; |
dd08ebf6 | 639 | |
9d858b69 MB |
640 | err = down_read_interruptible(&vm->lock); |
641 | if (err) { | |
642 | xe_vm_put(vm); | |
643 | return err; | |
644 | } | |
645 | ||
b8c1ba83 | 646 | if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) { |
9d858b69 MB |
647 | up_read(&vm->lock); |
648 | xe_vm_put(vm); | |
649 | return -ENOENT; | |
650 | } | |
651 | ||
9b9529ce | 652 | q = xe_exec_queue_create(xe, vm, logical_mask, |
f1a9abc0 | 653 | args->width, hwe, 0, |
25ce7c50 | 654 | args->extensions); |
9d858b69 | 655 | up_read(&vm->lock); |
dd08ebf6 | 656 | xe_vm_put(vm); |
9b9529ce FD |
657 | if (IS_ERR(q)) |
658 | return PTR_ERR(q); | |
e05c6c97 | 659 | |
fdb6a053 | 660 | if (xe_vm_in_preempt_fence_mode(vm)) { |
e05c6c97 MB |
661 | q->compute.context = dma_fence_context_alloc(1); |
662 | spin_lock_init(&q->compute.lock); | |
663 | ||
664 | err = xe_vm_add_compute_exec_queue(vm, q); | |
665 | if (XE_IOCTL_DBG(xe, err)) | |
666 | goto put_exec_queue; | |
667 | } | |
dd08ebf6 MB |
668 | } |
669 | ||
9b9529ce FD |
670 | mutex_lock(&xef->exec_queue.lock); |
671 | err = xa_alloc(&xef->exec_queue.xa, &id, q, xa_limit_32b, GFP_KERNEL); | |
672 | mutex_unlock(&xef->exec_queue.lock); | |
dd08ebf6 | 673 | if (err) |
e05c6c97 | 674 | goto kill_exec_queue; |
dd08ebf6 | 675 | |
9b9529ce | 676 | args->exec_queue_id = id; |
dd08ebf6 MB |
677 | |
678 | return 0; | |
679 | ||
e05c6c97 | 680 | kill_exec_queue: |
9b9529ce | 681 | xe_exec_queue_kill(q); |
e05c6c97 | 682 | put_exec_queue: |
9b9529ce | 683 | xe_exec_queue_put(q); |
dd08ebf6 MB |
684 | return err; |
685 | } | |
686 | ||
9b9529ce FD |
687 | int xe_exec_queue_get_property_ioctl(struct drm_device *dev, void *data, |
688 | struct drm_file *file) | |
19431b02 JRS |
689 | { |
690 | struct xe_device *xe = to_xe_device(dev); | |
691 | struct xe_file *xef = to_xe_file(file); | |
9b9529ce FD |
692 | struct drm_xe_exec_queue_get_property *args = data; |
693 | struct xe_exec_queue *q; | |
5db4afe1 | 694 | int ret; |
19431b02 | 695 | |
b8c1ba83 | 696 | if (XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1])) |
1799c761 CS |
697 | return -EINVAL; |
698 | ||
9b9529ce FD |
699 | q = xe_exec_queue_lookup(xef, args->exec_queue_id); |
700 | if (XE_IOCTL_DBG(xe, !q)) | |
19431b02 JRS |
701 | return -ENOENT; |
702 | ||
703 | switch (args->property) { | |
d5dc73db | 704 | case DRM_XE_EXEC_QUEUE_GET_PROPERTY_BAN: |
9b9529ce | 705 | args->value = !!(q->flags & EXEC_QUEUE_FLAG_BANNED); |
5db4afe1 | 706 | ret = 0; |
19431b02 JRS |
707 | break; |
708 | default: | |
5db4afe1 | 709 | ret = -EINVAL; |
19431b02 JRS |
710 | } |
711 | ||
9b9529ce | 712 | xe_exec_queue_put(q); |
5db4afe1 MK |
713 | |
714 | return ret; | |
19431b02 JRS |
715 | } |
716 | ||
8ae8a2e8 | 717 | /** |
9b9529ce FD |
718 | * xe_exec_queue_is_lr() - Whether an exec_queue is long-running |
719 | * @q: The exec_queue | |
8ae8a2e8 | 720 | * |
9b9529ce | 721 | * Return: True if the exec_queue is long-running, false otherwise. |
8ae8a2e8 | 722 | */ |
9b9529ce | 723 | bool xe_exec_queue_is_lr(struct xe_exec_queue *q) |
8ae8a2e8 | 724 | { |
fdb6a053 | 725 | return q->vm && xe_vm_in_lr_mode(q->vm) && |
9b9529ce | 726 | !(q->flags & EXEC_QUEUE_FLAG_VM); |
8ae8a2e8 MB |
727 | } |
728 | ||
9b9529ce | 729 | static s32 xe_exec_queue_num_job_inflight(struct xe_exec_queue *q) |
8ae8a2e8 | 730 | { |
9b9529ce | 731 | return q->lrc->fence_ctx.next_seqno - xe_lrc_seqno(q->lrc) - 1; |
8ae8a2e8 MB |
732 | } |
733 | ||
734 | /** | |
9b9529ce FD |
735 | * xe_exec_queue_ring_full() - Whether an exec_queue's ring is full |
736 | * @q: The exec_queue | |
8ae8a2e8 | 737 | * |
9b9529ce | 738 | * Return: True if the exec_queue's ring is full, false otherwise. |
8ae8a2e8 | 739 | */ |
9b9529ce | 740 | bool xe_exec_queue_ring_full(struct xe_exec_queue *q) |
8ae8a2e8 | 741 | { |
9b9529ce | 742 | struct xe_lrc *lrc = q->lrc; |
8ae8a2e8 MB |
743 | s32 max_job = lrc->ring.size / MAX_JOB_SIZE_BYTES; |
744 | ||
9b9529ce | 745 | return xe_exec_queue_num_job_inflight(q) >= max_job; |
8ae8a2e8 MB |
746 | } |
747 | ||
155c9165 | 748 | /** |
9b9529ce FD |
749 | * xe_exec_queue_is_idle() - Whether an exec_queue is idle. |
750 | * @q: The exec_queue | |
155c9165 TH |
751 | * |
752 | * FIXME: Need to determine what to use as the short-lived | |
9b9529ce | 753 | * timeline lock for the exec_queues, so that the return value |
155c9165 TH |
754 | * of this function becomes more than just an advisory |
755 | * snapshot in time. The timeline lock must protect the | |
9b9529ce | 756 | * seqno from racing submissions on the same exec_queue. |
155c9165 TH |
757 | * Typically vm->resv, but user-created timeline locks use the migrate vm |
758 | * and never grabs the migrate vm->resv so we have a race there. | |
759 | * | |
9b9529ce | 760 | * Return: True if the exec_queue is idle, false otherwise. |
155c9165 | 761 | */ |
9b9529ce | 762 | bool xe_exec_queue_is_idle(struct xe_exec_queue *q) |
155c9165 | 763 | { |
5009d554 MB |
764 | if (xe_exec_queue_is_parallel(q)) { |
765 | int i; | |
766 | ||
767 | for (i = 0; i < q->width; ++i) { | |
768 | if (xe_lrc_seqno(&q->lrc[i]) != | |
769 | q->lrc[i].fence_ctx.next_seqno - 1) | |
770 | return false; | |
771 | } | |
772 | ||
773 | return true; | |
774 | } | |
155c9165 | 775 | |
9b9529ce FD |
776 | return xe_lrc_seqno(&q->lrc[0]) == |
777 | q->lrc[0].fence_ctx.next_seqno - 1; | |
155c9165 TH |
778 | } |
779 | ||
9b9529ce | 780 | void xe_exec_queue_kill(struct xe_exec_queue *q) |
dd08ebf6 | 781 | { |
9b9529ce | 782 | struct xe_exec_queue *eq = q, *next; |
dd08ebf6 | 783 | |
9b9529ce | 784 | list_for_each_entry_safe(eq, next, &eq->multi_gt_list, |
dd08ebf6 | 785 | multi_gt_link) { |
9b9529ce | 786 | q->ops->kill(eq); |
abce4e4b | 787 | xe_vm_remove_compute_exec_queue(q->vm, eq); |
dd08ebf6 MB |
788 | } |
789 | ||
9b9529ce | 790 | q->ops->kill(q); |
abce4e4b | 791 | xe_vm_remove_compute_exec_queue(q->vm, q); |
dd08ebf6 MB |
792 | } |
793 | ||
9b9529ce FD |
794 | int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data, |
795 | struct drm_file *file) | |
dd08ebf6 MB |
796 | { |
797 | struct xe_device *xe = to_xe_device(dev); | |
798 | struct xe_file *xef = to_xe_file(file); | |
9b9529ce FD |
799 | struct drm_xe_exec_queue_destroy *args = data; |
800 | struct xe_exec_queue *q; | |
dd08ebf6 | 801 | |
b8c1ba83 FD |
802 | if (XE_IOCTL_DBG(xe, args->pad) || |
803 | XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1])) | |
dd08ebf6 MB |
804 | return -EINVAL; |
805 | ||
9b9529ce FD |
806 | mutex_lock(&xef->exec_queue.lock); |
807 | q = xa_erase(&xef->exec_queue.xa, args->exec_queue_id); | |
808 | mutex_unlock(&xef->exec_queue.lock); | |
809 | if (XE_IOCTL_DBG(xe, !q)) | |
dd08ebf6 MB |
810 | return -ENOENT; |
811 | ||
f1a9abc0 | 812 | xe_exec_queue_kill(q); |
dd08ebf6 | 813 | |
9b9529ce FD |
814 | trace_xe_exec_queue_close(q); |
815 | xe_exec_queue_put(q); | |
dd08ebf6 MB |
816 | |
817 | return 0; | |
818 | } | |
819 | ||
e669f10c MB |
820 | static void xe_exec_queue_last_fence_lockdep_assert(struct xe_exec_queue *q, |
821 | struct xe_vm *vm) | |
822 | { | |
eb9702ad MB |
823 | if (q->flags & EXEC_QUEUE_FLAG_VM) |
824 | lockdep_assert_held(&vm->lock); | |
825 | else | |
826 | xe_vm_assert_held(vm); | |
e669f10c MB |
827 | } |
828 | ||
829 | /** | |
830 | * xe_exec_queue_last_fence_put() - Drop ref to last fence | |
831 | * @q: The exec queue | |
832 | * @vm: The VM the engine does a bind or exec for | |
833 | */ | |
834 | void xe_exec_queue_last_fence_put(struct xe_exec_queue *q, struct xe_vm *vm) | |
835 | { | |
836 | xe_exec_queue_last_fence_lockdep_assert(q, vm); | |
837 | ||
838 | if (q->last_fence) { | |
839 | dma_fence_put(q->last_fence); | |
840 | q->last_fence = NULL; | |
841 | } | |
842 | } | |
843 | ||
844 | /** | |
845 | * xe_exec_queue_last_fence_put_unlocked() - Drop ref to last fence unlocked | |
846 | * @q: The exec queue | |
847 | * | |
848 | * Only safe to be called from xe_exec_queue_destroy(). | |
849 | */ | |
850 | void xe_exec_queue_last_fence_put_unlocked(struct xe_exec_queue *q) | |
851 | { | |
852 | if (q->last_fence) { | |
853 | dma_fence_put(q->last_fence); | |
854 | q->last_fence = NULL; | |
855 | } | |
856 | } | |
857 | ||
858 | /** | |
859 | * xe_exec_queue_last_fence_get() - Get last fence | |
860 | * @q: The exec queue | |
861 | * @vm: The VM the engine does a bind or exec for | |
862 | * | |
a856b67a | 863 | * Get last fence, takes a ref |
e669f10c MB |
864 | * |
865 | * Returns: last fence if not signaled, dma fence stub if signaled | |
866 | */ | |
867 | struct dma_fence *xe_exec_queue_last_fence_get(struct xe_exec_queue *q, | |
868 | struct xe_vm *vm) | |
869 | { | |
a856b67a MB |
870 | struct dma_fence *fence; |
871 | ||
e669f10c MB |
872 | xe_exec_queue_last_fence_lockdep_assert(q, vm); |
873 | ||
874 | if (q->last_fence && | |
875 | test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &q->last_fence->flags)) | |
876 | xe_exec_queue_last_fence_put(q, vm); | |
877 | ||
a856b67a MB |
878 | fence = q->last_fence ? q->last_fence : dma_fence_get_stub(); |
879 | dma_fence_get(fence); | |
880 | return fence; | |
e669f10c MB |
881 | } |
882 | ||
883 | /** | |
884 | * xe_exec_queue_last_fence_set() - Set last fence | |
885 | * @q: The exec queue | |
886 | * @vm: The VM the engine does a bind or exec for | |
887 | * @fence: The fence | |
888 | * | |
889 | * Set the last fence for the engine. Increases reference count for fence, when | |
890 | * closing engine xe_exec_queue_last_fence_put should be called. | |
891 | */ | |
892 | void xe_exec_queue_last_fence_set(struct xe_exec_queue *q, struct xe_vm *vm, | |
893 | struct dma_fence *fence) | |
894 | { | |
895 | xe_exec_queue_last_fence_lockdep_assert(q, vm); | |
896 | ||
897 | xe_exec_queue_last_fence_put(q, vm); | |
898 | q->last_fence = dma_fence_get(fence); | |
899 | } |