lkml.org 
[lkml]   [2019]   [Feb]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH net-next v3 11/21] ethtool: provide driver/device information in GET_INFO request
    Date
    Implement GET_INFO request to get basic driver and device information as
    provided by ETHTOOL_GDRVINFO ioct command. The information is read only so
    that the corresponding SET_INFO message is only used in kernel replies.

    Move most of ethtool_get_drvinfo() int common.c so that the code can be
    shared by both ioctl and netlink interface.

    Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
    ---
    Documentation/networking/ethtool-netlink.txt | 43 ++++-
    include/uapi/linux/ethtool_netlink.h | 32 ++++
    net/ethtool/Makefile | 2 +-
    net/ethtool/common.c | 54 +++++++
    net/ethtool/common.h | 2 +
    net/ethtool/info.c | 155 +++++++++++++++++++
    net/ethtool/ioctl.c | 52 +------
    net/ethtool/netlink.c | 9 ++
    8 files changed, 300 insertions(+), 49 deletions(-)
    create mode 100644 net/ethtool/info.c

    diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
    index f0fe4f50db9f..b6999a2167e8 100644
    --- a/Documentation/networking/ethtool-netlink.txt
    +++ b/Documentation/networking/ethtool-netlink.txt
    @@ -128,6 +128,8 @@ List of message types
    ETHNL_CMD_EVENT notification only
    ETHNL_CMD_GET_STRSET
    ETHNL_CMD_SET_STRSET response only
    + ETHNL_CMD_GET_INFO
    + ETHNL_CMD_SET_INFO response only

    All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
    to indicate the type.
    @@ -208,6 +210,45 @@ ETHA_STRSET_COUNTS tells kernel to only return string counts of the sets, not
    the actual strings.


    +GET_INFO
    +--------
    +
    +GET_INFO requests information provided by ioctl commands ETHTOOL_GDRVINFO,
    +ETHTOOL_GPERMADDR and ETHTOOL_GET_TS_INFO to provide basic device information.
    +Common pattern is that all information is read only so that SET_INFO message
    +exists but is only used by kernel for replies to GET_INFO requests. There is
    +also no corresponding notification.
    +
    +Request contents:
    +
    + ETHA_INFO_DEV (nested) device identification
    + ETHA_INFO_INFOMASK (u32) info mask
    + ETHA_INFO_COMPACT (flag) request compact bitsets
    +
    +Info mask bits meaning:
    +
    + ETH_INFO_IM_DRVINFO driver info (GDRVINFO)
    + ETH_INFO_IM_PERMADDR permanent HW address (GPERMADDR)
    + ETH_INFO_IM_TSINFO timestamping info (GET_TS_INFO)
    +
    +Kernel response contents:
    +
    + ETHA_INFO_DEV (nested) device identification
    + ETHA_INFO_DRVINFO (nested) driver information
    + ETHA_DRVINFO_DRIVER (string) driver name
    + ETHA_DRVINFO_FWVERSION (string) firmware version
    + ETHA_DRVINFO_BUSINFO (string) device bus address
    + ETHA_DRVINFO_EROM_VER (string) expansion ROM version
    +
    +The meaning of DRVINFO attributes follows the corresponding fields of
    +ETHTOOL_GDRVINFO response. Second part with various counts and sizes is
    +omitted as these are not really needed (and if they are, they can be easily
    +found by different means). Driver version is also omitted as it is rather
    +misleading in most cases.
    +
    +GET_INFO requests allow dumps.
    +
    +
    Request translation
    -------------------

    @@ -219,7 +260,7 @@ ioctl command netlink command
    ---------------------------------------------------------------------
    ETHTOOL_GSET n/a
    ETHTOOL_SSET n/a
    -ETHTOOL_GDRVINFO n/a
    +ETHTOOL_GDRVINFO ETHNL_CMD_GET_INFO
    ETHTOOL_GREGS n/a
    ETHTOOL_GWOL n/a
    ETHTOOL_SWOL n/a
    diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
    index 630b66732dc9..fdae12b6c6b6 100644
    --- a/include/uapi/linux/ethtool_netlink.h
    +++ b/include/uapi/linux/ethtool_netlink.h
    @@ -10,6 +10,8 @@ enum {
    ETHNL_CMD_EVENT, /* only for notifications */
    ETHNL_CMD_GET_STRSET,
    ETHNL_CMD_SET_STRSET, /* only for reply */
    + ETHNL_CMD_GET_INFO,
    + ETHNL_CMD_SET_INFO, /* only for reply */

    __ETHNL_CMD_CNT,
    ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
    @@ -135,6 +137,36 @@ enum {
    ETHA_STRSET_MAX = (__ETHA_STRSET_CNT - 1)
    };

    +/* GET_INFO / SET_INFO */
    +
    +enum {
    + ETHA_INFO_UNSPEC,
    + ETHA_INFO_DEV, /* nest - ETHA_DEV_* */
    + ETHA_INFO_INFOMASK, /* u32 */
    + ETHA_INFO_COMPACT, /* flag */
    + ETHA_INFO_DRVINFO, /* nest - ETHA_DRVINFO_* */
    + ETHA_INFO_PERMADDR, /* nest - ETHA_PERMADDR_* */
    + ETHA_INFO_TSINFO, /* nest - ETHA_TSINFO_* */
    +
    + __ETHA_INFO_CNT,
    + ETHA_INFO_MAX = (__ETHA_INFO_CNT - 1)
    +};
    +
    +#define ETH_INFO_IM_DRVINFO 0x01
    +
    +#define ETH_INFO_IM_ALL 0x01
    +
    +enum {
    + ETHA_DRVINFO_UNSPEC,
    + ETHA_DRVINFO_DRIVER, /* string */
    + ETHA_DRVINFO_FWVERSION, /* string */
    + ETHA_DRVINFO_BUSINFO, /* string */
    + ETHA_DRVINFO_EROM_VER, /* string */
    +
    + __ETHA_DRVINFO_CNT,
    + ETHA_DRVINFO_MAX = (__ETHA_DRVINFO_CNT - 1)
    +};
    +
    /* generic netlink info */
    #define ETHTOOL_GENL_NAME "ethtool"
    #define ETHTOOL_GENL_VERSION 1
    diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
    index 11ceb00821b3..96d41dc45d4f 100644
    --- a/net/ethtool/Makefile
    +++ b/net/ethtool/Makefile
    @@ -4,4 +4,4 @@ obj-y += ioctl.o common.o

    obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o

    -ethtool_nl-y := netlink.o bitset.o strset.o
    +ethtool_nl-y := netlink.o bitset.o strset.o info.o
    diff --git a/net/ethtool/common.c b/net/ethtool/common.c
    index 73f721a1c557..e0bd7c6c5874 100644
    --- a/net/ethtool/common.c
    +++ b/net/ethtool/common.c
    @@ -1,5 +1,7 @@
    // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note

    +#include <linux/rtnetlink.h>
    +#include <net/devlink.h>
    #include "common.h"

    const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
    @@ -81,3 +83,55 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
    [ETHTOOL_ID_UNSPEC] = "Unspec",
    [ETHTOOL_PHY_DOWNSHIFT] = "phy-downshift",
    };
    +
    +int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
    +{
    + const struct ethtool_ops *ops = dev->ethtool_ops;
    +
    + memset(info, 0, sizeof(*info));
    + info->cmd = ETHTOOL_GDRVINFO;
    + if (ops->get_drvinfo) {
    + ops->get_drvinfo(dev, info);
    + } else if (dev->dev.parent && dev->dev.parent->driver) {
    + strlcpy(info->bus_info, dev_name(dev->dev.parent),
    + sizeof(info->bus_info));
    + strlcpy(info->driver, dev->dev.parent->driver->name,
    + sizeof(info->driver));
    + } else {
    + return -EOPNOTSUPP;
    + }
    +
    + /* this method of obtaining string set info is deprecated;
    + * Use ETHTOOL_GSSET_INFO instead.
    + */
    + if (ops->get_sset_count) {
    + int rc;
    +
    + rc = ops->get_sset_count(dev, ETH_SS_TEST);
    + if (rc >= 0)
    + info->testinfo_len = rc;
    + rc = ops->get_sset_count(dev, ETH_SS_STATS);
    + if (rc >= 0)
    + info->n_stats = rc;
    + rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
    + if (rc >= 0)
    + info->n_priv_flags = rc;
    + }
    + if (ops->get_regs_len) {
    + int ret = ops->get_regs_len(dev);
    +
    + if (ret > 0)
    + info->regdump_len = ret;
    + }
    +
    + if (ops->get_eeprom_len)
    + info->eedump_len = ops->get_eeprom_len(dev);
    +
    + rtnl_unlock();
    + if (!info->fw_version[0])
    + devlink_compat_running_version(dev, info->fw_version,
    + sizeof(info->fw_version));
    + rtnl_lock();
    +
    + return 0;
    +}
    diff --git a/net/ethtool/common.h b/net/ethtool/common.h
    index 41b2efc1e4e1..e87e58b3a274 100644
    --- a/net/ethtool/common.h
    +++ b/net/ethtool/common.h
    @@ -3,6 +3,7 @@
    #ifndef _ETHTOOL_COMMON_H
    #define _ETHTOOL_COMMON_H

    +#include <linux/netdevice.h>
    #include <linux/ethtool.h>

    extern const char
    @@ -14,4 +15,5 @@ tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN];
    extern const char
    phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];

    +int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
    #endif /* _ETHTOOL_COMMON_H */
    diff --git a/net/ethtool/info.c b/net/ethtool/info.c
    new file mode 100644
    index 000000000000..1a2e78d238e3
    --- /dev/null
    +++ b/net/ethtool/info.c
    @@ -0,0 +1,155 @@
    +// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
    +
    +#include "netlink.h"
    +#include "common.h"
    +#include "bitset.h"
    +
    +struct info_data {
    + struct common_req_info reqinfo_base;
    +
    + /* everything below here will be reset for each device in dumps */
    + struct common_reply_data repdata_base;
    + struct ethtool_drvinfo drvinfo;
    +};
    +
    +static const struct nla_policy get_info_policy[ETHA_INFO_MAX + 1] = {
    + [ETHA_INFO_UNSPEC] = { .type = NLA_REJECT },
    + [ETHA_INFO_DEV] = { .type = NLA_NESTED },
    + [ETHA_INFO_INFOMASK] = { .type = NLA_U32 },
    + [ETHA_INFO_COMPACT] = { .type = NLA_FLAG },
    + [ETHA_INFO_DRVINFO] = { .type = NLA_REJECT },
    +};
    +
    +static int parse_info(struct common_req_info *req_info, struct sk_buff *skb,
    + struct genl_info *info, const struct nlmsghdr *nlhdr)
    +{
    + struct nlattr *tb[ETHA_INFO_MAX + 1];
    + int ret;
    +
    + ret = genlmsg_parse(nlhdr, &ethtool_genl_family, tb, ETHA_INFO_MAX,
    + get_info_policy, info ? info->extack : NULL);
    + if (ret < 0)
    + return ret;
    +
    + if (tb[ETHA_INFO_DEV]) {
    + req_info->dev = ethnl_dev_get(info, tb[ETHA_INFO_DEV]);
    + if (IS_ERR(req_info->dev)) {
    + ret = PTR_ERR(req_info->dev);
    + req_info->dev = NULL;
    + return ret;
    + }
    + }
    + if (tb[ETHA_INFO_INFOMASK])
    + req_info->req_mask = nla_get_u32(tb[ETHA_INFO_INFOMASK]);
    + if (tb[ETHA_INFO_COMPACT])
    + req_info->compact = true;
    + if (req_info->req_mask == 0)
    + req_info->req_mask = ETH_INFO_IM_ALL;
    +
    + return 0;
    +}
    +
    +static int prepare_info(struct common_req_info *req_info,
    + struct genl_info *info)
    +{
    + struct info_data *data =
    + container_of(req_info, struct info_data, reqinfo_base);
    + struct net_device *dev = data->repdata_base.dev;
    + u32 req_mask = req_info->req_mask & ETH_INFO_IM_ALL;
    + int ret;
    +
    + ret = ethnl_before_ops(dev);
    + if (ret < 0)
    + return ret;
    + if (req_mask & ETH_INFO_IM_DRVINFO) {
    + ret = __ethtool_get_drvinfo(dev, &data->drvinfo);
    + if (ret < 0)
    + req_mask &= ~ETH_INFO_IM_DRVINFO;
    + }
    + ethnl_after_ops(dev);
    +
    + data->repdata_base.info_mask = req_mask;
    + if (req_info->req_mask & ~req_mask)
    + warn_partial_info(info);
    + return 0;
    +}
    +
    +static int drvinfo_size(const struct ethtool_drvinfo *drvinfo)
    +{
    + int len = 0;
    +
    + len += ethnl_str_ifne_size(drvinfo->driver);
    + len += ethnl_str_ifne_size(drvinfo->fw_version);
    + len += ethnl_str_ifne_size(drvinfo->bus_info);
    + len += ethnl_str_ifne_size(drvinfo->erom_version);
    +
    + return nla_total_size(len);
    +}
    +
    +static int info_size(const struct common_req_info *req_info)
    +{
    + const struct info_data *data =
    + container_of(req_info, struct info_data, reqinfo_base);
    + u32 info_mask = data->repdata_base.info_mask;
    + int len = 0;
    +
    + len += dev_ident_size();
    + if (info_mask & ETH_INFO_IM_DRVINFO)
    + len += drvinfo_size(&data->drvinfo);
    +
    + return len;
    +}
    +
    +static int fill_drvinfo(struct sk_buff *skb,
    + const struct ethtool_drvinfo *drvinfo)
    +{
    + struct nlattr *nest = ethnl_nest_start(skb, ETHA_INFO_DRVINFO);
    + int ret;
    +
    + if (!nest)
    + return -EMSGSIZE;
    + ret = -EMSGSIZE;
    + if (ethnl_put_str_ifne(skb, ETHA_DRVINFO_DRIVER, drvinfo->driver) ||
    + ethnl_put_str_ifne(skb, ETHA_DRVINFO_FWVERSION,
    + drvinfo->fw_version) ||
    + ethnl_put_str_ifne(skb, ETHA_DRVINFO_BUSINFO, drvinfo->bus_info) ||
    + ethnl_put_str_ifne(skb, ETHA_DRVINFO_EROM_VER,
    + drvinfo->erom_version))
    + goto err;
    +
    + nla_nest_end(skb, nest);
    + return 0;
    +err:
    + nla_nest_cancel(skb, nest);
    + return ret;
    +}
    +
    +static int fill_info(struct sk_buff *skb,
    + const struct common_req_info *req_info)
    +{
    + const struct info_data *data =
    + container_of(req_info, struct info_data, reqinfo_base);
    + u32 info_mask = data->repdata_base.info_mask;
    + int ret;
    +
    + if (info_mask & ETH_INFO_IM_DRVINFO) {
    + ret = fill_drvinfo(skb, &data->drvinfo);
    + if (ret < 0)
    + return ret;
    + }
    +
    + return 0;
    +}
    +
    +const struct get_request_ops info_request_ops = {
    + .request_cmd = ETHNL_CMD_GET_INFO,
    + .reply_cmd = ETHNL_CMD_SET_INFO,
    + .dev_attrtype = ETHA_INFO_DEV,
    + .data_size = sizeof(struct info_data),
    + .repdata_offset = offsetof(struct info_data, repdata_base),
    +
    + .parse_request = parse_info,
    + .prepare_data = prepare_info,
    + .reply_size = info_size,
    + .fill_reply = fill_info,
    +};
    diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
    index 71a1643adb2b..c883239001a4 100644
    --- a/net/ethtool/ioctl.c
    +++ b/net/ethtool/ioctl.c
    @@ -685,56 +685,14 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
    void __user *useraddr)
    {
    struct ethtool_drvinfo info;
    - const struct ethtool_ops *ops = dev->ethtool_ops;
    -
    - memset(&info, 0, sizeof(info));
    - info.cmd = ETHTOOL_GDRVINFO;
    - if (ops->get_drvinfo) {
    - ops->get_drvinfo(dev, &info);
    - } else if (dev->dev.parent && dev->dev.parent->driver) {
    - strlcpy(info.bus_info, dev_name(dev->dev.parent),
    - sizeof(info.bus_info));
    - strlcpy(info.driver, dev->dev.parent->driver->name,
    - sizeof(info.driver));
    - } else {
    - return -EOPNOTSUPP;
    - }
    -
    - /*
    - * this method of obtaining string set info is deprecated;
    - * Use ETHTOOL_GSSET_INFO instead.
    - */
    - if (ops->get_sset_count) {
    - int rc;
    -
    - rc = ops->get_sset_count(dev, ETH_SS_TEST);
    - if (rc >= 0)
    - info.testinfo_len = rc;
    - rc = ops->get_sset_count(dev, ETH_SS_STATS);
    - if (rc >= 0)
    - info.n_stats = rc;
    - rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
    - if (rc >= 0)
    - info.n_priv_flags = rc;
    - }
    - if (ops->get_regs_len) {
    - int ret = ops->get_regs_len(dev);
    -
    - if (ret > 0)
    - info.regdump_len = ret;
    - }
    -
    - if (ops->get_eeprom_len)
    - info.eedump_len = ops->get_eeprom_len(dev);
    -
    - rtnl_unlock();
    - if (!info.fw_version[0])
    - devlink_compat_running_version(dev, info.fw_version,
    - sizeof(info.fw_version));
    - rtnl_lock();
    + int rc;

    + rc = __ethtool_get_drvinfo(dev, &info);
    + if (rc < 0)
    + return rc;
    if (copy_to_user(useraddr, &info, sizeof(info)))
    return -EFAULT;
    +
    return 0;
    }

    diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
    index 082a9f2aa0a7..e27dec427414 100644
    --- a/net/ethtool/netlink.c
    +++ b/net/ethtool/netlink.c
    @@ -87,9 +87,11 @@ int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype)
    /* GET request helpers */

    extern const struct get_request_ops strset_request_ops;
    +extern const struct get_request_ops info_request_ops;

    const struct get_request_ops *get_requests[__ETHNL_CMD_CNT] = {
    [ETHNL_CMD_GET_STRSET] = &strset_request_ops,
    + [ETHNL_CMD_GET_INFO] = &info_request_ops,
    };

    static struct common_req_info *alloc_get_data(const struct get_request_ops *ops)
    @@ -508,6 +510,13 @@ static const struct genl_ops ethtool_genl_ops[] = {
    .dumpit = ethnl_get_dumpit,
    .done = ethnl_get_done,
    },
    + {
    + .cmd = ETHNL_CMD_GET_INFO,
    + .doit = ethnl_get_doit,
    + .start = ethnl_get_start,
    + .dumpit = ethnl_get_dumpit,
    + .done = ethnl_get_done,
    + },
    };

    static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
    --
    2.20.1
    \
     
     \ /
      Last update: 2019-02-18 19:24    [W:4.133 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site