enetc_msg_task() processes VF-to-PF mailbox messages using an unbounded for(;;) loop that repeatedly polls ENETC_PSIMSGRR until no MR bits set. A malicious guest VM can exploit this by immediately sending a new message as soon as the PF acknowledges the previous one via a w1c write to ENETC_PSIMSGRR. Since there is no processing budget or yield point, a VF can keep the MR bit continuously re-asserted, preventing the loop from ever terminating and starving other tasks on the PF worker thread. Fix this by replacing the unbounded loop with a single snapshot read of ENETC_PSIMSGRR at task entry. The task processes only the VFs whose MR bits were set at that point, clears the corresponding bits in ENETC_PSIMSGRR and ENETC_PSIIDR, and re-enables the message interrupt before returning. No messages are lost: the message interrupt is disabled before enetc_msg_task() is scheduled (in enetc_msg_psi_msix()), so new messages arriving during processing do not generate additional CPU interrupts. When enetc_msg_task() re-enables the interrupts at exit, the hardware detects any MR bits that were set during execution and generates a new interrupt, scheduling another task invocation. This bounds the work per task invocation to at most num_vfs message processing iterations, regardless of how aggressively VFs send messages. Fixes: beb74ac878c8 ("enetc: Add vf to pf messaging support") Signed-off-by: Wei Fang --- .../net/ethernet/freescale/enetc/enetc_hw.h | 1 + .../net/ethernet/freescale/enetc/enetc_msg.c | 38 +++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h index 662e4fbafb74..39b82faaf041 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h @@ -96,6 +96,7 @@ static inline u32 enetc_vsi_set_msize(u32 size) #define ENETC_PSIIER 0xa00 #define ENETC_PSIIER_MR_MASK GENMASK(2, 1) #define ENETC_PSIIDR 0xa08 +#define ENETC_PSIIDR_MR(n) BIT((n) + 1) /* n = VSI index */ #define ENETC_SITXIDR 0xa18 #define ENETC_SIRXIDR 0xa28 #define ENETC_SIMSIVR 0xa30 diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c index b4d7457097e6..39f057fe85c7 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c @@ -32,32 +32,32 @@ static void enetc_msg_task(struct work_struct *work) { struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task); 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) & ENETC_PSIMSGRR_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_PSIMSGRR_MR(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_PSIIDR_MR(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_PSIMSGRR_MR(i); /* w1c */ + enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr); } + +out: + enetc_msg_enable_mr_int(hw); } /* Init */ -- 2.34.1