DF-3935 / age_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 | /* * age_replica.c - deterministic replica of if_age.c's callout protocol * (DF-3715 family, age instance). * * age(4) uses ONE callout, initialized NON-MP: * callout_init(&sc->age_tick_ch); if_age.c:517 * whose handler takes the driver serializer by hand and self re-arms: * age_tick() if_age.c:2188-2200 * lwkt_serialize_enter(ifp->if_serializer); :2194 (BLOCKING) * mii_tick(mii); * callout_reset(&sc->age_tick_ch, hz, age_tick, sc);:2197 (self re-arm) * lwkt_serialize_exit(ifp->if_serializer); :2199 * * age_stop() tears it down while HOLDING that same serializer: * age_stop() if_age.c:2516-2534 * ASSERT_SERIALIZED(ifp->if_serializer); :2524 * callout_stop(&sc->age_tick_ch); :2534 * (every age_stop caller holds the serializer: age_ioctl SIOCSIFFLAGS * :1673-1674, age_init :2242+2249, age_detach :686-688, age_suspend :1393) * * DragonFly callout_stop() is synchronous (kern_timeout.c:1074-1077 -> * _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 * Neither side can progress; the interface serializer is never released * and the affected CPU's softclock thread stops delivering callouts. * * Same protocol shape as the PROVEN em(4) instance (DF-3715) except em * uses callout_init_mp callouts; age uses a plain (non-MP) callout, which * this replica matches. */ #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; /* age_tick_ch replica (non-MP) */ static volatile int tick_fired, tick_ran; static void age_tick_replica(void *arg) { tick_fired++; lwkt_serialize_enter(&ser); /* if_age.c:2194 (BLOCKING) */ tick_ran++; callout_reset(&tick_c, hz / 10, age_tick_replica, NULL); lwkt_serialize_exit(&ser); /* if_age.c:2199 */ } 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, age_tick_replica, NULL); kprintf("age_replica: armed age_tick replica " "(non-mp callout, ticks hz/10)\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); /* age_stop context: serializer held (if_age.c:2524) */ lwkt_serialize_enter(&ser); while (tick_ran == tick_fired) tsleep(&tick_ran, 0, "agew", 2); kprintf("age_replica: tick handler BLOCKED on our serializer " "(fired=%d ran=%d) - entering callout_stop()\n", tick_fired, tick_ran); kprintf("age_replica: IF THIS IS THE LAST age_replica LINE, " "AB-BA DEADLOCK PROVEN (DF-3715 family / age instance)\n"); callout_stop(&tick_c); /* if_age.c:2534 */ kprintf("age_replica: callout_stop returned (NO deadlock)\n"); lwkt_serialize_exit(&ser); return (0); } static SYSCTL_NODE(_kern, OID_AUTO, agereplica, CTLFLAG_RW, 0, "age callout replica"); SYSCTL_PROC(_kern_agereplica, OID_AUTO, arm, CTLTYPE_INT | CTLFLAG_RW, 0, 0, arm_sysctl, "I", "arm age_tick replica"); SYSCTL_PROC(_kern_agereplica, OID_AUTO, stop, CTLTYPE_INT | CTLFLAG_RW, 0, 0, stop_sysctl, "I", "age_stop vs age_tick (AB-BA)"); static int age_replica_load(struct module *m, int what, void *arg) { switch (what) { case MOD_LOAD: lwkt_serialize_init(&ser); callout_init(&tick_c); /* if_age.c:517: NON-mp */ kprintf("age_replica: loaded (hz=%d)\n", hz); return (0); case MOD_UNLOAD: callout_stop(&tick_c); return (0); } return (0); } static moduledata_t age_replica_mod = { "age_replica", age_replica_load, NULL }; DECLARE_MODULE(age_replica, age_replica_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |