]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
ethtool: support for netlink notifications
authorMichal Kubecek <mkubecek@suse.cz>
Fri, 27 Dec 2019 14:55:33 +0000 (15:55 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sat, 28 Dec 2019 00:40:01 +0000 (16:40 -0800)
Add infrastructure for ethtool netlink notifications. There is only one
multicast group "monitor" which is used to notify userspace about changes
and actions performed. Notification messages (types using suffix _NTF)
share the format with replies to GET requests.

Notifications are supposed to be broadcasted on every configuration change,
whether it is done using the netlink interface or ioctl one. Netlink SET
requests only trigger a notification if some data is actually changed.

To trigger an ethtool notification, both ethtool netlink and external code
use ethtool_notify() helper. This helper requires RTNL to be held and may
sleep. Handlers sending messages for specific notification message types
are registered in ethnl_notify_handlers array. As notifications can be
triggered from other code, ethnl_ok flag is used to prevent an attempt to
send notification before genetlink family is registered.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/ethtool_netlink.h
include/linux/netdevice.h
include/uapi/linux/ethtool_netlink.h
net/ethtool/netlink.c

index f27e92b5f344b77abf1fc4d817b4dcc76ea725c1..c98f6852c8eb5954cd679f388e91f511b27e2e5f 100644 (file)
@@ -5,5 +5,10 @@
 
 #include <uapi/linux/ethtool_netlink.h>
 #include <linux/ethtool.h>
+#include <linux/netdevice.h>
+
+enum ethtool_multicast_groups {
+       ETHNL_MCGRP_MONITOR,
+};
 
 #endif /* _LINUX_ETHTOOL_NETLINK_H_ */
index 469a297b58c0ea74124fa94a3d5fce1147a18db0..f007155ae8f4825f4d8a8b63f746bd2a4bff6b00 100644 (file)
@@ -4393,6 +4393,15 @@ struct netdev_notifier_bonding_info {
 void netdev_bonding_info_change(struct net_device *dev,
                                struct netdev_bonding_info *bonding_info);
 
+#if IS_ENABLED(CONFIG_ETHTOOL_NETLINK)
+void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data);
+#else
+static inline void ethtool_notify(struct net_device *dev, unsigned int cmd,
+                                 const void *data)
+{
+}
+#endif
+
 static inline
 struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features)
 {
index 951203049615b2436847223dbc28596458680257..d530ccb6f7e6d61023ceefc1a2179c439e4ed8de 100644 (file)
@@ -89,4 +89,6 @@ enum {
 #define ETHTOOL_GENL_NAME "ethtool"
 #define ETHTOOL_GENL_VERSION 1
 
+#define ETHTOOL_MCGRP_MONITOR_NAME "monitor"
+
 #endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */
index aef882e0c3f572fa6bf98cfdfbf7d3e9734e5262..c0f25c8f35655407ed3e52636718ec08d4e9c446 100644 (file)
@@ -6,6 +6,8 @@
 
 static struct genl_family ethtool_genl_family;
 
+static bool ethnl_ok __read_mostly;
+
 static const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1] = {
        [ETHTOOL_A_HEADER_UNSPEC]       = { .type = NLA_REJECT },
        [ETHTOOL_A_HEADER_DEV_INDEX]    = { .type = NLA_U32 },
@@ -169,11 +171,38 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
        return NULL;
 }
 
+/* notifications */
+
+typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd,
+                                      const void *data);
+
+static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
+};
+
+void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
+{
+       if (unlikely(!ethnl_ok))
+               return;
+       ASSERT_RTNL();
+
+       if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
+                  ethnl_notify_handlers[cmd]))
+               ethnl_notify_handlers[cmd](dev, cmd, data);
+       else
+               WARN_ONCE(1, "notification %u not implemented (dev=%s)\n",
+                         cmd, netdev_name(dev));
+}
+EXPORT_SYMBOL(ethtool_notify);
+
 /* genetlink setup */
 
 static const struct genl_ops ethtool_genl_ops[] = {
 };
 
+static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
+       [ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME },
+};
+
 static struct genl_family ethtool_genl_family = {
        .name           = ETHTOOL_GENL_NAME,
        .version        = ETHTOOL_GENL_VERSION,
@@ -181,6 +210,8 @@ static struct genl_family ethtool_genl_family = {
        .parallel_ops   = true,
        .ops            = ethtool_genl_ops,
        .n_ops          = ARRAY_SIZE(ethtool_genl_ops),
+       .mcgrps         = ethtool_nl_mcgrps,
+       .n_mcgrps       = ARRAY_SIZE(ethtool_nl_mcgrps),
 };
 
 /* module setup */
@@ -192,6 +223,7 @@ static int __init ethnl_init(void)
        ret = genl_register_family(&ethtool_genl_family);
        if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
                return ret;
+       ethnl_ok = true;
 
        return 0;
 }