wifi: mac80211: transmit deauth only if link is available
authorJohannes Berg <johannes.berg@intel.com>
Mon, 15 Apr 2024 08:27:12 +0000 (11:27 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 19 Apr 2024 08:14:08 +0000 (10:14 +0200)
There's an issue in that when we disconnect from an AP
due to the AP switching to an unsupported channel, we
might not tell the driver about this before we try to
send the deauth. If the underlying implementation has
detected the quiet CSA, this may cause issues if this
is the only active link. Avoid this by transmitting
(and flushing) the deauth only when there's an active
link available that's not affected by quiet CSA.

Since this introduces link->u.mgd.csa_blocked_tx and we
no longer check sdata->csa_blocked_tx for the TX itself
also rename the latter to csa_blocked_queues.

Fixes: 6f0107d195a8 ("wifi: mac80211: introduce a feature flag for quiet in CSA")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240415112355.1d91db5e95aa.Iad3a5df3367f305dff48cd61776abfd6cf0fd4ab@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mlme.c

index f67c1d0218121dc98b67af7201686a0c0b2ad817..07abaf7820c565e96993b95176972f430ef9967a 100644 (file)
@@ -1607,10 +1607,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
        /* abort any running channel switch or color change */
        link_conf->csa_active = false;
        link_conf->color_change_active = false;
-       if (sdata->csa_blocked_tx) {
+       if (sdata->csa_blocked_queues) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = false;
+               sdata->csa_blocked_queues = false;
        }
 
        ieee80211_free_next_beacon(link);
@@ -3648,7 +3648,7 @@ void ieee80211_channel_switch_disconnect(struct ieee80211_vif *vif, bool block_t
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
 
-       sdata->csa_blocked_tx = block_tx;
+       sdata->csa_blocked_queues = block_tx;
        sdata_info(sdata, "channel switch failed, disconnecting\n");
        wiphy_work_queue(local->hw.wiphy, &ifmgd->csa_connection_drop_work);
 }
@@ -3734,10 +3734,10 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
 
        ieee80211_link_info_change_notify(sdata, link_data, changed);
 
-       if (sdata->csa_blocked_tx) {
+       if (sdata->csa_blocked_queues) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = false;
+               sdata->csa_blocked_queues = false;
        }
 
        err = drv_post_channel_switch(link_data);
@@ -4019,7 +4019,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
            !ieee80211_hw_check(&local->hw, HANDLES_QUIET_CSA)) {
                ieee80211_stop_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = true;
+               sdata->csa_blocked_queues = true;
        }
 
        cfg80211_ch_switch_started_notify(sdata->dev,
index 81a9c5b7642dd1db7874a4d84d57675453b545ff..5c95ec9f499f4080fd4d8e055d982b24dc230303 100644 (file)
@@ -976,6 +976,7 @@ struct ieee80211_link_data_managed {
 
        bool csa_waiting_bcn;
        bool csa_ignored_same_chan;
+       bool csa_blocked_tx;
        struct wiphy_delayed_work chswitch_work;
 
        struct wiphy_work request_smps_work;
@@ -1094,7 +1095,7 @@ struct ieee80211_sub_if_data {
 
        unsigned long state;
 
-       bool csa_blocked_tx;
+       bool csa_blocked_queues;
 
        char name[IFNAMSIZ];
 
index 395de62d9cb2dd30c2f283c41c4671c00d20f406..ef6b0fc82d022769d839c822ef108f130f0ccc4e 100644 (file)
@@ -544,10 +544,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
        sdata->vif.bss_conf.csa_active = false;
        if (sdata->vif.type == NL80211_IFTYPE_STATION)
                sdata->deflink.u.mgd.csa_waiting_bcn = false;
-       if (sdata->csa_blocked_tx) {
+       if (sdata->csa_blocked_queues) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = false;
+               sdata->csa_blocked_queues = false;
        }
 
        wiphy_work_cancel(local->hw.wiphy, &sdata->deflink.csa_finalize_work);
index cd4b54e026538396124349d7104f926e92e1fbf6..742cf2561bda017c766d667280c903b1311c76e8 100644 (file)
@@ -1930,13 +1930,14 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link)
 
        WARN_ON(!link->conf->csa_active);
 
-       if (sdata->csa_blocked_tx) {
+       if (sdata->csa_blocked_queues) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = false;
+               sdata->csa_blocked_queues = false;
        }
 
        link->conf->csa_active = false;
+       link->u.mgd.csa_blocked_tx = false;
        link->u.mgd.csa_waiting_bcn = false;
 
        ret = drv_post_channel_switch(link);
@@ -1996,13 +1997,14 @@ ieee80211_sta_abort_chanswitch(struct ieee80211_link_data *link)
 
        ieee80211_link_unreserve_chanctx(link);
 
-       if (sdata->csa_blocked_tx) {
+       if (sdata->csa_blocked_queues) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = false;
+               sdata->csa_blocked_queues = false;
        }
 
        link->conf->csa_active = false;
+       link->u.mgd.csa_blocked_tx = false;
 
        drv_abort_channel_switch(link);
 }
@@ -2162,12 +2164,13 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
        link->csa_chanreq = csa_ie.chanreq;
        link->u.mgd.csa_ignored_same_chan = false;
        link->u.mgd.beacon_crc_valid = false;
+       link->u.mgd.csa_blocked_tx = csa_ie.mode;
 
        if (csa_ie.mode &&
            !ieee80211_hw_check(&local->hw, HANDLES_QUIET_CSA)) {
                ieee80211_stop_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = true;
+               sdata->csa_blocked_queues = true;
        }
 
        cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chanreq.oper,
@@ -2196,7 +2199,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
         * reset when the disconnection worker runs.
         */
        link->conf->csa_active = true;
-       sdata->csa_blocked_tx =
+       link->u.mgd.csa_blocked_tx = csa_ie.mode;
+       sdata->csa_blocked_queues =
                csa_ie.mode && !ieee80211_hw_check(&local->hw, HANDLES_QUIET_CSA);
 
        wiphy_work_queue(sdata->local->hw.wiphy,
@@ -3249,12 +3253,13 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        }
 
        sdata->vif.bss_conf.csa_active = false;
+       sdata->deflink.u.mgd.csa_blocked_tx = false;
        sdata->deflink.u.mgd.csa_waiting_bcn = false;
        sdata->deflink.u.mgd.csa_ignored_same_chan = false;
-       if (sdata->csa_blocked_tx) {
+       if (sdata->csa_blocked_queues) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = false;
+               sdata->csa_blocked_queues = false;
        }
 
        /* existing TX TSPEC sessions no longer exist */
@@ -3560,19 +3565,32 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
-       bool tx;
+       bool tx = false;
 
        lockdep_assert_wiphy(local->hw.wiphy);
 
        if (!ifmgd->associated)
                return;
 
-       /*
-        * MLO drivers should have HANDLES_QUIET_CSA, so that csa_blocked_tx
-        * is always false; if they don't then this may try to transmit the
-        * frame but queues will be stopped.
-        */
-       tx = !sdata->csa_blocked_tx;
+       /* only transmit if we have a link that makes that worthwhile */
+       for (unsigned int link_id = 0;
+            link_id < ARRAY_SIZE(sdata->link);
+            link_id++) {
+               struct ieee80211_link_data *link;
+
+               if (!ieee80211_vif_link_active(&sdata->vif, link_id))
+                       continue;
+
+               link = sdata_dereference(sdata->link[link_id], sdata);
+               if (WARN_ON_ONCE(!link))
+                       continue;
+
+               if (link->u.mgd.csa_blocked_tx)
+                       continue;
+
+               tx = true;
+               break;
+       }
 
        if (!ifmgd->driver_disconnect) {
                unsigned int link_id;
@@ -3605,10 +3623,11 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        /* the other links will be destroyed */
        sdata->vif.bss_conf.csa_active = false;
        sdata->deflink.u.mgd.csa_waiting_bcn = false;
-       if (sdata->csa_blocked_tx) {
+       sdata->deflink.u.mgd.csa_blocked_tx = false;
+       if (sdata->csa_blocked_queues) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_blocked_tx = false;
+               sdata->csa_blocked_queues = false;
        }
 
        ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx,