Skip to content

Commit

Permalink
page_pool: add frag page recycling support in page pool
Browse files Browse the repository at this point in the history
Currently page pool only support page recycling when there
is only one user of the page, and the split page reusing
implemented in the most driver can not use the page pool as
bing-pong way of reusing requires the multi user support in
page pool.

Those reusing or recycling has below limitations:
1. page from page pool can only be used be one user in order
   for the page recycling to happen.
2. Bing-pong way of reusing in most driver does not support
   multi desc using different part of the same page in order
   to save memory.

So add multi-users support and frag page recycling in page
pool to overcome the above limitation.

Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Yunsheng Lin authored and Jakub Kicinski committed Aug 9, 2021
1 parent 0e9d2a0 commit 53e0961
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
15 changes: 15 additions & 0 deletions include/net/page_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ struct page_pool {
unsigned long defer_warn;

u32 pages_state_hold_cnt;
unsigned int frag_offset;
struct page *frag_page;
long frag_users;

/*
* Data structure for allocation side
Expand Down Expand Up @@ -140,6 +143,18 @@ static inline struct page *page_pool_dev_alloc_pages(struct page_pool *pool)
return page_pool_alloc_pages(pool, gfp);
}

struct page *page_pool_alloc_frag(struct page_pool *pool, unsigned int *offset,
unsigned int size, gfp_t gfp);

static inline struct page *page_pool_dev_alloc_frag(struct page_pool *pool,
unsigned int *offset,
unsigned int size)
{
gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);

return page_pool_alloc_frag(pool, offset, size, gfp);
}

/* get the stored dma direction. A driver might decide to treat this locally and
* avoid the extra cache line from page_pool to determine the direction
*/
Expand Down
87 changes: 87 additions & 0 deletions net/core/page_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#define DEFER_TIME (msecs_to_jiffies(1000))
#define DEFER_WARN_INTERVAL (60 * HZ)

#define BIAS_MAX LONG_MAX

static int page_pool_init(struct page_pool *pool,
const struct page_pool_params *params)
{
Expand Down Expand Up @@ -423,6 +425,11 @@ static __always_inline struct page *
__page_pool_put_page(struct page_pool *pool, struct page *page,
unsigned int dma_sync_size, bool allow_direct)
{
/* It is not the last user for the page frag case */
if (pool->p.flags & PP_FLAG_PAGE_FRAG &&
page_pool_atomic_sub_frag_count_return(page, 1))
return NULL;

/* This allocator is optimized for the XDP mode that uses
* one-frame-per-page, but have fallbacks that act like the
* regular page allocator APIs.
Expand Down Expand Up @@ -515,6 +522,84 @@ void page_pool_put_page_bulk(struct page_pool *pool, void **data,
}
EXPORT_SYMBOL(page_pool_put_page_bulk);

static struct page *page_pool_drain_frag(struct page_pool *pool,
struct page *page)
{
long drain_count = BIAS_MAX - pool->frag_users;

/* Some user is still using the page frag */
if (likely(page_pool_atomic_sub_frag_count_return(page,
drain_count)))
return NULL;

if (page_ref_count(page) == 1 && !page_is_pfmemalloc(page)) {
if (pool->p.flags & PP_FLAG_DMA_SYNC_DEV)
page_pool_dma_sync_for_device(pool, page, -1);

return page;
}

page_pool_return_page(pool, page);
return NULL;
}

static void page_pool_free_frag(struct page_pool *pool)
{
long drain_count = BIAS_MAX - pool->frag_users;
struct page *page = pool->frag_page;

pool->frag_page = NULL;

if (!page ||
page_pool_atomic_sub_frag_count_return(page, drain_count))
return;

page_pool_return_page(pool, page);
}

struct page *page_pool_alloc_frag(struct page_pool *pool,
unsigned int *offset,
unsigned int size, gfp_t gfp)
{
unsigned int max_size = PAGE_SIZE << pool->p.order;
struct page *page = pool->frag_page;

if (WARN_ON(!(pool->p.flags & PP_FLAG_PAGE_FRAG) ||
size > max_size))
return NULL;

size = ALIGN(size, dma_get_cache_alignment());
*offset = pool->frag_offset;

if (page && *offset + size > max_size) {
page = page_pool_drain_frag(pool, page);
if (page)
goto frag_reset;
}

if (!page) {
page = page_pool_alloc_pages(pool, gfp);
if (unlikely(!page)) {
pool->frag_page = NULL;
return NULL;
}

pool->frag_page = page;

frag_reset:
pool->frag_users = 1;
*offset = 0;
pool->frag_offset = size;
page_pool_set_frag_count(page, BIAS_MAX);
return page;
}

pool->frag_users++;
pool->frag_offset = *offset + size;
return page;
}
EXPORT_SYMBOL(page_pool_alloc_frag);

static void page_pool_empty_ring(struct page_pool *pool)
{
struct page *page;
Expand Down Expand Up @@ -620,6 +705,8 @@ void page_pool_destroy(struct page_pool *pool)
if (!page_pool_put(pool))
return;

page_pool_free_frag(pool);

if (!page_pool_release(pool))
return;

Expand Down

0 comments on commit 53e0961

Please sign in to comment.