Loading...
Note: File does not exist in v4.6.
1/*
2 * Copyright © 2017 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
25#include "../i915_selftest.h"
26
27#include "mock_drm.h"
28#include "huge_gem_object.h"
29
30#define DW_PER_PAGE (PAGE_SIZE / sizeof(u32))
31
32static struct i915_vma *
33gpu_fill_dw(struct i915_vma *vma, u64 offset, unsigned long count, u32 value)
34{
35 struct drm_i915_gem_object *obj;
36 const int gen = INTEL_GEN(vma->vm->i915);
37 unsigned long n, size;
38 u32 *cmd;
39 int err;
40
41 size = (4 * count + 1) * sizeof(u32);
42 size = round_up(size, PAGE_SIZE);
43 obj = i915_gem_object_create_internal(vma->vm->i915, size);
44 if (IS_ERR(obj))
45 return ERR_CAST(obj);
46
47 cmd = i915_gem_object_pin_map(obj, I915_MAP_WB);
48 if (IS_ERR(cmd)) {
49 err = PTR_ERR(cmd);
50 goto err;
51 }
52
53 GEM_BUG_ON(offset + (count - 1) * PAGE_SIZE > vma->node.size);
54 offset += vma->node.start;
55
56 for (n = 0; n < count; n++) {
57 if (gen >= 8) {
58 *cmd++ = MI_STORE_DWORD_IMM_GEN4;
59 *cmd++ = lower_32_bits(offset);
60 *cmd++ = upper_32_bits(offset);
61 *cmd++ = value;
62 } else if (gen >= 4) {
63 *cmd++ = MI_STORE_DWORD_IMM_GEN4 |
64 (gen < 6 ? 1 << 22 : 0);
65 *cmd++ = 0;
66 *cmd++ = offset;
67 *cmd++ = value;
68 } else {
69 *cmd++ = MI_STORE_DWORD_IMM | 1 << 22;
70 *cmd++ = offset;
71 *cmd++ = value;
72 }
73 offset += PAGE_SIZE;
74 }
75 *cmd = MI_BATCH_BUFFER_END;
76 i915_gem_object_unpin_map(obj);
77
78 err = i915_gem_object_set_to_gtt_domain(obj, false);
79 if (err)
80 goto err;
81
82 vma = i915_vma_instance(obj, vma->vm, NULL);
83 if (IS_ERR(vma)) {
84 err = PTR_ERR(vma);
85 goto err;
86 }
87
88 err = i915_vma_pin(vma, 0, 0, PIN_USER);
89 if (err)
90 goto err;
91
92 return vma;
93
94err:
95 i915_gem_object_put(obj);
96 return ERR_PTR(err);
97}
98
99static unsigned long real_page_count(struct drm_i915_gem_object *obj)
100{
101 return huge_gem_object_phys_size(obj) >> PAGE_SHIFT;
102}
103
104static unsigned long fake_page_count(struct drm_i915_gem_object *obj)
105{
106 return huge_gem_object_dma_size(obj) >> PAGE_SHIFT;
107}
108
109static int gpu_fill(struct drm_i915_gem_object *obj,
110 struct i915_gem_context *ctx,
111 struct intel_engine_cs *engine,
112 unsigned int dw)
113{
114 struct drm_i915_private *i915 = to_i915(obj->base.dev);
115 struct i915_address_space *vm =
116 ctx->ppgtt ? &ctx->ppgtt->base : &i915->ggtt.base;
117 struct i915_request *rq;
118 struct i915_vma *vma;
119 struct i915_vma *batch;
120 unsigned int flags;
121 int err;
122
123 GEM_BUG_ON(obj->base.size > vm->total);
124 GEM_BUG_ON(!intel_engine_can_store_dword(engine));
125
126 vma = i915_vma_instance(obj, vm, NULL);
127 if (IS_ERR(vma))
128 return PTR_ERR(vma);
129
130 err = i915_gem_object_set_to_gtt_domain(obj, false);
131 if (err)
132 return err;
133
134 err = i915_vma_pin(vma, 0, 0, PIN_HIGH | PIN_USER);
135 if (err)
136 return err;
137
138 /* Within the GTT the huge objects maps every page onto
139 * its 1024 real pages (using phys_pfn = dma_pfn % 1024).
140 * We set the nth dword within the page using the nth
141 * mapping via the GTT - this should exercise the GTT mapping
142 * whilst checking that each context provides a unique view
143 * into the object.
144 */
145 batch = gpu_fill_dw(vma,
146 (dw * real_page_count(obj)) << PAGE_SHIFT |
147 (dw * sizeof(u32)),
148 real_page_count(obj),
149 dw);
150 if (IS_ERR(batch)) {
151 err = PTR_ERR(batch);
152 goto err_vma;
153 }
154
155 rq = i915_request_alloc(engine, ctx);
156 if (IS_ERR(rq)) {
157 err = PTR_ERR(rq);
158 goto err_batch;
159 }
160
161 flags = 0;
162 if (INTEL_GEN(vm->i915) <= 5)
163 flags |= I915_DISPATCH_SECURE;
164
165 err = engine->emit_bb_start(rq,
166 batch->node.start, batch->node.size,
167 flags);
168 if (err)
169 goto err_request;
170
171 i915_vma_move_to_active(batch, rq, 0);
172 i915_gem_object_set_active_reference(batch->obj);
173 i915_vma_unpin(batch);
174 i915_vma_close(batch);
175
176 i915_vma_move_to_active(vma, rq, 0);
177 i915_vma_unpin(vma);
178
179 reservation_object_lock(obj->resv, NULL);
180 reservation_object_add_excl_fence(obj->resv, &rq->fence);
181 reservation_object_unlock(obj->resv);
182
183 __i915_request_add(rq, true);
184
185 return 0;
186
187err_request:
188 __i915_request_add(rq, false);
189err_batch:
190 i915_vma_unpin(batch);
191err_vma:
192 i915_vma_unpin(vma);
193 return err;
194}
195
196static int cpu_fill(struct drm_i915_gem_object *obj, u32 value)
197{
198 const bool has_llc = HAS_LLC(to_i915(obj->base.dev));
199 unsigned int n, m, need_flush;
200 int err;
201
202 err = i915_gem_obj_prepare_shmem_write(obj, &need_flush);
203 if (err)
204 return err;
205
206 for (n = 0; n < real_page_count(obj); n++) {
207 u32 *map;
208
209 map = kmap_atomic(i915_gem_object_get_page(obj, n));
210 for (m = 0; m < DW_PER_PAGE; m++)
211 map[m] = value;
212 if (!has_llc)
213 drm_clflush_virt_range(map, PAGE_SIZE);
214 kunmap_atomic(map);
215 }
216
217 i915_gem_obj_finish_shmem_access(obj);
218 obj->read_domains = I915_GEM_DOMAIN_GTT | I915_GEM_DOMAIN_CPU;
219 obj->write_domain = 0;
220 return 0;
221}
222
223static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max)
224{
225 unsigned int n, m, needs_flush;
226 int err;
227
228 err = i915_gem_obj_prepare_shmem_read(obj, &needs_flush);
229 if (err)
230 return err;
231
232 for (n = 0; n < real_page_count(obj); n++) {
233 u32 *map;
234
235 map = kmap_atomic(i915_gem_object_get_page(obj, n));
236 if (needs_flush & CLFLUSH_BEFORE)
237 drm_clflush_virt_range(map, PAGE_SIZE);
238
239 for (m = 0; m < max; m++) {
240 if (map[m] != m) {
241 pr_err("Invalid value at page %d, offset %d: found %x expected %x\n",
242 n, m, map[m], m);
243 err = -EINVAL;
244 goto out_unmap;
245 }
246 }
247
248 for (; m < DW_PER_PAGE; m++) {
249 if (map[m] != 0xdeadbeef) {
250 pr_err("Invalid value at page %d, offset %d: found %x expected %x\n",
251 n, m, map[m], 0xdeadbeef);
252 err = -EINVAL;
253 goto out_unmap;
254 }
255 }
256
257out_unmap:
258 kunmap_atomic(map);
259 if (err)
260 break;
261 }
262
263 i915_gem_obj_finish_shmem_access(obj);
264 return err;
265}
266
267static int file_add_object(struct drm_file *file,
268 struct drm_i915_gem_object *obj)
269{
270 int err;
271
272 GEM_BUG_ON(obj->base.handle_count);
273
274 /* tie the object to the drm_file for easy reaping */
275 err = idr_alloc(&file->object_idr, &obj->base, 1, 0, GFP_KERNEL);
276 if (err < 0)
277 return err;
278
279 i915_gem_object_get(obj);
280 obj->base.handle_count++;
281 return 0;
282}
283
284static struct drm_i915_gem_object *
285create_test_object(struct i915_gem_context *ctx,
286 struct drm_file *file,
287 struct list_head *objects)
288{
289 struct drm_i915_gem_object *obj;
290 struct i915_address_space *vm =
291 ctx->ppgtt ? &ctx->ppgtt->base : &ctx->i915->ggtt.base;
292 u64 size;
293 int err;
294
295 size = min(vm->total / 2, 1024ull * DW_PER_PAGE * PAGE_SIZE);
296 size = round_down(size, DW_PER_PAGE * PAGE_SIZE);
297
298 obj = huge_gem_object(ctx->i915, DW_PER_PAGE * PAGE_SIZE, size);
299 if (IS_ERR(obj))
300 return obj;
301
302 err = file_add_object(file, obj);
303 i915_gem_object_put(obj);
304 if (err)
305 return ERR_PTR(err);
306
307 err = cpu_fill(obj, 0xdeadbeef);
308 if (err) {
309 pr_err("Failed to fill object with cpu, err=%d\n",
310 err);
311 return ERR_PTR(err);
312 }
313
314 list_add_tail(&obj->st_link, objects);
315 return obj;
316}
317
318static unsigned long max_dwords(struct drm_i915_gem_object *obj)
319{
320 unsigned long npages = fake_page_count(obj);
321
322 GEM_BUG_ON(!IS_ALIGNED(npages, DW_PER_PAGE));
323 return npages / DW_PER_PAGE;
324}
325
326static int igt_ctx_exec(void *arg)
327{
328 struct drm_i915_private *i915 = arg;
329 struct drm_i915_gem_object *obj = NULL;
330 struct drm_file *file;
331 IGT_TIMEOUT(end_time);
332 LIST_HEAD(objects);
333 unsigned long ncontexts, ndwords, dw;
334 bool first_shared_gtt = true;
335 int err = -ENODEV;
336
337 /* Create a few different contexts (with different mm) and write
338 * through each ctx/mm using the GPU making sure those writes end
339 * up in the expected pages of our obj.
340 */
341
342 file = mock_file(i915);
343 if (IS_ERR(file))
344 return PTR_ERR(file);
345
346 mutex_lock(&i915->drm.struct_mutex);
347
348 ncontexts = 0;
349 ndwords = 0;
350 dw = 0;
351 while (!time_after(jiffies, end_time)) {
352 struct intel_engine_cs *engine;
353 struct i915_gem_context *ctx;
354 unsigned int id;
355
356 if (first_shared_gtt) {
357 ctx = __create_hw_context(i915, file->driver_priv);
358 first_shared_gtt = false;
359 } else {
360 ctx = i915_gem_create_context(i915, file->driver_priv);
361 }
362 if (IS_ERR(ctx)) {
363 err = PTR_ERR(ctx);
364 goto out_unlock;
365 }
366
367 for_each_engine(engine, i915, id) {
368 if (!intel_engine_can_store_dword(engine))
369 continue;
370
371 if (!obj) {
372 obj = create_test_object(ctx, file, &objects);
373 if (IS_ERR(obj)) {
374 err = PTR_ERR(obj);
375 goto out_unlock;
376 }
377 }
378
379 intel_runtime_pm_get(i915);
380 err = gpu_fill(obj, ctx, engine, dw);
381 intel_runtime_pm_put(i915);
382 if (err) {
383 pr_err("Failed to fill dword %lu [%lu/%lu] with gpu (%s) in ctx %u [full-ppgtt? %s], err=%d\n",
384 ndwords, dw, max_dwords(obj),
385 engine->name, ctx->hw_id,
386 yesno(!!ctx->ppgtt), err);
387 goto out_unlock;
388 }
389
390 if (++dw == max_dwords(obj)) {
391 obj = NULL;
392 dw = 0;
393 }
394 ndwords++;
395 }
396 ncontexts++;
397 }
398 pr_info("Submitted %lu contexts (across %u engines), filling %lu dwords\n",
399 ncontexts, INTEL_INFO(i915)->num_rings, ndwords);
400
401 dw = 0;
402 list_for_each_entry(obj, &objects, st_link) {
403 unsigned int rem =
404 min_t(unsigned int, ndwords - dw, max_dwords(obj));
405
406 err = cpu_check(obj, rem);
407 if (err)
408 break;
409
410 dw += rem;
411 }
412
413out_unlock:
414 mutex_unlock(&i915->drm.struct_mutex);
415
416 mock_file_free(i915, file);
417 return err;
418}
419
420static int fake_aliasing_ppgtt_enable(struct drm_i915_private *i915)
421{
422 struct drm_i915_gem_object *obj;
423 int err;
424
425 err = i915_gem_init_aliasing_ppgtt(i915);
426 if (err)
427 return err;
428
429 list_for_each_entry(obj, &i915->mm.bound_list, mm.link) {
430 struct i915_vma *vma;
431
432 vma = i915_vma_instance(obj, &i915->ggtt.base, NULL);
433 if (IS_ERR(vma))
434 continue;
435
436 vma->flags &= ~I915_VMA_LOCAL_BIND;
437 }
438
439 return 0;
440}
441
442static void fake_aliasing_ppgtt_disable(struct drm_i915_private *i915)
443{
444 i915_gem_fini_aliasing_ppgtt(i915);
445}
446
447int i915_gem_context_live_selftests(struct drm_i915_private *dev_priv)
448{
449 static const struct i915_subtest tests[] = {
450 SUBTEST(igt_ctx_exec),
451 };
452 bool fake_alias = false;
453 int err;
454
455 /* Install a fake aliasing gtt for exercise */
456 if (USES_PPGTT(dev_priv) && !dev_priv->mm.aliasing_ppgtt) {
457 mutex_lock(&dev_priv->drm.struct_mutex);
458 err = fake_aliasing_ppgtt_enable(dev_priv);
459 mutex_unlock(&dev_priv->drm.struct_mutex);
460 if (err)
461 return err;
462
463 GEM_BUG_ON(!dev_priv->mm.aliasing_ppgtt);
464 fake_alias = true;
465 }
466
467 err = i915_subtests(tests, dev_priv);
468
469 if (fake_alias) {
470 mutex_lock(&dev_priv->drm.struct_mutex);
471 fake_aliasing_ppgtt_disable(dev_priv);
472 mutex_unlock(&dev_priv->drm.struct_mutex);
473 }
474
475 return err;
476}