DF-4218 / ae_replica.c
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | /* * ae_replica.c โ deterministic replica of if_ae.c's callout protocol * (DF-3715 family, ae instance; adapted from the PROVEN ale replica * DF-3991 / age replica DF-3935 โ same vendor driver family, identical * protocol shape, character-for-character the same dance as ae(4)). * * ae(4) uses ONE callout, initialized NON-MP: * callout_init(&sc->ae_tick_ch); if_ae.c:1262 * whose handler takes the driver serializer by hand and self re-arms: * ae_tick() if_ae.c:988-999 * lwkt_serialize_enter(ifp->if_serializer); :995 (BLOCKING) * mii_tick(mii); * callout_reset(&sc->ae_tick_ch, hz, ae_tick, sc); :997 (self re-arm) * lwkt_serialize_exit(ifp->if_serializer); :998 * * ae_stop() tears it down while HOLDING that same serializer: * ae_stop() if_ae.c:219-262 * ASSERT_SERIALIZED(ifp->if_serializer); :225 * callout_stop(&sc->ae_tick_ch); :232 * (every ae_stop caller holds the serializer: ae_ioctl SIOCSIFFLAGS * "down" :1220-1221, ae_init :799 โ also reached from the ae_intr * error path :770-775 IN INTERRUPT CONTEXT (DMAR/DMAW timeout, * PHY linkdown) โ, ae_watchdog :983, ae_detach :1417, * ae_suspend :1786, ae_shutdown :1801.) * * DragonFly callout_stop() is synchronous (kern_timeout.c:1091-1094 -> * _callout_cancel_or_stop(..., sync=1) -> ssleep(c, ..., "costp", 0) at * kern_timeout.c:923). A callback dequeued and parked inside * lwkt_serialize_enter therefore deadlocks against the stopper (AB-BA): * stopper: holds serializer -> sleeps in callout_stop waiting for the * INPROG callback * callback: INPROG -> blocked in lwkt_serialize_enter waiting for the * serializer the stopper holds (and, being a NON-MP callout, * it parked there holding the Giant/mplock) * Neither side can progress; the interface serializer is never released * and the affected CPU's softclock thread stops delivering callouts. * * Replica acceleration: ae_tick's real period is hz (1 s); the replica * ticks at hz/10 purely to make the park observable immediately. The * protocol (blocking enter + self re-arm + sync stop under serializer) * is identical. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/serialize.h> #include <sys/callout.h> #include <sys/sysctl.h> #include <sys/systm.h> static struct lwkt_serialize ser; /* ifp->if_serializer replica */ static struct callout tick_c; /* ae_tick_ch replica (non-MP) */ static volatile int tick_fired, tick_ran; static void ae_tick_replica(void *arg) { tick_fired++; lwkt_serialize_enter(&ser); /* if_ae.c:995 (BLOCKING) */ tick_ran++; callout_reset(&tick_c, hz / 10, ae_tick_replica, NULL); /* :997 */ lwkt_serialize_exit(&ser); /* if_ae.c:998 */ } static int arm_sysctl(SYSCTL_HANDLER_ARGS) { int error, v = 0; error = sysctl_handle_int(oidp, &v, 0, req); if (error || req->newptr == NULL) return (error); if (v == 1) { tick_fired = tick_ran = 0; callout_reset(&tick_c, 1, ae_tick_replica, NULL); kprintf("ae_replica: armed ae_tick replica " "(non-mp callout, ticks hz/10; real ae_tick is hz)\n"); } return (0); } static int stop_sysctl(SYSCTL_HANDLER_ARGS) { int error, v = 0; error = sysctl_handle_int(oidp, &v, 0, req); if (error || req->newptr == NULL) return (error); if (v != 1) return (0); /* ae_stop context: serializer held (if_ae.c:225) */ lwkt_serialize_enter(&ser); while (tick_ran == tick_fired) tsleep(&tick_ran, 0, "aewait", 2); kprintf("ae_replica: tick handler BLOCKED on our serializer " "(fired=%d ran=%d) - entering callout_stop()\n", tick_fired, tick_ran); kprintf("ae_replica: IF THIS IS THE LAST ae_replica LINE, " "AB-BA DEADLOCK PROVEN (DF-3715 family / ae instance)\n"); callout_stop(&tick_c); /* if_ae.c:232 */ kprintf("ae_replica: callout_stop returned (NO deadlock)\n"); lwkt_serialize_exit(&ser); return (0); } static SYSCTL_NODE(_kern, OID_AUTO, aereplica, CTLFLAG_RW, 0, "ae callout replica"); SYSCTL_PROC(_kern_aereplica, OID_AUTO, arm, CTLTYPE_INT | CTLFLAG_RW, 0, 0, arm_sysctl, "I", "arm ae_tick replica"); SYSCTL_PROC(_kern_aereplica, OID_AUTO, stop, CTLTYPE_INT | CTLFLAG_RW, 0, 0, stop_sysctl, "I", "ae_stop vs ae_tick (AB-BA)"); static int ae_replica_load(struct module *m, int what, void *arg) { switch (what) { case MOD_LOAD: lwkt_serialize_init(&ser); callout_init(&tick_c); /* if_ae.c:1262: NON-mp */ kprintf("ae_replica: loaded (hz=%d)\n", hz); return (0); case MOD_UNLOAD: callout_stop(&tick_c); return (0); } return (0); } static moduledata_t ae_replica_mod = { "ae_replica", ae_replica_load, NULL }; DECLARE_MODULE(ae_replica, ae_replica_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |