lkml.org 
[lkml]   [2017]   [Dec]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH 5/9] ethtool: implement GET_DRVINFO message
    Date
    Request the same information as ETHTOOL_GDRVINFO. This is read-only so that
    corresponding SET_DRVINFO exists but is only used in kernel replies. Rip
    the code to query the driver out of the legacy interface and move it to
    a new file ethtool_common.c so that both interfaces can use it.

    Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
    ---
    Documentation/networking/ethtool-netlink.txt | 30 ++++++++++-
    include/uapi/linux/ethtool_netlink.h | 21 ++++++++
    net/core/Makefile | 2 +-
    net/core/ethtool.c | 42 +++-------------
    net/core/ethtool_common.c | 46 +++++++++++++++++
    net/core/ethtool_common.h | 11 ++++
    net/core/ethtool_netlink.c | 75 ++++++++++++++++++++++++++++
    7 files changed, 189 insertions(+), 38 deletions(-)
    create mode 100644 net/core/ethtool_common.c
    create mode 100644 net/core/ethtool_common.h

    diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
    index 893e5156f6a7..cb992180b211 100644
    --- a/Documentation/networking/ethtool-netlink.txt
    +++ b/Documentation/networking/ethtool-netlink.txt
    @@ -107,6 +107,9 @@ which the request applies.
    List of message types
    ---------------------

    + ETHTOOL_CMD_GET_DRVINFO
    + ETHTOOL_CMD_SET_DRVINFO response only
    +
    All constants use ETHTOOL_CMD_ prefix followed by "GET", "SET" or "ACT" to
    indicate the type.

    @@ -119,6 +122,31 @@ messages marked as "response only" in the table above.
    Later sections describe the format and semantics of these request messages.


    +GET_DRVINFO
    +-----------
    +
    +GET_DRVINFO request corresponds to ETHTOOL_GDRVINFO ioctl command and provides
    +basic driver information. The request doesn't use any attributes and flags,
    +info_mask and index field in request header are ignored. Kernel response
    +contents:
    +
    + ETHA_DRVINFO_DRIVER (string) driver name
    + ETHA_DRVINFO_VERSION (string) driver version
    + ETHA_DRVINFO_FWVERSION (string) firmware version
    + ETHA_DRVINFO_BUSINFO (string) device bus address
    + ETHA_DRVINFO_EROM_VER (string) expansion ROM version
    + ETHA_DRVINFO_N_PRIV_FLAGS (u32) number of private flags
    + ETHA_DRVINFO_N_STATS (u32) number of device stats
    + ETHA_DRVINFO_TESTINFO_LEN (u32) number of test results
    + ETHA_DRVINFO_EEDUMP_LEN (u32) EEPROM dump size
    + ETHA_DRVINFO_REGDUMP_LEN (u32) register dump size
    +
    +The meaning of these follows the corresponding fields of ETHTOOL_GDRVINFO
    +response.
    +
    +All information is read only, SET_DRVINFO request is not implemented.
    +
    +
    Request translation
    -------------------

    @@ -130,7 +158,7 @@ ioctl command netlink command
    ---------------------------------------------------------------------
    ETHTOOL_GSET n/a
    ETHTOOL_SSET n/a
    -ETHTOOL_GDRVINFO n/a
    +ETHTOOL_GDRVINFO ETHTOOL_CMD_GET_DRVINFO
    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 6db35f00ea32..d6ab1d73d494 100644
    --- a/include/uapi/linux/ethtool_netlink.h
    +++ b/include/uapi/linux/ethtool_netlink.h
    @@ -21,6 +21,8 @@ struct ethnlmsghdr {

    enum {
    ETHTOOL_CMD_NOOP,
    + ETHTOOL_CMD_GET_DRVINFO,
    + ETHTOOL_CMD_SET_DRVINFO, /* only for reply */

    __ETHTOOL_CMD_MAX,
    ETHTOOL_CMD_MAX = (__ETHTOOL_CMD_MAX - 1),
    @@ -57,6 +59,25 @@ enum {
    ETHA_BITSET_MAX = (__ETHA_BITSET_MAX - 1),
    };

    +/* GET_DRVINFO / SET_DRVINFO */
    +
    +enum {
    + ETHA_DRVINFO_UNSPEC,
    + ETHA_DRVINFO_DRIVER, /* string */
    + ETHA_DRVINFO_VERSION, /* string */
    + ETHA_DRVINFO_FWVERSION, /* string */
    + ETHA_DRVINFO_BUSINFO, /* string */
    + ETHA_DRVINFO_EROM_VER, /* string */
    + ETHA_DRVINFO_N_PRIV_FLAGS, /* u32 */
    + ETHA_DRVINFO_N_STATS, /* u32 */
    + ETHA_DRVINFO_TESTINFO_LEN, /* u32 */
    + ETHA_DRVINFO_EEDUMP_LEN, /* u32 */
    + ETHA_DRVINFO_REGDUMP_LEN, /* u32 */
    +
    + __ETHA_DRVINFO_MAX,
    + ETHA_DRVINFO_MAX = (__ETHA_DRVINFO_MAX - 1),
    +};
    +
    /* generic netlink info */
    #define ETHTOOL_GENL_NAME "ethtool"
    #define ETHTOOL_GENL_VERSION 1
    diff --git a/net/core/Makefile b/net/core/Makefile
    index 617ab2abecdf..3196641c0e70 100644
    --- a/net/core/Makefile
    +++ b/net/core/Makefile
    @@ -11,7 +11,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
    obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
    neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
    sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
    - fib_notifier.o
    + fib_notifier.o ethtool_common.o

    obj-y += net-sysfs.o
    obj-$(CONFIG_PROC_FS) += net-procfs.o
    diff --git a/net/core/ethtool.c b/net/core/ethtool.c
    index f8fcf450a36e..09e780a748f9 100644
    --- a/net/core/ethtool.c
    +++ b/net/core/ethtool.c
    @@ -26,6 +26,7 @@
    #include <linux/rtnetlink.h>
    #include <linux/sched/signal.h>
    #include <linux/net.h>
    +#include "ethtool_common.h"

    /*
    * Some useful ethtool_ops methods that're device independent.
    @@ -885,45 +886,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)
    - info.regdump_len = ops->get_regs_len(dev);
    - if (ops->get_eeprom_len)
    - info.eedump_len = ops->get_eeprom_len(dev);
    + 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/core/ethtool_common.c b/net/core/ethtool_common.c
    new file mode 100644
    index 000000000000..2c0abab0e43c
    --- /dev/null
    +++ b/net/core/ethtool_common.c
    @@ -0,0 +1,46 @@
    +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
    +
    +#include <linux/rtnetlink.h>
    +#include "ethtool_common.h"
    +
    +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)
    + info->regdump_len = ops->get_regs_len(dev);
    + if (ops->get_eeprom_len)
    + info->eedump_len = ops->get_eeprom_len(dev);
    +
    + return 0;
    +}
    +EXPORT_SYMBOL(__ethtool_get_drvinfo);
    diff --git a/net/core/ethtool_common.h b/net/core/ethtool_common.h
    new file mode 100644
    index 000000000000..1f031c1d943a
    --- /dev/null
    +++ b/net/core/ethtool_common.h
    @@ -0,0 +1,11 @@
    +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
    +
    +#ifndef _ETHTOOL_COMMON_H
    +#define _ETHTOOL_COMMON_H
    +
    +#include <linux/netdevice.h>
    +#include <linux/ethtool.h>
    +
    +int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
    +
    +#endif /* _ETHTOOL_COMMON_H */
    diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
    index 9f287e67f30b..077814fd36bd 100644
    --- a/net/core/ethtool_netlink.c
    +++ b/net/core/ethtool_netlink.c
    @@ -531,9 +531,84 @@ static struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev,
    return rskb;
    }

    +/* GET_DRVINFO */
    +
    +static int ethnl_drvinfo_size(struct ethtool_drvinfo *drvinfo)
    +{
    + int len = 0;
    +
    + len += ethnl_str_ifne_size(drvinfo->driver);
    + len += ethnl_str_ifne_size(drvinfo->version);
    + len += ethnl_str_ifne_size(drvinfo->fw_version);
    + len += ethnl_str_ifne_size(drvinfo->bus_info);
    + len += ethnl_str_ifne_size(drvinfo->erom_version);
    + /* n_priv_flags, n_stats, testinfo_len, eedump_len, regdump_len */
    + len += 5 * nla_total_size(sizeof(u32));
    +
    + return len;
    +}
    +
    +static int ethnl_get_drvinfo(struct sk_buff *skb, struct genl_info *info)
    +{
    + struct ethnlmsghdr *ehdr;
    + struct ethtool_drvinfo drvinfo = {};
    + struct net_device *dev;
    + struct sk_buff *rskb;
    + int reply_len;
    + int rc = 0;
    +
    + dev = ethnl_dev_get(info);
    + if (IS_ERR(dev))
    + return PTR_ERR(dev);
    + rc = __ethtool_get_drvinfo(dev, &drvinfo);
    + if (rc < 0) {
    + dev_put(dev);
    + GENL_SET_ERR_MSG(info,
    + "failed to retrieve driver info from driver");
    + return rc;
    + }
    +
    + reply_len = ethnl_drvinfo_size(&drvinfo);
    + rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_CMD_SET_DRVINFO, info,
    + &ehdr);
    + dev_put(dev);
    + if (!rskb)
    + return -ENOMEM;
    +
    + if (ethnl_put_str_ifne(rskb, ETHA_DRVINFO_DRIVER, drvinfo.driver) ||
    + ethnl_put_str_ifne(rskb, ETHA_DRVINFO_VERSION, drvinfo.version) ||
    + ethnl_put_str_ifne(rskb, ETHA_DRVINFO_FWVERSION,
    + drvinfo.fw_version) ||
    + ethnl_put_str_ifne(rskb, ETHA_DRVINFO_BUSINFO, drvinfo.bus_info) ||
    + ethnl_put_str_ifne(rskb, ETHA_DRVINFO_EROM_VER,
    + drvinfo.erom_version) ||
    + nla_put_u32(rskb, ETHA_DRVINFO_N_PRIV_FLAGS,
    + drvinfo.n_priv_flags) ||
    + nla_put_u32(rskb, ETHA_DRVINFO_N_STATS, drvinfo.n_stats) ||
    + nla_put_u32(rskb, ETHA_DRVINFO_TESTINFO_LEN,
    + drvinfo.testinfo_len) ||
    + nla_put_u32(rskb, ETHA_DRVINFO_EEDUMP_LEN, drvinfo.eedump_len) ||
    + nla_put_u32(rskb, ETHA_DRVINFO_REGDUMP_LEN, drvinfo.regdump_len))
    + goto err;
    +
    + genlmsg_end(rskb, ehdr);
    + return genlmsg_reply(rskb, info);
    +
    +err:
    + nlmsg_free(rskb);
    + GENL_SET_ERR_MSG(info, "kernel error, see kernel log for details");
    + WARN_ONCE(1, "calculated message payload length (%d) not sufficient\n",
    + reply_len);
    + return -EMSGSIZE;
    +}
    +
    /* genetlink paperwork */

    static const struct genl_ops ethtool_genl_ops[] = {
    + {
    + .cmd = ETHTOOL_CMD_GET_DRVINFO,
    + .doit = ethnl_get_drvinfo,
    + },
    };

    static struct genl_family ethtool_genl_family = {
    --
    2.15.1
    \
     
     \ /
      Last update: 2017-12-11 14:56    [W:2.694 / U:0.044 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site