ramips: ethernet: use own page_frag_cache
authorFelix Fietkau <nbd@nbd.name>
Thu, 12 Jul 2018 15:19:07 +0000 (17:19 +0200)
committerFelix Fietkau <nbd@nbd.name>
Thu, 12 Jul 2018 16:43:53 +0000 (18:43 +0200)
Using the NAPI or netdev frag cache along with other drivers can lead to
32 KiB pages being held for a long time, despite only being used for
very few page fragment.
This can happen if the ethernet driver grabs one or two fragments for rx
ring refill, while other drivers use (and free up) the remaining
fragments. The 32 KiB higher-order page can only be freed once all users
have freed their fragments, which only happens after the rings of all
drivers holding the fragments have wrapped around.

Depending on the traffic patterns, this can waste a lot of memory and
look a lot like a memory leak

Signed-off-by: Felix Fietkau <nbd@nbd.name>
target/linux/ramips/files-4.14/drivers/net/ethernet/mediatek/mtk_eth_soc.c
target/linux/ramips/files-4.14/drivers/net/ethernet/mediatek/mtk_eth_soc.h

index 8dcc17774b50f68afd4ba92ac07d270dccc5d6d0..8bf17f629c2fa5f29f37eed0315a59c62f6046cf 100644 (file)
@@ -219,8 +219,9 @@ static inline void fe_set_txd(struct fe_tx_dma *txd, struct fe_tx_dma *dma_txd)
 
 static void fe_clean_rx(struct fe_priv *priv)
 {
 
 static void fe_clean_rx(struct fe_priv *priv)
 {
-       int i;
        struct fe_rx_ring *ring = &priv->rx_ring;
        struct fe_rx_ring *ring = &priv->rx_ring;
+       struct page *page;
+       int i;
 
        if (ring->rx_data) {
                for (i = 0; i < ring->rx_ring_size; i++)
 
        if (ring->rx_data) {
                for (i = 0; i < ring->rx_ring_size; i++)
@@ -244,6 +245,13 @@ static void fe_clean_rx(struct fe_priv *priv)
                                  ring->rx_phys);
                ring->rx_dma = NULL;
        }
                                  ring->rx_phys);
                ring->rx_dma = NULL;
        }
+
+       if (!ring->frag_cache.va)
+           return;
+
+       page = virt_to_page(ring->frag_cache.va);
+       __page_frag_cache_drain(page, ring->frag_cache.pagecnt_bias);
+       memset(&ring->frag_cache, 0, sizeof(ring->frag_cache));
 }
 
 static int fe_alloc_rx(struct fe_priv *priv)
 }
 
 static int fe_alloc_rx(struct fe_priv *priv)
@@ -258,7 +266,9 @@ static int fe_alloc_rx(struct fe_priv *priv)
                goto no_rx_mem;
 
        for (i = 0; i < ring->rx_ring_size; i++) {
                goto no_rx_mem;
 
        for (i = 0; i < ring->rx_ring_size; i++) {
-               ring->rx_data[i] = netdev_alloc_frag(ring->frag_size);
+               ring->rx_data[i] = page_frag_alloc(&ring->frag_cache,
+                                                  ring->frag_size,
+                                                  GFP_KERNEL);
                if (!ring->rx_data[i])
                        goto no_rx_mem;
        }
                if (!ring->rx_data[i])
                        goto no_rx_mem;
        }
@@ -881,7 +891,8 @@ static int fe_poll_rx(struct napi_struct *napi, int budget,
                        break;
 
                /* alloc new buffer */
                        break;
 
                /* alloc new buffer */
-               new_data = netdev_alloc_frag(ring->frag_size);
+               new_data = page_frag_alloc(&ring->frag_cache, ring->frag_size,
+                                          GFP_ATOMIC);
                if (unlikely(!new_data)) {
                        stats->rx_dropped++;
                        goto release_desc;
                if (unlikely(!new_data)) {
                        stats->rx_dropped++;
                        goto release_desc;
index 517d8ba4dc4bb3323188dce7ff7828d26be5b315..c42126116fc6c43b1b23a00fb379c4be9a6a6e15 100644 (file)
@@ -454,6 +454,7 @@ struct fe_tx_ring {
 };
 
 struct fe_rx_ring {
 };
 
 struct fe_rx_ring {
+       struct page_frag_cache frag_cache;
        struct fe_rx_dma *rx_dma;
        u8 **rx_data;
        dma_addr_t rx_phys;
        struct fe_rx_dma *rx_dma;
        u8 **rx_data;
        dma_addr_t rx_phys;