DF-2849 / gtq_race2.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 | /* * DF-2849 PoC variant B — high-contention hunter for the ta_flags * lost-update (mode 2): detacher's UNLOCKED "ta_flags &= ~TASK_NOENQUEUE" * (subr_gtaskqueue.c:747) vs racer's locked "ta_flags |= TASK_ENQUEUED" * (subr_gtaskqueue.c:219). A lost TASK_ENQUEUED bit while the gtask is * linked in the STAILQ makes the next enqueue double-insert -> self-loop. * * 4 racer threads (cpus 1,3,4,5) x 8 grouptasks x 2M attach/detach cycles * on the detacher (cpu 2). Detection: fn_count still growing after all * enqueuers stopped == STAILQ self-loop (worker re-runs task forever). */ #include <sys/param.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/lock.h> #include <sys/gtaskqueue.h> #include <sys/thread.h> #include <sys/thread2.h> #define NTASK 8 #define NRACER 4 static struct taskqgroup *rg; static struct grouptask gts[NTASK]; static volatile int stop; static volatile long enq_ok, enq_eagain, enq_null, fn_count; static struct thread *racer_td[NRACER], *det_td; static void gt_fn(void *ctx) { atomic_add_long(&fn_count, 1); } static void racer(void *arg) { int idx = (int)(intptr_t)arg; long spin = 0; while (!stop) { struct gtaskqueue *q = gts[idx].gt_taskqueue; if (q == NULL) { atomic_add_long(&enq_null, 1); continue; } if (grouptaskqueue_enqueue(q, >s[idx].gt_task) == 0) atomic_add_long(&enq_ok, 1); else atomic_add_long(&enq_eagain, 1); /* * Yield periodically: a pinned LWKT daemon-priority thread * that never blocks starves userland/protocol threads on its * CPU and wedges the box for reasons unrelated to the bug * under test. */ if ((++spin & 0xff) == 0) lwkt_yield(); } lwkt_exit(); } static void detacher(void *arg) { int i, loops = 2000000; for (i = 0; i < loops && !stop; i++) { int t; for (t = 0; t < NTASK; t++) { taskqgroup_attach(rg, >s[t], NULL, NULL, NULL, "gt"); taskqgroup_detach(rg, >s[t]); } } kprintf("gtq_race2: detacher finished %d cycles x %d tasks\n", i, NTASK); stop = 1; lwkt_exit(); } static int gtq_race2_ev(module_t mod, int type, void *data) { long c1, c2; int t, r; switch (type) { case MOD_LOAD: rg = taskqgroup_create("gtq_race2", 2, 1); if (rg == NULL) return (ENOMEM); for (t = 0; t < NTASK; t++) { GROUPTASK_INIT(>s[t], 0, gt_fn, NULL); gts[t].gt_taskqueue = NULL; } for (t = 0; t < NTASK; t += 2) taskqgroup_attach(rg, >s[t], NULL, NULL, NULL, "gt"); for (r = 0; r < NRACER; r++) { lwkt_create(racer, (void *)(intptr_t)(r % NTASK), &racer_td[r], NULL, TDF_NOSTART, 1 + (r % 4) + (r / 4) * 2, "gtr2%d", r); lwkt_setpri_initial(racer_td[r], TDPRI_KERN_DAEMON); lwkt_schedule(racer_td[r]); } lwkt_create(detacher, NULL, &det_td, NULL, TDF_NOSTART, 2, "gtdet2"); lwkt_setpri_initial(det_td, TDPRI_KERN_DAEMON); lwkt_schedule(det_td); kprintf("gtq_race2: %d racers + detacher running\n", NRACER); return (0); case MOD_UNLOAD: while (!stop) tsleep(gtq_race2_ev, 0, "gtr2wait", hz / 4); c1 = fn_count; tsleep(gtq_race2_ev, 0, "gtr2chk", 2 * hz); c2 = fn_count; kprintf("gtq_race2: enq_ok=%ld enq_eagain=%ld enq_null=%ld " "fn_count=%ld -> %ld\n", enq_ok, enq_eagain, enq_null, c1, c2); if (c2 > c1) kprintf("gtq_race2: CORRUPTION PROVEN (self-loop)\n"); else kprintf("gtq_race2: no post-stop growth this run\n"); return (0); default: break; } return (EOPNOTSUPP); } DEV_MODULE(gtq_race2, gtq_race2_ev, NULL); |