A NAPI poll can retain a partially assembled frame across invocations. The
stop path clears that pointer without releasing its fragment references,
leaking every page already attached to the frame.
Free a pending fragment skb after disabling NAPI and before clearing the
saved RX state. Count the discarded frame as dropped under the RX
statistics sequence counter. Use the irqsave update helpers because
ndo_stop runs in preemptible process context while 32-bit statistics
writers must not be preempted.
The issue was found by inspecting the partial frame lifetime and statistics
writer contexts. The series was tested on a D-Link DIR-685 with media
playback and ksmbd transfers under ping load.
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
drivers/net/ethernet/cortina/gemini.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c
index 43fd8fd773e3..32291d4c3390 100644
--- a/drivers/net/ethernet/cortina/gemini.c
+++ b/drivers/net/ethernet/cortina/gemini.c
@@ -2209,12 +2209,19 @@ static int gmac_open(struct net_device *netdev)
static int gmac_stop(struct net_device *netdev)
{
struct gemini_ethernet_port *port = netdev_priv(netdev);
+ unsigned long flags;
hrtimer_cancel(&port->rx_coalesce_timer);
netif_tx_stop_all_queues(netdev);
gmac_disable_tx_rx(netdev);
gmac_stop_dma(port);
napi_disable(&port->napi);
+ if (port->rx_skb) {
+ napi_free_frags(&port->napi);
+ flags = u64_stats_update_begin_irqsave(&port->rx_stats_syncp);
+ port->stats.rx_dropped++;
+ u64_stats_update_end_irqrestore(&port->rx_stats_syncp, flags);
+ }
port->rx_skb = NULL;
port->rx_frag_nr = 0;
port->rx_dropping = false;
--
2.56.0