DF-5261 / drain_fifo_sim.c
/* * DF-5261 - mtw_drain_fifo() unbounded loop whose exit depends on a * device-controlled bit, where the error value keeps the loop alive. * * if_mtw.c:2173-2220: * for (;;) { * mtw_read(sc, MTW_TX_STAT_FIFO, &stat); (return ignored) * if (!(stat & MTW_TXQ_VLD)) break; * ... * } * MTW_TXQ_VLD = (1 << 0) (if_mtwreg.h:702). * mtw_read() (if_mtw.c:1284-1285) sets *val = 0xffffffff on USB error. * 0xffffffff & (1<<0) != 0 -> an unreachable/gone device (unplug, * stall) makes the loop spin forever with the driver lock held, in the * net80211 taskqueue thread (queued from mtw_tx(), if_mtw.c:3206-3216, * HOSTAP/multi-vap modes). * * This harness reproduces the control flow exactly and shows the * iteration count diverges (capped) when the device read starts failing, * versus terminating at 16 (the hardware FIFO depth) when it works. */ #include <stdio.h> #define MTW_TXQ_VLD (1 << 0) static int device_gone; static int real_count = 16; /* HW TX status FIFO has 16 slots */ static int mtw_read_sim(unsigned long *val) { if (device_gone) { /* device unplugged/stalled: every read errors out and * mtw_read() stores 0xffffffff (if_mtw.c:1284-1285) */ *val = 0xffffffff; return (-1); } if (real_count > 0) { real_count--; *val = 0x00000001; /* VLD set: a real stat entry */ return (0); } *val = 0; /* FIFO drained: VLD clear */ return (0); } static unsigned long drain_fifo_sim(int gone) { unsigned long stat, iters = 0; const unsigned long CAP = 100000000ULL; /* 1e8: proof of divergence */ device_gone = gone; real_count = 16; for (;;) { mtw_read_sim(&stat); if (!(stat & MTW_TXQ_VLD)) break; if (++iters >= CAP) /* real kernel loop has no such cap */ return (CAP); } return (iters); } int main(void) { unsigned long ok; unsigned long gone = drain_fifo_sim(1); ok = drain_fifo_sim(0); printf("device healthy : drain_fifo exits after %lu iterations (16 VLD entries then FIFO empty)\n", ok); printf("device failing : drain_fifo iterations = %lu%s\n", gone, gone >= 100000000ULL ? " (hit harness cap - KERNEL LOOP WOULD NEVER EXIT)" : ""); return (gone >= 100000000ULL ? 0 : 1); } |