diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h index 6b2a5dccb106..5432de92beab 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -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 */ diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 3a0c4366dfdb..6740504b30e6 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -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 = diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c index aa4aef0920f4..22805c3723e6 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c @@ -26,8 +26,13 @@ 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; +} diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h index e86e7f9e9a3c..6a0780797273 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h @@ -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 */