Add information about device link state (as provided by ETHTOOL_GLINK ioctl
command) into the GET_SETTINGS reply when ETH_SETTINGS_IM_LINK flag is set
in the request.

Note: we cannot use NLA_FLAG for link state as we need three states: off,
on and unknown.

Signed-off-by: Michal Kubecek <mkube...@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  4 +++-
 include/uapi/linux/ethtool_netlink.h         |  4 +++-
 net/ethtool/common.c                         |  8 ++++++++
 net/ethtool/common.h                         |  1 +
 net/ethtool/ioctl.c                          |  8 ++++----
 net/ethtool/settings.c                       | 11 +++++++++++
 6 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt 
b/Documentation/networking/ethtool-netlink.txt
index 057eb3213cfe..538dad93009f 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -285,6 +285,7 @@ Info mask bits meaning:
     ETH_SETTINGS_IM_LINKMODES          link modes from link_ksettings
     ETH_SETTINGS_IM_WOLINFO            struct ethtool_wolinfo
     ETH_SETTINGS_IM_MSGLEVEL           msglevel
+    ETH_SETTINGS_IM_LINK               link state
 
 Response contents:
 
@@ -304,6 +305,7 @@ Response contents:
         ETHA_WOL_MODES                 (bitfield32)    wake on LAN modes
         ETHA_WOL_SOPASS                        (binary)        SecureOn(tm) 
password
     ETHA_SETTINGS_MSGLEVEL     (bitfield32)    debug level
+    ETHA_SETTINGS_LINK         (u8)            link state
 
 Most of the attributes and their values have the same meaning as matching
 members of the corresponding ioctl structures. For ETHA_SETTINGS_LINK_MODES,
@@ -343,7 +345,7 @@ ETHTOOL_SWOL                        n/a
 ETHTOOL_GMSGLVL                        ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SMSGLVL                        n/a
 ETHTOOL_NWAY_RST               n/a
-ETHTOOL_GLINK                  n/a
+ETHTOOL_GLINK                  ETHNL_CMD_GET_SETTINGS
 ETHTOOL_GEEPROM                        n/a
 ETHTOOL_SEEPROM                        n/a
 ETHTOOL_GCOALESCE              n/a
diff --git a/include/uapi/linux/ethtool_netlink.h 
b/include/uapi/linux/ethtool_netlink.h
index 360e20a73f19..06e78d94cacc 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -203,6 +203,7 @@ enum {
        ETHA_SETTINGS_PEER_MODES,               /* bitset */
        ETHA_SETTINGS_WOL,                      /* nested */
        ETHA_SETTINGS_MSGLEVEL,                 /* bitfield32 */
+       ETHA_SETTINGS_LINK,                     /* u8 */
 
        __ETHA_SETTINGS_CNT,
        ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -212,8 +213,9 @@ enum {
 #define ETH_SETTINGS_IM_LINKMODES              0x02
 #define ETH_SETTINGS_IM_WOLINFO                        0x04
 #define ETH_SETTINGS_IM_MSGLEVEL               0x08
+#define ETH_SETTINGS_IM_LINK                   0x10
 
-#define ETH_SETTINGS_IM_ALL                    0x0f
+#define ETH_SETTINGS_IM_ALL                    0x1f
 
 enum {
        ETHA_LINKINFO_UNSPEC,
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 9fba57f554dd..a188f07bcb4c 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -217,3 +217,11 @@ int __ethtool_get_wol(struct net_device *dev, struct 
ethtool_wolinfo *wol)
 
        return 0;
 }
+
+int __ethtool_get_link(struct net_device *dev)
+{
+       if (!dev->ethtool_ops->get_link)
+               return -EOPNOTSUPP;
+
+       return netif_running(dev) && dev->ethtool_ops->get_link(dev);
+}
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index ed3f1ca54660..e5b5c5c2a4b9 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -18,6 +18,7 @@ 
phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
 int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo 
*info);
 int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info 
*info);
 int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
+int __ethtool_get_link(struct net_device *dev);
 
 bool convert_legacy_settings_to_link_ksettings(
        struct ethtool_link_ksettings *link_ksettings,
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 945eaf551a4c..63662a3fa2ae 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1306,12 +1306,12 @@ static int ethtool_nway_reset(struct net_device *dev)
 static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
 {
        struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
+       int link = __ethtool_get_link(dev);
 
-       if (!dev->ethtool_ops->get_link)
-               return -EOPNOTSUPP;
-
-       edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev);
+       if (link < 0)
+               return link;
 
+       edata.data = link;
        if (copy_to_user(useraddr, &edata, sizeof(edata)))
                return -EFAULT;
        return 0;
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 58cd2d19a75d..099300901c12 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -14,6 +14,7 @@ struct settings_data {
        struct ethtool_link_settings    *lsettings;
        struct ethtool_wolinfo          wolinfo;
        u32                             msglevel;
+       int                             link;
        bool                            lpm_empty;
 };
 
@@ -27,6 +28,7 @@ static const struct nla_policy 
get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
        [ETHA_SETTINGS_PEER_MODES]      = { .type = NLA_REJECT },
        [ETHA_SETTINGS_WOL]             = { .type = NLA_REJECT },
        [ETHA_SETTINGS_MSGLEVEL]        = { .type = NLA_REJECT },
+       [ETHA_SETTINGS_LINK]            = { .type = NLA_REJECT },
 };
 
 static int parse_settings(struct common_req_info *req_info,
@@ -99,6 +101,7 @@ static int prepare_settings(struct common_req_info *req_info,
 
        data->lsettings = &data->ksettings.base;
        data->lpm_empty = true;
+       data->link = -EOPNOTSUPP;
 
        ret = ethnl_before_ops(dev);
        if (ret < 0)
@@ -131,6 +134,8 @@ static int prepare_settings(struct common_req_info 
*req_info,
                else
                        req_mask &= ~ETH_SETTINGS_IM_MSGLEVEL;
        }
+       if (req_mask & ETH_SETTINGS_IM_LINK)
+               data->link = __ethtool_get_link(dev);
        ethnl_after_ops(dev);
 
        data->repdata_base.info_mask = req_mask;
@@ -209,6 +214,8 @@ static int settings_size(const struct common_req_info 
*req_info)
                len += wol_size();
        if (info_mask & ETH_SETTINGS_IM_MSGLEVEL)
                len += nla_total_size(sizeof(struct nla_bitfield32));
+       if (info_mask & ETH_SETTINGS_IM_LINK)
+               len += nla_total_size(sizeof(u32));
 
        return len;
 }
@@ -324,6 +331,10 @@ static int fill_settings(struct sk_buff *skb,
                                       data->msglevel, NETIF_MSG_ALL))
                        return -EMSGSIZE;
        }
+       if (info_mask & ETH_SETTINGS_IM_LINK && data->link >= 0) {
+               if (nla_put_u8(skb, ETHA_SETTINGS_LINK, data->link))
+                       return -EMSGSIZE;
+       }
 
        return 0;
 }
-- 
2.20.1

Reply via email to