]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mac80211_hwsim: limit TX of frames to the NAN DW
authorBenjamin Berg <benjamin.berg@intel.com>
Wed, 6 May 2026 03:44:20 +0000 (06:44 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 6 May 2026 09:41:49 +0000 (11:41 +0200)
Frames submitted on the NAN device interface should only be transmitted
during one of the discovery windows (DWs). It is assumed that software
submits frames from the DW end notifications for the next DW period.

Simulate this behaviour by checking that we are currently in a DW before
transmitting from ieee80211_hwsim_wake_tx_queue. As frames will be
queued up at the start of a DW, wake the management TX queue every time
a DW is started. Do so with a randomized offset just to avoid every
client transmitting at the same time.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260506064301.f3456f159655.Id6780e2f7f7cab03264299b7d696ba5b1269e451@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/virtual/mac80211_hwsim_i.h
drivers/net/wireless/virtual/mac80211_hwsim_main.c
drivers/net/wireless/virtual/mac80211_hwsim_nan.c
drivers/net/wireless/virtual/mac80211_hwsim_nan.h

index 6b2a5dccb106afd3159901a0e4890469e2277551..5432de92beab3e58658f6a375492d822b7f229ab 100644 (file)
@@ -136,4 +136,7 @@ u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data,
 u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
                           struct ieee80211_vif *vif);
 
+void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
+                                  struct ieee80211_txq *txq);
+
 #endif /* __MAC80211_HWSIM_I_H */
index 3a0c4366dfdb9d6e135971666ee5887da5fab69b..6740504b30e6d0b9993a001746e9c0dad3348b53 100644 (file)
@@ -2235,14 +2235,18 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
        ieee80211_tx_status_irqsafe(hw, skb);
 }
 
-static void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
-                                         struct ieee80211_txq *txq)
+void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
+                                  struct ieee80211_txq *txq)
 {
        struct ieee80211_tx_control control = {
                .sta = txq->sta,
        };
        struct sk_buff *skb;
 
+       if (txq->vif->type == NL80211_IFTYPE_NAN &&
+           !mac80211_hwsim_nan_txq_transmitting(hw, txq))
+               return;
+
        while ((skb = ieee80211_tx_dequeue(hw, txq)))
                mac80211_hwsim_tx(hw, &control, skb);
 }
@@ -5603,6 +5607,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
                hrtimer_setup(&data->nan.slot_timer,
                              mac80211_hwsim_nan_slot_timer,
                              CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
+               hrtimer_setup(&data->nan.resume_txqs_timer,
+                             mac80211_hwsim_nan_resume_txqs_timer,
+                             CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
        }
 
        data->if_combination.radar_detect_widths =
index aa4aef0920f4131058296e30857a57a1574effd9..22805c3723e63999b9f6b7e022c2adfaa5d52e70 100644 (file)
 static_assert(16 * DWST_TU * 1024 == 8192 * 1024);
 static_assert(DW0_TSF_MASK + 1 == 8192 * 1024);
 
+/* Quiet time at the end of each slot where TX is suppressed */
+#define NAN_CHAN_SWITCH_TIME_US                256
+
 static u8 hwsim_nan_cluster_id[ETH_ALEN];
 
+static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data);
+
 static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data)
 {
        ktime_t expires = hrtimer_get_expires(&data->nan.slot_timer);
@@ -130,6 +135,8 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
                cfg80211_next_nan_dw_notif(wdev, notify_dw_chan, GFP_ATOMIC);
        }
 
+       mac80211_hwsim_nan_resume_txqs(data);
+
        mac80211_hwsim_nan_schedule_slot(data, slot + 1);
 
        return HRTIMER_RESTART;
@@ -190,6 +197,7 @@ int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
                return -EINVAL;
 
        hrtimer_cancel(&data->nan.slot_timer);
+       hrtimer_cancel(&data->nan.resume_txqs_timer);
        data->nan.device_vif = NULL;
 
        spin_lock_bh(&hwsim_radio_lock);
@@ -231,3 +239,74 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
 
        return 0;
 }
+
+static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data)
+{
+       u32 timeout_ns;
+
+       /* Nothing to do if we are not in a DW */
+       if (!mac80211_hwsim_nan_txq_transmitting(data->hw,
+                                                data->nan.device_vif->txq_mgmt))
+               return;
+
+       /*
+        * Wait a bit and also randomize things so that not everyone is TXing
+        * at the same time. Each slot is 16 TU long, this waits between 100 us
+        * and 5 ms before starting to TX (unless a new frame arrives).
+        */
+       timeout_ns = get_random_u32_inclusive(100 * NSEC_PER_USEC,
+                                             5 * NSEC_PER_MSEC);
+
+       hrtimer_start(&data->nan.resume_txqs_timer,
+                     ns_to_ktime(timeout_ns),
+                     HRTIMER_MODE_REL_SOFT);
+}
+
+enum hrtimer_restart
+mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer)
+{
+       struct mac80211_hwsim_data *data =
+               container_of(timer, struct mac80211_hwsim_data,
+                            nan.resume_txqs_timer);
+
+       guard(rcu)();
+
+       /* Wake TX queue for management frames on the NAN device interface */
+       if (mac80211_hwsim_nan_txq_transmitting(data->hw,
+                                               data->nan.device_vif->txq_mgmt))
+               ieee80211_hwsim_wake_tx_queue(data->hw,
+                                             data->nan.device_vif->txq_mgmt);
+
+       return HRTIMER_NORESTART;
+}
+
+bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
+                                        struct ieee80211_txq *txq)
+{
+       struct mac80211_hwsim_data *data = hw->priv;
+       u64 tsf;
+       u8 slot;
+
+       if (WARN_ON_ONCE(!data->nan.device_vif))
+               return true;
+
+       tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif);
+       slot = hwsim_nan_slot_from_tsf(tsf);
+
+       /* Enforce a maximum channel switch time and guard against TX delays */
+       if (slot != hwsim_nan_slot_from_tsf(tsf + NAN_CHAN_SWITCH_TIME_US))
+               return false;
+
+       /* Check NAN device interface management frame transmission */
+       if (!txq->sta) {
+               /* Only transmit these during one of the DWs */
+               if (slot == SLOT_24GHZ_DW ||
+                   (slot == SLOT_5GHZ_DW &&
+                    (data->nan.bands & BIT(NL80211_BAND_5GHZ))))
+                       return true;
+
+               return false;
+       }
+
+       return true;
+}
index e86e7f9e9a3c39463a7cae4dd42f6aacb60a2af9..6a07807972734fb3475f3ba871edcb9bcfa5f1a7 100644 (file)
@@ -15,11 +15,14 @@ struct mac80211_hwsim_nan_data {
        struct ieee80211_channel *channel;
 
        struct hrtimer slot_timer;
+       struct hrtimer resume_txqs_timer;
        bool notify_dw;
 };
 
 enum hrtimer_restart
 mac80211_hwsim_nan_slot_timer(struct hrtimer *timer);
+enum hrtimer_restart
+mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer);
 
 int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
@@ -33,4 +36,7 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
                                     struct cfg80211_nan_conf *conf,
                                     u32 changes);
 
+bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
+                                        struct ieee80211_txq *txq);
+
 #endif /* __MAC80211_HWSIM_NAN_H */