DF-4820 / harness_env.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* * DF-4820 — mock environment for the verbatim radeon_ring_restore() body. * * Reproduces, in userspace, the exact kernel conditions that hold when * radeon_ring_restore() is called from radeon_gpu_reset() * (sys/dev/drm/radeon/radeon_device.c:1879-1887): * * - radeon_ring_lock() -> radeon_ring_alloc() fails with an error. * Two deterministic failure modes exist at that point, both proven in * the surrounding kernel source: * * (1) -EDEADLK: the GPU was just reset, so rdev->needs_reset is still * true (it is only cleared at radeon_device.c:1921-1922, AFTER the * restore loop). radeon_fence_wait_seq_timeout() * (radeon_fence.c:511-517) has `|| rdev->needs_reset` in its wait * condition and converts it to -EDEADLK. * * (2) -ENOENT: no fences pending (radeon_fence.c:649-654). * * alloc() can only reach the fence wait when the aligned request equals * ring_size/4 dwords — i.e. the ring was saturated (within align_mask * dwords of full) when the GPU hung. On the fresh post-reset ring * (ring->wptr = 0, cik.c:4104) free_dw maxes at ring_size/4, so the * request can never be satisfied -> error return is guaranteed. * * - `data` points to a kvmalloc_array() allocation made by * radeon_ring_backup() (radeon_ring.c:317). The caller * (radeon_device.c:1881-1882) ignores radeon_ring_restore()'s return * value and never frees `data` on the success-of-reset branch. * * kvmalloc_array()/kvfree() are instrumented to track live allocations. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <stdbool.h> /* ---- kernel types / return codes (minimal mock) ---------------------- */ /* values are cosmetic for the mock; only sign matters to the body */ #define ENOENT 2 #define EDEADLK 35 #define ENOMEM 12 struct radeon_device { int dummy; }; struct radeon_ring { unsigned ring_size; /* bytes, power of 2 */ unsigned align_mask; unsigned ptr_mask; unsigned ring_free_dw; }; /* ---- allocation accounting (all test blocks are the same size) ------- */ static unsigned long live_allocs; static unsigned long total_allocs; static unsigned long block_bytes; static void *kvmalloc_array(size_t n, size_t sz, unsigned gfp) { void *p = malloc(n * sz); if (p) { live_allocs++; total_allocs++; } return p; } static void kvfree(void *p) { if (p) { if (live_allocs == 0) { fprintf(stderr, "kvfree of untracked pointer %p!\n", p); exit(3); } live_allocs--; free(p); } } /* ---- mocked collaborators -------------------------------------------- */ /* error injected by radeon_ring_lock(); -EDEADLK (mode 1) or -ENOENT (2) */ static int lock_err = -EDEADLK; static int radeon_ring_lock(struct radeon_device *rdev, struct radeon_ring *ring, unsigned ndw) { /* * Model of radeon_ring_alloc() (radeon_ring.c:104-127) reduced to * its decision on the fresh (just-resumed, wptr=0) ring: * * ndw = 16383 (saturated-ring backup size: full - 1 dw) * aligned = (16383 + 0xf) & ~0xf = 16384 == ring->ring_size/4 * while (16384 > 16384 - 1): * radeon_ring_free_size -> free_dw = 16384 (maximum) * ndw < free_dw ? 16384 < 16384 ? no * radeon_fence_wait_next -> -EDEADLK / -ENOENT -> return r */ unsigned aligned = (ndw + ring->align_mask) & ~ring->align_mask; if (aligned == ring->ring_size / 4) { printf(" [alloc-model] ndw=%u aligned=%u == ring_size/4=%u:" " unsatisfiable on fresh ring -> fence wait -> error %d\n", ndw, aligned, ring->ring_size / 4, lock_err); return lock_err; } return 0; } static void radeon_ring_write(struct radeon_ring *ring, uint32_t v) { } static void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *ring, bool hdp_flush) { /* successful path: body then calls kvfree(data) itself */ } |