DF-4327 / sf_callout_harness.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 152 153 154 | /* * sf_callout_harness.c - replica of if_sf.c's callout/serializer protocol * (DF-4327, DF-3715 family). * * if_sf.c arms one callout: * * sf_stats_update() if_sf.c:1387-1424 * lwkt_serialize_enter(ifp->if_serializer); [BLOCKING, :1396] * ... stats + mii_tick ... * callout_reset(&sc->sf_stat_timer, hz, sf_stats_update, sc); [:1421] * lwkt_serialize_exit(...); * * sf_stop() if_sf.c:1337-1377 - always called WITH the * serializer held (ioctl, detach, * watchdog, abnormal-IRQ reinit): * callout_stop(&sc->sf_stat_timer); [:1344] BLOCKING stop * * DragonFly callout_stop() = _callout_cancel_or_stop(cc, CALLOUT_STOP, 1) * (sys/kern/kern_timeout.c:1091) which ssleep("costp")s until an INPROG * callback completes (kern_timeout.c:923). If the 1s stat timer has fired * on another CPU and is blocked acquiring the serializer that the thread * inside sf_stop() holds, callout_stop() waits for the callback while the * callback waits for the serializer held by the stopper: AB-BA, permanent * wedge of the sysctl/ioctl thread *and* of that CPU's softclock thread. * * This module reproduces exactly that protocol: * * sysctl kern.sfcal.arm =1 arm the sf_stats_update-shaped timer * sysctl kern.sfcal.stop=1 (RUN IN BACKGROUND) hold the serializer, * wait until the callback is provably blocked * on it, then callout_stop() -> NEVER RETURNS. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/serialize.h> #include <sys/callout.h> #include <sys/sysctl.h> #include <sys/thread.h> static struct lwkt_serialize ser; static struct callout c; static volatile int fired, ran; static volatile int stop_entered, stop_returned, stop_ret; static void sf_timer(void *arg) { fired++; /* if_sf.c:1396 - BLOCKING acquire, exactly like sf_stats_update */ lwkt_serialize_enter(&ser); ran++; /* if_sf.c:1421 - self re-arm under the serializer */ callout_reset(&c, hz, sf_timer, 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) return (error); if (v == 1) { fired = ran = 0; stop_entered = stop_returned = 0; callout_reset(&c, 1, sf_timer, NULL); kprintf("sfcal: armed sf_stats_update-shaped timer (1 tick)\n"); } return (0); } static int stop_sysctl(SYSCTL_HANDLER_ARGS) { int error, v = 0; int spins, f0; error = sysctl_handle_int(oidp, &v, 0, req); if (error || req->newptr == NULL) return (error); if (v != 1) return (0); /* == sf_stop() entry: serializer held == */ stop_entered = 1; lwkt_serialize_enter(&ser); kprintf("sfcal[stop]: serializer held; waiting for callback to be " "provably blocked on it...\n"); /* wait until the handler has fired again but is blocked on ser */ f0 = fired; { int ran0 = ran; spins = 0; while (fired == f0 || ran != ran0) { if (++spins > 2000) { /* ~seconds-scale safety */ kprintf("sfcal[stop]: FAILED to catch blocked " "state (fired=%d ran=%d)\n", fired, ran); lwkt_serialize_exit(&ser); stop_returned = 1; return (0); } tsleep(&ran, 0, "sfcalw", 1); if (fired != f0 && ran == ran0) break; } } kprintf("sfcal[stop]: callback fired=%d ran=%d -> BLOCKED on " "serializer. calling callout_stop() under serializer " "(if_sf.c:1344 protocol)...\n", fired, ran); /* == if_sf.c:1344 - expected to NEVER return == */ stop_ret = callout_stop(&c); stop_returned = 1; /* only reached if no deadlock */ kprintf("sfcal[stop]: UNEXPECTED: callout_stop returned %d\n", stop_ret); lwkt_serialize_exit(&ser); return (0); } static SYSCTL_NODE(_kern, OID_AUTO, sfcal, CTLFLAG_RW, 0, "sf callout replica"); SYSCTL_PROC(_kern_sfcal, OID_AUTO, arm, CTLTYPE_INT | CTLFLAG_RW, 0, 0, arm_sysctl, "I", "arm the sf timer replica"); SYSCTL_PROC(_kern_sfcal, OID_AUTO, stop, CTLTYPE_INT | CTLFLAG_RW, 0, 0, stop_sysctl, "I", "deadlock replica (blocks)"); static int sfcal_modevent(module_t mod, int type, void *data) { int error = 0; switch (type) { case MOD_LOAD: lwkt_serialize_init(&ser); callout_init(&c); kprintf("sfcal: loaded (kern.sfcal.arm / kern.sfcal.stop)\n"); break; case MOD_UNLOAD: callout_stop_async(&c); break; default: break; } return (error); } static struct moduledata sf_callout_harness_mod = { "sf_callout_harness", sfcal_modevent, NULL }; DECLARE_MODULE(sf_callout_harness, sf_callout_harness_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(sf_callout_harness, 1); |