Files
openwrt/target/linux/airoha/patches-6.12/920-14-net-airoha-Better-handle-MIB-for-GDM-with-multiple-p.patch
T
Lorenzo Bianconi d22ceb8d24 airoha: Improve LRO performances
Add hardware TCP Large Receive Offload (LRO) support to the airoha_eth
driver, leveraging the EN7581/AN7583 SoC's 8 dedicated LRO hardware queues
mapped to RX queues 24–31. LRO hw offloading does not support
Scatter-Gather (SG) so it is required to increase the page_pool allocation
order to 2 for RX queues 24–31 (LRO queues).

Performance comparison between GRO and hw LRO has been carried out using
a 10Gbps NIC:

GRO: ~2.7 Gbps
LRO: ~8.1 Gbps

Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://github.com/openwrt/openwrt/pull/23530
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
2026-05-27 09:17:12 +02:00

416 lines
15 KiB
Diff

From 9652322e0b47eacfef497828f6476a2a3169fd13 Mon Sep 17 00:00:00 2001
Message-ID: <9652322e0b47eacfef497828f6476a2a3169fd13.1779351672.git.lorenzo@kernel.org>
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Sat, 17 Jan 2026 14:46:12 +0100
Subject: [PATCH] net: airoha: Better handle MIB for GDM with multiple port
attached
In the context of a GDM that can have multiple port attached (GDM3/4)
the HW counter (MIB) are global for the GDM port. This cause duplicated
stats reported to the kernel for the related interface.
The SoC supports a split MIB feature where each counter is tracked based
on the relevant HW channel (NBQ) to account for this scenario and
provide a way to select the related counter on accessing the MIB
registers.
Enable this feature for GDM3 and GDM4 and configure the relevant HW
channel before updating the HW stats to report correct HW counter to the
kernel for the related interface.
Also move the stats struct from port to dev since HW counter are
now specific to the network interface instead of the GDM port.
Co-developed-by: Brown Huang <Brown.huang@airoha.com>
Signed-off-by: Brown Huang <Brown.huang@airoha.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 191 +++++++++++++----------
drivers/net/ethernet/airoha/airoha_eth.h | 7 +-
2 files changed, 112 insertions(+), 86 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -612,6 +612,14 @@ static int airoha_fe_init(struct airoha_
airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX),
GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK);
+ /* Enable split for MIB counters for GDM3 and GDM4 */
+ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM3_IDX),
+ FE_GDM_TX_MIB_SPLIT_EN_MASK |
+ FE_GDM_RX_MIB_SPLIT_EN_MASK);
+ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM4_IDX),
+ FE_GDM_TX_MIB_SPLIT_EN_MASK |
+ FE_GDM_RX_MIB_SPLIT_EN_MASK);
+
airoha_fe_crsn_qsel_init(eth);
airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK);
@@ -1767,149 +1775,169 @@ static void airoha_qdma_stop_napi(struct
}
}
-static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
+static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
- spin_lock(&port->stats.lock);
- u64_stats_update_begin(&port->stats.syncp);
+ u64_stats_update_begin(&dev->stats.syncp);
+
+ /* Read relevant MIB for GDM with multiple port attached */
+ if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
+ airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id),
+ FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK,
+ FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) |
+ FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq));
/* TX */
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
- port->stats.tx_ok_pkts += ((u64)val << 32);
+ dev->stats.tx_ok_pkts += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
- port->stats.tx_ok_pkts += val;
+ dev->stats.tx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
- port->stats.tx_ok_bytes += ((u64)val << 32);
+ dev->stats.tx_ok_bytes += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
- port->stats.tx_ok_bytes += val;
+ dev->stats.tx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
- port->stats.tx_drops += val;
+ dev->stats.tx_drops += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
- port->stats.tx_broadcast += val;
+ dev->stats.tx_broadcast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
- port->stats.tx_multicast += val;
+ dev->stats.tx_multicast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
- port->stats.tx_len[i] += val;
+ dev->stats.tx_len[i] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
/* RX */
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
- port->stats.rx_ok_pkts += ((u64)val << 32);
+ dev->stats.rx_ok_pkts += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
- port->stats.rx_ok_pkts += val;
+ dev->stats.rx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
- port->stats.rx_ok_bytes += ((u64)val << 32);
+ dev->stats.rx_ok_bytes += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
- port->stats.rx_ok_bytes += val;
+ dev->stats.rx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
- port->stats.rx_drops += val;
+ dev->stats.rx_drops += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
- port->stats.rx_broadcast += val;
+ dev->stats.rx_broadcast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
- port->stats.rx_multicast += val;
+ dev->stats.rx_multicast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
- port->stats.rx_errors += val;
+ dev->stats.rx_errors += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
- port->stats.rx_crc_error += val;
+ dev->stats.rx_crc_error += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
- port->stats.rx_over_errors += val;
+ dev->stats.rx_over_errors += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
- port->stats.rx_fragment += val;
+ dev->stats.rx_fragment += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
- port->stats.rx_jabber += val;
+ dev->stats.rx_jabber += val;
i = 0;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
- port->stats.rx_len[i] += val;
+ dev->stats.rx_len[i] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
+
+ u64_stats_update_end(&dev->stats.syncp);
+}
- /* reset mib counters */
- airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
+{
+ struct airoha_gdm_port *port = dev->port;
+ int i;
+
+ spin_lock(&port->stats_lock);
+
+ for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
+ if (port->devs[i])
+ airoha_dev_get_hw_stats(port->devs[i]);
+ }
+
+ /* Reset MIB counters */
+ airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id),
FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
- u64_stats_update_end(&port->stats.syncp);
- spin_unlock(&port->stats.lock);
+ spin_unlock(&port->stats_lock);
}
void airoha_set_port_mtu(struct airoha_eth *eth, struct airoha_gdm_port *port)
@@ -2237,23 +2265,22 @@ static void airoha_dev_get_stats64(struc
struct rtnl_link_stats64 *storage)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(dev);
do {
- start = u64_stats_fetch_begin(&port->stats.syncp);
- storage->rx_packets = port->stats.rx_ok_pkts;
- storage->tx_packets = port->stats.tx_ok_pkts;
- storage->rx_bytes = port->stats.rx_ok_bytes;
- storage->tx_bytes = port->stats.tx_ok_bytes;
- storage->multicast = port->stats.rx_multicast;
- storage->rx_errors = port->stats.rx_errors;
- storage->rx_dropped = port->stats.rx_drops;
- storage->tx_dropped = port->stats.tx_drops;
- storage->rx_crc_errors = port->stats.rx_crc_error;
- storage->rx_over_errors = port->stats.rx_over_errors;
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
+ storage->rx_packets = dev->stats.rx_ok_pkts;
+ storage->tx_packets = dev->stats.tx_ok_pkts;
+ storage->rx_bytes = dev->stats.rx_ok_bytes;
+ storage->tx_bytes = dev->stats.tx_ok_bytes;
+ storage->multicast = dev->stats.rx_multicast;
+ storage->rx_errors = dev->stats.rx_errors;
+ storage->rx_dropped = dev->stats.rx_drops;
+ storage->tx_dropped = dev->stats.tx_drops;
+ storage->rx_crc_errors = dev->stats.rx_crc_error;
+ storage->rx_over_errors = dev->stats.rx_over_errors;
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
@@ -2651,20 +2678,19 @@ static void airoha_ethtool_get_mac_stats
struct ethtool_eth_mac_stats *stats)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(dev);
do {
- start = u64_stats_fetch_begin(&port->stats.syncp);
- stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
- stats->OctetsTransmittedOK = port->stats.tx_ok_bytes;
- stats->MulticastFramesXmittedOK = port->stats.tx_multicast;
- stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast;
- stats->FramesReceivedOK = port->stats.rx_ok_pkts;
- stats->OctetsReceivedOK = port->stats.rx_ok_bytes;
- stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast;
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
+ stats->FramesTransmittedOK = dev->stats.tx_ok_pkts;
+ stats->OctetsTransmittedOK = dev->stats.tx_ok_bytes;
+ stats->MulticastFramesXmittedOK = dev->stats.tx_multicast;
+ stats->BroadcastFramesXmittedOK = dev->stats.tx_broadcast;
+ stats->FramesReceivedOK = dev->stats.rx_ok_pkts;
+ stats->OctetsReceivedOK = dev->stats.rx_ok_bytes;
+ stats->BroadcastFramesReceivedOK = dev->stats.rx_broadcast;
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
@@ -2684,8 +2710,7 @@ airoha_ethtool_get_rmon_stats(struct net
const struct ethtool_rmon_hist_range **ranges)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_hw_stats *hw_stats = &port->stats;
+ struct airoha_hw_stats *hw_stats = &dev->stats;
unsigned int start;
BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
@@ -2698,7 +2723,7 @@ airoha_ethtool_get_rmon_stats(struct net
do {
int i;
- start = u64_stats_fetch_begin(&port->stats.syncp);
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
stats->fragments = hw_stats->rx_fragment;
stats->jabbers = hw_stats->rx_jabber;
for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1;
@@ -2706,7 +2731,7 @@ airoha_ethtool_get_rmon_stats(struct net
stats->hist[i] = hw_stats->rx_len[i];
stats->hist_tx[i] = hw_stats->tx_len[i];
}
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static int airoha_ethtool_set_priv_flags(struct net_device *netdev, u32 flags)
@@ -3714,6 +3739,7 @@ static int airoha_alloc_gdm_device(struc
netdev->dev.of_node = of_node_get(np);
dev = netdev_priv(netdev);
+ u64_stats_init(&dev->stats.syncp);
dev->dev = netdev;
dev->port = port;
dev->eth = eth;
@@ -3762,9 +3788,8 @@ static int airoha_alloc_gdm_port(struct
if (!port)
return -ENOMEM;
- u64_stats_init(&port->stats.syncp);
- spin_lock_init(&port->stats.lock);
port->id = id;
+ spin_lock_init(&port->stats_lock);
eth->ports[p] = port;
err = airoha_metadata_dst_alloc(port);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -227,8 +227,6 @@ struct airoha_tx_irq_queue {
};
struct airoha_hw_stats {
- /* protect concurrent hw_stats accesses */
- spinlock_t lock;
struct u64_stats_sync syncp;
/* get_stats64 */
@@ -572,6 +570,8 @@ struct airoha_gdm_dev {
u32 flags;
int nbq;
+
+ struct airoha_hw_stats stats;
};
struct airoha_gdm_port {
@@ -579,7 +579,8 @@ struct airoha_gdm_port {
int id;
int users;
- struct airoha_hw_stats stats;
+ /* protect concurrent hw_stats accesses */
+ spinlock_t stats_lock;
struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
};