Skip to content

Commit c3f812c

Browse files
jlemondavem330
authored andcommitted
page_pool: do not release pool until inflight == 0.
The page pool keeps track of the number of pages in flight, and it isn't safe to remove the pool until all pages are returned. Disallow removing the pool until all pages are back, so the pool is always available for page producers. Make the page pool responsible for its own delayed destruction instead of relying on XDP, so the page pool can be used without the xdp memory model. When all pages are returned, free the pool and notify xdp if the pool is registered with the xdp memory system. Have the callback perform a table walk since some drivers (cpsw) may share the pool among multiple xdp_rxq_info. Note that the increment of pages_state_release_cnt may result in inflight == 0, resulting in the pool being released. Fixes: d956a04 ("xdp: force mem allocator removal and periodic warning") Signed-off-by: Jonathan Lemon <[email protected]> Acked-by: Jesper Dangaard Brouer <[email protected]> Acked-by: Ilias Apalodimas <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3af7ff9 commit c3f812c

File tree

6 files changed

+139
-183
lines changed

6 files changed

+139
-183
lines changed

drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,10 +1503,8 @@ static void free_dma_rx_desc_resources(struct stmmac_priv *priv)
15031503
rx_q->dma_erx, rx_q->dma_rx_phy);
15041504

15051505
kfree(rx_q->buf_pool);
1506-
if (rx_q->page_pool) {
1507-
page_pool_request_shutdown(rx_q->page_pool);
1506+
if (rx_q->page_pool)
15081507
page_pool_destroy(rx_q->page_pool);
1509-
}
15101508
}
15111509
}
15121510

include/net/page_pool.h

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@ struct page_pool_params {
7070
struct page_pool {
7171
struct page_pool_params p;
7272

73-
u32 pages_state_hold_cnt;
73+
struct delayed_work release_dw;
74+
void (*disconnect)(void *);
75+
unsigned long defer_start;
76+
unsigned long defer_warn;
77+
78+
u32 pages_state_hold_cnt;
7479

7580
/*
7681
* Data structure for allocation side
@@ -129,25 +134,19 @@ inline enum dma_data_direction page_pool_get_dma_dir(struct page_pool *pool)
129134

130135
struct page_pool *page_pool_create(const struct page_pool_params *params);
131136

132-
void __page_pool_free(struct page_pool *pool);
133-
static inline void page_pool_free(struct page_pool *pool)
134-
{
135-
/* When page_pool isn't compiled-in, net/core/xdp.c doesn't
136-
* allow registering MEM_TYPE_PAGE_POOL, but shield linker.
137-
*/
138137
#ifdef CONFIG_PAGE_POOL
139-
__page_pool_free(pool);
140-
#endif
141-
}
142-
143-
/* Drivers use this instead of page_pool_free */
138+
void page_pool_destroy(struct page_pool *pool);
139+
void page_pool_use_xdp_mem(struct page_pool *pool, void (*disconnect)(void *));
140+
#else
144141
static inline void page_pool_destroy(struct page_pool *pool)
145142
{
146-
if (!pool)
147-
return;
143+
}
148144

149-
page_pool_free(pool);
145+
static inline void page_pool_use_xdp_mem(struct page_pool *pool,
146+
void (*disconnect)(void *))
147+
{
150148
}
149+
#endif
151150

152151
/* Never call this directly, use helpers below */
153152
void __page_pool_put_page(struct page_pool *pool,
@@ -170,24 +169,6 @@ static inline void page_pool_recycle_direct(struct page_pool *pool,
170169
__page_pool_put_page(pool, page, true);
171170
}
172171

173-
/* API user MUST have disconnected alloc-side (not allowed to call
174-
* page_pool_alloc_pages()) before calling this. The free-side can
175-
* still run concurrently, to handle in-flight packet-pages.
176-
*
177-
* A request to shutdown can fail (with false) if there are still
178-
* in-flight packet-pages.
179-
*/
180-
bool __page_pool_request_shutdown(struct page_pool *pool);
181-
static inline bool page_pool_request_shutdown(struct page_pool *pool)
182-
{
183-
bool safe_to_remove = false;
184-
185-
#ifdef CONFIG_PAGE_POOL
186-
safe_to_remove = __page_pool_request_shutdown(pool);
187-
#endif
188-
return safe_to_remove;
189-
}
190-
191172
/* Disconnects a page (from a page_pool). API users can have a need
192173
* to disconnect a page (from a page_pool), to allow it to be used as
193174
* a regular page (that will eventually be returned to the normal
@@ -216,11 +197,6 @@ static inline bool is_page_pool_compiled_in(void)
216197
#endif
217198
}
218199

219-
static inline void page_pool_get(struct page_pool *pool)
220-
{
221-
refcount_inc(&pool->user_cnt);
222-
}
223-
224200
static inline bool page_pool_put(struct page_pool *pool)
225201
{
226202
return refcount_dec_and_test(&pool->user_cnt);

include/net/xdp_priv.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,8 @@ struct xdp_mem_allocator {
1212
struct page_pool *page_pool;
1313
struct zero_copy_allocator *zc_alloc;
1414
};
15-
int disconnect_cnt;
16-
unsigned long defer_start;
1715
struct rhash_head node;
1816
struct rcu_head rcu;
19-
struct delayed_work defer_wq;
20-
unsigned long defer_warn;
2117
};
2218

2319
#endif /* __LINUX_NET_XDP_PRIV_H__ */

include/trace/events/xdp.h

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -317,39 +317,28 @@ __MEM_TYPE_MAP(__MEM_TYPE_TP_FN)
317317

318318
TRACE_EVENT(mem_disconnect,
319319

320-
TP_PROTO(const struct xdp_mem_allocator *xa,
321-
bool safe_to_remove, bool force),
320+
TP_PROTO(const struct xdp_mem_allocator *xa),
322321

323-
TP_ARGS(xa, safe_to_remove, force),
322+
TP_ARGS(xa),
324323

325324
TP_STRUCT__entry(
326325
__field(const struct xdp_mem_allocator *, xa)
327326
__field(u32, mem_id)
328327
__field(u32, mem_type)
329328
__field(const void *, allocator)
330-
__field(bool, safe_to_remove)
331-
__field(bool, force)
332-
__field(int, disconnect_cnt)
333329
),
334330

335331
TP_fast_assign(
336332
__entry->xa = xa;
337333
__entry->mem_id = xa->mem.id;
338334
__entry->mem_type = xa->mem.type;
339335
__entry->allocator = xa->allocator;
340-
__entry->safe_to_remove = safe_to_remove;
341-
__entry->force = force;
342-
__entry->disconnect_cnt = xa->disconnect_cnt;
343336
),
344337

345-
TP_printk("mem_id=%d mem_type=%s allocator=%p"
346-
" safe_to_remove=%s force=%s disconnect_cnt=%d",
338+
TP_printk("mem_id=%d mem_type=%s allocator=%p",
347339
__entry->mem_id,
348340
__print_symbolic(__entry->mem_type, __MEM_TYPE_SYM_TAB),
349-
__entry->allocator,
350-
__entry->safe_to_remove ? "true" : "false",
351-
__entry->force ? "true" : "false",
352-
__entry->disconnect_cnt
341+
__entry->allocator
353342
)
354343
);
355344

net/core/page_pool.c

Lines changed: 76 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
#include <trace/events/page_pool.h>
2020

21+
#define DEFER_TIME (msecs_to_jiffies(1000))
22+
#define DEFER_WARN_INTERVAL (60 * HZ)
23+
2124
static int page_pool_init(struct page_pool *pool,
2225
const struct page_pool_params *params)
2326
{
@@ -193,29 +196,22 @@ static s32 page_pool_inflight(struct page_pool *pool)
193196
{
194197
u32 release_cnt = atomic_read(&pool->pages_state_release_cnt);
195198
u32 hold_cnt = READ_ONCE(pool->pages_state_hold_cnt);
196-
s32 distance;
197-
198-
distance = _distance(hold_cnt, release_cnt);
199-
200-
trace_page_pool_inflight(pool, distance, hold_cnt, release_cnt);
201-
return distance;
202-
}
199+
s32 inflight;
203200

204-
static bool __page_pool_safe_to_destroy(struct page_pool *pool)
205-
{
206-
s32 inflight = page_pool_inflight(pool);
201+
inflight = _distance(hold_cnt, release_cnt);
207202

208-
/* The distance should not be able to become negative */
203+
trace_page_pool_inflight(pool, inflight, hold_cnt, release_cnt);
209204
WARN(inflight < 0, "Negative(%d) inflight packet-pages", inflight);
210205

211-
return (inflight == 0);
206+
return inflight;
212207
}
213208

214209
/* Cleanup page_pool state from page */
215210
static void __page_pool_clean_page(struct page_pool *pool,
216211
struct page *page)
217212
{
218213
dma_addr_t dma;
214+
int count;
219215

220216
if (!(pool->p.flags & PP_FLAG_DMA_MAP))
221217
goto skip_dma_unmap;
@@ -227,9 +223,11 @@ static void __page_pool_clean_page(struct page_pool *pool,
227223
DMA_ATTR_SKIP_CPU_SYNC);
228224
page->dma_addr = 0;
229225
skip_dma_unmap:
230-
atomic_inc(&pool->pages_state_release_cnt);
231-
trace_page_pool_state_release(pool, page,
232-
atomic_read(&pool->pages_state_release_cnt));
226+
/* This may be the last page returned, releasing the pool, so
227+
* it is not safe to reference pool afterwards.
228+
*/
229+
count = atomic_inc_return(&pool->pages_state_release_cnt);
230+
trace_page_pool_state_release(pool, page, count);
233231
}
234232

235233
/* unmap the page and clean our state */
@@ -338,31 +336,10 @@ static void __page_pool_empty_ring(struct page_pool *pool)
338336
}
339337
}
340338

341-
static void __warn_in_flight(struct page_pool *pool)
339+
static void page_pool_free(struct page_pool *pool)
342340
{
343-
u32 release_cnt = atomic_read(&pool->pages_state_release_cnt);
344-
u32 hold_cnt = READ_ONCE(pool->pages_state_hold_cnt);
345-
s32 distance;
346-
347-
distance = _distance(hold_cnt, release_cnt);
348-
349-
/* Drivers should fix this, but only problematic when DMA is used */
350-
WARN(1, "Still in-flight pages:%d hold:%u released:%u",
351-
distance, hold_cnt, release_cnt);
352-
}
353-
354-
void __page_pool_free(struct page_pool *pool)
355-
{
356-
/* Only last user actually free/release resources */
357-
if (!page_pool_put(pool))
358-
return;
359-
360-
WARN(pool->alloc.count, "API usage violation");
361-
WARN(!ptr_ring_empty(&pool->ring), "ptr_ring is not empty");
362-
363-
/* Can happen due to forced shutdown */
364-
if (!__page_pool_safe_to_destroy(pool))
365-
__warn_in_flight(pool);
341+
if (pool->disconnect)
342+
pool->disconnect(pool);
366343

367344
ptr_ring_cleanup(&pool->ring, NULL);
368345

@@ -371,12 +348,8 @@ void __page_pool_free(struct page_pool *pool)
371348

372349
kfree(pool);
373350
}
374-
EXPORT_SYMBOL(__page_pool_free);
375351

376-
/* Request to shutdown: release pages cached by page_pool, and check
377-
* for in-flight pages
378-
*/
379-
bool __page_pool_request_shutdown(struct page_pool *pool)
352+
static void page_pool_scrub(struct page_pool *pool)
380353
{
381354
struct page *page;
382355

@@ -393,7 +366,64 @@ bool __page_pool_request_shutdown(struct page_pool *pool)
393366
* be in-flight.
394367
*/
395368
__page_pool_empty_ring(pool);
369+
}
370+
371+
static int page_pool_release(struct page_pool *pool)
372+
{
373+
int inflight;
374+
375+
page_pool_scrub(pool);
376+
inflight = page_pool_inflight(pool);
377+
if (!inflight)
378+
page_pool_free(pool);
379+
380+
return inflight;
381+
}
382+
383+
static void page_pool_release_retry(struct work_struct *wq)
384+
{
385+
struct delayed_work *dwq = to_delayed_work(wq);
386+
struct page_pool *pool = container_of(dwq, typeof(*pool), release_dw);
387+
int inflight;
388+
389+
inflight = page_pool_release(pool);
390+
if (!inflight)
391+
return;
392+
393+
/* Periodic warning */
394+
if (time_after_eq(jiffies, pool->defer_warn)) {
395+
int sec = (s32)((u32)jiffies - (u32)pool->defer_start) / HZ;
396+
397+
pr_warn("%s() stalled pool shutdown %d inflight %d sec\n",
398+
__func__, inflight, sec);
399+
pool->defer_warn = jiffies + DEFER_WARN_INTERVAL;
400+
}
401+
402+
/* Still not ready to be disconnected, retry later */
403+
schedule_delayed_work(&pool->release_dw, DEFER_TIME);
404+
}
405+
406+
void page_pool_use_xdp_mem(struct page_pool *pool, void (*disconnect)(void *))
407+
{
408+
refcount_inc(&pool->user_cnt);
409+
pool->disconnect = disconnect;
410+
}
411+
412+
void page_pool_destroy(struct page_pool *pool)
413+
{
414+
if (!pool)
415+
return;
416+
417+
if (!page_pool_put(pool))
418+
return;
419+
420+
if (!page_pool_release(pool))
421+
return;
422+
423+
pool->defer_start = jiffies;
424+
pool->defer_warn = jiffies + DEFER_WARN_INTERVAL;
396425

397-
return __page_pool_safe_to_destroy(pool);
426+
INIT_DELAYED_WORK(&pool->release_dw, page_pool_release_retry);
427+
schedule_delayed_work(&pool->release_dw, DEFER_TIME);
398428
}
399-
EXPORT_SYMBOL(__page_pool_request_shutdown);
429+
EXPORT_SYMBOL(page_pool_destroy);

0 commit comments

Comments
 (0)