DF-4025 / rereplica.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 | /* * rereplica.c - deterministic replica of if_re.c's re_timer callout protocol. * * if_re.c arms ONE MP-safe callout (callout_init_mp(&sc->re_timer), * if_re.c:604) whose handler takes the driver serializer by hand: * * re_tick() if_re.c:1354-1361 lwkt_serialize_enter (BLOCKING, * :1358) then * re_tick_serialized() if_re.c:1363-1381 IFF_RUNNING gate (:1371) + * self re-arm under the * serializer (:1380) * * re_stop(sc, TRUE) (if_re.c:1988-2029) tears it down with * callout_stop(&sc->re_timer) (:2000) while holding that same serializer * (every caller holds it: re_detach :909-911, re_suspend :2045-2047, * re_shutdown :2116-2118, re_ioctl :1918, re_init :1871), and clears * IFF_RUNNING only AFTER the stop (:2001). * * DragonFly callout_stop() is synchronous (kern_timeout.c:1091 -> * _callout_cancel_or_stop(..., sync=1), ssleep "costp"): it waits for an * INPROG callback to finish. A re_tick dequeued on another CPU that is * parked in lwkt_serialize_enter therefore deadlocks against the stopper * (AB-BA): both threads wedge permanently, the interface serializer is * never released, and the affected CPU's softclock thread stops delivering * ALL callouts on that CPU. * * Sysctls: * kern.rereplica.arm =1 arm the re_timer replica (short period for a * deterministic window; the real driver uses hz) * kern.rereplica.stop =1 re_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->re_timer replica */ static volatile int running; /* IFF_RUNNING replica */ static volatile int tick_fired, tick_ran; static void tick_timer(void *); static void tick_serialized(void *arg) /* if_re.c:1363-1381 shape */ { /* if_re.c:1371 - IFF_RUNNING gate */ if (!running) return; /* if_re.c:1380 - self re-arm UNDER the serializer */ callout_reset(&tick_c, 1, tick_timer, arg); } static void tick_timer(void *arg) /* if_re.c:1354-1361 shape */ { tick_fired++; /* if_re.c:1358 - BLOCKING acquire */ lwkt_serialize_enter(&ser); tick_ran++; tick_serialized(arg); 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) { tick_fired = tick_ran = 0; running = 1; /* if_re.c:1881 callout_reset(&sc->re_timer, hz, re_tick, sc) */ callout_reset(&tick_c, 1, tick_timer, NULL); kprintf("rereplica: armed (re_timer shape, 1-tick period)\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; /* * re_stop(sc, TRUE) shape - if_re.c:1993 (ASSERT_SERIALIZED), * :2000 callout_stop(), :2001 clear IFF_RUNNING, 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, "retkw", 2); kprintf("rereplica: re_tick parked on our serializer " "(fired=%d ran=%d) - entering callout_stop()\n", tick_fired, tick_ran); kprintf("rereplica: IF THIS IS THE LAST rereplica LINE, " "AB-BA DEADLOCK IS PROVEN\n"); callout_stop(&tick_c); /* if_re.c:2000 */ running = 0; /* if_re.c:2001 */ kprintf("rereplica: callout_stop returned (NO deadlock)\n"); lwkt_serialize_exit(&ser); return 0; } static SYSCTL_NODE(_kern, OID_AUTO, rereplica, CTLFLAG_RW, 0, "re(4) callout replica"); SYSCTL_PROC(_kern_rereplica, OID_AUTO, arm, CTLTYPE_INT | CTLFLAG_RW, 0, 0, arm_sysctl, "I", "arm re_timer replica"); SYSCTL_PROC(_kern_rereplica, OID_AUTO, stop, CTLTYPE_INT | CTLFLAG_RW, 0, 0, stop_sysctl, "I", "re_stop(TRUE) replica vs re_tick (AB-BA)"); static int rereplica_load(struct module *m, int what, void *arg) { switch (what) { case MOD_LOAD: lwkt_serialize_init(&ser); callout_init_mp(&tick_c); running = 0; kprintf("rereplica: loaded (hz=%d)\n", hz); return 0; case MOD_UNLOAD: callout_stop_async(&tick_c); return 0; } return 0; } static moduledata_t rereplica_mod = { "rereplica", rereplica_load, NULL }; DECLARE_MODULE(rereplica, rereplica_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |