netdev-genl: create a simple family for netdev stuff
authorJakub Kicinski <kuba@kernel.org>
Wed, 1 Feb 2023 10:24:17 +0000 (11:24 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 3 Feb 2023 04:48:23 +0000 (20:48 -0800)
Add a Netlink spec-compatible family for netdevs.
This is a very simple implementation without much
thought going into it.

It allows us to reap all the benefits of Netlink specs,
one can use the generic client to issue the commands:

  $ ./cli.py --spec netdev.yaml --dump dev_get
  [{'ifindex': 1, 'xdp-features': set()},
   {'ifindex': 2, 'xdp-features': {'basic', 'ndo-xmit', 'redirect'}},
   {'ifindex': 3, 'xdp-features': {'rx-sg'}}]

the generic python library does not have flags-by-name
support, yet, but we also don't have to carry strings
in the messages, as user space can get the names from
the spec.

Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Co-developed-by: Marek Majtyka <alardam@gmail.com>
Signed-off-by: Marek Majtyka <alardam@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Link: https://lore.kernel.org/r/327ad9c9868becbe1e601b580c962549c8cd81f2.1675245258.git.lorenzo@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Documentation/netlink/specs/netdev.yaml [new file with mode: 0644]
include/linux/netdevice.h
include/net/xdp.h
include/uapi/linux/netdev.h [new file with mode: 0644]
net/core/Makefile
net/core/dev.c
net/core/netdev-genl-gen.c [new file with mode: 0644]
net/core/netdev-genl-gen.h [new file with mode: 0644]
net/core/netdev-genl.c [new file with mode: 0644]
tools/include/uapi/linux/netdev.h [new file with mode: 0644]

diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
new file mode 100644 (file)
index 0000000..b4dcdae
--- /dev/null
@@ -0,0 +1,100 @@
+name: netdev
+
+doc:
+  netdev configuration over generic netlink.
+
+definitions:
+  -
+    type: flags
+    name: xdp-act
+    entries:
+      -
+        name: basic
+        doc:
+          XDP feautues set supported by all drivers
+          (XDP_ABORTED, XDP_DROP, XDP_PASS, XDP_TX)
+      -
+        name: redirect
+        doc:
+          The netdev supports XDP_REDIRECT
+      -
+        name: ndo-xmit
+        doc:
+          This feature informs if netdev implements ndo_xdp_xmit callback.
+      -
+        name: xsk-zerocopy
+        doc:
+          This feature informs if netdev supports AF_XDP in zero copy mode.
+      -
+        name: hw-offload
+        doc:
+         This feature informs if netdev supports XDP hw oflloading.
+      -
+        name: rx-sg
+        doc:
+          This feature informs if netdev implements non-linear XDP buffer
+          support in the driver napi callback.
+      -
+        name: ndo-xmit-sg
+        doc:
+          This feature informs if netdev implements non-linear XDP buffer
+          support in ndo_xdp_xmit callback.
+
+attribute-sets:
+  -
+    name: dev
+    attributes:
+      -
+        name: ifindex
+        doc: netdev ifindex
+        type: u32
+        value: 1
+        checks:
+          min: 1
+      -
+        name: pad
+        type: pad
+      -
+        name: xdp-features
+        doc: Bitmask of enabled xdp-features.
+        type: u64
+        enum: xdp-act
+        enum-as-flags: true
+
+operations:
+  list:
+    -
+      name: dev-get
+      doc: Get / dump information about a netdev.
+      value: 1
+      attribute-set: dev
+      do:
+        request:
+          attributes:
+            - ifindex
+        reply: &dev-all
+          attributes:
+            - ifindex
+            - xdp-features
+      dump:
+        reply: *dev-all
+    -
+      name: dev-add-ntf
+      doc: Notification about device appearing.
+      notify: dev-get
+      mcgrp: mgmt
+    -
+      name: dev-del-ntf
+      doc: Notification about device disappearing.
+      notify: dev-get
+      mcgrp: mgmt
+    -
+      name: dev-change-ntf
+      doc: Notification about device configuration being changed.
+      notify: dev-get
+      mcgrp: mgmt
+
+mcast-groups:
+  list:
+    -
+      name: mgmt
index 2466afa25078186af815fd244fad1f7c8959d999..0f7967591288a568e6f2e35158a2c81c2d4668ac 100644 (file)
@@ -47,6 +47,7 @@
 #include <uapi/linux/netdevice.h>
 #include <uapi/linux/if_bonding.h>
 #include <uapi/linux/pkt_cls.h>
+#include <uapi/linux/netdev.h>
 #include <linux/hashtable.h>
 #include <linux/rbtree.h>
 #include <net/net_trackers.h>
@@ -2055,6 +2056,7 @@ struct net_device {
 
        /* Read-mostly cache-line for fast-path access */
        unsigned int            flags;
+       xdp_features_t          xdp_features;
        unsigned long long      priv_flags;
        const struct net_device_ops *netdev_ops;
        const struct xdp_metadata_ops *xdp_metadata_ops;
@@ -2839,6 +2841,7 @@ enum netdev_cmd {
        NETDEV_OFFLOAD_XSTATS_DISABLE,
        NETDEV_OFFLOAD_XSTATS_REPORT_USED,
        NETDEV_OFFLOAD_XSTATS_REPORT_DELTA,
+       NETDEV_XDP_FEAT_CHANGE,
 };
 const char *netdev_cmd_to_name(enum netdev_cmd cmd);
 
index 91292aa13bc06970a57fd58bf259fd22f4288ebe..8d1c86914f4c94394d3fd51a3aa3342e4b8635d5 100644 (file)
@@ -7,6 +7,7 @@
 #define __LINUX_NET_XDP_H__
 
 #include <linux/skbuff.h> /* skb_shared_info */
+#include <uapi/linux/netdev.h>
 
 /**
  * DOC: XDP RX-queue information
@@ -43,6 +44,8 @@ enum xdp_mem_type {
        MEM_TYPE_MAX,
 };
 
+typedef u32 xdp_features_t;
+
 /* XDP flags for ndo_xdp_xmit */
 #define XDP_XMIT_FLUSH         (1U << 0)       /* doorbell signal consumer */
 #define XDP_XMIT_FLAGS_MASK    XDP_XMIT_FLUSH
diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h
new file mode 100644 (file)
index 0000000..9ee4598
--- /dev/null
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Do not edit directly, auto-generated from: */
+/*     Documentation/netlink/specs/netdev.yaml */
+/* YNL-GEN uapi header */
+
+#ifndef _UAPI_LINUX_NETDEV_H
+#define _UAPI_LINUX_NETDEV_H
+
+#define NETDEV_FAMILY_NAME     "netdev"
+#define NETDEV_FAMILY_VERSION  1
+
+/**
+ * enum netdev_xdp_act
+ * @NETDEV_XDP_ACT_BASIC: XDP feautues set supported by all drivers
+ *   (XDP_ABORTED, XDP_DROP, XDP_PASS, XDP_TX)
+ * @NETDEV_XDP_ACT_REDIRECT: The netdev supports XDP_REDIRECT
+ * @NETDEV_XDP_ACT_NDO_XMIT: This feature informs if netdev implements
+ *   ndo_xdp_xmit callback.
+ * @NETDEV_XDP_ACT_XSK_ZEROCOPY: This feature informs if netdev supports AF_XDP
+ *   in zero copy mode.
+ * @NETDEV_XDP_ACT_HW_OFFLOAD: This feature informs if netdev supports XDP hw
+ *   oflloading.
+ * @NETDEV_XDP_ACT_RX_SG: This feature informs if netdev implements non-linear
+ *   XDP buffer support in the driver napi callback.
+ * @NETDEV_XDP_ACT_NDO_XMIT_SG: This feature informs if netdev implements
+ *   non-linear XDP buffer support in ndo_xdp_xmit callback.
+ */
+enum netdev_xdp_act {
+       NETDEV_XDP_ACT_BASIC = 1,
+       NETDEV_XDP_ACT_REDIRECT = 2,
+       NETDEV_XDP_ACT_NDO_XMIT = 4,
+       NETDEV_XDP_ACT_XSK_ZEROCOPY = 8,
+       NETDEV_XDP_ACT_HW_OFFLOAD = 16,
+       NETDEV_XDP_ACT_RX_SG = 32,
+       NETDEV_XDP_ACT_NDO_XMIT_SG = 64,
+};
+
+enum {
+       NETDEV_A_DEV_IFINDEX = 1,
+       NETDEV_A_DEV_PAD,
+       NETDEV_A_DEV_XDP_FEATURES,
+
+       __NETDEV_A_DEV_MAX,
+       NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_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_MAX,
+       NETDEV_CMD_MAX = (__NETDEV_CMD_MAX - 1)
+};
+
+#define NETDEV_MCGRP_MGMT      "mgmt"
+
+#endif /* _UAPI_LINUX_NETDEV_H */
index 10edd66a8a3727bec6c46db99af1014afd45d1bc..8f367813bc681a53a3338c32763264e1a00aa6b4 100644 (file)
@@ -12,7 +12,8 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
 obj-y               += dev.o dev_addr_lists.o dst.o netevent.o \
                        neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
                        sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
-                       fib_notifier.o xdp.o flow_offload.o gro.o
+                       fib_notifier.o xdp.o flow_offload.o gro.o \
+                       netdev-genl.o netdev-genl-gen.o
 
 obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o
 
index f72f5c4ee7e2fbaa74ec27a9c466d4301c145a88..9ac0eeb2c8cdf8ed729b83bf130bd0b35f9acd5a 100644 (file)
@@ -1614,6 +1614,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd)
        N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO)
        N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE)
        N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA)
+       N(XDP_FEAT_CHANGE)
        }
 #undef N
        return "UNKNOWN_NETDEV_EVENT";
diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c
new file mode 100644 (file)
index 0000000..48812ec
--- /dev/null
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/* Do not edit directly, auto-generated from: */
+/*     Documentation/netlink/specs/netdev.yaml */
+/* YNL-GEN kernel source */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "netdev-genl-gen.h"
+
+#include <linux/netdev.h>
+
+/* NETDEV_CMD_DEV_GET - do */
+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),
+};
+
+/* Ops table for netdev */
+static const struct genl_split_ops netdev_nl_ops[2] = {
+       {
+               .cmd            = NETDEV_CMD_DEV_GET,
+               .doit           = netdev_nl_dev_get_doit,
+               .policy         = netdev_dev_get_nl_policy,
+               .maxattr        = NETDEV_A_DEV_IFINDEX,
+               .flags          = GENL_CMD_CAP_DO,
+       },
+       {
+               .cmd    = NETDEV_CMD_DEV_GET,
+               .dumpit = netdev_nl_dev_get_dumpit,
+               .flags  = GENL_CMD_CAP_DUMP,
+       },
+};
+
+static const struct genl_multicast_group netdev_nl_mcgrps[] = {
+       [NETDEV_NLGRP_MGMT] = { "mgmt", },
+};
+
+struct genl_family netdev_nl_family __ro_after_init = {
+       .name           = NETDEV_FAMILY_NAME,
+       .version        = NETDEV_FAMILY_VERSION,
+       .netnsok        = true,
+       .parallel_ops   = true,
+       .module         = THIS_MODULE,
+       .split_ops      = netdev_nl_ops,
+       .n_split_ops    = ARRAY_SIZE(netdev_nl_ops),
+       .mcgrps         = netdev_nl_mcgrps,
+       .n_mcgrps       = ARRAY_SIZE(netdev_nl_mcgrps),
+};
diff --git a/net/core/netdev-genl-gen.h b/net/core/netdev-genl-gen.h
new file mode 100644 (file)
index 0000000..b16dc7e
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Do not edit directly, auto-generated from: */
+/*     Documentation/netlink/specs/netdev.yaml */
+/* YNL-GEN kernel header */
+
+#ifndef _LINUX_NETDEV_GEN_H
+#define _LINUX_NETDEV_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <linux/netdev.h>
+
+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);
+
+enum {
+       NETDEV_NLGRP_MGMT,
+};
+
+extern struct genl_family netdev_nl_family;
+
+#endif /* _LINUX_NETDEV_GEN_H */
diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c
new file mode 100644 (file)
index 0000000..a4270fa
--- /dev/null
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/netdevice.h>
+#include <linux/notifier.h>
+#include <linux/rtnetlink.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "netdev-genl-gen.h"
+
+static int
+netdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp,
+                  u32 portid, u32 seq, int flags, u32 cmd)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(rsp, portid, seq, &netdev_nl_family, flags, cmd);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       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;
+       }
+
+       genlmsg_end(rsp, hdr);
+
+       return 0;
+}
+
+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;
+       }
+
+       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;
+
+       if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_DEV_IFINDEX))
+               return -EINVAL;
+
+       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);
+       else
+               err = -ENODEV;
+
+       rtnl_unlock();
+
+       if (err)
+               goto err_free_msg;
+
+       return genlmsg_reply(rsp, info);
+
+err_free_msg:
+       nlmsg_free(rsp);
+       return err;
+}
+
+int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct net_device *netdev;
+       int idx = 0, s_idx;
+       int h, s_h;
+       int err;
+
+       s_h = cb->args[0];
+       s_idx = cb->args[1];
+
+       rtnl_lock();
+
+       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+               struct hlist_head *head;
+
+               idx = 0;
+               head = &net->dev_index_head[h];
+               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);
+                       if (err < 0)
+                               break;
+cont:
+                       idx++;
+               }
+       }
+
+       rtnl_unlock();
+
+       if (err != -EMSGSIZE)
+               return err;
+
+       cb->args[1] = idx;
+       cb->args[0] = h;
+       cb->seq = net->dev_base_seq;
+
+       return skb->len;
+}
+
+static int netdev_genl_netdevice_event(struct notifier_block *nb,
+                                      unsigned long event, void *ptr)
+{
+       struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+
+       switch (event) {
+       case NETDEV_REGISTER:
+               netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_ADD_NTF);
+               break;
+       case NETDEV_UNREGISTER:
+               netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF);
+               break;
+       case NETDEV_XDP_FEAT_CHANGE:
+               netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_CHANGE_NTF);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block netdev_genl_nb = {
+       .notifier_call  = netdev_genl_netdevice_event,
+};
+
+static int __init netdev_genl_init(void)
+{
+       int err;
+
+       err = register_netdevice_notifier(&netdev_genl_nb);
+       if (err)
+               return err;
+
+       err = genl_register_family(&netdev_nl_family);
+       if (err)
+               goto err_unreg_ntf;
+
+       return 0;
+
+err_unreg_ntf:
+       unregister_netdevice_notifier(&netdev_genl_nb);
+       return err;
+}
+
+subsys_initcall(netdev_genl_init);
diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h
new file mode 100644 (file)
index 0000000..9ee4598
--- /dev/null
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Do not edit directly, auto-generated from: */
+/*     Documentation/netlink/specs/netdev.yaml */
+/* YNL-GEN uapi header */
+
+#ifndef _UAPI_LINUX_NETDEV_H
+#define _UAPI_LINUX_NETDEV_H
+
+#define NETDEV_FAMILY_NAME     "netdev"
+#define NETDEV_FAMILY_VERSION  1
+
+/**
+ * enum netdev_xdp_act
+ * @NETDEV_XDP_ACT_BASIC: XDP feautues set supported by all drivers
+ *   (XDP_ABORTED, XDP_DROP, XDP_PASS, XDP_TX)
+ * @NETDEV_XDP_ACT_REDIRECT: The netdev supports XDP_REDIRECT
+ * @NETDEV_XDP_ACT_NDO_XMIT: This feature informs if netdev implements
+ *   ndo_xdp_xmit callback.
+ * @NETDEV_XDP_ACT_XSK_ZEROCOPY: This feature informs if netdev supports AF_XDP
+ *   in zero copy mode.
+ * @NETDEV_XDP_ACT_HW_OFFLOAD: This feature informs if netdev supports XDP hw
+ *   oflloading.
+ * @NETDEV_XDP_ACT_RX_SG: This feature informs if netdev implements non-linear
+ *   XDP buffer support in the driver napi callback.
+ * @NETDEV_XDP_ACT_NDO_XMIT_SG: This feature informs if netdev implements
+ *   non-linear XDP buffer support in ndo_xdp_xmit callback.
+ */
+enum netdev_xdp_act {
+       NETDEV_XDP_ACT_BASIC = 1,
+       NETDEV_XDP_ACT_REDIRECT = 2,
+       NETDEV_XDP_ACT_NDO_XMIT = 4,
+       NETDEV_XDP_ACT_XSK_ZEROCOPY = 8,
+       NETDEV_XDP_ACT_HW_OFFLOAD = 16,
+       NETDEV_XDP_ACT_RX_SG = 32,
+       NETDEV_XDP_ACT_NDO_XMIT_SG = 64,
+};
+
+enum {
+       NETDEV_A_DEV_IFINDEX = 1,
+       NETDEV_A_DEV_PAD,
+       NETDEV_A_DEV_XDP_FEATURES,
+
+       __NETDEV_A_DEV_MAX,
+       NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_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_MAX,
+       NETDEV_CMD_MAX = (__NETDEV_CMD_MAX - 1)
+};
+
+#define NETDEV_MCGRP_MGMT      "mgmt"
+
+#endif /* _UAPI_LINUX_NETDEV_H */