diff mbox series

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

Message ID 169266032552.10199.11622842596696957776.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: 5466 this patch: 5466
netdev/cc_maintainers warning 7 maintainers not CCed: lorenzo@kernel.org willemb@google.com sdf@google.com tariqt@nvidia.com pabeni@redhat.com ast@kernel.org edumazet@google.com
netdev/build_clang success Errors and warnings before: 2249 this patch: 2249
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: 5706 this patch: 5706
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 81 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 Aug. 21, 2023, 11:25 p.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 |   42 ++++++++
 include/uapi/linux/netdev.h             |   11 ++
 net/core/netdev-genl-gen.c              |   24 +++++
 net/core/netdev-genl-gen.h              |    2 
 net/core/netdev-genl.c                  |   10 ++
 tools/include/uapi/linux/netdev.h       |   11 ++
 tools/net/ynl/generated/netdev-user.c   |  165 +++++++++++++++++++++++++++++++
 tools/net/ynl/generated/netdev-user.h   |   79 +++++++++++++++
 8 files changed, 344 insertions(+)

Comments

Jakub Kicinski Aug. 23, 2023, 12:39 a.m. UTC | #1
On Mon, 21 Aug 2023 16:25:25 -0700 Amritha Nambiar wrote:
> +      -
> +        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

Queues should be separate objects, with NAPI ID as their attr.
That's much simpler - since the relation is 1:n it's easier 
to store it on the side of the "1".
Nambiar, Amritha Aug. 24, 2023, 12:46 a.m. UTC | #2
On 8/22/2023 5:39 PM, Jakub Kicinski wrote:
> On Mon, 21 Aug 2023 16:25:25 -0700 Amritha Nambiar wrote:
>> +      -
>> +        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
> 
> Queues should be separate objects, with NAPI ID as their attr.
> That's much simpler - since the relation is 1:n it's easier
> to store it on the side of the "1".
> 

For the "napi-get" command to generate an output as below:

{'napi-id': 385, 'ifindex': 12, 'irq': 291, 'pid': 3614, 'rx-queues': 
[0,1,2], 'tx-queues': [0,1,2]},

wouldn't the napi attribute-set need an attribute for the list of queue 
IDs for the display ?

Internally, I can change the implementation in patch 1/9 to what we 
discussed previously (remove maintaining the napi_rxq/txq_list within 
napi, make use of the NAPI pointer in queue struct that stores the state 
on the side of "1" for 1:n. This would although change the complexity to 
#napis * (#max_rx + #max_tx). Currently it is #napis * 
(len(napi_rxq_list) + len(napi_txq_list)).

But, to display the list of queue IDs for the NAPI with napi-get, 
wouldn't the YAML still need such an attribute within the NAPI object? 
Or are you suggesting that the "napi-get" command does not need to show 
the queue-list associated with NAPI and a "queue-get" command which 
shows the NAPI_ID associated with the queue would suffice ?

Can we have both: "queue-get" (queue object with with NAPI-ID attr) and 
"napi-get" (NAPI object with "set of queue IDs" as an attribute), 
something like below (rx-queue is the queue object. rx-queues is the 
list attr within the NAPI object):

     name: rx-queue
     attributes:
       -
         name: qid
         doc: queue ID
         type: u32
       -
         name: ifindex
         doc: netdev ifindex
         type: u32
         checks:
           min: 1
       -
         name: napi-id
         doc: napi id
         type: u32

     name: napi
     attributes:
       -
         name: ifindex
         doc: netdev ifindex
         type: u32
         checks:
           min: 1
       -
         name: napi-id
         doc: napi id
         type: u32
       -
         name: rx-queues
         doc: list of rx queue ids associated with a napi
         type: u32
         multi-attr: true
       -
         name: tx-queues
         doc: list of tx queue ids associated with a napi
         type: u32
         multi-attr: true
Jakub Kicinski Aug. 24, 2023, 1:34 a.m. UTC | #3
On Wed, 23 Aug 2023 17:46:20 -0700 Nambiar, Amritha wrote:
> Can we have both: "queue-get" (queue object with with NAPI-ID attr) and 
> "napi-get" (NAPI object with "set of queue IDs" as an attribute), 
> something like below (rx-queue is the queue object. rx-queues is the 
> list attr within the NAPI object):

I think just queue-get is better, the IDs of queues are not unique.
It's more of an index within a group than and identifier. 
Displaying information about e.g. XDP TX queues within would get messy.
Nambiar, Amritha Aug. 24, 2023, 10:26 p.m. UTC | #4
On 8/23/2023 6:34 PM, Jakub Kicinski wrote:
> On Wed, 23 Aug 2023 17:46:20 -0700 Nambiar, Amritha wrote:
>> Can we have both: "queue-get" (queue object with with NAPI-ID attr) and
>> "napi-get" (NAPI object with "set of queue IDs" as an attribute),
>> something like below (rx-queue is the queue object. rx-queues is the
>> list attr within the NAPI object):
> 
> I think just queue-get is better, the IDs of queues are not unique.
> It's more of an index within a group than and identifier.
> Displaying information about e.g. XDP TX queues within would get messy.

Okay. So, I think we can support napi-get and queue-get commands as below:

--do napi-get --json='{"napi-id": 385}'
{'napi-id': 385, 'ifindex': 12, 'irq': 291, 'pid': 3614}

--do queue-get --json='{"q_index": 0, "q_type": RX}'
{'q_index': 0, 'q_type': RX, 'ifindex': 12, 'napi-id': 385}

As for queue-get, should we have a single queue object with a 'type' 
attribute for RX, TX, XDP etc. or should each of these queue types have 
their own distinct queue objects as they could have different attributes 
within.
Jakub Kicinski Aug. 24, 2023, 11:55 p.m. UTC | #5
On Thu, 24 Aug 2023 15:26:37 -0700 Nambiar, Amritha wrote:
> --do queue-get --json='{"q_index": 0, "q_type": RX}'
> {'q_index': 0, 'q_type': RX, 'ifindex': 12, 'napi-id': 385}
> 
> As for queue-get, should we have a single queue object with a 'type' 
> attribute for RX, TX, XDP etc. or should each of these queue types have 
> their own distinct queue objects as they could have different attributes 
> within.

Separate objects, I think. 
The "key" for queues is a tuple of <type, id>.

e.g.

 <Rx, 0>
 <Tx, 0>
 <XDP_Tx, 0>

are 3 different objects (feel free to start with just rx and tx, 
we can add support for xdp queues later as needed).
diff mbox series

Patch

diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
index 1c7284fd535b..a068cf3b5a7e 100644
--- a/Documentation/netlink/specs/netdev.yaml
+++ b/Documentation/netlink/specs/netdev.yaml
@@ -68,6 +68,29 @@  attribute-sets:
         type: u32
         checks:
           min: 1
+  -
+    name: napi
+    attributes:
+      -
+        name: ifindex
+        doc: netdev ifindex
+        type: u32
+        checks:
+          min: 1
+      -
+        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
 
 operations:
   list:
@@ -101,6 +124,25 @@  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:
+            - napi-id
+        reply: &napi-get-op
+          attributes:
+            - napi-id
+            - ifindex
+            - rx-queues
+            - tx-queues
+      dump:
+        request:
+          attributes:
+            - ifindex
+        reply: *napi-get-op
 
 mcast-groups:
   list:
diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h
index c1634b95c223..28f6bad7c48e 100644
--- a/include/uapi/linux/netdev.h
+++ b/include/uapi/linux/netdev.h
@@ -48,11 +48,22 @@  enum {
 	NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)
 };
 
+enum {
+	NETDEV_A_NAPI_IFINDEX = 1,
+	NETDEV_A_NAPI_NAPI_ID,
+	NETDEV_A_NAPI_RX_QUEUES,
+	NETDEV_A_NAPI_TX_QUEUES,
+
+	__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..37cce2d64a14 100644
--- a/net/core/netdev-genl-gen.c
+++ b/net/core/netdev-genl-gen.c
@@ -15,6 +15,16 @@  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_do_nl_policy[NETDEV_A_NAPI_NAPI_ID + 1] = {
+	[NETDEV_A_NAPI_NAPI_ID] = { .type = NLA_U32, },
+};
+
+/* NETDEV_CMD_NAPI_GET - dump */
+static const struct nla_policy netdev_napi_get_dump_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 +39,20 @@  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_do_nl_policy,
+		.maxattr	= NETDEV_A_NAPI_NAPI_ID,
+		.flags		= GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= NETDEV_CMD_NAPI_GET,
+		.dumpit		= netdev_nl_napi_get_dumpit,
+		.policy		= netdev_napi_get_dump_nl_policy,
+		.maxattr	= NETDEV_A_NAPI_IFINDEX,
+		.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 c1aea8b756b6..b146679d6112 100644
--- a/net/core/netdev-genl.c
+++ b/net/core/netdev-genl.c
@@ -119,6 +119,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..28f6bad7c48e 100644
--- a/tools/include/uapi/linux/netdev.h
+++ b/tools/include/uapi/linux/netdev.h
@@ -48,11 +48,22 @@  enum {
 	NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1)
 };
 
+enum {
+	NETDEV_A_NAPI_IFINDEX = 1,
+	NETDEV_A_NAPI_NAPI_ID,
+	NETDEV_A_NAPI_RX_QUEUES,
+	NETDEV_A_NAPI_TX_QUEUES,
+
+	__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..0cc8c0151b36 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)
@@ -58,6 +59,18 @@  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_ID] = { .name = "napi-id", .type = YNL_PT_U32, },
+	[NETDEV_A_NAPI_RX_QUEUES] = { .name = "rx-queues", .type = YNL_PT_U32, },
+	[NETDEV_A_NAPI_TX_QUEUES] = { .name = "tx-queues", .type = YNL_PT_U32, },
+};
+
+struct ynl_policy_nest netdev_napi_nest = {
+	.max_attr = NETDEV_A_NAPI_MAX,
+	.table = netdev_napi_policy,
+};
+
 /* Common nested types */
 /* ============== NETDEV_CMD_DEV_GET ============== */
 /* NETDEV_CMD_DEV_GET - do */
@@ -178,6 +191,158 @@  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)
+{
+	free(rsp->rx_queues);
+	free(rsp->tx_queues);
+	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_rx_queues = 0;
+	unsigned int n_tx_queues = 0;
+	const struct nlattr *attr;
+	int i;
+
+	dst = yarg->data;
+
+	if (dst->rx_queues)
+		return ynl_error_parse(yarg, "attribute already present (napi.rx-queues)");
+	if (dst->tx_queues)
+		return ynl_error_parse(yarg, "attribute already present (napi.tx-queues)");
+
+	mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
+		unsigned int type = mnl_attr_get_type(attr);
+
+		if (type == NETDEV_A_NAPI_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_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_RX_QUEUES) {
+			n_rx_queues++;
+		} else if (type == NETDEV_A_NAPI_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(attr, nlh, sizeof(struct genlmsghdr)) {
+			if (mnl_attr_get_type(attr) == NETDEV_A_NAPI_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(attr, nlh, sizeof(struct genlmsghdr)) {
+			if (mnl_attr_get_type(attr) == NETDEV_A_NAPI_TX_QUEUES) {
+				dst->tx_queues[i] = mnl_attr_get_u32(attr);
+				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.napi_id)
+		mnl_attr_put_u32(nlh, NETDEV_A_NAPI_NAPI_ID, req->napi_id);
+
+	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) {
+		rsp = next;
+		next = rsp->next;
+
+		free(rsp->obj.rx_queues);
+		free(rsp->obj.tx_queues);
+		free(rsp);
+	}
+}
+
+struct netdev_napi_get_list *
+netdev_napi_get_dump(struct ynl_sock *ys, struct netdev_napi_get_req_dump *req)
+{
+	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);
+	ys->req_policy = &netdev_napi_nest;
+
+	if (req->_present.ifindex)
+		mnl_attr_put_u32(nlh, NETDEV_A_NAPI_IFINDEX, req->ifindex);
+
+	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..742cb37b6844 100644
--- a/tools/net/ynl/generated/netdev-user.h
+++ b/tools/net/ynl/generated/netdev-user.h
@@ -84,4 +84,83 @@  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 napi_id:1;
+	} _present;
+
+	__u32 napi_id;
+};
+
+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_napi_id(struct netdev_napi_get_req *req, __u32 napi_id)
+{
+	req->_present.napi_id = 1;
+	req->napi_id = napi_id;
+}
+
+struct netdev_napi_get_rsp {
+	struct {
+		__u32 napi_id:1;
+		__u32 ifindex:1;
+	} _present;
+
+	__u32 napi_id;
+	__u32 ifindex;
+	unsigned int n_rx_queues;
+	__u32 *rx_queues;
+	unsigned int n_tx_queues;
+	__u32 *tx_queues;
+};
+
+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_req_dump {
+	struct {
+		__u32 ifindex:1;
+	} _present;
+
+	__u32 ifindex;
+};
+
+static inline struct netdev_napi_get_req_dump *
+netdev_napi_get_req_dump_alloc(void)
+{
+	return calloc(1, sizeof(struct netdev_napi_get_req_dump));
+}
+void netdev_napi_get_req_dump_free(struct netdev_napi_get_req_dump *req);
+
+static inline void
+netdev_napi_get_req_dump_set_ifindex(struct netdev_napi_get_req_dump *req,
+				     __u32 ifindex)
+{
+	req->_present.ifindex = 1;
+	req->ifindex = ifindex;
+}
+
+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, struct netdev_napi_get_req_dump *req);
+
 #endif /* _LINUX_NETDEV_GEN_H */