DF-4046 / stgereplica.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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /* * stgereplica.c - deterministic replica of if_stge.c's sc_tick_ch callout * protocol (stge(4) instance of the DF-3715(em) / DF-4025(re) AB-BA family). * * if_stge.c arms ONE callout with plain callout_init() -- the NON-MPSAFE * variant, dispatched by softclock under the MPLOCK (if_stge.c:587, * kern_timeout.c:575-590) -- whose handler takes the driver serializer by * hand and re-arms UNCONDITIONALLY while holding it: * * stge_tick() if_stge.c:1752-1778 * :1758 lwkt_serialize_enter (BLOCKING acquire) * :1761 mii_tick / :1764 stge_stats_update / * :1772-1773 conditional stge_txeof * :1775 callout_reset(..., hz, stge_tick, sc) <-- no * IFF_RUNNING gate at all (unlike re_tick_serialized) * :1777 lwkt_serialize_exit * * stge_stop() (if_stge.c:2098-2169) tears it down with * callout_stop(&sc->sc_tick_ch) at if_stge.c:2111 -- the FIRST action after * ASSERT_SERIALIZED -- while HOLDING that serializer. Every stge_stop * caller holds it: stge_detach :820-825, stge_shutdown :1049-1051, * stge_suspend :1060-1063, stge_ioctl SIOCSIFFLAGS :1280, stge_init * :1900/:1907 (itself reachable from stge_ioctl SIOCSIFMTU :1265, the * watchdog :1238, and stge_intr's force_init :1438-1440). IFF_RUNNING is * cleared only at the very END of stge_stop (:2166). * * DragonFly callout_stop() is synchronous (kern_timeout.c:1091-1093 -> * _callout_cancel_or_stop(..., sync=1); ssleep "costp" at :920): it waits * for an INPROG callback to finish. A stge_tick dequeued by a softclock * thread that is parked in lwkt_serialize_enter therefore deadlocks * against the stopper (AB-BA): both threads wedge permanently, the * interface serializer is never released (all later ioctls/if_start on * stgeX block), and the affected softclock stops delivering ALL callouts * on that CPU. stge_tick's unconditional re-arm (:1775) additionally * means nothing in the driver can prevent the parked callback from * re-arming once it eventually runs. * * Sysctls: * kern.stgereplica.arm =1 arm the stge_tick replica (short period for * a deterministic window; real driver uses hz) * kern.stgereplica.stop =1 stge_stop replica: hold serializer, wait * until the handler is parked, callout_stop() * -> if the AB-BA analysis is right this NEVER * returns. */ #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; /* sc->sc_tick_ch replica */ static volatile int running; /* IFF_RUNNING replica */ static volatile int tick_fired, tick_ran; static void tick_handler(void *); static void tick_handler(void *arg) /* if_stge.c:1752-1778 shape */ { tick_fired++; /* pre-acquire */ lwkt_serialize_enter(&ser); /* if_stge.c:1758 (BLOCKING) */ tick_ran++; /* mii_tick / stats / conditional txeof elided */ callout_reset(&tick_c, 1, tick_handler, arg); /* :1775, UNCONDITIONAL */ lwkt_serialize_exit(&ser); /* if_stge.c:1777 */ } 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; running = 1; /* if_stge.c:2059 callout_reset(&sc->sc_tick_ch, hz, ...) */ callout_reset(&tick_c, 1, tick_handler, NULL); kprintf("stgereplica: armed (stge_tick shape, 1-tick period; " "stge uses 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; /* * stge_stop() shape - if_stge.c:2106 ASSERT_SERIALIZED, * :2111 callout_stop(), :2166 clear IFF_RUNNING (last), in that * order, all while HOLDING the serializer. */ lwkt_serialize_enter(&ser); while (tick_ran == tick_fired) /* wait until handler is parked */ tsleep(&tick_ran, 0, "stgetkw", 2); kprintf("stgereplica: stge_tick parked on our serializer " "(fired=%d ran=%d) - entering callout_stop()\n", tick_fired, tick_ran); kprintf("stgereplica: IF THIS IS THE LAST stgereplica LINE, " "AB-BA DEADLOCK IS PROVEN\n"); callout_stop(&tick_c); /* if_stge.c:2111 */ running = 0; /* if_stge.c:2166 */ kprintf("stgereplica: callout_stop returned (NO deadlock)\n"); lwkt_serialize_exit(&ser); return 0; } static SYSCTL_NODE(_kern, OID_AUTO, stgereplica, CTLFLAG_RW, 0, "stge(4) callout replica"); SYSCTL_PROC(_kern_stgereplica, OID_AUTO, arm, CTLTYPE_INT | CTLFLAG_RW, 0, 0, arm_sysctl, "I", "arm stge_tick replica"); SYSCTL_PROC(_kern_stgereplica, OID_AUTO, stop, CTLTYPE_INT | CTLFLAG_RW, 0, 0, stop_sysctl, "I", "stge_stop replica vs stge_tick (AB-BA)"); static int stgereplica_load(struct module *m, int what, void *arg) { switch (what) { case MOD_LOAD: lwkt_serialize_init(&ser); callout_init(&tick_c); /* if_stge.c:587: plain NON-MP init */ running = 0; kprintf("stgereplica: loaded (hz=%d)\n", hz); return 0; case MOD_UNLOAD: callout_stop_async(&tick_c); return 0; } return 0; } static moduledata_t stgereplica_mod = { "stgereplica", stgereplica_load, NULL }; DECLARE_MODULE(stgereplica, stgereplica_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |