RE: [PATCH net 2/2] net: enetc: restore RX ring congestion mode for ENETC v4

"Wei Fang (OSS)" <[email protected]> Wed, 29 Jul 2026 06:16:47 +0000
Newsgroups dev.linux.lists.imx,dev.linux.lists.sashiko-reviews,org.kernel.vger.netdev
Message-ID <GV2PR04MB117397EC2484493DC4BB40A5488CA2@GV2PR04MB11739.eurprd04.prod.outlook.com>
> >  static void enetc4_set_tx_pause(struct enetc_pf *pf, int num_rxbdr, bool
> tx_pause)
> >  {
> > +	struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
> >  	u32 pause_off_thresh = 0, pause_on_thresh = 0;
> >  	u32 init_quanta = 0, refresh_quanta = 0;
> >  	struct enetc_hw *hw = &pf->si->hw;
> > @@ -764,6 +765,8 @@ static void enetc4_set_tx_pause(struct enetc_pf *pf,
> int num_rxbdr, bool tx_paus
> >  	int i;
> >
> >  	for (i = 0; i < num_rxbdr; i++) {
> >  		old_rbmr = enetc_rxbdr_rd(hw, i, ENETC_RBMR);
> >  		rbmr = u32_replace_bits(old_rbmr, tx_pause ? 1 : 0,
> ENETC_RBMR_CM);
> >  		if (rbmr == old_rbmr)
> >  			continue;
> >
> >  		enetc_rxbdr_wr(hw, i, ENETC_RBMR, rbmr);
> >  	}
> 
> [Severity: Critical]
> This is a pre-existing issue, but could this unprotected read-modify-write
> lead to memory corruption if it races with enetc_disable_rxbdr()?

Yes, this is a potential issue, l can add a spinlock to fix this corner issue,
but I'm not sure if it is worth doing this, because the benefits are minimal.

> 
> If phylink_resolve() executes enetc4_set_tx_pause() concurrently with
> enetc_reconfigure(), this loop can read ENETC_RBMR before the ring is
> torn down.
> 
> If it writes the modified value back after enetc_disable_rxbdr()
> clears the enable bit, it would inadvertently re-enable the RX ring in
> hardware:
> 
> drivers/net/ethernet/freescale/enetc/enetc.c:enetc_disable_rxbdr() {
>     ...
>     /* disable EN bit on ring */
>     enetc_rxbdr_wr(hw, idx, ENETC_RBMR, 0);
>     ...
> }
> 
> Because the software frees the associated DMA buffers during teardown, the
> hardware would then DMA incoming packets directly into freed memory.
> 
> >
> >  	if (tx_pause) {
> > +		set_bit(ENETC_RXBDR_CM, &priv->flags);
> > +
> 
> [Severity: High]
> Can this result in the software state and hardware register becoming
> desynchronized?
> 
> Since ENETC_RXBDR_CM is set after the hardware registers are updated in the
> loop above, there appears to be a race window. If enetc_setup_rxbdr() is
> called by a concurrent enetc_reconfigure() during this window, it would
> see the unset flag:
> 
> drivers/net/ethernet/freescale/enetc/enetc.c:enetc_setup_rxbdr() {
>     ...
>     if (test_bit(ENETC_RXBDR_CM, &priv->flags))
>         rbmr |= ENETC_RBMR_CM;
>     ...
> }
> 
> This would overwrite the hardware register without the CM bit. Then this
> thread sets the software flag, leaving flow control disabled in hardware
> when the software state expects it to be enabled.
> 
> >  		/* When the port first enters congestion, send a PAUSE request
> >  		 * with the maximum number of quanta. When the port exits
> 
> [ ... ]
> 
> > @@ -949,7 +954,10 @@ static void enetc4_pl_mac_link_down(struct
> phylink_config *config,
> >  				    phy_interface_t interface)
> >  {
> >  	struct enetc_pf *pf = phylink_to_enetc_pf(config);
> > +	struct enetc_ndev_priv *priv;
> >
> > +	priv = netdev_priv(pf->si->ndev);
> > +	clear_bit(ENETC_RXBDR_CM, &priv->flags);
> >  	enetc4_mac_rx_graceful_stop(pf);
> >  	enetc4_mac_tx_graceful_stop(pf);
> >  }
> 
> --
> Sashiko AI
> review ยท https://sashiko.dev/#/patchset/20260728023217.2499420-1-wei.fang
> @oss.nxp.com?part=2