diff mbox series

[net-next/RFC,v1,4/4] netdev-genl: Add support for exposing napi info from netdev

Message ID 168564136118.7284.18138054610456895287.stgit@anambiarhost.jf.intel.com (mailing list archive)
State RFC
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: 4174 this patch: 4174
netdev/cc_maintainers warning 11 maintainers not CCed: hawk@kernel.org pabeni@redhat.com daniel@iogearbox.net john.fastabend@gmail.com alardam@gmail.com memxor@gmail.com tariqt@nvidia.com bpf@vger.kernel.org lorenzo@kernel.org ast@kernel.org edumazet@google.com
netdev/build_clang success Errors and warnings before: 923 this patch: 923
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: 4393 this patch: 4393
netdev/checkpatch warning WARNING: line length of 83 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 2 this patch: 2
netdev/source_inline success Was 0 now: 0

Commit Message

Nambiar, Amritha June 1, 2023, 5:42 p.m. UTC
Add support in ynl/netdev.yaml for napi related information. The
netdev structure tracks all the napi instances and napi fields.
The napi instances and associated queue[s] can be retrieved this way.

Refactored netdev-genl to support exposing napi<->queue[s] mapping
that is retained in a netdev.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 Documentation/netlink/specs/netdev.yaml |   39 +++++
 include/uapi/linux/netdev.h             |    4 +
 net/core/netdev-genl.c                  |  239 ++++++++++++++++++++++++++-----
 tools/include/uapi/linux/netdev.h       |    4 +
 4 files changed, 247 insertions(+), 39 deletions(-)

Comments

Simon Horman June 2, 2023, 3:47 p.m. UTC | #1
On Thu, Jun 01, 2023 at 10:42:41AM -0700, Amritha Nambiar wrote:
> Add support in ynl/netdev.yaml for napi related information. The
> netdev structure tracks all the napi instances and napi fields.
> The napi instances and associated queue[s] can be retrieved this way.
> 
> Refactored netdev-genl to support exposing napi<->queue[s] mapping
> that is retained in a netdev.

Hi Amritha,

This feels like it should be two patches to me.
Though it is not something I feel strongly about.

> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>

...

> +static int
> +netdev_nl_dev_napi_prepare_fill(struct net_device *netdev,
> +				struct sk_buff **pskb, u32 portid, u32 seq,
> +				int flags, u32 cmd, enum netdev_nl_type type)
> +{
> +	struct nlmsghdr *nlh;
> +	struct sk_buff *skb = *pskb;
> +	bool last = false;
> +	int index = 0;
> +	void *hdr;
> +	int err;
> +

nit: please use reverse xmas tree - longest line to shortest - for
     local variable declarations in (new) Networking code.

...
Jakub Kicinski June 3, 2023, 6:08 a.m. UTC | #2
On Fri, 2 Jun 2023 17:47:17 +0200 Simon Horman wrote:
> This feels like it should be two patches to me.
> Though it is not something I feel strongly about.

+1 I'd put the YAML and what's generated from it in one patch, 
and the hand-written code in another.
Jakub Kicinski June 3, 2023, 6:17 a.m. UTC | #3
On Thu, 01 Jun 2023 10:42:41 -0700 Amritha Nambiar wrote:
> Add support in ynl/netdev.yaml for napi related information. The
> netdev structure tracks all the napi instances and napi fields.
> The napi instances and associated queue[s] can be retrieved this way.
> 
> Refactored netdev-genl to support exposing napi<->queue[s] mapping
> that is retained in a netdev.
> 
> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> ---
>  Documentation/netlink/specs/netdev.yaml |   39 +++++
>  include/uapi/linux/netdev.h             |    4 +
>  net/core/netdev-genl.c                  |  239 ++++++++++++++++++++++++++-----
>  tools/include/uapi/linux/netdev.h       |    4 +
>  4 files changed, 247 insertions(+), 39 deletions(-)
> 
> diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
> index b99e7ffef7a1..8d0edb529563 100644
> --- a/Documentation/netlink/specs/netdev.yaml
> +++ b/Documentation/netlink/specs/netdev.yaml
> @@ -62,6 +62,44 @@ attribute-sets:
>          type: u64
>          enum: xdp-act
>          enum-as-flags: true
> +      -
> +        name: napi-info
> +        doc: napi information such as napi-id, napi queues etc.
> +        type: nest
> +        multi-attr: true

Let's make a new attr space for the napi info command.
We don't reuse much of the attributes, and as the commands 
grow stuffing all attrs into one space makes finding stuff 
harder.

> +        nested-attributes: dev-napi-info

And what's inside this nest should also be a separate attr space.


> +      -
> +        name: napi-id
> +        doc: napi id
> +        type: u32
> +      -
> +        name: rx-queues
> +        doc: list of rx queues associated with a napi
> +        type: u16

Make it u32, at the uAPI level we're tried to the width of fields, and
u16 ends up being the same size as u32 "on the wire" due to padding.

> +        multi-attr: true
> +      -
> +        name: tx-queues
> +        doc: list of tx queues associated with a napi
> +        type: u16
> +        multi-attr: true


> +  -
> +    name: dev-napi-info
> +    subset-of: dev

Yeah, this shouldn't be a subset just a full-on separate attr space.
The handshake family may be a good example to look at, it's the biggest
so far written with the new rules in mind. 

> +    attributes:
> +      -
> +        name: napi-id
> +        doc: napi id
> +        type: u32
> +      -
> +        name: rx-queues
> +        doc: list rx of queues associated with a napi
> +        type: u16
> +        multi-attr: true
> +      -
> +        name: tx-queues
> +        doc: list tx of queues associated with a napi
> +        type: u16
> +        multi-attr: true
>  
>  operations:
>    list:
> @@ -77,6 +115,7 @@ operations:
>            attributes:
>              - ifindex
>              - xdp-features
> +            - napi-info

Aaah, separate command, please. Let's not stuff all the information
into a single command like we did for rtnl.

>        dump:
>          reply: *dev-all
>      -
Nambiar, Amritha July 12, 2023, 7:54 p.m. UTC | #4
On 6/2/2023 8:47 AM, Simon Horman wrote:
> On Thu, Jun 01, 2023 at 10:42:41AM -0700, Amritha Nambiar wrote:
>> Add support in ynl/netdev.yaml for napi related information. The
>> netdev structure tracks all the napi instances and napi fields.
>> The napi instances and associated queue[s] can be retrieved this way.
>>
>> Refactored netdev-genl to support exposing napi<->queue[s] mapping
>> that is retained in a netdev.
> 
> Hi Amritha,
> 
> This feels like it should be two patches to me.
> Though it is not something I feel strongly about.
> 

Thanks for pointing that out, I'll split this patch into two.

>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
> 
> ...
> 
>> +static int
>> +netdev_nl_dev_napi_prepare_fill(struct net_device *netdev,
>> +				struct sk_buff **pskb, u32 portid, u32 seq,
>> +				int flags, u32 cmd, enum netdev_nl_type type)
>> +{
>> +	struct nlmsghdr *nlh;
>> +	struct sk_buff *skb = *pskb;
>> +	bool last = false;
>> +	int index = 0;
>> +	void *hdr;
>> +	int err;
>> +
> 
> nit: please use reverse xmas tree - longest line to shortest - for
>       local variable declarations in (new) Networking code.
> 
> ...
Will fix in next version.
Nambiar, Amritha July 12, 2023, 8:05 p.m. UTC | #5
On 6/2/2023 11:08 PM, Jakub Kicinski wrote:
> On Fri, 2 Jun 2023 17:47:17 +0200 Simon Horman wrote:
>> This feels like it should be two patches to me.
>> Though it is not something I feel strongly about.
> 
> +1 I'd put the YAML and what's generated from it in one patch,
> and the hand-written code in another.

Will fix in the next version.
Nambiar, Amritha July 12, 2023, 8:10 p.m. UTC | #6
On 6/2/2023 11:17 PM, Jakub Kicinski wrote:
> On Thu, 01 Jun 2023 10:42:41 -0700 Amritha Nambiar wrote:
>> Add support in ynl/netdev.yaml for napi related information. The
>> netdev structure tracks all the napi instances and napi fields.
>> The napi instances and associated queue[s] can be retrieved this way.
>>
>> Refactored netdev-genl to support exposing napi<->queue[s] mapping
>> that is retained in a netdev.
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> ---
>>   Documentation/netlink/specs/netdev.yaml |   39 +++++
>>   include/uapi/linux/netdev.h             |    4 +
>>   net/core/netdev-genl.c                  |  239 ++++++++++++++++++++++++++-----
>>   tools/include/uapi/linux/netdev.h       |    4 +
>>   4 files changed, 247 insertions(+), 39 deletions(-)
>>
>> diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
>> index b99e7ffef7a1..8d0edb529563 100644
>> --- a/Documentation/netlink/specs/netdev.yaml
>> +++ b/Documentation/netlink/specs/netdev.yaml
>> @@ -62,6 +62,44 @@ attribute-sets:
>>           type: u64
>>           enum: xdp-act
>>           enum-as-flags: true
>> +      -
>> +        name: napi-info
>> +        doc: napi information such as napi-id, napi queues etc.
>> +        type: nest
>> +        multi-attr: true
> 
> Let's make a new attr space for the napi info command.
> We don't reuse much of the attributes, and as the commands
> grow stuffing all attrs into one space makes finding stuff
> harder.
> 

Agree. I should not have overloaded the 'dev' command to begin with.

>> +        nested-attributes: dev-napi-info
> 
> And what's inside this nest should also be a separate attr space.
> 

So, I think we could have two new commands for napi data. Would this be 
acceptable, a 'napi-queue-get' command for napi-queue specific 
information (set of TX and RX queues, IRQ number etc.), and another 
'napi-info-get' for other information,  such as PID for the napi thread, 
CPU etc.

Example:
  $ ./cli.py --spec netdev.yaml  --do napi-queue-get --json='{"ifindex": 
12}'

[{'napi-info': [{'napi-id': 600, 'rx-queues': [7], 'tx-queues': [7], 
'irq': 298},
                 {'napi-id': 599, 'rx-queues': [6], 'tx-queues': [6], 
'irq': 297},
                 {'napi-id': 598, 'rx-queues': [5], 'tx-queues': [5], 
'irq': 296},
                 {'napi-id': 597, 'rx-queues': [4], 'tx-queues': [4], 
'irq': 295},
                 {'napi-id': 596, 'rx-queues': [3], 'tx-queues': [3], 
'irq': 294},
                 {'napi-id': 595, 'rx-queues': [2], 'tx-queues': [2], 
'irq': 293},
                 {'napi-id': 594, 'rx-queues': [1], 'tx-queues': [1], 
'irq': 292},
                 {'napi-id': 593, 'rx-queues': [0], 'tx-queues': [0], 
'irq': 291}]}]
				

$ ./cli.py --spec netdev.yaml  --do napi-info-get --json='{"ifindex": 12}'

[{'napi-info': [{'napi-id': 600, 'pid': 68114},
                 {'napi-id': 599, 'pid': 68113},
                 {'napi-id': 598, 'pid': 68112},
                 {'napi-id': 597, 'pid': 68111},
                 {'napi-id': 596, 'pid': 68110},
                 {'napi-id': 595, 'pid': 68109},
                 {'napi-id': 594, 'pid': 68108},
                 {'napi-id': 593, 'pid': 68107}]}]
> 
>> +      -
>> +        name: napi-id
>> +        doc: napi id
>> +        type: u32
>> +      -
>> +        name: rx-queues
>> +        doc: list of rx queues associated with a napi
>> +        type: u16
> 
> Make it u32, at the uAPI level we're tried to the width of fields, and
> u16 ends up being the same size as u32 "on the wire" due to padding.
> 

Makes sense. Will fix.

>> +        multi-attr: true
>> +      -
>> +        name: tx-queues
>> +        doc: list of tx queues associated with a napi
>> +        type: u16
>> +        multi-attr: true
> 
> 
>> +  -
>> +    name: dev-napi-info
>> +    subset-of: dev
> 
> Yeah, this shouldn't be a subset just a full-on separate attr space.
> The handshake family may be a good example to look at, it's the biggest
> so far written with the new rules in mind.
> 

Okay.

>> +    attributes:
>> +      -
>> +        name: napi-id
>> +        doc: napi id
>> +        type: u32
>> +      -
>> +        name: rx-queues
>> +        doc: list rx of queues associated with a napi
>> +        type: u16
>> +        multi-attr: true
>> +      -
>> +        name: tx-queues
>> +        doc: list tx of queues associated with a napi
>> +        type: u16
>> +        multi-attr: true
>>   
>>   operations:
>>     list:
>> @@ -77,6 +115,7 @@ operations:
>>             attributes:
>>               - ifindex
>>               - xdp-features
>> +            - napi-info
> 
> Aaah, separate command, please. Let's not stuff all the information
> into a single command like we did for rtnl.
> 

Okay.

>>         dump:
>>           reply: *dev-all
>>       -
Jakub Kicinski July 12, 2023, 9:19 p.m. UTC | #7
On Wed, 12 Jul 2023 13:10:45 -0700 Nambiar, Amritha wrote:
> So, I think we could have two new commands for napi data. Would this be 
> acceptable, a 'napi-queue-get' command for napi-queue specific 
> information (set of TX and RX queues, IRQ number etc.), and another 
> 'napi-info-get' for other information,  such as PID for the napi thread, 
> CPU etc.
> 
> Example:
>   $ ./cli.py --spec netdev.yaml  --do napi-queue-get --json='{"ifindex": 
> 12}'
> 
> [{'napi-info': [{'napi-id': 600, 'rx-queues': [7], 'tx-queues': [7], 
> 'irq': 298},

I think the commands make sense. You should echo back the ifindex, tho,
it threw me off initially that there's only on attribute ('napi-info')
in the reply, in which case the nest would have been pointless..
diff mbox series

Patch

diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
index b99e7ffef7a1..8d0edb529563 100644
--- a/Documentation/netlink/specs/netdev.yaml
+++ b/Documentation/netlink/specs/netdev.yaml
@@ -62,6 +62,44 @@  attribute-sets:
         type: u64
         enum: xdp-act
         enum-as-flags: true
+      -
+        name: napi-info
+        doc: napi information such as napi-id, napi queues etc.
+        type: nest
+        multi-attr: true
+        nested-attributes: dev-napi-info
+      -
+        name: napi-id
+        doc: napi id
+        type: u32
+      -
+        name: rx-queues
+        doc: list of rx queues associated with a napi
+        type: u16
+        multi-attr: true
+      -
+        name: tx-queues
+        doc: list of tx queues associated with a napi
+        type: u16
+        multi-attr: true
+  -
+    name: dev-napi-info
+    subset-of: dev
+    attributes:
+      -
+        name: napi-id
+        doc: napi id
+        type: u32
+      -
+        name: rx-queues
+        doc: list rx of queues associated with a napi
+        type: u16
+        multi-attr: true
+      -
+        name: tx-queues
+        doc: list tx of queues associated with a napi
+        type: u16
+        multi-attr: true
 
 operations:
   list:
@@ -77,6 +115,7 @@  operations:
           attributes:
             - ifindex
             - xdp-features
+            - napi-info
       dump:
         reply: *dev-all
     -
diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h
index 639524b59930..16538fb1406a 100644
--- a/include/uapi/linux/netdev.h
+++ b/include/uapi/linux/netdev.h
@@ -41,6 +41,10 @@  enum {
 	NETDEV_A_DEV_IFINDEX = 1,
 	NETDEV_A_DEV_PAD,
 	NETDEV_A_DEV_XDP_FEATURES,
+	NETDEV_A_DEV_NAPI_INFO,
+	NETDEV_A_DEV_NAPI_ID,
+	NETDEV_A_DEV_RX_QUEUES,
+	NETDEV_A_DEV_TX_QUEUES,
 
 	__NETDEV_A_DEV_MAX,
 	NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)
diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c
index 8d6a840821c7..fdaa67f53b22 100644
--- a/net/core/netdev-genl.c
+++ b/net/core/netdev-genl.c
@@ -11,6 +11,7 @@ 
 struct netdev_nl_dump_ctx {
 	int dev_entry_hash;
 	int dev_entry_idx;
+	int napi_idx;
 };
 
 static inline struct netdev_nl_dump_ctx *
@@ -21,54 +22,187 @@  netdev_dump_ctx(struct netlink_callback *cb)
 	return (struct netdev_nl_dump_ctx *)cb->ctx;
 }
 
+enum netdev_nl_type {
+	NETDEV_NL_DO,
+	NETDEV_NL_NOTIFY,
+};
+
+static int netdev_nl_send_func(struct net_device *netdev, struct sk_buff *skb,
+			       u32 portid, enum netdev_nl_type type)
+{
+	switch (type) {
+	case NETDEV_NL_DO:
+		return genlmsg_unicast(dev_net(netdev), skb, portid);
+	case NETDEV_NL_NOTIFY:
+		return genlmsg_multicast_netns(&netdev_nl_family,
+					       dev_net(netdev), skb, 0,
+					       NETDEV_NLGRP_MGMT, GFP_KERNEL);
+	default:
+		return -EINVAL;
+	}
+}
+
 static int
-netdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp,
-		   u32 portid, u32 seq, int flags, u32 cmd)
+netdev_nl_dev_napi_fill_one(struct sk_buff *msg, struct napi_struct *napi)
 {
-	void *hdr;
+	struct nlattr *napi_info;
+	struct napi_queue *q, *n;
 
-	hdr = genlmsg_put(rsp, portid, seq, &netdev_nl_family, flags, cmd);
-	if (!hdr)
+	napi_info = nla_nest_start(msg, NETDEV_A_DEV_NAPI_INFO);
+	if (!napi_info)
 		return -EMSGSIZE;
 
+	if (nla_put_u32(msg, NETDEV_A_DEV_NAPI_ID, napi->napi_id))
+		goto nla_put_failure;
+
+	list_for_each_entry_safe(q, n, &napi->napi_rxq_list, q_list) {
+		if (nla_put_u16(msg, NETDEV_A_DEV_RX_QUEUES, q->queue_index))
+			goto nla_put_failure;
+	}
+
+	list_for_each_entry_safe(q, n, &napi->napi_txq_list, q_list) {
+		if (nla_put_u16(msg, NETDEV_A_DEV_TX_QUEUES, q->queue_index))
+			goto nla_put_failure;
+	}
+	nla_nest_end(msg, napi_info);
+	return 0;
+nla_put_failure:
+	nla_nest_cancel(msg, napi_info);
+	return -EMSGSIZE;
+}
+
+static int
+netdev_nl_dev_napi_fill(struct net_device *netdev, struct sk_buff *msg, int *start)
+{
+	struct napi_struct *napi, *n;
+	int i = 0;
+
+	list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) {
+		if (i < *start) {
+			i++;
+			continue;
+		}
+		if (netdev_nl_dev_napi_fill_one(msg, napi))
+			return -EMSGSIZE;
+		*start = ++i;
+	}
+	return 0;
+}
+
+static int
+netdev_nl_dev_napi_prepare_fill(struct net_device *netdev,
+				struct sk_buff **pskb, u32 portid, u32 seq,
+				int flags, u32 cmd, enum netdev_nl_type type)
+{
+	struct nlmsghdr *nlh;
+	struct sk_buff *skb = *pskb;
+	bool last = false;
+	int index = 0;
+	void *hdr;
+	int err;
+
+	while (!last) {
+		int tmp_index = index;
+
+		skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+		if (!skb)
+			return -ENOMEM;
+
+		hdr = genlmsg_put(skb, portid, seq, &netdev_nl_family,
+				  flags | NLM_F_MULTI, cmd);
+		if (!hdr) {
+			err = -EMSGSIZE;
+			goto nla_put_failure;
+		}
+		err = netdev_nl_dev_napi_fill(netdev, skb, &index);
+		if (!err)
+			last = true;
+		else if (err != -EMSGSIZE || tmp_index == index)
+			goto nla_put_failure;
+
+		genlmsg_end(skb, hdr);
+		err = netdev_nl_send_func(netdev, skb, portid, type);
+		if (err)
+			return err;
+	}
+
+	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+	nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
+	if (!nlh) {
+		err = -EMSGSIZE;
+		goto nla_put_failure;
+	}
+
+	return netdev_nl_send_func(netdev, skb, portid, type);
+
+nla_put_failure:
+	nlmsg_free(skb);
+	return err;
+}
+
+static int
+netdev_nl_dev_info_fill(struct net_device *netdev, struct sk_buff *rsp)
+{
 	if (nla_put_u32(rsp, NETDEV_A_DEV_IFINDEX, netdev->ifindex) ||
 	    nla_put_u64_64bit(rsp, NETDEV_A_DEV_XDP_FEATURES,
-			      netdev->xdp_features, NETDEV_A_DEV_PAD)) {
-		genlmsg_cancel(rsp, hdr);
-		return -EINVAL;
+			      netdev->xdp_features, NETDEV_A_DEV_PAD))
+		return -EMSGSIZE;
+	return 0;
+}
+
+static int
+netdev_nl_dev_fill(struct net_device *netdev, u32 portid, u32 seq, int flags,
+		   u32 cmd, enum netdev_nl_type type)
+{
+	struct sk_buff *skb;
+	void *hdr;
+	int err;
+
+	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(skb, portid, seq, &netdev_nl_family, flags, cmd);
+	if (!hdr) {
+		err = -EMSGSIZE;
+		goto err_free_msg;
+	}
+	err = netdev_nl_dev_info_fill(netdev, skb);
+	if (err) {
+		genlmsg_cancel(skb, hdr);
+		goto err_free_msg;
 	}
 
-	genlmsg_end(rsp, hdr);
+	genlmsg_end(skb, hdr);
 
-	return 0;
+	err = netdev_nl_send_func(netdev, skb, portid, type);
+	if (err)
+		return err;
+
+	return netdev_nl_dev_napi_prepare_fill(netdev, &skb, portid, seq, flags,
+					       cmd, type);
+
+err_free_msg:
+	nlmsg_free(skb);
+	return err;
 }
 
 static void
 netdev_genl_dev_notify(struct net_device *netdev, int cmd)
 {
-	struct sk_buff *ntf;
-
 	if (!genl_has_listeners(&netdev_nl_family, dev_net(netdev),
 				NETDEV_NLGRP_MGMT))
 		return;
 
-	ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
-	if (!ntf)
-		return;
-
-	if (netdev_nl_dev_fill(netdev, ntf, 0, 0, 0, cmd)) {
-		nlmsg_free(ntf);
-		return;
-	}
+	netdev_nl_dev_fill(netdev, 0, 0, 0, cmd, NETDEV_NL_NOTIFY);
 
-	genlmsg_multicast_netns(&netdev_nl_family, dev_net(netdev), ntf,
-				0, NETDEV_NLGRP_MGMT, GFP_KERNEL);
 }
 
 int netdev_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct net_device *netdev;
-	struct sk_buff *rsp;
 	u32 ifindex;
 	int err;
 
@@ -77,29 +211,53 @@  int netdev_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
 
 	ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]);
 
-	rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
-	if (!rsp)
-		return -ENOMEM;
-
 	rtnl_lock();
 
 	netdev = __dev_get_by_index(genl_info_net(info), ifindex);
 	if (netdev)
-		err = netdev_nl_dev_fill(netdev, rsp, info->snd_portid,
-					 info->snd_seq, 0, info->genlhdr->cmd);
+		err = netdev_nl_dev_fill(netdev, info->snd_portid,
+					 info->snd_seq, 0, info->genlhdr->cmd,
+					 NETDEV_NL_DO);
 	else
 		err = -ENODEV;
 
 	rtnl_unlock();
-
 	if (err)
-		goto err_free_msg;
+		return err;
 
-	return genlmsg_reply(rsp, info);
+	return 0;
+}
+
+static int
+netdev_nl_dev_dump_entry(struct net_device *netdev, struct sk_buff *rsp,
+			 struct netlink_callback *cb, int *start)
+{
+	int index = *start;
+	int tmp_index = index;
+	void *hdr;
+	int err;
+
+	hdr = genlmsg_put(rsp, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+			  &netdev_nl_family, NLM_F_MULTI, NETDEV_CMD_DEV_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	if (netdev_nl_dev_info_fill(netdev, rsp))
+		goto nla_put_failure;
+
+	err =  netdev_nl_dev_napi_fill(netdev, rsp, &index);
+	if (err) {
+		if (err != -EMSGSIZE || tmp_index == index)
+			goto nla_put_failure;
+	}
+	*start = index;
+	genlmsg_end(rsp, hdr);
 
-err_free_msg:
-	nlmsg_free(rsp);
 	return err;
+
+nla_put_failure:
+	genlmsg_cancel(rsp, hdr);
+	return -EINVAL;
 }
 
 int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
@@ -107,12 +265,13 @@  int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 	struct netdev_nl_dump_ctx *ctx = netdev_dump_ctx(cb);
 	struct net *net = sock_net(skb->sk);
 	struct net_device *netdev;
-	int idx = 0, s_idx;
+	int idx = 0, s_idx, n_idx;
 	int h, s_h;
 	int err;
 
 	s_h = ctx->dev_entry_hash;
 	s_idx = ctx->dev_entry_idx;
+	n_idx = ctx->napi_idx;
 
 	rtnl_lock();
 
@@ -124,10 +283,10 @@  int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 		hlist_for_each_entry(netdev, head, index_hlist) {
 			if (idx < s_idx)
 				goto cont;
-			err = netdev_nl_dev_fill(netdev, skb,
-						 NETLINK_CB(cb->skb).portid,
-						 cb->nlh->nlmsg_seq, 0,
-						 NETDEV_CMD_DEV_GET);
+			err = netdev_nl_dev_dump_entry(netdev, skb, cb, &n_idx);
+			if (err == -EMSGSIZE)
+				goto out;
+			n_idx = 0;
 			if (err < 0)
 				break;
 cont:
@@ -135,6 +294,7 @@  int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 		}
 	}
 
+out:
 	rtnl_unlock();
 
 	if (err != -EMSGSIZE)
@@ -142,6 +302,7 @@  int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 
 	ctx->dev_entry_idx = idx;
 	ctx->dev_entry_hash = h;
+	ctx->napi_idx = n_idx;
 	cb->seq = net->dev_base_seq;
 
 	return skb->len;
diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h
index 639524b59930..16538fb1406a 100644
--- a/tools/include/uapi/linux/netdev.h
+++ b/tools/include/uapi/linux/netdev.h
@@ -41,6 +41,10 @@  enum {
 	NETDEV_A_DEV_IFINDEX = 1,
 	NETDEV_A_DEV_PAD,
 	NETDEV_A_DEV_XDP_FEATURES,
+	NETDEV_A_DEV_NAPI_INFO,
+	NETDEV_A_DEV_NAPI_ID,
+	NETDEV_A_DEV_RX_QUEUES,
+	NETDEV_A_DEV_TX_QUEUES,
 
 	__NETDEV_A_DEV_MAX,
 	NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)