DF-4064 / fxp_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 | /* * fxp_callout_replica.c - deterministic replica of if_fxp.c's callout * protocol (pass-2, DF-4064; adapted from DF-3738's emx replica). * * if_fxp.c arms ONE NON-MPSAFE callout (plain callout_init at :399): * * fxp_tick() if_fxp.c:1441-1555 lwkt_serialize_enter( * sc->arpcom.ac_if.if_serializer) * BLOCKING at :1449 * + self re-arm at :1552 * (callout_reset(&sc->fxp_stat_timer, * hz, fxp_tick, sc) INSIDE the * serializer, before exit :1554) * * fxp_stop() tears it down with callout_stop(&sc->fxp_stat_timer) at :1577 * while holding the ifnet serializer (ASSERT_SERIALIZED :1568; callers: * fxp_detach :738, fxp_shutdown :787, fxp_suspend :805, fxp_init :1651, * fxp_ioctl SIOCSIFFLAGS :2078). Because the callout is NON-MPSAFE, the * callback also executes while holding the MP lock (kern_timeout.c:575-580), * so a parked fxp_tick wedges every BGL-dependent path system-wide. * * DragonFly callout_stop() is SYNCHRONOUS (kern_timeout.c:1091 -> * _callout_cancel_or_stop sync=1; ssleep "costp" at :920). A callback * dequeued and parked on the serializer deadlocks against the stopper * (AB-BA): stopper sleeps in callout_stop holding the serializer; the * softclock thread is parked inside the callback; and (fxp-only twist) the * MP lock is held by the parked softclock thread. * * sysctls: * kern.fxr.arm =1 arm the replica timer (hz period, fxp shape) * kern.fxr.stop_blk=1 hold serializer, wait until the handler is parked * on it (fired-but-not-ran), callout_stop() -> * must NEVER return if the AB-BA analysis is right * (fxp_stop shape; wedges the writer + a softclock * thread + the MP lock). */ #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 blk_c; /* == sc->fxp_stat_timer replica */ static volatile int blk_fired, blk_ran; static void blk_timer(void *arg) { blk_fired++; /* if_fxp.c:1449 - BLOCKING acquire of the ifnet serializer */ lwkt_serialize_enter(&ser); blk_ran++; /* if_fxp.c:1552 - self re-arm INSIDE the serializer */ callout_reset(&blk_c, hz, blk_timer, NULL); /* if_fxp.c:1554 - exit */ 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) return error; if (v == 1) { blk_fired = blk_ran = 0; callout_reset(&blk_c, 1, blk_timer, NULL); kprintf("fxr: armed (fxp_stat_timer shape, hz period)\n"); } return 0; } static int stop_blk_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; /* * AB-BA test (fxp_stop shape): take the serializer, wait until the * blocking handler is parked inside lwkt_serialize_enter (fired but * not ran), then callout_stop(). */ lwkt_serialize_enter(&ser); while (blk_ran == blk_fired) tsleep(&blk_ran, 0, "blkw", 2); kprintf("fxr: handler BLOCKED on our serializer (fired=%d ran=%d)" " - entering callout_stop() [fxp_stop:1577 shape]\n", blk_fired, blk_ran); kprintf("fxr: IF THIS IS THE LAST fxr LINE, AB-BA DEADLOCK IS PROVEN" " (writer stuck D-'costp', softclock B-'slize', MP lock held)\n"); callout_stop(&blk_c); kprintf("fxr: callout_stop returned (NO deadlock)\n"); lwkt_serialize_exit(&ser); return 0; } static SYSCTL_NODE(_kern, OID_AUTO, fxr, CTLFLAG_RW, 0, "fxp callout replica"); SYSCTL_PROC(_kern_fxr, OID_AUTO, arm, CTLTYPE_INT | CTLFLAG_RW, 0, 0, arm_sysctl, "I", "arm fxp_stat_timer replica"); SYSCTL_PROC(_kern_fxr, OID_AUTO, stop_blk, CTLTYPE_INT | CTLFLAG_RW, 0, 0, stop_blk_sysctl, "I", "fxp_stop vs blocking fxp_tick (AB-BA)"); static int fxp_callout_replica_load(struct module *m, int what, void *arg) { switch (what) { case MOD_LOAD: lwkt_serialize_init(&ser); callout_init(&blk_c); /* NON-MP: exactly if_fxp.c:399 */ kprintf("fxp_callout_replica: loaded (hz=%d)\n", hz); return 0; case MOD_UNLOAD: /* only safe if the AB-BA test was not run to the deadlock */ callout_stop(&blk_c); return 0; } return 0; } static moduledata_t fxp_callout_replica_mod = { "fxp_callout_replica", fxp_callout_replica_load, NULL }; DECLARE_MODULE(fxp_callout_replica, fxp_callout_replica_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |