@@ -263,7 +263,8 @@ requests.
GET_SETTINGS requests allow dumps and messages in the same format as response
to them are broadcasted as notifications on change of these settings using
-netlink or ioctl ethtool interface.
+netlink or ioctl ethtool interface; feature notifications are also sent
+whenever netdev_update_features() or netdev_change_features() is called.
Request translation
@@ -93,6 +93,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/ethtool_netlink.h>
#include <linux/notifier.h>
#include <linux/skbuff.h>
#include <linux/bpf.h>
@@ -1318,6 +1319,8 @@ int dev_get_alias(const struct net_device *dev, char *name, size_t len)
void netdev_features_change(struct net_device *dev)
{
call_netdevice_notifiers(NETDEV_FEAT_CHANGE, dev);
+ netdev_ethtool_info_change(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_FEATURES);
}
EXPORT_SYMBOL(netdev_features_change);
@@ -27,6 +27,7 @@
#include <linux/rtnetlink.h>
#include <linux/sched/signal.h>
#include <linux/net.h>
+#include <linux/ethtool_netlink.h>
#include "common.h"
/*
@@ -125,6 +126,8 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
dev->wanted_features &= ~valid;
dev->wanted_features |= wanted & valid;
__netdev_update_features(dev);
+ netdev_ethtool_info_change(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_FEATURES);
if ((dev->wanted_features ^ dev->features) & valid)
ret |= ETHTOOL_F_WISH;
@@ -243,6 +246,8 @@ static int ethtool_set_one_feature(struct net_device *dev,
dev->wanted_features &= ~mask;
__netdev_update_features(dev);
+ netdev_ethtool_info_change(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_FEATURES);
return 0;
}
@@ -298,6 +303,8 @@ static int __ethtool_set_flags(struct net_device *dev, u32 data)
(dev->wanted_features & ~changed) | (features & changed);
__netdev_update_features(dev);
+ netdev_ethtool_info_change(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_FEATURES);
return 0;
}
@@ -638,6 +645,7 @@ static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
{
struct ethtool_cmd cmd;
+ int ret;
ASSERT_RTNL();
@@ -655,8 +663,14 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
link_ksettings.base.cmd = ETHTOOL_SLINKSETTINGS;
link_ksettings.base.link_mode_masks_nwords
= __ETHTOOL_LINK_MODE_MASK_NU32;
- return dev->ethtool_ops->set_link_ksettings(dev,
- &link_ksettings);
+ ret = dev->ethtool_ops->set_link_ksettings(dev,
+ &link_ksettings);
+ if (ret >= 0)
+ netdev_ethtool_info_change(dev, NULL,
+ ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_LINKINFO |
+ ETH_SETTINGS_IM_LINKMODES);
+ return ret;
}
/* legacy %ethtool_cmd API */
@@ -668,7 +682,12 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
if (!dev->ethtool_ops->set_settings)
return -EOPNOTSUPP;
- return dev->ethtool_ops->set_settings(dev, &cmd);
+ ret = dev->ethtool_ops->set_settings(dev, &cmd);
+ if (ret >= 0)
+ netdev_ethtool_info_change(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_LINKINFO |
+ ETH_SETTINGS_IM_LINKMODES);
+ return ret;
}
static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
@@ -1279,6 +1298,7 @@ static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
{
struct ethtool_wolinfo wol;
+ int ret;
if (!dev->ethtool_ops->set_wol)
return -EOPNOTSUPP;
@@ -1286,7 +1306,11 @@ static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
if (copy_from_user(&wol, useraddr, sizeof(wol)))
return -EFAULT;
- return dev->ethtool_ops->set_wol(dev, &wol);
+ ret = dev->ethtool_ops->set_wol(dev, &wol);
+ if (ret >= 0)
+ netdev_ethtool_info_change(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_WOLINFO);
+ return ret;
}
static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)
@@ -2478,6 +2502,10 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_SMSGLVL:
rc = ethtool_set_value_void(dev, useraddr,
dev->ethtool_ops->set_msglevel);
+ if (rc >= 0)
+ netdev_ethtool_info_change(dev, NULL,
+ ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_MSGLEVEL);
break;
case ETHTOOL_GEEE:
rc = ethtool_get_eee(dev, useraddr);
@@ -629,7 +629,9 @@ int ethnl_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
typedef void (*ethnl_notify_handler_t)(struct netdev_notifier_ethtool_info *);
+void ethnl_settings_notify(struct netdev_notifier_ethtool_info *);
ethnl_notify_handler_t ethnl_notify_handlers[] = {
+ [ETHNL_CMD_SET_SETTINGS] = ethnl_settings_notify,
};
static void __ethnl_notify(struct netdev_notifier_ethtool_info *info)
@@ -565,3 +565,44 @@ int ethnl_settings_done(struct netlink_callback *cb)
return 0;
}
+
+void ethnl_settings_notify(struct netdev_notifier_ethtool_info *info)
+{
+ struct settings_reqinfo req_info = {
+ .dev = info->info.dev,
+ .req_mask = info->ethtool_info.req_mask,
+ .compact = true,
+ .is_privileged = false,
+ .have_rtnl = true,
+ };
+ struct settings_data data;
+ struct sk_buff *skb;
+ int reply_len;
+ void *ehdr;
+ int ret;
+
+ ret = prepare_settings(&data, &req_info, NULL, req_info.dev);
+ if (ret < 0)
+ return;
+ reply_len = settings_size(&data, &req_info);
+ if (ret < 0)
+ return;
+ skb = genlmsg_new(reply_len, GFP_KERNEL);
+ if (!skb)
+ return;
+ ehdr = genlmsg_put(skb, 0, ++ethnl_bcast_seq, ðtool_genl_family, 0,
+ ETHNL_CMD_SET_SETTINGS);
+ ret = ethnl_fill_dev(skb, req_info.dev, ETHA_SETTINGS_DEV);
+ if (ret < 0)
+ goto err_skb;
+ ret = fill_settings(skb, &data, &req_info);
+ if (ret < 0)
+ goto err_skb;
+ genlmsg_end(skb, ehdr);
+
+ genlmsg_multicast(ðtool_genl_family, skb, 0, ETHNL_MCGRP_MONITOR,
+ GFP_KERNEL);
+ return;
+err_skb:
+ nlmsg_free(skb);
+}
SET_SETTINGS notification message has the same format as response to GET_SETTINGS request and is broadcasted on change of relevant fields. Info mask can be used to limit the information passed to userspace. Also trigger the notification on analogous changes performed via the legacy ioctl interface. Signed-off-by: Michal Kubecek <mkubecek@suse.cz> --- Documentation/networking/ethtool-netlink.txt | 3 +- net/core/dev.c | 3 ++ net/ethtool/ioctl.c | 36 +++++++++++++++-- net/ethtool/netlink.c | 2 + net/ethtool/settings.c | 41 ++++++++++++++++++++ 5 files changed, 80 insertions(+), 5 deletions(-)