From: Lorenzo Bianconi <lorenzo@kernel.org>
To: Vitaliy Sochnev <sochnev.v.74@gmail.com>
Cc: netdev@vger.kernel.org, Andrew Lunn <andrew+netdev@lunn.ch>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
linux-mediatek@lists.infradead.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, upstream@airoha.com
Subject: Re: [PATCH net 1/4] net: airoha: handle RX_NO_CPU_DSCP interrupt, not just RX_DONE
Date: Sun, 30 Aug 2026 15:26:06 +0200 [thread overview]
Message-ID: <apQvbk23AHg3w62J@lore-desk> (raw)
In-Reply-To: <20260830095717.37218-2-sochnev.v.74@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 7354 bytes --]
> The QDMA hardware raises a dedicated interrupt (NO_CPU_DSCP, one bit
> per ring in QDMA_CSR_INT_ENABLE2/3) when an RX ring runs out of free
> CPU descriptors. airoha_qdma_hw_init() already unmasks this interrupt
> for every ring (INT_RX1_MASK()/INT_RX2_MASK() OR it together with the
> RX_DONE bits before writing the enable register), but
> airoha_irq_handler() only ever extracts the RX_DONE bits from the same
> status word - the NO_CPU_DSCP bits are read and acknowledged (cleared)
> along with everything else at the top of the handler, then silently
> dropped.
>
> This matters because once a ring is genuinely drained to zero posted
> descriptors, no further RX_DONE interrupt can fire for it: hardware
> has nothing left to receive a frame into, so NAPI is never rescheduled
> and airoha_qdma_fill_rx_queue() (which reposts descriptors) is never
> called again. The ring is stuck until the interface is brought down
> and back up.
>
> This is most visible on rings that carry low, bursty volumes of
> protocol control traffic, in particular RX ring 4, to which
> airoha_fe_vip_setup() force-routes ~15 unrelated VIP-classified
> protocols (BOOTP, PPPoE Discovery, ISAKMP, DHCPv6, SIP, LLDP, PPP
> LCP/IPCP/CHAP/PAP/IPv6CP, ...) via PATN_FCPU_EN_MASK, all sharing the
> same RX_DSCP_NUM() default of 16 descriptors. A short burst on that
> ring (e.g. a DHCP lease renewal exchange, or the LCP/IPCP/CHAP/PAP
> negotiation that follows a PPPoE PADO) can drain it faster than the
> CPU reposts descriptors, after which every one of those protocols
> silently stops being received on that device until it is reconfigured
> - with no error, warning, or netdev/ethtool counter indicating why.
>
> Fix airoha_irq_handler() to treat NO_CPU_DSCP the same as RX_DONE for
> the purpose of scheduling NAPI: airoha_qdma_rx_process() already calls
> airoha_qdma_fill_rx_queue() unconditionally at the end of every poll,
> even when zero descriptors were reaped, so scheduling NAPI in response
> to NO_CPU_DSCP is sufficient to make an emptied ring recover on its
> own. airoha_qdma_rx_napi_poll() is updated to re-enable the
> NO_CPU_DSCP bit alongside RX_DONE when napi_complete() runs, mirroring
> the existing disable/enable dance so the interrupt isn't left masked
> after its first use.
>
> One open question worth flagging explicitly: if the underlying
> no-free-descriptor condition re-latches this bit immediately after the
> ack write (rather than only on the next empty->non-empty transition),
> a ring that airoha_qdma_fill_rx_queue() genuinely cannot repost into
> (e.g. page_pool_dev_alloc_frag() returning NULL under memory pressure)
> would turn this into a self-reasserting interrupt storm on the hard
> IRQ path: mask -> napi_schedule() -> poll reaps 0, refills 0 ->
> napi_complete() -> unmask -> NO_CPU_DSCP fires again immediately. I
> don't have documentation confirming which behavior this bit actually
> has. Regardless of the answer, masking NO_CPU_DSCP until a refill
> actually succeeds - the natural-looking alternative - is worse: a
> fully memory-starved ring can never fire RX_DONE either (nothing was
> posted for hw to complete), so that would leave it permanently dead
> once the memory pressure clears rather than self-healing. A bounded
> storm tied to genuine memory pressure, if that's what this is, seems
> preferable to a ring with no way back either way.
Hi Vitaliy,
I do not have info about it. Adding airoha folks in CC.
>
> Fixes: 23290c7bc190 ("net: airoha: Introduce Airoha NPU support")
> Link: https://github.com/openwrt/openwrt/issues/24715
> Signed-off-by: Vitaliy Sochnev <sochnev.v.74@gmail.com>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 28 +++++++++++++++++++-----
> 1 file changed, 23 insertions(+), 5 deletions(-)
thx for working on it. Just a couple of nit inline. Fixing them:
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 64619e9a704d..a3e5aaeb75b3 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -784,13 +784,15 @@ static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget)
> int i, qid = q - &qdma->q_rx[0];
> int intr_reg = qid < RX_DONE_HIGH_OFFSET ? QDMA_INT_REG_IDX1
> : QDMA_INT_REG_IDX2;
> + u32 bit = qid % RX_DONE_HIGH_OFFSET;
nit: what about doing something like:
u32 irq_id = qid % RX_DONE_HIGH_OFFSET;
u32 intr_mask = BIT(irq_id) |
BIT(irq_id + RX_NO_CPU_DSCP_LOW_OFFSET);
>
> for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) {
> if (!(BIT(qid) & RX_IRQ_BANK_PIN_MASK(i)))
> continue;
>
> airoha_qdma_irq_enable(&qdma->irq_banks[i], intr_reg,
> - BIT(qid % RX_DONE_HIGH_OFFSET));
> + BIT(bit) |
> + BIT(bit + RX_NO_CPU_DSCP_LOW_OFFSET));
> }
> }
>
> @@ -1468,16 +1470,32 @@ static irqreturn_t airoha_irq_handler(int irq, void *dev_instance)
> if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state))
> return IRQ_NONE;
>
> - rx_intr1 = intr[1] & RX_DONE_LOW_INT_MASK;
> + /* A ring can also raise NO_CPU_DSCP when it runs out of free RX
> + * descriptors (e.g. a burst of VIP-classified control traffic
> + * forced onto a small ring). Once a ring is fully drained no more
> + * RX_DONE interrupts can fire for it, since there are no free
> + * descriptors left for hardware to receive into, so without this
> + * NAPI is never rescheduled and the ring never gets refilled again.
> + * Treat NO_CPU_DSCP the same as RX_DONE for scheduling NAPI:
> + * airoha_qdma_rx_process() unconditionally calls
> + * airoha_qdma_fill_rx_queue() at the end of every poll, even when
> + * zero descriptors were reaped, so this alone is enough to recover
> + * the ring.
> + */
I guess this comment (AI driven??) should go into the commit message, it seems too
verbose for the code and it is quite obvious what you want to do.
> + rx_intr1 = intr[1] & (RX_DONE_LOW_INT_MASK | RX_NO_CPU_DSCP_LOW_INT_MASK);
> if (rx_intr1) {
> airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX1, rx_intr1);
> - rx_intr_mask |= rx_intr1;
> + rx_intr_mask |= (rx_intr1 & RX_DONE_LOW_INT_MASK) |
> + ((rx_intr1 & RX_NO_CPU_DSCP_LOW_INT_MASK) >>
> + RX_NO_CPU_DSCP_LOW_OFFSET);
What about defining a macro in airoha_regs.h
#define RX_NO_CPU_DSCP_INT_RX1_MASK(_n) (((_n) & RX_NO_CPU_DSCP_LOW_INT_MASK) >>\
RX_NO_CPU_DSCP_LOW_OFFSET)
> }
>
> - rx_intr2 = intr[2] & RX_DONE_HIGH_INT_MASK;
> + rx_intr2 = intr[2] & (RX_DONE_HIGH_INT_MASK | RX_NO_CPU_DSCP_HIGH_INT_MASK);
> if (rx_intr2) {
> airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX2, rx_intr2);
> - rx_intr_mask |= (rx_intr2 << 16);
> + rx_intr_mask |= ((rx_intr2 & RX_DONE_HIGH_INT_MASK) |
> + ((rx_intr2 & RX_NO_CPU_DSCP_HIGH_INT_MASK) >>
> + RX_NO_CPU_DSCP_LOW_OFFSET)) << 16;
I guess here you do not need to shift RX_NO_CPU_DSCP_HIGH_INT_MASK mask since
it is already in the range [31:16], just mask like:
(rx_intr2 & RX_NO_CPU_DSCP_HIGH_INT_MASK)
> }
>
> for (i = 0; rx_intr_mask && i < ARRAY_SIZE(qdma->q_rx); i++) {
> --
> 2.55.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
next prev parent reply other threads:[~2026-08-30 13:26 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-30 9:57 [PATCH 0/4] net: airoha: fix silent RX packet loss on ring 4 Vitaliy Sochnev
2026-08-30 9:57 ` [PATCH net 1/4] net: airoha: handle RX_NO_CPU_DSCP interrupt, not just RX_DONE Vitaliy Sochnev
2026-08-30 13:26 ` Lorenzo Bianconi [this message]
2026-08-30 9:57 ` [PATCH net-next 2/4] net: airoha: recover RX ring after hw completion race Vitaliy Sochnev
2026-08-30 14:18 ` Lorenzo Bianconi
2026-08-30 9:57 ` [PATCH net-next 3/4] net: airoha: add rx_stall_recover ethtool counter Vitaliy Sochnev
2026-08-30 9:57 ` [PATCH net-next 4/4] net: airoha: grow RX ring 4 to 128 descriptors Vitaliy Sochnev
2026-08-30 14:24 ` Lorenzo Bianconi
2026-08-31 23:46 ` [PATCH net v2 0/3] net: airoha: fix silent RX loss on the shared CPU ring Vitaliy Sochnev
2026-08-31 23:46 ` [PATCH net v2 1/3] net: airoha: handle RX_NO_CPU_DSCP interrupt, not just RX_DONE Vitaliy Sochnev
2026-08-31 23:47 ` [PATCH net v2 2/3] net: airoha: recover RX ring after hw completion stall Vitaliy Sochnev
2026-09-01 7:38 ` Lorenzo Bianconi
2026-09-01 18:33 ` Vitaliy Sochnev
2026-08-31 23:47 ` [PATCH net v2 3/3] net: airoha: grow the small RX rings Vitaliy Sochnev
2026-09-01 7:23 ` Lorenzo Bianconi
2026-09-01 18:32 ` [PATCH net v3 0/2] net: airoha: fix silent RX loss on the shared CPU ring Vitaliy Sochnev
2026-09-01 18:32 ` [PATCH net v3 1/2] net: airoha: handle RX_NO_CPU_DSCP interrupt, not just RX_DONE Vitaliy Sochnev
2026-09-04 0:35 ` Jakub Kicinski
2026-09-10 23:06 ` Vitaliy Sochnev
2026-09-04 0:50 ` patchwork-bot+netdevbpf
2026-09-01 18:32 ` [PATCH net v3 2/2] net: airoha: grow the small RX rings Vitaliy Sochnev
2026-09-06 6:37 ` net: airoha: RX rings below 32 descriptors let hw DMA past the ring Vitaliy Sochnev
2026-09-06 8:02 ` Vitaliy Sochnev
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=apQvbk23AHg3w62J@lore-desk \
--to=lorenzo@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sochnev.v.74@gmail.com \
--cc=upstream@airoha.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.