Skip to content

Commit

Permalink
page_pool: unlink from napi during destroy
Browse files Browse the repository at this point in the history
Jesper points out that we must prevent recycling into cache
after page_pool_destroy() is called, because page_pool_destroy()
is not synchronized with recycling (some pages may still be
outstanding when destroy() gets called).

I assumed this will not happen because NAPI can't be scheduled
if its page pool is being destroyed. But I missed the fact that
NAPI may get reused. For instance when user changes ring configuration
driver may allocate a new page pool, stop NAPI, swap, start NAPI,
and then destroy the old pool. The NAPI is running so old page
pool will think it can recycle to the cache, but the consumer
at that point is the destroy() path, not NAPI.

To avoid extra synchronization let the drivers do "unlinking"
during the "swap" stage while NAPI is indeed disabled.

Fixes: 8c48eea ("page_pool: allow caching from safely localized NAPI")
Reported-by: Jesper Dangaard Brouer <jbrouer@redhat.com>
Link: https://lore.kernel.org/all/e8df2654-6a5b-3c92-489d-2fe5e444135f@redhat.com/
Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
Link: https://lore.kernel.org/r/20230419182006.719923-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Apr 21, 2023
1 parent 4bb7aac commit dd64b23
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 1 deletion.
5 changes: 5 additions & 0 deletions include/net/page_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,18 @@ struct page_pool *page_pool_create(const struct page_pool_params *params);
struct xdp_mem_info;

#ifdef CONFIG_PAGE_POOL
void page_pool_unlink_napi(struct page_pool *pool);
void page_pool_destroy(struct page_pool *pool);
void page_pool_use_xdp_mem(struct page_pool *pool, void (*disconnect)(void *),
struct xdp_mem_info *mem);
void page_pool_release_page(struct page_pool *pool, struct page *page);
void page_pool_put_page_bulk(struct page_pool *pool, void **data,
int count);
#else
static inline void page_pool_unlink_napi(struct page_pool *pool)
{
}

static inline void page_pool_destroy(struct page_pool *pool)
{
}
Expand Down
18 changes: 17 additions & 1 deletion net/core/page_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,21 @@ void page_pool_use_xdp_mem(struct page_pool *pool, void (*disconnect)(void *),
pool->xdp_mem_id = mem->id;
}

void page_pool_unlink_napi(struct page_pool *pool)
{
if (!pool->p.napi)
return;

/* To avoid races with recycling and additional barriers make sure
* pool and NAPI are unlinked when NAPI is disabled.
*/
WARN_ON(!test_bit(NAPI_STATE_SCHED, &pool->p.napi->state) ||
READ_ONCE(pool->p.napi->list_owner) != -1);

WRITE_ONCE(pool->p.napi, NULL);
}
EXPORT_SYMBOL(page_pool_unlink_napi);

void page_pool_destroy(struct page_pool *pool)
{
if (!pool)
Expand All @@ -847,6 +862,7 @@ void page_pool_destroy(struct page_pool *pool)
if (!page_pool_put(pool))
return;

page_pool_unlink_napi(pool);
page_pool_free_frag(pool);

if (!page_pool_release(pool))
Expand Down Expand Up @@ -900,7 +916,7 @@ bool page_pool_return_skb_page(struct page *page, bool napi_safe)
* in the same context as the consumer would run, so there's
* no possible race.
*/
napi = pp->p.napi;
napi = READ_ONCE(pp->p.napi);
allow_direct = napi_safe && napi &&
READ_ONCE(napi->list_owner) == smp_processor_id();

Expand Down

0 comments on commit dd64b23

Please sign in to comment.