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 <mkube...@suse.cz>
---
 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

Reply via email to