mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
net: atlantic: free stranded TX buffers on ring deinit
commit452636ea54upstream. 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. Fixes:eb36bedf28("net: aquantia: remove function aq_ring_tx_deinit") Cc: stable@vger.kernel.org # v4.11+ Reviewed-by: Sukhdeep Singh <sukhdeeps@marvell.com> Signed-off-by: Yangyu Chen <cyy@cyyself.name> Acked-by: Mina Almasry <almasrymina@google.com> Link: https://patch.msgid.link/tencent_EEDC35FAF2750A3A6A0B39BAE0E2C484860A@qq.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
0424186d57
commit
b13202d401
@@ -336,6 +336,35 @@ out:
|
||||
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)
|
||||
|
||||
@@ -199,6 +199,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,
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user