[RFC PATCH net-next v3 19/21] ethtool: provide device features in GET_SETTINGS request

From: Michal Kubecek
Date: Mon Feb 18 2019 - 13:23:09 EST


Add information about network device features (as provided by
ETHTOOL_GFEATURES ioctl command) in GET_SETTINGS reply when
ETH_SETTINGS_IM_FEATURES flag is set in the request.

This request also provides information provided by ETHTOOL_GRXCSUM,
ETHTOOL_GTXCSUM, ETHTOOL_GSG, ETHTOOL_GTSO, ETHTOOL_GUFO, ETHTOOL_GGSO,
ETHTOOL_GFLAGS and ETHTOOL_GGRO ioctl commands.

Signed-off-by: Michal Kubecek <mkubecek@xxxxxxx>
---
Documentation/networking/ethtool-netlink.txt | 36 +++++--
include/uapi/linux/ethtool_netlink.h | 15 ++-
net/ethtool/common.h | 2 +
net/ethtool/ioctl.c | 2 -
net/ethtool/settings.c | 108 +++++++++++++++++++
5 files changed, 150 insertions(+), 13 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 538dad93009f..664c922a05eb 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -286,6 +286,7 @@ Info mask bits meaning:
ETH_SETTINGS_IM_WOLINFO struct ethtool_wolinfo
ETH_SETTINGS_IM_MSGLEVEL msglevel
ETH_SETTINGS_IM_LINK link state
+ ETH_SETTINGS_IM_FEATURES features

Response contents:

@@ -306,6 +307,11 @@ Response contents:
ETHA_WOL_SOPASS (binary) SecureOn(tm) password
ETHA_SETTINGS_MSGLEVEL (bitfield32) debug level
ETHA_SETTINGS_LINK (u8) link state
+ ETHA_SETTINGS_FEATURES (nested) device features
+ ETHA_FEATURES_HW (bitset) dev->hw_features
+ ETHA_FEATURES_WANTED (bitset) dev->wanted_features
+ ETHA_FEATURES_ACTIVE (bitset) dev->features
+ ETHA_FEATURES_NOCHANGE (bitset) NETIF_F_NEVER_CHANGE

Most of the attributes and their values have the same meaning as matching
members of the corresponding ioctl structures. For ETHA_SETTINGS_LINK_MODES,
@@ -318,13 +324,23 @@ device and value enabled modes.
For ETHA_SETTINGS_MSGLEVEL, selector reports all flags supported by kernel and
value flags enabled for the device.

+Bitmaps contained in ETHA_SETTINGS_FEATURES have the same meaning as bitmaps
+used in ioctl interference but attribute names are different (they are based
+on corresponding members of struct net_device). Legacy "flags" are not
+provided, if userspace needs them (most likely only ethtool for backward
+compatibility), it can calculate their values from related feature bits
+itself. ETHA_FEATURES_HW uses mask consisting of all features recognized by
+kernel (to provide all names when using verbose bitmap format), remaining
+three use mask equal to value (to save space).
+
GET_SETTINGS request is allowed for unprivileged user but ETHA_SETTINGS_SOPASS
is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
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
@@ -354,30 +370,30 @@ ETHTOOL_GRINGPARAM n/a
ETHTOOL_SRINGPARAM n/a
ETHTOOL_GPAUSEPARAM n/a
ETHTOOL_SPAUSEPARAM n/a
-ETHTOOL_GRXCSUM n/a
+ETHTOOL_GRXCSUM ETHNL_CMD_GET_SETTINGS
ETHTOOL_SRXCSUM n/a
-ETHTOOL_GTXCSUM n/a
+ETHTOOL_GTXCSUM ETHNL_CMD_GET_SETTINGS
ETHTOOL_STXCSUM n/a
-ETHTOOL_GSG n/a
+ETHTOOL_GSG ETHNL_CMD_GET_SETTINGS
ETHTOOL_SSG n/a
ETHTOOL_TEST n/a
ETHTOOL_GSTRINGS ETHNL_CMD_GET_STRSET
ETHTOOL_PHYS_ID n/a
ETHTOOL_GSTATS n/a
-ETHTOOL_GTSO n/a
+ETHTOOL_GTSO ETHNL_CMD_GET_SETTINGS
ETHTOOL_STSO n/a
ETHTOOL_GPERMADDR ETHNL_CMD_GET_INFO
-ETHTOOL_GUFO n/a
+ETHTOOL_GUFO ETHNL_CMD_GET_SETTINGS
ETHTOOL_SUFO n/a
-ETHTOOL_GGSO n/a
+ETHTOOL_GGSO ETHNL_CMD_GET_SETTINGS
ETHTOOL_SGSO n/a
-ETHTOOL_GFLAGS n/a
+ETHTOOL_GFLAGS ETHNL_CMD_GET_SETTINGS
ETHTOOL_SFLAGS n/a
ETHTOOL_GPFLAGS n/a
ETHTOOL_SPFLAGS n/a
ETHTOOL_GRXFH n/a
ETHTOOL_SRXFH n/a
-ETHTOOL_GGRO n/a
+ETHTOOL_GGRO ETHNL_CMD_GET_SETTINGS
ETHTOOL_SGRO n/a
ETHTOOL_GRXRINGS n/a
ETHTOOL_GRXCLSRLCNT n/a
@@ -392,7 +408,7 @@ ETHTOOL_GRXNTUPLE n/a
ETHTOOL_GSSET_INFO ETHNL_CMD_GET_STRSET
ETHTOOL_GRXFHINDIR n/a
ETHTOOL_SRXFHINDIR n/a
-ETHTOOL_GFEATURES n/a
+ETHTOOL_GFEATURES ETHNL_CMD_GET_SETTINGS
ETHTOOL_SFEATURES n/a
ETHTOOL_GCHANNELS n/a
ETHTOOL_SCHANNELS n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 06e78d94cacc..154d7e6a59dd 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -204,6 +204,7 @@ enum {
ETHA_SETTINGS_WOL, /* nested */
ETHA_SETTINGS_MSGLEVEL, /* bitfield32 */
ETHA_SETTINGS_LINK, /* u8 */
+ ETHA_SETTINGS_FEATURES, /* nest - ETHA_FEATURES_* */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -214,8 +215,20 @@ enum {
#define ETH_SETTINGS_IM_WOLINFO 0x04
#define ETH_SETTINGS_IM_MSGLEVEL 0x08
#define ETH_SETTINGS_IM_LINK 0x10
+#define ETH_SETTINGS_IM_FEATURES 0x20

-#define ETH_SETTINGS_IM_ALL 0x1f
+#define ETH_SETTINGS_IM_ALL 0x3f
+
+enum {
+ ETHA_FEATURES_UNSPEC,
+ ETHA_FEATURES_HW, /* bitset */
+ ETHA_FEATURES_WANTED, /* bitset */
+ ETHA_FEATURES_ACTIVE, /* bitset */
+ ETHA_FEATURES_NOCHANGE, /* bitset */
+
+ __ETHA_FEATURES_CNT,
+ ETHA_FEATURES_MAX = (__ETHA_FEATURES_CNT - 1)
+};

enum {
ETHA_LINKINFO_UNSPEC,
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index e5b5c5c2a4b9..cf0b81af2d9f 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -6,6 +6,8 @@
#include <linux/netdevice.h>
#include <linux/ethtool.h>

+#define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32)
+
extern const char
netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN];
extern const char
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 63662a3fa2ae..6c3b492a88fe 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -58,8 +58,6 @@ EXPORT_SYMBOL(ethtool_op_get_ts_info);

/* Handlers for each ethtool command */

-#define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32)
-
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
{
struct ethtool_gfeatures cmd = {
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 099300901c12..32ee273de879 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -16,6 +16,12 @@ struct settings_data {
u32 msglevel;
int link;
bool lpm_empty;
+ struct {
+ u32 hw[ETHTOOL_DEV_FEATURE_WORDS];
+ u32 wanted[ETHTOOL_DEV_FEATURE_WORDS];
+ u32 active[ETHTOOL_DEV_FEATURE_WORDS];
+ u32 nochange[ETHTOOL_DEV_FEATURE_WORDS];
+ } features;
};

static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
@@ -29,6 +35,7 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_WOL] = { .type = NLA_REJECT },
[ETHA_SETTINGS_MSGLEVEL] = { .type = NLA_REJECT },
[ETHA_SETTINGS_LINK] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_FEATURES] = { .type = NLA_REJECT },
};

static int parse_settings(struct common_req_info *req_info,
@@ -89,6 +96,24 @@ static int ethnl_get_wol(struct genl_info *info, struct net_device *dev,
return ret;
}

+static void features_to_bitmap(u32 *dest, netdev_features_t src)
+{
+ unsigned int i;
+
+ for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++)
+ dest[i] = (u32)(src >> (32 * i));
+}
+
+static int ethnl_get_features(struct net_device *dev,
+ struct settings_data *data)
+{
+ features_to_bitmap(data->features.hw, dev->hw_features);
+ features_to_bitmap(data->features.wanted, dev->wanted_features);
+ features_to_bitmap(data->features.active, dev->features);
+ features_to_bitmap(data->features.nochange, NETIF_F_NEVER_CHANGE);
+ return 0;
+}
+
static int prepare_settings(struct common_req_info *req_info,
struct genl_info *info)
{
@@ -136,6 +161,8 @@ static int prepare_settings(struct common_req_info *req_info,
}
if (req_mask & ETH_SETTINGS_IM_LINK)
data->link = __ethtool_get_link(dev);
+ if (req_mask & ETH_SETTINGS_IM_FEATURES)
+ ethnl_get_features(dev, data);
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -189,6 +216,38 @@ static int wol_size(void)
nla_total_size(SOPASS_MAX));
}

+static int features_size(const struct settings_data *data)
+{
+ unsigned int flags =
+ (data->reqinfo_base.compact ? ETHNL_BITSET_COMPACT : 0) |
+ ETHNL_BITSET_LEGACY_NAMES;
+ int len = 0, ret;
+
+ ret = ethnl_bitset32_size(NETDEV_FEATURE_COUNT, data->features.hw,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ flags |= ETHNL_BITSET_LIST;
+ ret = ethnl_bitset32_size(NETDEV_FEATURE_COUNT, data->features.wanted,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ ret = ethnl_bitset32_size(NETDEV_FEATURE_COUNT, data->features.active,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ ret = ethnl_bitset32_size(NETDEV_FEATURE_COUNT, data->features.nochange,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+
+ return len;
+}
+
/* To keep things simple, reserve space for some attributes which may not
* be added to the message (e.g. ETHA_SETTINGS_SOPASS); therefore the length
* returned may be bigger than the actual length of the message sent
@@ -216,6 +275,12 @@ static int settings_size(const struct common_req_info *req_info)
len += nla_total_size(sizeof(struct nla_bitfield32));
if (info_mask & ETH_SETTINGS_IM_LINK)
len += nla_total_size(sizeof(u32));
+ if (info_mask & ETH_SETTINGS_IM_FEATURES) {
+ ret = features_size(data);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }

return len;
}
@@ -301,6 +366,44 @@ static int fill_wolinfo(struct sk_buff *skb,
return -EMSGSIZE;
}

+static int fill_features(struct sk_buff *skb, const struct settings_data *data)
+{
+ unsigned int flags =
+ (data->reqinfo_base.compact ? ETHNL_BITSET_COMPACT : 0) |
+ ETHNL_BITSET_LEGACY_NAMES;
+ struct nlattr *feat_attr;
+ int ret;
+
+ feat_attr = ethnl_nest_start(skb, ETHA_SETTINGS_FEATURES);
+ if (!feat_attr)
+ return -EMSGSIZE;
+
+ ret = ethnl_put_bitset32(skb, ETHA_FEATURES_HW, NETDEV_FEATURE_COUNT,
+ data->features.hw, NULL,
+ netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ flags |= ETHNL_BITSET_LIST;
+ ret = ethnl_put_bitset32(skb, ETHA_FEATURES_WANTED,
+ NETDEV_FEATURE_COUNT, data->features.wanted,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ ret = ethnl_put_bitset32(skb, ETHA_FEATURES_ACTIVE,
+ NETDEV_FEATURE_COUNT, data->features.active,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ ret = ethnl_put_bitset32(skb, ETHA_FEATURES_NOCHANGE,
+ NETDEV_FEATURE_COUNT, data->features.nochange,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+
+ nla_nest_end(skb, feat_attr);
+ return 0;
+}
+
static int fill_settings(struct sk_buff *skb,
const struct common_req_info *req_info)
{
@@ -335,6 +438,11 @@ static int fill_settings(struct sk_buff *skb,
if (nla_put_u8(skb, ETHA_SETTINGS_LINK, data->link))
return -EMSGSIZE;
}
+ if (info_mask & ETH_SETTINGS_IM_FEATURES) {
+ ret = fill_features(skb, data);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}
--
2.20.1