Commit 2af6106a authored by Jose Abreu's avatar Jose Abreu Committed by David S. Miller

net: stmmac: Introducing support for Page Pool

Mapping and unmapping DMA region is an high bottleneck in stmmac driver,
specially in the RX path.

This commit introduces support for Page Pool API and uses it in all RX
queues. With this change, we get more stable troughput and some increase
of banwidth with iperf:
	- MAC1000 - 950 Mbps
	- XGMAC: 9.22 Gbps

Changes from v3:
	- Use page_pool_destroy() (Ilias)
Changes from v2:
	- Uncoditionally call page_pool_free() (Jesper)
Changes from v1:
	- Use page_pool_get_dma_addr() (Jesper)
	- Add a comment (Jesper)
	- Add page_pool_free() call (Jesper)
	- Reintroduce sync_single_for_device (Arnd / Ilias)
Signed-off-by: default avatarJose Abreu <joabreu@synopsys.com>
Acked-by: default avatarIlias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 06a80a7d
...@@ -3,6 +3,7 @@ config STMMAC_ETH ...@@ -3,6 +3,7 @@ config STMMAC_ETH
tristate "STMicroelectronics Multi-Gigabit Ethernet driver" tristate "STMicroelectronics Multi-Gigabit Ethernet driver"
depends on HAS_IOMEM && HAS_DMA depends on HAS_IOMEM && HAS_DMA
select MII select MII
select PAGE_POOL
select PHYLINK select PHYLINK
select CRC32 select CRC32
imply PTP_1588_CLOCK imply PTP_1588_CLOCK
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/ptp_clock_kernel.h> #include <linux/ptp_clock_kernel.h>
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <net/page_pool.h>
struct stmmac_resources { struct stmmac_resources {
void __iomem *addr; void __iomem *addr;
...@@ -54,14 +55,19 @@ struct stmmac_tx_queue { ...@@ -54,14 +55,19 @@ struct stmmac_tx_queue {
u32 mss; u32 mss;
}; };
struct stmmac_rx_buffer {
struct page *page;
dma_addr_t addr;
};
struct stmmac_rx_queue { struct stmmac_rx_queue {
u32 rx_count_frames; u32 rx_count_frames;
u32 queue_index; u32 queue_index;
struct page_pool *page_pool;
struct stmmac_rx_buffer *buf_pool;
struct stmmac_priv *priv_data; struct stmmac_priv *priv_data;
struct dma_extended_desc *dma_erx; struct dma_extended_desc *dma_erx;
struct dma_desc *dma_rx ____cacheline_aligned_in_smp; struct dma_desc *dma_rx ____cacheline_aligned_in_smp;
struct sk_buff **rx_skbuff;
dma_addr_t *rx_skbuff_dma;
unsigned int cur_rx; unsigned int cur_rx;
unsigned int dirty_rx; unsigned int dirty_rx;
u32 rx_zeroc_thresh; u32 rx_zeroc_thresh;
......
...@@ -1197,26 +1197,14 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, ...@@ -1197,26 +1197,14 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
int i, gfp_t flags, u32 queue) int i, gfp_t flags, u32 queue)
{ {
struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
struct sk_buff *skb; struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); buf->page = page_pool_dev_alloc_pages(rx_q->page_pool);
if (!skb) { if (!buf->page)
netdev_err(priv->dev,
"%s: Rx init fails; skb is NULL\n", __func__);
return -ENOMEM; return -ENOMEM;
}
rx_q->rx_skbuff[i] = skb;
rx_q->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
priv->dma_buf_sz,
DMA_FROM_DEVICE);
if (dma_mapping_error(priv->device, rx_q->rx_skbuff_dma[i])) {
netdev_err(priv->dev, "%s: DMA mapping error\n", __func__);
dev_kfree_skb_any(skb);
return -EINVAL;
}
stmmac_set_desc_addr(priv, p, rx_q->rx_skbuff_dma[i]);
buf->addr = page_pool_get_dma_addr(buf->page);
stmmac_set_desc_addr(priv, p, buf->addr);
if (priv->dma_buf_sz == BUF_SIZE_16KiB) if (priv->dma_buf_sz == BUF_SIZE_16KiB)
stmmac_init_desc3(priv, p); stmmac_init_desc3(priv, p);
...@@ -1232,13 +1220,11 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, ...@@ -1232,13 +1220,11 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i)
{ {
struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
if (rx_q->rx_skbuff[i]) { if (buf->page)
dma_unmap_single(priv->device, rx_q->rx_skbuff_dma[i], page_pool_put_page(rx_q->page_pool, buf->page, false);
priv->dma_buf_sz, DMA_FROM_DEVICE); buf->page = NULL;
dev_kfree_skb_any(rx_q->rx_skbuff[i]);
}
rx_q->rx_skbuff[i] = NULL;
} }
/** /**
...@@ -1321,10 +1307,6 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) ...@@ -1321,10 +1307,6 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags)
queue); queue);
if (ret) if (ret)
goto err_init_rx_buffers; goto err_init_rx_buffers;
netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n",
rx_q->rx_skbuff[i], rx_q->rx_skbuff[i]->data,
(unsigned int)rx_q->rx_skbuff_dma[i]);
} }
rx_q->cur_rx = 0; rx_q->cur_rx = 0;
...@@ -1498,8 +1480,11 @@ static void free_dma_rx_desc_resources(struct stmmac_priv *priv) ...@@ -1498,8 +1480,11 @@ static void free_dma_rx_desc_resources(struct stmmac_priv *priv)
sizeof(struct dma_extended_desc), sizeof(struct dma_extended_desc),
rx_q->dma_erx, rx_q->dma_rx_phy); rx_q->dma_erx, rx_q->dma_rx_phy);
kfree(rx_q->rx_skbuff_dma); kfree(rx_q->buf_pool);
kfree(rx_q->rx_skbuff); if (rx_q->page_pool) {
page_pool_request_shutdown(rx_q->page_pool);
page_pool_destroy(rx_q->page_pool);
}
} }
} }
...@@ -1551,20 +1536,29 @@ static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv) ...@@ -1551,20 +1536,29 @@ static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv)
/* RX queues buffers and DMA */ /* RX queues buffers and DMA */
for (queue = 0; queue < rx_count; queue++) { for (queue = 0; queue < rx_count; queue++) {
struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
struct page_pool_params pp_params = { 0 };
rx_q->queue_index = queue; rx_q->queue_index = queue;
rx_q->priv_data = priv; rx_q->priv_data = priv;
rx_q->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, pp_params.flags = PP_FLAG_DMA_MAP;
sizeof(dma_addr_t), pp_params.pool_size = DMA_RX_SIZE;
GFP_KERNEL); pp_params.order = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE);
if (!rx_q->rx_skbuff_dma) pp_params.nid = dev_to_node(priv->device);
pp_params.dev = priv->device;
pp_params.dma_dir = DMA_FROM_DEVICE;
rx_q->page_pool = page_pool_create(&pp_params);
if (IS_ERR(rx_q->page_pool)) {
ret = PTR_ERR(rx_q->page_pool);
rx_q->page_pool = NULL;
goto err_dma; goto err_dma;
}
rx_q->rx_skbuff = kmalloc_array(DMA_RX_SIZE, rx_q->buf_pool = kmalloc_array(DMA_RX_SIZE,
sizeof(struct sk_buff *), sizeof(*rx_q->buf_pool),
GFP_KERNEL); GFP_KERNEL);
if (!rx_q->rx_skbuff) if (!rx_q->buf_pool)
goto err_dma; goto err_dma;
if (priv->extend_desc) { if (priv->extend_desc) {
...@@ -3286,9 +3280,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) ...@@ -3286,9 +3280,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
int dirty = stmmac_rx_dirty(priv, queue); int dirty = stmmac_rx_dirty(priv, queue);
unsigned int entry = rx_q->dirty_rx; unsigned int entry = rx_q->dirty_rx;
int bfsize = priv->dma_buf_sz;
while (dirty-- > 0) { while (dirty-- > 0) {
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[entry];
struct dma_desc *p; struct dma_desc *p;
bool use_rx_wd; bool use_rx_wd;
...@@ -3297,49 +3290,22 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) ...@@ -3297,49 +3290,22 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
else else
p = rx_q->dma_rx + entry; p = rx_q->dma_rx + entry;
if (likely(!rx_q->rx_skbuff[entry])) { if (!buf->page) {
struct sk_buff *skb; buf->page = page_pool_dev_alloc_pages(rx_q->page_pool);
if (!buf->page)
skb = netdev_alloc_skb_ip_align(priv->dev, bfsize);
if (unlikely(!skb)) {
/* so for a while no zero-copy! */
rx_q->rx_zeroc_thresh = STMMAC_RX_THRESH;
if (unlikely(net_ratelimit()))
dev_err(priv->device,
"fail to alloc skb entry %d\n",
entry);
break;
}
rx_q->rx_skbuff[entry] = skb;
rx_q->rx_skbuff_dma[entry] =
dma_map_single(priv->device, skb->data, bfsize,
DMA_FROM_DEVICE);
if (dma_mapping_error(priv->device,
rx_q->rx_skbuff_dma[entry])) {
netdev_err(priv->dev, "Rx DMA map failed\n");
dev_kfree_skb(skb);
break; break;
}
stmmac_set_desc_addr(priv, p, rx_q->rx_skbuff_dma[entry]);
stmmac_refill_desc3(priv, rx_q, p);
if (rx_q->rx_zeroc_thresh > 0)
rx_q->rx_zeroc_thresh--;
netif_dbg(priv, rx_status, priv->dev,
"refill entry #%d\n", entry);
} }
dma_wmb();
buf->addr = page_pool_get_dma_addr(buf->page);
stmmac_set_desc_addr(priv, p, buf->addr);
stmmac_refill_desc3(priv, rx_q, p);
rx_q->rx_count_frames++; rx_q->rx_count_frames++;
rx_q->rx_count_frames %= priv->rx_coal_frames; rx_q->rx_count_frames %= priv->rx_coal_frames;
use_rx_wd = priv->use_riwt && rx_q->rx_count_frames; use_rx_wd = priv->use_riwt && rx_q->rx_count_frames;
stmmac_set_rx_owner(priv, p, use_rx_wd);
dma_wmb(); dma_wmb();
stmmac_set_rx_owner(priv, p, use_rx_wd);
entry = STMMAC_GET_ENTRY(entry, DMA_RX_SIZE); entry = STMMAC_GET_ENTRY(entry, DMA_RX_SIZE);
} }
...@@ -3364,9 +3330,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) ...@@ -3364,9 +3330,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
unsigned int next_entry = rx_q->cur_rx; unsigned int next_entry = rx_q->cur_rx;
int coe = priv->hw->rx_csum; int coe = priv->hw->rx_csum;
unsigned int count = 0; unsigned int count = 0;
bool xmac;
xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
if (netif_msg_rx_status(priv)) { if (netif_msg_rx_status(priv)) {
void *rx_head; void *rx_head;
...@@ -3380,11 +3343,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) ...@@ -3380,11 +3343,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
stmmac_display_ring(priv, rx_head, DMA_RX_SIZE, true); stmmac_display_ring(priv, rx_head, DMA_RX_SIZE, true);
} }
while (count < limit) { while (count < limit) {
struct stmmac_rx_buffer *buf;
struct dma_desc *np, *p;
int entry, status; int entry, status;
struct dma_desc *p;
struct dma_desc *np;
entry = next_entry; entry = next_entry;
buf = &rx_q->buf_pool[entry];
if (priv->extend_desc) if (priv->extend_desc)
p = (struct dma_desc *)(rx_q->dma_erx + entry); p = (struct dma_desc *)(rx_q->dma_erx + entry);
...@@ -3414,20 +3378,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) ...@@ -3414,20 +3378,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
stmmac_rx_extended_status(priv, &priv->dev->stats, stmmac_rx_extended_status(priv, &priv->dev->stats,
&priv->xstats, rx_q->dma_erx + entry); &priv->xstats, rx_q->dma_erx + entry);
if (unlikely(status == discard_frame)) { if (unlikely(status == discard_frame)) {
page_pool_recycle_direct(rx_q->page_pool, buf->page);
priv->dev->stats.rx_errors++; priv->dev->stats.rx_errors++;
if (priv->hwts_rx_en && !priv->extend_desc) { buf->page = NULL;
/* DESC2 & DESC3 will be overwritten by device
* with timestamp value, hence reinitialize
* them in stmmac_rx_refill() function so that
* device can reuse it.
*/
dev_kfree_skb_any(rx_q->rx_skbuff[entry]);
rx_q->rx_skbuff[entry] = NULL;
dma_unmap_single(priv->device,
rx_q->rx_skbuff_dma[entry],
priv->dma_buf_sz,
DMA_FROM_DEVICE);
}
} else { } else {
struct sk_buff *skb; struct sk_buff *skb;
int frame_len; int frame_len;
...@@ -3467,58 +3420,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) ...@@ -3467,58 +3420,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
frame_len, status); frame_len, status);
} }
/* The zero-copy is always used for all the sizes skb = netdev_alloc_skb_ip_align(priv->dev, frame_len);
* in case of GMAC4 because it needs if (unlikely(!skb)) {
* to refill the used descriptors, always. priv->dev->stats.rx_dropped++;
*/ continue;
if (unlikely(!xmac &&
((frame_len < priv->rx_copybreak) ||
stmmac_rx_threshold_count(rx_q)))) {
skb = netdev_alloc_skb_ip_align(priv->dev,
frame_len);
if (unlikely(!skb)) {
if (net_ratelimit())
dev_warn(priv->device,
"packet dropped\n");
priv->dev->stats.rx_dropped++;
continue;
}
dma_sync_single_for_cpu(priv->device,
rx_q->rx_skbuff_dma
[entry], frame_len,
DMA_FROM_DEVICE);
skb_copy_to_linear_data(skb,
rx_q->
rx_skbuff[entry]->data,
frame_len);
skb_put(skb, frame_len);
dma_sync_single_for_device(priv->device,
rx_q->rx_skbuff_dma
[entry], frame_len,
DMA_FROM_DEVICE);
} else {
skb = rx_q->rx_skbuff[entry];
if (unlikely(!skb)) {
if (net_ratelimit())
netdev_err(priv->dev,
"%s: Inconsistent Rx chain\n",
priv->dev->name);
priv->dev->stats.rx_dropped++;
continue;
}
prefetch(skb->data - NET_IP_ALIGN);
rx_q->rx_skbuff[entry] = NULL;
rx_q->rx_zeroc_thresh++;
skb_put(skb, frame_len);
dma_unmap_single(priv->device,
rx_q->rx_skbuff_dma[entry],
priv->dma_buf_sz,
DMA_FROM_DEVICE);
} }
dma_sync_single_for_cpu(priv->device, buf->addr,
frame_len, DMA_FROM_DEVICE);
skb_copy_to_linear_data(skb, page_address(buf->page),
frame_len);
skb_put(skb, frame_len);
dma_sync_single_for_device(priv->device, buf->addr,
frame_len, DMA_FROM_DEVICE);
if (netif_msg_pktdata(priv)) { if (netif_msg_pktdata(priv)) {
netdev_dbg(priv->dev, "frame received (%dbytes)", netdev_dbg(priv->dev, "frame received (%dbytes)",
frame_len); frame_len);
...@@ -3538,6 +3453,10 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) ...@@ -3538,6 +3453,10 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
napi_gro_receive(&ch->rx_napi, skb); napi_gro_receive(&ch->rx_napi, skb);
/* Data payload copied into SKB, page ready for recycle */
page_pool_recycle_direct(rx_q->page_pool, buf->page);
buf->page = NULL;
priv->dev->stats.rx_packets++; priv->dev->stats.rx_packets++;
priv->dev->stats.rx_bytes += frame_len; priv->dev->stats.rx_bytes += frame_len;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment