Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
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 --]

      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