Commit f8ae63de2a87 for kernel

commit f8ae63de2a872fa3b68c287c35379f6d73d38a5d
Author: Wei Fang <wei.fang@nxp.com>
Date:   Wed May 20 14:44:19 2026 +0800

    net: enetc: fix unbounded loop and interrupt handling in VF-to-PF messaging

    The enetc_msg_task() function has several issues that need to be addressed:

    1. Unbounded loop causing potential DoS:

    enetc_msg_task() processes VF-to-PF mailbox messages in an unbounded
    for(;;) loop that keeps polling ENETC_PSIMSGRR until no MR bits are set.
    A malicious guest VM can exploit this by continuously sending messages at
    a high rate - immediately sending a new message as soon as the PF
    acknowledges the previous one. Since the worker thread never yields or
    enforces a processing budget, the mr_mask check frequently evaluates to
    non-zero, causing the PF to spin indefinitely and starving other tasks.

    Fix this by replacing the unbounded loop with a single snapshot read at
    task entry. The task processes only the VFs whose MR bits were set at
    that point, then re-enables message interrupts before returning. This
    bounds work per invocation to at most num_vfs iterations. No messages are
    lost because the message interrupt is disabled in enetc_msg_psi_msix()
    before scheduling enetc_msg_task(), so any new messages arriving during
    processing will trigger a fresh interrupt once re-enabled, scheduling
    another task invocation.

    2. Write order of ENETC_PSIIDR and ENETC_PSIMSGRR:

    Both ENETC_PSIIDR and ENETC_PSIMSGRR contain MR bits indicating messages
    have been received from VSIs, but only ENETC_PSIIDR trigger the CPU
    interrupt. Previously, ENETC_PSIMSGRR was written before ENETC_PSIIDR.
    Writing ENETC_PSIMSGRR returns the message code to the VSI in its upper
    16 bits, signaling to the VF that message processing is complete and it
    may send the next message. If the VF sends a new message before
    ENETC_PSIIDR is written, the subsequent w1c write to ENETC_PSIIDR would
    inadvertently clear the MR bit set by the new message, causing the
    interrupt to be lost and the new message to go unprocessed.

    Therefore, write ENETC_PSIIDR first to clear the interrupt source, then
    write ENETC_PSIMSGRR to acknowledge the message to the VSI.

    3. Check both ENETC_PSIMSGRR and ENETC_PSIIDR for mr_status:

    The write order change above introduces a potential race: if a VF sends
    a new message in the window between the ENETC_PSIIDR w1c and the
    ENETC_PSIMSGRR w1c, the ENETC_PSIMSGRR MR bit for the new message may
    not be set. If mr_status was derived solely from ENETC_PSIMSGRR, this
    message would never be detected despite ENETC_PSIIDR retaining its MR
    bit, leading to an unacknowledged interrupt storm.

    Fix this by computing mr_status as the union of both ENETC_PSIMSGRR and
    ENETC_PSIIDR MR bits, ensuring all pending messages are detected
    regardless of which register reflects the new message state.

    Additionally, rename the per-register MR macros (ENETC_PSI*_MR_MASK,
    ENETC_PSI*_MR) to register-agnostic names (ENETC_PSIMR_MASK,
    ENETC_PSIMR_BIT) since the MR bit layout is shared across ENETC_PSIMSGRR,
    ENETC_PSIIER, and ENETC_PSIIDR. Make the mask macro dynamic based on
    the actual number of active VFs rather than hardcoded.

    Fixes: beb74ac878c8 ("enetc: Add vf to pf messaging support")
    Signed-off-by: Wei Fang <wei.fang@nxp.com>
    Link: https://patch.msgid.link/20260520064421.91569-8-wei.fang@nxp.com
    Signed-off-by: Jakub Kicinski <kuba@kernel.org>

diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 662e4fbafb74..e58cc81d199d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -56,11 +56,21 @@ static inline u32 enetc_vsi_set_msize(u32 size)
 }

 #define ENETC_PSIMSGRR	0x204
-#define ENETC_PSIMSGRR_MR_MASK	GENMASK(2, 1)
-#define ENETC_PSIMSGRR_MR(n) BIT((n) + 1) /* n = VSI index */
 #define ENETC_PSIVMSGRCVAR0(n)	(0x210 + (n) * 0x8) /* n = VSI index */
 #define ENETC_PSIVMSGRCVAR1(n)	(0x214 + (n) * 0x8)

+/* Message received mask, n is the active number of VSIs.
+ * It is available for ENETC_PSIMSGRR, ENETC_PSIIER, and
+ * ENETC_PSIIDR registers.
+ */
+#define ENETC_PSIMR_MASK(n)	\
+	({ typeof(n) _n = (n); (_n) ? GENMASK((_n), 1) : 0; })
+
+/* Message received bit, n is VSI index. It is available for
+ * ENETC_PSIMSGRR, ENETC_PSIIER, and ENETC_PSIIDR registers.
+ */
+#define ENETC_PSIMR_BIT(n)	BIT((n) + 1)
+
 #define ENETC_VSIMSGSR	0x204	/* RO */
 #define ENETC_VSIMSGSR_MB	BIT(0)
 #define ENETC_VSIMSGSR_MS	BIT(1)
@@ -94,7 +104,6 @@ static inline u32 enetc_vsi_set_msize(u32 size)
 #define ENETC_SICAPR1	0x904

 #define ENETC_PSIIER	0xa00
-#define ENETC_PSIIER_MR_MASK	GENMASK(2, 1)
 #define ENETC_PSIIDR	0xa08
 #define ENETC_SITXIDR	0xa18
 #define ENETC_SIRXIDR	0xa28
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index b4d7457097e6..3136e8321e4d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -3,18 +3,25 @@

 #include "enetc_pf.h"

-static void enetc_msg_disable_mr_int(struct enetc_hw *hw)
+static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
 {
-	u32 psiier = enetc_rd(hw, ENETC_PSIIER);
+	struct enetc_hw *hw = &pf->si->hw;
+	u32 psiier;
+
+	psiier = enetc_rd(hw, ENETC_PSIIER) & ~ENETC_PSIMR_MASK(pf->num_vfs);
+
 	/* disable MR int source(s) */
-	enetc_wr(hw, ENETC_PSIIER, psiier & ~ENETC_PSIIER_MR_MASK);
+	enetc_wr(hw, ENETC_PSIIER, psiier);
 }

-static void enetc_msg_enable_mr_int(struct enetc_hw *hw)
+static void enetc_msg_enable_mr_int(struct enetc_pf *pf)
 {
-	u32 psiier = enetc_rd(hw, ENETC_PSIIER);
+	struct enetc_hw *hw = &pf->si->hw;
+	u32 psiier;

-	enetc_wr(hw, ENETC_PSIIER, psiier | ENETC_PSIIER_MR_MASK);
+	psiier = enetc_rd(hw, ENETC_PSIIER) | ENETC_PSIMR_MASK(pf->num_vfs);
+
+	enetc_wr(hw, ENETC_PSIIER, psiier);
 }

 static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
@@ -22,7 +29,7 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
 	struct enetc_si *si = (struct enetc_si *)data;
 	struct enetc_pf *pf = enetc_si_priv(si);

-	enetc_msg_disable_mr_int(&si->hw);
+	enetc_msg_disable_mr_int(pf);
 	schedule_work(&pf->msg_task);

 	return IRQ_HANDLED;
@@ -31,33 +38,35 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
 static void enetc_msg_task(struct work_struct *work)
 {
 	struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task);
+	u32 mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
 	struct enetc_hw *hw = &pf->si->hw;
-	unsigned long mr_mask;
+	u32 mr_status;
 	int i;

-	for (;;) {
-		mr_mask = enetc_rd(hw, ENETC_PSIMSGRR) & ENETC_PSIMSGRR_MR_MASK;
-		if (!mr_mask) {
-			/* re-arm MR interrupts, w1c the IDR reg */
-			enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIIER_MR_MASK);
-			enetc_msg_enable_mr_int(hw);
-			return;
-		}
+	mr_status = (enetc_rd(hw, ENETC_PSIMSGRR) & mr_mask) |
+		    (enetc_rd(hw, ENETC_PSIIDR) & mr_mask);
+	if (!mr_status)
+		goto out;
+
+	for (i = 0; i < pf->num_vfs; i++) {
+		u32 psimsgrr;
+		u16 msg_code;

-		for (i = 0; i < pf->num_vfs; i++) {
-			u32 psimsgrr;
-			u16 msg_code;
+		if (!(ENETC_PSIMR_BIT(i) & mr_status))
+			continue;

-			if (!(ENETC_PSIMSGRR_MR(i) & mr_mask))
-				continue;
+		enetc_msg_handle_rxmsg(pf, i, &msg_code);

-			enetc_msg_handle_rxmsg(pf, i, &msg_code);
+		/* w1c to clear the corresponding VF MR bit */
+		enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIMR_BIT(i));

-			psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
-			psimsgrr |= ENETC_PSIMSGRR_MR(i); /* w1c */
-			enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
-		}
+		psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
+		psimsgrr |= ENETC_PSIMR_BIT(i); /* w1c */
+		enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
 	}
+
+out:
+	enetc_msg_enable_mr_int(pf);
 }

 /* Init */
@@ -133,7 +142,7 @@ int enetc_msg_psi_init(struct enetc_pf *pf)
 	}

 	/* enable MR interrupts */
-	enetc_msg_enable_mr_int(&si->hw);
+	enetc_msg_enable_mr_int(pf);

 	return 0;

@@ -154,7 +163,7 @@ void enetc_msg_psi_free(struct enetc_pf *pf)
 	cancel_work_sync(&pf->msg_task);

 	/* disable MR interrupts */
-	enetc_msg_disable_mr_int(&si->hw);
+	enetc_msg_disable_mr_int(pf);

 	for (i = 0; i < pf->num_vfs; i++)
 		enetc_msg_free_mbx(si, i);