diff mbox series

[net-next,v1,3/9] netdev-genl: spec: Extend netdev netlink spec in YAML for NAPI

Message ID 169059162756.3736.16797255590375805440.stgit@anambiarhost.jf.intel.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series Introduce NAPI queues support | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
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 success Errors and warnings before: 5473 this patch: 5473
netdev/cc_maintainers warning 9 maintainers not CCed: lorenzo@kernel.org willemb@google.com maciej.fijalkowski@intel.com sdf@google.com tariqt@nvidia.com pabeni@redhat.com leon@kernel.org ast@kernel.org edumazet@google.com
netdev/build_clang fail Errors and warnings before: 1002 this patch: 947
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 fail Errors and warnings before: 4382 this patch: 4382
netdev/checkpatch warning CHECK: Please use a blank line after function/struct/union/enum declarations WARNING: Prefer __aligned(8) over __attribute__((aligned(8))) WARNING: line length of 102 exceeds 80 columns WARNING: line length of 120 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 91 exceeds 80 columns WARNING: line length of 92 exceeds 80 columns WARNING: space prohibited between function name and open parenthesis '('
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Nambiar, Amritha July 29, 2023, 12:47 a.m. UTC
Add support in netlink spec(netdev.yaml) for napi related information.
Add code generated from the spec.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 Documentation/netlink/specs/netdev.yaml |   46 ++++++
 include/uapi/linux/netdev.h             |   18 +++
 net/core/netdev-genl-gen.c              |   17 ++
 net/core/netdev-genl-gen.h              |    2 
 net/core/netdev-genl.c                  |   10 +
 tools/include/uapi/linux/netdev.h       |   18 +++
 tools/net/ynl/generated/netdev-user.c   |  220 +++++++++++++++++++++++++++++++
 tools/net/ynl/generated/netdev-user.h   |   63 +++++++++
 8 files changed, 394 insertions(+)

Comments

Jakub Kicinski July 31, 2023, 7:36 p.m. UTC | #1
On Fri, 28 Jul 2023 17:47:07 -0700 Amritha Nambiar wrote:
> +  -
> +    name: napi
> +    attributes:
> +      -
> +        name: ifindex
> +        doc: netdev ifindex
> +        type: u32
> +        checks:
> +          min: 1
> +      -
> +        name: napi-info
> +        doc: napi information such as napi-id, napi queues etc.
> +        type: nest
> +        multi-attr: true
> +        nested-attributes: napi-info-entry

Every NAPI instance should be dumped as a separate object. We can
implemented filtered dump to get NAPIs of a single netdev.
Nambiar, Amritha July 31, 2023, 11:12 p.m. UTC | #2
On 7/31/2023 12:36 PM, Jakub Kicinski wrote:
> On Fri, 28 Jul 2023 17:47:07 -0700 Amritha Nambiar wrote:
>> +  -
>> +    name: napi
>> +    attributes:
>> +      -
>> +        name: ifindex
>> +        doc: netdev ifindex
>> +        type: u32
>> +        checks:
>> +          min: 1
>> +      -
>> +        name: napi-info
>> +        doc: napi information such as napi-id, napi queues etc.
>> +        type: nest
>> +        multi-attr: true
>> +        nested-attributes: napi-info-entry
> 
> Every NAPI instance should be dumped as a separate object. We can
> implemented filtered dump to get NAPIs of a single netdev.
> 

Today, the 'do napi-get <ifindex>' will show all the NAPIs for a single 
netdev:
Example: --do napi-get --json='{"ifindex": 6}'

and the 'dump napi-get' will dump all the NAPIs for all the netdevs.
Example: netdev.yaml  --dump napi-get

Are you suggesting that we also dump each NAPI instance individually,
'do napi-get <ifindex> <NAPI_ID>'

Example:
netdev.yaml  --do napi-get --json='{"ifindex": 6, "napi-id": 390}'

[{'ifindex': 6},
  {'napi-info': [{'irq': 296,
                  'napi-id': 390,
                  'pid': 3475,
                  'rx-queues': [5],
                  'tx-queues': [5]}]}]
Jakub Kicinski Aug. 1, 2023, 12:13 a.m. UTC | #3
On Mon, 31 Jul 2023 16:12:23 -0700 Nambiar, Amritha wrote:
> > Every NAPI instance should be dumped as a separate object. We can
> > implemented filtered dump to get NAPIs of a single netdev.
> 
> Today, the 'do napi-get <ifindex>' will show all the NAPIs for a single 
> netdev:
> Example: --do napi-get --json='{"ifindex": 6}'
> 
> and the 'dump napi-get' will dump all the NAPIs for all the netdevs.
> Example: netdev.yaml  --dump napi-get
> 
> Are you suggesting that we also dump each NAPI instance individually,
> 'do napi-get <ifindex> <NAPI_ID>'
> 
> Example:
> netdev.yaml  --do napi-get --json='{"ifindex": 6, "napi-id": 390}'
> 
> [{'ifindex': 6},
>   {'napi-info': [{'irq': 296,
>                   'napi-id': 390,
>                   'pid': 3475,
>                   'rx-queues': [5],
>                   'tx-queues': [5]}]}]

Dumps can be filtered, I'm saying:

$ netdev.yaml --dump napi-get --json='{"ifindex": 6}'
                ^^^^

[{'napi-id': 390, 'ifindex': 6, 'irq': 296, ...},
 {'napi-id': 391, 'ifindex': 6, 'irq': 297, ...}]
Nambiar, Amritha Aug. 1, 2023, 12:24 a.m. UTC | #4
On 7/31/2023 5:13 PM, Jakub Kicinski wrote:
> On Mon, 31 Jul 2023 16:12:23 -0700 Nambiar, Amritha wrote:
>>> Every NAPI instance should be dumped as a separate object. We can
>>> implemented filtered dump to get NAPIs of a single netdev.
>>
>> Today, the 'do napi-get <ifindex>' will show all the NAPIs for a single
>> netdev:
>> Example: --do napi-get --json='{"ifindex": 6}'
>>
>> and the 'dump napi-get' will dump all the NAPIs for all the netdevs.
>> Example: netdev.yaml  --dump napi-get
>>
>> Are you suggesting that we also dump each NAPI instance individually,
>> 'do napi-get <ifindex> <NAPI_ID>'
>>
>> Example:
>> netdev.yaml  --do napi-get --json='{"ifindex": 6, "napi-id": 390}'
>>
>> [{'ifindex': 6},
>>    {'napi-info': [{'irq': 296,
>>                    'napi-id': 390,
>>                    'pid': 3475,
>>                    'rx-queues': [5],
>>                    'tx-queues': [5]}]}]
> 
> Dumps can be filtered, I'm saying:
> 
> $ netdev.yaml --dump napi-get --json='{"ifindex": 6}'
>                  ^^^^
> 
> [{'napi-id': 390, 'ifindex': 6, 'irq': 296, ...},
>   {'napi-id': 391, 'ifindex': 6, 'irq': 297, ...}]

I see. Okay. Looks like this needs to be supported for "dump dev-get 
ifindex" as well.
Jakub Kicinski Aug. 1, 2023, 12:35 a.m. UTC | #5
On Mon, 31 Jul 2023 17:24:51 -0700 Nambiar, Amritha wrote:
> >> [{'ifindex': 6},
> >>    {'napi-info': [{'irq': 296,
> >>                    'napi-id': 390,
> >>                    'pid': 3475,
> >>                    'rx-queues': [5],
> >>                    'tx-queues': [5]}]}]  
> > 
> > Dumps can be filtered, I'm saying:
> > 
> > $ netdev.yaml --dump napi-get --json='{"ifindex": 6}'
> >                  ^^^^
> > 
> > [{'napi-id': 390, 'ifindex': 6, 'irq': 296, ...},
> >   {'napi-id': 391, 'ifindex': 6, 'irq': 297, ...}]  
> 
> I see. Okay. Looks like this needs to be supported for "dump dev-get 
> ifindex" as well.

The main thing to focus on for next version is to make the NAPI objects
"flat" and individual, rather than entries in multi-attr nest within
per-netdev object.

I'm 100% sure implementing the filtering by ifindex will be doable as
a follow up so we can defer it.
Nambiar, Amritha Aug. 9, 2023, 12:17 a.m. UTC | #6
On 7/31/2023 5:35 PM, Jakub Kicinski wrote:
> On Mon, 31 Jul 2023 17:24:51 -0700 Nambiar, Amritha wrote:
>>>> [{'ifindex': 6},
>>>>     {'napi-info': [{'irq': 296,
>>>>                     'napi-id': 390,
>>>>                     'pid': 3475,
>>>>                     'rx-queues': [5],
>>>>                     'tx-queues': [5]}]}]
>>>
>>> Dumps can be filtered, I'm saying:
>>>
>>> $ netdev.yaml --dump napi-get --json='{"ifindex": 6}'
>>>                   ^^^^
>>>
>>> [{'napi-id': 390, 'ifindex': 6, 'irq': 296, ...},
>>>    {'napi-id': 391, 'ifindex': 6, 'irq': 297, ...}]
>>
>> I see. Okay. Looks like this needs to be supported for "dump dev-get
>> ifindex" as well.
> 
> The main thing to focus on for next version is to make the NAPI objects
> "flat" and individual, rather than entries in multi-attr nest within
> per-netdev object.
> 

Would this be acceptable:
$ netdev.yaml  --do napi-get --json='{"ifindex": 12}'

{'napi-info': [{'ifindex': 12, 'irq': 293, 'napi-id': 595, ...},
                {'ifindex': 12, 'irq': 292, 'napi-id': 594, ...},
                {'ifindex': 12, 'irq': 291, 'napi-id': 593, ...}]}

Here, "napi-info" represents a list of NAPI objects. A NAPI object is an 
individual element in the list, and are not within per-netdev object. 
The ifindex is just another attribute that is part of the NAPI object. 
The result for non-NAPI devices will just be empty.

I am not sure how to totally avoid multi-attr nest in this case. For the 
'do napi-get' command, the response is a list of elements with multiple 
attributes and not an individual entry. This transforms to a struct 
array of NAPI objects in the 'do' response, and is achieved with the 
multi-attr nest in the YAML. Subsequently, the 'dump napi-get' response 
is a list of the 'NAPI objects struct list' for all netdevs. Am I 
missing any special type in the YAML that can also give out a 
struct-array in the 'do' response besides using multi-attr nest ?


> I'm 100% sure implementing the filtering by ifindex will be doable as
> a follow up so we can defer it.
Jakub Kicinski Aug. 9, 2023, 2:45 a.m. UTC | #7
On Tue, 8 Aug 2023 17:17:34 -0700 Nambiar, Amritha wrote:
> > The main thing to focus on for next version is to make the NAPI objects
> > "flat" and individual, rather than entries in multi-attr nest within
> > per-netdev object.
> 
> Would this be acceptable:
> $ netdev.yaml  --do napi-get --json='{"ifindex": 12}'
> 
> {'napi-info': [{'ifindex': 12, 'irq': 293, 'napi-id': 595, ...},
>                 {'ifindex': 12, 'irq': 292, 'napi-id': 594, ...},
>                 {'ifindex': 12, 'irq': 291, 'napi-id': 593, ...}]}
> 
> Here, "napi-info" represents a list of NAPI objects. A NAPI object is an 
> individual element in the list, and are not within per-netdev object. 
> The ifindex is just another attribute that is part of the NAPI object. 
> The result for non-NAPI devices will just be empty.
> 
> I am not sure how to totally avoid multi-attr nest in this case. For the 
> 'do napi-get' command, the response is a list of elements with multiple 
> attributes and not an individual entry. This transforms to a struct 
> array of NAPI objects in the 'do' response, and is achieved with the 
> multi-attr nest in the YAML. Subsequently, the 'dump napi-get' response 
> is a list of the 'NAPI objects struct list' for all netdevs. Am I 
> missing any special type in the YAML that can also give out a 
> struct-array in the 'do' response besides using multi-attr nest ?

napi-get needs to take napi-id as an argument, if you want to filter 
by ifindex - that means a dump. Dumps work kind-of similar to do, you
still get the attributes for the request (although in a somewhat
convoluted way:

static int your_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
       const struct genl_dumpit_info *info = genl_dumpit_info(cb);

	info->attr[attrs-as-normal]

So if info->attrs[IFINDEX] is provided by the user - limit the dump to
only napis from that netdev.
diff mbox series

Patch

diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
index 1c7284fd535b..507cea4f2319 100644
--- a/Documentation/netlink/specs/netdev.yaml
+++ b/Documentation/netlink/specs/netdev.yaml
@@ -68,6 +68,38 @@  attribute-sets:
         type: u32
         checks:
           min: 1
+  -
+    name: napi-info-entry
+    attributes:
+      -
+        name: napi-id
+        doc: napi id
+        type: u32
+      -
+        name: rx-queues
+        doc: list of rx queues associated with a napi
+        type: u32
+        multi-attr: true
+      -
+        name: tx-queues
+        doc: list of tx queues associated with a napi
+        type: u32
+        multi-attr: true
+  -
+    name: napi
+    attributes:
+      -
+        name: ifindex
+        doc: netdev ifindex
+        type: u32
+        checks:
+          min: 1
+      -
+        name: napi-info
+        doc: napi information such as napi-id, napi queues etc.
+        type: nest
+        multi-attr: true
+        nested-attributes: napi-info-entry
 
 operations:
   list:
@@ -101,6 +133,20 @@  operations:
       doc: Notification about device configuration being changed.
       notify: dev-get
       mcgrp: mgmt
+    -
+      name: napi-get
+      doc: napi information such as napi-id, napi queues etc.
+      attribute-set: napi
+      do:
+        request:
+          attributes:
+            - ifindex
+        reply: &napi-all
+          attributes:
+            - ifindex
+            - napi-info
+      dump:
+        reply: *napi-all
 
 mcast-groups:
   list:
diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h
index c1634b95c223..bc06f692d9fd 100644
--- a/include/uapi/linux/netdev.h
+++ b/include/uapi/linux/netdev.h
@@ -48,11 +48,29 @@  enum {
 	NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)
 };
 
+enum {
+	NETDEV_A_NAPI_INFO_ENTRY_NAPI_ID = 1,
+	NETDEV_A_NAPI_INFO_ENTRY_RX_QUEUES,
+	NETDEV_A_NAPI_INFO_ENTRY_TX_QUEUES,
+
+	__NETDEV_A_NAPI_INFO_ENTRY_MAX,
+	NETDEV_A_NAPI_INFO_ENTRY_MAX = (__NETDEV_A_NAPI_INFO_ENTRY_MAX - 1)
+};
+
+enum {
+	NETDEV_A_NAPI_IFINDEX = 1,
+	NETDEV_A_NAPI_NAPI_INFO,
+
+	__NETDEV_A_NAPI_MAX,
+	NETDEV_A_NAPI_MAX = (__NETDEV_A_NAPI_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_NAPI_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..d09ce5db8b79 100644
--- a/net/core/netdev-genl-gen.c
+++ b/net/core/netdev-genl-gen.c
@@ -15,6 +15,11 @@  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_NAPI_GET - do */
+static const struct nla_policy netdev_napi_get_nl_policy[NETDEV_A_NAPI_IFINDEX + 1] = {
+	[NETDEV_A_NAPI_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),
+};
+
 /* Ops table for netdev */
 static const struct genl_split_ops netdev_nl_ops[] = {
 	{
@@ -29,6 +34,18 @@  static const struct genl_split_ops netdev_nl_ops[] = {
 		.dumpit	= netdev_nl_dev_get_dumpit,
 		.flags	= GENL_CMD_CAP_DUMP,
 	},
+	{
+		.cmd		= NETDEV_CMD_NAPI_GET,
+		.doit		= netdev_nl_napi_get_doit,
+		.policy		= netdev_napi_get_nl_policy,
+		.maxattr	= NETDEV_A_NAPI_IFINDEX,
+		.flags		= GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd	= NETDEV_CMD_NAPI_GET,
+		.dumpit	= netdev_nl_napi_get_dumpit,
+		.flags	= GENL_CMD_CAP_DUMP,
+	},
 };
 
 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..46dab8ccd568 100644
--- a/net/core/netdev-genl-gen.h
+++ b/net/core/netdev-genl-gen.h
@@ -13,6 +13,8 @@ 
 
 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_napi_get_doit(struct sk_buff *skb, struct genl_info *info);
+int netdev_nl_napi_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
 
 enum {
 	NETDEV_NLGRP_MGMT,
diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c
index 797c813c7c77..e35cfa3cd173 100644
--- a/net/core/netdev-genl.c
+++ b/net/core/netdev-genl.c
@@ -120,6 +120,16 @@  int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 	return skb->len;
 }
 
+int netdev_nl_napi_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return -EOPNOTSUPP;
+}
+
+int netdev_nl_napi_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	return -EOPNOTSUPP;
+}
+
 static int netdev_genl_netdevice_event(struct notifier_block *nb,
 				       unsigned long event, void *ptr)
 {
diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h
index c1634b95c223..bc06f692d9fd 100644
--- a/tools/include/uapi/linux/netdev.h
+++ b/tools/include/uapi/linux/netdev.h
@@ -48,11 +48,29 @@  enum {
 	NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)
 };
 
+enum {
+	NETDEV_A_NAPI_INFO_ENTRY_NAPI_ID = 1,
+	NETDEV_A_NAPI_INFO_ENTRY_RX_QUEUES,
+	NETDEV_A_NAPI_INFO_ENTRY_TX_QUEUES,
+
+	__NETDEV_A_NAPI_INFO_ENTRY_MAX,
+	NETDEV_A_NAPI_INFO_ENTRY_MAX = (__NETDEV_A_NAPI_INFO_ENTRY_MAX - 1)
+};
+
+enum {
+	NETDEV_A_NAPI_IFINDEX = 1,
+	NETDEV_A_NAPI_NAPI_INFO,
+
+	__NETDEV_A_NAPI_MAX,
+	NETDEV_A_NAPI_MAX = (__NETDEV_A_NAPI_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_NAPI_GET,
 
 	__NETDEV_CMD_MAX,
 	NETDEV_CMD_MAX = (__NETDEV_CMD_MAX - 1)
diff --git a/tools/net/ynl/generated/netdev-user.c b/tools/net/ynl/generated/netdev-user.c
index 68b408ca0f7f..e9a6c8cb5c68 100644
--- a/tools/net/ynl/generated/netdev-user.c
+++ b/tools/net/ynl/generated/netdev-user.c
@@ -18,6 +18,7 @@  static const char * const netdev_op_strmap[] = {
 	[NETDEV_CMD_DEV_ADD_NTF] = "dev-add-ntf",
 	[NETDEV_CMD_DEV_DEL_NTF] = "dev-del-ntf",
 	[NETDEV_CMD_DEV_CHANGE_NTF] = "dev-change-ntf",
+	[NETDEV_CMD_NAPI_GET] = "napi-get",
 };
 
 const char *netdev_op_str(int op)
@@ -46,6 +47,17 @@  const char *netdev_xdp_act_str(enum netdev_xdp_act value)
 }
 
 /* Policies */
+struct ynl_policy_attr netdev_napi_info_entry_policy[NETDEV_A_NAPI_INFO_ENTRY_MAX + 1] = {
+	[NETDEV_A_NAPI_INFO_ENTRY_NAPI_ID] = { .name = "napi-id", .type = YNL_PT_U32, },
+	[NETDEV_A_NAPI_INFO_ENTRY_RX_QUEUES] = { .name = "rx-queues", .type = YNL_PT_U32, },
+	[NETDEV_A_NAPI_INFO_ENTRY_TX_QUEUES] = { .name = "tx-queues", .type = YNL_PT_U32, },
+};
+
+struct ynl_policy_nest netdev_napi_info_entry_nest = {
+	.max_attr = NETDEV_A_NAPI_INFO_ENTRY_MAX,
+	.table = netdev_napi_info_entry_policy,
+};
+
 struct ynl_policy_attr netdev_dev_policy[NETDEV_A_DEV_MAX + 1] = {
 	[NETDEV_A_DEV_IFINDEX] = { .name = "ifindex", .type = YNL_PT_U32, },
 	[NETDEV_A_DEV_PAD] = { .name = "pad", .type = YNL_PT_IGNORE, },
@@ -58,7 +70,78 @@  struct ynl_policy_nest netdev_dev_nest = {
 	.table = netdev_dev_policy,
 };
 
+struct ynl_policy_attr netdev_napi_policy[NETDEV_A_NAPI_MAX + 1] = {
+	[NETDEV_A_NAPI_IFINDEX] = { .name = "ifindex", .type = YNL_PT_U32, },
+	[NETDEV_A_NAPI_NAPI_INFO] = { .name = "napi-info", .type = YNL_PT_NEST, .nest = &netdev_napi_info_entry_nest, },
+};
+
+struct ynl_policy_nest netdev_napi_nest = {
+	.max_attr = NETDEV_A_NAPI_MAX,
+	.table = netdev_napi_policy,
+};
+
 /* Common nested types */
+void netdev_napi_info_entry_free(struct netdev_napi_info_entry *obj)
+{
+	free(obj->rx_queues);
+	free(obj->tx_queues);
+}
+
+int netdev_napi_info_entry_parse(struct ynl_parse_arg *yarg,
+				 const struct nlattr *nested)
+{
+	struct netdev_napi_info_entry *dst = yarg->data;
+	unsigned int n_rx_queues = 0;
+	unsigned int n_tx_queues = 0;
+	const struct nlattr *attr;
+	int i;
+
+	if (dst->rx_queues)
+		return ynl_error_parse(yarg, "attribute already present (napi-info-entry.rx-queues)");
+	if (dst->tx_queues)
+		return ynl_error_parse(yarg, "attribute already present (napi-info-entry.tx-queues)");
+
+	mnl_attr_for_each_nested(attr, nested) {
+		unsigned int type = mnl_attr_get_type(attr);
+
+		if (type == NETDEV_A_NAPI_INFO_ENTRY_NAPI_ID) {
+			if (ynl_attr_validate(yarg, attr))
+				return MNL_CB_ERROR;
+			dst->_present.napi_id = 1;
+			dst->napi_id = mnl_attr_get_u32(attr);
+		} else if (type == NETDEV_A_NAPI_INFO_ENTRY_RX_QUEUES) {
+			n_rx_queues++;
+		} else if (type == NETDEV_A_NAPI_INFO_ENTRY_TX_QUEUES) {
+			n_tx_queues++;
+		}
+	}
+
+	if (n_rx_queues) {
+		dst->rx_queues = calloc(n_rx_queues, sizeof(*dst->rx_queues));
+		dst->n_rx_queues = n_rx_queues;
+		i = 0;
+		mnl_attr_for_each_nested(attr, nested) {
+			if (mnl_attr_get_type(attr) == NETDEV_A_NAPI_INFO_ENTRY_RX_QUEUES) {
+				dst->rx_queues[i] = mnl_attr_get_u32(attr);
+				i++;
+			}
+		}
+	}
+	if (n_tx_queues) {
+		dst->tx_queues = calloc(n_tx_queues, sizeof(*dst->tx_queues));
+		dst->n_tx_queues = n_tx_queues;
+		i = 0;
+		mnl_attr_for_each_nested(attr, nested) {
+			if (mnl_attr_get_type(attr) == NETDEV_A_NAPI_INFO_ENTRY_TX_QUEUES) {
+				dst->tx_queues[i] = mnl_attr_get_u32(attr);
+				i++;
+			}
+		}
+	}
+
+	return 0;
+}
+
 /* ============== NETDEV_CMD_DEV_GET ============== */
 /* NETDEV_CMD_DEV_GET - do */
 void netdev_dev_get_req_free(struct netdev_dev_get_req *req)
@@ -178,6 +261,143 @@  void netdev_dev_get_ntf_free(struct netdev_dev_get_ntf *rsp)
 	free(rsp);
 }
 
+/* ============== NETDEV_CMD_NAPI_GET ============== */
+/* NETDEV_CMD_NAPI_GET - do */
+void netdev_napi_get_req_free(struct netdev_napi_get_req *req)
+{
+	free(req);
+}
+
+void netdev_napi_get_rsp_free(struct netdev_napi_get_rsp *rsp)
+{
+	unsigned int i;
+
+	for (i = 0; i < rsp->n_napi_info; i++)
+		netdev_napi_info_entry_free(&rsp->napi_info[i]);
+	free(rsp->napi_info);
+	free(rsp);
+}
+
+int netdev_napi_get_rsp_parse(const struct nlmsghdr *nlh, void *data)
+{
+	struct ynl_parse_arg *yarg = data;
+	struct netdev_napi_get_rsp *dst;
+	unsigned int n_napi_info = 0;
+	const struct nlattr *attr;
+	struct ynl_parse_arg parg;
+	int i;
+
+	dst = yarg->data;
+	parg.ys = yarg->ys;
+
+	if (dst->napi_info)
+		return ynl_error_parse(yarg, "attribute already present (napi.napi-info)");
+
+	mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
+		unsigned int type = mnl_attr_get_type(attr);
+
+		if (type == NETDEV_A_NAPI_IFINDEX) {
+			if (ynl_attr_validate(yarg, attr))
+				return MNL_CB_ERROR;
+			dst->_present.ifindex = 1;
+			dst->ifindex = mnl_attr_get_u32(attr);
+		} else if (type == NETDEV_A_NAPI_NAPI_INFO) {
+			n_napi_info++;
+		}
+	}
+
+	if (n_napi_info) {
+		dst->napi_info = calloc(n_napi_info, sizeof(*dst->napi_info));
+		dst->n_napi_info = n_napi_info;
+		i = 0;
+		parg.rsp_policy = &netdev_napi_info_entry_nest;
+		mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
+			if (mnl_attr_get_type(attr) == NETDEV_A_NAPI_NAPI_INFO) {
+				parg.data = &dst->napi_info[i];
+				if (netdev_napi_info_entry_parse(&parg, attr))
+					return MNL_CB_ERROR;
+				i++;
+			}
+		}
+	}
+
+	return MNL_CB_OK;
+}
+
+struct netdev_napi_get_rsp *
+netdev_napi_get(struct ynl_sock *ys, struct netdev_napi_get_req *req)
+{
+	struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };
+	struct netdev_napi_get_rsp *rsp;
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = ynl_gemsg_start_req(ys, ys->family_id, NETDEV_CMD_NAPI_GET, 1);
+	ys->req_policy = &netdev_napi_nest;
+	yrs.yarg.rsp_policy = &netdev_napi_nest;
+
+	if (req->_present.ifindex)
+		mnl_attr_put_u32(nlh, NETDEV_A_NAPI_IFINDEX, req->ifindex);
+
+	rsp = calloc(1, sizeof(*rsp));
+	yrs.yarg.data = rsp;
+	yrs.cb = netdev_napi_get_rsp_parse;
+	yrs.rsp_cmd = NETDEV_CMD_NAPI_GET;
+
+	err = ynl_exec(ys, nlh, &yrs);
+	if (err < 0)
+		goto err_free;
+
+	return rsp;
+
+err_free:
+	netdev_napi_get_rsp_free(rsp);
+	return NULL;
+}
+
+/* NETDEV_CMD_NAPI_GET - dump */
+void netdev_napi_get_list_free(struct netdev_napi_get_list *rsp)
+{
+	struct netdev_napi_get_list *next = rsp;
+
+	while ((void *)next != YNL_LIST_END) {
+		unsigned int i;
+
+		rsp = next;
+		next = rsp->next;
+
+		for (i = 0; i < rsp->obj.n_napi_info; i++)
+			netdev_napi_info_entry_free(&rsp->obj.napi_info[i]);
+		free(rsp->obj.napi_info);
+		free(rsp);
+	}
+}
+
+struct netdev_napi_get_list *netdev_napi_get_dump(struct ynl_sock *ys)
+{
+	struct ynl_dump_state yds = {};
+	struct nlmsghdr *nlh;
+	int err;
+
+	yds.ys = ys;
+	yds.alloc_sz = sizeof(struct netdev_napi_get_list);
+	yds.cb = netdev_napi_get_rsp_parse;
+	yds.rsp_cmd = NETDEV_CMD_NAPI_GET;
+	yds.rsp_policy = &netdev_napi_nest;
+
+	nlh = ynl_gemsg_start_dump(ys, ys->family_id, NETDEV_CMD_NAPI_GET, 1);
+
+	err = ynl_exec_dump(ys, nlh, &yds);
+	if (err < 0)
+		goto free_list;
+
+	return yds.first;
+
+free_list:
+	netdev_napi_get_list_free(yds.first);
+	return NULL;
+}
+
 static const struct ynl_ntf_info netdev_ntf_info[] =  {
 	[NETDEV_CMD_DEV_ADD_NTF] =  {
 		.alloc_sz	= sizeof(struct netdev_dev_get_ntf),
diff --git a/tools/net/ynl/generated/netdev-user.h b/tools/net/ynl/generated/netdev-user.h
index 0952d3261f4d..9274711bd862 100644
--- a/tools/net/ynl/generated/netdev-user.h
+++ b/tools/net/ynl/generated/netdev-user.h
@@ -20,6 +20,18 @@  const char *netdev_op_str(int op);
 const char *netdev_xdp_act_str(enum netdev_xdp_act value);
 
 /* Common nested types */
+struct netdev_napi_info_entry {
+	struct {
+		__u32 napi_id:1;
+	} _present;
+
+	__u32 napi_id;
+	unsigned int n_rx_queues;
+	__u32 *rx_queues;
+	unsigned int n_tx_queues;
+	__u32 *tx_queues;
+};
+
 /* ============== NETDEV_CMD_DEV_GET ============== */
 /* NETDEV_CMD_DEV_GET - do */
 struct netdev_dev_get_req {
@@ -84,4 +96,55 @@  struct netdev_dev_get_ntf {
 
 void netdev_dev_get_ntf_free(struct netdev_dev_get_ntf *rsp);
 
+/* ============== NETDEV_CMD_NAPI_GET ============== */
+/* NETDEV_CMD_NAPI_GET - do */
+struct netdev_napi_get_req {
+	struct {
+		__u32 ifindex:1;
+	} _present;
+
+	__u32 ifindex;
+};
+
+static inline struct netdev_napi_get_req *netdev_napi_get_req_alloc(void)
+{
+	return calloc(1, sizeof(struct netdev_napi_get_req));
+}
+void netdev_napi_get_req_free(struct netdev_napi_get_req *req);
+
+static inline void
+netdev_napi_get_req_set_ifindex(struct netdev_napi_get_req *req, __u32 ifindex)
+{
+	req->_present.ifindex = 1;
+	req->ifindex = ifindex;
+}
+
+struct netdev_napi_get_rsp {
+	struct {
+		__u32 ifindex:1;
+	} _present;
+
+	__u32 ifindex;
+	unsigned int n_napi_info;
+	struct netdev_napi_info_entry *napi_info;
+};
+
+void netdev_napi_get_rsp_free(struct netdev_napi_get_rsp *rsp);
+
+/*
+ * napi information such as napi-id, napi queues etc.
+ */
+struct netdev_napi_get_rsp *
+netdev_napi_get(struct ynl_sock *ys, struct netdev_napi_get_req *req);
+
+/* NETDEV_CMD_NAPI_GET - dump */
+struct netdev_napi_get_list {
+	struct netdev_napi_get_list *next;
+	struct netdev_napi_get_rsp obj __attribute__ ((aligned (8)));
+};
+
+void netdev_napi_get_list_free(struct netdev_napi_get_list *rsp);
+
+struct netdev_napi_get_list *netdev_napi_get_dump(struct ynl_sock *ys);
+
 #endif /* _LINUX_NETDEV_GEN_H */