FAILED: patch "[PATCH] net: atlantic: free stranded TX buffers on ring deinit" failed to apply to 5.15-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 452636ea5410a96e02ebaaf80b21e3620b98e0dd
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026081728-ounce-implicit-0ec5@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 452636ea5410a96e02ebaaf80b21e3620b98e0dd Mon Sep 17 00:00:00 2001
From: Yangyu Chen <[email protected]>
Date: Sun, 2 Aug 2026 23:46:00 +0800
Subject: [PATCH] net: atlantic: free stranded TX buffers on ring deinit
aq_vec_deinit() drains the TX rings with a single aq_ring_tx_clean() call, which frees at most AQ_CFG_TX_CLEAN_BUDGET (256) descriptors and stops at hw_head, which no longer moves once aq_vec_stop() has stopped the hardware and NAPI. Completed descriptors beyond the budget and everything still posted in [hw_head, sw_tail) keep their skb or xdp_frame when the interface goes down: aq_vec_ring_free() then frees the buffer ring and the references are lost for good.
Today this is a silent memory leak on every interface down under TX/XDP_TX load. With the conversion of the RX path to page_pool posted for net-next it becomes much more visible: XDP_TX frames carry fragment references on the RX ring's page_pool, so a single stranded frame keeps the pool's inflight count above zero forever. page_pool_destroy() then never completes, the pool is leaked together with its pages, and "page_pool_release_retry() stalled pool shutdown" is warned every 60 seconds from that point on, on every ifdown, XDP detach or ring resize under XDP_TX load.
Bring back aq_ring_tx_deinit() as it was before the removal and use it for teardown again, with one extension: TX rings can hold xdp_frames nowadays, so release those too. They are returned with xdp_return_frame() since this runs in process context.
drivers/net/ethernet/aquantia/atlantic/aq_ring.c +29 -0
@@ -360,6 +360,35 @@ bool aq_ring_tx_clean(struct aq_ring_s *self)return !!budget; } +void aq_ring_tx_deinit (struct aq_ring_s *self) +{ +if (!self) +return ; + +for (; self->sw_head != self->sw_tail; + self->sw_head =aq_ring_next_dx (self, self->sw_head)) { +struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; +struct device *ndev =aq_nic_get_dev (self->aq_nic); + +if (buff->is_mapped) { +if (buff->is_sop) { +dma_unmap_single (ndev, buff->pa, buff->len, +DMA_TO_DEVICE ); + }else { +dma_unmap_page (ndev, buff->pa, buff->len, +DMA_TO_DEVICE ); + } + } + +if (buff->is_eop) { +if (buff->skb) +dev_kfree_skb_any (buff->skb); +else if (buff->xdpf) +xdp_return_frame (buff->xdpf); + } + } +} +static void aq_rx_checksum (struct aq_ring_s *self,struct aq_ring_buff_s *buff,struct sk_buff *skb)
drivers/net/ethernet/aquantia/atlantic/aq_ring.h +1 -0
@@ -202,6 +202,7 @@ void aq_ring_update_queue_state(struct aq_ring_s *ring);void aq_ring_queue_wake (struct aq_ring_s *ring);void aq_ring_queue_stop (struct aq_ring_s *ring);bool aq_ring_tx_clean (struct aq_ring_s *self); +void aq_ring_tx_deinit (struct aq_ring_s *self);int aq_xdp_xmit (struct net_device *dev,int num_frames,struct xdp_frame **frames,u32 flags); int aq_ring_rx_clean(struct aq_ring_s *self,
drivers/net/ethernet/aquantia/atlantic/aq_vec.c +1 -1
@@ -275,7 +275,7 @@ void aq_vec_deinit(struct aq_vec_s *self)for (i =0U ; self->tx_rings > i; ++i) { ring = self->ring[i]; -aq_ring_tx_clean (&ring[AQ_VEC_TX_ID ]); +aq_ring_tx_deinit (&ring[AQ_VEC_TX_ID ]);aq_ring_rx_deinit (&ring[AQ_VEC_RX_ID ]); }
Sasha Levin
maintainerPATCH 1/1 net: atlantic: free stranded TX buffers on ring deinit
From: Yangyu Chen <[email protected]>
[ Upstream commit 452636ea5410a96e02ebaaf80b21e3620b98e0dd ]
aq_vec_deinit() drains the TX rings with a single aq_ring_tx_clean() call, which frees at most AQ_CFG_TX_CLEAN_BUDGET (256) descriptors and stops at hw_head, which no longer moves once aq_vec_stop() has stopped the hardware and NAPI. Completed descriptors beyond the budget and everything still posted in [hw_head, sw_tail) keep their skb or xdp_frame when the interface goes down: aq_vec_ring_free() then frees the buffer ring and the references are lost for good.
Today this is a silent memory leak on every interface down under TX/XDP_TX load. With the conversion of the RX path to page_pool posted for net-next it becomes much more visible: XDP_TX frames carry fragment references on the RX ring's page_pool, so a single stranded frame keeps the pool's inflight count above zero forever. page_pool_destroy() then never completes, the pool is leaked together with its pages, and "page_pool_release_retry() stalled pool shutdown" is warned every 60 seconds from that point on, on every ifdown, XDP detach or ring resize under XDP_TX load.
Bring back aq_ring_tx_deinit() as it was before the removal and use it for teardown again, with one extension: TX rings can hold xdp_frames nowadays, so release those too. They are returned with xdp_return_frame() since this runs in process context.
--- .../net/ethernet/aquantia/atlantic/aq_ring.c | 25 +++++++++++++++++++ .../net/ethernet/aquantia/atlantic/aq_ring.h | 1 + .../net/ethernet/aquantia/atlantic/aq_vec.c | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-)
drivers/net/ethernet/aquantia/atlantic/aq_ring.c +25 -0
@@ -314,6 +314,31 @@ bool aq_ring_tx_clean(struct aq_ring_s *self)return !!budget; } +void aq_ring_tx_deinit (struct aq_ring_s *self) +{ +if (!self) +return ; + +for (; self->sw_head != self->sw_tail; + self->sw_head =aq_ring_next_dx (self, self->sw_head)) { +struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; +struct device *ndev =aq_nic_get_dev (self->aq_nic); + +if (buff->is_mapped) { +if (buff->is_sop) { +dma_unmap_single (ndev, buff->pa, buff->len, +DMA_TO_DEVICE ); + }else { +dma_unmap_page (ndev, buff->pa, buff->len, +DMA_TO_DEVICE ); + } + } + +if (buff->is_eop && buff->skb) +dev_kfree_skb_any (buff->skb); + } +} +static void aq_rx_checksum (struct aq_ring_s *self,struct aq_ring_buff_s *buff,struct sk_buff *skb)
drivers/net/ethernet/aquantia/atlantic/aq_ring.h +1 -0
@@ -182,6 +182,7 @@ void aq_ring_update_queue_state(struct aq_ring_s *ring);void aq_ring_queue_wake (struct aq_ring_s *ring);void aq_ring_queue_stop (struct aq_ring_s *ring);bool aq_ring_tx_clean (struct aq_ring_s *self); +void aq_ring_tx_deinit (struct aq_ring_s *self); int aq_ring_rx_clean(struct aq_ring_s *self, struct napi_struct *napi, int *work_done,
drivers/net/ethernet/aquantia/atlantic/aq_vec.c +1 -1
@@ -270,7 +270,7 @@ void aq_vec_deinit(struct aq_vec_s *self)for (i =0U ; self->tx_rings > i; ++i) { ring = self->ring[i]; -aq_ring_tx_clean (&ring[AQ_VEC_TX_ID ]); +aq_ring_tx_deinit (&ring[AQ_VEC_TX_ID ]);aq_ring_rx_deinit (&ring[AQ_VEC_RX_ID ]); }
Signature · 1 line
2.53.0