Created
February 7, 2026 01:54
-
-
Save lostmsu/a0cdd213676223fc7669726b3a24d50a to your computer and use it in GitHub Desktop.
MediaTek WiFi 7 MT7925E (PCIe) kernel driver deadlock patch by GPT-5.2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c | |
| +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c | |
| @@ -449,12 +449,16 @@ | |
| { | |
| struct mt792x_phy *phy = &dev->phy; | |
| + WRITE_ONCE(phy->roc_abort, true); | |
| timer_delete_sync(&phy->roc_timer); | |
| cancel_work_sync(&phy->roc_work); | |
| + /* make sure roc_work didn't rearm the timer on lock contention */ | |
| + timer_delete_sync(&phy->roc_timer); | |
| if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) | |
| ieee80211_iterate_interfaces(mt76_hw(dev), | |
| IEEE80211_IFACE_ITER_RESUME_ALL, | |
| mt7925_roc_iter, (void *)phy); | |
| + WRITE_ONCE(phy->roc_abort, false); | |
| } | |
| EXPORT_SYMBOL_GPL(mt7925_roc_abort_sync); | |
| @@ -465,14 +469,40 @@ | |
| phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy, | |
| roc_work); | |
| - if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) | |
| + if (READ_ONCE(phy->roc_abort)) | |
| return; | |
| - mt792x_mutex_acquire(phy->dev); | |
| + if (!test_bit(MT76_STATE_ROC, &phy->mt76->state)) | |
| + return; | |
| + | |
| + /* | |
| + * Avoid deadlocking with callers that cancel roc_work while holding | |
| + * dev->mt76.mutex (e.g. during station removal). If the mutex is held, | |
| + * reschedule via roc_timer for a short backoff instead of blocking. | |
| + */ | |
| + if (!mutex_trylock(&phy->dev->mt76.mutex)) { | |
| + if (!READ_ONCE(phy->roc_abort)) { | |
| + unsigned long delay = max_t(unsigned long, 1, HZ / 50); | |
| + | |
| + mod_timer(&phy->roc_timer, jiffies + delay); | |
| + } | |
| + return; | |
| + } | |
| + | |
| + mt76_connac_pm_wake(&phy->dev->mt76.phy, &phy->dev->pm); | |
| + | |
| + if (READ_ONCE(phy->roc_abort) || | |
| + !test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) { | |
| + mt76_connac_power_save_sched(&phy->dev->mt76.phy, &phy->dev->pm); | |
| + mutex_unlock(&phy->dev->mt76.mutex); | |
| + return; | |
| + } | |
| + | |
| ieee80211_iterate_active_interfaces(phy->mt76->hw, | |
| IEEE80211_IFACE_ITER_RESUME_ALL, | |
| mt7925_roc_iter, phy); | |
| - mt792x_mutex_release(phy->dev); | |
| + mt76_connac_power_save_sched(&phy->dev->mt76.phy, &phy->dev->pm); | |
| + mutex_unlock(&phy->dev->mt76.mutex); | |
| ieee80211_remain_on_channel_expired(phy->mt76->hw); | |
| } | |
| @@ -481,14 +511,17 @@ | |
| { | |
| int err = 0; | |
| + WRITE_ONCE(phy->roc_abort, true); | |
| timer_delete_sync(&phy->roc_timer); | |
| cancel_work_sync(&phy->roc_work); | |
| + timer_delete_sync(&phy->roc_timer); | |
| mt792x_mutex_acquire(phy->dev); | |
| if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) | |
| err = mt7925_mcu_abort_roc(phy, mconf, phy->roc_token_id); | |
| mt792x_mutex_release(phy->dev); | |
| + WRITE_ONCE(phy->roc_abort, false); | |
| return err; | |
| } | |
| --- a/drivers/net/wireless/mediatek/mt76/mt792x.h | |
| +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h | |
| @@ -185,6 +185,7 @@ | |
| wait_queue_head_t roc_wait; | |
| u8 roc_token_id; | |
| bool roc_grant; | |
| + bool roc_abort; | |
| }; | |
| struct mt792x_irq_map { |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is for kernel 6.18
On NixOS just add to your configuration.nix:
Basically the problem was that the implementation of station disconnect took a lock on the device object and then tried to synchronously cancel pending work. But these work items might have already entered the path where they will need the same lock, creating a deadlock situation. The fix is to replace unconditional locking in the work items with try -> reschedule loop that stops when it sees the abort flag.