1 /*
2 * Copyright © 2011,2013 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 * Authors:
24 * Daniel Vetter <daniel.vetter@ffwll.ch>
25 *
26 */
27
28 #include "igt.h"
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <inttypes.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <signal.h>
38 #include <sys/wait.h>
39
40 #include <drm.h>
41
42
43 IGT_TEST_DESCRIPTION("Test kernel relocations vs. gpu races.");
44
45 /*
46 * Testcase: Kernel relocations vs. gpu races
47 *
48 */
49
50 static drm_intel_bufmgr *bufmgr;
51 struct intel_batchbuffer *batch;
52
53 uint32_t blob[2048*2048];
54 #define NUM_TARGET_BOS 16
55 drm_intel_bo *pc_target_bo[NUM_TARGET_BOS];
56 drm_intel_bo *dummy_bo;
57 drm_intel_bo *special_bo;
58 uint32_t devid;
59 int special_reloc_ofs;
60 int special_batch_len;
61
create_special_bo(void)62 static void create_special_bo(void)
63 {
64 uint32_t data[1024];
65 int len = 0;
66 int small_pitch = 64;
67 #define BATCH(dw) data[len++] = (dw);
68
69 memset(data, 0, 4096);
70 special_bo = drm_intel_bo_alloc(bufmgr, "special batch", 4096, 4096);
71
72 if (intel_gen(devid) >= 8) {
73 BATCH(MI_NOOP);
74 BATCH(XY_COLOR_BLT_CMD_NOLEN | 5 |
75 COLOR_BLT_WRITE_ALPHA | XY_COLOR_BLT_WRITE_RGB);
76 } else {
77 BATCH(XY_COLOR_BLT_CMD_NOLEN | 4 |
78 COLOR_BLT_WRITE_ALPHA | XY_COLOR_BLT_WRITE_RGB);
79 }
80
81 BATCH((3 << 24) | (0xf0 << 16) | small_pitch);
82 BATCH(0);
83 BATCH(1 << 16 | 1);
84 special_reloc_ofs = 4*len;
85 BATCH(0);
86 if (intel_gen(devid) >= 8)
87 BATCH(0);
88 BATCH(0xdeadbeef);
89
90 #define CMD_POLY_STIPPLE_OFFSET 0x7906
91 /* batchbuffer end */
92 if (IS_GEN5(batch->devid)) {
93 BATCH(CMD_POLY_STIPPLE_OFFSET << 16);
94 BATCH(0);
95 }
96 igt_assert_eq(len % 2, 0);
97 BATCH(MI_NOOP);
98 BATCH(MI_BATCH_BUFFER_END);
99
100 drm_intel_bo_subdata(special_bo, 0, 4096, data);
101 special_batch_len = len*4;
102 }
103
emit_dummy_load(int pitch)104 static void emit_dummy_load(int pitch)
105 {
106 int i;
107 uint32_t tile_flags = 0;
108
109 if (IS_965(devid)) {
110 pitch /= 4;
111 tile_flags = XY_SRC_COPY_BLT_SRC_TILED |
112 XY_SRC_COPY_BLT_DST_TILED;
113 }
114
115 for (i = 0; i < 10; i++) {
116 BLIT_COPY_BATCH_START(tile_flags);
117 OUT_BATCH((3 << 24) | /* 32 bits */
118 (0xcc << 16) | /* copy ROP */
119 pitch);
120 OUT_BATCH(0 << 16 | 1024);
121 OUT_BATCH((2048) << 16 | (2048));
122 OUT_RELOC_FENCED(dummy_bo, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0);
123 OUT_BATCH(0 << 16 | 0);
124 OUT_BATCH(pitch);
125 OUT_RELOC_FENCED(dummy_bo, I915_GEM_DOMAIN_RENDER, 0, 0);
126 ADVANCE_BATCH();
127
128 if (batch->gen >= 6) {
129 BEGIN_BATCH(3, 0);
130 OUT_BATCH(XY_SETUP_CLIP_BLT_CMD);
131 OUT_BATCH(0);
132 OUT_BATCH(0);
133 ADVANCE_BATCH();
134 }
135 }
136 intel_batchbuffer_flush(batch);
137 }
138
reloc_and_emit(int fd,drm_intel_bo * target_bo,bool faulting_reloc)139 static void reloc_and_emit(int fd, drm_intel_bo *target_bo, bool faulting_reloc)
140 {
141 struct drm_i915_gem_execbuffer2 execbuf;
142 struct drm_i915_gem_exec_object2 exec[2];
143 struct drm_i915_gem_relocation_entry reloc[1];
144 uint32_t handle_relocs;
145 void *gtt_relocs;
146
147 memset(&execbuf, 0, sizeof(execbuf));
148 memset(exec, 0, sizeof(exec));
149 memset(reloc, 0, sizeof(reloc));
150
151 exec[0].handle = target_bo->handle;
152
153 reloc[0].offset = special_reloc_ofs;
154 reloc[0].target_handle = target_bo->handle;
155 reloc[0].read_domains = I915_GEM_DOMAIN_RENDER;
156 reloc[0].write_domain = I915_GEM_DOMAIN_RENDER;
157 /* We do not track the last patched value, so force the relocation
158 * every time.
159 */
160 reloc[0].presumed_offset = -1;
161
162 handle_relocs = gem_create(fd, 4096);
163 gem_write(fd, handle_relocs, 0, reloc, sizeof(reloc));
164 gtt_relocs = gem_mmap__gtt(fd, handle_relocs, 4096,
165 PROT_READ | PROT_WRITE);
166
167 exec[1].handle = special_bo->handle;
168 exec[1].relocation_count = 1;
169 /* A newly mmap gtt bo will fault on first access. */
170 if (faulting_reloc)
171 exec[1].relocs_ptr = to_user_pointer(gtt_relocs);
172 else
173 exec[1].relocs_ptr = to_user_pointer(reloc);
174
175 execbuf.buffers_ptr = to_user_pointer(exec);
176 execbuf.buffer_count = 2;
177 execbuf.batch_len = special_batch_len;
178 if (intel_gen(devid) >= 6)
179 execbuf.flags |= I915_EXEC_BLT;
180
181 gem_execbuf(fd, &execbuf);
182
183 gem_close(fd, handle_relocs);
184 }
185
no_hang(int fd)186 static igt_hang_t no_hang(int fd)
187 {
188 return (igt_hang_t){0};
189 }
190
bcs_hang(int fd)191 static igt_hang_t bcs_hang(int fd)
192 {
193 return igt_hang_ring(fd, I915_EXEC_BLT);
194 }
195
do_test(int fd,bool faulting_reloc,igt_hang_t (* do_hang)(int fd))196 static void do_test(int fd, bool faulting_reloc,
197 igt_hang_t (*do_hang)(int fd))
198 {
199 uint32_t tiling_mode = I915_TILING_X;
200 unsigned long pitch, act_size;
201 uint32_t test;
202 int i;
203
204 if (faulting_reloc)
205 igt_disable_prefault();
206
207 act_size = 2048;
208 dummy_bo = drm_intel_bo_alloc_tiled(bufmgr, "tiled dummy_bo", act_size, act_size,
209 4, &tiling_mode, &pitch, 0);
210
211 drm_intel_bo_subdata(dummy_bo, 0, act_size*act_size*4, blob);
212
213 create_special_bo();
214
215 for (i = 0; i < NUM_TARGET_BOS; i++) {
216 igt_hang_t hang;
217
218 pc_target_bo[i] = drm_intel_bo_alloc(bufmgr, "special batch", 4096, 4096);
219 emit_dummy_load(pitch);
220 igt_assert(pc_target_bo[i]->offset == 0);
221 hang = do_hang(fd);
222
223 reloc_and_emit(fd, pc_target_bo[i], faulting_reloc);
224
225 igt_post_hang_ring(fd, hang);
226 }
227
228 /* Only check at the end to avoid unnecessary synchronous behaviour. */
229 for (i = 0; i < NUM_TARGET_BOS; i++) {
230 drm_intel_bo_get_subdata(pc_target_bo[i], 0, 4, &test);
231 igt_assert_f(test == 0xdeadbeef,
232 "mismatch in buffer %i: 0x%08x instead of 0xdeadbeef\n", i, test);
233 drm_intel_bo_unreference(pc_target_bo[i]);
234 }
235
236 drm_intel_gem_bo_map_gtt(dummy_bo);
237 drm_intel_gem_bo_unmap_gtt(dummy_bo);
238
239 drm_intel_bo_unreference(special_bo);
240 drm_intel_bo_unreference(dummy_bo);
241
242 if (faulting_reloc)
243 igt_enable_prefault();
244 }
245
246 #define INTERRUPT (1 << 0)
247 #define FAULTING (1 << 1)
248 #define THRASH (1 << 2)
249 #define THRASH_INACTIVE (1 << 3)
250 #define HANG (1 << 4)
251 #define ALL_FLAGS (HANG | INTERRUPT | FAULTING | THRASH | THRASH_INACTIVE)
do_forked_test(int fd,unsigned flags)252 static void do_forked_test(int fd, unsigned flags)
253 {
254 int num_threads = sysconf(_SC_NPROCESSORS_ONLN);
255 struct igt_helper_process thrasher = {};
256
257 if (flags & HANG)
258 igt_require_hang_ring(fd, I915_EXEC_BLT);
259
260 if (flags & (THRASH | THRASH_INACTIVE)) {
261 igt_fork_helper(&thrasher) {
262 uint64_t val;
263
264 val = DROP_RETIRE | DROP_BOUND | DROP_UNBOUND;
265 if (!(flags & THRASH_INACTIVE))
266 val |= DROP_ACTIVE | DROP_SHRINK_ALL;
267
268 while (1) {
269 usleep(1000);
270 igt_drop_caches_set(fd, val);
271 }
272 }
273 }
274
275 igt_fork(i, num_threads * 4) {
276 /* re-create process local data */
277 fd = drm_open_driver(DRIVER_INTEL);
278 bufmgr = drm_intel_bufmgr_gem_init(fd, 4096);
279 batch = intel_batchbuffer_alloc(bufmgr, devid);
280
281 if (flags & INTERRUPT)
282 igt_fork_signal_helper();
283
284 do_test(fd, flags & FAULTING, flags & HANG ? bcs_hang : no_hang);
285
286 if (flags & INTERRUPT)
287 igt_stop_signal_helper();
288 }
289
290 igt_waitchildren();
291 if (flags & (THRASH | THRASH_INACTIVE))
292 igt_stop_helper(&thrasher);
293 }
294
295 int fd;
296
297 #define MAX_BLT_SIZE 128
298 igt_main
299 {
300 igt_skip_on_simulation();
301
302 memset(blob, 'A', sizeof(blob));
303
304 igt_fixture {
305 fd = drm_open_driver(DRIVER_INTEL);
306 igt_require_gem(fd);
307 bufmgr = drm_intel_bufmgr_gem_init(fd, 4096);
308 /* disable reuse, otherwise the test fails */
309 //drm_intel_bufmgr_gem_enable_reuse(bufmgr);
310 devid = intel_get_drm_devid(fd);
311 batch = intel_batchbuffer_alloc(bufmgr, devid);
312 }
313
314 igt_subtest("normal")
315 do_test(fd, false, no_hang);
316
317 igt_subtest("faulting-reloc")
318 do_test(fd, true, no_hang);
319
320 igt_fork_signal_helper();
321 igt_subtest("interruptible")
322 do_test(fd, false, no_hang);
323
324 igt_subtest("interruptible-hang")
325 do_test(fd, false, bcs_hang);
326
327 igt_subtest("faulting-reloc-interruptible")
328 do_test(fd, true, no_hang);
329
330 igt_subtest("faulting-reloc-interruptible-hang")
331 do_test(fd, true, bcs_hang);
332 igt_stop_signal_helper();
333
334 for (unsigned flags = 0; flags <= ALL_FLAGS; flags++) {
335 if ((flags & THRASH) && (flags & THRASH_INACTIVE))
336 continue;
337
338 igt_subtest_f("forked%s%s%s%s%s",
339 flags & INTERRUPT ? "-interruptible" : "",
340 flags & FAULTING ? "-faulting-reloc" : "",
341 flags & THRASH ? "-thrashing" : "",
342 flags & THRASH_INACTIVE ? "-thrash-inactive" : "",
343 flags & HANG ? "-hang": "")
344 do_forked_test(fd, flags);
345 }
346
347 igt_fixture {
348 intel_batchbuffer_free(batch);
349 drm_intel_bufmgr_destroy(bufmgr);
350
351 close(fd);
352 }
353 }
354