Commit e8a492cd authored by David S. Miller's avatar David S. Miller

Merge branch 'Avoid-local_irq_save-and-use-napi_alloc_frag-where-possible'

Sebastian Andrzej says:

====================
Avoid local_irq_save() and use napi_alloc_frag() where possible

The first two patches remove local_irq_save() around
`netdev_alloc_cache' which does not work on -RT. Besides helping -RT it
whould benefit the users of the function since they can avoid disabling
interrupts and save a few cycles.
The remaining patches are from a time when I tried to remove
`netdev_alloc_cache' but then noticed that we still have non-NAPI
drivers using netdev_alloc_skb() and I dropped that idea. Using
napi_alloc_frag() over netdev_alloc_frag() would skip the not required
local_bh_disable() around the allocation.

v1…v2:
  - 1/7 + 2/7 use now "(in_irq() || irqs_disabled())" instead just
    "irqs_disabled()" to align with __dev_kfree_skb_any(). Pointed out
    by Eric Dumazet.

  - 6/7 has a typo less. Pointed out by Sergei Shtylyov.

  - 3/7 + 4/7 added acks from Ioana Radulescu.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9e49fe4d 6dcdd884
...@@ -684,7 +684,7 @@ static void *bnx2x_frag_alloc(const struct bnx2x_fastpath *fp, gfp_t gfp_mask) ...@@ -684,7 +684,7 @@ static void *bnx2x_frag_alloc(const struct bnx2x_fastpath *fp, gfp_t gfp_mask)
if (unlikely(gfpflags_allow_blocking(gfp_mask))) if (unlikely(gfpflags_allow_blocking(gfp_mask)))
return (void *)__get_free_page(gfp_mask); return (void *)__get_free_page(gfp_mask);
return netdev_alloc_frag(fp->rx_frag_size); return napi_alloc_frag(fp->rx_frag_size);
} }
return kmalloc(fp->rx_buf_size + NET_SKB_PAD, gfp_mask); return kmalloc(fp->rx_buf_size + NET_SKB_PAD, gfp_mask);
......
...@@ -6710,7 +6710,7 @@ static int tg3_alloc_rx_data(struct tg3 *tp, struct tg3_rx_prodring_set *tpr, ...@@ -6710,7 +6710,7 @@ static int tg3_alloc_rx_data(struct tg3 *tp, struct tg3_rx_prodring_set *tpr,
skb_size = SKB_DATA_ALIGN(data_size + TG3_RX_OFFSET(tp)) + skb_size = SKB_DATA_ALIGN(data_size + TG3_RX_OFFSET(tp)) +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
if (skb_size <= PAGE_SIZE) { if (skb_size <= PAGE_SIZE) {
data = netdev_alloc_frag(skb_size); data = napi_alloc_frag(skb_size);
*frag_size = skb_size; *frag_size = skb_size;
} else { } else {
data = kmalloc(skb_size, GFP_ATOMIC); data = kmalloc(skb_size, GFP_ATOMIC);
......
...@@ -555,7 +555,7 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv, ...@@ -555,7 +555,7 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv,
/* Prepare the HW SGT structure */ /* Prepare the HW SGT structure */
sgt_buf_size = priv->tx_data_offset + sgt_buf_size = priv->tx_data_offset +
sizeof(struct dpaa2_sg_entry) * num_dma_bufs; sizeof(struct dpaa2_sg_entry) * num_dma_bufs;
sgt_buf = netdev_alloc_frag(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN); sgt_buf = napi_alloc_frag(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN);
if (unlikely(!sgt_buf)) { if (unlikely(!sgt_buf)) {
err = -ENOMEM; err = -ENOMEM;
goto sgt_buf_alloc_failed; goto sgt_buf_alloc_failed;
...@@ -997,13 +997,6 @@ static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid) ...@@ -997,13 +997,6 @@ static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid)
int i, j; int i, j;
int new_count; int new_count;
/* This is the lazy seeding of Rx buffer pools.
* dpaa2_add_bufs() is also used on the Rx hotpath and calls
* napi_alloc_frag(). The trouble with that is that it in turn ends up
* calling this_cpu_ptr(), which mandates execution in atomic context.
* Rather than splitting up the code, do a one-off preempt disable.
*/
preempt_disable();
for (j = 0; j < priv->num_channels; j++) { for (j = 0; j < priv->num_channels; j++) {
for (i = 0; i < DPAA2_ETH_NUM_BUFS; for (i = 0; i < DPAA2_ETH_NUM_BUFS;
i += DPAA2_ETH_BUFS_PER_CMD) { i += DPAA2_ETH_BUFS_PER_CMD) {
...@@ -1011,12 +1004,10 @@ static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid) ...@@ -1011,12 +1004,10 @@ static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid)
priv->channel[j]->buf_count += new_count; priv->channel[j]->buf_count += new_count;
if (new_count < DPAA2_ETH_BUFS_PER_CMD) { if (new_count < DPAA2_ETH_BUFS_PER_CMD) {
preempt_enable();
return -ENOMEM; return -ENOMEM;
} }
} }
} }
preempt_enable();
return 0; return 0;
} }
......
...@@ -1119,7 +1119,7 @@ static void mvneta_bm_update_mtu(struct mvneta_port *pp, int mtu) ...@@ -1119,7 +1119,7 @@ static void mvneta_bm_update_mtu(struct mvneta_port *pp, int mtu)
SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(bm_pool->pkt_size)); SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(bm_pool->pkt_size));
/* Fill entire long pool */ /* Fill entire long pool */
num = hwbm_pool_add(hwbm_pool, hwbm_pool->size, GFP_ATOMIC); num = hwbm_pool_add(hwbm_pool, hwbm_pool->size);
if (num != hwbm_pool->size) { if (num != hwbm_pool->size) {
WARN(1, "pool %d: %d of %d allocated\n", WARN(1, "pool %d: %d of %d allocated\n",
bm_pool->id, num, hwbm_pool->size); bm_pool->id, num, hwbm_pool->size);
......
...@@ -190,7 +190,7 @@ struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id, ...@@ -190,7 +190,7 @@ struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
hwbm_pool->construct = mvneta_bm_construct; hwbm_pool->construct = mvneta_bm_construct;
hwbm_pool->priv = new_pool; hwbm_pool->priv = new_pool;
spin_lock_init(&hwbm_pool->lock); mutex_init(&hwbm_pool->buf_lock);
/* Create new pool */ /* Create new pool */
err = mvneta_bm_pool_create(priv, new_pool); err = mvneta_bm_pool_create(priv, new_pool);
...@@ -201,7 +201,7 @@ struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id, ...@@ -201,7 +201,7 @@ struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
} }
/* Allocate buffers for this pool */ /* Allocate buffers for this pool */
num = hwbm_pool_add(hwbm_pool, hwbm_pool->size, GFP_ATOMIC); num = hwbm_pool_add(hwbm_pool, hwbm_pool->size);
if (num != hwbm_pool->size) { if (num != hwbm_pool->size) {
WARN(1, "pool %d: %d of %d allocated\n", WARN(1, "pool %d: %d of %d allocated\n",
new_pool->id, num, hwbm_pool->size); new_pool->id, num, hwbm_pool->size);
......
...@@ -12,18 +12,18 @@ struct hwbm_pool { ...@@ -12,18 +12,18 @@ struct hwbm_pool {
/* constructor called during alocation */ /* constructor called during alocation */
int (*construct)(struct hwbm_pool *bm_pool, void *buf); int (*construct)(struct hwbm_pool *bm_pool, void *buf);
/* protect acces to the buffer counter*/ /* protect acces to the buffer counter*/
spinlock_t lock; struct mutex buf_lock;
/* private data */ /* private data */
void *priv; void *priv;
}; };
#ifdef CONFIG_HWBM #ifdef CONFIG_HWBM
void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf); void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf);
int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp); int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp);
int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp); int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num);
#else #else
void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf) {} void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf) {}
int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp) { return 0; } int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp) { return 0; }
int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp) int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num)
{ return 0; } { return 0; }
#endif /* CONFIG_HWBM */ #endif /* CONFIG_HWBM */
#endif /* _HWBM_H */ #endif /* _HWBM_H */
...@@ -43,34 +43,33 @@ int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp) ...@@ -43,34 +43,33 @@ int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp)
} }
EXPORT_SYMBOL_GPL(hwbm_pool_refill); EXPORT_SYMBOL_GPL(hwbm_pool_refill);
int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp) int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num)
{ {
int err, i; int err, i;
unsigned long flags;
spin_lock_irqsave(&bm_pool->lock, flags); mutex_lock(&bm_pool->buf_lock);
if (bm_pool->buf_num == bm_pool->size) { if (bm_pool->buf_num == bm_pool->size) {
pr_warn("pool already filled\n"); pr_warn("pool already filled\n");
spin_unlock_irqrestore(&bm_pool->lock, flags); mutex_unlock(&bm_pool->buf_lock);
return bm_pool->buf_num; return bm_pool->buf_num;
} }
if (buf_num + bm_pool->buf_num > bm_pool->size) { if (buf_num + bm_pool->buf_num > bm_pool->size) {
pr_warn("cannot allocate %d buffers for pool\n", pr_warn("cannot allocate %d buffers for pool\n",
buf_num); buf_num);
spin_unlock_irqrestore(&bm_pool->lock, flags); mutex_unlock(&bm_pool->buf_lock);
return 0; return 0;
} }
if ((buf_num + bm_pool->buf_num) < bm_pool->buf_num) { if ((buf_num + bm_pool->buf_num) < bm_pool->buf_num) {
pr_warn("Adding %d buffers to the %d current buffers will overflow\n", pr_warn("Adding %d buffers to the %d current buffers will overflow\n",
buf_num, bm_pool->buf_num); buf_num, bm_pool->buf_num);
spin_unlock_irqrestore(&bm_pool->lock, flags); mutex_unlock(&bm_pool->buf_lock);
return 0; return 0;
} }
for (i = 0; i < buf_num; i++) { for (i = 0; i < buf_num; i++) {
err = hwbm_pool_refill(bm_pool, gfp); err = hwbm_pool_refill(bm_pool, GFP_KERNEL);
if (err < 0) if (err < 0)
break; break;
} }
...@@ -79,7 +78,7 @@ int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp) ...@@ -79,7 +78,7 @@ int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
bm_pool->buf_num += i; bm_pool->buf_num += i;
pr_debug("hwpm pool: %d of %d buffers added\n", i, buf_num); pr_debug("hwpm pool: %d of %d buffers added\n", i, buf_num);
spin_unlock_irqrestore(&bm_pool->lock, flags); mutex_unlock(&bm_pool->buf_lock);
return i; return i;
} }
......
...@@ -366,19 +366,21 @@ struct napi_alloc_cache { ...@@ -366,19 +366,21 @@ struct napi_alloc_cache {
static DEFINE_PER_CPU(struct page_frag_cache, netdev_alloc_cache); static DEFINE_PER_CPU(struct page_frag_cache, netdev_alloc_cache);
static DEFINE_PER_CPU(struct napi_alloc_cache, napi_alloc_cache); static DEFINE_PER_CPU(struct napi_alloc_cache, napi_alloc_cache);
static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) static void *__napi_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
{ {
struct page_frag_cache *nc; struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache);
unsigned long flags;
void *data;
local_irq_save(flags); return page_frag_alloc(&nc->page, fragsz, gfp_mask);
nc = this_cpu_ptr(&netdev_alloc_cache);
data = page_frag_alloc(nc, fragsz, gfp_mask);
local_irq_restore(flags);
return data;
} }
void *napi_alloc_frag(unsigned int fragsz)
{
fragsz = SKB_DATA_ALIGN(fragsz);
return __napi_alloc_frag(fragsz, GFP_ATOMIC);
}
EXPORT_SYMBOL(napi_alloc_frag);
/** /**
* netdev_alloc_frag - allocate a page fragment * netdev_alloc_frag - allocate a page fragment
* @fragsz: fragment size * @fragsz: fragment size
...@@ -388,26 +390,21 @@ static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) ...@@ -388,26 +390,21 @@ static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
*/ */
void *netdev_alloc_frag(unsigned int fragsz) void *netdev_alloc_frag(unsigned int fragsz)
{ {
fragsz = SKB_DATA_ALIGN(fragsz); struct page_frag_cache *nc;
void *data;
return __netdev_alloc_frag(fragsz, GFP_ATOMIC);
}
EXPORT_SYMBOL(netdev_alloc_frag);
static void *__napi_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
{
struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache);
return page_frag_alloc(&nc->page, fragsz, gfp_mask);
}
void *napi_alloc_frag(unsigned int fragsz)
{
fragsz = SKB_DATA_ALIGN(fragsz); fragsz = SKB_DATA_ALIGN(fragsz);
if (in_irq() || irqs_disabled()) {
return __napi_alloc_frag(fragsz, GFP_ATOMIC); nc = this_cpu_ptr(&netdev_alloc_cache);
data = page_frag_alloc(nc, fragsz, GFP_ATOMIC);
} else {
local_bh_disable();
data = __napi_alloc_frag(fragsz, GFP_ATOMIC);
local_bh_enable();
}
return data;
} }
EXPORT_SYMBOL(napi_alloc_frag); EXPORT_SYMBOL(netdev_alloc_frag);
/** /**
* __netdev_alloc_skb - allocate an skbuff for rx on a specific device * __netdev_alloc_skb - allocate an skbuff for rx on a specific device
...@@ -426,7 +423,6 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len, ...@@ -426,7 +423,6 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len,
gfp_t gfp_mask) gfp_t gfp_mask)
{ {
struct page_frag_cache *nc; struct page_frag_cache *nc;
unsigned long flags;
struct sk_buff *skb; struct sk_buff *skb;
bool pfmemalloc; bool pfmemalloc;
void *data; void *data;
...@@ -447,13 +443,17 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len, ...@@ -447,13 +443,17 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len,
if (sk_memalloc_socks()) if (sk_memalloc_socks())
gfp_mask |= __GFP_MEMALLOC; gfp_mask |= __GFP_MEMALLOC;
local_irq_save(flags); if (in_irq() || irqs_disabled()) {
nc = this_cpu_ptr(&netdev_alloc_cache);
nc = this_cpu_ptr(&netdev_alloc_cache); data = page_frag_alloc(nc, len, gfp_mask);
data = page_frag_alloc(nc, len, gfp_mask); pfmemalloc = nc->pfmemalloc;
pfmemalloc = nc->pfmemalloc; } else {
local_bh_disable();
local_irq_restore(flags); nc = this_cpu_ptr(&napi_alloc_cache.page);
data = page_frag_alloc(nc, len, gfp_mask);
pfmemalloc = nc->pfmemalloc;
local_bh_enable();
}
if (unlikely(!data)) if (unlikely(!data))
return NULL; return NULL;
......
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