]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: enetc: fix the deadlock of enetc_mdio_lock
authorJianpeng Chang <jianpeng.chang.cn@windriver.com>
Wed, 15 Oct 2025 02:14:27 +0000 (10:14 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Oct 2025 13:08:54 +0000 (14:08 +0100)
[ Upstream commit 50bd33f6b3922a6b760aa30d409cae891cec8fb5 ]

After applying the workaround for err050089, the LS1028A platform
experiences RCU stalls on RT kernel. This issue is caused by the
recursive acquisition of the read lock enetc_mdio_lock. Here list some
of the call stacks identified under the enetc_poll path that may lead to
a deadlock:

enetc_poll
  -> enetc_lock_mdio
  -> enetc_clean_rx_ring OR napi_complete_done
     -> napi_gro_receive
        -> enetc_start_xmit
           -> enetc_lock_mdio
           -> enetc_map_tx_buffs
           -> enetc_unlock_mdio
  -> enetc_unlock_mdio

After enetc_poll acquires the read lock, a higher-priority writer attempts
to acquire the lock, causing preemption. The writer detects that a
read lock is already held and is scheduled out. However, readers under
enetc_poll cannot acquire the read lock again because a writer is already
waiting, leading to a thread hang.

Currently, the deadlock is avoided by adjusting enetc_lock_mdio to prevent
recursive lock acquisition.

Fixes: 6d36ecdbc441 ("net: enetc: take the MDIO lock only once per NAPI poll cycle")
Signed-off-by: Jianpeng Chang <jianpeng.chang.cn@windriver.com>
Acked-by: Wei Fang <wei.fang@nxp.com>
Link: https://patch.msgid.link/20251015021427.180757-1-jianpeng.chang.cn@windriver.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/ethernet/freescale/enetc/enetc.c

index d8272b7a55fcb0b75af1334cd4f3a66d87b082f6..749b65aab14a90261d642fb2279e2febd3a2d29f 100644 (file)
@@ -1246,6 +1246,8 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
        /* next descriptor to process */
        i = rx_ring->next_to_clean;
 
+       enetc_lock_mdio();
+
        while (likely(rx_frm_cnt < work_limit)) {
                union enetc_rx_bd *rxbd;
                struct sk_buff *skb;
@@ -1281,7 +1283,9 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
                rx_byte_cnt += skb->len + ETH_HLEN;
                rx_frm_cnt++;
 
+               enetc_unlock_mdio();
                napi_gro_receive(napi, skb);
+               enetc_lock_mdio();
        }
 
        rx_ring->next_to_clean = i;
@@ -1289,6 +1293,8 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
        rx_ring->stats.packets += rx_frm_cnt;
        rx_ring->stats.bytes += rx_byte_cnt;
 
+       enetc_unlock_mdio();
+
        return rx_frm_cnt;
 }
 
@@ -1598,6 +1604,8 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
        /* next descriptor to process */
        i = rx_ring->next_to_clean;
 
+       enetc_lock_mdio();
+
        while (likely(rx_frm_cnt < work_limit)) {
                union enetc_rx_bd *rxbd, *orig_rxbd;
                int orig_i, orig_cleaned_cnt;
@@ -1657,7 +1665,9 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
                        if (unlikely(!skb))
                                goto out;
 
+                       enetc_unlock_mdio();
                        napi_gro_receive(napi, skb);
+                       enetc_lock_mdio();
                        break;
                case XDP_TX:
                        tx_ring = priv->xdp_tx_ring[rx_ring->index];
@@ -1692,7 +1702,9 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
                        }
                        break;
                case XDP_REDIRECT:
+                       enetc_unlock_mdio();
                        err = xdp_do_redirect(rx_ring->ndev, &xdp_buff, prog);
+                       enetc_lock_mdio();
                        if (unlikely(err)) {
                                enetc_xdp_drop(rx_ring, orig_i, i);
                                rx_ring->stats.xdp_redirect_failures++;
@@ -1712,8 +1724,11 @@ out:
        rx_ring->stats.packets += rx_frm_cnt;
        rx_ring->stats.bytes += rx_byte_cnt;
 
-       if (xdp_redirect_frm_cnt)
+       if (xdp_redirect_frm_cnt) {
+               enetc_unlock_mdio();
                xdp_do_flush();
+               enetc_lock_mdio();
+       }
 
        if (xdp_tx_frm_cnt)
                enetc_update_tx_ring_tail(tx_ring);
@@ -1722,6 +1737,8 @@ out:
                enetc_refill_rx_ring(rx_ring, enetc_bd_unused(rx_ring) -
                                     rx_ring->xdp.xdp_tx_in_flight);
 
+       enetc_unlock_mdio();
+
        return rx_frm_cnt;
 }
 
@@ -1740,6 +1757,7 @@ static int enetc_poll(struct napi_struct *napi, int budget)
        for (i = 0; i < v->count_tx_rings; i++)
                if (!enetc_clean_tx_ring(&v->tx_ring[i], budget))
                        complete = false;
+       enetc_unlock_mdio();
 
        prog = rx_ring->xdp.prog;
        if (prog)
@@ -1751,10 +1769,8 @@ static int enetc_poll(struct napi_struct *napi, int budget)
        if (work_done)
                v->rx_napi_work = true;
 
-       if (!complete) {
-               enetc_unlock_mdio();
+       if (!complete)
                return budget;
-       }
 
        napi_complete_done(napi, work_done);
 
@@ -1763,6 +1779,7 @@ static int enetc_poll(struct napi_struct *napi, int budget)
 
        v->rx_napi_work = false;
 
+       enetc_lock_mdio();
        /* enable interrupts */
        enetc_wr_reg_hot(v->rbier, ENETC_RBIER_RXTIE);