From ca28b8b125c27063b9b4bc60bb85206ca8e0d403 Mon Sep 17 00:00:00 2001
From: Yuvasree Sivasankaran <quic_ysivasan@quicinc.com>
Date: Thu, 31 Aug 2023 10:59:33 +0530
Subject: [PATCH] wifi: mac80211: Add mac hw flag to avoid queue skb

Queue SKB in mac80211 become mandatory from latest 6.1 kernel. Because of
this queuing, there will be performance degradation. Add hw flag option
to enable tx queue in Driver/Hardware.

Driver/hardware can register for HAS_TX_QUEUE HW flag and avoid tx queuing
in mac80211.

Add same HW flag checks to avoid accessing skb queues which will be
NULL or invalid and also NULL checks for sta txqs for NULL or invalid
access.

Signed-off-by: Yuvasree Sivasankaran <quic_ysivasan@quicinc.com>
---
 include/net/mac80211.h                |  1 +
 net/mac80211/debugfs.c                |  1 +
 net/mac80211/tx.c                     | 18 ++++++++++++++----
 4 files changed, 17 insertions(+), 4 deletions(-)

--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2882,6 +2882,9 @@ struct ieee80211_txq {
  *
  * @IEEE80211_HW_SUPPORTS_TID_CLASS_OFFLOAD: Hardware suports tid calssification offload.
  *
+ * @IEE80211_HW_HAS_TX_QUEUE: Hardware/drivers has tx queue, does skb queuing itself,
+ *	the stack will not do tx queuing.
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -2945,6 +2948,7 @@ enum ieee80211_hw_flags {
 	IEEE80211_HW_SUPPORTS_NSS_OFFLOAD,
 	IEEE80211_HW_SUPPORTS_MESH_NSS_OFFLOAD,
 	IEEE80211_HW_SUPPORTS_TID_CLASS_OFFLOAD,
+	IEEE80211_HW_HAS_TX_QUEUE,
 
 	/* keep last, obviously */
 	NUM_IEEE80211_HW_FLAGS
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -512,6 +512,7 @@ static const char *hw_flag_names[] = {
 	FLAG(SUPPORTS_NSS_OFFLOAD),
 	FLAG(SUPPORTS_MESH_NSS_OFFLOAD),
 	FLAG(SUPPORTS_TID_CLASS_OFFLOAD),
+	FLAG(HAS_TX_QUEUE),
 #undef FLAG
 };
 
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1601,6 +1601,9 @@ int ieee80211_txq_setup_flows(struct iee
 	bool supp_vht = false;
 	enum nl80211_band band;
 
+	if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))
+		return 0;
+
 	ret = fq_init(fq, 4096);
 	if (ret)
 		return ret;
@@ -1648,6 +1651,9 @@ void ieee80211_txq_teardown_flows(struct
 {
 	struct fq *fq = &local->fq;
 
+	if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))
+		return;
+
 	kvfree(local->cvars);
 	local->cvars = NULL;
 
@@ -1664,7 +1670,8 @@ static bool ieee80211_queue_skb(struct i
 	struct ieee80211_vif *vif;
 	struct txq_info *txqi;
 
-	if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+	if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE) ||
+	    sdata->vif.type == NL80211_IFTYPE_MONITOR)
 		return false;
 
 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -4353,7 +4360,8 @@ void __ieee80211_subif_start_xmit(struct
 		}
 	}
 
-	skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
+	if (unlikely(!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)))
+		skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
 	ieee80211_aggr_check(sdata, sta, skb);
 
 	if (sta) {
@@ -4707,8 +4715,10 @@ static void ieee80211_8023_xmit(struct i
 	bool multicast;
 	u8 tid;
 
-	queue = ieee80211_select_queue(sdata, sta, skb);
-	skb_set_queue_mapping(skb, queue);
+	if (unlikely(!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))) {
+		queue = ieee80211_select_queue(sdata, sta, skb);
+		skb_set_queue_mapping(skb, queue);
+	}
 
 	multicast = is_multicast_ether_addr(ra);
 
@@ -6429,9 +6439,12 @@ int ieee80211_tx_control_port(struct wip
 	}
 
 	if (!IS_ERR(sta)) {
-		u16 queue = ieee80211_select_queue(sdata, sta, skb);
 
-		skb_set_queue_mapping(skb, queue);
+		if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) {
+			u16 queue = ieee80211_select_queue(sdata, sta, skb);
+
+			skb_set_queue_mapping(skb, queue);
+		}
 
 		/*
 		 * for MLO STA, the SA should be the AP MLD address, but
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -4601,6 +4601,9 @@ static int ieee80211_get_txq_stats(struc
 	struct ieee80211_sub_if_data *sdata;
 	int ret = 0;
 
+	if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))
+		return 1;
+
 	spin_lock_bh(&local->fq.lock);
 	rcu_read_lock();
 
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1003,7 +1003,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_
 		atomic_set(&local->agg_queue_stop[i], 0);
 	}
 	tasklet_setup(&local->tx_pending_tasklet, ieee80211_tx_pending);
-	tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs);
+
+	if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))
+		tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs);
+
 	tasklet_setup(&local->tasklet, ieee80211_tasklet_handler);
 
 	skb_queue_head_init(&local->skb_queue);
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1588,6 +1588,9 @@ static void sta_ps_start(struct sta_info
 
 	ieee80211_clear_fast_xmit(sta);
 
+	if (!sta->sta.txq[0])
+		return;
+
 	for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
 		struct ieee80211_txq *txq = sta->sta.txq[tid];
 		struct txq_info *txqi = to_txq_info(txq);
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -118,15 +118,17 @@ void ieee80211_purge_sta_txqs(struct sta
 	struct ieee80211_local *local = sta->sdata->local;
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-		struct txq_info *txqi;
+	if (sta->sta.txq[0]) {
+		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+			struct txq_info *txqi;
 
-		if (!sta->sta.txq[i])
-			continue;
+			if (!sta->sta.txq[i])
+				continue;
 
-		txqi = to_txq_info(sta->sta.txq[i]);
+			txqi = to_txq_info(sta->sta.txq[i]);
 
-		ieee80211_txq_purge(local, txqi);
+			ieee80211_txq_purge(local, txqi);
+		}
 	}
 }
 
@@ -436,7 +438,9 @@ void sta_info_free(struct ieee80211_loca
 
 	sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
 
-	kfree(to_txq_info(sta->sta.txq[0]));
+	if (sta->sta.txq[0])
+		kfree(to_txq_info(sta->sta.txq[0]));
+
 	kfree(rcu_dereference_raw(sta->sta.rates));
 #ifdef CPTCFG_MAC80211_MESH
 	kfree(sta->mesh);
@@ -537,8 +541,6 @@ __sta_info_alloc(struct ieee80211_sub_if
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_hw *hw = &local->hw;
 	struct sta_info *sta;
-	void *txq_data;
-	int size;
 	int i;
 
 	sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
@@ -611,18 +613,22 @@ __sta_info_alloc(struct ieee80211_sub_if
 
 	sta->last_connected = ktime_get_seconds();
 
-	size = sizeof(struct txq_info) +
-	       ALIGN(hw->txq_data_size, sizeof(void *));
 
-	txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
-	if (!txq_data)
-		goto free;
+	if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) {
+		void *txq_data;
+		int size = sizeof(struct txq_info) +
+			ALIGN(hw->txq_data_size, sizeof(void *));
+
+		txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
+		if (!txq_data)
+			goto free;
 
-	for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-		struct txq_info *txq = txq_data + i * size;
+		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+			struct txq_info *txq = txq_data + i * size;
 
-		/* might not do anything for the (bufferable) MMPDU TXQ */
-		ieee80211_txq_init(sdata, sta, txq, i);
+			/* might not do anything for the (bufferable) MMPDU TXQ */
+			ieee80211_txq_init(sdata, sta, txq, i);
+		}
 	}
 
 	if (sta_prepare_rate_control(local, sta, gfp))
@@ -696,7 +702,8 @@ __sta_info_alloc(struct ieee80211_sub_if
 	return sta;
 
 free_txq:
-	kfree(to_txq_info(sta->sta.txq[0]));
+	if (sta->sta.txq[0])
+		kfree(to_txq_info(sta->sta.txq[0]));
 free:
 	sta_info_free_link(&sta->deflink);
 #ifdef CPTCFG_MAC80211_MESH
@@ -1715,11 +1722,13 @@ void ieee80211_sta_ps_deliver_wakeup(str
 	if (!ieee80211_hw_check(&local->hw, AP_LINK_PS))
 		drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
 
-	for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-		if (!sta->sta.txq[i] || !txq_has_queue(sta->sta.txq[i]))
-			continue;
+	if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) {
+		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+			if (!sta->sta.txq[i] || !txq_has_queue(sta->sta.txq[i]))
+				continue;
 
-		schedule_and_wake_txq(local, to_txq_info(sta->sta.txq[i]));
+			schedule_and_wake_txq(local, to_txq_info(sta->sta.txq[i]));
+		}
 	}
 
 	skb_queue_head_init(&pending);
@@ -2134,6 +2143,9 @@ ieee80211_sta_ps_deliver_response(struct
 		 * TIM recalculation.
 		 */
 
+		if (!sta->sta.txq[0])
+			return;
+
 		for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
 			if (!sta->sta.txq[tid] ||
 			    !(driver_release_tids & BIT(tid)) ||
@@ -2568,7 +2580,7 @@ static void sta_set_tidstats(struct sta_
 		tidstats->tx_msdu_failed = sta->deflink.status_stats.msdu_failed[tid];
 	}
 
-	if (tid < IEEE80211_NUM_TIDS) {
+	if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE) && tid < IEEE80211_NUM_TIDS) {
 		spin_lock_bh(&local->fq.lock);
 		rcu_read_lock();
 
@@ -2897,6 +2909,9 @@ unsigned long ieee80211_sta_last_active(
 
 static void sta_update_codel_params(struct sta_info *sta, u32 thr)
 {
+	if (ieee80211_hw_check(&sta->sdata->local->hw, HAS_TX_QUEUE))
+		return;
+
 	if (thr && thr < STA_SLOW_THRESHOLD * sta->local->num_sta) {
 		sta->cparams.target = MS2TIME(50);
 		sta->cparams.interval = MS2TIME(300);
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -162,6 +162,9 @@ static ssize_t sta_aqm_read(struct file
 		       bufsz + buf - p,
 		       "tid ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets flags\n");
 
+	if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))
+		goto skip_txq_info;
+
 	for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
 		if (!sta->sta.txq[i])
 			continue;
@@ -186,6 +189,7 @@ static ssize_t sta_aqm_read(struct file
 			       test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ? " DIRTY" : "");
 	}
 
+skip_txq_info:
 	rcu_read_unlock();
 	spin_unlock_bh(&local->fq.lock);
 
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -814,7 +814,8 @@ bool ieee80211_mesh_xmit_fast(struct iee
 	if (!skb)
 		return true;
 
-	skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
+	if (unlikely(!ieee80211_hw_check(&sdata->local->hw, HAS_TX_QUEUE)))
+		skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
 
 	meshhdr = (struct ieee80211s_hdr *)entry->hdr;
 	if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) {
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -470,10 +470,8 @@ static void __ieee80211_wake_queue(struc
 	 * release someone's lock, but it is fine because all the callers of
 	 * __ieee80211_wake_queue call it right before releasing the lock.
 	 */
-	if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
+	if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))
 		tasklet_schedule(&local->wake_txqs_tasklet);
-	else
-		_ieee80211_wake_txqs(local, flags);
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
