Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753262AbdLKNyK (ORCPT ); Mon, 11 Dec 2017 08:54:10 -0500 Received: from mx2.suse.de ([195.135.220.15]:60248 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753052AbdLKNyD (ORCPT ); Mon, 11 Dec 2017 08:54:03 -0500 Message-Id: <219f457821c4cbca64ead6a8f8a92246ed7f32a3.1513000306.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [RFC PATCH 5/9] ethtool: implement GET_DRVINFO message To: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Date: Mon, 11 Dec 2017 14:54:01 +0100 (CET) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 10927 Lines: 348 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 --- 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 #include #include +#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 +#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 +#include + +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