diff mbox series

[net-next,v3,07/13] net: page_pool: implement GET in the netlink API

Message ID 20231122034420.1158898-8-kuba@kernel.org (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series net: page_pool: add netlink-based introspection | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/codegen success Generated files up to date
netdev/tree_selection success Clearly marked for net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 5301 this patch: 5302
netdev/cc_maintainers warning 3 maintainers not CCed: tariqt@nvidia.com lorenzo@kernel.org sdf@google.com
netdev/build_clang success Errors and warnings before: 1378 this patch: 1378
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 5633 this patch: 5633
netdev/checkpatch warning WARNING: line length of 92 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Jakub Kicinski Nov. 22, 2023, 3:44 a.m. UTC
Expose 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}]

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 include/uapi/linux/netdev.h |  10 +++
 net/core/netdev-genl-gen.c  |  27 ++++++++
 net/core/netdev-genl-gen.h  |   3 +
 net/core/page_pool_user.c   | 127 ++++++++++++++++++++++++++++++++++++
 4 files changed, 167 insertions(+)

Comments

Eric Dumazet Nov. 22, 2023, 9:35 a.m. UTC | #1
On Wed, Nov 22, 2023 at 4:44 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> Expose 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}]
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Reviewed-by: Eric Dumazet <edumazet@google.com>
Jesper Dangaard Brouer Nov. 22, 2023, 2:39 p.m. UTC | #2
On 11/22/23 04:44, Jakub Kicinski wrote:
> Expose 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}]
> 
> Signed-off-by: Jakub Kicinski<kuba@kernel.org>
> ---
>   include/uapi/linux/netdev.h |  10 +++
>   net/core/netdev-genl-gen.c  |  27 ++++++++
>   net/core/netdev-genl-gen.h  |   3 +
>   net/core/page_pool_user.c   | 127 ++++++++++++++++++++++++++++++++++++
>   4 files changed, 167 insertions(+)

Acked-by: Jesper Dangaard Brouer <hawk@kernel.org>


Can we still somehow list "detached" page_pool's with ifindex==1 
(LOOPBACK_IFINDEX) ?

page_pool_unreg_netdev() does pool->slow.netdev = lo;


 > [...]
 > +page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool,
 > +		  const struct genl_info *info)
 > +{
 > +	void *hdr;
 > +
 > +	hdr = genlmsg_iput(rsp, info);
 > +	if (!hdr)
 > +		return -EMSGSIZE;
 > +
 > +	if (nla_put_uint(rsp, NETDEV_A_PAGE_POOL_ID, pool->user.id))
 > +		goto err_cancel;
 > +
 > +	if (pool->slow.netdev->ifindex != LOOPBACK_IFINDEX &&
 > +	    nla_put_u32(rsp, NETDEV_A_PAGE_POOL_IFINDEX,
 > +			pool->slow.netdev->ifindex))
 > +		goto err_cancel;
 > +	if (pool->user.napi_id &&
 > +	    nla_put_uint(rsp, NETDEV_A_PAGE_POOL_NAPI_ID, pool->user.napi_id))
 > +		goto err_cancel;
 > +
 > +	genlmsg_end(rsp, hdr);
 > +
 > +	return 0;
 > +err_cancel:
 > +	genlmsg_cancel(rsp, hdr);
 > +	return -EMSGSIZE;
 > +}
Jakub Kicinski Nov. 22, 2023, 4 p.m. UTC | #3
On Wed, 22 Nov 2023 15:39:48 +0100 Jesper Dangaard Brouer wrote:
> Can we still somehow list "detached" page_pool's with ifindex==1 
> (LOOPBACK_IFINDEX) ?

Do you mean filter at the kernel level to dump just them? Or whether
they are visible at all?

They are visible, with ifindex attribute absent.

If you mean filtering - I was considering various filters but it seems
like in "production" use we dump all the page pools, anyway. 
The detached ones need to be kept in check, obviously, but the "live"
ones also have to be monitored to make sure they don't eat up too much
memory.

The code we prepared at Meta for when this gets merged logs:
 - sum(inflight-mem), not detached
 - sum(inflight-mem), detached
 - recycling rate (similar calculation to the sample in the last patch)

It may be useful in CLI to dump all detached pools but ad-hoc CLI can
just filter in user space, doesn't have to be super efficient.
Jesper Dangaard Brouer Nov. 22, 2023, 4:21 p.m. UTC | #4
On 11/22/23 17:00, Jakub Kicinski wrote:
> On Wed, 22 Nov 2023 15:39:48 +0100 Jesper Dangaard Brouer wrote:
>> Can we still somehow list "detached" page_pool's with ifindex==1
>> (LOOPBACK_IFINDEX) ?
> 
> Do you mean filter at the kernel level to dump just them? Or whether
> they are visible at all?
> 

I mean if they are visible.

> They are visible, with ifindex attribute absent.
> 

Okay, I thought they would be listed with ifindex==1, but good to know.

> If you mean filtering - I was considering various filters but it seems
> like in "production" use we dump all the page pools, anyway.
> The detached ones need to be kept in check, obviously, but the "live"
> ones also have to be monitored to make sure they don't eat up too much
> memory.
> 
> The code we prepared at Meta for when this gets merged logs:
>   - sum(inflight-mem), not detached
>   - sum(inflight-mem), detached
>   - recycling rate (similar calculation to the sample in the last patch)
> 
> It may be useful in CLI to dump all detached pools but ad-hoc CLI can
> just filter in user space, doesn't have to be super efficient.

I think it will be okay to let userspace filter these.

--Jesper
diff mbox series

Patch

diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h
index 2943a151d4f1..176665bcf0da 100644
--- a/include/uapi/linux/netdev.h
+++ b/include/uapi/linux/netdev.h
@@ -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)
diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c
index ea9231378aa6..9d34b451cfa7 100644
--- a/net/core/netdev-genl-gen.c
+++ b/net/core/netdev-genl-gen.c
@@ -10,11 +10,24 @@ 
 
 #include <uapi/linux/netdev.h>
 
+/* Integer value ranges */
+static const 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 */
+#ifdef CONFIG_PAGE_POOL
+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),
+};
+#endif /* CONFIG_PAGE_POOL */
+
 /* Ops table for netdev */
 static const struct genl_split_ops netdev_nl_ops[] = {
 	{
@@ -29,6 +42,20 @@  static const struct genl_split_ops netdev_nl_ops[] = {
 		.dumpit	= netdev_nl_dev_get_dumpit,
 		.flags	= GENL_CMD_CAP_DUMP,
 	},
+#ifdef CONFIG_PAGE_POOL
+	{
+		.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,
+	},
+#endif /* CONFIG_PAGE_POOL */
 };
 
 static const struct genl_multicast_group netdev_nl_mcgrps[] = {
diff --git a/net/core/netdev-genl-gen.h b/net/core/netdev-genl-gen.h
index 7b370c073e7d..a011d12abff4 100644
--- a/net/core/netdev-genl-gen.h
+++ b/net/core/netdev-genl-gen.h
@@ -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,
diff --git a/net/core/page_pool_user.c b/net/core/page_pool_user.c
index 23074d4c75fc..24f4e54f6266 100644
--- a/net/core/page_pool_user.c
+++ b/net/core/page_pool_user.c
@@ -5,8 +5,10 @@ 
 #include <linux/xarray.h>
 #include <net/net_debug.h>
 #include <net/page_pool/types.h>
+#include <net/sock.h>
 
 #include "page_pool_priv.h"
+#include "netdev-genl-gen.h"
 
 static DEFINE_XARRAY_FLAGS(page_pools, XA_FLAGS_ALLOC1);
 /* Protects: page_pools, netdevice->page_pools, pool->slow.netdev, pool->user.
@@ -26,6 +28,131 @@  static DEFINE_MUTEX(page_pools_lock);
  *    - user.list: unhashed, netdev: unknown
  */
 
+typedef int (*pp_nl_fill_cb)(struct sk_buff *rsp, const struct page_pool *pool,
+			     const struct genl_info *info);
+
+static int
+netdev_nl_page_pool_get_do(struct genl_info *info, u32 id, pp_nl_fill_cb fill)
+{
+	struct page_pool *pool;
+	struct sk_buff *rsp;
+	int err;
+
+	mutex_lock(&page_pools_lock);
+	pool = xa_load(&page_pools, id);
+	if (!pool || hlist_unhashed(&pool->user.list) ||
+	    !net_eq(dev_net(pool->slow.netdev), genl_info_net(info))) {
+		err = -ENOENT;
+		goto err_unlock;
+	}
+
+	rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!rsp) {
+		err = -ENOMEM;
+		goto err_unlock;
+	}
+
+	err = fill(rsp, pool, info);
+	if (err)
+		goto err_free_msg;
+
+	mutex_unlock(&page_pools_lock);
+
+	return genlmsg_reply(rsp, info);
+
+err_free_msg:
+	nlmsg_free(rsp);
+err_unlock:
+	mutex_unlock(&page_pools_lock);
+	return err;
+}
+
+struct page_pool_dump_cb {
+	unsigned long ifindex;
+	u32 pp_id;
+};
+
+static int
+netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb,
+			     pp_nl_fill_cb fill)
+{
+	struct page_pool_dump_cb *state = (void *)cb->ctx;
+	const struct genl_info *info = genl_info_dump(cb);
+	struct net *net = sock_net(skb->sk);
+	struct net_device *netdev;
+	struct page_pool *pool;
+	int err = 0;
+
+	rtnl_lock();
+	mutex_lock(&page_pools_lock);
+	for_each_netdev_dump(net, netdev, state->ifindex) {
+		hlist_for_each_entry(pool, &netdev->page_pools, user.list) {
+			if (state->pp_id && state->pp_id < pool->user.id)
+				continue;
+
+			state->pp_id = pool->user.id;
+			err = fill(skb, pool, info);
+			if (err)
+				break;
+		}
+
+		state->pp_id = 0;
+	}
+	mutex_unlock(&page_pools_lock);
+	rtnl_unlock();
+
+	if (skb->len && err == -EMSGSIZE)
+		return skb->len;
+	return err;
+}
+
+static int
+page_pool_nl_fill(struct sk_buff *rsp, const struct page_pool *pool,
+		  const struct genl_info *info)
+{
+	void *hdr;
+
+	hdr = genlmsg_iput(rsp, info);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	if (nla_put_uint(rsp, NETDEV_A_PAGE_POOL_ID, pool->user.id))
+		goto err_cancel;
+
+	if (pool->slow.netdev->ifindex != LOOPBACK_IFINDEX &&
+	    nla_put_u32(rsp, NETDEV_A_PAGE_POOL_IFINDEX,
+			pool->slow.netdev->ifindex))
+		goto err_cancel;
+	if (pool->user.napi_id &&
+	    nla_put_uint(rsp, NETDEV_A_PAGE_POOL_NAPI_ID, pool->user.napi_id))
+		goto err_cancel;
+
+	genlmsg_end(rsp, hdr);
+
+	return 0;
+err_cancel:
+	genlmsg_cancel(rsp, hdr);
+	return -EMSGSIZE;
+}
+
+int netdev_nl_page_pool_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	u32 id;
+
+	if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_PAGE_POOL_ID))
+		return -EINVAL;
+
+	id = nla_get_uint(info->attrs[NETDEV_A_PAGE_POOL_ID]);
+
+	return netdev_nl_page_pool_get_do(info, id, page_pool_nl_fill);
+}
+
+int netdev_nl_page_pool_get_dumpit(struct sk_buff *skb,
+				   struct netlink_callback *cb)
+{
+	return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_fill);
+}
+
 int page_pool_list(struct page_pool *pool)
 {
 	static u32 id_alloc_next;