]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211: ibss: wait for in-flight TX on disconnect
authorPagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Mon, 6 Jul 2026 19:37:56 +0000 (22:37 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 7 Jul 2026 07:28:55 +0000 (09:28 +0200)
While leaving an IBSS in ieee80211_ibss_disconnect() mac80211 flushes
stations, turns the carrier off and immediately tells the driver to
leave as well. While there may be synchronize_net() in station flush
and in this code later, packets can still be transmitted due to
cross-CPU race conditions after carrier off is set.
Therefore, it's possible for a race to happen where a TX to the
driver occurs while or after telling it to leave the IBSS. This can
be confusing to drivers, and in the case of iwlwifi leads to an
attempt to use invalid queues.

Move netif_carrier_off() to occur before sta_info_flush() during
IBSS disconnect, and add synchronize_net() if flushing didn't,
so that the synchronize_net() always happens between turning the
carrier off and telling the driver, avoiding this race.

Signed-off-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260706223751.da1ce439cc93.If5cf482f87ab98ce66dd48724e24c81fed236d3f@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ibss.c

index 1e5414ee27c0d17ac18a1e77aa185572a0e47454..882f91abbb6624b2d779ec4dbccf4229de765388 100644 (file)
@@ -668,7 +668,9 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
 
        ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
 
-       sta_info_flush(sdata, -1);
+       netif_carrier_off(sdata->dev);
+       if (!sta_info_flush(sdata, -1))
+               synchronize_net();
 
        spin_lock_bh(&ifibss->incomplete_lock);
        while (!list_empty(&ifibss->incomplete_stations)) {
@@ -682,8 +684,6 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
        }
        spin_unlock_bh(&ifibss->incomplete_lock);
 
-       netif_carrier_off(sdata->dev);
-
        sdata->vif.cfg.ibss_joined = false;
        sdata->vif.cfg.ibss_creator = false;
        sdata->vif.bss_conf.enable_beacon = false;
@@ -710,7 +710,6 @@ static void ieee80211_csa_connection_drop_work(struct wiphy *wiphy,
                             u.ibss.csa_connection_drop_work);
 
        ieee80211_ibss_disconnect(sdata);
-       synchronize_rcu();
        skb_queue_purge(&sdata->skb_queue);
 
        /* trigger a scan to find another IBSS network to join */
@@ -1797,8 +1796,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
        memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
 
-       synchronize_rcu();
-
        skb_queue_purge(&sdata->skb_queue);
 
        timer_delete_sync(&sdata->u.ibss.timer);