This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

ignavia: patch mt76 wcid poll-list race causing roam freezes

Six hard freezes on 2026-07-03, all during WiFi roaming between hoelle
APs. Captured oopses show identical list_add corruption in an 8k slab
(ieee80211_sta + drv_priv) -> kernel BUG at lib/list_debug.c:32 in
softirq context -> full system freeze.

Root cause: mt792x station teardown never clears wcid->sta, so the
rx/txs softirq path can re-add a dying wcid to sta_poll_list after
mt76_wcid_cleanup() unlinked it. Once the sta is freed and the slab
slot reused, mt76_wcid_init() self-loops the still-linked list node;
the next list_add_tail() panics. Also fixes the unlocked
list_first_entry read in mt7925_mac_sta_poll (mt7921 already takes
the lock there).

Full analysis in the patch header. Drop once fixed upstream
(https://github.com/zbowling/mt7925 tracks related work).

Also drop amdgpu.mes=0: the parameter no longer exists on 7.x
("amdgpu: unknown parameter mes ignored").

+114 -3
+97
machines/ignavia/mt76-wcid-poll-race.patch
··· 1 + wifi: mt76: fix wcid poll list corruption on station removal 2 + 3 + Three system freezes on 2026-07-03 (ignavia, Framework 13 AMD, MT7925), 4 + two with captured oopses, both identical, on kernels 7.0.11 and 7.1.2: 5 + 6 + wlp192s0: send auth to 80:2a:a8:52:b0:2f (try 1/3) 7 + slab kmalloc-rnd-11-8k start ... pointer offset 4160 size 8192 8 + list_add corruption. prev->next should be next (X), but was Y. (prev=Y) 9 + kernel BUG at lib/list_debug.c:32! 10 + 11 + prev == prev->next means the tail node of sta_poll_list was 12 + re-initialized (INIT_LIST_HEAD) while still linked. The 8k slab is the 13 + ieee80211_sta + drv_priv allocation holding the embedded mt76_wcid. 14 + 15 + Race (mt7925, frequent roaming between APs of the same ESS): 16 + 17 + CPU0 (rx/txs softirq) CPU1 (mac80211 sta teardown) 18 + wcid = rcu_dereference(...) 19 + rcu_assign_pointer(wcid[idx], NULL) 20 + mt76_wcid_cleanup() 21 + list_del_init(&wcid->poll_list) 22 + mt76_wcid_add_poll(wcid) 23 + wcid->sta still 1 -> re-adds 24 + dying wcid to sta_poll_list 25 + sta freed after RCU grace period 26 + ... sta_poll_list tail dangles into freed slab ... 27 + roam re-add reuses slab slot: 28 + mt76_wcid_init() 29 + INIT_LIST_HEAD(&wcid->poll_list) 30 + -> still-linked tail self-loops 31 + next mt76_wcid_add_poll() 32 + list_add_tail() -> BUG 33 + 34 + mt76_reset_device() and the mt7915/mt7996 remove paths already clear 35 + wcid->sta (and set sta_disabled) at teardown; the mt792x remove paths 36 + never do, so the !wcid->sta guard in mt76_wcid_add_poll() is 37 + ineffective there, and it runs before taking sta_poll_lock anyway. 38 + 39 + Fix: 40 + - mt76_wcid_cleanup(): mark the wcid dead (sta = 0, sta_disabled = 1) 41 + under sta_poll_lock, in the same critical section that unlinks 42 + poll_list. 43 + - mt76_wcid_add_poll(): re-check wcid->sta under sta_poll_lock so the 44 + check cannot race with cleanup. 45 + - mt7925_mac_sta_poll(): take list_first_entry under sta_poll_lock 46 + (same discipline mt7921 uses); the unlocked read can race with 47 + cleanup unlinking the entry from the spliced private list. 48 + 49 + diff -ruN a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c 50 + --- a/drivers/net/wireless/mediatek/mt76/mac80211.c 2026-06-27 12:08:18.000000000 +0200 51 + +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c 2026-07-03 16:04:01.869430650 +0200 52 + @@ -1727,6 +1727,13 @@ 53 + * when mt76_wcid_add_poll() checks list_empty(). 54 + */ 55 + spin_lock_bh(&dev->sta_poll_lock); 56 + + /* Mark the wcid dead in the same critical section, so a concurrent 57 + + * mt76_wcid_add_poll() from the rx/txs path cannot re-add it after 58 + + * we unlink it. Freed wcids left on sta_poll_list corrupt the list 59 + + * once the slab slot is reused and re-initialized. 60 + + */ 61 + + wcid->sta = 0; 62 + + wcid->sta_disabled = 1; 63 + if (!list_empty(&wcid->poll_list)) 64 + list_del_init(&wcid->poll_list); 65 + spin_unlock_bh(&dev->sta_poll_lock); 66 + @@ -1759,7 +1766,10 @@ 67 + return; 68 + 69 + spin_lock_bh(&dev->sta_poll_lock); 70 + - if (list_empty(&wcid->poll_list)) 71 + + /* Re-check under the lock: mt76_wcid_cleanup() clears wcid->sta in 72 + + * its sta_poll_lock section, so this cannot race with teardown. 73 + + */ 74 + + if (wcid->sta && list_empty(&wcid->poll_list)) 75 + list_add_tail(&wcid->poll_list, &dev->sta_poll_list); 76 + spin_unlock_bh(&dev->sta_poll_lock); 77 + } 78 + diff -ruN a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c 79 + --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c 2026-06-27 12:08:18.000000000 +0200 80 + +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c 2026-07-03 16:04:01.869762977 +0200 81 + @@ -46,12 +46,14 @@ 82 + u16 idx; 83 + u8 bw; 84 + 85 + - if (list_empty(&sta_poll_list)) 86 + + spin_lock_bh(&dev->mt76.sta_poll_lock); 87 + + if (list_empty(&sta_poll_list)) { 88 + + spin_unlock_bh(&dev->mt76.sta_poll_lock); 89 + break; 90 + + } 91 + mlink = list_first_entry(&sta_poll_list, 92 + struct mt792x_link_sta, wcid.poll_list); 93 + msta = mlink->sta; 94 + - spin_lock_bh(&dev->mt76.sta_poll_lock); 95 + list_del_init(&mlink->wcid.poll_list); 96 + spin_unlock_bh(&dev->mt76.sta_poll_lock); 97 +
+17 -3
machines/ignavia/physical.nix
··· 46 46 # regression fix (e3ac0d9f1a20) landed upstream in 7.0.10. 47 47 boot.kernelPackages = pkgs.linuxPackages_latest; 48 48 49 - # Disable MES (Micro Engine Scheduler) - known to cause GPU ring hangs on RDNA3+ 50 - # The hang manifests as: screen goes off 1-2s, comes back but frozen 51 - boot.kernelParams = [ "amdgpu.mes=0" ]; 49 + # MT7925 WiFi: wcid poll-list corruption on station removal during AP 50 + # roaming -> list_add corruption -> kernel BUG at lib/list_debug.c:32 51 + # -> hard freeze. Three freezes on 2026-07-03 alone (multi-AP ESS). 52 + # Local fix, see patch header for full analysis; related upstream work: 53 + # https://github.com/zbowling/mt7925 (series not yet merged, and the 54 + # already-merged parts in 7.1.x do not close this race). 55 + # Drop once the fix (or equivalent) lands upstream. 56 + boot.kernelPatches = [ 57 + { 58 + name = "mt76-wcid-poll-race"; 59 + patch = ./mt76-wcid-poll-race.patch; 60 + } 61 + ]; 62 + 63 + # amdgpu.mes=0 (old RDNA3+ MES ring-hang workaround) was removed here: 64 + # since kernel ~7.x the parameter no longer exists ("amdgpu: unknown 65 + # parameter 'mes' ignored") and the GPU has been stable without it. 52 66 53 67 boot.initrd.availableKernelModules = [ 54 68 "nvme"