summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2018-07-12 17:19:07 +0200
committerFelix Fietkau <nbd@nbd.name>2018-07-13 16:39:49 +0200
commit16a9ddfe64e2dc80328c3b7ac81d5d87a7da9233 (patch)
treea736b9332c93db2d0b9292c6f5ec33522d8c3651
parent0e6cfb6919149267202c6dbaf9e5aab3ab6d6b1d (diff)
downloadmtk-20170518-16a9ddfe64e2dc80328c3b7ac81d5d87a7da9233.zip
mtk-20170518-16a9ddfe64e2dc80328c3b7ac81d5d87a7da9233.tar.gz
mtk-20170518-16a9ddfe64e2dc80328c3b7ac81d5d87a7da9233.tar.bz2
ramips: ethernet: use own page_frag_cache
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>
-rw-r--r--target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.c17
-rw-r--r--target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.h1
2 files changed, 15 insertions, 3 deletions
diff --git a/target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.c b/target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.c
index 8dde2b8..6eae661 100644
--- a/target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.c
+++ b/target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.c
@@ -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)
{
- int i;
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++)
@@ -244,6 +245,13 @@ static void fe_clean_rx(struct fe_priv *priv)
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)
@@ -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++) {
- 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;
}
@@ -846,7 +856,8 @@ static int fe_poll_rx(struct napi_struct *napi, int budget,
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;
diff --git a/target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.h b/target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.h
index dfaa5fd..5203f5c 100644
--- a/target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.h
+++ b/target/linux/ramips/files-4.14/drivers/net/ethernet/mtk/mtk_eth_soc.h
@@ -461,6 +461,7 @@ struct fe_tx_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;