]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: atlantic: free RX pages of consumed but not refilled buffers
authorYangyu Chen <cyy@cyyself.name>
Sun, 2 Aug 2026 15:46:38 +0000 (23:46 +0800)
committerJakub Kicinski <kuba@kernel.org>
Wed, 5 Aug 2026 01:15:34 +0000 (18:15 -0700)
aq_ring_rx_deinit() only walks [sw_head, sw_tail), the region posted to
hardware. Since the page reuse strategy was added, a cleaned RX buffer
keeps its page (and its DMA mapping) in the ring for reuse, and refill
is batched: aq_ring_rx_fill() returns early until AQ_CFG_RX_REFILL_THRES
slots are free. Slots that were consumed but not yet reposted therefore
sit in the complementary [sw_tail, sw_head) gap with a live page, and
the deinit walk never visits them: up to a refill batch worth of pages
and DMA mappings leak on every interface down.

Walk the whole ring instead and release whatever is still there. Also
bail out if the buffer ring is already gone: a partial
aq_ptp_ring_alloc() failure frees the ring but leaves aq_nic set, so
aq_ptp_ring_deinit() still gets here on the unwind path.

Cc: stable@vger.kernel.org # v5.2+
Fixes: 46f4c29d9de6 ("net: aquantia: optimize rx performance by page reuse strategy")
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_607CBA8237DA438E36B844318B21538DE008@qq.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/aquantia/atlantic/aq_ring.c

index 81685a4dc5a6d35e29d77f687843f32bdf9d2c1d..e1193c6719d9d6e01ad95e3b80950b70a88d510f 100644 (file)
@@ -950,15 +950,29 @@ err_exit:
 
 void aq_ring_rx_deinit(struct aq_ring_s *self)
 {
-       if (!self)
+       unsigned int i;
+
+       if (!self || !self->buff_ring)
                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];
+       /* Release every page still owned by the ring.
+        *
+        * Walking [sw_head, sw_tail) is not enough: refill is batched
+        * (aq_ring_rx_fill() waits for AQ_CFG_RX_REFILL_THRES free slots),
+        * so slots that were cleaned but not yet reposted accumulate in the
+        * [sw_tail, sw_head) gap, and they keep their page for reuse. Walk
+        * the whole ring and release whatever is left.
+        */
+       for (i = 0; i < self->size; i++) {
+               struct aq_ring_buff_s *buff = &self->buff_ring[i];
+
+               if (!buff->rxdata.page)
+                       continue;
 
                aq_free_rxpage(&buff->rxdata, aq_nic_get_dev(self->aq_nic));
        }
+
+       self->sw_head = self->sw_tail;
 }
 
 void aq_ring_free(struct aq_ring_s *self)