DF-2915 / sqe2e.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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | /* * sqe2e.c - DF-2915 end-to-end trigger + DF-2917 demonstrator. * * Round 1 (nowake): a kthread does the real API sequence * sleepq_lock() -> subr_sleepqueue.c:177 (first use on the chain: * objcache_get() garbage -> KKASSERT :209 panics * on an INVARIANTS kernel without the DF-2915 fix) * sleepq_add() -> subr_sleepqueue.c:252 * sleepq_wait() -> subr_sleepqueue.c:387 (NO waker is ever issued) * * - kernel w/o DF-2915 fix: panics at :209 (0xAA-polluted M_SLEEPQ). * - kernel with DF-2915 fix only: sleepq_wait() returns IMMEDIATELY * with no wakeup ever issued -> DF-2917 (tsleep flags lost the * PDOMAIN_FBSD0 domain -> kern_synch.c:660-663 mismatch fast-path). * - kernel with both fixes: sleeper actually blocks; the main thread * observes it still blocked, then sleepq_broadcast() wakes it. * * Round 2 (broadcast): normal lock/add/broadcast/wait round trip. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/sleepqueue.h> #include <sys/proc.h> #include <sys/thread.h> #include <sys/kthread.h> #include <sys/module.h> extern struct malloc_type M_SLEEPQ[1]; /* subr_sleepqueue.c:107 */ /* sbticks is declared in <sys/kernel.h> (sys/kernel.h:86) */ #define WC_SIZE 48 #define SPRAY 8192 static int sq_wchan_obj; static const void *sq_wchan = &sq_wchan_obj; static volatile int sq_phase; /* 0=init 1=interlocked 2=woke */ static volatile sbintime_t sq_t0, sq_t1; static volatile int sq_ret; static void pollute_msleepq(void) { void **m; int i; m = kmalloc(SPRAY * sizeof(void *), M_TEMP, M_WAITOK | M_ZERO); for (i = 0; i < SPRAY; ++i) { m[i] = kmalloc(WC_SIZE, M_SLEEPQ, M_WAITOK); memset(m[i], 0xAA, WC_SIZE); } for (i = 0; i < SPRAY; ++i) kfree(m[i], M_SLEEPQ); kfree(m, M_TEMP); } static void sq_sleeper(void *arg) { /* * sleepq_lock() - FIRST use of this hash chain. On a kernel with * the DF-0139 hash bug this panics inside spin_lock() on a wild * chain pointer; with the DF-0139 mask test-patch but without the * DF-2915 fix this panics at the KKASSERT (:209). */ sleepq_lock(sq_wchan); /* :177 */ sleepq_add(sq_wchan, NULL, "sqdemo", SLEEPQ_SLEEP, 0); /* :252 */ sq_t0 = sbticks; sq_phase = 1; /* interlocked on sq_wchan now */ wakeup(&sq_phase); sq_ret = 0; sleepq_wait(sq_wchan, 0); /* :387 - NO wakeup will be issued */ sq_t1 = sbticks; sq_phase = 2; wakeup(&sq_phase); kprintf("SQDEMO e2e: round1 (nowake): sleepq_wait() returned " "ret=%d after %jd sbticks with NO wakeup issued\n", sq_ret, (intmax_t)(sq_t1 - sq_t0)); /* return -> kthread_exit via handler trampoline */ } static void sq_sleeper2(void *arg) { sleepq_lock(sq_wchan); sleepq_add(sq_wchan, NULL, "sqdemo2", SLEEPQ_SLEEP, 0); sq_t0 = sbticks; sq_phase = 3; wakeup(&sq_phase); sleepq_wait(sq_wchan, 0); sq_t1 = sbticks; sq_phase = 4; wakeup(&sq_phase); kprintf("SQDEMO e2e: round2 (broadcast): sleepq_wait() returned " "after %jd sbticks (broadcast was issued)\n", (intmax_t)(sq_t1 - sq_t0)); } static int sqe2e_run(void) { int i; pollute_msleepq(); kprintf("SQDEMO e2e: M_SLEEPQ polluted with 0xAA, running " "sleepq lock/add/wait round trip\n"); sq_phase = 0; kthread_create(sq_sleeper, NULL, NULL, "sq_sleeper"); /* wait (bounded) for the sleeper to become interlocked */ for (i = 0; i < 100 && sq_phase < 1; ++i) tsleep(&sq_phase, 0, "sqrdy", 1); if (sq_phase < 1) { kprintf("SQDEMO e2e: sleeper never reached sleepq_add " "(panicked?)\n"); return 0; } /* * Deliberately do NOT wake it. If sleepq_wait() really sleeps we * will still see phase==1 after ~2s; if DF-2917 is present the * sleeper completes instantly and phase becomes 2. */ for (i = 0; i < 50 && sq_phase < 2; ++i) tsleep(&sq_phase, 0, "sqwait", 4); /* 4 ticks */ if (sq_phase < 2) { kprintf("SQDEMO e2e: round1: sleeper STILL BLOCKED after " "~200 ticks with no wakeup - sleepq_wait() really " "sleeps (DF-2917 fixed); issuing broadcast\n"); sleepq_broadcast(sq_wchan, 0, 0, 0); /* :505 */ for (i = 0; i < 100 && sq_phase < 2; ++i) tsleep(&sq_phase, 0, "sqwake", 1); if (sq_phase < 2) kprintf("SQDEMO e2e: round1: broadcast failed to " "wake the sleeper!\n"); } else { kprintf("SQDEMO e2e: round1: DF-2917 CONFIRMED - " "sleepq_wait() returned without any wakeup\n"); } /* round 2: normal broadcast round trip */ sq_phase = 2; kthread_create(sq_sleeper2, NULL, NULL, "sq_sleeper2"); for (i = 0; i < 100 && sq_phase < 3; ++i) tsleep(&sq_phase, 0, "sqrd2", 1); if (sq_phase < 3) { kprintf("SQDEMO e2e: round2: sleeper2 never interlocked\n"); return 0; } tsleep(&sq_phase, 0, "sqgap", 2); /* let it block (or not) */ sleepq_broadcast(sq_wchan, 0, 0, 0); for (i = 0; i < 100 && sq_phase < 4; ++i) tsleep(&sq_phase, 0, "sqdn2", 1); kprintf("SQDEMO e2e: done (phase=%d)\n", sq_phase); return 0; } static int sqe2e_handler(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: sqe2e_run(); break; default: break; } return 0; } static moduledata_t sqe2e_mod = { "sqe2e", sqe2e_handler, NULL }; DECLARE_MODULE(sqe2e, sqe2e_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); |