Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb
@ 2026-10-31 16:51 Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 1/7] wifi: rt2x00: start the link tuner when an interface is added Ho Jie Feng
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-10-31 16:51 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka; +Cc: Ho Jie Feng

This series improves the throughput of rt2800usb.

Implements
- Frequency offset calibration
- Better fallback rates
- Aggregation
- GRO

Tested on WNDA4100 RT3593, 5 GHz with iperf3

  patch                          before        after
                                 TX    RX      TX    RX
  2 crystal calibration          27.7  31.6    28.5  69.7
  3 MCS16 to MCS8 TX status      27.0  70.0    29.4  70.0
  6 RX bulk aggregation          30.5  67.8    30.5  80.9
  7 threaded NAPI with GRO       30.5  80.9    30.5  161.7

RT3070 did not have any visible change for both TX and RX.

Ho Jie Feng (7):
  wifi: rt2x00: start the link tuner when an interface is added
  wifi: rt2800: calibrate the crystal of RT3593 with the beacons
  wifi: rt2800: make MCS 16 fall back to MCS 8 on devices with 3 TX
    chains
  wifi: rt2800: don't report an A-MPDU length to minstrel_ht
  wifi: rt2800: support A-MSDU in A-MPDU
  wifi: rt2800usb: enable RX bulk aggregation
  wifi: rt2x00usb: use a threaded NAPI with GRO for RX

 drivers/net/wireless/ralink/rt2x00/rt2800.h   |   1 +
 .../net/wireless/ralink/rt2x00/rt2800lib.c    | 113 +++++++++++++++++-
 .../net/wireless/ralink/rt2x00/rt2800lib.h    |   4 +
 .../net/wireless/ralink/rt2x00/rt2800usb.c    |  33 ++++-
 .../net/wireless/ralink/rt2x00/rt2800usb.h    |   6 +
 drivers/net/wireless/ralink/rt2x00/rt2x00.h   |  21 +++-
 .../net/wireless/ralink/rt2x00/rt2x00dev.c    | 106 +++++++++++-----
 .../net/wireless/ralink/rt2x00/rt2x00lib.h    |   2 +
 .../net/wireless/ralink/rt2x00/rt2x00link.c   |   9 ++
 .../net/wireless/ralink/rt2x00/rt2x00mac.c    |  13 ++
 .../net/wireless/ralink/rt2x00/rt2x00mmio.c   |   2 +-
 .../net/wireless/ralink/rt2x00/rt2x00usb.c    |  52 ++++++--
 12 files changed, 318 insertions(+), 44 deletions(-)


base-commit: 2c50cd5d10e8723797f28c7978f1f61688e0c0d2
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH wireless-next v2 1/7] wifi: rt2x00: start the link tuner when an interface is added
  2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
@ 2026-10-31 16:51 ` Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 2/7] wifi: rt2800: calibrate the crystal of RT3593 with the beacons Ho Jie Feng
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-10-31 16:51 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka; +Cc: Ho Jie Feng

rt2x00lib_start() enables the radio before mac80211 adds the first
interface, so rt2x00link_start_tuner(), called from
rt2x00lib_enable_radio(), finds no station or AP interface and returns.

Start the link tuner again when the first interface is added, and stop
it when the last interface is removed.

Fixes: d96324703ffa ("rt2x00: merge agc and vco works with link tuner")
Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/ralink/rt2x00/rt2x00mac.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
index 3bc0c1c906c91..86547ce81cd2e 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
@@ -264,6 +264,13 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
 	 */
 	rt2x00dev->packet_filter = 0;
 
+	/*
+	 * Start the link tuner again if the radio is enabled before the
+	 * first interface is added.
+	 */
+	if (rt2x00dev->intf_ap_count + rt2x00dev->intf_sta_count == 1)
+		rt2x00link_start_tuner(rt2x00dev);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_add_interface);
@@ -289,6 +296,12 @@ void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
 	else
 		rt2x00dev->intf_sta_count--;
 
+	/*
+	 * Stop the link tuner when the last interface is removed.
+	 */
+	if (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)
+		rt2x00link_stop_tuner(rt2x00dev);
+
 	/*
 	 * Release beacon entry so it is available for
 	 * new interfaces again.
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH wireless-next v2 2/7] wifi: rt2800: calibrate the crystal of RT3593 with the beacons
  2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 1/7] wifi: rt2x00: start the link tuner when an interface is added Ho Jie Feng
@ 2026-10-31 16:51 ` Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 3/7] wifi: rt2800: make MCS 16 fall back to MCS 8 on devices with 3 TX chains Ho Jie Feng
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-10-31 16:51 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka; +Cc: Ho Jie Feng

On a Netgear WNDA4100 (RT3593), as the adapter warms up, the
frequency offset drifts enough to affect the link quality.
In certain cases it can cause the link speed to drop
to 1Mbit/s or below.

Add a frequency calibration step function to RT3593 to use
the frequency offset obtained from the beacon frames of the AP
for tuning.

Test results:
   before after
TX 27.7   28.5
RX 31.6   69.7

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/ralink/rt2x00/rt2800.h   |   1 +
 .../net/wireless/ralink/rt2x00/rt2800lib.c    | 101 ++++++++++++++++++
 .../net/wireless/ralink/rt2x00/rt2800lib.h    |   4 +
 .../net/wireless/ralink/rt2x00/rt2800usb.c    |   1 +
 drivers/net/wireless/ralink/rt2x00/rt2x00.h   |   6 ++
 .../net/wireless/ralink/rt2x00/rt2x00lib.h    |   2 +
 .../net/wireless/ralink/rt2x00/rt2x00link.c   |   9 ++
 7 files changed, 124 insertions(+)

diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h
index 8930589b4967d..b9f6de3a462e3 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
@@ -3163,6 +3163,7 @@ enum rt2800_eeprom_word {
  */
 #define RXWI_W3_SNR0			FIELD32(0x000000ff)
 #define RXWI_W3_SNR1			FIELD32(0x0000ff00)
+#define RXWI_W3_FOFFSET			FIELD32(0xff000000)
 
 /*
  * Macros for converting txpower from EEPROM to mac80211 value
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 2e2e382b62475..e7b6edf485099 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -897,6 +897,28 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2)
 	return (int)max(rssi0, rssi2);
 }
 
+static void rt2800_rx_freq_offset(struct queue_entry *entry,
+				  struct rxdone_entry_desc *rxdesc)
+{
+	struct rt2800_drv_data *drv_data = entry->queue->rt2x00dev->drv_data;
+	__le32 *rxwi = (__le32 *) entry->skb->data;
+	struct ieee80211_hdr *hdr =
+	    (void *)(entry->skb->data + entry->queue->winfo_size);
+	u32 word;
+
+	if (!(rxdesc->dev_flags & RXDONE_MY_BSS) ||
+	    (rxdesc->flags & RX_FLAG_FAILED_FCS_CRC) ||
+	    !ieee80211_is_beacon(hdr->frame_control))
+		return;
+
+	word = rt2x00_desc_read(rxwi, 3);
+
+	spin_lock_bh(&drv_data->bcn_lock);
+	drv_data->bcn_freq_offset = rt2x00_get_field32(word, RXWI_W3_FOFFSET);
+	drv_data->bcn_rate_mode = rxdesc->rate_mode;
+	spin_unlock_bh(&drv_data->bcn_lock);
+}
+
 void rt2800_process_rxwi(struct queue_entry *entry,
 			 struct rxdone_entry_desc *rxdesc)
 {
@@ -935,6 +957,14 @@ void rt2800_process_rxwi(struct queue_entry *entry,
 	 * Convert descriptor AGC value to RSSI value.
 	 */
 	rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word);
+
+	/*
+	 * The frequency offset of the beacons is used for the frequency
+	 * calibration.
+	 */
+	if (rt2x00_rt(entry->queue->rt2x00dev, RT3593))
+		rt2800_rx_freq_offset(entry, rxdesc);
+
 	/*
 	 * Remove RXWI descriptor from start of the buffer.
 	 */
@@ -2443,6 +2473,7 @@ static inline bool rt2800_clk_is_20mhz(struct rt2x00_dev *rt2x00dev)
 }
 
 #define FREQ_OFFSET_BOUND	0x5f
+#define FREQ_OFFSET_INVALID	-128
 
 static void rt2800_freq_cal_mode1(struct rt2x00_dev *rt2x00dev)
 {
@@ -5633,6 +5664,73 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev)
 }
 EXPORT_SYMBOL_GPL(rt2800_vco_calibration);
 
+void rt2800_freq_calibration(struct rt2x00_dev *rt2x00dev)
+{
+	struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+	struct link *link = &rt2x00dev->link;
+	u8 activate_threshold, deactivate_threshold;
+	u8 freq_offset, rate_mode;
+	u16 eeprom;
+	s8 offset;
+
+	if (!rt2x00_rt(rt2x00dev, RT3593))
+		return;
+
+	if (!rt2x00dev->intf_associated) {
+		eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ);
+		rt2x00dev->freq_offset = rt2x00_get_field16(eeprom,
+							    EEPROM_FREQ_OFFSET);
+		link->freq_cal_adjusting = false;
+		return;
+	}
+
+	spin_lock_bh(&drv_data->bcn_lock);
+	offset = drv_data->bcn_freq_offset;
+	rate_mode = drv_data->bcn_rate_mode;
+	drv_data->bcn_freq_offset = FREQ_OFFSET_INVALID;
+	spin_unlock_bh(&drv_data->bcn_lock);
+
+	/* No beacons received */
+	if (offset == FREQ_OFFSET_INVALID)
+		return;
+
+	if (rate_mode == RATE_MODE_CCK) {
+		activate_threshold = 4;
+		deactivate_threshold = 2;
+	} else {
+		activate_threshold = 20;
+		deactivate_threshold = 10;
+	}
+
+	/* Hysteresis */
+	if (abs(offset) >= activate_threshold)
+		link->freq_cal_adjusting = true;
+	else if (abs(offset) <= deactivate_threshold)
+		link->freq_cal_adjusting = false;
+
+	if (!link->freq_cal_adjusting)
+		return;
+
+	freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE);
+	freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND);
+
+	if (offset > 0) {
+		if (freq_offset > 0)
+			freq_offset--;
+		else
+			link->freq_cal_adjusting = false;
+	} else {
+		if (freq_offset < FREQ_OFFSET_BOUND)
+			freq_offset++;
+		else
+			link->freq_cal_adjusting = false;
+	}
+
+	rt2x00dev->freq_offset = freq_offset;
+	rt2800_freq_cal_mode1(rt2x00dev);
+}
+EXPORT_SYMBOL_GPL(rt2800_freq_calibration);
+
 static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev,
 				      struct rt2x00lib_conf *libconf)
 {
@@ -12032,6 +12130,7 @@ static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev)
 
 int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
 {
+	struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
 	int retval;
 	u32 reg;
 
@@ -12105,6 +12204,8 @@ int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
 	 */
 	rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
 
+	spin_lock_init(&drv_data->bcn_lock);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(rt2800_probe_hw);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
index a3c3a751f57e3..9a04a0fba113f 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
@@ -45,6 +45,9 @@ struct rt2800_drv_data {
 	unsigned int ampdu_factor_cnt[4];
 	DECLARE_BITMAP(sta_ids, STA_IDS_SIZE);
 	struct ieee80211_sta *wcid_to_sta[STA_IDS_SIZE];
+	spinlock_t bcn_lock; /* protects bcn_freq_offset, bcn_rate_mode */
+	s8 bcn_freq_offset;
+	u8 bcn_rate_mode;
 };
 
 struct rt2800_ops {
@@ -241,6 +244,7 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
 		       const u32 count);
 void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev);
 void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev);
+void rt2800_freq_calibration(struct rt2x00_dev *rt2x00dev);
 
 int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev);
 void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index 15de3db124298..387c07e2251b7 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
@@ -693,6 +693,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
 	.link_tuner		= rt2800_link_tuner,
 	.gain_calibration	= rt2800_gain_calibration,
 	.vco_calibration	= rt2800_vco_calibration,
+	.freq_calibration	= rt2800_freq_calibration,
 	.watchdog		= rt2800_watchdog,
 	.start_queue		= rt2800usb_start_queue,
 	.kick_queue		= rt2x00usb_kick_queue,
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index 7d313e86d3f20..a80508db32d9c 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -319,6 +319,11 @@ struct link {
 	 */
 	struct ewma_rssi avg_rssi;
 
+	/*
+	 * Controls interval of frequency calibration
+	 */
+	bool freq_cal_adjusting;
+
 	/*
 	 * Work structure for scheduling periodic link tuning.
 	 */
@@ -559,6 +564,7 @@ struct rt2x00lib_ops {
 			    struct link_qual *qual, const u32 count);
 	void (*gain_calibration) (struct rt2x00_dev *rt2x00dev);
 	void (*vco_calibration) (struct rt2x00_dev *rt2x00dev);
+	void (*freq_calibration) (struct rt2x00_dev *rt2x00dev);
 
 	/*
 	 * Data queue handlers.
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
index 2f1385baa7ae9..a6f516ebdb2db 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
@@ -22,6 +22,8 @@
 #define LINK_TUNE_INTERVAL	round_jiffies_relative(LINK_TUNE_SECONDS * HZ)
 #define AGC_SECONDS		4
 #define VCO_SECONDS		10
+#define FREQ_SECONDS		10
+#define FREQ_ADJUST_SECONDS	1
 
 /*
  * rt2x00_rate: Per rate device information
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00link.c b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
index fb23d409fba87..2dc4edc12834f 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
@@ -343,6 +343,7 @@ static void rt2x00link_tuner(struct work_struct *work)
 	struct rt2x00_dev *rt2x00dev =
 	    container_of(work, struct rt2x00_dev, link.work.work);
 	struct link *link = &rt2x00dev->link;
+	unsigned int freq_interval;
 
 	/*
 	 * When the radio is shutting down we should
@@ -362,6 +363,14 @@ static void rt2x00link_tuner(struct work_struct *work)
 	    (link->count % (AGC_SECONDS / LINK_TUNE_SECONDS)) == 0)
 		rt2x00dev->ops->lib->gain_calibration(rt2x00dev);
 
+	freq_interval = link->freq_cal_adjusting ?
+			FREQ_ADJUST_SECONDS : FREQ_SECONDS;
+
+	/* VCO calibration should be done after frequency calibration */
+	if (rt2x00dev->ops->lib->freq_calibration &&
+	    (link->count % (freq_interval / LINK_TUNE_SECONDS)) == 0)
+		rt2x00dev->ops->lib->freq_calibration(rt2x00dev);
+
 	if (rt2x00dev->ops->lib->vco_calibration &&
 	    rt2x00_has_cap_vco_recalibration(rt2x00dev) &&
 	    (link->count % (VCO_SECONDS / LINK_TUNE_SECONDS)) == 0)
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH wireless-next v2 3/7] wifi: rt2800: make MCS 16 fall back to MCS 8 on devices with 3 TX chains
  2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 1/7] wifi: rt2x00: start the link tuner when an interface is added Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 2/7] wifi: rt2800: calibrate the crystal of RT3593 with the beacons Ho Jie Feng
@ 2026-10-31 16:51 ` Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 4/7] wifi: rt2800: don't report an A-MPDU length to minstrel_ht Ho Jie Feng
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-10-31 16:51 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka; +Cc: Ho Jie Feng

MCS 15 has worse reliability than MCS 16, since 16 and above
are 3 stream rates, and 15 is the top of the 2 stream rates.
Falling back from MCS 16 to MCS 15 does not make sense, so
make the fallback to MCS 8 on all devices with 3 TX chains,
like the Ralink driver does.

The retry chain is now MCS 16 to MCS 8, skipping 7 MCS rates
in between. Reflect that in the retry calculation code. This
also fixes the retry count on RT3883, which already used this
fallback.

Test results:
   before after
TX 28.2   29.1
RX 70.4   70.2

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 10 +++++++++-
 drivers/net/wireless/ralink/rt2x00/rt2x00.h    |  5 +++++
 drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 14 ++++++++++++--
 3 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index e7b6edf485099..5e1ee016624c6 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -1116,6 +1116,14 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi,
 		 */
 		__set_bit(TXDONE_SUCCESS, &txdesc.flags);
 		txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
+
+		/*
+		 * On devices with 3 TX chains MCS16 falls back to MCS8, so a
+		 * frame that was sent below MCS16 after a three stream rate
+		 * skipped MCS9 to MCS15.
+		 */
+		if (rt2x00_is_3t(rt2x00dev) && mcs >= 16 && real_mcs < 16)
+			txdesc.retry -= 7;
 	} else {
 		/*
 		 * Transmission failed. The number of retries is
@@ -6303,7 +6311,7 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
 	reg = rt2x00_rt(rt2x00dev, RT5592) ? 0x00000082 : 0x00000002;
 	rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, reg);
 
-	if (rt2x00_rt(rt2x00dev, RT3883)) {
+	if (rt2x00_is_3t(rt2x00dev)) {
 		rt2800_register_write(rt2x00dev, TX_FBK_CFG_3S_0, 0x12111008);
 		rt2800_register_write(rt2x00dev, TX_FBK_CFG_3S_1, 0x16151413);
 	}
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index a80508db32d9c..f82dda0266f9b 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -1193,6 +1193,11 @@ static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev)
 	return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC);
 }
 
+static inline bool rt2x00_is_3t(struct rt2x00_dev *rt2x00dev)
+{
+	return rt2x00dev->default_ant.tx_chain_num == 3;
+}
+
 /* Helpers for capability flags */
 
 static inline bool
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 4d94b7062f440..db33c1215de97 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -329,10 +329,10 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev,
 	 * lowest rate was used.
 	 */
 	for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) {
-		tx_info->status.rates[i].idx = rate_idx - i;
+		tx_info->status.rates[i].idx = rate_idx;
 		tx_info->status.rates[i].flags = rate_flags;
 
-		if (rate_idx - i == 0) {
+		if (rate_idx == 0) {
 			/*
 			 * The lowest rate (index 0) was used until the
 			 * number of max retries was reached.
@@ -340,8 +340,18 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev,
 			tx_info->status.rates[i].count = retry_rates - i;
 			i++;
 			break;
+		} else if (rate_idx == 16) {
+			/*
+			 * TX_FBK_CFG_3S_0 makes MCS16 fall back to MCS8 on
+			 * devices with 3 TX chains, the decrement below shifts
+			 * it back to 8.
+			 */
+			if (rt2x00_is_3t(rt2x00dev) &&
+			    (rate_flags & IEEE80211_TX_RC_MCS))
+				rate_idx = 9;
 		}
 		tx_info->status.rates[i].count = 1;
+		rate_idx--;
 	}
 	if (i < (IEEE80211_TX_MAX_RATES - 1))
 		tx_info->status.rates[i].idx = -1; /* terminate */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH wireless-next v2 4/7] wifi: rt2800: don't report an A-MPDU length to minstrel_ht
  2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
                   ` (2 preceding siblings ...)
  2026-10-31 16:51 ` [PATCH wireless-next v2 3/7] wifi: rt2800: make MCS 16 fall back to MCS 8 on devices with 3 TX chains Ho Jie Feng
@ 2026-10-31 16:51 ` Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 5/7] wifi: rt2800: support A-MSDU in A-MPDU Ho Jie Feng
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-10-31 16:51 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka; +Cc: Ho Jie Feng

Set IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN, which was added for drivers
that can't report the proper AMPDU length and record this workaround
below the TODO comment.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 1 +
 drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 5e1ee016624c6..1301bc24d9413 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -11860,6 +11860,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 	ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK);
 	ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM);
 	ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS);
+	ieee80211_hw_set(rt2x00dev->hw, TX_STATUS_NO_AMPDU_LEN);
 
 	/*
 	 * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index db33c1215de97..350e6bf24f7aa 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -374,6 +374,8 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev,
 	 * by the hw we could provide the real ampdu_len to mac80211
 	 * which would allow the rc algorithm to better decide on
 	 * which rates are suitable.
+	 *
+	 * Currently, we work around this by setting TX_STATUS_NO_AMPDU_LEN.
 	 */
 	if (test_bit(TXDONE_AMPDU, &txdesc->flags) ||
 	    tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH wireless-next v2 5/7] wifi: rt2800: support A-MSDU in A-MPDU
  2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
                   ` (3 preceding siblings ...)
  2026-10-31 16:51 ` [PATCH wireless-next v2 4/7] wifi: rt2800: don't report an A-MPDU length to minstrel_ht Ho Jie Feng
@ 2026-10-31 16:51 ` Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 6/7] wifi: rt2800usb: enable RX bulk aggregation Ho Jie Feng
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-10-31 16:51 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka; +Cc: Ho Jie Feng

Set SUPPORTS_AMSDU_IN_AMPDU, which can increase throughput.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 1301bc24d9413..83d349a238428 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -11861,6 +11861,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 	ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM);
 	ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS);
 	ieee80211_hw_set(rt2x00dev->hw, TX_STATUS_NO_AMPDU_LEN);
+	ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_AMSDU_IN_AMPDU);
 
 	/*
 	 * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH wireless-next v2 6/7] wifi: rt2800usb: enable RX bulk aggregation
  2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
                   ` (4 preceding siblings ...)
  2026-10-31 16:51 ` [PATCH wireless-next v2 5/7] wifi: rt2800: support A-MSDU in A-MPDU Ho Jie Feng
@ 2026-10-31 16:51 ` Ho Jie Feng
  2026-10-31 16:51 ` [PATCH wireless-next v2 7/7] wifi: rt2x00usb: use a threaded NAPI with GRO for RX Ho Jie Feng
  2026-10-31 16:55 ` [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Jie Feng
  7 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-10-31 16:51 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka; +Cc: Ho Jie Feng

Enable RX bulk aggregation on high speed USB, to receive more
frames per URB.

RX buffers are now 24KB, so reduce it to 16 buffers instead of 128,
so that they take 512 KB in total instead of 4 MB.

Test results:
   before after
TX 30.5   30.5
RX 67.8   80.9

This code references the mt7601u driver.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 .../net/wireless/ralink/rt2x00/rt2800usb.c    | 32 +++++--
 .../net/wireless/ralink/rt2x00/rt2800usb.h    |  6 ++
 drivers/net/wireless/ralink/rt2x00/rt2x00.h   |  3 +-
 .../net/wireless/ralink/rt2x00/rt2x00dev.c    | 84 +++++++++++++------
 .../net/wireless/ralink/rt2x00/rt2x00mmio.c   |  2 +-
 .../net/wireless/ralink/rt2x00/rt2x00usb.c    |  5 +-
 6 files changed, 100 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index 387c07e2251b7..2e4277b1e5d1b 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
@@ -301,15 +301,15 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
 		return -EIO;
 
 	rt2x00_set_field32(&reg, USB_DMA_CFG_PHY_CLEAR, 0);
-	rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_EN, 0);
+	rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_EN,
+			   rt2x00dev->rx->usb_maxpacket == 512);
 	rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128);
 	/*
 	 * Total room for RX frames in kilobytes, PBF might still exceed
 	 * this limit so reduce the number to prevent errors.
 	 */
 	rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_LIMIT,
-			   ((rt2x00dev->rx->limit * DATA_FRAME_SIZE)
-			    / 1024) - 3);
+			   (rt2x00dev->rx->data_size / 1024) - 4);
 	rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_EN, 1);
 	rt2x00_set_field32(&reg, USB_DMA_CFG_TX_BULK_EN, 1);
 	rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, reg);
@@ -478,6 +478,27 @@ static void rt2800usb_work_txdone(struct work_struct *work)
 /*
  * RX control handlers
  */
+static unsigned int rt2800usb_get_rx_seg_len(void *data, unsigned int len)
+{
+	unsigned int min_seg_len = RXINFO_DESC_SIZE + RXWI_DESC_SIZE_4WORDS +
+				   RXD_DESC_SIZE;
+	unsigned int rx_pkt_len;
+	u32 word;
+
+	if (len < min_seg_len)
+		return 0;
+
+	word = rt2x00_desc_read(data, 0);
+	rx_pkt_len = rt2x00_get_field32(word, RXINFO_W0_USB_DMA_RX_PKT_LEN);
+
+	if (!rx_pkt_len ||
+	    RXINFO_DESC_SIZE + rx_pkt_len + RXD_DESC_SIZE > len ||
+	    rx_pkt_len & 0x3)
+		return 0;
+
+	return RXINFO_DESC_SIZE + rx_pkt_len + RXD_DESC_SIZE;
+}
+
 static void rt2800usb_fill_rxdone(struct queue_entry *entry,
 				  struct rxdone_entry_desc *rxdesc)
 {
@@ -706,6 +727,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
 	.clear_beacon		= rt2800_clear_beacon,
 	.get_tx_data_len	= rt2800usb_get_tx_data_len,
 	.fill_rxdone		= rt2800usb_fill_rxdone,
+	.get_rx_seg_len		= rt2800usb_get_rx_seg_len,
 	.config_shared_key	= rt2800_config_shared_key,
 	.config_pairwise_key	= rt2800_config_pairwise_key,
 	.config_filter		= rt2800_config_filter,
@@ -725,8 +747,8 @@ static void rt2800usb_queue_init(struct data_queue *queue)
 
 	switch (queue->qid) {
 	case QID_RX:
-		queue->limit = 128;
-		queue->data_size = AGGREGATION_SIZE;
+		queue->limit = 16;
+		queue->data_size = MAX_RXBULK_SIZE;
 		queue->desc_size = RXINFO_DESC_SIZE;
 		queue->winfo_size = rxwi_size;
 		queue->priv_size = sizeof(struct queue_entry_priv_usb);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.h b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
index 9e180e9e41d92..2fae79072fd58 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
@@ -29,6 +29,12 @@
  */
 #define TXINFO_DESC_SIZE		(1 * sizeof(__le32))
 #define RXINFO_DESC_SIZE		(1 * sizeof(__le32))
+#define RXD_DESC_SIZE			(1 * sizeof(__le32))
+
+/*
+ * RX buffer size
+ */
+#define MAX_RXBULK_SIZE			24576
 
 /*
  * TX Info structure
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index f82dda0266f9b..4c83168e9c169 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -593,6 +593,7 @@ struct rt2x00lib_ops {
 	 */
 	void (*fill_rxdone) (struct queue_entry *entry,
 			     struct rxdone_entry_desc *rxdesc);
+	unsigned int (*get_rx_seg_len) (void *data, unsigned int len);
 
 	/*
 	 * Configuration handlers.
@@ -1453,7 +1454,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
 void rt2x00lib_txdone_nomatch(struct queue_entry *entry,
 			      struct txdone_entry_desc *txdesc);
 void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status);
-void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp);
+void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp);
 
 /*
  * mac80211 handlers.
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 350e6bf24f7aa..8f02b62cecdf6 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -746,35 +746,14 @@ static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
 	return 0;
 }
 
-void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
+static void rt2x00lib_rxdone_skb(struct queue_entry *entry)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct rxdone_entry_desc rxdesc;
-	struct sk_buff *skb;
 	struct ieee80211_rx_status *rx_status;
 	unsigned int header_length;
 	int rate_idx;
 
-	if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) ||
-	    !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
-		goto submit_entry;
-
-	if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
-		goto submit_entry;
-
-	/*
-	 * Allocate a new sk_buffer. If no new buffer available, drop the
-	 * received frame and reuse the existing buffer.
-	 */
-	skb = rt2x00queue_alloc_rxskb(entry, gfp);
-	if (!skb)
-		goto submit_entry;
-
-	/*
-	 * Unmap the skb.
-	 */
-	rt2x00queue_unmap_skb(entry);
-
 	/*
 	 * Extract the RXD details.
 	 */
@@ -790,7 +769,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
 		rt2x00_err(rt2x00dev, "Wrong frame size %d max %d\n",
 			   rxdesc.size, entry->queue->data_size);
 		dev_kfree_skb(entry->skb);
-		goto renew_skb;
+		return;
 	}
 
 	/*
@@ -869,8 +848,65 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
 	rx_status->antenna = rt2x00dev->link.ant.active.rx;
 
 	ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
+}
+
+void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp)
+{
+	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+	struct skb_frame_desc *skbdesc, *frame_skbdesc;
+	struct sk_buff *skb;
+	unsigned int seg_len;
+	u8 *data;
+
+	if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) ||
+	    !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+		goto submit_entry;
+
+	if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
+		goto submit_entry;
+
+	if (rt2x00dev->ops->lib->get_rx_seg_len) {
+		skb = entry->skb;
+		skbdesc = get_skb_frame_desc(skb);
+		data = skb->data;
+
+		while ((seg_len = rt2x00dev->ops->lib->get_rx_seg_len(data, len))) {
+			entry->skb = __dev_alloc_skb(seg_len, gfp);
+			if (!entry->skb)
+				break;
+
+			skb_put_data(entry->skb, data, seg_len);
+
+			frame_skbdesc = get_skb_frame_desc(entry->skb);
+			memset(frame_skbdesc, 0, sizeof(*frame_skbdesc));
+			frame_skbdesc->desc = skbdesc->desc;
+			frame_skbdesc->desc_len = skbdesc->desc_len;
+
+			rt2x00lib_rxdone_skb(entry);
+
+			data += seg_len;
+			len -= seg_len;
+		}
+
+		entry->skb = skb;
+		goto submit_entry;
+	}
+
+	/*
+	 * Allocate a new sk_buffer. If no new buffer available, drop the
+	 * received frame and reuse the existing buffer.
+	 */
+	skb = rt2x00queue_alloc_rxskb(entry, gfp);
+	if (!skb)
+		goto submit_entry;
+
+	/*
+	 * Unmap the skb.
+	 */
+	rt2x00queue_unmap_skb(entry);
+
+	rt2x00lib_rxdone_skb(entry);
 
-renew_skb:
 	/*
 	 * Replace the skb with the freshly allocated one.
 	 */
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
index 93f76acf3dc7d..a1d8b8d13daca 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
@@ -78,7 +78,7 @@ bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev)
 		/*
 		 * Send the frame to rt2x00lib for further processing.
 		 */
-		rt2x00lib_rxdone(entry, GFP_ATOMIC);
+		rt2x00lib_rxdone(entry, entry->skb->len, GFP_ATOMIC);
 	}
 
 	return !max_rx;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
index c9fbc3710fd8d..4519a20a7f4bc 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
@@ -345,6 +345,7 @@ static void rt2x00usb_work_rxdone(struct work_struct *work)
 {
 	struct rt2x00_dev *rt2x00dev =
 	    container_of(work, struct rt2x00_dev, rxdone_work);
+	struct queue_entry_priv_usb *entry_priv;
 	struct queue_entry *entry;
 	struct skb_frame_desc *skbdesc;
 	u8 rxd[32];
@@ -365,7 +366,9 @@ static void rt2x00usb_work_rxdone(struct work_struct *work)
 		/*
 		 * Send the frame to rt2x00lib for further processing.
 		 */
-		rt2x00lib_rxdone(entry, GFP_KERNEL);
+		entry_priv = entry->priv_data;
+		rt2x00lib_rxdone(entry, entry_priv->urb->actual_length,
+				 GFP_KERNEL);
 	}
 }
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH wireless-next v2 7/7] wifi: rt2x00usb: use a threaded NAPI with GRO for RX
  2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
                   ` (5 preceding siblings ...)
  2026-10-31 16:51 ` [PATCH wireless-next v2 6/7] wifi: rt2800usb: enable RX bulk aggregation Ho Jie Feng
@ 2026-10-31 16:51 ` Ho Jie Feng
  2026-10-31 16:55 ` [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Jie Feng
  7 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-10-31 16:51 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka; +Cc: Ho Jie Feng

GRO makes sense now that RX aggregation is enabled, since multiple
frames arrive at once.

This code is referenced from commit 5323d3e50c20
("wifi: mt76: mt76u: use a threaded NAPI for the RX path")

Test results:
   before after
TX 30.5   30.5
RX 80.9   161.7

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/ralink/rt2x00/rt2x00.h   |  7 ++-
 .../net/wireless/ralink/rt2x00/rt2x00dev.c    |  6 ++-
 .../net/wireless/ralink/rt2x00/rt2x00usb.c    | 49 ++++++++++++++++---
 3 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index 4c83168e9c169..25262b96c6941 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -955,9 +955,14 @@ struct rt2x00_dev {
 	/**
 	 * Scheduled work for TX/RX done handling (USB devices)
 	 */
-	struct work_struct rxdone_work;
 	struct work_struct txdone_work;
 
+	/*
+	 * NAPI context for RX done handling (USB devices).
+	 */
+	struct net_device *napi_dev;
+	struct napi_struct napi;
+
 	/*
 	 * Powersaving work
 	 */
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 8f02b62cecdf6..6226bcbf24e5b 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -847,7 +847,11 @@ static void rt2x00lib_rxdone_skb(struct queue_entry *entry)
 	rx_status->bw = rxdesc.bw;
 	rx_status->antenna = rt2x00dev->link.ant.active.rx;
 
-	ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
+	if (rt2x00dev->napi_dev)
+		ieee80211_rx_napi(rt2x00dev->hw, NULL, entry->skb,
+				  &rt2x00dev->napi);
+	else
+		ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
 }
 
 void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
index 4519a20a7f4bc..d11244a2c4f2e 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
@@ -341,10 +341,10 @@ static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void *data)
 /*
  * RX data handlers.
  */
-static void rt2x00usb_work_rxdone(struct work_struct *work)
+static int rt2x00usb_poll_rxdone(struct napi_struct *napi, int budget)
 {
 	struct rt2x00_dev *rt2x00dev =
-	    container_of(work, struct rt2x00_dev, rxdone_work);
+	    container_of(napi, struct rt2x00_dev, napi);
 	struct queue_entry_priv_usb *entry_priv;
 	struct queue_entry *entry;
 	struct skb_frame_desc *skbdesc;
@@ -368,8 +368,12 @@ static void rt2x00usb_work_rxdone(struct work_struct *work)
 		 */
 		entry_priv = entry->priv_data;
 		rt2x00lib_rxdone(entry, entry_priv->urb->actual_length,
-				 GFP_KERNEL);
+				 GFP_ATOMIC);
 	}
+
+	napi_complete(napi);
+
+	return 0;
 }
 
 static void rt2x00usb_interrupt_rxdone(struct urb *urb)
@@ -396,7 +400,7 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
 	/*
 	 * Schedule the delayed work for processing RX data
 	 */
-	queue_work(rt2x00dev->workqueue, &rt2x00dev->rxdone_work);
+	napi_schedule(&rt2x00dev->napi);
 }
 
 static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void *data)
@@ -496,7 +500,7 @@ void rt2x00usb_flush_queue(struct data_queue *queue, bool drop)
 		completion = &queue->rt2x00dev->txdone_work;
 		break;
 	case QID_RX:
-		completion = &queue->rt2x00dev->rxdone_work;
+		completion = NULL;
 		break;
 	default:
 		return;
@@ -515,7 +519,13 @@ void rt2x00usb_flush_queue(struct data_queue *queue, bool drop)
 		 * Schedule the completion handler manually, when this
 		 * worker function runs, it should cleanup the queue.
 		 */
-		queue_work(queue->rt2x00dev->workqueue, completion);
+		if (completion) {
+			queue_work(queue->rt2x00dev->workqueue, completion);
+		} else {
+			local_bh_disable();
+			napi_schedule(&queue->rt2x00dev->napi);
+			local_bh_enable();
+		}
 
 		/*
 		 * Wait for a little while to give the driver
@@ -750,7 +760,7 @@ void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev)
 
 	usb_kill_anchored_urbs(rt2x00dev->anchor);
 	hrtimer_cancel(&rt2x00dev->txstatus_timer);
-	cancel_work_sync(&rt2x00dev->rxdone_work);
+	napi_synchronize(&rt2x00dev->napi);
 	cancel_work_sync(&rt2x00dev->txdone_work);
 
 	queue_for_each(rt2x00dev, queue)
@@ -758,6 +768,14 @@ void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev)
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize);
 
+static void rt2x00usb_free_napi(struct rt2x00_dev *rt2x00dev)
+{
+	napi_disable(&rt2x00dev->napi);
+	netif_napi_del(&rt2x00dev->napi);
+	free_netdev(rt2x00dev->napi_dev);
+	rt2x00dev->napi_dev = NULL;
+}
+
 int rt2x00usb_probe(struct usb_interface *usb_intf,
 		    const struct rt2x00_ops *ops)
 {
@@ -801,9 +819,21 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
 
 	rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB);
 
-	INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone);
 	INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone);
 
+	rt2x00dev->napi_dev = alloc_netdev_dummy(0);
+	if (!rt2x00dev->napi_dev) {
+		retval = -ENOMEM;
+		goto exit_free_hw;
+	}
+
+	strscpy(rt2x00dev->napi_dev->name, wiphy_name(hw->wiphy),
+		sizeof(rt2x00dev->napi_dev->name));
+	netif_napi_add(rt2x00dev->napi_dev, &rt2x00dev->napi,
+		       rt2x00usb_poll_rxdone);
+	netif_threaded_enable(rt2x00dev->napi_dev);
+	napi_enable(&rt2x00dev->napi);
+
 	init_usb_anchor(rt2x00dev->anchor);
 
 	retval = rt2x00lib_probe_dev(rt2x00dev);
@@ -814,6 +844,8 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
 
 exit_free_anchor:
 	usb_kill_anchored_urbs(rt2x00dev->anchor);
+	rt2x00usb_free_napi(rt2x00dev);
+exit_free_hw:
 	ieee80211_free_hw(hw);
 	usb_set_intfdata(usb_intf, NULL);
 
@@ -830,6 +862,7 @@ void rt2x00usb_disconnect(struct usb_interface *usb_intf)
 	 * Free all allocated data.
 	 */
 	rt2x00lib_remove_dev(rt2x00dev);
+	rt2x00usb_free_napi(rt2x00dev);
 	ieee80211_free_hw(hw);
 
 	usb_set_intfdata(usb_intf, NULL);
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb
  2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
                   ` (6 preceding siblings ...)
  2026-10-31 16:51 ` [PATCH wireless-next v2 7/7] wifi: rt2x00usb: use a threaded NAPI with GRO for RX Ho Jie Feng
@ 2026-10-31 16:55 ` Jie Feng
  7 siblings, 0 replies; 9+ messages in thread
From: Jie Feng @ 2026-10-31 16:55 UTC (permalink / raw)
  To: linux-wireless, Stanislaw Gruszka

On Sun, 1 Nov 2026 at 00:51, Ho Jie Feng <hjf3108@gmail.com> wrote:
>
> This series improves the throughput of rt2800usb.
>
> Implements
> - Frequency offset calibration
> - Better fallback rates
> - Aggregation
> - GRO
>
> Tested on WNDA4100 RT3593, 5 GHz with iperf3
>
>   patch                          before        after
>                                  TX    RX      TX    RX
>   2 crystal calibration          27.7  31.6    28.5  69.7st
>   3 MCS16 to MCS8 TX status      27.0  70.0    29.4  70.0
>   6 RX bulk aggregation          30.5  67.8    30.5  80.9
>   7 threaded NAPI with GRO       30.5  80.9    30.5  161.7
>
> RT3070 did not have any visible change for both TX and RX.
Changes since v2:
- Correct the link tuner start and stop conditions
- Extend fallback and MCS retry calculation to all devices with 3 TX chains
- Remove TX AMSDU and fast-xmit

> Ho Jie Feng (7):
>   wifi: rt2x00: start the link tuner when an interface is added
>   wifi: rt2800: calibrate the crystal of RT3593 with the beacons
>   wifi: rt2800: make MCS 16 fall back to MCS 8 on devices with 3 TX
>     chains
>   wifi: rt2800: don't report an A-MPDU length to minstrel_ht
>   wifi: rt2800: support A-MSDU in A-MPDU
>   wifi: rt2800usb: enable RX bulk aggregation
>   wifi: rt2x00usb: use a threaded NAPI with GRO for RX
>
>  drivers/net/wireless/ralink/rt2x00/rt2800.h   |   1 +
>  .../net/wireless/ralink/rt2x00/rt2800lib.c    | 113 +++++++++++++++++-
>  .../net/wireless/ralink/rt2x00/rt2800lib.h    |   4 +
>  .../net/wireless/ralink/rt2x00/rt2800usb.c    |  33 ++++-
>  .../net/wireless/ralink/rt2x00/rt2800usb.h    |   6 +
>  drivers/net/wireless/ralink/rt2x00/rt2x00.h   |  21 +++-
>  .../net/wireless/ralink/rt2x00/rt2x00dev.c    | 106 +++++++++++-----
>  .../net/wireless/ralink/rt2x00/rt2x00lib.h    |   2 +
>  .../net/wireless/ralink/rt2x00/rt2x00link.c   |   9 ++
>  .../net/wireless/ralink/rt2x00/rt2x00mac.c    |  13 ++
>  .../net/wireless/ralink/rt2x00/rt2x00mmio.c   |   2 +-
>  .../net/wireless/ralink/rt2x00/rt2x00usb.c    |  52 ++++++--
>  12 files changed, 318 insertions(+), 44 deletions(-)
>
>
> base-commit: 2c50cd5d10e8723797f28c7978f1f61688e0c0d2
> --
> 2.53.0
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-10-31 16:55 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-31 16:51 [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Ho Jie Feng
2026-10-31 16:51 ` [PATCH wireless-next v2 1/7] wifi: rt2x00: start the link tuner when an interface is added Ho Jie Feng
2026-10-31 16:51 ` [PATCH wireless-next v2 2/7] wifi: rt2800: calibrate the crystal of RT3593 with the beacons Ho Jie Feng
2026-10-31 16:51 ` [PATCH wireless-next v2 3/7] wifi: rt2800: make MCS 16 fall back to MCS 8 on devices with 3 TX chains Ho Jie Feng
2026-10-31 16:51 ` [PATCH wireless-next v2 4/7] wifi: rt2800: don't report an A-MPDU length to minstrel_ht Ho Jie Feng
2026-10-31 16:51 ` [PATCH wireless-next v2 5/7] wifi: rt2800: support A-MSDU in A-MPDU Ho Jie Feng
2026-10-31 16:51 ` [PATCH wireless-next v2 6/7] wifi: rt2800usb: enable RX bulk aggregation Ho Jie Feng
2026-10-31 16:51 ` [PATCH wireless-next v2 7/7] wifi: rt2x00usb: use a threaded NAPI with GRO for RX Ho Jie Feng
2026-10-31 16:55 ` [PATCH wireless-next v2 0/7] wifi: rt2x00: improve the throughput of rt2800usb Jie Feng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox