1b414fcd5SChris Wilson /* 2b414fcd5SChris Wilson * SPDX-License-Identifier: MIT 3b414fcd5SChris Wilson * 4b414fcd5SChris Wilson * Copyright © 2016 Intel Corporation 5b414fcd5SChris Wilson */ 6b414fcd5SChris Wilson 7b414fcd5SChris Wilson #include <linux/prime_numbers.h> 8b414fcd5SChris Wilson 9b414fcd5SChris Wilson #include "gt/intel_gt_pm.h" 1010be98a7SChris Wilson #include "huge_gem_object.h" 11b414fcd5SChris Wilson #include "i915_selftest.h" 12b414fcd5SChris Wilson #include "selftests/igt_flush_test.h" 13b414fcd5SChris Wilson 14b414fcd5SChris Wilson struct tile { 15b414fcd5SChris Wilson unsigned int width; 16b414fcd5SChris Wilson unsigned int height; 17b414fcd5SChris Wilson unsigned int stride; 18b414fcd5SChris Wilson unsigned int size; 19b414fcd5SChris Wilson unsigned int tiling; 20b414fcd5SChris Wilson unsigned int swizzle; 21b414fcd5SChris Wilson }; 22b414fcd5SChris Wilson 23b414fcd5SChris Wilson static u64 swizzle_bit(unsigned int bit, u64 offset) 24b414fcd5SChris Wilson { 25b414fcd5SChris Wilson return (offset & BIT_ULL(bit)) >> (bit - 6); 26b414fcd5SChris Wilson } 27b414fcd5SChris Wilson 28b414fcd5SChris Wilson static u64 tiled_offset(const struct tile *tile, u64 v) 29b414fcd5SChris Wilson { 30b414fcd5SChris Wilson u64 x, y; 31b414fcd5SChris Wilson 32b414fcd5SChris Wilson if (tile->tiling == I915_TILING_NONE) 33b414fcd5SChris Wilson return v; 34b414fcd5SChris Wilson 35b414fcd5SChris Wilson y = div64_u64_rem(v, tile->stride, &x); 36b414fcd5SChris Wilson v = div64_u64_rem(y, tile->height, &y) * tile->stride * tile->height; 37b414fcd5SChris Wilson 38b414fcd5SChris Wilson if (tile->tiling == I915_TILING_X) { 39b414fcd5SChris Wilson v += y * tile->width; 40b414fcd5SChris Wilson v += div64_u64_rem(x, tile->width, &x) << tile->size; 41b414fcd5SChris Wilson v += x; 42b414fcd5SChris Wilson } else if (tile->width == 128) { 43b414fcd5SChris Wilson const unsigned int ytile_span = 16; 44b414fcd5SChris Wilson const unsigned int ytile_height = 512; 45b414fcd5SChris Wilson 46b414fcd5SChris Wilson v += y * ytile_span; 47b414fcd5SChris Wilson v += div64_u64_rem(x, ytile_span, &x) * ytile_height; 48b414fcd5SChris Wilson v += x; 49b414fcd5SChris Wilson } else { 50b414fcd5SChris Wilson const unsigned int ytile_span = 32; 51b414fcd5SChris Wilson const unsigned int ytile_height = 256; 52b414fcd5SChris Wilson 53b414fcd5SChris Wilson v += y * ytile_span; 54b414fcd5SChris Wilson v += div64_u64_rem(x, ytile_span, &x) * ytile_height; 55b414fcd5SChris Wilson v += x; 56b414fcd5SChris Wilson } 57b414fcd5SChris Wilson 58b414fcd5SChris Wilson switch (tile->swizzle) { 59b414fcd5SChris Wilson case I915_BIT_6_SWIZZLE_9: 60b414fcd5SChris Wilson v ^= swizzle_bit(9, v); 61b414fcd5SChris Wilson break; 62b414fcd5SChris Wilson case I915_BIT_6_SWIZZLE_9_10: 63b414fcd5SChris Wilson v ^= swizzle_bit(9, v) ^ swizzle_bit(10, v); 64b414fcd5SChris Wilson break; 65b414fcd5SChris Wilson case I915_BIT_6_SWIZZLE_9_11: 66b414fcd5SChris Wilson v ^= swizzle_bit(9, v) ^ swizzle_bit(11, v); 67b414fcd5SChris Wilson break; 68b414fcd5SChris Wilson case I915_BIT_6_SWIZZLE_9_10_11: 69b414fcd5SChris Wilson v ^= swizzle_bit(9, v) ^ swizzle_bit(10, v) ^ swizzle_bit(11, v); 70b414fcd5SChris Wilson break; 71b414fcd5SChris Wilson } 72b414fcd5SChris Wilson 73b414fcd5SChris Wilson return v; 74b414fcd5SChris Wilson } 75b414fcd5SChris Wilson 76b414fcd5SChris Wilson static int check_partial_mapping(struct drm_i915_gem_object *obj, 77b414fcd5SChris Wilson const struct tile *tile, 78b414fcd5SChris Wilson unsigned long end_time) 79b414fcd5SChris Wilson { 80b414fcd5SChris Wilson const unsigned int nreal = obj->scratch / PAGE_SIZE; 81b414fcd5SChris Wilson const unsigned long npages = obj->base.size / PAGE_SIZE; 82b414fcd5SChris Wilson struct i915_vma *vma; 83b414fcd5SChris Wilson unsigned long page; 84b414fcd5SChris Wilson int err; 85b414fcd5SChris Wilson 86b414fcd5SChris Wilson if (igt_timeout(end_time, 87b414fcd5SChris Wilson "%s: timed out before tiling=%d stride=%d\n", 88b414fcd5SChris Wilson __func__, tile->tiling, tile->stride)) 89b414fcd5SChris Wilson return -EINTR; 90b414fcd5SChris Wilson 91b414fcd5SChris Wilson err = i915_gem_object_set_tiling(obj, tile->tiling, tile->stride); 92b414fcd5SChris Wilson if (err) { 93b414fcd5SChris Wilson pr_err("Failed to set tiling mode=%u, stride=%u, err=%d\n", 94b414fcd5SChris Wilson tile->tiling, tile->stride, err); 95b414fcd5SChris Wilson return err; 96b414fcd5SChris Wilson } 97b414fcd5SChris Wilson 98b414fcd5SChris Wilson GEM_BUG_ON(i915_gem_object_get_tiling(obj) != tile->tiling); 99b414fcd5SChris Wilson GEM_BUG_ON(i915_gem_object_get_stride(obj) != tile->stride); 100b414fcd5SChris Wilson 101b414fcd5SChris Wilson for_each_prime_number_from(page, 1, npages) { 102b414fcd5SChris Wilson struct i915_ggtt_view view = 103b414fcd5SChris Wilson compute_partial_view(obj, page, MIN_CHUNK_PAGES); 104b414fcd5SChris Wilson u32 __iomem *io; 105b414fcd5SChris Wilson struct page *p; 106b414fcd5SChris Wilson unsigned int n; 107b414fcd5SChris Wilson u64 offset; 108b414fcd5SChris Wilson u32 *cpu; 109b414fcd5SChris Wilson 110b414fcd5SChris Wilson GEM_BUG_ON(view.partial.size > nreal); 111b414fcd5SChris Wilson cond_resched(); 112b414fcd5SChris Wilson 1136951e589SChris Wilson i915_gem_object_lock(obj); 114b414fcd5SChris Wilson err = i915_gem_object_set_to_gtt_domain(obj, true); 1156951e589SChris Wilson i915_gem_object_unlock(obj); 116b414fcd5SChris Wilson if (err) { 117b414fcd5SChris Wilson pr_err("Failed to flush to GTT write domain; err=%d\n", 118b414fcd5SChris Wilson err); 119b414fcd5SChris Wilson return err; 120b414fcd5SChris Wilson } 121b414fcd5SChris Wilson 122b414fcd5SChris Wilson vma = i915_gem_object_ggtt_pin(obj, &view, 0, 0, PIN_MAPPABLE); 123b414fcd5SChris Wilson if (IS_ERR(vma)) { 124b414fcd5SChris Wilson pr_err("Failed to pin partial view: offset=%lu; err=%d\n", 125b414fcd5SChris Wilson page, (int)PTR_ERR(vma)); 126b414fcd5SChris Wilson return PTR_ERR(vma); 127b414fcd5SChris Wilson } 128b414fcd5SChris Wilson 129b414fcd5SChris Wilson n = page - view.partial.offset; 130b414fcd5SChris Wilson GEM_BUG_ON(n >= view.partial.size); 131b414fcd5SChris Wilson 132b414fcd5SChris Wilson io = i915_vma_pin_iomap(vma); 133b414fcd5SChris Wilson i915_vma_unpin(vma); 134b414fcd5SChris Wilson if (IS_ERR(io)) { 135b414fcd5SChris Wilson pr_err("Failed to iomap partial view: offset=%lu; err=%d\n", 136b414fcd5SChris Wilson page, (int)PTR_ERR(io)); 137b414fcd5SChris Wilson return PTR_ERR(io); 138b414fcd5SChris Wilson } 139b414fcd5SChris Wilson 140b414fcd5SChris Wilson iowrite32(page, io + n * PAGE_SIZE / sizeof(*io)); 141b414fcd5SChris Wilson i915_vma_unpin_iomap(vma); 142b414fcd5SChris Wilson 143b414fcd5SChris Wilson offset = tiled_offset(tile, page << PAGE_SHIFT); 144b414fcd5SChris Wilson if (offset >= obj->base.size) 145b414fcd5SChris Wilson continue; 146b414fcd5SChris Wilson 1476951e589SChris Wilson i915_gem_object_lock(obj); 148b414fcd5SChris Wilson i915_gem_object_flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU); 1496951e589SChris Wilson i915_gem_object_unlock(obj); 150b414fcd5SChris Wilson 151b414fcd5SChris Wilson p = i915_gem_object_get_page(obj, offset >> PAGE_SHIFT); 152b414fcd5SChris Wilson cpu = kmap(p) + offset_in_page(offset); 153b414fcd5SChris Wilson drm_clflush_virt_range(cpu, sizeof(*cpu)); 154b414fcd5SChris Wilson if (*cpu != (u32)page) { 155b414fcd5SChris Wilson pr_err("Partial view for %lu [%u] (offset=%llu, size=%u [%llu, row size %u], fence=%d, tiling=%d, stride=%d) misalignment, expected write to page (%llu + %u [0x%llx]) of 0x%x, found 0x%x\n", 156b414fcd5SChris Wilson page, n, 157b414fcd5SChris Wilson view.partial.offset, 158b414fcd5SChris Wilson view.partial.size, 159b414fcd5SChris Wilson vma->size >> PAGE_SHIFT, 160b414fcd5SChris Wilson tile->tiling ? tile_row_pages(obj) : 0, 161b414fcd5SChris Wilson vma->fence ? vma->fence->id : -1, tile->tiling, tile->stride, 162b414fcd5SChris Wilson offset >> PAGE_SHIFT, 163b414fcd5SChris Wilson (unsigned int)offset_in_page(offset), 164b414fcd5SChris Wilson offset, 165b414fcd5SChris Wilson (u32)page, *cpu); 166b414fcd5SChris Wilson err = -EINVAL; 167b414fcd5SChris Wilson } 168b414fcd5SChris Wilson *cpu = 0; 169b414fcd5SChris Wilson drm_clflush_virt_range(cpu, sizeof(*cpu)); 170b414fcd5SChris Wilson kunmap(p); 171b414fcd5SChris Wilson if (err) 172b414fcd5SChris Wilson return err; 173b414fcd5SChris Wilson 174b414fcd5SChris Wilson i915_vma_destroy(vma); 175b414fcd5SChris Wilson } 176b414fcd5SChris Wilson 177b414fcd5SChris Wilson return 0; 178b414fcd5SChris Wilson } 179b414fcd5SChris Wilson 180b414fcd5SChris Wilson static int igt_partial_tiling(void *arg) 181b414fcd5SChris Wilson { 182b414fcd5SChris Wilson const unsigned int nreal = 1 << 12; /* largest tile row x2 */ 183b414fcd5SChris Wilson struct drm_i915_private *i915 = arg; 184b414fcd5SChris Wilson struct drm_i915_gem_object *obj; 185b414fcd5SChris Wilson intel_wakeref_t wakeref; 186b414fcd5SChris Wilson int tiling; 187b414fcd5SChris Wilson int err; 188b414fcd5SChris Wilson 189b414fcd5SChris Wilson /* We want to check the page mapping and fencing of a large object 190b414fcd5SChris Wilson * mmapped through the GTT. The object we create is larger than can 191b414fcd5SChris Wilson * possibly be mmaped as a whole, and so we must use partial GGTT vma. 192b414fcd5SChris Wilson * We then check that a write through each partial GGTT vma ends up 193b414fcd5SChris Wilson * in the right set of pages within the object, and with the expected 194b414fcd5SChris Wilson * tiling, which we verify by manual swizzling. 195b414fcd5SChris Wilson */ 196b414fcd5SChris Wilson 197b414fcd5SChris Wilson obj = huge_gem_object(i915, 198b414fcd5SChris Wilson nreal << PAGE_SHIFT, 199b414fcd5SChris Wilson (1 + next_prime_number(i915->ggtt.vm.total >> PAGE_SHIFT)) << PAGE_SHIFT); 200b414fcd5SChris Wilson if (IS_ERR(obj)) 201b414fcd5SChris Wilson return PTR_ERR(obj); 202b414fcd5SChris Wilson 203b414fcd5SChris Wilson err = i915_gem_object_pin_pages(obj); 204b414fcd5SChris Wilson if (err) { 205b414fcd5SChris Wilson pr_err("Failed to allocate %u pages (%lu total), err=%d\n", 206b414fcd5SChris Wilson nreal, obj->base.size / PAGE_SIZE, err); 207b414fcd5SChris Wilson goto out; 208b414fcd5SChris Wilson } 209b414fcd5SChris Wilson 210b414fcd5SChris Wilson mutex_lock(&i915->drm.struct_mutex); 211b414fcd5SChris Wilson wakeref = intel_runtime_pm_get(i915); 212b414fcd5SChris Wilson 213b414fcd5SChris Wilson if (1) { 214b414fcd5SChris Wilson IGT_TIMEOUT(end); 215b414fcd5SChris Wilson struct tile tile; 216b414fcd5SChris Wilson 217b414fcd5SChris Wilson tile.height = 1; 218b414fcd5SChris Wilson tile.width = 1; 219b414fcd5SChris Wilson tile.size = 0; 220b414fcd5SChris Wilson tile.stride = 0; 221b414fcd5SChris Wilson tile.swizzle = I915_BIT_6_SWIZZLE_NONE; 222b414fcd5SChris Wilson tile.tiling = I915_TILING_NONE; 223b414fcd5SChris Wilson 224b414fcd5SChris Wilson err = check_partial_mapping(obj, &tile, end); 225b414fcd5SChris Wilson if (err && err != -EINTR) 226b414fcd5SChris Wilson goto out_unlock; 227b414fcd5SChris Wilson } 228b414fcd5SChris Wilson 229b414fcd5SChris Wilson for (tiling = I915_TILING_X; tiling <= I915_TILING_Y; tiling++) { 230b414fcd5SChris Wilson IGT_TIMEOUT(end); 231b414fcd5SChris Wilson unsigned int max_pitch; 232b414fcd5SChris Wilson unsigned int pitch; 233b414fcd5SChris Wilson struct tile tile; 234b414fcd5SChris Wilson 235b414fcd5SChris Wilson if (i915->quirks & QUIRK_PIN_SWIZZLED_PAGES) 236b414fcd5SChris Wilson /* 237b414fcd5SChris Wilson * The swizzling pattern is actually unknown as it 238b414fcd5SChris Wilson * varies based on physical address of each page. 239b414fcd5SChris Wilson * See i915_gem_detect_bit_6_swizzle(). 240b414fcd5SChris Wilson */ 241b414fcd5SChris Wilson break; 242b414fcd5SChris Wilson 243b414fcd5SChris Wilson tile.tiling = tiling; 244b414fcd5SChris Wilson switch (tiling) { 245b414fcd5SChris Wilson case I915_TILING_X: 246b414fcd5SChris Wilson tile.swizzle = i915->mm.bit_6_swizzle_x; 247b414fcd5SChris Wilson break; 248b414fcd5SChris Wilson case I915_TILING_Y: 249b414fcd5SChris Wilson tile.swizzle = i915->mm.bit_6_swizzle_y; 250b414fcd5SChris Wilson break; 251b414fcd5SChris Wilson } 252b414fcd5SChris Wilson 253b414fcd5SChris Wilson GEM_BUG_ON(tile.swizzle == I915_BIT_6_SWIZZLE_UNKNOWN); 254b414fcd5SChris Wilson if (tile.swizzle == I915_BIT_6_SWIZZLE_9_17 || 255b414fcd5SChris Wilson tile.swizzle == I915_BIT_6_SWIZZLE_9_10_17) 256b414fcd5SChris Wilson continue; 257b414fcd5SChris Wilson 258b414fcd5SChris Wilson if (INTEL_GEN(i915) <= 2) { 259b414fcd5SChris Wilson tile.height = 16; 260b414fcd5SChris Wilson tile.width = 128; 261b414fcd5SChris Wilson tile.size = 11; 262b414fcd5SChris Wilson } else if (tile.tiling == I915_TILING_Y && 263b414fcd5SChris Wilson HAS_128_BYTE_Y_TILING(i915)) { 264b414fcd5SChris Wilson tile.height = 32; 265b414fcd5SChris Wilson tile.width = 128; 266b414fcd5SChris Wilson tile.size = 12; 267b414fcd5SChris Wilson } else { 268b414fcd5SChris Wilson tile.height = 8; 269b414fcd5SChris Wilson tile.width = 512; 270b414fcd5SChris Wilson tile.size = 12; 271b414fcd5SChris Wilson } 272b414fcd5SChris Wilson 273b414fcd5SChris Wilson if (INTEL_GEN(i915) < 4) 274b414fcd5SChris Wilson max_pitch = 8192 / tile.width; 275b414fcd5SChris Wilson else if (INTEL_GEN(i915) < 7) 276b414fcd5SChris Wilson max_pitch = 128 * I965_FENCE_MAX_PITCH_VAL / tile.width; 277b414fcd5SChris Wilson else 278b414fcd5SChris Wilson max_pitch = 128 * GEN7_FENCE_MAX_PITCH_VAL / tile.width; 279b414fcd5SChris Wilson 280b414fcd5SChris Wilson for (pitch = max_pitch; pitch; pitch >>= 1) { 281b414fcd5SChris Wilson tile.stride = tile.width * pitch; 282b414fcd5SChris Wilson err = check_partial_mapping(obj, &tile, end); 283b414fcd5SChris Wilson if (err == -EINTR) 284b414fcd5SChris Wilson goto next_tiling; 285b414fcd5SChris Wilson if (err) 286b414fcd5SChris Wilson goto out_unlock; 287b414fcd5SChris Wilson 288b414fcd5SChris Wilson if (pitch > 2 && INTEL_GEN(i915) >= 4) { 289b414fcd5SChris Wilson tile.stride = tile.width * (pitch - 1); 290b414fcd5SChris Wilson err = check_partial_mapping(obj, &tile, end); 291b414fcd5SChris Wilson if (err == -EINTR) 292b414fcd5SChris Wilson goto next_tiling; 293b414fcd5SChris Wilson if (err) 294b414fcd5SChris Wilson goto out_unlock; 295b414fcd5SChris Wilson } 296b414fcd5SChris Wilson 297b414fcd5SChris Wilson if (pitch < max_pitch && INTEL_GEN(i915) >= 4) { 298b414fcd5SChris Wilson tile.stride = tile.width * (pitch + 1); 299b414fcd5SChris Wilson err = check_partial_mapping(obj, &tile, end); 300b414fcd5SChris Wilson if (err == -EINTR) 301b414fcd5SChris Wilson goto next_tiling; 302b414fcd5SChris Wilson if (err) 303b414fcd5SChris Wilson goto out_unlock; 304b414fcd5SChris Wilson } 305b414fcd5SChris Wilson } 306b414fcd5SChris Wilson 307b414fcd5SChris Wilson if (INTEL_GEN(i915) >= 4) { 308b414fcd5SChris Wilson for_each_prime_number(pitch, max_pitch) { 309b414fcd5SChris Wilson tile.stride = tile.width * pitch; 310b414fcd5SChris Wilson err = check_partial_mapping(obj, &tile, end); 311b414fcd5SChris Wilson if (err == -EINTR) 312b414fcd5SChris Wilson goto next_tiling; 313b414fcd5SChris Wilson if (err) 314b414fcd5SChris Wilson goto out_unlock; 315b414fcd5SChris Wilson } 316b414fcd5SChris Wilson } 317b414fcd5SChris Wilson 318b414fcd5SChris Wilson next_tiling: ; 319b414fcd5SChris Wilson } 320b414fcd5SChris Wilson 321b414fcd5SChris Wilson out_unlock: 322b414fcd5SChris Wilson intel_runtime_pm_put(i915, wakeref); 323b414fcd5SChris Wilson mutex_unlock(&i915->drm.struct_mutex); 324b414fcd5SChris Wilson i915_gem_object_unpin_pages(obj); 325b414fcd5SChris Wilson out: 326b414fcd5SChris Wilson i915_gem_object_put(obj); 327b414fcd5SChris Wilson return err; 328b414fcd5SChris Wilson } 329b414fcd5SChris Wilson 330b414fcd5SChris Wilson static int make_obj_busy(struct drm_i915_gem_object *obj) 331b414fcd5SChris Wilson { 332b414fcd5SChris Wilson struct drm_i915_private *i915 = to_i915(obj->base.dev); 333b414fcd5SChris Wilson struct i915_request *rq; 334b414fcd5SChris Wilson struct i915_vma *vma; 335b414fcd5SChris Wilson int err; 336b414fcd5SChris Wilson 337b414fcd5SChris Wilson vma = i915_vma_instance(obj, &i915->ggtt.vm, NULL); 338b414fcd5SChris Wilson if (IS_ERR(vma)) 339b414fcd5SChris Wilson return PTR_ERR(vma); 340b414fcd5SChris Wilson 341b414fcd5SChris Wilson err = i915_vma_pin(vma, 0, 0, PIN_USER); 342b414fcd5SChris Wilson if (err) 343b414fcd5SChris Wilson return err; 344b414fcd5SChris Wilson 345b414fcd5SChris Wilson rq = i915_request_create(i915->engine[RCS0]->kernel_context); 346b414fcd5SChris Wilson if (IS_ERR(rq)) { 347b414fcd5SChris Wilson i915_vma_unpin(vma); 348b414fcd5SChris Wilson return PTR_ERR(rq); 349b414fcd5SChris Wilson } 350b414fcd5SChris Wilson 3516951e589SChris Wilson i915_vma_lock(vma); 352b414fcd5SChris Wilson err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE); 3536951e589SChris Wilson i915_vma_unlock(vma); 354b414fcd5SChris Wilson 355b414fcd5SChris Wilson i915_request_add(rq); 356b414fcd5SChris Wilson 357b414fcd5SChris Wilson i915_vma_unpin(vma); 358c017cf6bSChris Wilson i915_gem_object_put(obj); /* leave it only alive via its active ref */ 359b414fcd5SChris Wilson 360b414fcd5SChris Wilson return err; 361b414fcd5SChris Wilson } 362b414fcd5SChris Wilson 363b414fcd5SChris Wilson static bool assert_mmap_offset(struct drm_i915_private *i915, 364b414fcd5SChris Wilson unsigned long size, 365b414fcd5SChris Wilson int expected) 366b414fcd5SChris Wilson { 367b414fcd5SChris Wilson struct drm_i915_gem_object *obj; 368b414fcd5SChris Wilson int err; 369b414fcd5SChris Wilson 370b414fcd5SChris Wilson obj = i915_gem_object_create_internal(i915, size); 371b414fcd5SChris Wilson if (IS_ERR(obj)) 372b414fcd5SChris Wilson return PTR_ERR(obj); 373b414fcd5SChris Wilson 374b414fcd5SChris Wilson err = create_mmap_offset(obj); 375b414fcd5SChris Wilson i915_gem_object_put(obj); 376b414fcd5SChris Wilson 377b414fcd5SChris Wilson return err == expected; 378b414fcd5SChris Wilson } 379b414fcd5SChris Wilson 380b414fcd5SChris Wilson static void disable_retire_worker(struct drm_i915_private *i915) 381b414fcd5SChris Wilson { 382b414fcd5SChris Wilson i915_gem_shrinker_unregister(i915); 383b414fcd5SChris Wilson 384b414fcd5SChris Wilson intel_gt_pm_get(i915); 385b414fcd5SChris Wilson 386b414fcd5SChris Wilson cancel_delayed_work_sync(&i915->gem.retire_work); 387b414fcd5SChris Wilson flush_work(&i915->gem.idle_work); 388b414fcd5SChris Wilson } 389b414fcd5SChris Wilson 390b414fcd5SChris Wilson static void restore_retire_worker(struct drm_i915_private *i915) 391b414fcd5SChris Wilson { 392b414fcd5SChris Wilson intel_gt_pm_put(i915); 393b414fcd5SChris Wilson 394b414fcd5SChris Wilson mutex_lock(&i915->drm.struct_mutex); 395b414fcd5SChris Wilson igt_flush_test(i915, I915_WAIT_LOCKED); 396b414fcd5SChris Wilson mutex_unlock(&i915->drm.struct_mutex); 397b414fcd5SChris Wilson 398b414fcd5SChris Wilson i915_gem_shrinker_register(i915); 399b414fcd5SChris Wilson } 400b414fcd5SChris Wilson 401b414fcd5SChris Wilson static int igt_mmap_offset_exhaustion(void *arg) 402b414fcd5SChris Wilson { 403b414fcd5SChris Wilson struct drm_i915_private *i915 = arg; 404b414fcd5SChris Wilson struct drm_mm *mm = &i915->drm.vma_offset_manager->vm_addr_space_mm; 405b414fcd5SChris Wilson struct drm_i915_gem_object *obj; 406b414fcd5SChris Wilson struct drm_mm_node resv, *hole; 407b414fcd5SChris Wilson u64 hole_start, hole_end; 408b414fcd5SChris Wilson int loop, err; 409b414fcd5SChris Wilson 410b414fcd5SChris Wilson /* Disable background reaper */ 411b414fcd5SChris Wilson disable_retire_worker(i915); 412b414fcd5SChris Wilson GEM_BUG_ON(!i915->gt.awake); 413b414fcd5SChris Wilson 414b414fcd5SChris Wilson /* Trim the device mmap space to only a page */ 415b414fcd5SChris Wilson memset(&resv, 0, sizeof(resv)); 416b414fcd5SChris Wilson drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { 417b414fcd5SChris Wilson resv.start = hole_start; 418b414fcd5SChris Wilson resv.size = hole_end - hole_start - 1; /* PAGE_SIZE units */ 419b414fcd5SChris Wilson err = drm_mm_reserve_node(mm, &resv); 420b414fcd5SChris Wilson if (err) { 421b414fcd5SChris Wilson pr_err("Failed to trim VMA manager, err=%d\n", err); 422b414fcd5SChris Wilson goto out_park; 423b414fcd5SChris Wilson } 424b414fcd5SChris Wilson break; 425b414fcd5SChris Wilson } 426b414fcd5SChris Wilson 427b414fcd5SChris Wilson /* Just fits! */ 428b414fcd5SChris Wilson if (!assert_mmap_offset(i915, PAGE_SIZE, 0)) { 429b414fcd5SChris Wilson pr_err("Unable to insert object into single page hole\n"); 430b414fcd5SChris Wilson err = -EINVAL; 431b414fcd5SChris Wilson goto out; 432b414fcd5SChris Wilson } 433b414fcd5SChris Wilson 434b414fcd5SChris Wilson /* Too large */ 435b414fcd5SChris Wilson if (!assert_mmap_offset(i915, 2 * PAGE_SIZE, -ENOSPC)) { 436b414fcd5SChris Wilson pr_err("Unexpectedly succeeded in inserting too large object into single page hole\n"); 437b414fcd5SChris Wilson err = -EINVAL; 438b414fcd5SChris Wilson goto out; 439b414fcd5SChris Wilson } 440b414fcd5SChris Wilson 441b414fcd5SChris Wilson /* Fill the hole, further allocation attempts should then fail */ 442b414fcd5SChris Wilson obj = i915_gem_object_create_internal(i915, PAGE_SIZE); 443b414fcd5SChris Wilson if (IS_ERR(obj)) { 444b414fcd5SChris Wilson err = PTR_ERR(obj); 445b414fcd5SChris Wilson goto out; 446b414fcd5SChris Wilson } 447b414fcd5SChris Wilson 448b414fcd5SChris Wilson err = create_mmap_offset(obj); 449b414fcd5SChris Wilson if (err) { 450b414fcd5SChris Wilson pr_err("Unable to insert object into reclaimed hole\n"); 451b414fcd5SChris Wilson goto err_obj; 452b414fcd5SChris Wilson } 453b414fcd5SChris Wilson 454b414fcd5SChris Wilson if (!assert_mmap_offset(i915, PAGE_SIZE, -ENOSPC)) { 455b414fcd5SChris Wilson pr_err("Unexpectedly succeeded in inserting object into no holes!\n"); 456b414fcd5SChris Wilson err = -EINVAL; 457b414fcd5SChris Wilson goto err_obj; 458b414fcd5SChris Wilson } 459b414fcd5SChris Wilson 460b414fcd5SChris Wilson i915_gem_object_put(obj); 461b414fcd5SChris Wilson 462b414fcd5SChris Wilson /* Now fill with busy dead objects that we expect to reap */ 463b414fcd5SChris Wilson for (loop = 0; loop < 3; loop++) { 464b414fcd5SChris Wilson if (i915_terminally_wedged(i915)) 465b414fcd5SChris Wilson break; 466b414fcd5SChris Wilson 467b414fcd5SChris Wilson obj = i915_gem_object_create_internal(i915, PAGE_SIZE); 468b414fcd5SChris Wilson if (IS_ERR(obj)) { 469b414fcd5SChris Wilson err = PTR_ERR(obj); 470b414fcd5SChris Wilson goto out; 471b414fcd5SChris Wilson } 472b414fcd5SChris Wilson 473b414fcd5SChris Wilson mutex_lock(&i915->drm.struct_mutex); 474b414fcd5SChris Wilson err = make_obj_busy(obj); 475b414fcd5SChris Wilson mutex_unlock(&i915->drm.struct_mutex); 476b414fcd5SChris Wilson if (err) { 477b414fcd5SChris Wilson pr_err("[loop %d] Failed to busy the object\n", loop); 478b414fcd5SChris Wilson goto err_obj; 479b414fcd5SChris Wilson } 480b414fcd5SChris Wilson 481b414fcd5SChris Wilson /* NB we rely on the _active_ reference to access obj now */ 482b414fcd5SChris Wilson GEM_BUG_ON(!i915_gem_object_is_active(obj)); 483b414fcd5SChris Wilson err = create_mmap_offset(obj); 484b414fcd5SChris Wilson if (err) { 485b414fcd5SChris Wilson pr_err("[loop %d] create_mmap_offset failed with err=%d\n", 486b414fcd5SChris Wilson loop, err); 487b414fcd5SChris Wilson goto out; 488b414fcd5SChris Wilson } 489b414fcd5SChris Wilson } 490b414fcd5SChris Wilson 491b414fcd5SChris Wilson out: 492b414fcd5SChris Wilson drm_mm_remove_node(&resv); 493b414fcd5SChris Wilson out_park: 494b414fcd5SChris Wilson restore_retire_worker(i915); 495b414fcd5SChris Wilson return err; 496b414fcd5SChris Wilson err_obj: 497b414fcd5SChris Wilson i915_gem_object_put(obj); 498b414fcd5SChris Wilson goto out; 499b414fcd5SChris Wilson } 500b414fcd5SChris Wilson 501b414fcd5SChris Wilson int i915_gem_mman_live_selftests(struct drm_i915_private *i915) 502b414fcd5SChris Wilson { 503b414fcd5SChris Wilson static const struct i915_subtest tests[] = { 504b414fcd5SChris Wilson SUBTEST(igt_partial_tiling), 505b414fcd5SChris Wilson SUBTEST(igt_mmap_offset_exhaustion), 506b414fcd5SChris Wilson }; 507b414fcd5SChris Wilson 508b414fcd5SChris Wilson return i915_subtests(tests, i915); 509b414fcd5SChris Wilson } 510