From: Lorenzo Bianconi <lorenzo@kernel.org>
To: netdev-bot+sashiko@kernel.org
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, aleksander.lobakin@intel.com,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, netdev@vger.kernel.org,
madhur.agrawal@airoha.com, horms@kernel.org
Subject: Re: [PATCH net-next v7] net: airoha: add LRO offload support
Date: Sat, 12 Sep 2026 11:21:20 +0200 [thread overview]
Message-ID: <aqUZkDuNobNC-Zdo@lore-desk> (raw)
In-Reply-To: <178917159014.219967.5680210222972540082@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 9833 bytes --]
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider:
>
> Critical: 0 · High: 0 · Medium: 2 · Low: 0
>
> - [Medium] In airoha_qdma_init_rx_queue() the page_pool order, buffer
> size and the hardware scatter-gather setting for RX rings 24-31 are…
> - [Medium] The per-QDMA CDM LRO engine is armed from paths that do not
> require the netdev to be running, and there is no unconditional disarm…
>
> --
>
> net: airoha: add LRO offload support
>
> This adds LRO offload to airoha_eth using the eight hardware LRO queues
> mapped to RX rings 24-31, bumps the page_pool order for those rings and
> disables hardware scatter-gather on them, and sets GSO metadata plus
> CHECKSUM_PARTIAL on aggregated skbs. NETIF_F_LRO is exposed in
> hw_features and re-evaluated when the QDMA user count changes.
>
> A couple of questions below about the unconditional buffer sizing and
> about the lifetime of the CDM LRO enable bits.
>
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index 64619e9a704dd..21ac80bdd084a 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
>
> [ ... ]
>
> > @@ -800,12 +1027,10 @@ static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget)
> > static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
> > struct airoha_qdma *qdma, int ndesc)
> > {
> > - const struct page_pool_params pp_params = {
> > - .order = 0,
> > + struct page_pool_params pp_params = {
> > .pool_size = 256,
> > .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
> > .dma_dir = DMA_FROM_DEVICE,
> > - .max_len = PAGE_SIZE,
> > .nid = NUMA_NO_NODE,
> > .dev = qdma->eth->dev,
> > .napi = &q->napi,
> > @@ -813,9 +1038,10 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
> > struct airoha_eth *eth = qdma->eth;
> > int qid = q - &qdma->q_rx[0], thr;
> > dma_addr_t dma_addr;
> > + bool lro_q;
> >
> > - q->buf_size = PAGE_SIZE / 2;
> > q->qdma = qdma;
> > + lro_q = airoha_qdma_is_lro_queue(q);
> >
> > q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
> > GFP_KERNEL);
> > @@ -827,6 +1053,9 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
> > if (!q->desc)
> > return -ENOMEM;
> >
> > + pp_params.order = lro_q ? AIROHA_LRO_PAGE_ORDER : 0;
> > + pp_params.max_len = PAGE_SIZE << pp_params.order;
> > +
> > q->page_pool = page_pool_create(&pp_params);
> > if (IS_ERR(q->page_pool)) {
> > int err = PTR_ERR(q->page_pool);
> > @@ -835,6 +1064,7 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
> > return err;
> > }
> >
> > + q->buf_size = lro_q ? pp_params.max_len : pp_params.max_len / 2;
> > q->ndesc = ndesc;
> > netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
> >
> > @@ -848,7 +1078,12 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
> > FIELD_PREP(RX_RING_THR_MASK, thr));
> > airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
> > FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head));
> > - airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid), RX_RING_SG_EN_MASK);
> > + if (lro_q)
> > + airoha_qdma_clear(qdma, REG_RX_SCATTER_CFG(qid),
> > + RX_RING_SG_EN_MASK);
> > + else
> > + airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid),
> > + RX_RING_SG_EN_MASK);
>
> [Severity: Medium]
> Is the order-2 sizing meant to be paid even when LRO is never enabled?
>
> airoha_qdma_is_lro_queue() is a pure function of the ring index
> (AIROHA_RXQ_LRO_EN_MASK & BIT(qid)), so it does not depend on
> NETIF_F_LRO at all:
>
> return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
>
> airoha_qdma_init_rx() initializes every ring in RX_DONE_INT_MASK, which
> expands to 0xffffffff, so rings 24-31 of both eth->qdma[0] and
> eth->qdma[1] are always live. For those rings this hunk gives
> pp_params.order = get_order(SZ_16K) (2 with 4 KiB pages),
> pp_params.max_len = 16384 and q->buf_size = 16384.
>
> Since q->buf_size equals max_len, page_pool_alloc_frag_netmem() no
> longer sub-allocates (it only splits when the request is smaller than
> max_size), so airoha_qdma_fill_rx_queue() consumes one whole order-2
> compound page per descriptor:
>
> page = page_pool_dev_alloc_frag(q->page_pool, &offset,
> q->buf_size);
>
> That moves the per-ring in-flight RX memory from roughly 31 x 2 KiB to
> 31 x 16 KiB, i.e. about +4 MiB per QDMA block.
>
> Meanwhile NETIF_F_LRO is only added to netdev->hw_features:
>
> netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO;
> netdev->features |= AIROHA_HW_FEATURES;
>
> so LRO is off at probe, and airoha_dev_fix_features() strips
> NETIF_F_LRO whenever qdma->users > 1. On a board with two GDM devices
> sharing one QDMA the feature can never be turned on, yet those rings
> still hold 16 KiB buffers with RX_RING_SG_EN_MASK cleared, and the
> pools are never re-created when LRO is toggled.
>
> With scatter-gather cleared on those rings there is also no fragment
> fallback, and refill runs from NAPI with GFP_ATOMIC | __GFP_NOWARN.
> Could order-2 atomic allocation failures under fragmentation simply
> starve rings 24-31 here?
>
> Would it be possible to key the order, buf_size and SG setting on
> whether LRO is actually enabled, rather than on the ring index?
This capability will be added in a subsequent patch.
>
> [ ... ]
>
> > @@ -1954,6 +2235,10 @@ static int airoha_dev_stop(struct net_device *netdev)
> > airoha_set_gdm_port_fwd_cfg(dev->eth,
> > REG_GDM_FWD_CFG(port->id),
> > FE_PSE_PORT_DROP);
> > +
> > + if (netdev->features & NETIF_F_LRO)
> > + airoha_dev_lro_disable(dev);
> > +
> > return 0;
> > }
>
> [ ... ]
>
> > @@ -2136,8 +2424,14 @@ static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
> > airoha_qdma_rr(qdma, REG_CNTR_VAL((i << 1) + 1));
> > }
> >
> > - if (cur_qdma)
> > + if (cur_qdma) {
> > + cur_qdma->users--;
> > netif_tx_wake_all_queues(netdev);
> > + }
> > +
> > + airoha_update_netdev_features(dev);
> > + if (netdev->features & NETIF_F_LRO)
> > + airoha_dev_lro_enable(dev);
> > }
>
> [ ... ]
>
> > +static int airoha_dev_set_features(struct net_device *netdev,
> > + netdev_features_t features)
> > +{
> > + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> > +
> > + if (features & NETIF_F_LRO)
> > + airoha_dev_lro_enable(dev);
> > + else
> > + airoha_dev_lro_disable(dev);
> > +
> > + return 0;
> > +}
>
> [Severity: Medium]
> Should the LRO arming in airoha_dev_set_features() and at the tail of
> airoha_dev_set_qdma() be gated on netif_running()?
>
> __netdev_update_features() dispatches ndo_set_features regardless of
> the running state, so "ethtool -K ethX lro on" on an administratively
> down interface reaches airoha_dev_lro_enable(), which programs
> REG_CDM_LRO_LIMIT, REG_CDM_LRO_AGE_TIME, the REG_CDM_LRO_RXQ mapping
> and REG_CDM_LRO_EN for that QDMA block.
>
> The only automatic disarm is in airoha_dev_stop():
>
> if (netdev->features & NETIF_F_LRO)
> airoha_dev_lro_disable(dev);
>
> which is only reached for a device that was successfully opened, so
> nothing clears REG_CDM_LRO_EN for hardware armed while the device was
> down.
>
> The same applies to airoha_dev_set_qdma(): airoha_update_netdev_features()
> runs first, so a device whose wanted NETIF_F_LRO had been masked by
> qdma->users > 1 regains the bit and then gets the destination QDMA
> armed even while it is down.
I do not think this is harmful since you will not be able to set the device up if
it does not follow LRO/QDMA restrictions, single QDMA user for LRO. In order to
put the device up, if the QDMA is shared, the user will need to disable LRO.
>
> Neither airoha_qdma_cleanup() nor airoha_hw_cleanup() clears the LRO
> enable bits or the queue mapping before RX DMA is stopped and the rings
> and page pools are destroyed, so the CDM aggregation engine is left
> enabled with a stale queue mapping after teardown. Would an
> unconditional airoha_fe_lro_disable() on the QDMA cleanup path be
> appropriate?
airoha_qdma_cleanup() runs removing the airoha_eth module. When the driver is
loaded again, airoha_hw_init() will reset the QDMA blocks, disabling the LRO
configuration.
Regards,
Lorenzo
>
> This is also inconsistent with the surrounding convention in the same
> file: airoha_enable_qos_for_gdm34() and airoha_disable_qos_for_gdm34()
> both gate their REG_GDM_FWD_CFG writes on netif_running(netdev).
>
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> > index 527e3833e36b1..286f73e2e1e09 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.h
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> > @@ -46,6 +46,18 @@
> > (_n) == 15 ? 128 : \
> > (_n) == 0 ? 1024 : 32)
> >
> > +#define AIROHA_LRO_PAGE_ORDER get_order(SZ_16K)
> > +#define AIROHA_MAX_NUM_LRO_QUEUES 8
> > +#define AIROHA_RXQ_LRO_EN_MASK GENMASK(31, 24)
>
> [ ... ]
>
> > +static inline bool airoha_qdma_is_lro_queue(struct airoha_queue *q)
> > +{
> > + struct airoha_qdma *qdma = q->qdma;
> > + int qid = q - &qdma->q_rx[0];
> > +
> > + /* EN7581 SoC supports at most 8 LRO rx queues */
> > + BUILD_BUG_ON(hweight32(AIROHA_RXQ_LRO_EN_MASK) >
> > + AIROHA_MAX_NUM_LRO_QUEUES);
> > +
> > + return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
> > +}
>
> [ ... ]
>
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909-airoha-eth-lro-v7-1-e6317be79067%40kernel.org
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
prev parent reply other threads:[~2026-09-12 9:21 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 19:06 [PATCH net-next v7] net: airoha: add LRO offload support Lorenzo Bianconi
2026-09-12 0:06 ` netdev-bot+sashiko
2026-09-12 9:21 ` Lorenzo Bianconi [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aqUZkDuNobNC-Zdo@lore-desk \
--to=lorenzo@kernel.org \
--cc=aleksander.lobakin@intel.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=madhur.agrawal@airoha.com \
--cc=netdev-bot+sashiko@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox