netdev-genl: spec: Extend netdev netlink spec in YAML for queue
authorAmritha Nambiar <amritha.nambiar@intel.com>
Fri, 1 Dec 2023 23:28:29 +0000 (15:28 -0800)
committerJakub Kicinski <kuba@kernel.org>
Tue, 5 Dec 2023 02:04:05 +0000 (18:04 -0800)
Add support in netlink spec(netdev.yaml) for queue information.
Add code generated from the spec.

Note: The "queue-type" attribute takes values 0 and 1 for rx
and tx queue type respectively.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Reviewed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Link: https://lore.kernel.org/r/170147330963.5260.2576294626647300472.stgit@anambiarhost.jf.intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/netlink/specs/netdev.yaml
include/uapi/linux/netdev.h
net/core/netdev-genl-gen.c
net/core/netdev-genl-gen.h
net/core/netdev-genl.c
tools/include/uapi/linux/netdev.h
tools/net/ynl/generated/netdev-user.c
tools/net/ynl/generated/netdev-user.h

index eef6358ec587da04f3195e550e853fc712f29b5a..719e6aafbfdba9d3501b96abf74b82e19d07d51e 100644 (file)
@@ -66,6 +66,10 @@ definitions:
         name: tx-checksum
         doc:
           L3 checksum HW offload is supported by the driver.
+  -
+    name: queue-type
+    type: enum
+    entries: [ rx, tx ]
 
 attribute-sets:
   -
@@ -209,6 +213,31 @@ attribute-sets:
         name: recycle-released-refcnt
         type: uint
 
+  -
+    name: queue
+    attributes:
+      -
+        name: id
+        doc: Queue index; most queue types are indexed like a C array, with
+             indexes starting at 0 and ending at queue count - 1. Queue indexes
+             are scoped to an interface and queue type.
+        type: u32
+      -
+        name: ifindex
+        doc: ifindex of the netdevice to which the queue belongs.
+        type: u32
+        checks:
+          min: 1
+      -
+        name: type
+        doc: Queue type as rx, tx. Each queue type defines a separate ID space.
+        type: u32
+        enum: queue-type
+      -
+        name: napi-id
+        doc: ID of the NAPI instance which services this queue.
+        type: u32
+
 operations:
   list:
     -
@@ -307,6 +336,29 @@ operations:
       dump:
         reply: *pp-stats-reply
       config-cond: page-pool-stats
+    -
+      name: queue-get
+      doc: Get queue information from the kernel.
+           Only configured queues will be reported (as opposed to all available
+           hardware queues).
+      attribute-set: queue
+      do:
+        request:
+          attributes:
+            - ifindex
+            - type
+            - id
+        reply: &queue-get-op
+          attributes:
+            - id
+            - type
+            - napi-id
+            - ifindex
+      dump:
+        request:
+          attributes:
+            - ifindex
+        reply: *queue-get-op
 
 mcast-groups:
   list:
index 6244c0164976a28f7e31577a54d97aebaaf45c30..f857960a7f064aea11d5fc4ff7fb716a79b9f33f 100644 (file)
@@ -62,6 +62,11 @@ enum netdev_xsk_flags {
        NETDEV_XSK_FLAGS_TX_CHECKSUM = 2,
 };
 
+enum netdev_queue_type {
+       NETDEV_QUEUE_TYPE_RX,
+       NETDEV_QUEUE_TYPE_TX,
+};
+
 enum {
        NETDEV_A_DEV_IFINDEX = 1,
        NETDEV_A_DEV_PAD,
@@ -104,6 +109,16 @@ enum {
        NETDEV_A_PAGE_POOL_STATS_MAX = (__NETDEV_A_PAGE_POOL_STATS_MAX - 1)
 };
 
+enum {
+       NETDEV_A_QUEUE_ID = 1,
+       NETDEV_A_QUEUE_IFINDEX,
+       NETDEV_A_QUEUE_TYPE,
+       NETDEV_A_QUEUE_NAPI_ID,
+
+       __NETDEV_A_QUEUE_MAX,
+       NETDEV_A_QUEUE_MAX = (__NETDEV_A_QUEUE_MAX - 1)
+};
+
 enum {
        NETDEV_CMD_DEV_GET = 1,
        NETDEV_CMD_DEV_ADD_NTF,
@@ -114,6 +129,7 @@ enum {
        NETDEV_CMD_PAGE_POOL_DEL_NTF,
        NETDEV_CMD_PAGE_POOL_CHANGE_NTF,
        NETDEV_CMD_PAGE_POOL_STATS_GET,
+       NETDEV_CMD_QUEUE_GET,
 
        __NETDEV_CMD_MAX,
        NETDEV_CMD_MAX = (__NETDEV_CMD_MAX - 1)
index dccd8c3a141e995eb4e2eedfda174ad53d8edc96..b1dcf88c82cf9f3231bf943496bc5f29da966262 100644 (file)
@@ -46,6 +46,18 @@ static const struct nla_policy netdev_page_pool_stats_get_nl_policy[NETDEV_A_PAG
 };
 #endif /* CONFIG_PAGE_POOL_STATS */
 
+/* NETDEV_CMD_QUEUE_GET - do */
+static const struct nla_policy netdev_queue_get_do_nl_policy[NETDEV_A_QUEUE_TYPE + 1] = {
+       [NETDEV_A_QUEUE_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),
+       [NETDEV_A_QUEUE_TYPE] = NLA_POLICY_MAX(NLA_U32, 1),
+       [NETDEV_A_QUEUE_ID] = { .type = NLA_U32, },
+};
+
+/* NETDEV_CMD_QUEUE_GET - dump */
+static const struct nla_policy netdev_queue_get_dump_nl_policy[NETDEV_A_QUEUE_IFINDEX + 1] = {
+       [NETDEV_A_QUEUE_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),
+};
+
 /* Ops table for netdev */
 static const struct genl_split_ops netdev_nl_ops[] = {
        {
@@ -88,6 +100,20 @@ static const struct genl_split_ops netdev_nl_ops[] = {
                .flags  = GENL_CMD_CAP_DUMP,
        },
 #endif /* CONFIG_PAGE_POOL_STATS */
+       {
+               .cmd            = NETDEV_CMD_QUEUE_GET,
+               .doit           = netdev_nl_queue_get_doit,
+               .policy         = netdev_queue_get_do_nl_policy,
+               .maxattr        = NETDEV_A_QUEUE_TYPE,
+               .flags          = GENL_CMD_CAP_DO,
+       },
+       {
+               .cmd            = NETDEV_CMD_QUEUE_GET,
+               .dumpit         = netdev_nl_queue_get_dumpit,
+               .policy         = netdev_queue_get_dump_nl_policy,
+               .maxattr        = NETDEV_A_QUEUE_IFINDEX,
+               .flags          = GENL_CMD_CAP_DUMP,
+       },
 };
 
 static const struct genl_multicast_group netdev_nl_mcgrps[] = {
index 649e4b46eccf837d71dab9292aac49d1c318b9ce..086623c1797a56e1ef782186d3a191858420ad73 100644 (file)
@@ -23,6 +23,9 @@ 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);
+int netdev_nl_queue_get_doit(struct sk_buff *skb, struct genl_info *info);
+int netdev_nl_queue_get_dumpit(struct sk_buff *skb,
+                              struct netlink_callback *cb);
 
 enum {
        NETDEV_NLGRP_MGMT,
index 10f2124e9e23ec39948938255e991b60beb5f667..35e2d692f6515b2ece65490a5dc7d7f4ef5bd88b 100644 (file)
@@ -140,6 +140,16 @@ int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
+int netdev_nl_queue_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+       return -EOPNOTSUPP;
+}
+
+int netdev_nl_queue_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)
 {
index 6244c0164976a28f7e31577a54d97aebaaf45c30..f857960a7f064aea11d5fc4ff7fb716a79b9f33f 100644 (file)
@@ -62,6 +62,11 @@ enum netdev_xsk_flags {
        NETDEV_XSK_FLAGS_TX_CHECKSUM = 2,
 };
 
+enum netdev_queue_type {
+       NETDEV_QUEUE_TYPE_RX,
+       NETDEV_QUEUE_TYPE_TX,
+};
+
 enum {
        NETDEV_A_DEV_IFINDEX = 1,
        NETDEV_A_DEV_PAD,
@@ -104,6 +109,16 @@ enum {
        NETDEV_A_PAGE_POOL_STATS_MAX = (__NETDEV_A_PAGE_POOL_STATS_MAX - 1)
 };
 
+enum {
+       NETDEV_A_QUEUE_ID = 1,
+       NETDEV_A_QUEUE_IFINDEX,
+       NETDEV_A_QUEUE_TYPE,
+       NETDEV_A_QUEUE_NAPI_ID,
+
+       __NETDEV_A_QUEUE_MAX,
+       NETDEV_A_QUEUE_MAX = (__NETDEV_A_QUEUE_MAX - 1)
+};
+
 enum {
        NETDEV_CMD_DEV_GET = 1,
        NETDEV_CMD_DEV_ADD_NTF,
@@ -114,6 +129,7 @@ enum {
        NETDEV_CMD_PAGE_POOL_DEL_NTF,
        NETDEV_CMD_PAGE_POOL_CHANGE_NTF,
        NETDEV_CMD_PAGE_POOL_STATS_GET,
+       NETDEV_CMD_QUEUE_GET,
 
        __NETDEV_CMD_MAX,
        NETDEV_CMD_MAX = (__NETDEV_CMD_MAX - 1)
index 3b9dee94d4ce48d4d76d70d9f94e2cdab9a6d092..fbf7e24ade91d781f43782e4ee46ed042bf4d45d 100644 (file)
@@ -23,6 +23,7 @@ static const char * const netdev_op_strmap[] = {
        [NETDEV_CMD_PAGE_POOL_DEL_NTF] = "page-pool-del-ntf",
        [NETDEV_CMD_PAGE_POOL_CHANGE_NTF] = "page-pool-change-ntf",
        [NETDEV_CMD_PAGE_POOL_STATS_GET] = "page-pool-stats-get",
+       [NETDEV_CMD_QUEUE_GET] = "queue-get",
 };
 
 const char *netdev_op_str(int op)
@@ -76,6 +77,18 @@ const char *netdev_xsk_flags_str(enum netdev_xsk_flags value)
        return netdev_xsk_flags_strmap[value];
 }
 
+static const char * const netdev_queue_type_strmap[] = {
+       [0] = "rx",
+       [1] = "tx",
+};
+
+const char *netdev_queue_type_str(enum netdev_queue_type value)
+{
+       if (value < 0 || value >= (int)MNL_ARRAY_SIZE(netdev_queue_type_strmap))
+               return NULL;
+       return netdev_queue_type_strmap[value];
+}
+
 /* Policies */
 struct ynl_policy_attr netdev_page_pool_info_policy[NETDEV_A_PAGE_POOL_MAX + 1] = {
        [NETDEV_A_PAGE_POOL_ID] = { .name = "id", .type = YNL_PT_UINT, },
@@ -135,6 +148,18 @@ struct ynl_policy_nest netdev_page_pool_stats_nest = {
        .table = netdev_page_pool_stats_policy,
 };
 
+struct ynl_policy_attr netdev_queue_policy[NETDEV_A_QUEUE_MAX + 1] = {
+       [NETDEV_A_QUEUE_ID] = { .name = "id", .type = YNL_PT_U32, },
+       [NETDEV_A_QUEUE_IFINDEX] = { .name = "ifindex", .type = YNL_PT_U32, },
+       [NETDEV_A_QUEUE_TYPE] = { .name = "type", .type = YNL_PT_U32, },
+       [NETDEV_A_QUEUE_NAPI_ID] = { .name = "napi-id", .type = YNL_PT_U32, },
+};
+
+struct ynl_policy_nest netdev_queue_nest = {
+       .max_attr = NETDEV_A_QUEUE_MAX,
+       .table = netdev_queue_policy,
+};
+
 /* Common nested types */
 void netdev_page_pool_info_free(struct netdev_page_pool_info *obj)
 {
@@ -617,6 +642,134 @@ free_list:
        return NULL;
 }
 
+/* ============== NETDEV_CMD_QUEUE_GET ============== */
+/* NETDEV_CMD_QUEUE_GET - do */
+void netdev_queue_get_req_free(struct netdev_queue_get_req *req)
+{
+       free(req);
+}
+
+void netdev_queue_get_rsp_free(struct netdev_queue_get_rsp *rsp)
+{
+       free(rsp);
+}
+
+int netdev_queue_get_rsp_parse(const struct nlmsghdr *nlh, void *data)
+{
+       struct ynl_parse_arg *yarg = data;
+       struct netdev_queue_get_rsp *dst;
+       const struct nlattr *attr;
+
+       dst = yarg->data;
+
+       mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
+               unsigned int type = mnl_attr_get_type(attr);
+
+               if (type == NETDEV_A_QUEUE_ID) {
+                       if (ynl_attr_validate(yarg, attr))
+                               return MNL_CB_ERROR;
+                       dst->_present.id = 1;
+                       dst->id = mnl_attr_get_u32(attr);
+               } else if (type == NETDEV_A_QUEUE_TYPE) {
+                       if (ynl_attr_validate(yarg, attr))
+                               return MNL_CB_ERROR;
+                       dst->_present.type = 1;
+                       dst->type = mnl_attr_get_u32(attr);
+               } else if (type == NETDEV_A_QUEUE_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_QUEUE_IFINDEX) {
+                       if (ynl_attr_validate(yarg, attr))
+                               return MNL_CB_ERROR;
+                       dst->_present.ifindex = 1;
+                       dst->ifindex = mnl_attr_get_u32(attr);
+               }
+       }
+
+       return MNL_CB_OK;
+}
+
+struct netdev_queue_get_rsp *
+netdev_queue_get(struct ynl_sock *ys, struct netdev_queue_get_req *req)
+{
+       struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };
+       struct netdev_queue_get_rsp *rsp;
+       struct nlmsghdr *nlh;
+       int err;
+
+       nlh = ynl_gemsg_start_req(ys, ys->family_id, NETDEV_CMD_QUEUE_GET, 1);
+       ys->req_policy = &netdev_queue_nest;
+       yrs.yarg.rsp_policy = &netdev_queue_nest;
+
+       if (req->_present.ifindex)
+               mnl_attr_put_u32(nlh, NETDEV_A_QUEUE_IFINDEX, req->ifindex);
+       if (req->_present.type)
+               mnl_attr_put_u32(nlh, NETDEV_A_QUEUE_TYPE, req->type);
+       if (req->_present.id)
+               mnl_attr_put_u32(nlh, NETDEV_A_QUEUE_ID, req->id);
+
+       rsp = calloc(1, sizeof(*rsp));
+       yrs.yarg.data = rsp;
+       yrs.cb = netdev_queue_get_rsp_parse;
+       yrs.rsp_cmd = NETDEV_CMD_QUEUE_GET;
+
+       err = ynl_exec(ys, nlh, &yrs);
+       if (err < 0)
+               goto err_free;
+
+       return rsp;
+
+err_free:
+       netdev_queue_get_rsp_free(rsp);
+       return NULL;
+}
+
+/* NETDEV_CMD_QUEUE_GET - dump */
+void netdev_queue_get_list_free(struct netdev_queue_get_list *rsp)
+{
+       struct netdev_queue_get_list *next = rsp;
+
+       while ((void *)next != YNL_LIST_END) {
+               rsp = next;
+               next = rsp->next;
+
+               free(rsp);
+       }
+}
+
+struct netdev_queue_get_list *
+netdev_queue_get_dump(struct ynl_sock *ys,
+                     struct netdev_queue_get_req_dump *req)
+{
+       struct ynl_dump_state yds = {};
+       struct nlmsghdr *nlh;
+       int err;
+
+       yds.ys = ys;
+       yds.alloc_sz = sizeof(struct netdev_queue_get_list);
+       yds.cb = netdev_queue_get_rsp_parse;
+       yds.rsp_cmd = NETDEV_CMD_QUEUE_GET;
+       yds.rsp_policy = &netdev_queue_nest;
+
+       nlh = ynl_gemsg_start_dump(ys, ys->family_id, NETDEV_CMD_QUEUE_GET, 1);
+       ys->req_policy = &netdev_queue_nest;
+
+       if (req->_present.ifindex)
+               mnl_attr_put_u32(nlh, NETDEV_A_QUEUE_IFINDEX, req->ifindex);
+
+       err = ynl_exec_dump(ys, nlh, &yds);
+       if (err < 0)
+               goto free_list;
+
+       return yds.first;
+
+free_list:
+       netdev_queue_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),
index cc3d80d1cf8cce210d573f8f82bed9156528f2c2..d7daf6df3df01c66543d40f3f5c1064bd14f8fd5 100644 (file)
@@ -20,6 +20,7 @@ const char *netdev_op_str(int op);
 const char *netdev_xdp_act_str(enum netdev_xdp_act value);
 const char *netdev_xdp_rx_metadata_str(enum netdev_xdp_rx_metadata value);
 const char *netdev_xsk_flags_str(enum netdev_xsk_flags value);
+const char *netdev_queue_type_str(enum netdev_queue_type value);
 
 /* Common nested types */
 struct netdev_page_pool_info {
@@ -261,4 +262,102 @@ netdev_page_pool_stats_get_list_free(struct netdev_page_pool_stats_get_list *rsp
 struct netdev_page_pool_stats_get_list *
 netdev_page_pool_stats_get_dump(struct ynl_sock *ys);
 
+/* ============== NETDEV_CMD_QUEUE_GET ============== */
+/* NETDEV_CMD_QUEUE_GET - do */
+struct netdev_queue_get_req {
+       struct {
+               __u32 ifindex:1;
+               __u32 type:1;
+               __u32 id:1;
+       } _present;
+
+       __u32 ifindex;
+       enum netdev_queue_type type;
+       __u32 id;
+};
+
+static inline struct netdev_queue_get_req *netdev_queue_get_req_alloc(void)
+{
+       return calloc(1, sizeof(struct netdev_queue_get_req));
+}
+void netdev_queue_get_req_free(struct netdev_queue_get_req *req);
+
+static inline void
+netdev_queue_get_req_set_ifindex(struct netdev_queue_get_req *req,
+                                __u32 ifindex)
+{
+       req->_present.ifindex = 1;
+       req->ifindex = ifindex;
+}
+static inline void
+netdev_queue_get_req_set_type(struct netdev_queue_get_req *req,
+                             enum netdev_queue_type type)
+{
+       req->_present.type = 1;
+       req->type = type;
+}
+static inline void
+netdev_queue_get_req_set_id(struct netdev_queue_get_req *req, __u32 id)
+{
+       req->_present.id = 1;
+       req->id = id;
+}
+
+struct netdev_queue_get_rsp {
+       struct {
+               __u32 id:1;
+               __u32 type:1;
+               __u32 napi_id:1;
+               __u32 ifindex:1;
+       } _present;
+
+       __u32 id;
+       enum netdev_queue_type type;
+       __u32 napi_id;
+       __u32 ifindex;
+};
+
+void netdev_queue_get_rsp_free(struct netdev_queue_get_rsp *rsp);
+
+/*
+ * Get queue information from the kernel. Only configured queues will be reported (as opposed to all available hardware queues).
+ */
+struct netdev_queue_get_rsp *
+netdev_queue_get(struct ynl_sock *ys, struct netdev_queue_get_req *req);
+
+/* NETDEV_CMD_QUEUE_GET - dump */
+struct netdev_queue_get_req_dump {
+       struct {
+               __u32 ifindex:1;
+       } _present;
+
+       __u32 ifindex;
+};
+
+static inline struct netdev_queue_get_req_dump *
+netdev_queue_get_req_dump_alloc(void)
+{
+       return calloc(1, sizeof(struct netdev_queue_get_req_dump));
+}
+void netdev_queue_get_req_dump_free(struct netdev_queue_get_req_dump *req);
+
+static inline void
+netdev_queue_get_req_dump_set_ifindex(struct netdev_queue_get_req_dump *req,
+                                     __u32 ifindex)
+{
+       req->_present.ifindex = 1;
+       req->ifindex = ifindex;
+}
+
+struct netdev_queue_get_list {
+       struct netdev_queue_get_list *next;
+       struct netdev_queue_get_rsp obj __attribute__((aligned(8)));
+};
+
+void netdev_queue_get_list_free(struct netdev_queue_get_list *rsp);
+
+struct netdev_queue_get_list *
+netdev_queue_get_dump(struct ynl_sock *ys,
+                     struct netdev_queue_get_req_dump *req);
+
 #endif /* _LINUX_NETDEV_GEN_H */