DF-3903 / sk_callout_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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | /* * sk_callout_replica.c - deterministic replica of if_sk.c's callout protocol. * * if_sk.c arms ONE legacy callout per port for its 1 Hz tick: * * sk_attach() if_sk.c:1311 callout_init(&sc_if->sk_tick_timer) * (plain, NON-MPSAFE -> callback executes * under the Giant MP lock) * sk_yukon_tick() if_sk.c:2081 lwkt_serialize_enter(ifp->if_serializer) * (:2087, BLOCKING) * mii_tick(mii) ... under the serializer * self re-arm at :2089 (UNCONDITIONAL), * exit at :2090 * sk_tick() if_sk.c:2032 lwkt_serialize_enter (:2041, BLOCKING), * conditional self re-arm at :2067 while the * link is down (GPIO poll loop :2061-2070) * sk_stop() if_sk.c:2770 ASSERT_SERIALIZED (:2778) then * callout_stop(&sc_if->sk_tick_timer) (:2782) * * EVERY caller of sk_stop() holds ifp->if_serializer (= &sc->sk_serializer, * the one card-global serializer both ports share, ether_ifattach at :1281): * sk_ioctl SIOCSIFFLAGS-down :992, SIOCSIFMTU -> sk_init :977 -> * sk_stop :2606, sk_watchdog :1758, sk_detach :1540, skc_shutdown path. * Additional re-arm sites under the same serializer: sk_intr_bcom :2138, * sk_intr_xmac :2159 (ISR context). * * DragonFly callout_stop() is SYNCHRONOUS (kern_timeout.c:1091 -> * :859-932, ssleep 'costp' at :920) - it waits for an INPROG callback. * * => A sk_yukon_tick/sk_tick dequeued and parked in * lwkt_serialize_enter() on the serializer held by the sk_stop caller * deadlocks against it (AB-BA): the stopper sleeps forever in * callout_stop, the tick sleeps forever on the serializer, that CPU's * softclock thread stops delivering ALL callouts, and - because * sk_tick_timer is a non-MPSAFE callout - the parked softclock thread * holds the Giant MP lock while it sleeps. * * Sysctls (root): * kern.skreplica.arm = 1 arm the sk_yukon_tick-shaped timer (1 tick) * kern.skreplica.stop = 1 enter the serializer (sk_stop context), wait * until the tick is parked on it, then * callout_stop() -- NEVER RETURNS on the AB-BA. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/sysctl.h> #include <sys/serialize.h> #include <sys/callout.h> static struct lwkt_serialize ser; /* sc->sk_serializer replica */ static struct callout tick_c; /* sc_if->sk_tick_timer replica (NON-MPSAFE) */ static volatile int tick_fired; /* bumped before the park point */ static volatile int tick_ran; /* bumped only when acquired */ static void sk_tick_replica(void *arg) { tick_fired++; /* if_sk.c:2087 - BLOCKING acquire (park point) */ lwkt_serialize_enter(&ser); tick_ran++; /* if_sk.c:2089 - unconditional self re-arm under the serializer */ callout_reset(&tick_c, hz, sk_tick_replica, NULL); lwkt_serialize_exit(&ser); } static int arm_sysctl(SYSCTL_HANDLER_ARGS) { int error, v = 0; error = sysctl_handle_int(oidp, &v, 0, req); if (error || req->newptr == NULL || v == 0) return (error); callout_reset(&tick_c, 1, sk_tick_replica, NULL); kprintf("skreplica[arm]: sk_tick_timer armed (1 tick), waiting\n"); return (0); } static int stop_sysctl(SYSCTL_HANDLER_ARGS) { int error, v = 0; int i, base; error = sysctl_handle_int(oidp, &v, 0, req); if (error || req->newptr == NULL || v == 0) return (error); /* * sk_stop context (if_sk.c:2770-2782): serializer held while * calling callout_stop(). */ lwkt_serialize_enter(&ser); base = tick_fired; for (i = 0; i < 5000; i++) { /* wait <= 5s for a tick fire */ if (tick_fired != base && tick_ran == base) break; /* fired but parked on us */ DELAY(1000); } if (tick_fired != base) { kprintf("skreplica[stop]: tick INPROG and PARKED on the " "serializer (fired=%d ran=%d)\n", tick_fired, tick_ran); kprintf("skreplica[stop]: IF THIS IS THE LAST skreplica LINE " "callout_stop NEVER RETURNED (AB-BA deadlock)\n"); } else { kprintf("skreplica[stop]: no tick fired within 5s; " "callout_stop will trivially return\n"); } callout_stop(&tick_c); /* if_sk.c:2782 */ kprintf("skreplica[stop]: callout_stop RETURNED (no deadlock in " "this interleave)\n"); lwkt_serialize_exit(&ser); return (0); } static int skreplica_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: lwkt_serialize_init(&ser); callout_init(&tick_c); /* plain: NON-MPSAFE, like if_sk.c:1311 */ tick_fired = tick_ran = 0; sysctl_add_oid(NULL, SYSCTL_STATIC_CHILDREN(_kern), OID_AUTO, "skreplica_arm", CTLTYPE_INT|CTLFLAG_RW, NULL, 0, arm_sysctl, "I", "arm sk tick replica"); sysctl_add_oid(NULL, SYSCTL_STATIC_CHILDREN(_kern), OID_AUTO, "skreplica_stop", CTLTYPE_INT|CTLFLAG_RW, NULL, 0, stop_sysctl, "I", "sk_stop replica (callout_stop " "under serializer)"); kprintf("skreplica: loaded (if_sk.c callout protocol replica)\n"); return (0); case MOD_UNLOAD: return (EBUSY); /* nothing to clean while wedged */ } return (EOPNOTSUPP); } static moduledata_t skreplica_mod = { "skreplica", skreplica_load, NULL }; DECLARE_MODULE(skreplica, skreplica_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |