DF-5605 / lockowned_demo.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 | /* * DF-5605 KLD harness — demonstrates the DragonFly-specific lockowned() * semantics divergence relied on by ua_mixer_set()/ua_mixer_setrecsrc() * (sys/bus/u4b/audio/uaudio_pcm.c:139 / :159). * * Production kernel APIs only (lockmgr/lockowned/lockstatus/kthread_create); * no debug interfaces. * * What it shows, phase by phase (all kprintf'd with DF5605 markers): * P1 lock free -> lockowned=0 (dance acquires: correct) * P2 lock held exclusive by curthread -> lockowned=1 (dance skips reacquire: correct) * P3 lock held SHARED by ANOTHER thread -> lockowned=1 <-- WRONG for the * ua_mixer_set() decision shape: curthread holds nothing, yet the dance * concludes "already owned" and runs uaudio_mixer_set() with zero * exclusion while the foreign thread is inside a shared critical * section. The precise check the sound(4) core itself uses * (mixer.c:273 lockstatus(m->lock, curthread) == LK_EXCLUSIVE) returns * LK_SHARED here and would (block and) acquire. * P4 foreign holder released -> lockowned=0 (back to correct) */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/lock.h> #include <sys/kthread.h> #include <sys/thread.h> static struct lock dance_lock; /* created like snd_mtxcreate() */ static struct thread *sh_tp; static volatile int sh_state; /* 0=not started 1=shared-held 2=released */ static void shared_holder(void *arg __unused) { lockmgr(&dance_lock, LK_SHARED); sh_state = 1; tsleep(&sh_state, 0, "df5605sh", 3 * hz); /* hold ~3s, or until woken */ lockmgr(&dance_lock, LK_RELEASE); sh_state = 2; wakeup(&sh_state); kthread_exit(); } /* * The exact decision shape of ua_mixer_set(), uaudio_pcm.c:136-148. * Returns the do_unlock value the real code would compute. */ static int ua_mixer_set_dance(void) { struct lock *lock = &dance_lock; uint8_t do_unlock; if (lockowned(lock)) { do_unlock = 0; /* "we hold it" — skip acquire */ } else { do_unlock = 1; lockmgr(lock, LK_EXCLUSIVE); } /* uaudio_mixer_set(sc, ...) would run here */ if (do_unlock) lockmgr(lock, LK_RELEASE); return (do_unlock); } static void snap(const char *phase) { int owned = lockowned(&dance_lock); int st = lockstatus(&dance_lock, curthread); int do_unlock = ua_mixer_set_dance(); kprintf("DF5605 %s: lockowned=%d lockstatus=%d " "(LK_SHARED=1 LK_EXCLUSIVE=2 LK_EXCLOTHER=8) " "-> ua_mixer_set dance do_unlock=%d %s\n", phase, owned, st, do_unlock, do_unlock == 0 ? "(runs uaudio_mixer_set with NO lock held)" : "(acquired exclusive properly)"); } static int df5605_modevent(module_t mod, int type, void *arg) { int i; switch (type) { case MOD_LOAD: lockinit(&dance_lock, "df5605mix", 0, LK_CANRECURSE); sh_state = 0; snap("P1-free"); lockmgr(&dance_lock, LK_EXCLUSIVE); snap("P2-self-exclusive"); /* reentrant call path */ lockmgr(&dance_lock, LK_RELEASE); /* foreign shared holder, exactly the case lockowned() guesses * wrong about: returns (count & LKC_SMASK) != 0 without * checking WHO holds it (kern_lock.c:1365-1378). */ if (kthread_create(shared_holder, NULL, &sh_tp, "df5605_shold") != 0) { kprintf("DF5605: kthread_create failed\n"); lockuninit(&dance_lock); return (ENOMEM); } for (i = 0; i < 10 && sh_state == 0; i++) tsleep(&sh_state, 0, "df5605w", hz); if (sh_state != 1) { kprintf("DF5605: shared holder never engaged\n"); break; } snap("P3-foreign-SHARED"); wakeup(&sh_state); for (i = 0; i < 10 && sh_state != 2; i++) tsleep(&sh_state, 0, "df5605w", hz); snap("P4-released"); kprintf("DF5605 DONE: divergence demonstrated=%s\n", "see P3 line above"); break; case MOD_UNLOAD: /* threads finished; drain */ for (i = 0; i < 10 && sh_state == 1; i++) { wakeup(&sh_state); tsleep(&sh_state, 0, "df5605w", hz); } lockuninit(&dance_lock); kprintf("DF5605 unloaded\n"); break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t df5605_mod = { "df5605_demo", df5605_modevent, NULL }; DECLARE_MODULE(df5605_demo, df5605_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(df5605_demo, 1); |