diff options
-rw-r--r-- | net/core/skbuff.c | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 506f678e9d95..8b6d38fdb443 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -296,9 +296,12 @@ EXPORT_SYMBOL(build_skb); | |||
296 | struct netdev_alloc_cache { | 296 | struct netdev_alloc_cache { |
297 | struct page *page; | 297 | struct page *page; |
298 | unsigned int offset; | 298 | unsigned int offset; |
299 | unsigned int pagecnt_bias; | ||
299 | }; | 300 | }; |
300 | static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); | 301 | static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); |
301 | 302 | ||
303 | #define NETDEV_PAGECNT_BIAS (PAGE_SIZE / SMP_CACHE_BYTES) | ||
304 | |||
302 | /** | 305 | /** |
303 | * netdev_alloc_frag - allocate a page fragment | 306 | * netdev_alloc_frag - allocate a page fragment |
304 | * @fragsz: fragment size | 307 | * @fragsz: fragment size |
@@ -317,17 +320,26 @@ void *netdev_alloc_frag(unsigned int fragsz) | |||
317 | if (unlikely(!nc->page)) { | 320 | if (unlikely(!nc->page)) { |
318 | refill: | 321 | refill: |
319 | nc->page = alloc_page(GFP_ATOMIC | __GFP_COLD); | 322 | nc->page = alloc_page(GFP_ATOMIC | __GFP_COLD); |
323 | if (unlikely(!nc->page)) | ||
324 | goto end; | ||
325 | recycle: | ||
326 | atomic_set(&nc->page->_count, NETDEV_PAGECNT_BIAS); | ||
327 | nc->pagecnt_bias = NETDEV_PAGECNT_BIAS; | ||
320 | nc->offset = 0; | 328 | nc->offset = 0; |
321 | } | 329 | } |
322 | if (likely(nc->page)) { | 330 | |
323 | if (nc->offset + fragsz > PAGE_SIZE) { | 331 | if (nc->offset + fragsz > PAGE_SIZE) { |
324 | put_page(nc->page); | 332 | /* avoid unnecessary locked operations if possible */ |
325 | goto refill; | 333 | if ((atomic_read(&nc->page->_count) == nc->pagecnt_bias) || |
326 | } | 334 | atomic_sub_and_test(nc->pagecnt_bias, &nc->page->_count)) |
327 | data = page_address(nc->page) + nc->offset; | 335 | goto recycle; |
328 | nc->offset += fragsz; | 336 | goto refill; |
329 | get_page(nc->page); | ||
330 | } | 337 | } |
338 | |||
339 | data = page_address(nc->page) + nc->offset; | ||
340 | nc->offset += fragsz; | ||
341 | nc->pagecnt_bias--; | ||
342 | end: | ||
331 | local_irq_restore(flags); | 343 | local_irq_restore(flags); |
332 | return data; | 344 | return data; |
333 | } | 345 | } |