⬢ DragonFlyBSD Kernel Audit
DF-5223 / sim_accounting.c
← back to finding ↓ download raw
/*
 * DF-5223 sim_accounting.c — static arithmetic simulator of the radeon
 * DMA-ring dword accounting for rv770_copy_dma() (sys/dev/drm/radeon/rv770_dma.c).
 *
 * It mirrors, 1:1, the kernel semantics:
 *
 *   radeon_ring_alloc()  [radeon_ring.c:106-131]:
 *       ndw = (ndw + ring->align_mask) & ~ring->align_mask;   // :114
 *       ...
 *       ring->count_dw = ndw;                                 // :124
 *       (alloc loop guarantees ring_free_dw - 1 >= ndw at reservation time)
 *
 *   radeon_ring_write()  [radeon.h:2708-2716]:
 *       if (ring->count_dw <= 0)
 *               DRM_ERROR("radeon: writing more dwords to the ring than expected!");
 *       ring->ring[ring->wptr++] = v;          // written ANYWAY
 *       ring->wptr &= ring->ptr_mask;
 *       ring->count_dw--;
 *
 *   radeon_ring_commit()  [radeon_ring.c:157-185]:
 *       while (ring->wptr & ring->align_mask)
 *               radeon_ring_write(ring, ring->nop);           // :173
 *
 * rv770_copy_dma() consumers on the R600 DMA ring (rv770_dma.c:59-91):
 *   - S semaphore-wait packets emitted by radeon_sync_rings()
 *     [radeon_sync.c:187 -> r600_dma_semaphore_ring_emit, r600_dma.c:319-321]
 *     = 3 dwords each; on RV770 the possible source rings are GFX and UVD
 *     (same-ring fences excluded, radeon_fence.c:768-770) => S in {0,1,2},
 *     built from resv excl + shared-list fences (radeon_sync_resv,
 *     radeon_sync.c:101-123; shared fences added for read-only relocations,
 *     radeon_cs.c:182 + ttm_execbuf_util.c:203-206).
 *   - num_loops copy packets = 5 dwords each (rv770_dma.c:74-78).
 *   - fence emit = 5 dwords (r600_dma_fence_ring_emit, r600_dma.c:292-297).
 *
 * Reservation: radeon_ring_lock(rdev, ring, num_loops * 5 + 8)  [rv770_dma.c:59].
 * align_mask for the DMA ring is 16-1 (r600.c:2798). wptr at lock time is
 * 16-aligned because every prior commit padded it (radeon_ring.c:173).
 *
 * The simulator prints every (S, L) combination for which writes exceed the
 * reserved window — i.e. exactly the conditions in which the kernel emits
 * dwords past its reserved ring window (DRM_ERROR + wptr advancing past the
 * region the alloc-loop guaranteed free; if the ring was at its minimum free
 * space (reserved+1), wptr overtakes rptr and un-executed GPU commands from
 * previously committed work get overwritten).
 */
#include <stdio.h>

int main(void)
{
	const unsigned ALIGN_MASK = 16 - 1;	/* r600.c:2798 (DMA ring) */
	const unsigned PTR_MASK   = 0xFFFFFu;	/* big enough: no wrap in spans modeled */
	int hits = 0;

	printf("DF-5223: rv770_copy_dma DMA-ring accounting simulation\n");
	printf("reserve  = num_loops*5 + 8            (rv770_dma.c:59)\n");
	printf("consumers= 3*S semaphore waits         (radeon_sync.c:187, r600_dma.c:319-321)\n");
	printf("         + 5*num_loops copy dwords     (rv770_dma.c:74-78)\n");
	printf("         + 5 fence dwords              (r600_dma.c:292-297)\n");
	printf("         + commit nop padding to 16    (radeon_ring.c:173)\n\n");

	for (int S = 0; S <= 2; S++) {
		for (int L = 1; L <= 64; L++) {
			unsigned req = (unsigned)L * 5 + 8;		       /* rv770_dma.c:59 */
			unsigned res = (req + ALIGN_MASK) & ~ALIGN_MASK;      /* radeon_ring.c:114 */
			int count_dw = (int)res;			       /* radeon_ring.c:124 */
			unsigned wptr = 0;	/* 16-aligned: prior commit padded */
			unsigned over = 0;
			unsigned consumed = 0;

			for (int i = 0; i < S; i++)			       /* semaphore waits */
				for (int j = 0; j < 3; j++) {
					if (count_dw <= 0) over++;
					count_dw--; consumed++;
					wptr = (wptr + 1) & PTR_MASK;
				}
			for (int i = 0; i < L; i++)			       /* copy packets */
				for (int j = 0; j < 5; j++) {
					if (count_dw <= 0) over++;
					count_dw--; consumed++;
					wptr = (wptr + 1) & PTR_MASK;
				}
			for (int j = 0; j < 5; j++) {			       /* fence emit */
				if (count_dw <= 0) over++;
				count_dw--; consumed++;
				wptr = (wptr + 1) & PTR_MASK;
			}
			while (wptr & ALIGN_MASK) {			       /* commit pad */
				if (count_dw <= 0) over++;
				count_dw--; consumed++;
				wptr = (wptr + 1) & PTR_MASK;
			}
			if (over) {
				printf("DEFICIT: S=%d num_loops=%-3d reserved=%-4u "
				       "consumed=%-4u overflow=%u dwords past reserved window"
				       " (DRM_ERROR; wptr may overtake rptr)\n",
				       S, L, res, consumed, over);
				hits++;
			}
		}
	}
	printf("\n%d (S,L) combinations overflow the reservation.\n", hits);
	printf("%s\n", hits ? "DEFICIT CONFIRMED (static arithmetic; hardware-gated runtime)"
			     : "no deficit found");
	return hits ? 1 : 0;
}