netdev-genl: Add netlink framework functions for queue
authorAmritha Nambiar <amritha.nambiar@intel.com>
Fri, 1 Dec 2023 23:28:46 +0000 (15:28 -0800)
committerJakub Kicinski <kuba@kernel.org>
Tue, 5 Dec 2023 02:04:05 +0000 (18:04 -0800)
Implement the netdev netlink framework functions for
exposing queue information.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Reviewed-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
Link: https://lore.kernel.org/r/170147332603.5260.7982559672617639065.stgit@anambiarhost.jf.intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/core/netdev-genl.c

index 35e2d692f6515b2ece65490a5dc7d7f4ef5bd88b..3bc1661e6ebfc835ee18ec39e77069dfb2174d11 100644 (file)
@@ -7,9 +7,23 @@
 #include <net/sock.h>
 #include <net/xdp.h>
 #include <net/xdp_sock.h>
+#include <net/netdev_rx_queue.h>
 
 #include "netdev-genl-gen.h"
 
+struct netdev_nl_dump_ctx {
+       unsigned long   ifindex;
+       unsigned int    rxq_idx;
+       unsigned int    txq_idx;
+};
+
+static struct netdev_nl_dump_ctx *netdev_dump_ctx(struct netlink_callback *cb)
+{
+       NL_ASSERT_DUMP_CTX_FITS(struct netdev_nl_dump_ctx);
+
+       return (struct netdev_nl_dump_ctx *)cb->ctx;
+}
+
 static int
 netdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp,
                   const struct genl_info *info)
@@ -122,12 +136,13 @@ err_free_msg:
 
 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 err = 0;
 
        rtnl_lock();
-       for_each_netdev_dump(net, netdev, cb->args[0]) {
+       for_each_netdev_dump(net, netdev, ctx->ifindex) {
                err = netdev_nl_dev_fill(netdev, skb, genl_info_dump(cb));
                if (err < 0)
                        break;
@@ -140,14 +155,180 @@ int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
+static int
+netdev_nl_queue_fill_one(struct sk_buff *rsp, struct net_device *netdev,
+                        u32 q_idx, u32 q_type, const struct genl_info *info)
+{
+       struct netdev_rx_queue *rxq;
+       struct netdev_queue *txq;
+       void *hdr;
+
+       hdr = genlmsg_iput(rsp, info);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       if (nla_put_u32(rsp, NETDEV_A_QUEUE_ID, q_idx) ||
+           nla_put_u32(rsp, NETDEV_A_QUEUE_TYPE, q_type) ||
+           nla_put_u32(rsp, NETDEV_A_QUEUE_IFINDEX, netdev->ifindex))
+               goto nla_put_failure;
+
+       switch (q_type) {
+       case NETDEV_QUEUE_TYPE_RX:
+               rxq = __netif_get_rx_queue(netdev, q_idx);
+               if (rxq->napi && nla_put_u32(rsp, NETDEV_A_QUEUE_NAPI_ID,
+                                            rxq->napi->napi_id))
+                       goto nla_put_failure;
+               break;
+       case NETDEV_QUEUE_TYPE_TX:
+               txq = netdev_get_tx_queue(netdev, q_idx);
+               if (txq->napi && nla_put_u32(rsp, NETDEV_A_QUEUE_NAPI_ID,
+                                            txq->napi->napi_id))
+                       goto nla_put_failure;
+       }
+
+       genlmsg_end(rsp, hdr);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(rsp, hdr);
+       return -EMSGSIZE;
+}
+
+static int netdev_nl_queue_validate(struct net_device *netdev, u32 q_id,
+                                   u32 q_type)
+{
+       switch (q_type) {
+       case NETDEV_QUEUE_TYPE_RX:
+               if (q_id >= netdev->real_num_rx_queues)
+                       return -EINVAL;
+               return 0;
+       case NETDEV_QUEUE_TYPE_TX:
+               if (q_id >= netdev->real_num_tx_queues)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+netdev_nl_queue_fill(struct sk_buff *rsp, struct net_device *netdev, u32 q_idx,
+                    u32 q_type, const struct genl_info *info)
+{
+       int err = 0;
+
+       if (!(netdev->flags & IFF_UP))
+               return err;
+
+       err = netdev_nl_queue_validate(netdev, q_idx, q_type);
+       if (err)
+               return err;
+
+       return netdev_nl_queue_fill_one(rsp, netdev, q_idx, q_type, info);
+}
+
 int netdev_nl_queue_get_doit(struct sk_buff *skb, struct genl_info *info)
 {
-       return -EOPNOTSUPP;
+       u32 q_id, q_type, ifindex;
+       struct net_device *netdev;
+       struct sk_buff *rsp;
+       int err;
+
+       if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_QUEUE_ID) ||
+           GENL_REQ_ATTR_CHECK(info, NETDEV_A_QUEUE_TYPE) ||
+           GENL_REQ_ATTR_CHECK(info, NETDEV_A_QUEUE_IFINDEX))
+               return -EINVAL;
+
+       q_id = nla_get_u32(info->attrs[NETDEV_A_QUEUE_ID]);
+       q_type = nla_get_u32(info->attrs[NETDEV_A_QUEUE_TYPE]);
+       ifindex = nla_get_u32(info->attrs[NETDEV_A_QUEUE_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_queue_fill(rsp, netdev, q_id, q_type, info);
+       else
+               err = -ENODEV;
+
+       rtnl_unlock();
+
+       if (err)
+               goto err_free_msg;
+
+       return genlmsg_reply(rsp, info);
+
+err_free_msg:
+       nlmsg_free(rsp);
+       return err;
+}
+
+static int
+netdev_nl_queue_dump_one(struct net_device *netdev, struct sk_buff *rsp,
+                        const struct genl_info *info,
+                        struct netdev_nl_dump_ctx *ctx)
+{
+       int err = 0;
+       int i;
+
+       if (!(netdev->flags & IFF_UP))
+               return err;
+
+       for (i = ctx->rxq_idx; i < netdev->real_num_rx_queues;) {
+               err = netdev_nl_queue_fill_one(rsp, netdev, i,
+                                              NETDEV_QUEUE_TYPE_RX, info);
+               if (err)
+                       return err;
+               ctx->rxq_idx = i++;
+       }
+       for (i = ctx->txq_idx; i < netdev->real_num_tx_queues;) {
+               err = netdev_nl_queue_fill_one(rsp, netdev, i,
+                                              NETDEV_QUEUE_TYPE_TX, info);
+               if (err)
+                       return err;
+               ctx->txq_idx = i++;
+       }
+
+       return err;
 }
 
 int netdev_nl_queue_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       return -EOPNOTSUPP;
+       struct netdev_nl_dump_ctx *ctx = netdev_dump_ctx(cb);
+       const struct genl_info *info = genl_info_dump(cb);
+       struct net *net = sock_net(skb->sk);
+       struct net_device *netdev;
+       u32 ifindex = 0;
+       int err = 0;
+
+       if (info->attrs[NETDEV_A_QUEUE_IFINDEX])
+               ifindex = nla_get_u32(info->attrs[NETDEV_A_QUEUE_IFINDEX]);
+
+       rtnl_lock();
+       if (ifindex) {
+               netdev = __dev_get_by_index(net, ifindex);
+               if (netdev)
+                       err = netdev_nl_queue_dump_one(netdev, skb, info, ctx);
+               else
+                       err = -ENODEV;
+       } else {
+               for_each_netdev_dump(net, netdev, ctx->ifindex) {
+                       err = netdev_nl_queue_dump_one(netdev, skb, info, ctx);
+                       if (err < 0)
+                               break;
+                       ctx->rxq_idx = 0;
+                       ctx->txq_idx = 0;
+               }
+       }
+       rtnl_unlock();
+
+       if (err != -EMSGSIZE)
+               return err;
+
+       return skb->len;
 }
 
 static int netdev_genl_netdevice_event(struct notifier_block *nb,