wifi: mac80211: introduce a feature flag for quiet in CSA
authorJohannes Berg <johannes.berg@intel.com>
Wed, 28 Feb 2024 08:55:45 +0000 (09:55 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 4 Mar 2024 13:33:58 +0000 (14:33 +0100)
When doing CSA in multi-link, there really isn't a need to
stop transmissions entirely. Add a feature flag for drivers
to indicate they can handle quiet in CSA (be it by parsing
themselves, or by implementing drv_pre_channel_switch()),
to make that possible.

Also clean up the csa_block_tx handling: it clearly cannot
handle multi-link due to the way queues are stopped, move
it to the sdata. Drivers should be doing it themselves for
working properly during CSA in MLO anyway. Also rename it
to indicate that it reflects TX was blocked at mac80211.

Reviewed-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240228095719.258439191541.I2469d206e2bf5cb244cfde2b4bbc2ae6d1cd3dd9@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/debugfs.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mlme.c

index c622450ac012508c6db1f6a8952672b32e8b82cf..353488ab94a294fd1fb2a985d015f9c8b59d421d 100644 (file)
@@ -2778,6 +2778,11 @@ struct ieee80211_txq {
  * @IEEE80211_HW_DISALLOW_PUNCTURING: HW requires disabling puncturing in EHT
  *     and connecting with a lower bandwidth instead
  *
+ * @IEEE80211_HW_HANDLES_QUIET_CSA: HW/driver handles quieting for CSA, so
+ *     no need to stop queues. This really should be set by a driver that
+ *     implements MLO, so operation can continue on other links when one
+ *     link is switching.
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -2836,6 +2841,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_DETECTS_COLOR_COLLISION,
        IEEE80211_HW_MLO_MCAST_MULTI_LINK_TX,
        IEEE80211_HW_DISALLOW_PUNCTURING,
+       IEEE80211_HW_HANDLES_QUIET_CSA,
 
        /* keep last, obviously */
        NUM_IEEE80211_HW_FLAGS
index 4bc0cc2dff83a6ad259c56d9f2090e6cfbb8494e..3a9d2ceec75238f674ec51ba32252144120fa786 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 (link->csa_block_tx) {
+       if (sdata->csa_blocked_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               link->csa_block_tx = false;
+               sdata->csa_blocked_tx = false;
        }
 
        ieee80211_free_next_beacon(link);
@@ -3649,7 +3649,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->deflink.csa_block_tx = block_tx;
+       sdata->csa_blocked_tx = block_tx;
        sdata_info(sdata, "channel switch failed, disconnecting\n");
        wiphy_work_queue(local->hw.wiphy, &ifmgd->csa_connection_drop_work);
 }
@@ -3735,10 +3735,10 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
 
        ieee80211_link_info_change_notify(sdata, link_data, changed);
 
-       if (link_data->csa_block_tx) {
+       if (sdata->csa_blocked_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               link_data->csa_block_tx = false;
+               sdata->csa_blocked_tx = false;
        }
 
        err = drv_post_channel_switch(link_data);
@@ -4014,12 +4014,14 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        }
 
        link_data->csa_chanreq = chanreq; 
-       link_data->csa_block_tx = params->block_tx;
        link_conf->csa_active = true;
 
-       if (link_data->csa_block_tx)
+       if (params->block_tx &&
+           !ieee80211_hw_check(&local->hw, HANDLES_QUIET_CSA)) {
                ieee80211_stop_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_blocked_tx = true;
+       }
 
        cfg80211_ch_switch_started_notify(sdata->dev,
                                          &link_data->csa_chanreq.oper, 0,
index 74be49191e70439e7b2bfa4ef8ba97d6fa00c6d8..2f68e92a7404bbd894c15808b4fb5480098ab207 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
- * Copyright (C) 2018 - 2019, 2021-2023 Intel Corporation
+ * Copyright (C) 2018 - 2019, 2021-2024 Intel Corporation
  */
 
 #include <linux/debugfs.h>
@@ -498,6 +498,7 @@ static const char *hw_flag_names[] = {
        FLAG(DETECTS_COLOR_COLLISION),
        FLAG(MLO_MCAST_MULTI_LINK_TX),
        FLAG(DISALLOW_PUNCTURING),
+       FLAG(HANDLES_QUIET_CSA),
 #undef FLAG
 };
 
index da320799fbff8ba7696980613c1e613d177745d4..cfd0c03e4bd6a22f29e9873748674e124662def1 100644 (file)
@@ -1034,7 +1034,6 @@ struct ieee80211_link_data {
        struct ieee80211_key __rcu *default_beacon_key;
 
        struct wiphy_work csa_finalize_work;
-       bool csa_block_tx;
 
        bool operating_11g_mode;
 
@@ -1093,6 +1092,8 @@ struct ieee80211_sub_if_data {
 
        unsigned long state;
 
+       bool csa_blocked_tx;
+
        char name[IFNAMSIZ];
 
        struct ieee80211_fragment_cache frags;
index b75b83a5142be8b63782d43268d657c1a4d500f5..395de62d9cb2dd30c2f283c41c4671c00d20f406 100644 (file)
@@ -8,7 +8,7 @@
  * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (c) 2016        Intel Deutschland GmbH
- * Copyright (C) 2018-2023 Intel Corporation
+ * Copyright (C) 2018-2024 Intel Corporation
  */
 #include <linux/slab.h>
 #include <linux/kernel.h>
@@ -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->deflink.csa_block_tx) {
+       if (sdata->csa_blocked_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->deflink.csa_block_tx = false;
+               sdata->csa_blocked_tx = false;
        }
 
        wiphy_work_cancel(local->hw.wiphy, &sdata->deflink.csa_finalize_work);
index c5c7575141796fe67561d38e857af5ee18f186ef..3d5cc3dd923887b9cfa672e0df97b809b0570110 100644 (file)
@@ -1926,10 +1926,10 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link)
 
        WARN_ON(!link->conf->csa_active);
 
-       if (link->csa_block_tx) {
+       if (sdata->csa_blocked_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               link->csa_block_tx = false;
+               sdata->csa_blocked_tx = false;
        }
 
        link->conf->csa_active = false;
@@ -1997,11 +1997,12 @@ ieee80211_sta_abort_chanswitch(struct ieee80211_link_data *link)
 
        ieee80211_link_unreserve_chanctx(link);
 
-       if (link->csa_block_tx)
+       if (sdata->csa_blocked_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_blocked_tx = false;
+       }
 
-       link->csa_block_tx = false;
        link->conf->csa_active = false;
 
        drv_abort_channel_switch(link);
@@ -2145,13 +2146,15 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
 
        link->conf->csa_active = true;
        link->csa_chanreq = csa_ie.chanreq;
-       link->csa_block_tx = csa_ie.mode;
        link->u.mgd.csa_ignored_same_chan = false;
        link->u.mgd.beacon_crc_valid = false;
 
-       if (link->csa_block_tx)
+       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;
+       }
 
        cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chanreq.oper,
                                          link->link_id, csa_ie.count,
@@ -2179,7 +2182,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
         * reset when the disconnection worker runs.
         */
        link->conf->csa_active = true;
-       link->csa_block_tx = csa_ie.mode;
+       sdata->csa_blocked_tx =
+               csa_ie.mode && !ieee80211_hw_check(&local->hw, HANDLES_QUIET_CSA);
 
        wiphy_work_queue(sdata->local->hw.wiphy,
                         &ifmgd->csa_connection_drop_work);
@@ -3233,10 +3237,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        sdata->vif.bss_conf.csa_active = false;
        sdata->deflink.u.mgd.csa_waiting_bcn = false;
        sdata->deflink.u.mgd.csa_ignored_same_chan = false;
-       if (sdata->deflink.csa_block_tx) {
+       if (sdata->csa_blocked_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->deflink.csa_block_tx = false;
+               sdata->csa_blocked_tx = false;
        }
 
        /* existing TX TSPEC sessions no longer exist */
@@ -3549,9 +3553,12 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        if (!ifmgd->associated)
                return;
 
-       /* in MLO assume we have a link where we can TX the frame */
-       tx = ieee80211_vif_is_mld(&sdata->vif) ||
-               !sdata->deflink.csa_block_tx;
+       /*
+        * 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;
 
        if (!ifmgd->driver_disconnect) {
                unsigned int link_id;
@@ -3584,10 +3591,10 @@ 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->deflink.csa_block_tx) {
+       if (sdata->csa_blocked_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->deflink.csa_block_tx = false;
+               sdata->csa_blocked_tx = false;
        }
 
        ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx,