DF-4217 / stoploop.c
/* * DF-4217 stoploop.c โ termination model of the two idle-wait loops in * ae_stop_rxmac()/ae_stop_txmac() (sys/dev/netif/ae/if_ae.c:288 and :324). * * Both loops are written: * * for (i = 0; i < AE_IDLE_TIMEOUT; i--) { <-- i--, not i++ * val = AE_READ_4(sc, AE_IDLE_REG); * if ((val & (AE_IDLE_RXMAC | AE_IDLE_DMAWRITE)) == 0) * break; * DELAY(100); * } * if (i == AE_IDLE_TIMEOUT) { <-- unreachable: i never rises * if_printf(... "timed out while stopping ... MAC.\n"); * } * * AE_IDLE_TIMEOUT is 100 (if_aevar.h:41), i.e. an intended 10 ms wait. * But i starts at 0 and is only ever DECREMENTED, so (i < 100) is true * on every iteration forever: unless the device happens to report idle, * the loop NEVER terminates โ it busy-waits in DELAY(100) holding * ifp->if_serializer โ and the post-loop timeout branch can never fire * because i can never equal 100. (Compare the correct sibling loops: * ae_stop() itself :255 and ae_reset() :1510 both use i++.) * * Trigger reality: AE_READ_4 on a surprise-removed / halted / errored * device returns 0xFFFFFFFF, so the IDLE bits stay set and the loop is * fed forever. Reachable from every ae_stop()/ae_stop_{r,t}xmac() * caller: ifconfig down/up, watchdog, init-from-intr, suspend, detach, * miibus statchg. * * This model runs the exact guard/increment/exit shape of if_ae.c:288 * with a register stub that never idles, self-capped at 10,000,000 * iterations so the model itself terminates (the kernel loop has no * such cap). The patched (i++) variant is then run against the same * stub to show it terminates at exactly AE_IDLE_TIMEOUT reads. */ #include <stdio.h> #define AE_IDLE_TIMEOUT 100 #define IDLE_BITS 0x00000030 /* AE_IDLE_RXMAC | AE_IDLE_DMAWRITE */ static unsigned long nreads; static unsigned int dead_device_idle_reg(void) { nreads++; /* AE_READ_4: surprise-removed device */ return (0xFFFFFFFFu); /* -> IDLE bits always set */ } int main(void) { int i; unsigned long iters; int timeout_branch_taken; printf("DF-4217 model: ae_stop_rxmac/ae_stop_txmac idle-wait " "termination\n(sys/dev/netif/ae/if_ae.c:288 and :324, " "AE_IDLE_TIMEOUT=%d)\n\n", AE_IDLE_TIMEOUT); /* [1] as written: for (i = 0; i < AE_IDLE_TIMEOUT; i--) */ puts("[1] as-written (i--) vs a device that never reports idle:"); iters = 0; for (i = 0; i < AE_IDLE_TIMEOUT; i--) { /* if_ae.c:288/:324 */ unsigned int val = dead_device_idle_reg(); iters++; if ((val & IDLE_BITS) == 0) break; /* device idled */ if (iters >= 10000000UL) { puts(" MODEL SELF-CAP HIT (10,000,000 iterations) " "-- the kernel loop has NO cap;"); puts(" guard (i < 100) is permanently true because " "i only decrements from 0."); break; } } timeout_branch_taken = (i == AE_IDLE_TIMEOUT); /* the post-loop test */ printf(" iterations executed (model-capped): %lu\n", iters); printf(" post-loop timeout branch `i == AE_IDLE_TIMEOUT` taken: %s" " (i=%d)\n", timeout_branch_taken ? "YES" : "NO -- unreachable by construction", i); /* [2] fixed: for (i = 0; i < AE_IDLE_TIMEOUT; i++) */ nreads = 0; for (i = 0; i < AE_IDLE_TIMEOUT; i++) { unsigned int val = dead_device_idle_reg(); if ((val & IDLE_BITS) == 0) break; } printf("[2] fixed (i++): same dead device -> loop exits after exactly " "%lu reads\n (10 ms), then the \"timed out while stopping " "Rx/Tx MAC.\" message fires. Bounded either way.\n\n", nreads); puts("TRIGGER_RC=0 (model OK; hardware-gated on this guest: " "no Attansic L2 NIC)"); return (0); } |