1 /*
2 * Copyright © 2019 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "igt.h"
25 #include "igt_gt.h"
26 #include "i915/gem_vm.h"
27 #include "i915_drm.h"
28
ctx_create_ioctl(int i915,struct drm_i915_gem_context_create_ext * arg)29 static int ctx_create_ioctl(int i915, struct drm_i915_gem_context_create_ext *arg)
30 {
31 int err;
32
33 err = 0;
34 if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, arg)) {
35 err = -errno;
36 igt_assume(err);
37 }
38
39 errno = 0;
40 return err;
41 }
42
has_ctx_clone(int i915)43 static bool has_ctx_clone(int i915)
44 {
45 struct drm_i915_gem_context_create_ext_clone ext = {
46 { .name = I915_CONTEXT_CREATE_EXT_CLONE },
47 .clone_id = -1,
48 };
49 struct drm_i915_gem_context_create_ext create = {
50 .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
51 .extensions = to_user_pointer(&ext),
52 };
53 return ctx_create_ioctl(i915, &create) == -ENOENT;
54 }
55
invalid_clone(int i915)56 static void invalid_clone(int i915)
57 {
58 struct drm_i915_gem_context_create_ext_clone ext = {
59 { .name = I915_CONTEXT_CREATE_EXT_CLONE },
60 };
61 struct drm_i915_gem_context_create_ext create = {
62 .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
63 .extensions = to_user_pointer(&ext),
64 };
65
66 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
67 gem_context_destroy(i915, create.ctx_id);
68
69 ext.flags = -1; /* Hopefully we won't run out of flags */
70 igt_assert_eq(ctx_create_ioctl(i915, &create), -EINVAL);
71 ext.flags = 0;
72
73 ext.base.next_extension = -1;
74 igt_assert_eq(ctx_create_ioctl(i915, &create), -EFAULT);
75 ext.base.next_extension = to_user_pointer(&ext);
76 igt_assert_eq(ctx_create_ioctl(i915, &create), -E2BIG);
77 ext.base.next_extension = 0;
78
79 ext.clone_id = -1;
80 igt_assert_eq(ctx_create_ioctl(i915, &create), -ENOENT);
81 ext.clone_id = 0;
82 }
83
clone_flags(int i915)84 static void clone_flags(int i915)
85 {
86 struct drm_i915_gem_context_create_ext_setparam set = {
87 { .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
88 { .param = I915_CONTEXT_PARAM_RECOVERABLE },
89 };
90 struct drm_i915_gem_context_create_ext_clone ext = {
91 { .name = I915_CONTEXT_CREATE_EXT_CLONE },
92 .flags = I915_CONTEXT_CLONE_FLAGS,
93 };
94 struct drm_i915_gem_context_create_ext create = {
95 .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
96 .extensions = to_user_pointer(&ext),
97 };
98 int expected;
99
100 set.param.value = 1; /* default is recoverable */
101 igt_require(__gem_context_set_param(i915, &set.param) == 0);
102
103 for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
104 igt_debug("Cloning %d\n", ext.clone_id);
105 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
106
107 set.param.ctx_id = ext.clone_id;
108 gem_context_get_param(i915, &set.param);
109 expected = set.param.value;
110
111 set.param.ctx_id = create.ctx_id;
112 gem_context_get_param(i915, &set.param);
113
114 igt_assert_eq_u64(set.param.param,
115 I915_CONTEXT_PARAM_RECOVERABLE);
116 igt_assert_eq((int)set.param.value, expected);
117
118 gem_context_destroy(i915, create.ctx_id);
119
120 expected = set.param.value = 0;
121 set.param.ctx_id = ext.clone_id;
122 gem_context_set_param(i915, &set.param);
123
124 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
125
126 set.param.ctx_id = create.ctx_id;
127 gem_context_get_param(i915, &set.param);
128
129 igt_assert_eq_u64(set.param.param,
130 I915_CONTEXT_PARAM_RECOVERABLE);
131 igt_assert_eq((int)set.param.value, expected);
132
133 gem_context_destroy(i915, create.ctx_id);
134
135 /* clone but then reset priority to default... */
136 set.param.ctx_id = 0;
137 set.param.value = 1;
138 ext.base.next_extension = to_user_pointer(&set);
139 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
140 ext.base.next_extension = 0;
141
142 /* then new context should have updated priority... */
143 set.param.ctx_id = create.ctx_id;
144 gem_context_get_param(i915, &set.param);
145 igt_assert_eq_u64(set.param.value, 1);
146
147 /* but original context should have default priority */
148 set.param.ctx_id = ext.clone_id;
149 gem_context_get_param(i915, &set.param);
150 igt_assert_eq_u64(set.param.value, 0);
151
152 gem_context_destroy(i915, create.ctx_id);
153 ext.clone_id = gem_context_create(i915);
154 }
155
156 gem_context_destroy(i915, ext.clone_id);
157 }
158
clone_engines(int i915)159 static void clone_engines(int i915)
160 {
161 struct drm_i915_gem_context_create_ext_setparam set = {
162 { .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
163 { .param = I915_CONTEXT_PARAM_ENGINES },
164 };
165 struct drm_i915_gem_context_create_ext_clone ext = {
166 { .name = I915_CONTEXT_CREATE_EXT_CLONE },
167 .flags = I915_CONTEXT_CLONE_ENGINES,
168 };
169 struct drm_i915_gem_context_create_ext create = {
170 .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
171 .extensions = to_user_pointer(&ext),
172 };
173 I915_DEFINE_CONTEXT_PARAM_ENGINES(expected, 64);
174 I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, 64);
175 uint64_t ex_size;
176
177 memset(&expected, 0, sizeof(expected));
178 memset(&engines, 0, sizeof(engines));
179
180 igt_require(__gem_context_set_param(i915, &set.param) == 0);
181
182 for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
183 igt_debug("Cloning %d\n", ext.clone_id);
184 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
185
186 /* Check that we cloned the engine map */
187 set.param.ctx_id = ext.clone_id;
188 set.param.size = sizeof(expected);
189 set.param.value = to_user_pointer(&expected);
190 gem_context_get_param(i915, &set.param);
191 ex_size = set.param.size;
192
193 set.param.ctx_id = create.ctx_id;
194 set.param.size = sizeof(engines);
195 set.param.value = to_user_pointer(&engines);
196 gem_context_get_param(i915, &set.param);
197
198 igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_ENGINES);
199 igt_assert_eq_u64(set.param.size, ex_size);
200 igt_assert(!memcmp(&engines, &expected, ex_size));
201
202 gem_context_destroy(i915, create.ctx_id);
203
204 /* Check that the clone will replace an earlier set */
205 expected.engines[0].engine_class =
206 I915_ENGINE_CLASS_INVALID;
207 expected.engines[0].engine_instance =
208 I915_ENGINE_CLASS_INVALID_NONE;
209 ex_size = (sizeof(struct i915_context_param_engines) +
210 sizeof(expected.engines[0]));
211
212 set.param.ctx_id = ext.clone_id;
213 set.param.size = ex_size;
214 set.param.value = to_user_pointer(&expected);
215 gem_context_set_param(i915, &set.param);
216
217 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
218
219 set.param.ctx_id = create.ctx_id;
220 set.param.size = sizeof(engines);
221 set.param.value = to_user_pointer(&engines);
222 gem_context_get_param(i915, &set.param);
223
224 igt_assert_eq_u64(set.param.size, ex_size);
225 igt_assert(!memcmp(&engines, &expected, ex_size));
226
227 gem_context_destroy(i915, create.ctx_id);
228
229 /* clone but then reset engines to default */
230 set.param.ctx_id = 0;
231 set.param.size = 0;
232 set.param.value = 0;
233 ext.base.next_extension = to_user_pointer(&set);
234
235 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
236 ext.base.next_extension = 0;
237
238 set.param.ctx_id = create.ctx_id;
239 set.param.size = sizeof(engines);
240 set.param.value = to_user_pointer(&engines);
241 gem_context_get_param(i915, &set.param);
242 igt_assert_eq_u64(set.param.size, 0);
243
244 gem_context_destroy(i915, create.ctx_id);
245
246 /* And check we ignore the flag */
247 ext.flags = 0;
248 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
249 ext.flags = I915_CONTEXT_CLONE_ENGINES;
250
251 set.param.ctx_id = create.ctx_id;
252 set.param.size = sizeof(engines);
253 set.param.value = to_user_pointer(&engines);
254 gem_context_get_param(i915, &set.param);
255 igt_assert_eq_u64(set.param.size, 0);
256
257 ext.clone_id = gem_context_create(i915);
258 }
259
260 gem_context_destroy(i915, ext.clone_id);
261 }
262
clone_scheduler(int i915)263 static void clone_scheduler(int i915)
264 {
265 struct drm_i915_gem_context_create_ext_setparam set = {
266 { .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
267 { .param = I915_CONTEXT_PARAM_PRIORITY },
268 };
269 struct drm_i915_gem_context_create_ext_clone ext = {
270 { .name = I915_CONTEXT_CREATE_EXT_CLONE },
271 .flags = I915_CONTEXT_CLONE_SCHEDATTR,
272 };
273 struct drm_i915_gem_context_create_ext create = {
274 .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
275 .extensions = to_user_pointer(&ext),
276 };
277 int expected;
278
279 igt_require(__gem_context_set_param(i915, &set.param) == 0);
280
281 for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
282 igt_debug("Cloning %d\n", ext.clone_id);
283 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
284
285 set.param.ctx_id = ext.clone_id;
286 gem_context_get_param(i915, &set.param);
287 expected = set.param.value;
288
289 set.param.ctx_id = create.ctx_id;
290 gem_context_get_param(i915, &set.param);
291
292 igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_PRIORITY);
293 igt_assert_eq((int)set.param.value, expected);
294
295 gem_context_destroy(i915, create.ctx_id);
296
297 expected = set.param.value = 1;
298 set.param.ctx_id = ext.clone_id;
299 gem_context_set_param(i915, &set.param);
300
301 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
302
303 set.param.ctx_id = create.ctx_id;
304 gem_context_get_param(i915, &set.param);
305
306 igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_PRIORITY);
307 igt_assert_eq((int)set.param.value, expected);
308
309 gem_context_destroy(i915, create.ctx_id);
310
311 /* clone but then reset priority to default */
312 set.param.ctx_id = 0;
313 set.param.value = 0;
314 ext.base.next_extension = to_user_pointer(&set);
315 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
316 ext.base.next_extension = 0;
317
318 set.param.ctx_id = create.ctx_id;
319 gem_context_get_param(i915, &set.param);
320 igt_assert_eq_u64(set.param.value, 0);
321
322 set.param.ctx_id = ext.clone_id;
323 gem_context_get_param(i915, &set.param);
324 igt_assert_eq_u64(set.param.value, 1);
325
326 gem_context_destroy(i915, create.ctx_id);
327 ext.clone_id = gem_context_create(i915);
328 }
329
330 gem_context_destroy(i915, ext.clone_id);
331 }
332
__batch_create(int i915,uint32_t offset)333 static uint32_t __batch_create(int i915, uint32_t offset)
334 {
335 const uint32_t bbe = MI_BATCH_BUFFER_END;
336 uint32_t handle;
337
338 handle = gem_create(i915, ALIGN(offset + 4, 4096));
339 gem_write(i915, handle, offset, &bbe, sizeof(bbe));
340
341 return handle;
342 }
343
batch_create(int i915)344 static uint32_t batch_create(int i915)
345 {
346 return __batch_create(i915, 0);
347 }
348
check_same_vm(int i915,uint32_t ctx_a,uint32_t ctx_b)349 static void check_same_vm(int i915, uint32_t ctx_a, uint32_t ctx_b)
350 {
351 struct drm_i915_gem_exec_object2 batch = {
352 .handle = batch_create(i915),
353 };
354 struct drm_i915_gem_execbuffer2 eb = {
355 .buffers_ptr = to_user_pointer(&batch),
356 .buffer_count = 1,
357 };
358
359 /* First verify that we try to use "softpinning" by default */
360 batch.offset = 48 << 20;
361 eb.rsvd1 = ctx_a;
362 gem_execbuf(i915, &eb);
363 igt_assert_eq_u64(batch.offset, 48 << 20);
364
365 /* An already active VMA will try to keep its offset */
366 batch.offset = 0;
367 eb.rsvd1 = ctx_b;
368 gem_execbuf(i915, &eb);
369 igt_assert_eq_u64(batch.offset, 48 << 20);
370
371 gem_sync(i915, batch.handle);
372 gem_close(i915, batch.handle);
373
374 gem_quiescent_gpu(i915); /* evict the vma */
375 }
376
clone_vm(int i915)377 static void clone_vm(int i915)
378 {
379 struct drm_i915_gem_context_param set = {
380 .param = I915_CONTEXT_PARAM_VM,
381 };
382 struct drm_i915_gem_context_create_ext_clone ext = {
383 { .name = I915_CONTEXT_CREATE_EXT_CLONE },
384 .flags = I915_CONTEXT_CLONE_VM,
385 };
386 struct drm_i915_gem_context_create_ext create = {
387 .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
388 .extensions = to_user_pointer(&ext),
389 };
390 uint32_t vm_id[2];
391
392 igt_require(__gem_context_set_param(i915, &set) == -ENOENT);
393
394 /* Scrub the VM for our tests */
395 i915 = gem_reopen_driver(i915);
396
397 set.ctx_id = gem_context_create(i915);
398 gem_context_get_param(i915, &set);
399 vm_id[0] = set.value;
400 gem_context_destroy(i915, set.ctx_id);
401
402 vm_id[1] = gem_vm_create(i915);
403
404 for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
405 igt_debug("Cloning %d\n", ext.clone_id);
406
407 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
408 check_same_vm(i915, ext.clone_id, create.ctx_id);
409 gem_context_destroy(i915, create.ctx_id);
410
411 set.value = vm_id[pass];
412 set.ctx_id = ext.clone_id;
413 gem_context_set_param(i915, &set);
414
415 igt_assert_eq(ctx_create_ioctl(i915, &create), 0);
416 check_same_vm(i915, ext.clone_id, create.ctx_id);
417 gem_context_destroy(i915, create.ctx_id);
418
419 ext.clone_id = gem_context_create(i915);
420 }
421
422 gem_context_destroy(i915, ext.clone_id);
423
424 for (int i = 0; i < ARRAY_SIZE(vm_id); i++)
425 gem_vm_destroy(i915, vm_id[i]);
426
427 close(i915);
428 }
429
430 igt_main
431 {
432 int i915 = -1;
433
434 igt_fixture {
435 i915 = drm_open_driver(DRIVER_INTEL);
436 igt_require_gem(i915);
437 gem_require_contexts(i915);
438
439 igt_require(has_ctx_clone(i915));
440 igt_fork_hang_detector(i915);
441 }
442
443 igt_subtest("invalid")
444 invalid_clone(i915);
445
446 igt_subtest("engines")
447 clone_engines(i915);
448
449 igt_subtest("flags")
450 clone_flags(i915);
451
452 igt_subtest("scheduler")
453 clone_scheduler(i915);
454
455 igt_subtest("vm")
456 clone_vm(i915);
457
458 igt_fixture {
459 igt_stop_hang_detector();
460 close(i915);
461 }
462 }
463