On 32-bit PREEMPT_RT systems u64_stats_update_begin() disables preemption. The RX poll currently holds that writer section across gmac_rx(), which takes spinlock_t locks and calls into GRO and fragment handling. spinlock_t can sleep on PREEMPT_RT and must not be acquired with preemption disabled. Keep the writer sections around the counter updates only. NAPI ownership continues to serialize normal RX writers, while the stop path updates its counter after napi_disable(). This preserves the existing accounting without covering potentially sleeping operations. The issue was found by inspecting the u64_stats_sync writer scope. The series was tested on a D-Link DIR-685 with media playback and ksmbd transfers under ping load. PREEMPT_RT was not runtime-tested. Assisted-by: LLM Signed-off-by: Linus Walleij --- drivers/net/ethernet/cortina/gemini.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 0c6c7c938b2e..7ecc8db9b7d3 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -1720,12 +1720,12 @@ static struct sk_buff *gmac_skb_if_good_frame(struct gemini_ethernet_port *port, unsigned int rx_status = word0.bits.status; struct sk_buff *skb = NULL; - port->rx_stats[rx_status]++; - port->rx_csum_stats[rx_csum]++; - if (word0.bits.derr || word0.bits.perr || rx_status || frame_len < ETH_ZLEN || rx_csum >= RX_CHKSUM_IP_ERR_UNKNOWN) { + u64_stats_update_begin(&port->rx_stats_syncp); + port->rx_stats[rx_status]++; + port->rx_csum_stats[rx_csum]++; port->stats.rx_errors++; if (frame_len < ETH_ZLEN || RX_ERROR_LENGTH(rx_status)) @@ -1736,6 +1736,7 @@ static struct sk_buff *gmac_skb_if_good_frame(struct gemini_ethernet_port *port, port->stats.rx_crc_errors++; if (RX_ERROR_FRAME(rx_status)) port->stats.rx_frame_errors++; + u64_stats_update_end(&port->rx_stats_syncp); return NULL; } @@ -1747,8 +1748,12 @@ static struct sk_buff *gmac_skb_if_good_frame(struct gemini_ethernet_port *port, skb->ip_summed = CHECKSUM_UNNECESSARY; update_exit: + u64_stats_update_begin(&port->rx_stats_syncp); + port->rx_stats[rx_status]++; + port->rx_csum_stats[rx_csum]++; port->stats.rx_bytes += frame_len; port->stats.rx_packets++; + u64_stats_update_end(&port->rx_stats_syncp); return skb; } @@ -1806,7 +1811,9 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget, if (word3.bits32 & SOF_BIT) { if (skb) { napi_free_frags(&port->napi); + u64_stats_update_begin(&port->rx_stats_syncp); port->stats.rx_dropped++; + u64_stats_update_end(&port->rx_stats_syncp); skb = NULL; frag_nr = 0; } @@ -1898,7 +1905,9 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget, put_page(page); if (!dropping) { + u64_stats_update_begin(&port->rx_stats_syncp); port->stats.rx_dropped++; + u64_stats_update_end(&port->rx_stats_syncp); dropping = true; } @@ -1930,13 +1939,13 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget) bool reschedule; freeq_threshold = 1 << (geth->freeq_order - 1); - u64_stats_update_begin(&port->rx_stats_syncp); received = gmac_rx(napi->dev, budget, &freeq_consumed, &reschedule); - if (!reschedule && received < budget) + if (!reschedule && received < budget) { + u64_stats_update_begin(&port->rx_stats_syncp); ++port->rx_napi_exits; - - u64_stats_update_end(&port->rx_stats_syncp); + u64_stats_update_end(&port->rx_stats_syncp); + } port->freeq_refill += freeq_consumed; if (port->freeq_refill > freeq_threshold) { -- 2.56.0