* [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand
@ 2026-06-10 13:33 Lorenzo Bianconi
2026-06-10 13:33 ` [PATCH net-next v4 1/2] net: airoha: refactor QDMA start/stop into reusable helpers Lorenzo Bianconi
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Lorenzo Bianconi @ 2026-06-10 13:33 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal
Add the capability to configure GDM3/GDM4 as WAN/LAN on demand when QoS
offload is created or destroyed.
Make dev->qdma an RCU pointer so the TX path can safely dereference it
without holding RTNL.
Introduce airoha_qdma_start() and airoha_qdma_stop() helpers.
---
Changes in v4:
- Move back QDMA TX/RX DMA enable to airoha_dev_open()/airoha_dev_stop().
- Configure GDM3/4 as WAN if GDM2 is not available in ndo_init()
callback.
- Protect qdma pointer in airoha_gdm_dev struct using RCU.
- Rely on rtnl_dereference() to access qdma pointer in the control path.
- Add airoha_qdma_start() and airoha_qdma_stop() utility routines in
patch 1/2
- Link to v3: https://lore.kernel.org/r/20260608-airoha-ethtool-priv_flags-v3-1-3e8e3dc3f715@kernel.org
Changes in v3:
- Do not introduce ethtool private flags support to configure LAN/WAN
for GDM3/4 and rely on tc qdisc offload for it instead.
- Set GDM3/4 ports as LAN by default.
- Move QDMA TX/RX DMA enable from airoha_dev_open() to airoha_probe()
and the corresponding disable from airoha_dev_stop() to airoha_qdma_cleanup().
- Link to v2: https://lore.kernel.org/r/20260607-airoha-ethtool-priv_flags-v2-1-742c7aa1e182@kernel.org
Changes in v2:
- Rework airoha_dev_set_wan_flag routine
- Enable GDM_STRIP_CRC_MASK in airoha_disable_gdm2_loopback()
- Do not always reset REG_SRC_PORT_FC_MAP6 in
airoha_disable_gdm2_loopback() but use the same condition used in
airoha_enable_gdm2_loopback().
- Link to v1: https://lore.kernel.org/r/20260606-airoha-ethtool-priv_flags-v1-1-401b2c9fe9f1@kernel.org
---
Lorenzo Bianconi (2):
net: airoha: refactor QDMA start/stop into reusable helpers
net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload
drivers/net/ethernet/airoha/airoha_eth.c | 273 +++++++++++++++++++++++++-----
drivers/net/ethernet/airoha/airoha_eth.h | 24 ++-
drivers/net/ethernet/airoha/airoha_ppe.c | 18 +-
drivers/net/ethernet/airoha/airoha_regs.h | 1 +
4 files changed, 267 insertions(+), 49 deletions(-)
---
base-commit: 5855479abc796c3b5d7b2f2ca147d68fc56cae1f
change-id: 20260606-airoha-ethtool-priv_flags-b6aa70caa780
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH net-next v4 1/2] net: airoha: refactor QDMA start/stop into reusable helpers 2026-06-10 13:33 [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand Lorenzo Bianconi @ 2026-06-10 13:33 ` Lorenzo Bianconi 2026-06-11 15:21 ` Lorenzo Bianconi 2026-06-10 13:33 ` [PATCH net-next v4 2/2] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload Lorenzo Bianconi 2026-06-10 14:08 ` [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand Alexander Lobakin 2 siblings, 1 reply; 6+ messages in thread From: Lorenzo Bianconi @ 2026-06-10 13:33 UTC (permalink / raw) To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Lorenzo Bianconi Cc: linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal Factor out airoha_qdma_start() and airoha_qdma_stop() from airoha_dev_open() and airoha_dev_stop(). These helpers will be reused by the QDMA hot-migration logic introduced in the next patch to dynamically switch GDM3/GDM4 ports between LAN and WAN QDMA blocks. Add a DMA engine busy poll in airoha_qdma_stop() to wait for in-flight DMA transfers to complete before cleaning up TX queues. Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> --- drivers/net/ethernet/airoha/airoha_eth.c | 53 ++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 5a8e84fa9918..aeac66df5f3b 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1771,6 +1771,40 @@ static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) spin_unlock(&port->stats.lock); } +static void airoha_qdma_start(struct airoha_qdma *qdma) +{ + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); + atomic_inc(&qdma->users); +} + +static void airoha_qdma_stop(struct airoha_qdma *qdma) +{ + u32 status; + + if (!atomic_dec_and_test(&qdma->users)) + return; + + airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); + + if (read_poll_timeout(airoha_qdma_rr, status, + !(status & (GLOBAL_CFG_TX_DMA_BUSY_MASK | + GLOBAL_CFG_RX_DMA_BUSY_MASK)), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, true, + qdma, REG_QDMA_GLOBAL_CFG)) + dev_warn(qdma->eth->dev, "QDMA DMA engine busy timeout\n"); + + for (int i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { + if (!qdma->q_tx[i].ndesc) + continue; + + airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); + } +} + static int airoha_dev_open(struct net_device *netdev) { int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; @@ -1806,10 +1840,7 @@ static int airoha_dev_open(struct net_device *netdev) } port->users++; - airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, - GLOBAL_CFG_TX_DMA_EN_MASK | - GLOBAL_CFG_RX_DMA_EN_MASK); - atomic_inc(&qdma->users); + airoha_qdma_start(qdma); if (!airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(qdma->eth, 1)) @@ -1862,19 +1893,7 @@ static int airoha_dev_stop(struct net_device *netdev) airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), FE_PSE_PORT_DROP); - - if (atomic_dec_and_test(&qdma->users)) { - airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, - GLOBAL_CFG_TX_DMA_EN_MASK | - GLOBAL_CFG_RX_DMA_EN_MASK); - - for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { - if (!qdma->q_tx[i].ndesc) - continue; - - airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); - } - } + airoha_qdma_stop(qdma); return 0; } -- 2.54.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v4 1/2] net: airoha: refactor QDMA start/stop into reusable helpers 2026-06-10 13:33 ` [PATCH net-next v4 1/2] net: airoha: refactor QDMA start/stop into reusable helpers Lorenzo Bianconi @ 2026-06-11 15:21 ` Lorenzo Bianconi 0 siblings, 0 replies; 6+ messages in thread From: Lorenzo Bianconi @ 2026-06-11 15:21 UTC (permalink / raw) To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni Cc: linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal [-- Attachment #1: Type: text/plain, Size: 3392 bytes --] > Factor out airoha_qdma_start() and airoha_qdma_stop() from > airoha_dev_open() and airoha_dev_stop(). These helpers will be reused > by the QDMA hot-migration logic introduced in the next patch to > dynamically switch GDM3/GDM4 ports between LAN and WAN QDMA blocks. > Add a DMA engine busy poll in airoha_qdma_stop() to wait for in-flight > DMA transfers to complete before cleaning up TX queues. > > Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> > --- > drivers/net/ethernet/airoha/airoha_eth.c | 53 ++++++++++++++++++++++---------- > 1 file changed, 36 insertions(+), 17 deletions(-) Both of the issues reportd by sashiko here are not introduced by this patch https://sashiko.dev/#/patchset/20260610-airoha-ethtool-priv_flags-v4-0-60e89cf28fea%40kernel.org Regards, Lorenzo > > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c > index 5a8e84fa9918..aeac66df5f3b 100644 > --- a/drivers/net/ethernet/airoha/airoha_eth.c > +++ b/drivers/net/ethernet/airoha/airoha_eth.c > @@ -1771,6 +1771,40 @@ static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) > spin_unlock(&port->stats.lock); > } > > +static void airoha_qdma_start(struct airoha_qdma *qdma) > +{ > + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, > + GLOBAL_CFG_TX_DMA_EN_MASK | > + GLOBAL_CFG_RX_DMA_EN_MASK); > + atomic_inc(&qdma->users); > +} > + > +static void airoha_qdma_stop(struct airoha_qdma *qdma) > +{ > + u32 status; > + > + if (!atomic_dec_and_test(&qdma->users)) > + return; > + > + airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, > + GLOBAL_CFG_TX_DMA_EN_MASK | > + GLOBAL_CFG_RX_DMA_EN_MASK); > + > + if (read_poll_timeout(airoha_qdma_rr, status, > + !(status & (GLOBAL_CFG_TX_DMA_BUSY_MASK | > + GLOBAL_CFG_RX_DMA_BUSY_MASK)), > + USEC_PER_MSEC, 50 * USEC_PER_MSEC, true, > + qdma, REG_QDMA_GLOBAL_CFG)) > + dev_warn(qdma->eth->dev, "QDMA DMA engine busy timeout\n"); > + > + for (int i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { > + if (!qdma->q_tx[i].ndesc) > + continue; > + > + airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); > + } > +} > + > static int airoha_dev_open(struct net_device *netdev) > { > int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; > @@ -1806,10 +1840,7 @@ static int airoha_dev_open(struct net_device *netdev) > } > port->users++; > > - airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, > - GLOBAL_CFG_TX_DMA_EN_MASK | > - GLOBAL_CFG_RX_DMA_EN_MASK); > - atomic_inc(&qdma->users); > + airoha_qdma_start(qdma); > > if (!airoha_is_lan_gdm_dev(dev) && > airoha_ppe_is_enabled(qdma->eth, 1)) > @@ -1862,19 +1893,7 @@ static int airoha_dev_stop(struct net_device *netdev) > airoha_set_gdm_port_fwd_cfg(qdma->eth, > REG_GDM_FWD_CFG(port->id), > FE_PSE_PORT_DROP); > - > - if (atomic_dec_and_test(&qdma->users)) { > - airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, > - GLOBAL_CFG_TX_DMA_EN_MASK | > - GLOBAL_CFG_RX_DMA_EN_MASK); > - > - for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { > - if (!qdma->q_tx[i].ndesc) > - continue; > - > - airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); > - } > - } > + airoha_qdma_stop(qdma); > > return 0; > } > > -- > 2.54.0 > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next v4 2/2] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload 2026-06-10 13:33 [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand Lorenzo Bianconi 2026-06-10 13:33 ` [PATCH net-next v4 1/2] net: airoha: refactor QDMA start/stop into reusable helpers Lorenzo Bianconi @ 2026-06-10 13:33 ` Lorenzo Bianconi 2026-06-11 15:04 ` Lorenzo Bianconi 2026-06-10 14:08 ` [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand Alexander Lobakin 2 siblings, 1 reply; 6+ messages in thread From: Lorenzo Bianconi @ 2026-06-10 13:33 UTC (permalink / raw) To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Lorenzo Bianconi Cc: linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal GDM3 and GDM4 ports require GDM2 loopback to be enabled for hardware QoS offload to function. Without it, HTB and ETS offload on these ports do not work. Previously, GDM3/GDM4 ports were automatically configured as WAN with GDM2 loopback enabled during ndo_init(). Add the capability to configure GDM3/GDM4 as WAN/LAN on demand when QoS offload is created or destroyed. Hook airoha_enable_qos_for_gdm34() into TC_HTB_CREATE so that requesting HTB offload on a GDM3/GDM4 LAN port switches it to WAN mode and enables GDM2 loopback, with proper rollback on failure. Hook the counterpart airoha_disable_qos_for_gdm34() into TC_HTB_DESTROY to restore LAN mode when the offloaded qdisc is torn down. Since airoha_dev_set_qdma() can now be called on a running device to migrate between QDMA blocks, make dev->qdma an RCU pointer so the TX path can safely dereference it without holding RTNL. Hold flow_offload_mutex in airoha_dev_set_qdma() around the QDMA pointer update and __airoha_ppe_set_cpu_port() call, serializing against concurrent airoha_ppe_hw_init() in the TC_SETUP_CLSFLOWER offload path. Introduce airoha_qdma_deref() helper that wraps rcu_dereference_protected() with a lockdep condition accepting either rtnl_lock or flow_offload_mutex, and use it across all control-path dereferences of the RCU-protected dev->qdma pointer. Add airoha_disable_gdm2_loopback() to disable GDM2 hw loopback. Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> --- drivers/net/ethernet/airoha/airoha_eth.c | 220 ++++++++++++++++++++++++++---- drivers/net/ethernet/airoha/airoha_eth.h | 24 +++- drivers/net/ethernet/airoha/airoha_ppe.c | 18 ++- drivers/net/ethernet/airoha/airoha_regs.h | 1 + 4 files changed, 231 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index aeac66df5f3b..10232470a333 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -921,9 +921,11 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q) if (!dev) continue; - if (dev->qdma != qdma) - continue; - + /* Do not filter by dev->qdma here: the device + * may have migrated to a different QDMA block + * since the packet was submitted, so completions + * can arrive on the old block. + */ netdev = netdev_from_priv(dev); for (j = 0; j < netdev->num_tx_queues; j++) { if (airoha_qdma_get_txq(qdma, j) != qid) @@ -1811,13 +1813,14 @@ static int airoha_dev_open(struct net_device *netdev) struct airoha_gdm_dev *dev = netdev_priv(netdev); struct airoha_gdm_port *port = dev->port; u32 cur_len, pse_port = FE_PSE_PORT_PPE1; - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma; netif_tx_start_all_queues(netdev); err = airoha_set_vip_for_gdm_port(dev, true); if (err) return err; + qdma = airoha_qdma_deref(dev); if (netdev_uses_dsa(netdev)) airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), GDM_STAG_EN_MASK); @@ -1879,7 +1882,7 @@ static int airoha_dev_stop(struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); struct airoha_gdm_port *port = dev->port; - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma; int i; netif_tx_disable(netdev); @@ -1887,6 +1890,7 @@ static int airoha_dev_stop(struct net_device *netdev) for (i = 0; i < netdev->num_tx_queues; i++) netdev_tx_reset_subqueue(netdev, i); + qdma = airoha_qdma_deref(dev); if (--port->users) airoha_set_port_mtu(dev->eth, port); else @@ -1979,6 +1983,52 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev) return 0; } +static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev) +{ + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; + int i, src_port; + u32 pse_port; + + src_port = eth->soc->ops.get_sport(dev->port, dev->nbq); + if (src_port < 0) + return src_port; + + airoha_fe_clear(eth, + REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)), + SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK)); + + airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + GDM_STRIP_CRC_MASK); + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + FE_PSE_PORT_DROP); + airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX), + LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK); + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2 + : FE_PSE_PORT_PPE1; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + pse_port); + + airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK, + FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX)); + + for (i = 0; i < eth->soc->num_ppe; i++) + airoha_ppe_clear_cpu_port(dev, i, AIROHA_GDM2_IDX); + + /* Enable VIP and IFC for GDM2 */ + airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); + airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); + + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { + u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq); + + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask, + FC_MAP6_DEF_VALUE & mask); + } + + return 0; +} + static struct airoha_gdm_dev * airoha_get_wan_gdm_dev(struct airoha_eth *eth) { @@ -2005,15 +2055,36 @@ airoha_get_wan_gdm_dev(struct airoha_eth *eth) static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev) { struct net_device *netdev = netdev_from_priv(dev); + struct airoha_qdma *cur_qdma, *qdma; struct airoha_eth *eth = dev->eth; int ppe_id; /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ - dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; - netdev->irq = dev->qdma->irq_banks[0].irq; + qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; + cur_qdma = airoha_qdma_deref(dev); + if (netif_running(netdev)) + airoha_qdma_start(qdma); + + /* Serialize QDMA pointer and PPE CPU port configuration against + * concurrent airoha_ppe_hw_init() in airoha_ppe_setup_tc_block_cb(). + */ + mutex_lock(&flow_offload_mutex); + + rcu_assign_pointer(dev->qdma, qdma); + netdev->irq = qdma->irq_banks[0].irq; ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); - airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev)); + __airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev)); + + mutex_unlock(&flow_offload_mutex); + + if (!cur_qdma) + return; + + synchronize_rcu(); + + if (netif_running(netdev)) + airoha_qdma_stop(cur_qdma); } static int airoha_dev_init(struct net_device *netdev) @@ -2177,9 +2248,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); - struct airoha_qdma *qdma = dev->qdma; u32 nr_frags, tag, msg0, msg1, len; struct airoha_queue_entry *e; + struct airoha_qdma *qdma; struct netdev_queue *txq; struct airoha_queue *q; LIST_HEAD(tx_list); @@ -2188,6 +2259,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, u16 index; u8 fport; + rcu_read_lock(); + qdma = rcu_dereference(dev->qdma); qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb)); tag = airoha_get_dsa_tag(skb, netdev); @@ -2234,6 +2307,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, netif_tx_stop_queue(txq); q->txq_stopped = true; spin_unlock_bh(&q->lock); + rcu_read_unlock(); + return NETDEV_TX_BUSY; } @@ -2296,6 +2371,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); spin_unlock_bh(&q->lock); + rcu_read_unlock(); return NETDEV_TX_OK; @@ -2311,6 +2387,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, error: dev_kfree_skb_any(skb); netdev->stats.tx_dropped++; + rcu_read_unlock(); return NETDEV_TX_OK; } @@ -2392,17 +2469,19 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev, const u16 *weights, u8 n_weights) { struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_qdma *qdma; int i; + qdma = airoha_qdma_deref(dev); for (i = 0; i < AIROHA_NUM_TX_RING; i++) - airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel), + airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel), TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); for (i = 0; i < n_weights; i++) { u32 status; int err; - airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG, + airoha_qdma_wr(qdma, REG_TXWRR_WEIGHT_CFG, TWRR_RW_CMD_MASK | FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) | FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) | @@ -2410,12 +2489,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev, err = read_poll_timeout(airoha_qdma_rr, status, status & TWRR_RW_CMD_DONE, USEC_PER_MSEC, 10 * USEC_PER_MSEC, - true, dev->qdma, REG_TXWRR_WEIGHT_CFG); + true, qdma, REG_TXWRR_WEIGHT_CFG); if (err) return err; } - airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3), + airoha_qdma_rmw(qdma, REG_CHAN_QOS_MODE(channel >> 3), CHAN_QOS_MODE_MASK(channel), __field_prep(CHAN_QOS_MODE_MASK(channel), mode)); @@ -2479,13 +2558,15 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel, struct tc_ets_qopt_offload *opt) { struct airoha_gdm_dev *dev = netdev_priv(netdev); - struct airoha_qdma *qdma = dev->qdma; + u64 cpu_tx_packets, fwd_tx_packets, tx_packets; + struct airoha_qdma *qdma; - u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1)); - u64 fwd_tx_packets = airoha_qdma_rr(qdma, - REG_CNTR_VAL((channel << 1) + 1)); - u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) + - (fwd_tx_packets - dev->fwd_tx_packets); + qdma = airoha_qdma_deref(dev); + cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1)); + fwd_tx_packets = airoha_qdma_rr(qdma, + REG_CNTR_VAL((channel << 1) + 1)); + tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) + + (fwd_tx_packets - dev->fwd_tx_packets); _bstats_update(opt->stats.bstats, 0, tx_packets); dev->cpu_tx_packets = cpu_tx_packets; @@ -2745,16 +2826,18 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev, u32 bucket_size) { struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_qdma *qdma; int i, err; + qdma = airoha_qdma_deref(dev); for (i = 0; i <= TRTCM_PEAK_MODE; i++) { - err = airoha_qdma_set_trtcm_config(dev->qdma, channel, + err = airoha_qdma_set_trtcm_config(qdma, channel, REG_EGRESS_TRTCM_CFG, i, !!rate, TRTCM_METER_MODE); if (err) return err; - err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel, + err = airoha_qdma_set_trtcm_token_bucket(qdma, channel, REG_EGRESS_TRTCM_CFG, i, rate, bucket_size); if (err) @@ -2790,11 +2873,12 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev, u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; int err, num_tx_queues = netdev->real_num_tx_queues; struct airoha_gdm_dev *dev = netdev_priv(netdev); - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma; /* Here we need to check the requested QDMA channel is not already * in use by another net_device running on the same QDMA block. */ + qdma = airoha_qdma_deref(dev); if (test_and_set_bit(channel, qdma->qos_channel_map)) { NL_SET_ERR_MSG_MOD(opt->extack, "qdma qos channel already in use"); @@ -2828,7 +2912,7 @@ static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev, u32 rate, u32 bucket_size, enum trtcm_unit_type unit_type) { - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma = airoha_qdma_deref(dev); int i; for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { @@ -3002,11 +3086,12 @@ static int airoha_dev_setup_tc_block(struct net_device *dev, static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue) { struct airoha_gdm_dev *dev = netdev_priv(netdev); - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma; netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1); airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0); + qdma = airoha_qdma_deref(dev); clear_bit(queue, qdma->qos_channel_map); clear_bit(queue, dev->qos_sq_bmap); } @@ -3027,6 +3112,89 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev, return 0; } +static int airoha_enable_qos_for_gdm34(struct net_device *netdev, + struct netlink_ext_ack *extack) +{ + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; + int err; + + if (port->id != AIROHA_GDM3_IDX && + port->id != AIROHA_GDM4_IDX) { + /* HW QoS is always supported by GDM1 and GDM2 */ + return 0; + } + + if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */ + return 0; + + /* Verify the WAN device is not already configured */ + if (airoha_get_wan_gdm_dev(eth)) { + NL_SET_ERR_MSG_MOD(extack, + "WAN device already configured"); + return -EBUSY; + } + + dev->flags |= AIROHA_PRIV_F_WAN; + airoha_dev_set_qdma(dev); + err = airoha_enable_gdm2_loopback(dev); + if (err) + goto error_disable_wan; + + err = airoha_set_macaddr(dev, netdev->dev_addr); + if (err) + goto error_disable_loopback; + + if (netif_running(netdev)) { + u32 pse_port; + + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2 + : FE_PSE_PORT_PPE1; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), + pse_port); + } + + return 0; + +error_disable_loopback: + /* Restore previous LAN configuration */ + airoha_disable_gdm2_loopback(dev); +error_disable_wan: + dev->flags &= ~AIROHA_PRIV_F_WAN; + airoha_dev_set_qdma(dev); + + return err; +} + +static void airoha_disable_qos_for_gdm34(struct net_device *netdev) +{ + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; + int err; + + if (port->id != AIROHA_GDM3_IDX && + port->id != AIROHA_GDM4_IDX) { + return; + } + + if (airoha_is_lan_gdm_dev(dev)) /* Already disabled */ + return; + + err = airoha_disable_gdm2_loopback(dev); + if (err) + netdev_warn(netdev, + "failed disabling GDM2 loopback: %d\n", err); + + dev->flags &= ~AIROHA_PRIV_F_WAN; + airoha_dev_set_qdma(dev); + airoha_set_macaddr(dev, netdev->dev_addr); + if (netif_running(netdev)) + airoha_set_gdm_port_fwd_cfg(dev->eth, + REG_GDM_FWD_CFG(port->id), + FE_PSE_PORT_PPE1); +} + static int airoha_tc_htb_destroy(struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); @@ -3035,6 +3203,8 @@ static int airoha_tc_htb_destroy(struct net_device *netdev) for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) airoha_tc_remove_htb_queue(netdev, q); + airoha_disable_qos_for_gdm34(netdev); + return 0; } @@ -3059,7 +3229,7 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev, { switch (opt->command) { case TC_HTB_CREATE: - break; + return airoha_enable_qos_for_gdm34(dev, opt->extack); case TC_HTB_DESTROY: return airoha_tc_htb_destroy(dev); case TC_HTB_NODE_MODIFY: diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 8f42973f9cf5..8795af0010b6 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -543,8 +543,8 @@ enum airoha_priv_flags { }; struct airoha_gdm_dev { + struct airoha_qdma __rcu *qdma; struct airoha_gdm_port *port; - struct airoha_qdma *qdma; struct airoha_eth *eth; DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); @@ -676,7 +676,27 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev); bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, struct airoha_gdm_dev *dev); -void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); +extern struct mutex flow_offload_mutex; + +static inline struct airoha_qdma * +airoha_qdma_deref(struct airoha_gdm_dev *dev) +{ + return rcu_dereference_protected(dev->qdma, + lockdep_rtnl_is_held() || + lockdep_is_held(&flow_offload_mutex)); +} + +void __airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); +void airoha_ppe_clear_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); + +static inline void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, + u8 ppe_id, u8 fport) +{ + mutex_lock(&flow_offload_mutex); + __airoha_ppe_set_cpu_port(dev, ppe_id, fport); + mutex_unlock(&flow_offload_mutex); +} + bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, u16 hash, bool rx_wlan); diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 91bcc55a6ac6..0ee0dd385645 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -15,7 +15,7 @@ #include "airoha_regs.h" #include "airoha_eth.h" -static DEFINE_MUTEX(flow_offload_mutex); +DEFINE_MUTEX(flow_offload_mutex); static DEFINE_SPINLOCK(ppe_lock); static const struct rhashtable_params airoha_flow_table_params = { @@ -84,10 +84,10 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) AIROHA_FOE_IB1_BIND_TIMESTAMP); } -void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) +void __airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) { - struct airoha_qdma *qdma = dev->qdma; - struct airoha_eth *eth = qdma->eth; + struct airoha_qdma *qdma = airoha_qdma_deref(dev); + struct airoha_eth *eth = dev->eth; u8 qdma_id = qdma - ð->qdma[0]; u32 fe_cpu_port; @@ -97,6 +97,14 @@ void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) __field_prep(DFT_CPORT_MASK(fport), fe_cpu_port)); } +void airoha_ppe_clear_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) +{ + mutex_lock(&flow_offload_mutex); + airoha_fe_clear(dev->eth, REG_PPE_DFT_CPORT(ppe_id, fport), + DFT_CPORT_MASK(fport)); + mutex_unlock(&flow_offload_mutex); +} + static void airoha_ppe_hw_init(struct airoha_ppe *ppe) { u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries; @@ -195,7 +203,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); fport = airoha_get_fe_port(dev); - airoha_ppe_set_cpu_port(dev, ppe_id, fport); + __airoha_ppe_set_cpu_port(dev, ppe_id, fport); } } } diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h index 436f3c8779c1..4e17dfbcf2b8 100644 --- a/drivers/net/ethernet/airoha/airoha_regs.h +++ b/drivers/net/ethernet/airoha/airoha_regs.h @@ -376,6 +376,7 @@ #define REG_SRC_PORT_FC_MAP6 0x2298 #define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3)) +#define FC_MAP6_DEF_VALUE 0x1b1a1918 #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 -- 2.54.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v4 2/2] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload 2026-06-10 13:33 ` [PATCH net-next v4 2/2] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload Lorenzo Bianconi @ 2026-06-11 15:04 ` Lorenzo Bianconi 0 siblings, 0 replies; 6+ messages in thread From: Lorenzo Bianconi @ 2026-06-11 15:04 UTC (permalink / raw) To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni Cc: linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal [-- Attachment #1: Type: text/plain, Size: 13669 bytes --] > GDM3 and GDM4 ports require GDM2 loopback to be enabled for hardware > QoS offload to function. Without it, HTB and ETS offload on these ports > do not work. > Previously, GDM3/GDM4 ports were automatically configured as WAN with > GDM2 loopback enabled during ndo_init(). Add the capability to configure > GDM3/GDM4 as WAN/LAN on demand when QoS offload is created or destroyed. > Hook airoha_enable_qos_for_gdm34() into TC_HTB_CREATE so that requesting > HTB offload on a GDM3/GDM4 LAN port switches it to WAN mode and enables > GDM2 loopback, with proper rollback on failure. Hook the counterpart > airoha_disable_qos_for_gdm34() into TC_HTB_DESTROY to restore LAN mode > when the offloaded qdisc is torn down. > Since airoha_dev_set_qdma() can now be called on a running device to > migrate between QDMA blocks, make dev->qdma an RCU pointer so the TX > path can safely dereference it without holding RTNL. > Hold flow_offload_mutex in airoha_dev_set_qdma() around the QDMA pointer > update and __airoha_ppe_set_cpu_port() call, serializing against > concurrent airoha_ppe_hw_init() in the TC_SETUP_CLSFLOWER offload path. > Introduce airoha_qdma_deref() helper that wraps rcu_dereference_protected() > with a lockdep condition accepting either rtnl_lock or flow_offload_mutex, > and use it across all control-path dereferences of the RCU-protected > dev->qdma pointer. > Add airoha_disable_gdm2_loopback() to disable GDM2 hw loopback. > > Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Please find my comments about the following sashiko's report: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260610-airoha-ethtool-priv_flags-v4-0-60e89cf28fea%40kernel.org > --- > drivers/net/ethernet/airoha/airoha_eth.c | 220 ++++++++++++++++++++++++++---- > drivers/net/ethernet/airoha/airoha_eth.h | 24 +++- > drivers/net/ethernet/airoha/airoha_ppe.c | 18 ++- > drivers/net/ethernet/airoha/airoha_regs.h | 1 + > 4 files changed, 231 insertions(+), 32 deletions(-) > > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c > index aeac66df5f3b..10232470a333 100644 > --- a/drivers/net/ethernet/airoha/airoha_eth.c > +++ b/drivers/net/ethernet/airoha/airoha_eth.c [...] > +static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev) > +{ > + struct airoha_gdm_port *port = dev->port; > + struct airoha_eth *eth = dev->eth; > + int i, src_port; > + u32 pse_port; > + > + src_port = eth->soc->ops.get_sport(dev->port, dev->nbq); > + if (src_port < 0) > + return src_port; > + > + airoha_fe_clear(eth, > + REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)), > + SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK)); > + > + airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), > + GDM_STRIP_CRC_MASK); > + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), > + FE_PSE_PORT_DROP); > + airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX), > + LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK); > + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2 > + : FE_PSE_PORT_PPE1; > + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), > + pse_port); > + > + airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK, > + FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX)); > + > + for (i = 0; i < eth->soc->num_ppe; i++) > + airoha_ppe_clear_cpu_port(dev, i, AIROHA_GDM2_IDX); > + > + /* Enable VIP and IFC for GDM2 */ > + airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); > + airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); > + > + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { > + u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq); > + > + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask, > + FC_MAP6_DEF_VALUE & mask); > + } > + > + return 0; > +} - Does this disable counterpart fully undo what airoha_enable_gdm2_loopback() does? I think the current implementation is correct since: - 0xffffffff is already the default value for REG_GDM_TXCHN_EN() - 0xffff is already the default value for REG_GDM_RXCHN_EN() - REG_GDM_LEN_CFG() will be modified by another patch (not in the series). - WAN1_MASK/WAN1_EN_MASK default value is 0 and the driver does not configure WAN1. - if the device is configured properly get_sport() callback can't fail. > + > static struct airoha_gdm_dev * > airoha_get_wan_gdm_dev(struct airoha_eth *eth) > { > @@ -2005,15 +2055,36 @@ airoha_get_wan_gdm_dev(struct airoha_eth *eth) > static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev) > { > struct net_device *netdev = netdev_from_priv(dev); > + struct airoha_qdma *cur_qdma, *qdma; > struct airoha_eth *eth = dev->eth; > int ppe_id; [...] > } > @@ -3027,6 +3112,89 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev, > return 0; > } > > +static int airoha_enable_qos_for_gdm34(struct net_device *netdev, > + struct netlink_ext_ack *extack) > +{ > + struct airoha_gdm_dev *dev = netdev_priv(netdev); > + struct airoha_gdm_port *port = dev->port; > + struct airoha_eth *eth = dev->eth; > + int err; > + > + if (port->id != AIROHA_GDM3_IDX && > + port->id != AIROHA_GDM4_IDX) { > + /* HW QoS is always supported by GDM1 and GDM2 */ > + return 0; > + } > + > + if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */ > + return 0; > + - Is there a behavioural regression for GDM3/GDM4 devices that were auto-configured as WAN at ndo_init() time? I do not think there is any behavioural regression since in the current codebase it is not possible modify WAN/LAN configuration at runtime. Moreover, using tc APIs to set WAN/LAN configuration as suggested by Andrew, in order to configure a second device as WAN (or to set the current one as LAN), requires to move the current WAN device to LAN destroying the associated Qdisc. > + /* Verify the WAN device is not already configured */ > + if (airoha_get_wan_gdm_dev(eth)) { > + NL_SET_ERR_MSG_MOD(extack, > + "WAN device already configured"); > + return -EBUSY; > + } - The commit message says flow_offload_mutex was added to "serialize against concurrent airoha_ppe_hw_init() in the TC_SETUP_CLSFLOWER offload path". I think this is a bug and I will address the issue on the next revision. > + > + dev->flags |= AIROHA_PRIV_F_WAN; > + airoha_dev_set_qdma(dev); > + err = airoha_enable_gdm2_loopback(dev); > + if (err) > + goto error_disable_wan; > + > + err = airoha_set_macaddr(dev, netdev->dev_addr); > + if (err) > + goto error_disable_loopback; > + > + if (netif_running(netdev)) { > + u32 pse_port; > + > + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2 > + : FE_PSE_PORT_PPE1; > + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), > + pse_port); > + } > + > + return 0; > + > +error_disable_loopback: > + /* Restore previous LAN configuration */ > + airoha_disable_gdm2_loopback(dev); > +error_disable_wan: > + dev->flags &= ~AIROHA_PRIV_F_WAN; > + airoha_dev_set_qdma(dev); > + > + return err; > +} - Is the rollback symmetric on the airoha_enable_gdm2_loopback() failure path? airoha_enable_gdm2_loopback() performs many register writes before its only failure check (eth->soc->ops.get_sport()): - This has been already addressed in https://lore.kernel.org/netdev/20260608-airoha_enable_gdm2_loopback-minor-change-v1-1-1787a0f42b31@kernel.org/ > + > +static void airoha_disable_qos_for_gdm34(struct net_device *netdev) > +{ > + struct airoha_gdm_dev *dev = netdev_priv(netdev); > + struct airoha_gdm_port *port = dev->port; > + int err; > + > + if (port->id != AIROHA_GDM3_IDX && > + port->id != AIROHA_GDM4_IDX) { > + return; > + } > + > + if (airoha_is_lan_gdm_dev(dev)) /* Already disabled */ > + return; > + > + err = airoha_disable_gdm2_loopback(dev); > + if (err) > + netdev_warn(netdev, > + "failed disabling GDM2 loopback: %d\n", err); > + > + dev->flags &= ~AIROHA_PRIV_F_WAN; > + airoha_dev_set_qdma(dev); > + airoha_set_macaddr(dev, netdev->dev_addr); > + if (netif_running(netdev)) > + airoha_set_gdm_port_fwd_cfg(dev->eth, > + REG_GDM_FWD_CFG(port->id), > + FE_PSE_PORT_PPE1); > +} > + > static int airoha_tc_htb_destroy(struct net_device *netdev) > { > struct airoha_gdm_dev *dev = netdev_priv(netdev); > @@ -3035,6 +3203,8 @@ static int airoha_tc_htb_destroy(struct net_device *netdev) > for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) > airoha_tc_remove_htb_queue(netdev, q); > > + airoha_disable_qos_for_gdm34(netdev); > + > return 0; > } > > @@ -3059,7 +3229,7 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev, > { > switch (opt->command) { > case TC_HTB_CREATE: > - break; > + return airoha_enable_qos_for_gdm34(dev, opt->extack); - Should ETS installed directly on a GDM3/GDM4 LAN-configured device also enable the loopback, or should that case be rejected with an extack message so the behaviour matches the description in the commit message? - ETS can't be used as ROOT Qdisc. Regards, Lorenzo > case TC_HTB_DESTROY: > return airoha_tc_htb_destroy(dev); > case TC_HTB_NODE_MODIFY: > diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h > index 8f42973f9cf5..8795af0010b6 100644 > --- a/drivers/net/ethernet/airoha/airoha_eth.h > +++ b/drivers/net/ethernet/airoha/airoha_eth.h > @@ -543,8 +543,8 @@ enum airoha_priv_flags { > }; > > struct airoha_gdm_dev { > + struct airoha_qdma __rcu *qdma; > struct airoha_gdm_port *port; > - struct airoha_qdma *qdma; > struct airoha_eth *eth; > > DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); > @@ -676,7 +676,27 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev); > bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, > struct airoha_gdm_dev *dev); > > -void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); > +extern struct mutex flow_offload_mutex; > + > +static inline struct airoha_qdma * > +airoha_qdma_deref(struct airoha_gdm_dev *dev) > +{ > + return rcu_dereference_protected(dev->qdma, > + lockdep_rtnl_is_held() || > + lockdep_is_held(&flow_offload_mutex)); > +} > + > +void __airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); > +void airoha_ppe_clear_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); > + > +static inline void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, > + u8 ppe_id, u8 fport) > +{ > + mutex_lock(&flow_offload_mutex); > + __airoha_ppe_set_cpu_port(dev, ppe_id, fport); > + mutex_unlock(&flow_offload_mutex); > +} > + > bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); > void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, > u16 hash, bool rx_wlan); > diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c > index 91bcc55a6ac6..0ee0dd385645 100644 > --- a/drivers/net/ethernet/airoha/airoha_ppe.c > +++ b/drivers/net/ethernet/airoha/airoha_ppe.c > @@ -15,7 +15,7 @@ > #include "airoha_regs.h" > #include "airoha_eth.h" > > -static DEFINE_MUTEX(flow_offload_mutex); > +DEFINE_MUTEX(flow_offload_mutex); > static DEFINE_SPINLOCK(ppe_lock); > > static const struct rhashtable_params airoha_flow_table_params = { > @@ -84,10 +84,10 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) > AIROHA_FOE_IB1_BIND_TIMESTAMP); > } > > -void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) > +void __airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) > { > - struct airoha_qdma *qdma = dev->qdma; > - struct airoha_eth *eth = qdma->eth; > + struct airoha_qdma *qdma = airoha_qdma_deref(dev); > + struct airoha_eth *eth = dev->eth; > u8 qdma_id = qdma - ð->qdma[0]; > u32 fe_cpu_port; > > @@ -97,6 +97,14 @@ void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) > __field_prep(DFT_CPORT_MASK(fport), fe_cpu_port)); > } > > +void airoha_ppe_clear_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) > +{ > + mutex_lock(&flow_offload_mutex); > + airoha_fe_clear(dev->eth, REG_PPE_DFT_CPORT(ppe_id, fport), > + DFT_CPORT_MASK(fport)); > + mutex_unlock(&flow_offload_mutex); > +} > + > static void airoha_ppe_hw_init(struct airoha_ppe *ppe) > { > u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries; > @@ -195,7 +203,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) > ppe_id = !airoha_is_lan_gdm_dev(dev) && > airoha_ppe_is_enabled(eth, 1); > fport = airoha_get_fe_port(dev); > - airoha_ppe_set_cpu_port(dev, ppe_id, fport); > + __airoha_ppe_set_cpu_port(dev, ppe_id, fport); > } > } > } > diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h > index 436f3c8779c1..4e17dfbcf2b8 100644 > --- a/drivers/net/ethernet/airoha/airoha_regs.h > +++ b/drivers/net/ethernet/airoha/airoha_regs.h > @@ -376,6 +376,7 @@ > > #define REG_SRC_PORT_FC_MAP6 0x2298 > #define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3)) > +#define FC_MAP6_DEF_VALUE 0x1b1a1918 > > #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 > > > -- > 2.54.0 > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand 2026-06-10 13:33 [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand Lorenzo Bianconi 2026-06-10 13:33 ` [PATCH net-next v4 1/2] net: airoha: refactor QDMA start/stop into reusable helpers Lorenzo Bianconi 2026-06-10 13:33 ` [PATCH net-next v4 2/2] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload Lorenzo Bianconi @ 2026-06-10 14:08 ` Alexander Lobakin 2 siblings, 0 replies; 6+ messages in thread From: Alexander Lobakin @ 2026-06-10 14:08 UTC (permalink / raw) To: Lorenzo Bianconi Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal From: Lorenzo Bianconi <lorenzo@kernel.org> Date: Wed, 10 Jun 2026 15:33:35 +0200 > Add the capability to configure GDM3/GDM4 as WAN/LAN on demand when QoS > offload is created or destroyed. > Make dev->qdma an RCU pointer so the TX path can safely dereference it > without holding RTNL. > Introduce airoha_qdma_start() and airoha_qdma_stop() helpers. Series: Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com> Thanks, Olek ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-11 15:22 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-10 13:33 [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand Lorenzo Bianconi 2026-06-10 13:33 ` [PATCH net-next v4 1/2] net: airoha: refactor QDMA start/stop into reusable helpers Lorenzo Bianconi 2026-06-11 15:21 ` Lorenzo Bianconi 2026-06-10 13:33 ` [PATCH net-next v4 2/2] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload Lorenzo Bianconi 2026-06-11 15:04 ` Lorenzo Bianconi 2026-06-10 14:08 ` [PATCH net-next v4 0/2] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand Alexander Lobakin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox