net: page_pool: add netlink-based introspection
Subsystems (10)
Jakub Kicinski
maintainercover net: page_pool: add netlink-based introspection
This is a new revision of the RFC posted in August:
https://lore.kernel.org/all/[email protected]/
There's been a handful of fixes and tweaks but the overall architecture is unchanged.
As a reminder the RFC was posted as the first step towards an API which could configure the page pools (GET API as a stepping stone for a SET API to come later). I wasn't sure whether we should commit to the GET API before the SET takes shape, hence the large delay between versions.
Unfortunately, real deployment experience made this series much more urgent. We recently started to deploy newer kernels / drivers at Meta, making significant use of page pools for the first time. We immediately run into page pool leaks both real and false positive warnings. As Eric pointed out/predicted there's no guarantee that applications will read / close their sockets so a page pool page may be stuck in a socket (but not leaked) forever. This happens a lot in our fleet. Most of these are obviously due to application bugs but we should not be printing kernel warnings due to minor application resource leaks.
Conversely the page pool memory may get leaked at runtime, and we have no way to detect / track that, unless someone reconfigures the NIC and destroys the page pools which leaked the pages.
The solution presented here is to expose the memory use of page pools via netlink. This allows for continuous monitoring of memory used by page pools, regardless if they were destroyed or not. Sample in patch 15 can print the memory use and recycling efficiency:
$ ./page-pool
eth0[2] page pools: 10 (zombies: 0)
refs: 41984 bytes: 171966464 (refs: 0 bytes: 0)
recycling: 90.3% (alloc: 656:397681 recycle: 89652:270201)The main change compared to the RFC is that the API now exposes outstanding references and byte counts even for "live" page pools. The warning is no longer printed if page pool is accessible via netlink.
Jakub Kicinski (15):
net: page_pool: split the page_pool_params into fast and slow
net: page_pool: avoid touching slow on the fastpath
net: page_pool: factor out uninit
net: page_pool: id the page pools
net: page_pool: record pools per netdev
net: page_pool: stash the NAPI ID for easier access
eth: link netdev to page_pools in drivers
net: page_pool: add nlspec for basic access to page pools
net: page_pool: implement GET in the netlink API
net: page_pool: add netlink notifications for state changes
net: page_pool: report amount of memory held by page pools
net: page_pool: report when page pool was destroyed
net: page_pool: expose page pool stats via netlink
net: page_pool: mute the periodic warning for visible page pools
tools: ynl: add sample for getting page-pool information
Documentation/netlink/specs/netdev.yaml | 161 +++++++ Documentation/networking/page_pool.rst | 10 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 + .../net/ethernet/mellanox/mlx5/core/en_main.c | 1 + drivers/net/ethernet/microsoft/mana/mana_en.c | 1 + include/linux/list.h | 20 + include/linux/netdevice.h | 4 + include/linux/poison.h | 2 + include/net/page_pool/helpers.h | 8 +- include/net/page_pool/types.h | 43 +- include/uapi/linux/netdev.h | 36 ++ net/core/Makefile | 2 +- net/core/netdev-genl-gen.c | 52 +++ net/core/netdev-genl-gen.h | 11 + net/core/page_pool.c | 78 ++-- net/core/page_pool_priv.h | 12 + net/core/page_pool_user.c | 414 +++++++++++++++++ tools/include/uapi/linux/netdev.h | 36 ++ tools/net/ynl/generated/netdev-user.c | 419 ++++++++++++++++++ tools/net/ynl/generated/netdev-user.h | 171 +++++++ tools/net/ynl/lib/ynl.h | 2 +- tools/net/ynl/samples/.gitignore | 1 + tools/net/ynl/samples/Makefile | 2 +- tools/net/ynl/samples/page-pool.c | 147 ++++++ 24 files changed, 1586 insertions(+), 48 deletions(-) create mode 100644 net/core/page_pool_priv.h create mode 100644 net/core/page_pool_user.c create mode 100644 tools/net/ynl/samples/page-pool.c
Signature · 1 line
2.41.0
Jakub Kicinski
maintainerPATCH 1/15 net: page_pool: split the page_pool_params into fast and slow
struct page_pool is rather performance critical and we use 16B of the first cache line to store 2 pointers used only by test code. Future patches will add more informational (non-fast path) attributes.
It's convenient for the user of the API to not have to worry which fields are fast and which are slow path. Use struct groups to split the params into the two categories internally.
--- include/net/page_pool/types.h | 31 +++++++++++++++++++------------ net/core/page_pool.c | 7 ++++--- 2 files changed, 23 insertions(+), 15 deletions(-)
include/net/page_pool/types.h +19 -12
@@ -54,18 +54,22 @@ struct pp_alloc_cache {* @offset: DMA sync address offset for PP_FLAG_DMA_SYNC_DEV */ struct page_pool_params { -unsigned int flags; -unsigned int order; -unsigned int pool_size; -int nid; -struct device *dev; -struct napi_struct *napi; -enum dma_data_direction dma_dir; -unsigned int max_len; -unsigned int offset; + struct_group_tagged(page_pool_params_fast, fast, + unsigned int flags; +unsigned int order; +unsigned int pool_size; +int nid; +struct device *dev; +struct napi_struct *napi; +enum dma_data_direction dma_dir; +unsigned int max_len; +unsigned int offset; + ); + struct_group_tagged(page_pool_params_slow, slow,/* private: used by test code only */ -void (*init_callback)(struct page *page,void *arg); -void *init_arg; + void (*init_callback)(struct page *page, void *arg); +void *init_arg; + ); };#ifdef CONFIG_PAGE_POOL_STATS
@@ -119,7 +123,7 @@ struct page_pool_stats {#endif struct page_pool { -struct page_pool_params p; +struct page_pool_params_fast p;long frag_users;struct page *frag_page;
@@ -178,6 +182,9 @@ struct page_pool {refcount_t user_cnt;u64 destroy_cnt; + +/* Slow/Control-path information follows */ +struct page_pool_params_slow slow; };struct page *page_pool_alloc_pages (struct page_pool *pool,gfp_t gfp);
net/core/page_pool.c +4 -3
@@ -173,7 +173,8 @@ static int page_pool_init(struct page_pool *pool, {unsigned int ring_qsize =1024 ;/* Default */ -memcpy (&pool->p, params,sizeof (pool->p)); +memcpy (&pool->p, ¶ms->fast,sizeof (pool->p)); +memcpy (&pool->slow, ¶ms->slow,sizeof (pool->slow));/* Validate only known flags were used */ if (pool->p.flags & ~(PP_FLAG_ALL ))
@@ -384,8 +385,8 @@ static void page_pool_set_pp_info(struct page_pool *pool,* the overhead is negligible. */ page_pool_fragment_page (page,1 ); -if (pool->p.init_callback) - pool->p.init_callback (page, pool->p.init_arg); +if (pool->slow.init_callback) + pool->slow.init_callback (page, pool->slow.init_arg); }static void page_pool_clear_pp_info (struct page *page)
Signature · 1 line
2.41.0
Re: [PATCH net-next 01/15] net: page_pool: split the page_pool_params into fast and slow
Quoted 102 lines · Jakub Kicinski
struct
page_poolis rather performance critical and we use 16B of the first cache line to store 2 pointers used only by test code. Future patches will add more informational (non-fast path) attributes.It's convenient for the user of the API to not have to worry which fields are fast and which are slow path. Use struct groups to split the params into the two categories internally.
Acked-by Ilias Apalodimas <[email protected]>Reviewed-by Mina Almasry <[email protected]>Signed-off-by Jakub Kicinski <[email protected]>--- include/net/page_pool/types.h | 31 +++++++++++++++++++------------ net/core/page_pool.c | 7 ++++--- 2 files changed, 23 insertions(+), 15 deletions(-)include/net/page_pool/types.h +19
-12@@ -54,18 +54,22 @@ struct pp_alloc_cache {* @offset: DMA sync address offset for PP_FLAG_DMA_SYNC_DEV */ struct page_pool_params { -unsigned int flags; -unsigned int order; -unsigned int pool_size; -int nid; -struct device *dev; -struct napi_struct *napi; -enum dma_data_direction dma_dir; -unsigned int max_len; -unsigned int offset; + struct_group_tagged(page_pool_params_fast, fast, + unsigned int flags; +unsigned int order; +unsigned int pool_size; +int nid; +struct device *dev; +struct napi_struct *napi; +enum dma_data_direction dma_dir; +unsigned int max_len; +unsigned int offset; + ); + struct_group_tagged(page_pool_params_slow, slow,/* private: used by test code only */ -void (*init_callback)(struct page *page,void *arg); -void *init_arg; + void (*init_callback)(struct page *page, void *arg); +void *init_arg; + ); };#ifdef CONFIG_PAGE_POOL_STATS @@ -119,7 +123,7 @@ struct page_pool_stats {#endif struct page_pool { -struct page_pool_params p; +struct page_pool_params_fast p;long frag_users;struct page *frag_page;@@ -178,6 +182,9 @@ struct page_pool {refcount_t user_cnt;u64 destroy_cnt; + +/* Slow/Control-path information follows */ +struct page_pool_params_slow slow; };struct page *page_pool_alloc_pages (struct page_pool *pool,gfp_t gfp);net/core/page_pool.c +4
-4@@ -173,7 +173,8 @@ static int page_pool_init(struct page_pool *pool, {unsigned int ring_qsize =1024 ;/* Default */ -memcpy (&pool->p, params,sizeof (pool->p)); +memcpy (&pool->p, ¶ms->fast,sizeof (pool->p)); +memcpy (&pool->slow, ¶ms->slow,sizeof (pool->slow));/* Validate only known flags were used */ if (pool->p.flags & ~(PP_FLAG_ALL ))@@ -384,8 +385,8 @@ static void page_pool_set_pp_info(struct page_pool *pool,* the overhead is negligible. */ page_pool_fragment_page (page,1 ); -if (pool->p.init_callback) - pool->p.init_callback (page, pool->p.init_arg); +if (pool->slow.init_callback) + pool->slow.init_callback (page, pool->slow.init_arg); }static void page_pool_clear_pp_info (struct page *page) --
Quoted 2 lines
2.41.0
Had time for a close look, feel free to replace my ack with
Jakub Kicinski
maintainerPATCH 2/15 net: page_pool: avoid touching slow on the fastpath
In reply to Jakub KicinskiTo fully benefit from previous commit add one byte of state in the first cache line recording if we need to look at the slow part.
The packing isn't all that impressive right now, we create a 7B hole. I'm expecting Olek's rework will reshuffle this, anyway.
--- include/net/page_pool/types.h | 2 ++ net/core/page_pool.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-)
include/net/page_pool/types.h +2 -0
@@ -125,6 +125,8 @@ struct page_pool_stats {struct page_pool {struct page_pool_params_fast p; +bool has_init_callback; +long frag_users;struct page *frag_page;unsigned int frag_offset;
net/core/page_pool.c +3 -1
@@ -212,6 +212,8 @@ static int page_pool_init(struct page_pool *pool,*/ } + pool->has_init_callback = !!pool->slow.init_callback; +#ifdef CONFIG_PAGE_POOL_STATS pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats);if (!pool->recycle_stats)
@@ -385,7 +387,7 @@ static void page_pool_set_pp_info(struct page_pool *pool,* the overhead is negligible. */ page_pool_fragment_page (page,1 ); -if (pool->slow.init_callback) +if (pool->has_init_callback) pool->slow.init_callback (page, pool->slow.init_arg); }
Signature · 1 line
2.41.0
Re: [PATCH net-next 02/15] net: page_pool: avoid touching slow on the fastpath
Quoted 54 lines · Jakub Kicinski
To fully benefit from previous commit add one byte of state in the first cache line recording if we need to look at the slow part.
The packing isn't all that impressive right now, we create a 7B hole. I'm expecting Olek's rework will reshuffle this, anyway.
Acked-by Ilias Apalodimas <[email protected]>Reviewed-by Mina Almasry <[email protected]>Signed-off-by Jakub Kicinski <[email protected]>--- include/net/page_pool/types.h | 2 ++ net/core/page_pool.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-)include/net/page_pool/types.h +2
-0@@ -125,6 +125,8 @@ struct page_pool_stats {struct page_pool {struct page_pool_params_fast p; +bool has_init_callback; +long frag_users;struct page *frag_page;unsigned int frag_offset;net/core/page_pool.c +3
-2@@ -212,6 +212,8 @@ static int page_pool_init(struct page_pool *pool,*/ } + pool->has_init_callback = !!pool->slow.init_callback; +#ifdef CONFIG_PAGE_POOL_STATS pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats);if (!pool->recycle_stats)@@ -385,7 +387,7 @@ static void page_pool_set_pp_info(struct page_pool *pool,* the overhead is negligible. */ page_pool_fragment_page (page,1 ); -if (pool->slow.init_callback) +if (pool->has_init_callback) pool->slow.init_callback (page, pool->slow.init_arg); } --
Quoted 2 lines
2.41.0
Same here, please swap my ack with
Jakub Kicinski
maintainerPATCH 3/15 net: page_pool: factor out uninit
In reply to Jakub KicinskiWe'll soon (next change in the series) need a fuller unwind path in page_pool_create() so create the inverse of page_pool_init().
--- net/core/page_pool.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
net/core/page_pool.c +13 -8
@@ -234,6 +234,18 @@ static int page_pool_init(struct page_pool *pool,return 0 ; } +static void page_pool_uninit (struct page_pool *pool) +{ +ptr_ring_cleanup (&pool->ring,NULL ); + +if (pool->p.flags &PP_FLAG_DMA_MAP ) +put_device (pool->p.dev); + +#ifdef CONFIG_PAGE_POOL_STATS +free_percpu (pool->recycle_stats); +#endif +} +/** * page_pool_create() - create a page pool. * @params: parameters, see struct page_pool_params
@@ -817,14 +829,7 @@ static void __page_pool_destroy(struct page_pool *pool)if (pool->disconnect) pool->disconnect (pool); -ptr_ring_cleanup (&pool->ring,NULL ); - -if (pool->p.flags &PP_FLAG_DMA_MAP ) -put_device (pool->p.dev); - -#ifdef CONFIG_PAGE_POOL_STATS -free_percpu (pool->recycle_stats); -#endif +page_pool_uninit (pool);kfree (pool); }
Signature · 1 line
2.41.0
Re: [PATCH net-next 03/15] net: page_pool: factor out uninit
Quoted 5 lines · Jakub Kicinski
We'll soon (next change in the series) need a fuller unwind path in
page_pool_create()so create the inverse ofpage_pool_init().Reviewed-by Ilias Apalodimas <[email protected]>
Quoted 47 lines
Signed-off-by Jakub Kicinski <[email protected]>--- net/core/page_pool.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)net/core/page_pool.c +13
-9@@ -234,6 +234,18 @@ static int page_pool_init(struct page_pool *pool,return 0 ; } +static void page_pool_uninit (struct page_pool *pool) +{ +ptr_ring_cleanup (&pool->ring,NULL ); + +if (pool->p.flags &PP_FLAG_DMA_MAP ) +put_device (pool->p.dev); + +#ifdef CONFIG_PAGE_POOL_STATS +free_percpu (pool->recycle_stats); +#endif +} +/** * page_pool_create() - create a page pool. * @params: parameters, see struct page_pool_params @@ -817,14 +829,7 @@ static void __page_pool_destroy(struct page_pool *pool)if (pool->disconnect) pool->disconnect (pool); -ptr_ring_cleanup (&pool->ring,NULL ); - -if (pool->p.flags &PP_FLAG_DMA_MAP ) -put_device (pool->p.dev); - -#ifdef CONFIG_PAGE_POOL_STATS -free_percpu (pool->recycle_stats); -#endif +page_pool_uninit (pool);kfree (pool); } --2.41.0
Signature · 2 lines
Thanks, Mina
Jakub Kicinski
maintainerPATCH 4/15 net: page_pool: id the page pools
In reply to Jakub KicinskiTo give ourselves the flexibility of creating netlink commands and ability to refer to page pool instances in uAPIs create IDs for page pools.
--- include/net/page_pool/types.h | 4 ++++ net/core/Makefile | 2 +- net/core/page_pool.c | 21 +++++++++++++++----- net/core/page_pool_priv.h | 9 +++++++++ net/core/page_pool_user.c | 36 +++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 net/core/page_pool_priv.h create mode 100644 net/core/page_pool_user.c
include/net/page_pool/types.h +4 -0
@@ -187,6 +187,10 @@ struct page_pool {/* Slow/Control-path information follows */ struct page_pool_params_slow slow; +/* User-facing fields, protected by page_pools_lock */ +struct { +u32 id; + } user; };struct page *page_pool_alloc_pages (struct page_pool *pool,gfp_t gfp);
net/core/Makefile +1 -1
@@ -18,7 +18,7 @@ obj-y += dev.o dev_addr_lists.o dst.o netevent.o \ obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o obj-y += net-sysfs.o -obj-$(CONFIG_PAGE_POOL) += page_pool.o +obj-$(CONFIG_PAGE_POOL) += page_pool.o page_pool_user.o obj-$(CONFIG_PROC_FS) += net-procfs.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o obj-$(CONFIG_NETPOLL) += netpoll.o
net/core/page_pool.c +16 -5
@@ -23,6 +23,8 @@#include <trace/events/page_pool.h> +#include "page_pool_priv.h" +#define DEFER_TIME (msecs_to_jiffies(1000))#define DEFER_WARN_INTERVAL (60 * HZ)
@@ -260,13 +262,21 @@ struct page_pool *page_pool_create(const struct page_pool_params *params)return ERR_PTR (-ENOMEM ); err =page_pool_init (pool, params); -if (err <0 ) { -pr_warn ("%s() gave up with errno %d\n" , __func__, err); -kfree (pool); -return ERR_PTR (err); - } +if (err <0 ) + goto err_free; + + err =page_pool_list (pool); +if (err) + goto err_uninit;return pool; + +err_uninit: +page_pool_uninit (pool); +err_free: +pr_warn ("%s() gave up with errno %d\n" , __func__, err); +kfree (pool); +return ERR_PTR (err); }EXPORT_SYMBOL (page_pool_create);
@@ -829,6 +839,7 @@ static void __page_pool_destroy(struct page_pool *pool)if (pool->disconnect) pool->disconnect (pool); +page_pool_unlist (pool);page_pool_uninit (pool);kfree (pool); }
net/core/page_pool_priv.h new file +9 -0
@@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PAGE_POOL_PRIV_H +#define __PAGE_POOL_PRIV_H + +int page_pool_list (struct page_pool *pool); +void page_pool_unlist (struct page_pool *pool); + +#endif
net/core/page_pool_user.c new file +36 -0
@@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/mutex.h> +#include <linux/xarray.h> +#include <net/page_pool/types.h> + +#include "page_pool_priv.h" + +static DEFINE_XARRAY_FLAGS(page_pools, XA_FLAGS_ALLOC1); +static DEFINE_MUTEX (page_pools_lock); + +int page_pool_list (struct page_pool *pool) +{ +static u32 id_alloc_next; +int err; + +mutex_lock (&page_pools_lock); + err =xa_alloc_cyclic (&page_pools, &pool->user.id, pool, xa_limit_32b, + &id_alloc_next,GFP_KERNEL ); +if (err <0 ) + goto err_unlock; + +mutex_unlock (&page_pools_lock); +return 0 ; + +err_unlock: +mutex_unlock (&page_pools_lock); +return err; +} + +void page_pool_unlist (struct page_pool *pool) +{ +mutex_lock (&page_pools_lock); +xa_erase (&page_pools, pool->user.id); +mutex_unlock (&page_pools_lock); +}
Signature · 1 line
2.41.0
Re: [PATCH net-next 04/15] net: page_pool: id the page pools
Quoted 6 lines · Jakub Kicinski
To give ourselves the flexibility of creating netlink commands and ability to refer to page pool instances in uAPIs create IDs for page pools.
Signed-off-by Jakub Kicinski <[email protected]>
Quoted 146 lines
--- include/net/page_pool/types.h | 4 ++++ net/core/Makefile | 2 +- net/core/page_pool.c | 21 +++++++++++++++----- net/core/page_pool_priv.h | 9 +++++++++ net/core/page_pool_user.c | 36 +++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 net/core/page_pool_priv.h create mode 100644 net/core/page_pool_user.cinclude/net/page_pool/types.h +4
-0@@ -187,6 +187,10 @@ struct page_pool {/* Slow/Control-path information follows */ struct page_pool_params_slow slow; +/* User-facing fields, protected by page_pools_lock */ +struct { +u32 id; + } user; };struct page *page_pool_alloc_pages (struct page_pool *pool,gfp_t gfp);net/core/Makefile +1
-1@@ -18,7 +18,7 @@ obj-y += dev.o dev_addr_lists.o dst.o netevent.o \ obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o obj-y += net-sysfs.o -obj-$(CONFIG_PAGE_POOL) += page_pool.o +obj-$(CONFIG_PAGE_POOL) += page_pool.o page_pool_user.o obj-$(CONFIG_PROC_FS) += net-procfs.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o obj-$(CONFIG_NETPOLL) += netpoll.onet/core/page_pool.c +16
-5@@ -23,6 +23,8 @@#include <trace/events/page_pool.h> +#include "page_pool_priv.h" +#define DEFER_TIME (msecs_to_jiffies(1000))#define DEFER_WARN_INTERVAL (60 * HZ)@@ -260,13 +262,21 @@ struct page_pool *page_pool_create(const struct page_pool_params *params)return ERR_PTR (-ENOMEM ); err =page_pool_init (pool, params); -if (err <0 ) { -pr_warn ("%s() gave up with errno %d\n" , __func__, err); -kfree (pool); -return ERR_PTR (err); - } +if (err <0 ) + goto err_free; + + err =page_pool_list (pool); +if (err) + goto err_uninit;return pool; + +err_uninit: +page_pool_uninit (pool); +err_free: +pr_warn ("%s() gave up with errno %d\n" , __func__, err); +kfree (pool); +return ERR_PTR (err); }EXPORT_SYMBOL (page_pool_create);@@ -829,6 +839,7 @@ static void __page_pool_destroy(struct page_pool *pool)if (pool->disconnect) pool->disconnect (pool); +page_pool_unlist (pool);page_pool_uninit (pool);kfree (pool); }net/core/page_pool_priv.h new file +9
-0@@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PAGE_POOL_PRIV_H +#define __PAGE_POOL_PRIV_H + +int page_pool_list (struct page_pool *pool); +void page_pool_unlist (struct page_pool *pool); + +#endif net/core/page_pool_user.c new file +36
-1@@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/mutex.h> +#include <linux/xarray.h> +#include <net/page_pool/types.h> + +#include "page_pool_priv.h" + +static DEFINE_XARRAY_FLAGS(page_pools, XA_FLAGS_ALLOC1); +static DEFINE_MUTEX (page_pools_lock); + +int page_pool_list (struct page_pool *pool) +{ +static u32 id_alloc_next; +int err; + +mutex_lock (&page_pools_lock); + err =xa_alloc_cyclic (&page_pools, &pool->user.id, pool, xa_limit_32b, + &id_alloc_next,GFP_KERNEL ); +if (err <0 ) + goto err_unlock; + +mutex_unlock (&page_pools_lock); +return 0 ; + +err_unlock: +mutex_unlock (&page_pools_lock); +return err; +} + +void page_pool_unlist (struct page_pool *pool) +{ +mutex_lock (&page_pools_lock); +xa_erase (&page_pools, pool->user.id); +mutex_unlock (&page_pools_lock); +} --2.41.0
Signature · 2 lines
Thanks, Mina
Ilias Apalodimas
maintainerRe: [PATCH net-next 04/15] net: page_pool: id the page pools
In reply to Jakub KicinskiQuoted 16 lines · Jakub Kicinski
To give ourselves the flexibility of creating netlink commands and ability to refer to page pool instances in uAPIs create IDs for page pools.
Signed-off-by Jakub Kicinski <[email protected]>--- include/net/page_pool/types.h | 4 ++++ net/core/Makefile | 2 +- net/core/page_pool.c | 21 +++++++++++++++----- net/core/page_pool_priv.h | 9 +++++++++ net/core/page_pool_user.c | 36 +++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 net/core/page_pool_priv.h create mode 100644 net/core/page_pool_user.c
[...]
net/core/page_pool_user.c line 21
Quoted 10 lines
net/core/page_pool_user.c new file +10
-0+int page_pool_list (struct page_pool *pool) +{ +static u32 id_alloc_next; +int err; + +mutex_lock (&page_pools_lock); + err =xa_alloc_cyclic (&page_pools, &pool->user.id, pool, xa_limit_32b, + &id_alloc_next,GFP_KERNEL ); +if (err <0 ) + goto err_unlock;
A nit really, but get rid of the if/goto and just let this return err; ?
Quoted 18 lines
net/core/page_pool_user.c new file +15
-1+ +mutex_unlock (&page_pools_lock); +return 0 ; + +err_unlock: +mutex_unlock (&page_pools_lock); +return err; +} + +void page_pool_unlist (struct page_pool *pool) +{ +mutex_lock (&page_pools_lock); +xa_erase (&page_pools, pool->user.id); +mutex_unlock (&page_pools_lock); +} --2.41.0
Jakub Kicinski
maintainerRe: [PATCH net-next 04/15] net: page_pool: id the page pools
Quoted 7 lines · On Thu, 9 Nov 2023 11:21:32 +0200 Ilias Apalodimas wrote:
net/core/page_pool_user.c line 21
Quoted 5 lines
net/core/page_pool_user.c new file +5
-0+mutex_lock (&page_pools_lock); + err =xa_alloc_cyclic (&page_pools, &pool->user.id, pool, xa_limit_32b, + &id_alloc_next,GFP_KERNEL ); +if (err <0 ) + goto err_unlock;A nit really, but get rid of the if/goto and just let this return err; ?
There's more stuff added here by a subsequent patch. It ends up like this:
int page_pool_list(struct page_pool *pool)
{
static u32 id_alloc_next;
int err;
mutex_lock(&page_pools_lock);
err = xa_alloc_cyclic(&page_pools, &pool->user.id, pool, xa_limit_32b,
&id_alloc_next, GFP_KERNEL);
if (err < 0)
goto err_unlock;
if (pool->slow.netdev) {
hlist_add_head(&pool->user.list,
&pool->slow.netdev->page_pools);
pool->user.napi_id = pool->p.napi ? pool->p.napi->napi_id : 0;
netdev_nl_page_pool_event(pool, NETDEV_CMD_PAGE_POOL_ADD_NTF);
}
mutex_unlock(&page_pools_lock);
return 0;err_unlock:
mutex_unlock(&page_pools_lock); return err; }
Do you want me to combine the error and non-error paths? I have a weak preference for not mixing, sometimes err gets set to a positive value and that starts to propagate, unlikely to happen here tho.
Re: [PATCH net-next 04/15] net: page_pool: id the page pools
Quoted 36 lines · Jakub Kicinski
Quoted 7 lines · On Thu, 9 Nov 2023 11:21:32 +0200 Ilias Apalodimas wrote:
net/core/page_pool_user.c line 21
Quoted 5 lines
net/core/page_pool_user.c new file +5
-0+mutex_lock (&page_pools_lock); + err =xa_alloc_cyclic (&page_pools, &pool->user.id, pool, xa_limit_32b, + &id_alloc_next,GFP_KERNEL ); +if (err <0 ) + goto err_unlock;A nit really, but get rid of the if/goto and just let this return err; ?
There's more stuff added here by a subsequent patch. It ends up like this:
int
page_pool_list(struct page_pool *pool){ static u32 id_alloc_next; int err; mutex_lock(&page_pools_lock); err = xa_alloc_cyclic(&page_pools, &pool->user.id, pool, xa_limit_32b, &id_alloc_next, GFP_KERNEL); if (err < 0) goto err_unlock; if (pool->slow.netdev) { hlist_add_head(&pool->user.list, &pool->slow.netdev->page_pools); pool->user.napi_id = pool->p.napi ? pool->p.napi->napi_id : 0; netdev_nl_page_pool_event(pool, NETDEV_CMD_PAGE_POOL_ADD_NTF); } mutex_unlock(&page_pools_lock); return 0;
err_unlock:
Quoted 8 lines
mutex_unlock(&page_pools_lock); return err; }Do you want me to combine the error and non-error paths? I have a weak preference for not mixing, sometimes err gets set to a positive value and that starts to propagate, unlikely to happen here tho.
Nop that's fine -- I actually prefer it myself. I just haven't got the follow up patches yet
Jakub Kicinski
maintainerPATCH 5/15 net: page_pool: record pools per netdev
In reply to Jakub KicinskiLink the page pools with netdevs. This needs to be netns compatible so we have two options. Either we record the pools per netns and have to worry about moving them as the netdev gets moved. Or we record them directly on the netdev so they move with the netdev without any extra work.
Implement the latter option. Since pools may outlast netdev we need a place to store orphans. In time honored tradition use loopback for this purpose.
---
v1: fix race between page pool and netdev disappearing (Simon)
--- include/linux/list.h | 20 ++++++++ include/linux/netdevice.h | 4 ++ include/linux/poison.h | 2 + include/net/page_pool/types.h | 4 ++ net/core/page_pool_user.c | 90 +++++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+)
include/linux/list.h +20 -0
@@ -1111,6 +1111,26 @@ static inline void hlist_move_list(struct hlist_head *old, old->first =NULL ; } +/** +* hlist_splice_init() - move all entries from one list to another +* @from: hlist_head from which entries will be moved +* @last: last entry on the @from list +* @to: hlist_head to which entries will be moved +* +* @to can be empty, @from must contain at least @last. +*/ +static inline void hlist_splice_init (struct hlist_head *from, +struct hlist_node *last, +struct hlist_head *to) +{ +if (to->first) + to->first->pprev = &last->next; + last->next = to->first; + to->first = from->first; + from->first->pprev = &to->first; + from->first =NULL ; +} +#define hlist_entry (ptr, type, member) container_of(ptr,type,member)#define hlist_for_each (pos, head) \
include/linux/netdevice.h +4 -0
@@ -2431,6 +2431,10 @@ struct net_device {#if IS_ENABLED (CONFIG_DPLL )struct dpll_pin *dpll_pin;#endif +#if IS_ENABLED (CONFIG_PAGE_POOL ) +/** @page_pools: page pools created for this netdevice */ +struct hlist_head page_pools; +#endif };#define to_net_dev (d) container_of(d, struct net_device, dev)
include/linux/poison.h +2 -0
@@ -83,6 +83,8 @@/********** net/core/skbuff.c **********/ #define SKB_LIST_POISON_NEXT ((void *)(0x800 + POISON_POINTER_DELTA)) +/********** net/ **********/ +#define NET_PTR_POISON ((void *)(0x801 + POISON_POINTER_DELTA))/********** kernel/bpf/ **********/ #define BPF_PTR_POISON ((void *)(0xeB9FUL + POISON_POINTER_DELTA))
include/net/page_pool/types.h +4 -0
@@ -5,6 +5,7 @@#include <linux/dma-direction.h> #include <linux/ptr_ring.h> +#include <linux/types.h> #define PP_FLAG_DMA_MAP BIT(0)/* Should page_pool do the DMA * map/unmap
@@ -48,6 +49,7 @@ struct pp_alloc_cache {* @pool_size: size of the ptr_ring * @nid: NUMA node id to allocate from pages from * @dev: device, for DMA pre-mapping purposes +* @netdev: netdev this pool will serve (leave as NULL if none or multiple) * @napi: NAPI which is the sole consumer of pages, otherwise NULL * @dma_dir: DMA mapping direction * @max_len: max DMA sync memory size for PP_FLAG_DMA_SYNC_DEV
@@ -66,6 +68,7 @@ struct page_pool_params {unsigned int offset; );struct_group_tagged (page_pool_params_slow, slow, + struct net_device *netdev;/* private: used by test code only */ void (*init_callback)(struct page *page,void *arg);void *init_arg;
@@ -189,6 +192,7 @@ struct page_pool {struct page_pool_params_slow slow;/* User-facing fields, protected by page_pools_lock */ struct { +struct hlist_node list;u32 id; } user; };
net/core/page_pool_user.c +90 -0
115 lines not on this page · raw message
Signature · 1 line
2.41.0
Re: [PATCH net-next 05/15] net: page_pool: record pools per netdev
Quoted 10 lines · Jakub Kicinski
Link the page pools with netdevs. This needs to be netns compatible so we have two options. Either we record the pools per netns and have to worry about moving them as the netdev gets moved. Or we record them directly on the netdev so they move with the netdev without any extra work.
Implement the latter option. Since pools may outlast netdev we need a place to store orphans. In time honored tradition use loopback for this purpose.
blackhole_netdev might be a better choice than loopback
Jakub Kicinski
maintainerRe: [PATCH net-next 05/15] net: page_pool: record pools per netdev
Quoted 7 lines · On Tue, 24 Oct 2023 11:31:46 -0600 David Ahern wrote:
Quoted 6 lines · Jakub Kicinski
Link the page pools with netdevs. This needs to be netns compatible so we have two options. Either we record the pools per netns and have to worry about moving them as the netdev gets moved. Or we record them directly on the netdev so they move with the netdev without any extra work.
Quoted 5 lines
Quoted 3 lines
Implement the latter option. Since pools may outlast netdev we need a place to store orphans. In time honored tradition use loopback for this purpose.
blackhole_netdevmight be a better choice than loopback
With loopback we still keep it per netns, and still accessible via netlink.
Re: [PATCH net-next 05/15] net: page_pool: record pools per netdev
Quoted 8 lines · Jakub Kicinski
Quoted 7 lines · On Tue, 24 Oct 2023 11:31:46 -0600 David Ahern wrote:
Quoted 6 lines · Jakub Kicinski
Link the page pools with netdevs. This needs to be netns compatible so we have two options. Either we record the pools per netns and have to worry about moving them as the netdev gets moved. Or we record them directly on the netdev so they move with the netdev without any extra work.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Implement the latter option. Since pools may outlast netdev we need a place to store orphans. In time honored tradition use loopback for this purpose.
blackhole_netdevmight be a better choice than loopbackWith loopback we still keep it per netns, and still accessible via netlink.
and if the namespace and loopback device get deleted?
Jakub Kicinski
maintainerRe: [PATCH net-next 05/15] net: page_pool: record pools per netdev
Quoted 6 lines · On Tue, 24 Oct 2023 13:19:35 -0600 David Ahern wrote:
Quoted 4 lines
Quoted 1 line
blackhole_netdevmight be a better choice than loopbackWith loopback we still keep it per netns, and still accessible via netlink.
and if the namespace and loopback device get deleted?
For now we're back to printing the warning to dmesg.
My instinct is that such double-orphans (both netdev and netns disappeared) should be reported via a separate netlink command accessible in init_net only. But that's for later, I hope falling back to the print is good enough for now.
Re: [PATCH net-next 05/15] net: page_pool: record pools per netdev
In reply to Jakub KicinskiQuoted 58 lines · Jakub Kicinski
Link the page pools with netdevs. This needs to be netns compatible so we have two options. Either we record the pools per netns and have to worry about moving them as the netdev gets moved. Or we record them directly on the netdev so they move with the netdev without any extra work.
Implement the latter option. Since pools may outlast netdev we need a place to store orphans. In time honored tradition use loopback for this purpose.
Signed-off-by Jakub Kicinski <[email protected]>---
v1: fix race between page pool and netdev disappearing (Simon)--- include/linux/list.h | 20 ++++++++ include/linux/netdevice.h | 4 ++ include/linux/poison.h | 2 + include/net/page_pool/types.h | 4 ++ net/core/page_pool_user.c | 90 +++++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+)include/linux/list.h +20
-0@@ -1111,6 +1111,26 @@ static inline void hlist_move_list(struct hlist_head *old, old->first =NULL ; } +/** +* hlist_splice_init() - move all entries from one list to another +* @from: hlist_head from which entries will be moved +* @last: last entry on the @from list +* @to: hlist_head to which entries will be moved +* +* @to can be empty, @from must contain at least @last. +*/ +static inline void hlist_splice_init (struct hlist_head *from, +struct hlist_node *last, +struct hlist_head *to) +{ +if (to->first) + to->first->pprev = &last->next; + last->next = to->first; + to->first = from->first; + from->first->pprev = &to->first; + from->first =NULL ; +} +#define hlist_entry (ptr, type, member) container_of(ptr,type,member)#define hlist_for_each (pos, head) \include/linux/netdevice.h +0
-0
include/linux/netdevice.h struct net_device { · line 2437
Quoted 7 lines
include/linux/netdevice.h +4
-0@@ -2431,6 +2431,10 @@ struct net_device {#if IS_ENABLED (CONFIG_DPLL )struct dpll_pin *dpll_pin;#endif +#if IS_ENABLED (CONFIG_PAGE_POOL ) +/** @page_pools: page pools created for this netdevice */ +struct hlist_head page_pools; +#endif
I wonder if this per netdev field is really necessary. Is it not possible to do the same simply looping over the (global) page_pools xarray? Or is that too silly of an idea. I guess on some systems you may end up with 100s or 1000s of active or orphaned page pools and then globally iterating over the whole page_pools xarray can be really slow..
Quoted 29 lines
include/linux/netdevice.h +0
-0};#define to_net_dev (d) container_of(d, struct net_device, dev)include/linux/poison.h +2
-0@@ -83,6 +83,8 @@/********** net/core/skbuff.c **********/ #define SKB_LIST_POISON_NEXT ((void *)(0x800 + POISON_POINTER_DELTA)) +/********** net/ **********/ +#define NET_PTR_POISON ((void *)(0x801 + POISON_POINTER_DELTA))/********** kernel/bpf/ **********/ #define BPF_PTR_POISON ((void *)(0xeB9FUL + POISON_POINTER_DELTA))include/net/page_pool/types.h +1
-0@@ -5,6 +5,7 @@#include <linux/dma-direction.h> #include <linux/ptr_ring.h> +#include <linux/types.h> #define PP_FLAG_DMA_MAP BIT(0)/* Should page_pool do the DMA * map/unmap
include/net/page_pool/types.h struct pp_alloc_cache { · line 52
Quoted 4 lines
include/net/page_pool/types.h +1
-0@@ -48,6 +49,7 @@ struct pp_alloc_cache {* @pool_size: size of the ptr_ring * @nid: NUMA node id to allocate from pages from * @dev: device, for DMA pre-mapping purposes +* @netdev: netdev this pool will serve (leave as NULL if none or multiple)
Is this an existing use case (page_pools that serve null or multiple netdevs), or a future use case? My understanding is that currently page_pools serve at most 1 rx-queue. Spot checking a few drivers that seems to be true.
Quoted 115 lines
include/net/page_pool/types.h +2
-0* @napi: NAPI which is the sole consumer of pages, otherwise NULL * @dma_dir: DMA mapping direction * @max_len: max DMA sync memory size for PP_FLAG_DMA_SYNC_DEV @@ -66,6 +68,7 @@ struct page_pool_params {unsigned int offset; );struct_group_tagged (page_pool_params_slow, slow, + struct net_device *netdev;/* private: used by test code only */ void (*init_callback)(struct page *page,void *arg);void *init_arg;@@ -189,6 +192,7 @@ struct page_pool {struct page_pool_params_slow slow;/* User-facing fields, protected by page_pools_lock */ struct { +struct hlist_node list;u32 id; } user; };net/core/page_pool_user.c +64
-0@@ -1,14 +1,31 @@// SPDX-License-Identifier: GPL-2.0 #include <linux/mutex.h> +#include <linux/netdevice.h> #include <linux/xarray.h> +#include <net/net_debug.h> #include <net/page_pool/types.h> #include "page_pool_priv.h" static DEFINE_XARRAY_FLAGS(page_pools, XA_FLAGS_ALLOC1); +/* Protects: page_pools, netdevice->page_pools, pool->slow.netdev, pool->user. +* Ordering: inside rtnl_lock +*/ static DEFINE_MUTEX (page_pools_lock); +/* Page pools are only reachable from user space (via netlink) if they are +* linked to a netdev at creation time. Following page pool "visibility" +* states are possible: +* - normal +* - user.list: linked to real netdev, netdev: real netdev +* - orphaned - real netdev has disappeared +* - user.list: linked to lo, netdev: lo +* - invisible - either (a) created without netdev linking, (b) unlisted due +* to error, or (c) the entire namespace which owned this pool disappeared +* - user.list: unhashed, netdev: unknown +*/ +int page_pool_list (struct page_pool *pool) {static u32 id_alloc_next;@@ -20,6 +37,10 @@ int page_pool_list(struct page_pool *pool)if (err <0 ) goto err_unlock; +if (pool->slow.netdev) +hlist_add_head (&pool->user.list, + &pool->slow.netdev->page_pools); +mutex_unlock (&page_pools_lock);return 0 ;@@ -32,5 +53,74 @@ void page_pool_unlist(struct page_pool *pool) {mutex_lock (&page_pools_lock);xa_erase (&page_pools, pool->user.id); +hlist_del (&pool->user.list);mutex_unlock (&page_pools_lock); } + +static void page_pool_unreg_netdev_wipe (struct net_device *netdev) +{ +struct page_pool *pool; +struct hlist_node *n; + +mutex_lock (&page_pools_lock); +hlist_for_each_entry_safe (pool, n, &netdev->page_pools, user.list) { +hlist_del_init (&pool->user.list); + pool->slow.netdev =NET_PTR_POISON ; + } +mutex_unlock (&page_pools_lock); +} + +static void page_pool_unreg_netdev (struct net_device *netdev) +{ +struct page_pool *pool, *last; +struct net_device *lo; + + lo =__dev_get_by_index (dev_net (netdev),1 ); +if (!lo) { +netdev_err_once (netdev, +"can't get lo to store orphan page pools\n" ); +page_pool_unreg_netdev_wipe (netdev); +return ; + } + +mutex_lock (&page_pools_lock); + last =NULL ; +hlist_for_each_entry (pool, &netdev->page_pools, user.list) { + pool->slow.netdev = lo; + last = pool; + } +if (last) +hlist_splice_init (&netdev->page_pools, &last->user.list, + &lo->page_pools); +mutex_unlock (&page_pools_lock); +} + +static int +page_pool_netdevice_event (struct notifier_block *nb, +unsigned long event,void *ptr)
net/core/page_pool_user.c void page_pool_unlist(struct page_pool *pool) · line 110
Quoted 10 lines
net/core/page_pool_user.c +10
-0+{ +struct net_device *netdev =netdev_notifier_info_to_dev (ptr); + +if (event !=NETDEV_UNREGISTER ) +return NOTIFY_DONE ; + +if (hlist_empty (&netdev->page_pools)) +return NOTIFY_OK ; + +if (netdev->ifindex !=1 )
I'm guessing 1 is _always_ loopback?
Quoted 17 lines
net/core/page_pool_user.c +16
-1+page_pool_unreg_netdev (netdev); + else +page_pool_unreg_netdev_wipe (netdev ); +return NOTIFY_OK ; +} + +static struct notifier_block page_pool_netdevice_nb = { + .notifier_call = page_pool_netdevice_event, +}; + +static int __init page_pool_user_init (void ) +{ +return register_netdevice_notifier (&page_pool_netdevice_nb); +} + +subsys_initcall (page_pool_user_init); --
Quoted 2 lines
2.41.0
--
Thanks,
Mina
Jakub Kicinski
maintainerRe: [PATCH net-next 05/15] net: page_pool: record pools per netdev
Quoted 11 lines · On Wed, 25 Oct 2023 12:56:44 -0700 Mina Almasry wrote:
include/linux/netdevice.h struct net_device { · line 2437
Quoted 4 lines
include/linux/netdevice.h +4
-0+#if IS_ENABLED (CONFIG_PAGE_POOL ) +/** @page_pools: page pools created for this netdevice */ +struct hlist_head page_pools; +#endif I wonder if this per netdev field is really necessary. Is it not possible to do the same simply looping over the (global)
page_poolsxarray? Or is that too silly of an idea. I guess on some systems you may end up with 100s or 1000s of active or orphaned page pools and then globally iterating over the wholepage_poolsxarray can be really slow..
I think we want the per-netdev hlist either way, on netdev unregistration we need to find its pools to clear the pointers. At which point we can as well use that to dump the pools.
I don't see a strong reason to use one approach over the other. Note that other objects like napi and queues (WIP patches) also walk netdevs and dump sub-objects from them.
Quoted 10 lines
include/net/page_pool/types.h struct pp_alloc_cache { · line 52
Quoted 5 lines
include/net/page_pool/types.h +1
-0@@ -48,6 +49,7 @@ struct pp_alloc_cache {* @pool_size: size of the ptr_ring * @nid: NUMA node id to allocate from pages from * @dev: device, for DMA pre-mapping purposes +* @netdev: netdev this pool will serve (leave as NULL if none or multiple) Is this an existing use case (
page_poolsthat serve null or multiple netdevs), or a future use case? My understanding is that currentlypage_poolsserve at most 1 rx-queue. Spot checking a few drivers that seems to be true.
I think I saw one embedded driver for a switch-like device which has queues servicing all ports, and therefore netdevs.
We'd need some help from people using such devices to figure out what the right way to represent them is, and what extra bits of functionality they need.
Quoted 1 line
I'm guessing 1 is _always_ loopback?
AFAIK, yes. I should probably use LOOPBACK_IFINDEX, to make it clearer.
Mina Almasry
Reviewed-byRe: [PATCH net-next 05/15] net: page_pool: record pools per netdev
Quoted 14 lines · Jakub Kicinski
Quoted 11 lines · On Wed, 25 Oct 2023 12:56:44 -0700 Mina Almasry wrote:
include/linux/netdevice.h struct net_device { · line 2437
Quoted 4 lines
include/linux/netdevice.h +4
-0+#if IS_ENABLED (CONFIG_PAGE_POOL ) +/** @page_pools: page pools created for this netdevice */ +struct hlist_head page_pools; +#endif I wonder if this per netdev field is really necessary. Is it not possible to do the same simply looping over the (global)
page_poolsxarray? Or is that too silly of an idea. I guess on some systems you may end up with 100s or 1000s of active or orphaned page pools and then globally iterating over the wholepage_poolsxarray can be really slow..
Quoted 8 lines
I think we want the per-netdev hlist either way, on netdev unregistration we need to find its pools to clear the pointers. At which point we can as well use that to dump the pools.
I don't see a strong reason to use one approach over the other. Note that other objects like napi and queues (WIP patches) also walk netdevs and dump sub-objects from them.
Sorry for the very late reply.
I'm not sure we need a per-netdev hlist either way, seems to me looping over the global list and filterying by pp->netdev is the equivalent of looping over the per-netdev hlist, I think.
The reason I was suggesting to only do the global list is because when the same info is stored in 2 places you may end up with the info going out of sync. Here, netdev->page_pools needs to match the entries in each pp->netdev. But, I think your approach is more reasonable as looping over the global array at all times may be a pain if there are lots of page_pools on the system. I also can't find any issues with how you're keeping netdev->page_pools & the page_pools->netdev, so FWIW:
Quoted 20 lines
Quoted 10 lines
include/net/page_pool/types.h struct pp_alloc_cache { · line 52
Quoted 5 lines
include/net/page_pool/types.h +1
-0@@ -48,6 +49,7 @@ struct pp_alloc_cache {* @pool_size: size of the ptr_ring * @nid: NUMA node id to allocate from pages from * @dev: device, for DMA pre-mapping purposes +* @netdev: netdev this pool will serve (leave as NULL if none or multiple) Is this an existing use case (
page_poolsthat serve null or multiple netdevs), or a future use case? My understanding is that currentlypage_poolsserve at most 1 rx-queue. Spot checking a few drivers that seems to be true.I think I saw one embedded driver for a switch-like device which has queues servicing all ports, and therefore netdevs.
We'd need some help from people using such devices to figure out what the right way to represent them is, and what extra bits of functionality they need.Quoted 1 line
I'm guessing 1 is _always_ loopback?
AFAIK, yes. I should probably use
LOOPBACK_IFINDEX, to make it clearer.
Signature · 2 lines
Thanks, Mina
Jakub Kicinski
maintainerPATCH 6/15 net: page_pool: stash the NAPI ID for easier access
In reply to Jakub KicinskiTo avoid any issues with race conditions on accessing napi and having to think about the lifetime of NAPI objects in netlink GET - stash the napi_id to which page pool was linked at creation time.
--- include/net/page_pool/types.h | 1 + net/core/page_pool_user.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-)
include/net/page_pool/types.h +1 -0
@@ -193,6 +193,7 @@ struct page_pool {/* User-facing fields, protected by page_pools_lock */ struct {struct hlist_node list; +u32 napi_id;u32 id; } user; };
net/core/page_pool_user.c +3 -1
@@ -37,9 +37,11 @@ int page_pool_list(struct page_pool *pool)if (err <0 ) goto err_unlock; -if (pool->slow.netdev) +if (pool->slow.netdev) {hlist_add_head (&pool->user.list, &pool->slow.netdev->page_pools); + pool->user.napi_id = pool->p.napi ? pool->p.napi->napi_id :0 ; + }mutex_unlock (&page_pools_lock);return 0 ;
Signature · 1 line
2.41.0
Jakub Kicinski
maintainerPATCH 7/15 eth: link netdev to page_pools in drivers
In reply to Jakub KicinskiLink page pool instances to netdev for the drivers which already link to NAPI. Unless the driver is doing something very weird per-NAPI should imply per-netdev.
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 + drivers/net/ethernet/microsoft/mana/mana_en.c | 1 + 3 files changed, 3 insertions(+)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +1 -0
@@ -3298,6 +3298,7 @@ static int bnxt_alloc_rx_page_pool(struct bnxt *bp, pp.pool_size += bp->rx_ring_size; pp.nid =dev_to_node (&bp->pdev->dev); pp.napi = &rxr->bnapi->napi; + pp.netdev = bp->dev; pp.dev = &bp->pdev->dev; pp.dma_dir = bp->rx_dir; pp.max_len =PAGE_SIZE ;
drivers/net/ethernet/mellanox/mlx5/core/en_main.c +1 -0
@@ -902,6 +902,7 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, pp_params.nid = node; pp_params.dev = rq->pdev; pp_params.napi = rq->cq.napi; + pp_params.netdev = rq->netdev; pp_params.dma_dir = rq->buff.map_dir; pp_params.max_len =PAGE_SIZE ;
drivers/net/ethernet/microsoft/mana/mana_en.c +1 -0
@@ -2137,6 +2137,7 @@ static int mana_create_page_pool(struct mana_rxq *rxq, struct gdma_context *gc) pprm.pool_size =RX_BUFFERS_PER_QUEUE ; pprm.nid = gc->numa_node; pprm.napi = &rxq->rx_cq.napi; + pprm.netdev = rxq->ndev; rxq->page_pool =page_pool_create (&pprm);
Signature · 1 line
2.41.0
Ilias Apalodimas
maintainerRe: [PATCH net-next 07/15] eth: link netdev to page_pools in drivers
Hi Jakub,
Quoted 11 lines · Jakub Kicinski
Link page pool instances to netdev for the drivers which already link to NAPI. Unless the driver is doing something very weird per-NAPI should imply per-netdev.
Signed-off-by Jakub Kicinski <[email protected]>--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 + drivers/net/ethernet/microsoft/mana/mana_en.c | 1 + 3 files changed, 3 insertions(+)
Mind add a line for netsec.c (probably in netsec_setup_rx_dring), since that's the only NIC I currently have around with pp support?
[...]
Thanks
/Ilias
Jakub Kicinski
maintainerRe: [PATCH net-next 07/15] eth: link netdev to page_pools in drivers
Quoted 8 lines · On Thu, 9 Nov 2023 11:11:04 +0200 Ilias Apalodimas wrote:
Quoted 5 lines
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 + drivers/net/ethernet/microsoft/mana/mana_en.c | 1 + 3 files changed, 3 insertions(+)Mind add a line for
netsec.c(probably innetsec_setup_rx_dring), since that's the only NIC I currently have around with pp support?
It's a single queue / single napi device? So like this?
drivers/net/ethernet/socionext/netsec.c +2 -0
@@ -1302,6 +1302,8 @@ static int netsec_setup_rx_dring(struct netsec_priv *priv) .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, .offset = NETSEC_RXBUF_HEADROOM, .max_len = NETSEC_RX_BUF_SIZE, + .napi = priv->napi, + .netdev = priv->ndev, };int i, err;
Ilias Apalodimas
maintainerRe: [PATCH net-next 07/15] eth: link netdev to page_pools in drivers
Quoted 8 lines · Jakub Kicinski
Quoted 6 lines · On Thu, 9 Nov 2023 11:11:04 +0200 Ilias Apalodimas wrote:
Quoted 5 lines
--- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 + drivers/net/ethernet/microsoft/mana/mana_en.c | 1 + 3 files changed, 3 insertions(+)
Quoted 4 lines
Quoted 2 lines
Mind add a line for
netsec.c(probably innetsec_setup_rx_dring), since that's the only NIC I currently have around with pp support?It's a single queue / single napi device? So like this?
Yes it is
drivers/net/ethernet/socionext/netsec.c static int netsec_setup_rx_dring(struct netsec_priv *priv) · line 1306
Quoted 11 lines
drivers/net/ethernet/socionext/netsec.c +2
-0@@ -1302,6 +1302,8 @@ static int netsec_setup_rx_dring(struct netsec_priv *priv) .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, .offset = NETSEC_RXBUF_HEADROOM, .max_len = NETSEC_RX_BUF_SIZE, + .napi = priv->napi, + .netdev = priv->ndev,
Yea
Thanks
/Ilias
Quoted 3 lines
drivers/net/ethernet/socionext/netsec.c +0
-0};int i, err;
Jakub Kicinski
maintainerPATCH 8/15 net: page_pool: add nlspec for basic access to page pools
In reply to Jakub KicinskiAdd a Netlink spec in YAML for getting very basic information about page pools.
--- Documentation/netlink/specs/netdev.yaml | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
Documentation/netlink/specs/netdev.yaml +45 -0
@@ -86,6 +86,34 @@ name: netdev See Documentation/networking/xdp-rx-metadata.rst for more details. type: u64 enum: xdp-rx-metadata + - + name: page-pool + attributes: + - + name: id + doc: Unique ID of a Page Pool instance. + type: uint + checks: + min: 1 + max: u32-max + - + name: ifindex + doc: | + ifindex of the netdev to which the pool belongs. + May be reported as 0 if the page pool was allocated for a netdev + which got destroyed already (page pools may outlast their netdevs + because they wait for all memory to be returned). + type: u32 + checks: + min: 1 + max: s32-max + - + name: napi-id + doc: Id of NAPI using this Page Pool instance. + type: uint + checks: + min: 1 + max: u32-max operations: list:
@@ -120,6 +148,23 @@ name: netdev doc: Notification about device configuration being changed. notify: dev-get mcgrp: mgmt + - + name: page-pool-get + doc: | + Get / dump information about Page Pools. + (Only Page Pools associated with a net_device can be listed.) + attribute-set: page-pool + do: + request: + attributes: + - id + reply: &pp-reply + attributes: + - id + - ifindex + - napi-id + dump: + reply: *pp-reply mcast-groups: list:
Signature · 1 line
2.41.0
Jakub Kicinski
maintainerPATCH 9/15 net: page_pool: implement GET in the netlink API
In reply to Jakub KicinskiExpose the very basic page pool information via netlink.
Example using ynl-py for a system with 9 queues:
$ ./cli.py --no-schema --spec netlink/specs/netdev.yaml \
--dump page-pool-get
[{'id': 19, 'ifindex': 2, 'napi-id': 147},
{'id': 18, 'ifindex': 2, 'napi-id': 146},
{'id': 17, 'ifindex': 2, 'napi-id': 145},
{'id': 16, 'ifindex': 2, 'napi-id': 144},
{'id': 15, 'ifindex': 2, 'napi-id': 143},
{'id': 14, 'ifindex': 2, 'napi-id': 142},
{'id': 13, 'ifindex': 2, 'napi-id': 141},
{'id': 12, 'ifindex': 2, 'napi-id': 140},
{'id': 11, 'ifindex': 2, 'napi-id': 139},
{'id': 10, 'ifindex': 2, 'napi-id': 138}]--- include/uapi/linux/netdev.h | 10 +++ net/core/netdev-genl-gen.c | 23 +++++++ net/core/netdev-genl-gen.h | 3 + net/core/page_pool_user.c | 127 ++++++++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+)
include/uapi/linux/netdev.h +10 -0
@@ -64,11 +64,21 @@ enum {NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX -1 ) }; +enum { +NETDEV_A_PAGE_POOL_ID =1 , +NETDEV_A_PAGE_POOL_IFINDEX , +NETDEV_A_PAGE_POOL_NAPI_ID , + + __NETDEV_A_PAGE_POOL_MAX, +NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX -1 ) +}; +enum {NETDEV_CMD_DEV_GET =1 ,NETDEV_CMD_DEV_ADD_NTF ,NETDEV_CMD_DEV_DEL_NTF ,NETDEV_CMD_DEV_CHANGE_NTF , +NETDEV_CMD_PAGE_POOL_GET , __NETDEV_CMD_MAX,NETDEV_CMD_MAX = (__NETDEV_CMD_MAX -1 )
net/core/netdev-genl-gen.c +23 -0
@@ -10,11 +10,22 @@#include <uapi/linux/netdev.h> +/* Integer value ranges */ +struct netlink_range_validation netdev_a_page_pool_id_range = { + .min =1 , + .max =4294967295 , +}; +/* NETDEV_CMD_DEV_GET - do */ static const struct nla_policy netdev_dev_get_nl_policy[NETDEV_A_DEV_IFINDEX +1 ] = { [NETDEV_A_DEV_IFINDEX ] =NLA_POLICY_MIN (NLA_U32 ,1 ), }; +/* NETDEV_CMD_PAGE_POOL_GET - do */ +static const struct nla_policy netdev_page_pool_get_nl_policy[NETDEV_A_PAGE_POOL_ID +1 ] = { + [NETDEV_A_PAGE_POOL_ID ] =NLA_POLICY_FULL_RANGE (NLA_UINT , &netdev_a_page_pool_id_range), +}; +/* Ops table for netdev */ static const struct genl_split_ops netdev_nl_ops[] = { {
@@ -29,6 +40,18 @@ static const struct genl_split_ops netdev_nl_ops[] = { .dumpit = netdev_nl_dev_get_dumpit, .flags = GENL_CMD_CAP_DUMP, }, + { + .cmd = NETDEV_CMD_PAGE_POOL_GET, + .doit = netdev_nl_page_pool_get_doit, + .policy = netdev_page_pool_get_nl_policy, + .maxattr = NETDEV_A_PAGE_POOL_ID, + .flags = GENL_CMD_CAP_DO, + }, + { + .cmd = NETDEV_CMD_PAGE_POOL_GET, + .dumpit = netdev_nl_page_pool_get_dumpit, + .flags = GENL_CMD_CAP_DUMP, + }, }; static const struct genl_multicast_group netdev_nl_mcgrps[] = {
net/core/netdev-genl-gen.h +3 -0
@@ -13,6 +13,9 @@int netdev_nl_dev_get_doit (struct sk_buff *skb,struct genl_info *info);int netdev_nl_dev_get_dumpit (struct sk_buff *skb,struct netlink_callback *cb); +int netdev_nl_page_pool_get_doit (struct sk_buff *skb,struct genl_info *info); +int netdev_nl_page_pool_get_dumpit (struct sk_buff *skb, +struct netlink_callback *cb);enum {NETDEV_NLGRP_MGMT ,
net/core/page_pool_user.c +127 -0
141 lines not on this page · raw message
Signature · 1 line
2.41.0
kernel test robot
build botRe: [PATCH net-next 09/15] net: page_pool: implement GET in the netlink API
kernel test robot noticed the following build errors:
Hi Jakub,
kernel test robot noticed the following build errors:
[auto build test ERROR on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Jakub-Kicinski/net-page_pool-split-the-page_pool_params-into-fast-and-slow/20231025-023128 base: net-next/main patch link: https://lore.kernel.org/r/20231024160220.3973311-10-kuba%40kernel.org patch subject: [PATCH net-next 09/15] net: page_pool: implement GET in the netlink API config: um-allnoconfig (https://download.01.org/0day-ci/archive/20231025/[email protected]/config) compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project.git 4a5ac14ee968ff0ad5d2cc1ffa0299048db4c88a) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231025/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
/usr/bin/ld: init/main.o: warning: relocation in read-only section `.ref.text' /usr/bin/ld: warning: .tmp_vmlinux.kallsyms1 has a LOAD segment with RWX permissions
>> /usr/bin/ld: net/core/netdev-genl-gen.o:(.rodata+0x98): undefined reference to `netdev_nl_page_pool_get_doit' >> /usr/bin/ld: net/core/netdev-genl-gen.o:(.rodata+0xc0): undefined reference to `netdev_nl_page_pool_get_dumpit'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
kernel test robot
build botRe: [PATCH net-next 09/15] net: page_pool: implement GET in the netlink API
In reply to Jakub Kicinskikernel test robot noticed the following build errors:
Hi Jakub,
kernel test robot noticed the following build errors:
[auto build test ERROR on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Jakub-Kicinski/net-page_pool-split-the-page_pool_params-into-fast-and-slow/20231025-023128 base: net-next/main patch link: https://lore.kernel.org/r/20231024160220.3973311-10-kuba%40kernel.org patch subject: [PATCH net-next 09/15] net: page_pool: implement GET in the netlink API config: csky-defconfig (https://download.01.org/0day-ci/archive/20231026/[email protected]/config) compiler: csky-linux-gcc (GCC) 13.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231026/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
>> csky-linux-ld: net/core/netdev-genl-gen.o:(.rodata+0x48): undefined reference to `netdev_nl_page_pool_get_doit' >> csky-linux-ld: net/core/netdev-genl-gen.o:(.rodata+0x60): undefined reference to `netdev_nl_page_pool_get_dumpit'
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Jakub Kicinski
maintainerPATCH 10/15 net: page_pool: add netlink notifications for state changes
In reply to Jakub KicinskiGenerate netlink notifications about page pool state changes.
--- Documentation/netlink/specs/netdev.yaml | 17 ++++++++++++ include/uapi/linux/netdev.h | 4 +++ net/core/netdev-genl-gen.c | 1 + net/core/netdev-genl-gen.h | 1 + net/core/page_pool_user.c | 36 +++++++++++++++++++++++++ 5 files changed, 59 insertions(+)
Documentation/netlink/specs/netdev.yaml +17 -0
@@ -165,8 +165,25 @@ name: netdev - napi-id dump: reply: *pp-reply + - + name: page-pool-add-ntf + doc: Notification about page pool appearing. + notify: page-pool-get + mcgrp: page-pool + - + name: page-pool-del-ntf + doc: Notification about page pool disappearing. + notify: page-pool-get + mcgrp: page-pool + - + name: page-pool-change-ntf + doc: Notification about page pool configuration being changed. + notify: page-pool-get + mcgrp: page-pool mcast-groups: list: - name: mgmt + - + name: page-pool
include/uapi/linux/netdev.h +4 -0
@@ -79,11 +79,15 @@ enum {NETDEV_CMD_DEV_DEL_NTF ,NETDEV_CMD_DEV_CHANGE_NTF ,NETDEV_CMD_PAGE_POOL_GET , +NETDEV_CMD_PAGE_POOL_ADD_NTF , +NETDEV_CMD_PAGE_POOL_DEL_NTF , +NETDEV_CMD_PAGE_POOL_CHANGE_NTF , __NETDEV_CMD_MAX,NETDEV_CMD_MAX = (__NETDEV_CMD_MAX -1 ) };#define NETDEV_MCGRP_MGMT "mgmt" +#define NETDEV_MCGRP_PAGE_POOL "page-pool"#endif /* _UAPI_LINUX_NETDEV_H */
net/core/netdev-genl-gen.c +1 -0
@@ -56,6 +56,7 @@ static const struct genl_split_ops netdev_nl_ops[] = {static const struct genl_multicast_group netdev_nl_mcgrps[] = { [NETDEV_NLGRP_MGMT ] = {"mgmt" , }, + [NETDEV_NLGRP_PAGE_POOL ] = {"page-pool" , }, };struct genl_family netdev_nl_family__ro_after_init = {
net/core/netdev-genl-gen.h +1 -0
@@ -19,6 +19,7 @@ int netdev_nl_page_pool_get_dumpit(struct sk_buff *skb,enum {NETDEV_NLGRP_MGMT , +NETDEV_NLGRP_PAGE_POOL , };extern struct genl_family netdev_nl_family;
net/core/page_pool_user.c +36 -0
@@ -135,6 +135,37 @@ page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool,return -EMSGSIZE ; } +static void netdev_nl_page_pool_event (const struct page_pool *pool,u32 cmd) +{ +struct genl_info info; +struct sk_buff *ntf; +struct net *net; + +lockdep_assert_held (&page_pools_lock); + +/* 'invisible' page pools don't matter */ +if (hlist_unhashed (&pool->user.list)) +return ; + net =dev_net (pool->slow.netdev); + +if (!genl_has_listeners (&netdev_nl_family, net,NETDEV_NLGRP_PAGE_POOL )) +return ; + +genl_info_init_ntf (&info, &netdev_nl_family, cmd); + + ntf =genlmsg_new (GENLMSG_DEFAULT_SIZE ,GFP_KERNEL ); +if (!ntf) +return ; + +if (page_pool_nl_fill (ntf, pool, &info)) { +nlmsg_free (ntf); +return ; + } + +genlmsg_multicast_netns (&netdev_nl_family, net, ntf, +0 ,NETDEV_NLGRP_PAGE_POOL ,GFP_KERNEL ); +} +int netdev_nl_page_pool_get_doit (struct sk_buff *skb,struct genl_info *info) {u32 id;
@@ -168,6 +199,8 @@ int page_pool_list(struct page_pool *pool)hlist_add_head (&pool->user.list, &pool->slow.netdev->page_pools); pool->user.napi_id = pool->p.napi ? pool->p.napi->napi_id :0 ; + +netdev_nl_page_pool_event (pool,NETDEV_CMD_PAGE_POOL_ADD_NTF ); }mutex_unlock (&page_pools_lock);
@@ -181,6 +214,7 @@ int page_pool_list(struct page_pool *pool)void page_pool_unlist (struct page_pool *pool) {mutex_lock (&page_pools_lock); +netdev_nl_page_pool_event (pool,NETDEV_CMD_PAGE_POOL_DEL_NTF );xa_erase (&page_pools, pool->user.id);hlist_del (&pool->user.list);mutex_unlock (&page_pools_lock);
@@ -216,6 +250,8 @@ static void page_pool_unreg_netdev(struct net_device *netdev) last =NULL ;hlist_for_each_entry (pool, &netdev->page_pools, user.list) { pool->slow.netdev = lo; +netdev_nl_page_pool_event (pool, +NETDEV_CMD_PAGE_POOL_CHANGE_NTF ); last = pool; }if (last)
Signature · 1 line
2.41.0
Jakub Kicinski
maintainerPATCH 11/15 net: page_pool: report amount of memory held by page pools
In reply to Jakub KicinskiAdvanced deployments need the ability to check memory use of various system components. It makes it possible to make informed decisions about memory allocation and to find regressions and leaks.
Report memory use of page pools. Report both number of references and bytes held.
--- Documentation/netlink/specs/netdev.yaml | 13 +++++++++++++ include/uapi/linux/netdev.h | 2 ++ net/core/page_pool.c | 13 +++++++++---- net/core/page_pool_priv.h | 2 ++ net/core/page_pool_user.c | 8 ++++++++ 5 files changed, 34 insertions(+), 4 deletions(-)
Documentation/netlink/specs/netdev.yaml +13 -0
@@ -114,6 +114,17 @@ name: netdev checks: min: 1 max: u32-max + - + name: inflight + type: uint + doc: | + Number of outstanding references to this page pool (allocated + but yet to be freed pages). + - + name: inflight-mem + type: uint + doc: | + Amount of memory held by inflight pages. operations: list:
@@ -163,6 +174,8 @@ name: netdev - id - ifindex - napi-id + - inflight + - inflight-mem dump: reply: *pp-reply -
include/uapi/linux/netdev.h +2 -0
@@ -68,6 +68,8 @@ enum {NETDEV_A_PAGE_POOL_ID =1 ,NETDEV_A_PAGE_POOL_IFINDEX ,NETDEV_A_PAGE_POOL_NAPI_ID , +NETDEV_A_PAGE_POOL_INFLIGHT , +NETDEV_A_PAGE_POOL_INFLIGHT_MEM , __NETDEV_A_PAGE_POOL_MAX,NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX -1 )
net/core/page_pool.c +9 -4
@@ -525,7 +525,7 @@ EXPORT_SYMBOL(page_pool_alloc_pages);*/ #define _distance (a, b) (s32)((a) - (b)) -static s32 page_pool_inflight (struct page_pool *pool) +s32 page_pool_inflight (const struct page_pool *pool,bool strict) {u32 release_cnt =atomic_read (&pool->pages_state_release_cnt);u32 hold_cnt =READ_ONCE (pool->pages_state_hold_cnt);
@@ -533,8 +533,13 @@ static s32 page_pool_inflight(struct page_pool *pool) inflight =_distance (hold_cnt, release_cnt); -trace_page_pool_release (pool, inflight, hold_cnt, release_cnt); -WARN (inflight <0 ,"Negative(%d) inflight packet-pages" , inflight); +if (strict) { +trace_page_pool_release (pool, inflight, hold_cnt, release_cnt); +WARN (inflight <0 ,"Negative(%d) inflight packet-pages" , + inflight); + }else { + inflight =max (0 , inflight); + }return inflight; }
@@ -877,7 +882,7 @@ static int page_pool_release(struct page_pool *pool)int inflight;page_pool_scrub (pool); - inflight =page_pool_inflight (pool); + inflight =page_pool_inflight (pool, true);if (!inflight)__page_pool_destroy (pool);
net/core/page_pool_priv.h +2 -0
@@ -3,6 +3,8 @@#ifndef __PAGE_POOL_PRIV_H#define __PAGE_POOL_PRIV_H +s32 page_pool_inflight (const struct page_pool *pool,bool strict); +int page_pool_list (struct page_pool *pool);void page_pool_unlist (struct page_pool *pool);
net/core/page_pool_user.c +8 -0
@@ -110,6 +110,7 @@ static int page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool, const struct genl_info *info) { + size_t inflight, refsz;void *hdr; hdr =genlmsg_iput (rsp, info);
@@ -127,6 +128,13 @@ page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool, nla_put_uint(rsp, NETDEV_A_PAGE_POOL_NAPI_ID, pool->user.napi_id)) goto err_cancel; + inflight =page_pool_inflight (pool, false); + refsz =PAGE_SIZE << pool->p.order; +if (nla_put_uint (rsp,NETDEV_A_PAGE_POOL_INFLIGHT , inflight) || +nla_put_uint (rsp,NETDEV_A_PAGE_POOL_INFLIGHT_MEM , + inflight * refsz)) + goto err_cancel; +genlmsg_end (rsp, hdr);return 0 ;
Signature · 1 line
2.41.0
Jakub Kicinski
maintainerPATCH 12/15 net: page_pool: report when page pool was destroyed
In reply to Jakub KicinskiReport when page pool was destroyed. Together with the inflight / memory use reporting this can serve as a replacement for the warning about leaked page pools we currently print to dmesg.
Example output for a fake leaked page pool using some hacks in netdevsim (one "live" pool, and one "leaked" on the same dev):
$ ./cli.py --no-schema --spec netlink/specs/netdev.yaml \
--dump page-pool-get
[{'id': 2, 'ifindex': 3},
{'id': 1, 'ifindex': 3, 'destroyed': 133, 'inflight': 1}]--- Documentation/netlink/specs/netdev.yaml | 9 +++++++++ include/net/page_pool/types.h | 1 + include/uapi/linux/netdev.h | 1 + net/core/page_pool.c | 1 + net/core/page_pool_priv.h | 1 + net/core/page_pool_user.c | 12 ++++++++++++ 6 files changed, 25 insertions(+)
Documentation/netlink/specs/netdev.yaml +9 -0
@@ -125,6 +125,14 @@ name: netdev type: uint doc: | Amount of memory held by inflight pages. + - + name: destroyed + type: uint + doc: | + Seconds in CLOCK_BOOTTIME of when Page Pool was destroyed. + Page Pools wait for all the memory allocated from them to be freed + before truly disappearing. + Absent if Page Pool hasn't been destroyed. operations: list:
@@ -176,6 +184,7 @@ name: netdev - napi-id - inflight - inflight-mem + - destroyed dump: reply: *pp-reply -
include/net/page_pool/types.h +1 -0
@@ -193,6 +193,7 @@ struct page_pool {/* User-facing fields, protected by page_pools_lock */ struct {struct hlist_node list; +u64 destroyed;u32 napi_id;u32 id; } user;
include/uapi/linux/netdev.h +1 -0
@@ -70,6 +70,7 @@ enum {NETDEV_A_PAGE_POOL_NAPI_ID ,NETDEV_A_PAGE_POOL_INFLIGHT ,NETDEV_A_PAGE_POOL_INFLIGHT_MEM , +NETDEV_A_PAGE_POOL_DESTROYED , __NETDEV_A_PAGE_POOL_MAX,NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX -1 )
net/core/page_pool.c +1 -0
@@ -949,6 +949,7 @@ void page_pool_destroy(struct page_pool *pool)if (!page_pool_release (pool))return ; +page_pool_destroyed (pool); pool->defer_start = jiffies; pool->defer_warn = jiffies +DEFER_WARN_INTERVAL ;
net/core/page_pool_priv.h +1 -0
@@ -6,6 +6,7 @@s32 page_pool_inflight (const struct page_pool *pool,bool strict);int page_pool_list (struct page_pool *pool); +void page_pool_destroyed (struct page_pool *pool);void page_pool_unlist (struct page_pool *pool);#endif
net/core/page_pool_user.c +12 -0
@@ -134,6 +134,10 @@ page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool, nla_put_uint(rsp, NETDEV_A_PAGE_POOL_INFLIGHT_MEM, inflight * refsz)) goto err_cancel; +if (pool->user.destroyed && +nla_put_uint (rsp,NETDEV_A_PAGE_POOL_DESTROYED , + pool->user.destroyed)) + goto err_cancel;genlmsg_end (rsp, hdr);
@@ -219,6 +223,14 @@ int page_pool_list(struct page_pool *pool)return err; } +void page_pool_destroyed (struct page_pool *pool) +{ +mutex_lock (&page_pools_lock); + pool->user.destroyed =ktime_get_boottime_seconds (); +netdev_nl_page_pool_event (pool,NETDEV_CMD_PAGE_POOL_CHANGE_NTF ); +mutex_unlock (&page_pools_lock); +} +void page_pool_unlist (struct page_pool *pool) {mutex_lock (&page_pools_lock);
Signature · 1 line
2.41.0
Re: [PATCH net-next 12/15] net: page_pool: report when page pool was destroyed
Quoted 4 lines · Jakub Kicinski
Report when page pool was destroyed. Together with the inflight / memory use reporting this can serve as a replacement for the warning about leaked page pools we currently print to dmesg.
Quoted 8 lines
Example output for a fake leaked page pool using some hacks in netdevsim (one "live" pool, and one "leaked" on the same dev):
$ ./cli.py --no-schema --spec netlink/specs/netdev.yaml \ --dump page-pool-get [{'id': 2, 'ifindex': 3}, {'id': 1, 'ifindex': 3, 'destroyed': 133, 'inflight': 1}]
The destroyed ts really helps to narrow down which tests/test ranges are triggering the leaks. Thanks!
I was planning to add a per page_pool "name" where the driver would encode the rq index + creation timestamp and the printout would show this name. This approach is much cleaner though.
For what it's worth:
Quoted 117 lines
Signed-off-by Jakub Kicinski <[email protected]>---
Documentation/netlink/specs/netdev.yaml | 9 +++++++++ include/net/page_pool/types.h | 1 + include/uapi/linux/netdev.h | 1 + net/core/page_pool.c | 1 + net/core/page_pool_priv.h | 1 + net/core/page_pool_user.c | 12 ++++++++++++6 files changed, 25 insertions(+)Documentation/netlink/specs/netdev.yaml +0
-0@@ -125,6 +125,14 @@ name: netdevtype: uint
doc: |
Amount of memory held by inflight pages.
+ -
+ name: destroyed
+ type: uint
+ doc: |
+ Seconds inCLOCK_BOOTTIMEof when Page Pool was destroyed. + Page Pools wait for all the memory allocated from them to be freed + before truly disappearing.
+ Absent if Page Pool hasn't been destroyed.
operations:
list:Documentation/netlink/specs/netdev.yaml +0
-0@@ -176,6 +184,7 @@ name: netdev- napi-id
- inflight
- inflight-mem
+ - destroyed
dump:
reply: *pp-reply
-include/net/page_pool/types.h +1
-0@@ -193,6 +193,7 @@ struct page_pool { /* User-facing fields, protected by page_pools_lock */ struct { struct hlist_node list; + u64 destroyed; u32 napi_id; u32 id; } user;include/uapi/linux/netdev.h +1
-0@@ -70,6 +70,7 @@ enum { NETDEV_A_PAGE_POOL_NAPI_ID, NETDEV_A_PAGE_POOL_INFLIGHT, NETDEV_A_PAGE_POOL_INFLIGHT_MEM, + NETDEV_A_PAGE_POOL_DESTROYED,
__NETDEV_A_PAGE_POOL_MAX,NETDEV_A_PAGE_POOL_MAX= (__NETDEV_A_PAGE_POOL_MAX- 1)net/core/page_pool.c +0
-0@@ -949,6 +949,7 @@ void page_pool_destroy(struct page_pool *pool) if (!page_pool_release(pool)) return;
+ page_pool_destroyed(pool);pool->defer_start= jiffies; pool->defer_warn = jiffies +DEFER_WARN_INTERVAL;net/core/page_pool_priv.h +0
-0@@ -6,6 +6,7 @@ s32 page_pool_inflight(const struct page_pool *pool, bool strict);
intpage_pool_list(struct page_pool *pool); +voidpage_pool_destroyed(struct page_pool *pool); voidpage_pool_unlist(struct page_pool *pool);
#endifnet/core/page_pool_user.c +4
-0@@ -134,6 +134,10 @@ page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool, nla_put_uint(rsp, NETDEV_A_PAGE_POOL_INFLIGHT_MEM, inflight * refsz)) goto err_cancel; + if (pool->user.destroyed && + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_DESTROYED, + pool->user.destroyed)) + goto err_cancel;
genlmsg_end(rsp, hdr);net/core/page_pool_user.c +0
-0@@ -219,6 +223,14 @@ int page_pool_list(struct page_pool *pool) return err; }net/core/page_pool_user.c +8
-0+void page_pool_destroyed (struct page_pool *pool) +{ + mutex_lock(&page_pools_lock); + pool->user.destroyed = ktime_get_boottime_seconds(); + netdev_nl_page_pool_event(pool, NETDEV_CMD_PAGE_POOL_CHANGE_NTF); + mutex_unlock(&page_pools_lock); +} +void
page_pool_unlist(struct page_pool *pool)
{
mutex_lock(&page_pools_lock);
Jakub Kicinski
maintainerPATCH 13/15 net: page_pool: expose page pool stats via netlink
In reply to Jakub KicinskiDump the stats into netlink. More clever approaches like dumping the stats per-CPU for each CPU individually to see where the packets get consumed can be implemented in the future.
A trimmed example from a real (but recently booted system):
$ ./cli.py --no-schema --spec netlink/specs/netdev.yaml \
--dump page-pool-stats-get
[{'info': {'id': 19, 'ifindex': 2},
'alloc-empty': 48,
'alloc-fast': 3024,
'alloc-refill': 0,
'alloc-slow': 48,
'alloc-slow-high-order': 0,
'alloc-waive': 0,
'recycle-cache-full': 0,
'recycle-cached': 0,
'recycle-released-refcnt': 0,
'recycle-ring': 0,
'recycle-ring-full': 0},
{'info': {'id': 18, 'ifindex': 2},
'alloc-empty': 66,
'alloc-fast': 11811,
'alloc-refill': 35,
'alloc-slow': 66,
'alloc-slow-high-order': 0,
'alloc-waive': 0,
'recycle-cache-full': 1145,
'recycle-cached': 6541,
'recycle-released-refcnt': 0,
'recycle-ring': 1275,
'recycle-ring-full': 0},
{'info': {'id': 17, 'ifindex': 2},
'alloc-empty': 73,
'alloc-fast': 62099,
'alloc-refill': 413,
...--- Documentation/netlink/specs/netdev.yaml | 77 ++++++++++++++++++ Documentation/networking/page_pool.rst | 10 ++- include/net/page_pool/helpers.h | 8 +- include/uapi/linux/netdev.h | 19 +++++ net/core/netdev-genl-gen.c | 28 +++++++ net/core/netdev-genl-gen.h | 7 ++ net/core/page_pool.c | 2 +- net/core/page_pool_user.c | 103 ++++++++++++++++++++++++ 8 files changed, 245 insertions(+), 9 deletions(-)
Documentation/netlink/specs/netdev.yaml +77 -0
@@ -133,6 +133,59 @@ name: netdev Page Pools wait for all the memory allocated from them to be freed before truly disappearing. Absent if Page Pool hasn't been destroyed. + - + name: page-pool-info + subset-of: page-pool + attributes: + - + name: id + - + name: ifindex + - + name: page-pool-stats + doc: | + Page pool statistics, see docs for struct page_pool_stats + for information about individual statistics. + attributes: + - + name: info + doc: Page pool identifying information. + type: nest + nested-attributes: page-pool-info + - + name: alloc-fast + type: uint + value: 8 # reserve some attr ids in case we need more metadata later + - + name: alloc-slow + type: uint + - + name: alloc-slow-high-order + type: uint + - + name: alloc-empty + type: uint + - + name: alloc-refill + type: uint + - + name: alloc-waive + type: uint + - + name: recycle-cached + type: uint + - + name: recycle-cache-full + type: uint + - + name: recycle-ring + type: uint + - + name: recycle-ring-full + type: uint + - + name: recycle-released-refcnt + type: uint operations: list:
@@ -202,6 +255,30 @@ name: netdev doc: Notification about page pool configuration being changed. notify: page-pool-get mcgrp: page-pool + - + name: page-pool-stats-get + doc: Get page pool statistics. + attribute-set: page-pool-stats + do: + request: + attributes: + - info + reply: &pp-stats-reply + attributes: + - info + - alloc-fast + - alloc-slow + - alloc-slow-high-order + - alloc-empty + - alloc-refill + - alloc-waive + - recycle-cached + - recycle-cache-full + - recycle-ring + - recycle-ring-full + - recycle-released-refcnt + dump: + reply: *pp-stats-reply mcast-groups: list:
Documentation/networking/page_pool.rst +8 -2
@@ -41,6 +41,11 @@ Architecture overview | Fast cache | | ptr-ring cache | +-----------------+ +------------------+ +Monitoring +========== +Information about page pools on the system can be accessed via the netdev +genetlink family (see Documentation/netlink/specs/netdev.yaml). + API interface ============= The number of pools created **must** match the number of hardware queues
@@ -107,8 +112,9 @@ 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. -The API will fill in the provided struct page_pool_stats with -statistics about the page_pool. +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
include/net/page_pool/helpers.h +2 -6
@@ -55,16 +55,12 @@#include <net/page_pool/types.h> #ifdef CONFIG_PAGE_POOL_STATS +/* Deprecated driver-facing API, use netlink instead */ 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,void *stats); -/* -* Drivers that wish to harvest page pool stats and report them to users -* (perhaps via ethtool, debugfs, or another mechanism) can allocate a -* struct page_pool_stats call page_pool_get_stats to get stats for the specified pool. -*/ -bool page_pool_get_stats (struct page_pool *pool, +bool 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 )
include/uapi/linux/netdev.h +19 -0
@@ -76,6 +76,24 @@ enum {NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX -1 ) }; +enum { +NETDEV_A_PAGE_POOL_STATS_INFO =1 , +NETDEV_A_PAGE_POOL_STATS_ALLOC_FAST =8 , +NETDEV_A_PAGE_POOL_STATS_ALLOC_SLOW , +NETDEV_A_PAGE_POOL_STATS_ALLOC_SLOW_HIGH_ORDER , +NETDEV_A_PAGE_POOL_STATS_ALLOC_EMPTY , +NETDEV_A_PAGE_POOL_STATS_ALLOC_REFILL , +NETDEV_A_PAGE_POOL_STATS_ALLOC_WAIVE , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_CACHED , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_CACHE_FULL , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_RING , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_RING_FULL , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_RELEASED_REFCNT , + + __NETDEV_A_PAGE_POOL_STATS_MAX, +NETDEV_A_PAGE_POOL_STATS_MAX = (__NETDEV_A_PAGE_POOL_STATS_MAX -1 ) +}; +enum {NETDEV_CMD_DEV_GET =1 ,NETDEV_CMD_DEV_ADD_NTF ,
@@ -85,6 +103,7 @@ enum {NETDEV_CMD_PAGE_POOL_ADD_NTF ,NETDEV_CMD_PAGE_POOL_DEL_NTF ,NETDEV_CMD_PAGE_POOL_CHANGE_NTF , +NETDEV_CMD_PAGE_POOL_STATS_GET , __NETDEV_CMD_MAX,NETDEV_CMD_MAX = (__NETDEV_CMD_MAX -1 )
net/core/netdev-genl-gen.c +28 -0
@@ -16,6 +16,17 @@ struct netlink_range_validation netdev_a_page_pool_id_range = { .max = 4294967295, }; +struct netlink_range_validation netdev_a_page_pool_ifindex_range = { + .min =1 , + .max =2147483647 , +}; + +/* Common nested types */ +const struct nla_policy netdev_page_pool_info_nl_policy[NETDEV_A_PAGE_POOL_IFINDEX +1 ] = { + [NETDEV_A_PAGE_POOL_ID ] =NLA_POLICY_FULL_RANGE (NLA_UINT , &netdev_a_page_pool_id_range), + [NETDEV_A_PAGE_POOL_IFINDEX ] =NLA_POLICY_FULL_RANGE (NLA_U32 , &netdev_a_page_pool_ifindex_range), +}; +/* NETDEV_CMD_DEV_GET - do */ static const struct nla_policy netdev_dev_get_nl_policy[NETDEV_A_DEV_IFINDEX +1 ] = { [NETDEV_A_DEV_IFINDEX ] =NLA_POLICY_MIN (NLA_U32 ,1 ),
@@ -26,6 +37,11 @@ static const struct nla_policy netdev_page_pool_get_nl_policy[NETDEV_A_PAGE_POOL [NETDEV_A_PAGE_POOL_ID] = NLA_POLICY_FULL_RANGE(NLA_UINT, &netdev_a_page_pool_id_range), }; +/* NETDEV_CMD_PAGE_POOL_STATS_GET - do */ +static const struct nla_policy netdev_page_pool_stats_get_nl_policy[NETDEV_A_PAGE_POOL_STATS_INFO +1 ] = { + [NETDEV_A_PAGE_POOL_STATS_INFO ] =NLA_POLICY_NESTED (netdev_page_pool_info_nl_policy), +}; +/* Ops table for netdev */ static const struct genl_split_ops netdev_nl_ops[] = { {
@@ -52,6 +68,18 @@ static const struct genl_split_ops netdev_nl_ops[] = { .dumpit = netdev_nl_page_pool_get_dumpit, .flags = GENL_CMD_CAP_DUMP, }, + { + .cmd = NETDEV_CMD_PAGE_POOL_STATS_GET, + .doit = netdev_nl_page_pool_stats_get_doit, + .policy = netdev_page_pool_stats_get_nl_policy, + .maxattr = NETDEV_A_PAGE_POOL_STATS_INFO, + .flags = GENL_CMD_CAP_DO, + }, + { + .cmd = NETDEV_CMD_PAGE_POOL_STATS_GET, + .dumpit = netdev_nl_page_pool_stats_get_dumpit, + .flags = GENL_CMD_CAP_DUMP, + }, }; static const struct genl_multicast_group netdev_nl_mcgrps[] = {
net/core/netdev-genl-gen.h +7 -0
@@ -11,11 +11,18 @@#include <uapi/linux/netdev.h> +/* Common nested types */ +extern const struct nla_policy netdev_page_pool_info_nl_policy[NETDEV_A_PAGE_POOL_IFINDEX +1 ]; +int netdev_nl_dev_get_doit (struct sk_buff *skb,struct genl_info *info);int netdev_nl_dev_get_dumpit (struct sk_buff *skb,struct netlink_callback *cb);int netdev_nl_page_pool_get_doit (struct sk_buff *skb,struct genl_info *info);int netdev_nl_page_pool_get_dumpit (struct sk_buff *skb,struct netlink_callback *cb); +int netdev_nl_page_pool_stats_get_doit (struct sk_buff *skb, +struct genl_info *info); +int netdev_nl_page_pool_stats_get_dumpit (struct sk_buff *skb, +struct netlink_callback *cb);enum {NETDEV_NLGRP_MGMT ,
net/core/page_pool.c +1 -1
@@ -71,7 +71,7 @@ static const char pp_stats[][ETH_GSTRING_LEN] = {* 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 (struct page_pool *pool, +bool page_pool_get_stats (const struct page_pool *pool,struct page_pool_stats *stats) {int cpu =0 ;
net/core/page_pool_user.c +103 -0
115 lines not on this page · raw message
Signature · 1 line
2.41.0
kernel test robot
build botRe: [PATCH net-next 13/15] net: page_pool: expose page pool stats via netlink
kernel test robot noticed the following build errors:
Hi Jakub,
kernel test robot noticed the following build errors:
[auto build test ERROR on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Jakub-Kicinski/net-page_pool-split-the-page_pool_params-into-fast-and-slow/20231025-023128 base: net-next/main patch link: https://lore.kernel.org/r/20231024160220.3973311-14-kuba%40kernel.org patch subject: [PATCH net-next 13/15] net: page_pool: expose page pool stats via netlink config: um-allnoconfig (https://download.01.org/0day-ci/archive/20231025/[email protected]/config) compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project.git 4a5ac14ee968ff0ad5d2cc1ffa0299048db4c88a) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231025/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
/usr/bin/ld: init/main.o: warning: relocation in read-only section `.ref.text'
/usr/bin/ld: warning: .tmp_vmlinux.kallsyms1 has a LOAD segment with RWX permissions /usr/bin/ld: net/core/netdev-genl-gen.o:(.rodata+0xf8): undefined reference to netdev_nl_page_pool_get_doit' /usr/bin/ld: net/core/netdev-genl-gen.o:(.rodata+0x120): undefined reference to netdev_nl_page_pool_get_dumpit'
>> /usr/bin/ld: net/core/netdev-genl-gen.o:(.rodata+0x148): undefined reference to `netdev_nl_page_pool_stats_get_doit' >> /usr/bin/ld: net/core/netdev-genl-gen.o:(.rodata+0x170): undefined reference to `netdev_nl_page_pool_stats_get_dumpit'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Jakub Kicinski
maintainerPATCH 14/15 net: page_pool: mute the periodic warning for visible page pools
In reply to Jakub KicinskiMute the periodic "stalled pool shutdown" warning if the page pool is visible to user space. Rolling out a driver using page pools to just a few hundred hosts at Meta surfaces applications which fail to reap their broken sockets. Obviously it's best if the applications are fixed, but we don't generally print warnings for application resource leaks. Admins can now depend on the netlink interface for getting page pool info to detect buggy apps.
While at it throw in the ID of the pool into the message, in rare cases (pools from destroyed netns) this will make finding the pool with a debugger easier.
--- net/core/page_pool.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
net/core/page_pool.c +7 -4
@@ -893,18 +893,21 @@ static void page_pool_release_retry(struct work_struct *wq) {struct delayed_work *dwq =to_delayed_work (wq);struct page_pool *pool =container_of (dwq,typeof (*pool), release_dw); +void *netdev;int inflight; inflight =page_pool_release (pool);if (!inflight)return ; -/* Periodic warning */ -if (time_after_eq (jiffies, pool->defer_warn)) { +/* Periodic warning for page pools the user can't see */ + netdev =READ_ONCE (pool->slow.netdev); +if (time_after_eq (jiffies, pool->defer_warn) && + (!netdev || netdev ==NET_PTR_POISON )) {int sec = (s32)((u32 )jiffies - (u32 )pool->defer_start) /HZ ; -pr_warn ("%s() stalled pool shutdown %d inflight %d sec\n" , - __func__, inflight, sec); +pr_warn ("%s() stalled pool shutdown: id %u, %d inflight %d sec\n" , + __func__, pool->user.id, inflight, sec); pool->defer_warn = jiffies +DEFER_WARN_INTERVAL ; }
Signature · 1 line
2.41.0
Jakub Kicinski
maintainerPATCH 15/15 tools: ynl: add sample for getting page-pool information
In reply to Jakub KicinskiRegenerate the tools/ code after netdev spec changes.
Add sample to query page-pool info in a concise fashion:
$ ./page-pool
eth0[2] page pools: 10 (zombies: 0)
refs: 41984 bytes: 171966464 (refs: 0 bytes: 0)
recycling: 90.3% (alloc: 656:397681 recycle: 89652:270201)--- tools/include/uapi/linux/netdev.h | 36 +++ tools/net/ynl/generated/netdev-user.c | 419 ++++++++++++++++++++++++++ tools/net/ynl/generated/netdev-user.h | 171 +++++++++++ tools/net/ynl/lib/ynl.h | 2 +- tools/net/ynl/samples/.gitignore | 1 + tools/net/ynl/samples/Makefile | 2 +- tools/net/ynl/samples/page-pool.c | 147 +++++++++ 7 files changed, 776 insertions(+), 2 deletions(-) create mode 100644 tools/net/ynl/samples/page-pool.c
tools/include/uapi/linux/netdev.h +36 -0
@@ -64,16 +64,52 @@ enum {NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX -1 ) }; +enum { +NETDEV_A_PAGE_POOL_ID =1 , +NETDEV_A_PAGE_POOL_IFINDEX , +NETDEV_A_PAGE_POOL_NAPI_ID , +NETDEV_A_PAGE_POOL_INFLIGHT , +NETDEV_A_PAGE_POOL_INFLIGHT_MEM , +NETDEV_A_PAGE_POOL_DESTROYED , + + __NETDEV_A_PAGE_POOL_MAX, +NETDEV_A_PAGE_POOL_MAX = (__NETDEV_A_PAGE_POOL_MAX -1 ) +}; + +enum { +NETDEV_A_PAGE_POOL_STATS_INFO =1 , +NETDEV_A_PAGE_POOL_STATS_ALLOC_FAST =8 , +NETDEV_A_PAGE_POOL_STATS_ALLOC_SLOW , +NETDEV_A_PAGE_POOL_STATS_ALLOC_SLOW_HIGH_ORDER , +NETDEV_A_PAGE_POOL_STATS_ALLOC_EMPTY , +NETDEV_A_PAGE_POOL_STATS_ALLOC_REFILL , +NETDEV_A_PAGE_POOL_STATS_ALLOC_WAIVE , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_CACHED , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_CACHE_FULL , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_RING , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_RING_FULL , +NETDEV_A_PAGE_POOL_STATS_RECYCLE_RELEASED_REFCNT , + + __NETDEV_A_PAGE_POOL_STATS_MAX, +NETDEV_A_PAGE_POOL_STATS_MAX = (__NETDEV_A_PAGE_POOL_STATS_MAX -1 ) +}; +enum {NETDEV_CMD_DEV_GET =1 ,NETDEV_CMD_DEV_ADD_NTF ,NETDEV_CMD_DEV_DEL_NTF ,NETDEV_CMD_DEV_CHANGE_NTF , +NETDEV_CMD_PAGE_POOL_GET , +NETDEV_CMD_PAGE_POOL_ADD_NTF , +NETDEV_CMD_PAGE_POOL_DEL_NTF , +NETDEV_CMD_PAGE_POOL_CHANGE_NTF , +NETDEV_CMD_PAGE_POOL_STATS_GET , __NETDEV_CMD_MAX,NETDEV_CMD_MAX = (__NETDEV_CMD_MAX -1 ) };#define NETDEV_MCGRP_MGMT "mgmt" +#define NETDEV_MCGRP_PAGE_POOL "page-pool"#endif /* _UAPI_LINUX_NETDEV_H */
tools/net/ynl/generated/netdev-user.c +419 -0
450 lines not on this page · raw message
tools/net/ynl/generated/netdev-user.h +171 -0
181 lines not on this page · raw message
tools/net/ynl/lib/ynl.h +1 -1
@@ -239,7 +239,7 @@ int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg);#ifndef MNL_HAS_AUTO_SCALARS static inline uint64_t mnl_attr_get_uint (const struct nlattr *attr) { -if (mnl_attr_get_len (attr) ==4 ) +if (mnl_attr_get_payload_len (attr) ==4 )return mnl_attr_get_u32 (attr);return mnl_attr_get_u64 (attr); }
tools/net/ynl/samples/.gitignore +1 -0
@@ -1,3 +1,4 @@ ethtool devlink netdev +page-pool
tools/net/ynl/samples/Makefile +1 -1
@@ -18,7 +18,7 @@ include $(wildcard *.d) all: $(BINS) -$(BINS): ../lib/ynl.a ../generated/protos.a +$(BINS): ../lib/ynl.a ../generated/protos.a $(SRCS) @echo -e '\tCC sample $@' @$(COMPILE.c) $(CFLAGS_$@) [email protected] -o [email protected] @$(LINK.c) [email protected] -o $@ $(LDLIBS)
tools/net/ynl/samples/page-pool.c new file +147 -0
147 lines not on this page · raw message
Signature · 1 line
2.41.0
Ilias Apalodimas
maintainerHi Jakub,
Quoted 12 lines · Jakub Kicinski
This is a new revision of the RFC posted in August:
https://lore.kernel.org/all/[email protected]/
There's been a handful of fixes and tweaks but the overall architecture is unchanged.As a reminder the RFC was posted as the first step towards an API which could configure the page pools (GET API as a stepping stone for a SET API to come later). I wasn't sure whether we should commit to the GET API before the SET takes shape, hence the large delay between versions.
Quoted 3 lines
Unfortunately, real deployment experience made this series much more urgent. We recently started to deploy newer kernels / drivers at Meta, making significant use of page pools for the first time.
That's nice and scary at the same time!
Quoted 7 lines
We immediately run into page pool leaks both real and false positive warnings. As Eric pointed out/predicted there's no guarantee that applications will read / close their sockets so a page pool page may be stuck in a socket (but not leaked) forever. This happens a lot in our fleet. Most of these are obviously due to application bugs but we should not be printing kernel warnings due to minor application resource leaks.
Fair enough, I guess you mean 'continuous warnings'?
Quoted 11 lines
Conversely the page pool memory may get leaked at runtime, and we have no way to detect / track that, unless someone reconfigures the NIC and destroys the page pools which leaked the pages.
The solution presented here is to expose the memory use of page pools via netlink. This allows for continuous monitoring of memory used by page pools, regardless if they were destroyed or not. Sample in patch 15 can print the memory use and recycling efficiency:
Quoted 5 lines
$ ./page-pool
eth0[2] page pools: 10 (zombies: 0)
refs: 41984 bytes: 171966464 (refs: 0 bytes: 0)
recycling: 90.3% (alloc: 656:397681 recycle: 89652:270201)That's reasonable, and the recycling rate is pretty impressive. Any idea how that translated to enhancements overall? mem/cpu pressure etc
Thanks
/Ilias
Quoted 53 lines
The main change compared to the RFC is that the API now exposes outstanding references and byte counts even for "live" page pools. The warning is no longer printed if page pool is accessible via netlink.
Jakub Kicinski (15):
net:page_pool: split thepage_pool_paramsinto fast and slow
net:page_pool: avoid touching slow on the fastpath
net:page_pool: factor out uninit
net:page_pool: id the page pools
net:page_pool: record pools per netdev
net:page_pool: stash the NAPI ID for easier access
eth: link netdev topage_poolsin drivers
net:page_pool: add nlspec for basic access to page pools
net:page_pool: implement GET in the netlink API
net:page_pool: add netlink notifications for state changes
net:page_pool: report amount of memory held by page pools
net:page_pool: report when page pool was destroyed
net:page_pool: expose page pool stats via netlink
net:page_pool: mute the periodic warning for visible page pools
tools: ynl: add sample for getting page-pool informationDocumentation/netlink/specs/netdev.yaml | 161 +++++++ Documentation/networking/page_pool.rst | 10 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 + .../net/ethernet/mellanox/mlx5/core/en_main.c | 1 + drivers/net/ethernet/microsoft/mana/mana_en.c | 1 + include/linux/list.h | 20 + include/linux/netdevice.h | 4 + include/linux/poison.h | 2 + include/net/page_pool/helpers.h | 8 +- include/net/page_pool/types.h | 43 +- include/uapi/linux/netdev.h | 36 ++ net/core/Makefile | 2 +- net/core/netdev-genl-gen.c | 52 +++ net/core/netdev-genl-gen.h | 11 + net/core/page_pool.c | 78 ++-- net/core/page_pool_priv.h | 12 + net/core/page_pool_user.c | 414 +++++++++++++++++ tools/include/uapi/linux/netdev.h | 36 ++ tools/net/ynl/generated/netdev-user.c | 419 ++++++++++++++++++ tools/net/ynl/generated/netdev-user.h | 171 +++++++ tools/net/ynl/lib/ynl.h | 2 +- tools/net/ynl/samples/.gitignore | 1 + tools/net/ynl/samples/Makefile | 2 +- tools/net/ynl/samples/page-pool.c | 147 ++++++ 24 files changed, 1586 insertions(+), 48 deletions(-) create mode 100644 net/core/page_pool_priv.h create mode 100644 net/core/page_pool_user.c create mode 100644 tools/net/ynl/samples/page-pool.c--
2.41.0
Jakub Kicinski
maintainerQuoted 9 lines · On Thu, 9 Nov 2023 10:11:47 +0200 Ilias Apalodimas wrote:
Quoted 7 lines
We immediately run into page pool leaks both real and false positive warnings. As Eric pointed out/predicted there's no guarantee that applications will read / close their sockets so a page pool page may be stuck in a socket (but not leaked) forever. This happens a lot in our fleet. Most of these are obviously due to application bugs but we should not be printing kernel warnings due to minor application resource leaks.
Fair enough, I guess you mean 'continuous warnings'?
Yes, in this case but I'm making a general statement. Or do you mean that there's a typo / grammar issue?
Quoted 10 lines
Quoted 10 lines
Conversely the page pool memory may get leaked at runtime, and we have no way to detect / track that, unless someone reconfigures the NIC and destroys the page pools which leaked the pages.
The solution presented here is to expose the memory use of page pools via netlink. This allows for continuous monitoring of memory used by page pools, regardless if they were destroyed or not. Sample in patch 15 can print the memory use and recycling efficiency:
Quoted 6 lines
Quoted 4 lines
$ ./page-pool eth0[2] page pools: 10 (zombies: 0) refs: 41984 bytes: 171966464 (refs: 0 bytes: 0) recycling: 90.3% (alloc: 656:397681 recycle: 89652:270201)That's reasonable, and the recycling rate is pretty impressive.
This is just from a test machine, fresh boot, maybe a short iperf run, I don't remember now :) In any case not real workload.
Quoted 1 line
Any idea how that translated to enhancements overall? mem/cpu pressure etc
I haven't collected much prod data at this stage, I'm hoping to add this to the internal kernel and then do a more thorough investigation.