Merge branch 'docs-page_pool-tweaks-and-updates'

Jakub Kicinski says:

====================
docs: page_pool: tweaks and updates

I'm hoping to start feeding our docs into the AI review tools, instead
of maintaining a separate repo with review prompts. To experiment with
that we have to refresh the docs a little bit.

This set exclusively focuses on the page pool API. First patch is
a straightforward fix for information which is now out of date.
Second one attempts to clarify the NAPI linking requirements.
Third drops the dedicated section about the stats; the document
is primarily developer-facing and the stats should require no
development effort in most cases. Last but not least minor
API cleanup.
====================

Link: https://patch.msgid.link/20260526155722.2790742-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-05-28 18:10:05 -07:00
commit 8415598365
5 changed files with 28 additions and 50 deletions

View File

@ -43,18 +43,32 @@ Architecture overview
Monitoring
==========
Information about page pools on the system can be accessed via the netdev
genetlink family (see Documentation/netlink/specs/netdev.yaml).
Information about allocated page pools, their memory use, recycling statistics
etc. can be accessed via the netdev genetlink family
(see Documentation/netlink/specs/netdev.yaml).
Statistics
----------
.. kernel-doc:: include/net/page_pool/types.h
:identifiers: struct page_pool_recycle_stats
struct page_pool_alloc_stats
struct page_pool_stats
API interface
=============
The number of pools created **must** match the number of hardware queues
The number of pools created **must** match the number of NAPI contexts / queues
unless hardware restrictions make that impossible. This would otherwise beat the
purpose of page pool, which is allocate pages fast from cache without locking.
This lockless guarantee naturally comes from running under a NAPI softirq.
The protection doesn't strictly have to be NAPI, any guarantee that allocating
a page will cause no race conditions is enough.
If ``params.napi`` is set, the NAPI instance must be the sole consumer
context for pages allocated from the pool. In other words, when running in
that NAPI context, the page pool may safely access consumer-side resources
**without any additional locking**.
.. kernel-doc:: net/core/page_pool.c
:identifiers: page_pool_create
@ -69,7 +83,7 @@ a page will cause no race conditions is enough.
page_pool_get_dma_addr page_pool_get_dma_dir
.. kernel-doc:: net/core/page_pool.c
:identifiers: page_pool_put_page_bulk page_pool_get_stats
:identifiers: page_pool_put_page_bulk
DMA sync
--------
@ -98,29 +112,12 @@ If in doubt set ``offset`` to 0, ``max_len`` to ``PAGE_SIZE`` and
pass -1 as ``dma_sync_size``. That combination of arguments is always
correct.
Note that the syncing parameters are for the entire page.
This is important to remember when using fragments (``PP_FLAG_PAGE_FRAG``),
where allocated buffers may be smaller than a full page.
Note that the syncing parameters are for the **entire page**, even if
the driver allocates fragments (e.g. via ``page_pool_dev_alloc_frag()``).
Unless the driver author really understands page pool internals
it's recommended to always use ``offset = 0``, ``max_len = PAGE_SIZE``
with fragmented page pools.
Stats API and structures
------------------------
If the kernel is configured with ``CONFIG_PAGE_POOL_STATS=y``, the API
page_pool_get_stats() and structures described below are available.
It takes a pointer to a ``struct page_pool`` and a pointer to a struct
page_pool_stats allocated by the caller.
Older drivers expose page pool statistics via ethtool or debugfs.
The same statistics are accessible via the netlink netdev family
in a driver-independent fashion.
.. kernel-doc:: include/net/page_pool/types.h
:identifiers: struct page_pool_recycle_stats
struct page_pool_alloc_stats
struct page_pool_stats
Coding examples
===============
@ -140,7 +137,7 @@ Registration
pp_params.pool_size = DESC_NUM;
pp_params.nid = NUMA_NO_NODE;
pp_params.dev = priv->dev;
pp_params.napi = napi; /* only if locking is tied to NAPI */
pp_params.napi = napi; /* only if this NAPI is the sole consumer, see above */
pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
page_pool = page_pool_create(&pp_params);
@ -174,21 +171,6 @@ NAPI poller
}
}
Stats
-----
.. code-block:: c
#ifdef CONFIG_PAGE_POOL_STATS
/* retrieve stats */
struct page_pool_stats stats = { 0 };
if (page_pool_get_stats(page_pool, &stats)) {
/* perhaps the driver reports statistics with ethool */
ethtool_print_allocation_stats(&stats.alloc_stats);
ethtool_print_recycle_stats(&stats.recycle_stats);
}
#endif
Driver unload
-------------

View File

@ -496,8 +496,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c)
struct page_pool *pool = c->rq.page_pool;
struct page_pool_stats stats = { 0 };
if (!page_pool_get_stats(pool, &stats))
return;
page_pool_get_stats(pool, &stats);
rq_stats->pp_alloc_fast = stats.alloc_stats.fast;
rq_stats->pp_alloc_slow = stats.alloc_stats.slow;

View File

@ -64,7 +64,7 @@ int page_pool_ethtool_stats_get_count(void);
u8 *page_pool_ethtool_stats_get_strings(u8 *data);
u64 *page_pool_ethtool_stats_get(u64 *data, const void *stats);
bool page_pool_get_stats(const struct page_pool *pool,
void page_pool_get_stats(const struct page_pool *pool,
struct page_pool_stats *stats);
#else
static inline int page_pool_ethtool_stats_get_count(void)

View File

@ -76,20 +76,20 @@ static const char pp_stats[][ETH_GSTRING_LEN] = {
* @pool: pool from which page was allocated
* @stats: struct page_pool_stats to fill in
*
* Deprecated driver API for querying stats. Page pool stats can be queried
* via netdev Netlink.
*
* Retrieve statistics about the page_pool. This API is only available
* if the kernel has been configured with ``CONFIG_PAGE_POOL_STATS=y``.
* A pointer to a caller allocated struct page_pool_stats structure
* is passed to this API which is filled in. The caller can then report
* those stats to the user (perhaps via ethtool, debugfs, etc.).
*/
bool page_pool_get_stats(const struct page_pool *pool,
void page_pool_get_stats(const struct page_pool *pool,
struct page_pool_stats *stats)
{
int cpu = 0;
if (!stats)
return false;
/* The caller is responsible to initialize stats. */
stats->alloc_stats.fast += pool->alloc_stats.fast;
stats->alloc_stats.slow += pool->alloc_stats.slow;
@ -108,8 +108,6 @@ bool page_pool_get_stats(const struct page_pool *pool,
stats->recycle_stats.ring_full += pcpu->ring_full;
stats->recycle_stats.released_refcnt += pcpu->released_refcnt;
}
return true;
}
EXPORT_SYMBOL(page_pool_get_stats);

View File

@ -127,8 +127,7 @@ page_pool_nl_stats_fill(struct sk_buff *rsp, const struct page_pool *pool,
struct nlattr *nest;
void *hdr;
if (!page_pool_get_stats(pool, &stats))
return 0;
page_pool_get_stats(pool, &stats);
hdr = genlmsg_iput(rsp, info);
if (!hdr)