2019-03-25 17:09:04

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 00/22] ethtool netlink interface, part 1

This is first part of netlink based alternative userspace interface for
ethtool. The aim is to address some long known issues with the ioctl
interface, mainly lack of extensibility, raciness, limited error reporting
and absence of notifications. The goal is to allow userspace ethtool
utility to provide all features it currently does but without using the
ioctl interface.

The interface uses generic netlink family "ethtool"; it provides multicast
group "monitor" which is used for notifications. Documentation for the
interface is in Documentation/networking/ethtool-netlink.txt file. The
netlink interface is optional, it is built when CONFIG_ETHTOOL_NETLINK
(bool) option is enabled.

There are three types of requests distinguished by prefix "GET" (query for
information), "SET" (modify parameters) and "ACT" (perform an action). The
GET_* and SET_* messages are paired even if the data they deal with is read
only because SET_* message is also used for reply to corresponding GET_*
message. Notifications on parameter changes are also sent as SET_* messages
in the same format as reply to corresponding GET request.

Basic concepts:

- make extensions easier not only by allowing new attributes but also by
imposing as few artificial limits as possible, e.g. by using arbitrary
size bit sets for most bitmap attributes or by not using fixed size
strings
- use extack for error reporting and warnings
- send netlink notifications on changes (even if they were done using the
ioctl interfaces)
- avoid the racy read/modify/write cycle between kernel and userspace by
sending only attributes which userspace wants to change; there is still
a read/modify/write cycle between generic kernel code and ethtool_ops
handler in NIC driver but it is only in kernel and under a lock
- reduce the number of value lists that need to be kept in sync between
kernel and userspace (e.g. recognized link modes)
- where feasible, allow dump requests to query specific information for all
network devices
- as the lack of extensibility of the ioctl interface led to having too
many commands, group some of these together to one netlink message but
allow querying only part(s) of the information (using "info mask" bitmap)
and modifying only some of the parameters (by providing only some
attributes)
- as parsing and generating netlink messages is more complicated than
simply copying data structures between userspace API and ethtool_ops
handlers (which most ioctl commands do), split the code into multiple
files in net/ethtool directory; move net/core/ethtool.c also to this
directory and rename it to ioctl.c

The full (work in progress) series, together with the (userspace) ethtool
counterpart can be found at https://github.com/mkubecek/ethnl

Changes between v4 and v5:

- do not panic on failed initialization, only WARN()

Main changes between RFC v3 and v4:

- use more kerneldoc style comments
- strict attribute policy checking
- use macros for tables of link mode names and parameters
- provide permanent hardware address in rtnetlink
- coding style cleanup
- split too long patches, reorder
- wrap more ETHA_SETTINGS_* attributes in nests
- add also some SET_* implementation into submitted part

Main changes between RFC v2 and RFC v3:

- do not allow building as a module (no netdev notifiers needed)
- drop some obsolete fields
- add permanent hw address, timestamping and private flags support
- rework bitset handling to get rid of variable length arrays
- notify monitor on device renames
- restructure GET_SETTINGS/SET_SETTINGS messages
- split too long patches and submit only first part of the series

Main changes between RFC v1 and RFC v2:

- support dumps for all "get" requests
- provide notifications for changes related to supported request types
- support getting string sets (both global and per device)
- support getting/setting device features
- get rid of family specific header, everything passed as attributes
- split netlink code into multiple files in net/ethtool/ directory

ToDo / open questions:

- some features provided by ethtool would rather belong to devlink (and
some are already superseded by devlink); however, only few drivers
provide devlink interface at the moment and as recent discussion on
flashing revealed, we cannot rely on devlink's presence

- while the netlink interface allows easy future extensions, ethtool_ops
interface does not; some settings could be implemented using tunables and
accessed via relevant netlink messages (as well as tunables) from
userspace but in the long term, something better will be needed

- currently, all communication with drivers via ethtool_ops is done
under RTNL as this is what ioctl interface does and likely many
ethtool_ops handlers rely on that; if we are going to rework ethtool_ops
in the future ("phase two"), it would be nice to get rid of it

- ethtool_ops should pass extack pointer to allow drivers more meaningful
error reporting; it's not clear, however, how to pass information about
offending attribute

- notifications are sent whenever a change is done via netlink API or
ioctl API and for netdev features also whenever they are updated using
netdev_change_features(); it would be desirable to notify also about
link state and negotiation result (speed/duplex and partner link
modes) but it would be more tricky

Michal Kubecek (22):
rtnetlink: provide permanent hardware address in RTM_NEWLINK
netlink: introduce nla_put_bitfield32()
netlink: add strict version of nla_parse_nested()
ethtool: move to its own directory
ethtool: introduce ethtool netlink interface
ethtool: helper functions for netlink interface
ethtool: netlink bitset handling
ethtool: support for netlink notifications
ethtool: implement EVENT notifications
ethtool: generic handlers for GET requests
ethtool: move string arrays into common file
ethtool: provide string sets with GET_STRSET request
ethtool: provide driver/device information in GET_INFO request
ethtool: provide timestamping information in GET_INFO request
ethtool: provide link mode names as a string set
ethtool: provide link settings and link modes in GET_SETTINGS request
ethtool: set link settings and link modes with SET_SETTINGS request
ethtool: provide link state in GET_SETTINGS request
ethtool: provide WoL information in GET_SETTINGS request
ethtool: set WoL settings with SET_SETTINGS request
ethtool: provide message level in GET_SETTINGS request
ethtool: set message level with SET_SETTINGS request

Documentation/networking/ethtool-netlink.txt | 458 +++++++++++
include/linux/ethtool.h | 4 +
include/linux/ethtool_netlink.h | 17 +
include/linux/netdevice.h | 14 +
include/net/netlink.h | 24 +
include/uapi/linux/ethtool.h | 10 +
include/uapi/linux/ethtool_netlink.h | 274 ++++++
include/uapi/linux/if_link.h | 1 +
include/uapi/linux/net_tstamp.h | 13 +
net/Kconfig | 8 +
net/Makefile | 2 +-
net/core/Makefile | 2 +-
net/core/rtnetlink.c | 4 +
net/ethtool/Makefile | 7 +
net/ethtool/bitset.c | 597 ++++++++++++++
net/ethtool/bitset.h | 40 +
net/ethtool/common.c | 225 +++++
net/ethtool/common.h | 27 +
net/ethtool/info.c | 296 +++++++
net/{core/ethtool.c => ethtool/ioctl.c} | 237 +-----
net/ethtool/netlink.c | 708 ++++++++++++++++
net/ethtool/netlink.h | 296 +++++++
net/ethtool/settings.c | 823 +++++++++++++++++++
net/ethtool/strset.c | 471 +++++++++++
24 files changed, 4355 insertions(+), 203 deletions(-)
create mode 100644 Documentation/networking/ethtool-netlink.txt
create mode 100644 include/linux/ethtool_netlink.h
create mode 100644 include/uapi/linux/ethtool_netlink.h
create mode 100644 net/ethtool/Makefile
create mode 100644 net/ethtool/bitset.c
create mode 100644 net/ethtool/bitset.h
create mode 100644 net/ethtool/common.c
create mode 100644 net/ethtool/common.h
create mode 100644 net/ethtool/info.c
rename net/{core/ethtool.c => ethtool/ioctl.c} (91%)
create mode 100644 net/ethtool/netlink.c
create mode 100644 net/ethtool/netlink.h
create mode 100644 net/ethtool/settings.c
create mode 100644 net/ethtool/strset.c

--
2.21.0



2019-03-25 17:09:13

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 03/22] netlink: add strict version of nla_parse_nested()

Similar to nla_parse_strict() and nlmsg_parse_strict(), add also
nla_parse_nested_strict() as a version of nla_parse_nested() with strict
policy checking.

Signed-off-by: Michal Kubecek <[email protected]>
---
include/net/netlink.h | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index bc0497076bec..723139637ba4 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -913,6 +913,15 @@ static inline int nla_parse_nested(struct nlattr *tb[], int maxtype,
extack);
}

+static inline int nla_parse_nested_strict(struct nlattr *tb[], int maxtype,
+ const struct nlattr *nla,
+ const struct nla_policy *policy,
+ struct netlink_ext_ack *extack)
+{
+ return nla_parse_strict(tb, maxtype, nla_data(nla), nla_len(nla),
+ policy, extack);
+}
+
/**
* nla_put_u8 - Add a u8 netlink attribute to a socket buffer
* @skb: socket buffer to add attribute to
--
2.21.0


2019-03-25 17:09:22

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 04/22] ethtool: move to its own directory

The ethtool netlink interface is going to be split into multiple files so
that it will be more convenient to put all of them in a separate directory
net/ethtool. Start by moving current ethtool.c with ioctl interface into
this directory and renaming it to ioctl.c.

Signed-off-by: Michal Kubecek <[email protected]>
---
net/Makefile | 2 +-
net/core/Makefile | 2 +-
net/ethtool/Makefile | 3 +++
net/{core/ethtool.c => ethtool/ioctl.c} | 0
4 files changed, 5 insertions(+), 2 deletions(-)
create mode 100644 net/ethtool/Makefile
rename net/{core/ethtool.c => ethtool/ioctl.c} (100%)

diff --git a/net/Makefile b/net/Makefile
index 449fc0b221f8..848303d98d3d 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_NET) += $(tmp-y)

# LLC has to be linked before the files in net/802/
obj-$(CONFIG_LLC) += llc/
-obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ bpf/
+obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ bpf/ ethtool/
obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_INET) += ipv4/
obj-$(CONFIG_TLS) += tls/
diff --git a/net/core/Makefile b/net/core/Makefile
index f97d6254e564..9ddc2f3ecd97 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -8,7 +8,7 @@ obj-y := sock.o request_sock.o skbuff.o datagram.o stream.o scm.o \

obj-$(CONFIG_SYSCTL) += sysctl_net_core.o

-obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
+obj-y += dev.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 xdp.o flow_offload.o
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
new file mode 100644
index 000000000000..3ebfab2bca66
--- /dev/null
+++ b/net/ethtool/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += ioctl.o
diff --git a/net/core/ethtool.c b/net/ethtool/ioctl.c
similarity index 100%
rename from net/core/ethtool.c
rename to net/ethtool/ioctl.c
--
2.21.0


2019-03-25 17:09:25

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Basic genetlink and init infrastructure for the netlink interface, register
genetlink family "ethtool". Introduce CONFIG_ETHTOOL_NETLINK Kconfig
option. Add interface description into Documentation/networking.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 168 +++++++++++++++++++
include/linux/ethtool_netlink.h | 9 +
include/uapi/linux/ethtool_netlink.h | 25 +++
net/Kconfig | 8 +
net/ethtool/Makefile | 6 +-
net/ethtool/netlink.c | 34 ++++
net/ethtool/netlink.h | 12 ++
7 files changed, 261 insertions(+), 1 deletion(-)
create mode 100644 Documentation/networking/ethtool-netlink.txt
create mode 100644 include/linux/ethtool_netlink.h
create mode 100644 include/uapi/linux/ethtool_netlink.h
create mode 100644 net/ethtool/netlink.c
create mode 100644 net/ethtool/netlink.h

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
new file mode 100644
index 000000000000..377d64c9b7fa
--- /dev/null
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -0,0 +1,168 @@
+ Netlink interface for ethtool
+ =============================
+
+
+Basic information
+-----------------
+
+Netlink interface for ethtool uses generic netlink family "ethtool" (userspace
+application should use macros ETHTOOL_GENL_NAME and ETHTOOL_GENL_VERSION
+defined in <linux/ethtool_netlink.h> uapi header). This family does not use
+a specific header, all information in requests and replies is passed using
+netlink attributes.
+
+In requests, device can be identified by ifindex or by name; if both are used,
+they must match. In replies, kernel fills both. The meaning of flags,
+info_mask and index fields depends on request type.
+
+The ethtool netlink interface uses extended ACK for error and warning
+reporting, userspace application developers are encouraged to make these
+messages available to user in a suitable way.
+
+Requests can be divided into three categories: "get" (retrieving information),
+"set" (setting parameters) and "action" (invoking an action).
+
+All "set" and "action" type requests require admin privileges (CAP_NET_ADMIN
+in the namespace). Most "get" type request are allowed for anyone but there
+are exceptions (where the response contains sensitive information). In some
+cases, the request as such is allowed for anyone but unprivileged users have
+attributes with sensitive information (e.g. wake-on-lan password) omitted.
+
+
+Conventions
+-----------
+
+Attributes which represent a boolean value usually use u8 type so that we can
+distinguish three states: "on", "off" and "not present" (meaning the
+information is not available in "get" requests or value is not to be changed
+in "set" requests). For these attributes, the "true" value should be passed as
+number 1 but any non-zero value should be understood as "true" by recipient.
+
+Some request types allow passing an attribute named ETHA_*_INFOMASK with
+a bitmask telling kernel that we are only interested in some parts of the
+information. If info mask is omitted, all available information is returned.
+Meaning of info mask bits depends on request type and is listed below.
+
+
+Device identification
+---------------------
+
+When appropriate, network device is identified by a nested attribute named
+ETHA_*_DEV. This attribute can contain
+
+ ETHA_DEV_INDEX (u32) device ifindex
+ ETHA_DEV_NAME (string) device name
+
+In device related requests, one of these is sufficient; if both are used, they
+must match (i.e. identify the same device). In device related replies both are
+provided by kernel. In dump requests, device is not specified and kernel
+replies with one message per network device (only those for which the request
+is supported).
+
+
+List of message types
+---------------------
+
+All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
+to indicate the type.
+
+Messages of type "get" are used by userspace to request information and
+usually do not contain any attributes (that may be added later for dump
+filtering). Kernel response is in the form of corresponding "set" message;
+the same message can be also used to set (some of) the parameters, except for
+messages marked as "response only" in the table above. "Get" messages with
+NLM_F_DUMP flags and no device identification dump the information for all
+devices supporting the request.
+
+Later sections describe the format and semantics of these request messages.
+
+
+Request translation
+-------------------
+
+The following table maps iosctl commands to netlink commands providing their
+functionality. Entries with "n/a" in right column are commands which do not
+have their netlink replacement yet.
+
+ioctl command netlink command
+---------------------------------------------------------------------
+ETHTOOL_GSET n/a
+ETHTOOL_SSET n/a
+ETHTOOL_GDRVINFO n/a
+ETHTOOL_GREGS n/a
+ETHTOOL_GWOL n/a
+ETHTOOL_SWOL n/a
+ETHTOOL_GMSGLVL n/a
+ETHTOOL_SMSGLVL n/a
+ETHTOOL_NWAY_RST n/a
+ETHTOOL_GLINK n/a
+ETHTOOL_GEEPROM n/a
+ETHTOOL_SEEPROM n/a
+ETHTOOL_GCOALESCE n/a
+ETHTOOL_SCOALESCE n/a
+ETHTOOL_GRINGPARAM n/a
+ETHTOOL_SRINGPARAM n/a
+ETHTOOL_GPAUSEPARAM n/a
+ETHTOOL_SPAUSEPARAM n/a
+ETHTOOL_GRXCSUM n/a
+ETHTOOL_SRXCSUM n/a
+ETHTOOL_GTXCSUM n/a
+ETHTOOL_STXCSUM n/a
+ETHTOOL_GSG n/a
+ETHTOOL_SSG n/a
+ETHTOOL_TEST n/a
+ETHTOOL_GSTRINGS n/a
+ETHTOOL_PHYS_ID n/a
+ETHTOOL_GSTATS n/a
+ETHTOOL_GTSO n/a
+ETHTOOL_STSO n/a
+ETHTOOL_GPERMADDR rtnetlink RTM_GETLINK
+ETHTOOL_GUFO n/a
+ETHTOOL_SUFO n/a
+ETHTOOL_GGSO n/a
+ETHTOOL_SGSO n/a
+ETHTOOL_GFLAGS n/a
+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_SGRO n/a
+ETHTOOL_GRXRINGS n/a
+ETHTOOL_GRXCLSRLCNT n/a
+ETHTOOL_GRXCLSRULE n/a
+ETHTOOL_GRXCLSRLALL n/a
+ETHTOOL_SRXCLSRLDEL n/a
+ETHTOOL_SRXCLSRLINS n/a
+ETHTOOL_FLASHDEV n/a
+ETHTOOL_RESET n/a
+ETHTOOL_SRXNTUPLE n/a
+ETHTOOL_GRXNTUPLE n/a
+ETHTOOL_GSSET_INFO n/a
+ETHTOOL_GRXFHINDIR n/a
+ETHTOOL_SRXFHINDIR n/a
+ETHTOOL_GFEATURES n/a
+ETHTOOL_SFEATURES n/a
+ETHTOOL_GCHANNELS n/a
+ETHTOOL_SCHANNELS n/a
+ETHTOOL_SET_DUMP n/a
+ETHTOOL_GET_DUMP_FLAG n/a
+ETHTOOL_GET_DUMP_DATA n/a
+ETHTOOL_GET_TS_INFO n/a
+ETHTOOL_GMODULEINFO n/a
+ETHTOOL_GMODULEEEPROM n/a
+ETHTOOL_GEEE n/a
+ETHTOOL_SEEE n/a
+ETHTOOL_GRSSH n/a
+ETHTOOL_SRSSH n/a
+ETHTOOL_GTUNABLE n/a
+ETHTOOL_STUNABLE n/a
+ETHTOOL_GPHYSTATS n/a
+ETHTOOL_PERQUEUE n/a
+ETHTOOL_GLINKSETTINGS n/a
+ETHTOOL_SLINKSETTINGS n/a
+ETHTOOL_PHY_GTUNABLE n/a
+ETHTOOL_PHY_STUNABLE n/a
+ETHTOOL_GFECPARAM n/a
+ETHTOOL_SFECPARAM n/a
diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h
new file mode 100644
index 000000000000..0412adb4f42f
--- /dev/null
+++ b/include/linux/ethtool_netlink.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _LINUX_ETHTOOL_NETLINK_H_
+#define _LINUX_ETHTOOL_NETLINK_H_
+
+#include <uapi/linux/ethtool_netlink.h>
+#include <linux/ethtool.h>
+
+#endif /* _LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
new file mode 100644
index 000000000000..6aa267451542
--- /dev/null
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool
+ *
+ * See Documentation/networking/ethtool-netlink.txt in kernel source tree for
+ * doucumentation of the interface.
+ */
+
+#ifndef _UAPI_LINUX_ETHTOOL_NETLINK_H_
+#define _UAPI_LINUX_ETHTOOL_NETLINK_H_
+
+#include <linux/ethtool.h>
+
+enum {
+ ETHNL_CMD_NOOP,
+
+ __ETHNL_CMD_CNT,
+ ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
+};
+
+/* generic netlink info */
+#define ETHTOOL_GENL_NAME "ethtool"
+#define ETHTOOL_GENL_VERSION 1
+
+#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/net/Kconfig b/net/Kconfig
index 3e8fdd688329..75c600b45775 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -448,6 +448,14 @@ config FAILOVER
migration of VMs with direct attached VFs by failing over to the
paravirtual datapath when the VF is unplugged.

+config ETHTOOL_NETLINK
+ bool "Netlink interface for ethtool"
+ default y
+ help
+ An alternative userspace interface for ethtool based on generic
+ netlink. It provides better extensibility and some new features,
+ e.g. notification messages.
+
endif # if NET

# Used by archs to tell that they support BPF JIT compiler plus which flavour.
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 3ebfab2bca66..f30e0da88be5 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -1,3 +1,7 @@
# SPDX-License-Identifier: GPL-2.0

-obj-y += ioctl.o
+obj-y += ioctl.o
+
+obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o
+
+ethtool_nl-y := netlink.o
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
new file mode 100644
index 000000000000..85dd6dac71a2
--- /dev/null
+++ b/net/ethtool/netlink.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+
+#include <linux/ethtool_netlink.h>
+#include "netlink.h"
+
+/* genetlink setup */
+
+static const struct genl_ops ethtool_genl_ops[] = {
+};
+
+struct genl_family ethtool_genl_family = {
+ .hdrsize = 0,
+ .name = ETHTOOL_GENL_NAME,
+ .version = ETHTOOL_GENL_VERSION,
+ .netnsok = true,
+ .parallel_ops = true,
+ .ops = ethtool_genl_ops,
+ .n_ops = ARRAY_SIZE(ethtool_genl_ops),
+};
+
+/* module setup */
+
+static int __init ethnl_init(void)
+{
+ int ret;
+
+ ret = genl_register_family(&ethtool_genl_family);
+ if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
+ return ret;
+
+ return 0;
+}
+
+subsys_initcall(ethnl_init);
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
new file mode 100644
index 000000000000..63063b582ca2
--- /dev/null
+++ b/net/ethtool/netlink.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _NET_ETHTOOL_NETLINK_H
+#define _NET_ETHTOOL_NETLINK_H
+
+#include <linux/ethtool_netlink.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+
+extern struct genl_family ethtool_genl_family;
+
+#endif /* _NET_ETHTOOL_NETLINK_H */
--
2.21.0


2019-03-25 17:09:34

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 06/22] ethtool: helper functions for netlink interface

Various helpers used by ethtool netlink code.

Signed-off-by: Michal Kubecek <[email protected]>
---
include/uapi/linux/ethtool_netlink.h | 11 ++
net/ethtool/netlink.c | 144 +++++++++++++++++++++++++++
net/ethtool/netlink.h | 144 +++++++++++++++++++++++++++
3 files changed, 299 insertions(+)

diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 6aa267451542..59240a2cda56 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -18,6 +18,17 @@ enum {
ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
};

+/* device specification */
+
+enum {
+ ETHA_DEV_UNSPEC,
+ ETHA_DEV_INDEX, /* u32 */
+ ETHA_DEV_NAME, /* string */
+
+ __ETHA_DEV_CNT,
+ ETHA_DEV_MAX = (__ETHA_DEV_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 85dd6dac71a2..cc6829ba4331 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -1,8 +1,152 @@
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note

+#include <net/sock.h>
#include <linux/ethtool_netlink.h>
#include "netlink.h"

+static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {
+ [ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_DEV_INDEX] = { .type = NLA_U32 },
+ [ETHA_DEV_NAME] = { .type = NLA_NUL_STRING,
+ .len = IFNAMSIZ - 1 },
+};
+
+/**
+ * ethnl_dev_get() - get device identified by nested attribute
+ * @info: genetlink info (also used for extack error reporting)
+ * @nest: nest attribute with device identification
+ *
+ * Finds the network device identified by ETHA_DEV_INDEX (ifindex) or
+ * ETHA_DEV_NAME (name) attributes in a nested attribute @nest. If both
+ * are supplied, they must identify the same device. If successful, takes
+ * a reference to the device which is to be released by caller.
+ *
+ * Return: pointer to the device if successful, ERR_PTR(err) on error
+ */
+struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest)
+{
+ struct net *net = genl_info_net(info);
+ struct nlattr *tb[ETHA_DEV_MAX + 1];
+ struct net_device *dev;
+ int ret;
+
+ if (!nest) {
+ ETHNL_SET_ERRMSG(info,
+ "mandatory device identification missing");
+ return ERR_PTR(-EINVAL);
+ }
+ ret = nla_parse_nested_strict(tb, ETHA_DEV_MAX, nest, dev_policy,
+ info->extack);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (tb[ETHA_DEV_INDEX]) {
+ dev = dev_get_by_index(net, nla_get_u32(tb[ETHA_DEV_INDEX]));
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+ /* if both ifindex and ifname are passed, they must match */
+ if (tb[ETHA_DEV_NAME]) {
+ const char *nl_name = nla_data(tb[ETHA_DEV_NAME]);
+
+ if (strncmp(dev->name, nl_name, IFNAMSIZ)) {
+ dev_put(dev);
+ ETHNL_SET_ERRMSG(info,
+ "ifindex and ifname do not match");
+ return ERR_PTR(-ENODEV);
+ }
+ }
+ return dev;
+ } else if (tb[ETHA_DEV_NAME]) {
+ dev = dev_get_by_name(net, nla_data(tb[ETHA_DEV_NAME]));
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+ } else {
+ ETHNL_SET_ERRMSG(info, "either ifindex or ifname required");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!netif_device_present(dev)) {
+ dev_put(dev);
+ ETHNL_SET_ERRMSG(info, "device not present");
+ return ERR_PTR(-ENODEV);
+ }
+ return dev;
+}
+
+/**
+ * ethnl_fill_dev() - Put device identification nest into a message
+ * @msg: skb with the message
+ * @dev: network device to describe
+ * @attrtype: attribute type to use for the nest
+ *
+ * Create a nested attribute with attributes describing given network device.
+ * Clean up on error.
+ *
+ * Return: 0 on success, error value (-EMSGSIZE only) on error
+ */
+int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype)
+{
+ struct nlattr *nest;
+ int ret = -EMSGSIZE;
+
+ nest = ethnl_nest_start(msg, attrtype);
+ if (!nest)
+ return -EMSGSIZE;
+
+ if (nla_put_u32(msg, ETHA_DEV_INDEX, (u32)dev->ifindex))
+ goto err;
+ if (nla_put_string(msg, ETHA_DEV_NAME, dev->name))
+ goto err;
+
+ nla_nest_end(msg, nest);
+ return 0;
+err:
+ nla_nest_cancel(msg, nest);
+ return ret;
+}
+
+/**
+ * ethnl_reply_init() - Create skb for a reply and fill device identification
+ * @payload: payload length (without netlink and genetlink header)
+ * @dev: device the reply is about (may be null)
+ * @cmd: ETHNL_CMD_* command for reply
+ * @info: genetlink info of the received packet we respond to
+ * @ehdrp: place to store payload pointer returned by genlmsg_new()
+ *
+ * Return: pointer to allocated skb on success, NULL on error
+ */
+struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
+ u16 dev_attrtype, struct genl_info *info,
+ void **ehdrp)
+{
+ struct sk_buff *rskb;
+ void *ehdr;
+
+ rskb = genlmsg_new(payload, GFP_KERNEL);
+ if (!rskb) {
+ ETHNL_SET_ERRMSG(info,
+ "failed to allocate reply message");
+ return NULL;
+ }
+
+ ehdr = genlmsg_put_reply(rskb, info, &ethtool_genl_family, 0, cmd);
+ if (!ehdr)
+ goto err;
+ if (ehdrp)
+ *ehdrp = ehdr;
+ if (dev) {
+ int ret = ethnl_fill_dev(rskb, dev, dev_attrtype);
+
+ if (ret < 0)
+ goto err;
+ }
+
+ return rskb;
+err:
+ nlmsg_free(rskb);
+ return NULL;
+}
+
/* genetlink setup */

static const struct genl_ops ethtool_genl_ops[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 63063b582ca2..db90d95410b1 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -6,7 +6,151 @@
#include <linux/ethtool_netlink.h>
#include <linux/netdevice.h>
#include <net/genetlink.h>
+#include <net/sock.h>
+
+#define ETHNL_SET_ERRMSG(info, msg) \
+ do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)

extern struct genl_family ethtool_genl_family;

+struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest);
+int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype);
+
+struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
+ u16 dev_attrtype, struct genl_info *info,
+ void **ehdrp);
+
+static inline int ethnl_str_size(const char *s)
+{
+ return nla_total_size(strlen(s) + 1);
+}
+
+static inline int ethnl_str_ifne_size(const char *s)
+{
+ return s[0] ? ethnl_str_size(s) : 0;
+}
+
+static inline int ethnl_put_str_ifne(struct sk_buff *skb, int attrtype,
+ const char *s)
+{
+ if (!s[0])
+ return 0;
+ return nla_put_string(skb, attrtype, s);
+}
+
+static inline struct nlattr *ethnl_nest_start(struct sk_buff *skb,
+ int attrtype)
+{
+ return nla_nest_start(skb, attrtype | NLA_F_NESTED);
+}
+
+static inline int ethnlmsg_parse(const struct nlmsghdr *nlh,
+ struct nlattr *tb[], int maxtype,
+ const struct nla_policy *policy,
+ struct genl_info *info)
+{
+ return nlmsg_parse_strict(nlh, GENL_HDRLEN, tb, maxtype, policy,
+ info ? info->extack : NULL);
+}
+
+/* ethnl_update_* return true if the value is changed */
+static inline bool ethnl_update_u32(u32 *dst, struct nlattr *attr)
+{
+ u32 val;
+
+ if (!attr)
+ return false;
+ val = nla_get_u32(attr);
+ if (*dst == val)
+ return false;
+
+ *dst = val;
+ return true;
+}
+
+static inline bool ethnl_update_u8(u8 *dst, struct nlattr *attr)
+{
+ u8 val;
+
+ if (!attr)
+ return false;
+ val = nla_get_u8(attr);
+ if (*dst == val)
+ return false;
+
+ *dst = val;
+ return true;
+}
+
+/* update u32 value used as bool from NLA_U8 */
+static inline bool ethnl_update_bool32(u32 *dst, struct nlattr *attr)
+{
+ u8 val;
+
+ if (!attr)
+ return false;
+ val = !!nla_get_u8(attr);
+ if (!!*dst == val)
+ return false;
+
+ *dst = val;
+ return true;
+}
+
+static inline bool ethnl_update_binary(u8 *dst, unsigned int len,
+ struct nlattr *attr)
+{
+ if (!attr)
+ return false;
+ if (nla_len(attr) < len)
+ len = nla_len(attr);
+ if (!memcmp(dst, nla_data(attr), len))
+ return false;
+
+ memcpy(dst, nla_data(attr), len);
+ return true;
+}
+
+static inline bool ethnl_update_bitfield32(u32 *dst, struct nlattr *attr)
+{
+ struct nla_bitfield32 change;
+ u32 newval;
+
+ if (!attr)
+ return false;
+ change = nla_get_bitfield32(attr);
+ newval = (*dst & ~change.selector) | (change.value & change.selector);
+ if (*dst == newval)
+ return false;
+
+ *dst = newval;
+ return true;
+}
+
+static inline void warn_partial_info(struct genl_info *info)
+{
+ ETHNL_SET_ERRMSG(info, "not all requested data could be retrieved");
+}
+
+/* Check user privileges explicitly to allow finer access control based on
+ * context of the request or hiding part of the information from unprivileged
+ * users
+ */
+static inline bool ethnl_is_privileged(struct sk_buff *skb)
+{
+ struct net *net = sock_net(skb->sk);
+
+ return netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN);
+}
+
+/* total size of ETHA_*_DEV nested attribute; this is an upper estimate so that
+ * we do not need to hold RTNL longer than necessary to prevent rename between
+ * estimating the size and composing the message
+ */
+static inline unsigned int dev_ident_size(void)
+{
+ return nla_total_size(nla_total_size(sizeof(u32)) +
+ nla_total_size(IFNAMSIZ));
+}
+
#endif /* _NET_ETHTOOL_NETLINK_H */
--
2.21.0


2019-03-25 17:09:37

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 07/22] ethtool: netlink bitset handling

Declare attribute type constants and add helper functions to generate and
parse arbitrary length bit sets.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 63 ++
include/uapi/linux/ethtool_netlink.h | 32 +
net/ethtool/Makefile | 2 +-
net/ethtool/bitset.c | 597 +++++++++++++++++++
net/ethtool/bitset.h | 40 ++
net/ethtool/netlink.h | 9 +
6 files changed, 742 insertions(+), 1 deletion(-)
create mode 100644 net/ethtool/bitset.c
create mode 100644 net/ethtool/bitset.h

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 377d64c9b7fa..e97218d820c0 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -60,6 +60,69 @@ replies with one message per network device (only those for which the request
is supported).


+Bit sets
+--------
+
+For short bitmaps of (reasonably) fixed length, standard NLA_BITFIELD32 type
+is used. For arbitrary length bitmaps, ethtool netlink uses a nested attribute
+with contents of one of two forms: compact (two binary bitmaps representing
+bit values and mask of affected bits) and bit-by-bit (list of bits identified
+by either index or name).
+
+Compact form: nested (bitset) atrribute contents:
+
+ ETHA_BITSET_LIST (flag) no mask, only a list
+ ETHA_BITSET_SIZE (u32) number of significant bits
+ ETHA_BITSET_VALUE (binary) bitmap of bit values
+ ETHA_BITSET_MASK (binary) bitmap of valid bits
+
+Value and mask must have length at least ETHA_BITSET_SIZE bits rounded up to
+a multiple of 32 bits. They consist of 32-bit words in host byte order, words
+ordered from least significant to most significant (i.e. the same way as
+bitmaps are passed with ioctl interface).
+
+For compact form, ETHA_BITSET_SIZE and ETHA_BITSET_VALUE are mandatory.
+Similar to BITFIELD32, a compact form bit set requests to set bits in the mask
+to 1 (if the bit is set in value) or 0 (if not) and preserve the rest. If
+ETHA_BITSET_LIST is present, there is no mask and bitset represents a simple
+list of bits.
+
+Kernel bit set length may differ from userspace length if older application is
+used on newer kernel or vice versa. If userspace bitmap is longer, an error is
+issued only if the request actually tries to set values of some bits not
+recognized by kernel.
+
+Bit-by-bit form: nested (bitset) attribute contents:
+
+ ETHA_BITSET_LIST (flag) no mask, only a list
+ ETHA_BITSET_SIZE (u32) number of significant bits (optional)
+ ETHA_BITSET_BITS (nested) array of bits
+ ETHA_BITSET_BIT
+ ETHA_BIT_INDEX (u32) bit index (0 for LSB)
+ ETHA_BIT_NAME (string) bit name
+ ETHA_BIT_VALUE (flag) present if bit is set
+ ETHA_BITSET_BIT
+ ...
+
+Bit size is optional for bit-by-bit form. ETHA_BITSET_BITS nest can only
+contain ETHA_BITS_BIT attributes but there can be an arbitrary number of them.
+A bit may be identified by its index or by its name. When used in requests,
+listed bits are set to 0 or 1 according to ETHA_BIT_VALUE, the rest is
+preserved. A request fails if index exceeds kernel bit length or if name is
+not recognized.
+
+When ETHA_BITSET_LIST flag is present, bitset is interpreted as a simple bit
+list. ETHA_BIT_VALUE attributes are not used in such case. Bit list represents
+a bitmap with listed bits set and the rest zero.
+
+In requests, application can use either form. Form used by kernel in reply is
+determined by a flag in flags field of request header. Semantics of value and
+mask depends on the attribute. General idea is that flags control request
+processing, info_mask control which parts of the information are returned in
+"get" request and index identifies a particular subcommand or an object to
+which the request applies.
+
+
List of message types
---------------------

diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 59240a2cda56..de18e076ed69 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -29,6 +29,38 @@ enum {
ETHA_DEV_MAX = (__ETHA_DEV_CNT - 1)
};

+/* bit sets */
+
+enum {
+ ETHA_BIT_UNSPEC,
+ ETHA_BIT_INDEX, /* u32 */
+ ETHA_BIT_NAME, /* string */
+ ETHA_BIT_VALUE, /* flag */
+
+ __ETHA_BIT_CNT,
+ ETHA_BIT_MAX = (__ETHA_BIT_CNT - 1)
+};
+
+enum {
+ ETHA_BITS_UNSPEC,
+ ETHA_BITS_BIT,
+
+ __ETHA_BITS_CNT,
+ ETHA_BITS_MAX = (__ETHA_BITS_CNT - 1)
+};
+
+enum {
+ ETHA_BITSET_UNSPEC,
+ ETHA_BITSET_LIST, /* flag */
+ ETHA_BITSET_SIZE, /* u32 */
+ ETHA_BITSET_BITS, /* nest - ETHA_BITS_* */
+ ETHA_BITSET_VALUE, /* binary */
+ ETHA_BITSET_MASK, /* binary */
+
+ __ETHA_BITSET_CNT,
+ ETHA_BITSET_MAX = (__ETHA_BITSET_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 f30e0da88be5..482fdb9380fa 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -4,4 +4,4 @@ obj-y += ioctl.o

obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o

-ethtool_nl-y := netlink.o
+ethtool_nl-y := netlink.o bitset.o
diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c
new file mode 100644
index 000000000000..28d27a93e51e
--- /dev/null
+++ b/net/ethtool/bitset.c
@@ -0,0 +1,597 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+
+#include <linux/ethtool_netlink.h>
+#include <linux/bitmap.h>
+#include "netlink.h"
+#include "bitset.h"
+
+static bool ethnl_test_bit(const void *val, unsigned int index, bool is_u32)
+{
+ if (!val)
+ return true;
+ else if (is_u32)
+ return ((const u32 *)val)[index / 32] & (1U << (index % 32));
+ else
+ return test_bit(index, val);
+}
+
+static void __bitmap_to_u32(u32 *dst, const void *src, unsigned int size,
+ bool is_u32)
+{
+ unsigned int full_words = size / 32;
+ const u32 *src32 = src;
+
+ if (!is_u32) {
+ bitmap_to_arr32(dst, src, size);
+ return;
+ }
+
+ memcpy(dst, src32, full_words * sizeof(u32));
+ if (size % 32 != 0)
+ dst[full_words] = src32[full_words] & ((1U << (size % 32)) - 1);
+}
+
+/* convert standard kernel bitmap (long sized words) to ethtool one (u32 words)
+ * bitmap_to_arr32() is not guaranteed to do "in place" conversion correctly;
+ * moreover, we can use the fact that the conversion is no-op except for 64-bit
+ * big endian architectures
+ */
+#if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN)
+void ethnl_bitmap_to_u32(unsigned long *bitmap, unsigned int nwords)
+{
+ u32 *dst = (u32 *)bitmap;
+ unsigned int i;
+
+ for (i = 0; i < nwords; i++) {
+ unsigned long tmp = READ_ONCE(bitmap[i]);
+
+ dst[2 * i] = tmp & 0xffffffff;
+ dst[2 * i + 1] = tmp >> 32;
+ }
+}
+#endif
+
+static const char *bit_name(const void *names, bool legacy, unsigned int idx)
+{
+ const char (*const legacy_names)[ETH_GSTRING_LEN] = names;
+ const char *const *simple_names = names;
+
+ return legacy ? legacy_names[idx] : simple_names[idx];
+}
+
+/* calculate size for a bitset attribute
+ * see ethnl_put_bitset() for arguments
+ */
+static int __ethnl_bitset_size(unsigned int size, const void *val,
+ const void *mask, const void *names,
+ unsigned int flags)
+{
+ const bool legacy = flags & ETHNL_BITSET_LEGACY_NAMES;
+ const bool compact = flags & ETHNL_BITSET_COMPACT;
+ const bool is_list = flags & ETHNL_BITSET_LIST;
+ const bool is_u32 = flags & ETHNL_BITSET_U32;
+ unsigned int nwords = DIV_ROUND_UP(size, 32);
+ unsigned int len = 0;
+
+ if (WARN_ON(!compact && !names))
+ return -EINVAL;
+ /* list flag */
+ if (flags & ETHNL_BITSET_LIST)
+ len += nla_total_size(sizeof(u32));
+ /* size */
+ len += nla_total_size(sizeof(u32));
+
+ if (compact) {
+ /* values, mask */
+ len += 2 * nla_total_size(nwords * sizeof(u32));
+ } else {
+ unsigned int bits_len = 0;
+ unsigned int bit_len, i;
+
+ for (i = 0; i < size; i++) {
+ const char *name = bit_name(names, legacy, i) ?: "";
+
+ if ((is_list || mask) &&
+ !ethnl_test_bit(is_list ? val : mask, i, is_u32))
+ continue;
+ /* index */
+ bit_len = nla_total_size(sizeof(u32));
+ /* name */
+ bit_len += ethnl_str_size(name);
+ /* value */
+ if (!is_list && ethnl_test_bit(val, i, is_u32))
+ bit_len += nla_total_size(0);
+
+ /* bit nest */
+ bits_len += nla_total_size(bit_len);
+ }
+ /* bits nest */
+ len += nla_total_size(bits_len);
+ }
+
+ /* outermost nest */
+ return nla_total_size(len);
+}
+
+int ethnl_bitset_size(unsigned int size, const unsigned long *val,
+ const unsigned long *mask, const void *names,
+ unsigned int flags)
+{
+ return __ethnl_bitset_size(size, val, mask, names,
+ flags & ~ETHNL_BITSET_U32);
+}
+
+int ethnl_bitset32_size(unsigned int size, const u32 *val, const u32 *mask,
+ const void *names, unsigned int flags)
+{
+ return __ethnl_bitset_size(size, val, mask, names,
+ flags | ETHNL_BITSET_U32);
+}
+
+/**
+ * __ethnl_put_bitset() - Put a bitset nest into a message
+ * @skb: skb with the message
+ * @attrtype: attribute type for the bitset nest
+ * @size: size of the set in bits
+ * @val: bitset values
+ * @mask: mask of valid bits; NULL is interpreted as "all bits"
+ * @names: bit names (only used for verbose format)
+ * @flags: combination of ETHNL_BITSET_* flags
+ *
+ * This is the actual implementation of putting a bitset nested attribute into
+ * a netlink message but callers are supposed to use either ethnl_put_bitset()
+ * for unsigned long based bitmaps or ethnl_put_bitset32() for u32 based ones.
+ * Cleans the nest up on error.
+ *
+ * Return: 0 on success, error value on error
+ */
+static int __ethnl_put_bitset(struct sk_buff *skb, int attrtype,
+ unsigned int size, const void *val,
+ const void *mask, const void *names,
+ unsigned int flags)
+{
+ const bool legacy = flags & ETHNL_BITSET_LEGACY_NAMES;
+ const bool compact = flags & ETHNL_BITSET_COMPACT;
+ const bool is_list = flags & ETHNL_BITSET_LIST;
+ const bool is_u32 = flags & ETHNL_BITSET_U32;
+ struct nlattr *nest;
+ struct nlattr *attr;
+ int ret;
+
+ if (WARN_ON(!compact && !names))
+ return -EINVAL;
+ nest = ethnl_nest_start(skb, attrtype);
+ if (!nest)
+ return -EMSGSIZE;
+
+ ret = -EMSGSIZE;
+ if (is_list && nla_put_flag(skb, ETHA_BITSET_LIST))
+ goto err;
+ if (nla_put_u32(skb, ETHA_BITSET_SIZE, size))
+ goto err;
+ if (compact) {
+ unsigned int bytesize = DIV_ROUND_UP(size, 32) * sizeof(u32);
+
+ attr = nla_reserve(skb, ETHA_BITSET_VALUE, bytesize);
+ if (!attr)
+ goto err;
+ __bitmap_to_u32(nla_data(attr), val, size, is_u32);
+ if (mask) {
+ attr = nla_reserve(skb, ETHA_BITSET_MASK, bytesize);
+ if (!attr)
+ goto err;
+ __bitmap_to_u32(nla_data(attr), mask, size, is_u32);
+ }
+ } else {
+ struct nlattr *bits;
+ unsigned int i;
+
+ bits = ethnl_nest_start(skb, ETHA_BITSET_BITS);
+ if (!bits)
+ goto err;
+ for (i = 0; i < size; i++) {
+ const char *name = bit_name(names, legacy, i) ?: "";
+
+ if ((is_list || mask) &&
+ !ethnl_test_bit(is_list ? val : mask, i, is_u32))
+ continue;
+ attr = ethnl_nest_start(skb, ETHA_BITS_BIT);
+ if (!attr ||
+ nla_put_u32(skb, ETHA_BIT_INDEX, i) ||
+ nla_put_string(skb, ETHA_BIT_NAME, name))
+ goto err;
+ if (!is_list && ethnl_test_bit(val, i, is_u32) &&
+ nla_put_flag(skb, ETHA_BIT_VALUE))
+ goto err;
+ nla_nest_end(skb, attr);
+ }
+ nla_nest_end(skb, bits);
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+err:
+ nla_nest_cancel(skb, nest);
+ return ret;
+}
+
+int ethnl_put_bitset(struct sk_buff *skb, int attrtype, unsigned int size,
+ const unsigned long *val, const unsigned long *mask,
+ const void *names, unsigned int flags)
+{
+ return __ethnl_put_bitset(skb, attrtype, size, val, mask, names,
+ flags & ~ETHNL_BITSET_U32);
+}
+
+int ethnl_put_bitset32(struct sk_buff *skb, int attrtype, unsigned int size,
+ const u32 *val, const u32 *mask, const void *names,
+ unsigned int flags)
+{
+ return __ethnl_put_bitset(skb, attrtype, size, val, mask, names,
+ flags | ETHNL_BITSET_U32);
+}
+
+static const struct nla_policy bitset_policy[ETHA_BITSET_MAX + 1] = {
+ [ETHA_BITSET_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_BITSET_LIST] = { .type = NLA_FLAG },
+ [ETHA_BITSET_SIZE] = { .type = NLA_U32 },
+ [ETHA_BITSET_BITS] = { .type = NLA_NESTED },
+ [ETHA_BITSET_VALUE] = { .type = NLA_BINARY },
+ [ETHA_BITSET_MASK] = { .type = NLA_BINARY },
+};
+
+static const struct nla_policy bit_policy[ETHA_BIT_MAX + 1] = {
+ [ETHA_BIT_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_BIT_INDEX] = { .type = NLA_U32 },
+ [ETHA_BIT_NAME] = { .type = NLA_NUL_STRING },
+ [ETHA_BIT_VALUE] = { .type = NLA_FLAG },
+};
+
+static int ethnl_name_to_idx(const void *names, bool legacy,
+ unsigned int n_names, const char *name,
+ unsigned int name_len)
+{
+ unsigned int i;
+
+ for (i = 0; i < n_names; i++) {
+ const char *bname = bit_name(names, legacy, i);
+
+ if (bname && !strncmp(bname, name, name_len) &&
+ strlen(bname) <= name_len)
+ return i;
+ }
+
+ return n_names;
+}
+
+static int ethnl_update_bit(unsigned long *bitmap, unsigned long *bitmask,
+ unsigned int nbits, const struct nlattr *bit_attr,
+ bool is_list, const void *names, bool legacy,
+ struct genl_info *info)
+{
+ struct nlattr *tb[ETHA_BIT_MAX + 1];
+ int ret, idx;
+
+ if (nla_type(bit_attr) != ETHA_BITS_BIT) {
+ ETHNL_SET_ERRMSG(info,
+ "ETHA_BITSET_BITS can contain only ETHA_BITS_BIT");
+ return genl_err_attr(info, -EINVAL, bit_attr);
+ }
+ ret = nla_parse_nested_strict(tb, ETHA_BIT_MAX, bit_attr, bit_policy,
+ info->extack);
+ if (ret < 0)
+ return ret;
+
+ if (tb[ETHA_BIT_INDEX]) {
+ const char *name;
+
+ idx = nla_get_u32(tb[ETHA_BIT_INDEX]);
+ if (idx >= nbits) {
+ ETHNL_SET_ERRMSG(info, "bit index too high");
+ return genl_err_attr(info, -EOPNOTSUPP,
+ tb[ETHA_BIT_INDEX]);
+ }
+ name = bit_name(names, legacy, idx);
+ if (tb[ETHA_BIT_NAME] && name &&
+ strncmp(nla_data(tb[ETHA_BIT_NAME]), name,
+ nla_len(tb[ETHA_BIT_NAME]))) {
+ ETHNL_SET_ERRMSG(info, "bit index and name mismatch");
+ return genl_err_attr(info, -EINVAL, bit_attr);
+ }
+ } else if (tb[ETHA_BIT_NAME]) {
+ idx = ethnl_name_to_idx(names, legacy, nbits,
+ nla_data(tb[ETHA_BIT_NAME]),
+ nla_len(tb[ETHA_BIT_NAME]));
+ if (idx >= nbits) {
+ ETHNL_SET_ERRMSG(info, "bit name not found");
+ return genl_err_attr(info, -EOPNOTSUPP,
+ tb[ETHA_BIT_NAME]);
+ }
+ } else {
+ ETHNL_SET_ERRMSG(info, "neither bit index nor name specified");
+ return genl_err_attr(info, -EINVAL, bit_attr);
+ }
+
+ if (is_list || tb[ETHA_BIT_VALUE])
+ set_bit(idx, bitmap);
+ else
+ clear_bit(idx, bitmap);
+ if (!is_list || bitmask)
+ set_bit(idx, bitmask);
+ return 0;
+}
+
+int ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact)
+{
+ struct nlattr *tb[ETHA_BITSET_MAX + 1];
+ int ret;
+
+ ret = nla_parse_nested_strict(tb, ETHA_BITSET_MAX, bitset,
+ bitset_policy, NULL);
+ if (ret < 0)
+ return ret;
+
+ if (tb[ETHA_BITSET_BITS]) {
+ if (tb[ETHA_BITSET_VALUE] || tb[ETHA_BITSET_MASK])
+ return -EINVAL;
+ *compact = false;
+ return 0;
+ }
+ if (!tb[ETHA_BITSET_SIZE] || !tb[ETHA_BITSET_VALUE])
+ return -EINVAL;
+
+ *compact = true;
+ return 0;
+}
+
+/* 64-bit long endian is the only case when u32 based bitmap and unsigned long
+ * based bitmap layouts differ
+ */
+#if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN)
+/* dst &= src */
+static void __bitmap_and_u32(unsigned long *dst, const u32 *src,
+ unsigned int nbits)
+{
+ unsigned long op;
+
+ while (nbits >= BITS_PER_LONG) {
+ op = src[0] | ((unsigned long)src[1] << 32);
+ *dst &= op;
+
+ dst++;
+ src += 2;
+ nbits -= BITS_PER_LONG;
+ }
+
+ if (!nbits)
+ return;
+ op = src[0];
+ if (nbits > 32)
+ op |= ((unsigned long)src[1] << 32);
+ *dst = (op & BITMAP_LAST_WORD_MASK(nbits));
+}
+
+/* map1 == map2 */
+static bool __bitmap_equal_u32(const unsigned long *map1, const u32 *map2,
+ unsigned int nbits)
+{
+ unsigned long dword;
+
+ while (nbits >= BITS_PER_LONG) {
+ dword = map2[0] | ((unsigned long)map2[1] << 32);
+ if (*map1 != dword)
+ return false;
+
+ map1++;
+ map2 += 2;
+ nbits -= BITS_PER_LONG;
+ }
+
+ if (!nbits)
+ return true;
+ dword = map2[0];
+ if (nbits > 32)
+ dword |= ((unsigned long)map2[1] << 32);
+ return !((*map1 ^ dword) & BITMAP_LAST_WORD_MASK(nbits));
+}
+#else
+/* On 32-bit and 64-bit LE, unsigned long and u32 bitmap layout is the same
+ * but we must not write past dst buffer if the number of words is odd.
+ */
+static void __bitmap_and_u32(unsigned long *dst, const u32 *src,
+ unsigned int nbits)
+{
+ u32 *dst32 = (u32 *)dst;
+
+ while (nbits >= 32) {
+ *dst32++ &= *src++;
+ nbits -= 32;
+ }
+ if (!nbits)
+ return;
+ *dst32 &= (*src & ((1U << nbits) - 1));
+}
+
+static bool __bitmap_equal_u32(const unsigned long *map1, const u32 *map2,
+ unsigned int nbits)
+{
+ unsigned int full_words = nbits / 32;
+ u32 last_word_mask;
+ u32 *map1_32 = (u32 *)map1;
+
+ if (memcmp(map1, map2, full_words * BITS_PER_BYTE))
+ return false;
+ if (!(nbits % 32))
+ return true;
+ last_word_mask = (1U << (nbits % 32)) - 1;
+ return !((map1_32[full_words] ^ map2[full_words]) & last_word_mask);
+}
+#endif
+
+/* copy unsigned long bitmap to unsigned long or u32 */
+static void __bitmap_to_any(void *dst, const unsigned long *src,
+ unsigned int nbits, bool dst_is_u32)
+{
+ if (dst_is_u32)
+ bitmap_to_arr32(dst, src, nbits);
+ else
+ bitmap_copy(dst, src, nbits);
+}
+
+static bool __bitmap_equal_any(const unsigned long *map1, const void *map2,
+ unsigned int nbits, bool is_u32)
+{
+ if (!is_u32)
+ return bitmap_equal(map1, map2, nbits);
+ else
+ return __bitmap_equal_u32(map1, map2, nbits);
+}
+
+/**
+ * __ethnl_update_bitset() - Apply a bitset nest to a bitmap
+ * @bitmap: bitmap to update
+ * @bitmask: if not, mask from the nest is copied here
+ * @nbits: size of the updated bitmap in bits
+ * @attr: nest attribute to parse and apply
+ * @err: pointer to variable to put error value (or 0 on success) to
+ * @names: array of bit names; may be null for compact format
+ * @legacy: true if @names is ioctl style array of char[32], false if it is
+ * a simple array of (char *) strings
+ * @info: genetlink info (also used for extack error reporting)
+ * @is_u32: true: bitmaps are unsigned long based, false: u32 based bitmaps
+ *
+ * This is the actual implementation of bitset nested attribute parser but
+ * callers are supposed to use ethnl_update_bitset() for unsigned long based
+ * bitmaps or ethnl_update_bitset32() for u32 based ones.
+ *
+ * Return: true if the bitmap contents was modified, false if not
+ */
+static bool __ethnl_update_bitset(void *bitmap, void *bitmask,
+ unsigned int nbits, const struct nlattr *attr,
+ int *err, const void *names, bool legacy,
+ struct genl_info *info, bool is_u32)
+{
+ struct nlattr *tb[ETHA_BITSET_MAX + 1];
+ unsigned int change_bits = 0;
+ unsigned int max_bits = 0;
+ unsigned long *val, *mask;
+ bool mod = false;
+ bool is_list;
+
+ *err = 0;
+ if (!attr)
+ return mod;
+ *err = nla_parse_nested_strict(tb, ETHA_BITSET_MAX, attr, bitset_policy,
+ info->extack);
+ if (*err < 0)
+ return mod;
+ *err = -EINVAL;
+ if (tb[ETHA_BITSET_BITS] &&
+ (tb[ETHA_BITSET_VALUE] || tb[ETHA_BITSET_MASK]))
+ return mod;
+ if (!tb[ETHA_BITSET_BITS] &&
+ (!tb[ETHA_BITSET_SIZE] || !tb[ETHA_BITSET_VALUE]))
+ return mod;
+ is_list = (tb[ETHA_BITSET_LIST] != NULL);
+ if (is_list && tb[ETHA_BITSET_MASK])
+ return mod;
+
+ /* To let new userspace to work with old kernel, we allow bitmaps
+ * from userspace to be longer than kernel ones and only issue an
+ * error if userspace actually tries to change a bit not existing
+ * in kernel.
+ */
+ if (tb[ETHA_BITSET_SIZE])
+ change_bits = nla_get_u32(tb[ETHA_BITSET_SIZE]);
+ max_bits = max_t(unsigned int, nbits, change_bits);
+ mask = bitmap_zalloc(max_bits, GFP_KERNEL);
+ val = bitmap_zalloc(max_bits, GFP_KERNEL);
+
+ if (tb[ETHA_BITSET_BITS]) {
+ struct nlattr *bit_attr;
+ int rem;
+
+ if (is_list)
+ bitmap_fill(mask, nbits);
+ else if (is_u32)
+ bitmap_from_arr32(val, bitmap, nbits);
+ else
+ bitmap_copy(val, bitmap, nbits);
+ nla_for_each_nested(bit_attr, tb[ETHA_BITSET_BITS], rem) {
+ *err = ethnl_update_bit(val, mask, nbits, bit_attr,
+ is_list, names, legacy, info);
+ if (*err < 0)
+ goto out_free;
+ }
+ if (bitmask)
+ __bitmap_to_any(bitmask, mask, nbits, is_u32);
+ } else {
+ unsigned int change_words = DIV_ROUND_UP(change_bits, 32);
+
+ *err = 0;
+ if (change_bits == 0 && tb[ETHA_BITSET_MASK])
+ goto out_free;
+ *err = -EINVAL;
+ if (nla_len(tb[ETHA_BITSET_VALUE]) < change_words * sizeof(u32))
+ goto out_free;
+ if (tb[ETHA_BITSET_MASK] &&
+ nla_len(tb[ETHA_BITSET_MASK]) < change_words * sizeof(u32))
+ goto out_free;
+
+ bitmap_from_arr32(val, nla_data(tb[ETHA_BITSET_VALUE]),
+ change_bits);
+ if (tb[ETHA_BITSET_MASK])
+ bitmap_from_arr32(mask, nla_data(tb[ETHA_BITSET_MASK]),
+ change_bits);
+ else
+ bitmap_fill(mask, nbits);
+
+ if (nbits < change_bits) {
+ unsigned int idx = find_next_bit(mask, max_bits, nbits);
+
+ *err = -EINVAL;
+ if (idx < max_bits)
+ goto out_free;
+ }
+
+ if (bitmask)
+ __bitmap_to_any(bitmask, mask, nbits, is_u32);
+ if (!is_list) {
+ bitmap_and(val, val, mask, nbits);
+ bitmap_complement(mask, mask, nbits);
+ if (is_u32)
+ __bitmap_and_u32(mask, bitmap, nbits);
+ else
+ bitmap_and(mask, mask, bitmap, nbits);
+ bitmap_or(val, val, mask, nbits);
+ }
+ }
+
+ mod = !__bitmap_equal_any(val, bitmap, nbits, is_u32);
+ if (mod)
+ __bitmap_to_any(bitmap, val, nbits, is_u32);
+
+ *err = 0;
+out_free:
+ bitmap_free(val);
+ bitmap_free(mask);
+ return mod;
+}
+
+bool ethnl_update_bitset(unsigned long *bitmap, unsigned long *bitmask,
+ unsigned int nbits, const struct nlattr *attr,
+ int *err, const void *names, bool legacy,
+ struct genl_info *info)
+{
+ return __ethnl_update_bitset(bitmap, bitmask, nbits, attr, err, names,
+ legacy, info, false);
+}
+
+bool ethnl_update_bitset32(u32 *bitmap, u32 *bitmask, unsigned int nbits,
+ const struct nlattr *attr, int *err,
+ const void *names, bool legacy,
+ struct genl_info *info)
+{
+ return __ethnl_update_bitset(bitmap, bitmask, nbits, attr, err, names,
+ legacy, info, true);
+}
diff --git a/net/ethtool/bitset.h b/net/ethtool/bitset.h
new file mode 100644
index 000000000000..761d0c47fe23
--- /dev/null
+++ b/net/ethtool/bitset.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _NET_ETHTOOL_BITSET_H
+#define _NET_ETHTOOL_BITSET_H
+
+/* when set, value and mask bitmaps are arrays of u32, when not, arrays of
+ * unsigned long
+ */
+#define ETHNL_BITSET_U32 BIT(0)
+/* generate a compact format bitset */
+#define ETHNL_BITSET_COMPACT BIT(1)
+/* generate a bit list */
+#define ETHNL_BITSET_LIST BIT(2)
+/* when set, names are interpreted as legacy string set (an array of
+ * char[ETH_GSTRING_LEN]), when not, as a simple array of char *
+ */
+#define ETHNL_BITSET_LEGACY_NAMES BIT(3)
+
+int ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact);
+int ethnl_bitset_size(unsigned int size, const unsigned long *val,
+ const unsigned long *mask, const void *names,
+ unsigned int flags);
+int ethnl_bitset32_size(unsigned int size, const u32 *val, const u32 *mask,
+ const void *names, unsigned int flags);
+int ethnl_put_bitset(struct sk_buff *skb, int attrtype, unsigned int size,
+ const unsigned long *val, const unsigned long *mask,
+ const void *names, unsigned int flags);
+int ethnl_put_bitset32(struct sk_buff *skb, int attrtype, unsigned int size,
+ const u32 *val, const u32 *mask, const void *names,
+ unsigned int flags);
+bool ethnl_update_bitset(unsigned long *bitmap, unsigned long *bitmask,
+ unsigned int nbits, const struct nlattr *attr,
+ int *err, const void *names, bool legacy,
+ struct genl_info *info);
+bool ethnl_update_bitset32(u32 *bitmap, u32 *bitmask, unsigned int nbits,
+ const struct nlattr *attr, int *err,
+ const void *names, bool legacy,
+ struct genl_info *info);
+
+#endif /* _NET_ETHTOOL_BITSET_H */
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index db90d95410b1..b8a6cd3dc3e3 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -20,6 +20,15 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
u16 dev_attrtype, struct genl_info *info,
void **ehdrp);

+#if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN)
+void ethnl_bitmap_to_u32(unsigned long *bitmap, unsigned int nwords);
+#else
+static inline void ethnl_bitmap_to_u32(unsigned long *bitmap,
+ unsigned int nwords)
+{
+}
+#endif
+
static inline int ethnl_str_size(const char *s)
{
return nla_total_size(strlen(s) + 1);
--
2.21.0


2019-03-25 17:10:12

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 16/22] ethtool: provide link settings and link modes in GET_SETTINGS request

Implement GET_SETTINGS netlink request to get link settings and link mode
information provided by ETHTOOL_GLINKSETTINGS ioctl command.

The information in SET_SETTINGS message sent as reply is divided into two
parts: autonegotiation, speed, duplex and supported, advertised and peer
advertised link modes when ETH_SETTINGS_IM_LINKMODES flag is set in the
request and other settings when ETH_SETTINGS_IM_LINKINFO is set.

Send notification in the same format as the reply message when relevant
fields are modified using the ioctl interface (ETHTOOL_SLINKSETTINGS or
ETHTOOL_SSET command).

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 50 +++-
include/linux/ethtool_netlink.h | 3 +
include/uapi/linux/ethtool_netlink.h | 46 +++
net/ethtool/Makefile | 2 +-
net/ethtool/common.c | 48 ++++
net/ethtool/common.h | 4 +
net/ethtool/ioctl.c | 64 +----
net/ethtool/netlink.c | 9 +
net/ethtool/netlink.h | 1 +
net/ethtool/settings.c | 277 +++++++++++++++++++
10 files changed, 451 insertions(+), 53 deletions(-)
create mode 100644 net/ethtool/settings.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 74c62f11810c..d723b3537c46 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -131,6 +131,8 @@ List of message types
ETHNL_CMD_SET_STRSET response only
ETHNL_CMD_GET_INFO
ETHNL_CMD_SET_INFO response only
+ ETHNL_CMD_GET_SETTINGS
+ ETHNL_CMD_SET_SETTINGS response only (for now)

All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
to indicate the type.
@@ -258,6 +260,50 @@ if no PHC is associated, the attribute is not present.
GET_INFO requests allow dumps.


+GET_SETTINGS
+------------
+
+GET_SETTINGS request retrieves information provided by ETHTOOL_GLINKSETTINGS,
+ETHTOOL_GWOL, ETHTOOL_GMSGLVL and ETHTOOL_GLINK ioctl requests. The request
+doesn't use any attributes.
+
+Request attributes:
+
+ ETHA_SETTINGS_DEV (nested) device identification
+ ETHA_SETTINGS_INFOMASK (u32) info mask
+ ETHA_SETTINGS_COMPACT (flag) request compact bitsets
+
+Info mask bits meaning:
+
+ ETH_SETTINGS_IM_LINKINFO link settings
+ ETH_SETTINGS_IM_LINKMODES link modes and related
+
+Response contents:
+
+ ETHA_SETTINGS_DEV (nested) device identification
+ ETHA_SETTINGS_LINK_INFO (nested) link settings
+ ETHA_LINKINFO_PORT (u8) physical port
+ ETHA_LINKINFO_PHYADDR (u8) MDIO address of phy
+ ETHA_LINKINFO_TP_MDIX (u8) MDI(-X) status
+ ETHA_LINKINFO_TP_MDIX_CTRL (u8) MDI(-X) control
+ ETHA_LINKINFO_TRANSCEIVER (u8) transceiver
+ ETHA_SETTINGS_LINK_MODES (nested) link modes
+ ETHA_LINKMODES_AUTONEG (u8) autoneotiation status
+ ETHA_LINKMODES_OURS (bitset) advertised link modes
+ ETHA_LINKMODES_PEER (bitset) partner link modes
+ ETHA_LINKMODES_SPEED (u32) link speed (Mb/s)
+ ETHA_LINKMODES_DUPLEX (u8) duplex mode
+
+Most of the attributes and their values have the same meaning as matching
+members of the corresponding ioctl structures. For ETHA_LINKMODES_OURS,
+value represents advertised modes and mask represents supported modes.
+ETHA_LINKMODES_PEER in the reply is a bit list.
+
+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.
+
+
Request translation
-------------------

@@ -267,7 +313,7 @@ have their netlink replacement yet.

ioctl command netlink command
---------------------------------------------------------------------
-ETHTOOL_GSET n/a
+ETHTOOL_GSET ETHNL_CMD_GET_SETTINGS
ETHTOOL_SSET n/a
ETHTOOL_GDRVINFO ETHNL_CMD_GET_INFO
ETHTOOL_GREGS n/a
@@ -341,7 +387,7 @@ ETHTOOL_GTUNABLE n/a
ETHTOOL_STUNABLE n/a
ETHTOOL_GPHYSTATS n/a
ETHTOOL_PERQUEUE n/a
-ETHTOOL_GLINKSETTINGS n/a
+ETHTOOL_GLINKSETTINGS ETHNL_CMD_GET_SETTINGS
ETHTOOL_SLINKSETTINGS n/a
ETHTOOL_PHY_GTUNABLE n/a
ETHTOOL_PHY_STUNABLE n/a
diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h
index 2a15e64a16f3..e770e6e9acca 100644
--- a/include/linux/ethtool_netlink.h
+++ b/include/linux/ethtool_netlink.h
@@ -7,6 +7,9 @@
#include <linux/ethtool.h>
#include <linux/netdevice.h>

+#define __ETHTOOL_LINK_MODE_MASK_NWORDS \
+ DIV_ROUND_UP(__ETHTOOL_LINK_MODE_MASK_NBITS, 32)
+
enum ethtool_multicast_groups {
ETHNL_MCGRP_MONITOR,
};
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index e7a6e813150b..895ba9cd8924 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -18,6 +18,8 @@ enum {
ETHNL_CMD_SET_STRSET, /* only for reply */
ETHNL_CMD_GET_INFO,
ETHNL_CMD_SET_INFO, /* only for reply */
+ ETHNL_CMD_GET_SETTINGS,
+ ETHNL_CMD_SET_SETTINGS,

__ETHNL_CMD_CNT,
ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
@@ -185,6 +187,50 @@ enum {
ETHA_TSINFO_MAX = (__ETHA_TSINFO_CNT - 1)
};

+/* GET_SETTINGS / SET_SETTINGS */
+
+enum {
+ ETHA_SETTINGS_UNSPEC,
+ ETHA_SETTINGS_DEV, /* nest - ETHA_DEV_* */
+ ETHA_SETTINGS_INFOMASK, /* u32 */
+ ETHA_SETTINGS_COMPACT, /* flag */
+ ETHA_SETTINGS_LINK_INFO, /* nest - ETHA_LINKINFO_* */
+ ETHA_SETTINGS_LINK_MODES, /* nest - ETHA_LINKMODES_* */
+
+ __ETHA_SETTINGS_CNT,
+ ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
+};
+
+#define ETH_SETTINGS_IM_LINKINFO (1U << 0)
+#define ETH_SETTINGS_IM_LINKMODES (1U << 1)
+
+#define ETH_SETTINGS_IM_ALL (ETH_SETTINGS_IM_LINKINFO | \
+ ETH_SETTINGS_IM_LINKMODES)
+
+enum {
+ ETHA_LINKINFO_UNSPEC,
+ ETHA_LINKINFO_PORT, /* u8 */
+ ETHA_LINKINFO_PHYADDR, /* u8 */
+ ETHA_LINKINFO_TP_MDIX, /* u8 */
+ ETHA_LINKINFO_TP_MDIX_CTRL, /* u8 */
+ ETHA_LINKINFO_TRANSCEIVER, /* u8 */
+
+ __ETHA_LINKINFO_CNT,
+ ETHA_LINKINFO_MAX = (__ETHA_LINKINFO_CNT - 1)
+};
+
+enum {
+ ETHA_LINKMODES_UNSPEC,
+ ETHA_LINKMODES_AUTONEG, /* u8 */
+ ETHA_LINKMODES_OURS, /* bitset */
+ ETHA_LINKMODES_PEER, /* bitset */
+ ETHA_LINKMODES_SPEED, /* u32 */
+ ETHA_LINKMODES_DUPLEX, /* u8 */
+
+ __ETHA_LINKMODES_CNT,
+ ETHA_LINKMODES_MAX = (__ETHA_LINKMODES_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 96d41dc45d4f..6a7a182e1568 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 info.o
+ethtool_nl-y := netlink.o bitset.o strset.o info.o settings.o
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 7e846f4151f9..a91a4f00d275 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -157,3 +157,51 @@ int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)

return err;
}
+
+/* return false if legacy contained non-0 deprecated fields
+ * maxtxpkt/maxrxpkt. rest of ksettings always updated
+ */
+bool
+convert_legacy_settings_to_link_ksettings(
+ struct ethtool_link_ksettings *link_ksettings,
+ const struct ethtool_cmd *legacy_settings)
+{
+ bool retval = true;
+
+ memset(link_ksettings, 0, sizeof(*link_ksettings));
+
+ /* This is used to tell users that driver is still using these
+ * deprecated legacy fields, and they should not use
+ * %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS
+ */
+ if (legacy_settings->maxtxpkt ||
+ legacy_settings->maxrxpkt)
+ retval = false;
+
+ ethtool_convert_legacy_u32_to_link_mode(
+ link_ksettings->link_modes.supported,
+ legacy_settings->supported);
+ ethtool_convert_legacy_u32_to_link_mode(
+ link_ksettings->link_modes.advertising,
+ legacy_settings->advertising);
+ ethtool_convert_legacy_u32_to_link_mode(
+ link_ksettings->link_modes.lp_advertising,
+ legacy_settings->lp_advertising);
+ link_ksettings->base.speed
+ = ethtool_cmd_speed(legacy_settings);
+ link_ksettings->base.duplex
+ = legacy_settings->duplex;
+ link_ksettings->base.port
+ = legacy_settings->port;
+ link_ksettings->base.phy_address
+ = legacy_settings->phy_address;
+ link_ksettings->base.autoneg
+ = legacy_settings->autoneg;
+ link_ksettings->base.mdio_support
+ = legacy_settings->mdio_support;
+ link_ksettings->base.eth_tp_mdix
+ = legacy_settings->eth_tp_mdix;
+ link_ksettings->base.eth_tp_mdix_ctrl
+ = legacy_settings->eth_tp_mdix_ctrl;
+ return retval;
+}
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index 02cbee79da35..7a3e0b10e69a 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -18,4 +18,8 @@ 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);

+bool convert_legacy_settings_to_link_ksettings(
+ struct ethtool_link_ksettings *link_ksettings,
+ const struct ethtool_cmd *legacy_settings);
+
#endif /* _ETHTOOL_COMMON_H */
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 18721b5aa353..be3e34ac8cc7 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -30,6 +30,7 @@
#include <net/devlink.h>
#include <net/xdp_sock.h>
#include <net/flow_offload.h>
+#include <linux/ethtool_netlink.h>

#include "common.h"

@@ -356,54 +357,6 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
}
EXPORT_SYMBOL(ethtool_convert_link_mode_to_legacy_u32);

-/* return false if legacy contained non-0 deprecated fields
- * maxtxpkt/maxrxpkt. rest of ksettings always updated
- */
-static bool
-convert_legacy_settings_to_link_ksettings(
- struct ethtool_link_ksettings *link_ksettings,
- const struct ethtool_cmd *legacy_settings)
-{
- bool retval = true;
-
- memset(link_ksettings, 0, sizeof(*link_ksettings));
-
- /* This is used to tell users that driver is still using these
- * deprecated legacy fields, and they should not use
- * %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS
- */
- if (legacy_settings->maxtxpkt ||
- legacy_settings->maxrxpkt)
- retval = false;
-
- ethtool_convert_legacy_u32_to_link_mode(
- link_ksettings->link_modes.supported,
- legacy_settings->supported);
- ethtool_convert_legacy_u32_to_link_mode(
- link_ksettings->link_modes.advertising,
- legacy_settings->advertising);
- ethtool_convert_legacy_u32_to_link_mode(
- link_ksettings->link_modes.lp_advertising,
- legacy_settings->lp_advertising);
- link_ksettings->base.speed
- = ethtool_cmd_speed(legacy_settings);
- link_ksettings->base.duplex
- = legacy_settings->duplex;
- link_ksettings->base.port
- = legacy_settings->port;
- link_ksettings->base.phy_address
- = legacy_settings->phy_address;
- link_ksettings->base.autoneg
- = legacy_settings->autoneg;
- link_ksettings->base.mdio_support
- = legacy_settings->mdio_support;
- link_ksettings->base.eth_tp_mdix
- = legacy_settings->eth_tp_mdix;
- link_ksettings->base.eth_tp_mdix_ctrl
- = legacy_settings->eth_tp_mdix_ctrl;
- return retval;
-}
-
/* return false if ksettings link modes had higher bits
* set. legacy_settings always updated (best effort)
*/
@@ -617,7 +570,12 @@ static int ethtool_set_link_ksettings(struct net_device *dev,
!= link_ksettings.base.link_mode_masks_nwords)
return -EINVAL;

- return dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
+ err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
+ if (err >= 0)
+ ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_LINKINFO |
+ ETH_SETTINGS_IM_LINKMODES, NULL);
+ return err;
}

/* Query device for its ethtool_cmd settings.
@@ -666,6 +624,7 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
{
struct ethtool_link_ksettings link_ksettings;
struct ethtool_cmd cmd;
+ int ret;

ASSERT_RTNL();

@@ -678,7 +637,12 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
return -EINVAL;
link_ksettings.base.link_mode_masks_nwords =
__ETHTOOL_LINK_MODE_MASK_NU32;
- return dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
+ ret = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
+ if (ret >= 0)
+ ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_LINKINFO |
+ ETH_SETTINGS_IM_LINKMODES, NULL);
+ return ret;
}

static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index f0385c30169b..9b4ad09799c5 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -234,6 +234,7 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
const struct get_request_ops *get_requests[__ETHNL_CMD_CNT] = {
[ETHNL_CMD_GET_STRSET] = &strset_request_ops,
[ETHNL_CMD_GET_INFO] = &info_request_ops,
+ [ETHNL_CMD_GET_SETTINGS] = &settings_request_ops,
};

/**
@@ -558,6 +559,7 @@ typedef void (*ethnl_notify_handler_t)(struct net_device *dev,
const void *data);

ethnl_notify_handler_t ethnl_notify_handlers[] = {
+ [ETHNL_CMD_SET_SETTINGS] = ethnl_std_notify,
};

void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
@@ -654,6 +656,13 @@ static const struct genl_ops ethtool_genl_ops[] = {
.dumpit = ethnl_get_dumpit,
.done = ethnl_get_done,
},
+ {
+ .cmd = ETHNL_CMD_GET_SETTINGS,
+ .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[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index ce60d7e18651..fd7a362d79fa 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -289,5 +289,6 @@ struct get_request_ops {

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

#endif /* _NET_ETHTOOL_NETLINK_H */
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
new file mode 100644
index 000000000000..5d0c44a58883
--- /dev/null
+++ b/net/ethtool/settings.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+
+#include "netlink.h"
+#include "common.h"
+#include "bitset.h"
+
+struct settings_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_link_ksettings ksettings;
+ struct ethtool_link_settings *lsettings;
+ bool lpm_empty;
+};
+
+static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
+ [ETHA_SETTINGS_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_DEV] = { .type = NLA_NESTED },
+ [ETHA_SETTINGS_INFOMASK] = { .type = NLA_U32 },
+ [ETHA_SETTINGS_COMPACT] = { .type = NLA_FLAG },
+ [ETHA_SETTINGS_LINK_INFO] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_LINK_MODES] = { .type = NLA_REJECT },
+};
+
+static int parse_settings(struct common_req_info *req_info,
+ struct sk_buff *skb, struct genl_info *info,
+ const struct nlmsghdr *nlhdr)
+{
+ struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
+ int ret;
+
+ ret = ethnlmsg_parse(nlhdr, tb, ETHA_SETTINGS_MAX, get_settings_policy,
+ info);
+ if (ret < 0)
+ return ret;
+
+ if (tb[ETHA_SETTINGS_DEV]) {
+ req_info->dev = ethnl_dev_get(info, tb[ETHA_SETTINGS_DEV]);
+ if (IS_ERR(req_info->dev)) {
+ ret = PTR_ERR(req_info->dev);
+ req_info->dev = NULL;
+ return ret;
+ }
+ }
+ if (tb[ETHA_SETTINGS_INFOMASK])
+ req_info->req_mask = nla_get_u32(tb[ETHA_SETTINGS_INFOMASK]);
+ if (tb[ETHA_SETTINGS_COMPACT])
+ req_info->compact = true;
+ if (req_info->req_mask == 0)
+ req_info->req_mask = ETH_SETTINGS_IM_ALL;
+
+ return 0;
+}
+
+static int ethnl_get_link_ksettings(struct genl_info *info,
+ struct net_device *dev,
+ struct ethtool_link_ksettings *ksettings)
+{
+ int ret;
+
+ ret = __ethtool_get_link_ksettings(dev, ksettings);
+
+ if (ret < 0)
+ ETHNL_SET_ERRMSG(info, "failed to retrieve link settings");
+ return ret;
+}
+
+static int prepare_settings(struct common_req_info *req_info,
+ struct genl_info *info)
+{
+ struct settings_data *data =
+ container_of(req_info, struct settings_data, reqinfo_base);
+ struct net_device *dev = data->repdata_base.dev;
+ u32 req_mask = req_info->req_mask;
+ int ret;
+
+ data->lsettings = &data->ksettings.base;
+ data->lpm_empty = true;
+
+ ret = ethnl_before_ops(dev);
+ if (ret < 0)
+ return ret;
+ if (req_mask & (ETH_SETTINGS_IM_LINKINFO | ETH_SETTINGS_IM_LINKMODES)) {
+ ret = ethnl_get_link_ksettings(info, dev, &data->ksettings);
+ if (ret < 0)
+ req_mask &= ~(ETH_SETTINGS_IM_LINKINFO |
+ ETH_SETTINGS_IM_LINKMODES);
+ }
+ if (req_mask & ETH_SETTINGS_IM_LINKMODES) {
+ data->lpm_empty =
+ bitmap_empty(data->ksettings.link_modes.lp_advertising,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+ ethnl_bitmap_to_u32(data->ksettings.link_modes.supported,
+ __ETHTOOL_LINK_MODE_MASK_NWORDS);
+ ethnl_bitmap_to_u32(data->ksettings.link_modes.advertising,
+ __ETHTOOL_LINK_MODE_MASK_NWORDS);
+ ethnl_bitmap_to_u32(data->ksettings.link_modes.lp_advertising,
+ __ETHTOOL_LINK_MODE_MASK_NWORDS);
+ }
+ 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 link_info_size(void)
+{
+ int len = 0;
+
+ /* port, phyaddr, mdix, mdixctrl, transcvr */
+ len += 5 * nla_total_size(sizeof(u8));
+ /* mdio_support */
+ len += nla_total_size(sizeof(struct nla_bitfield32));
+
+ /* nest */
+ return nla_total_size(len);
+}
+
+static int link_modes_size(const struct ethtool_link_ksettings *ksettings,
+ bool compact)
+{
+ unsigned int flags = compact ? ETHNL_BITSET_COMPACT : 0;
+ u32 *supported = (u32 *)ksettings->link_modes.supported;
+ u32 *advertising = (u32 *)ksettings->link_modes.advertising;
+ u32 *lp_advertising = (u32 *)ksettings->link_modes.lp_advertising;
+ int len = 0, ret;
+
+ /* speed, duplex, autoneg */
+ len += nla_total_size(sizeof(u32)) + 2 * nla_total_size(sizeof(u8));
+ ret = ethnl_bitset32_size(__ETHTOOL_LINK_MODE_MASK_NBITS, advertising,
+ supported, link_mode_names, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ ret = ethnl_bitset32_size(__ETHTOOL_LINK_MODE_MASK_NBITS,
+ lp_advertising, NULL, link_mode_names,
+ flags & ETHNL_BITSET_LIST);
+ if (ret < 0)
+ return ret;
+ len += ret;
+
+ /* nest */
+ return nla_total_size(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
+ */
+static int settings_size(const struct common_req_info *req_info)
+{
+ struct settings_data *data =
+ container_of(req_info, struct settings_data, reqinfo_base);
+ u32 info_mask = data->repdata_base.info_mask;
+ bool compact = req_info->compact;
+ int len = 0, ret;
+
+ len += dev_ident_size();
+ if (info_mask & ETH_SETTINGS_IM_LINKINFO)
+ len += link_info_size();
+ if (info_mask & ETH_SETTINGS_IM_LINKMODES) {
+ ret = link_modes_size(&data->ksettings, compact);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }
+
+ return len;
+}
+
+static int fill_link_info(struct sk_buff *skb,
+ const struct ethtool_link_settings *lsettings)
+{
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_SETTINGS_LINK_INFO);
+
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_u8(skb, ETHA_LINKINFO_PORT, lsettings->port) ||
+ nla_put_u8(skb, ETHA_LINKINFO_PHYADDR,
+ lsettings->phy_address) ||
+ nla_put_u8(skb, ETHA_LINKINFO_TP_MDIX,
+ lsettings->eth_tp_mdix) ||
+ nla_put_u8(skb, ETHA_LINKINFO_TP_MDIX_CTRL,
+ lsettings->eth_tp_mdix_ctrl) ||
+ nla_put_u8(skb, ETHA_LINKINFO_TRANSCEIVER,
+ lsettings->transceiver)) {
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+}
+
+static int fill_link_modes(struct sk_buff *skb,
+ const struct ethtool_link_ksettings *ksettings,
+ bool lpm_empty, bool compact)
+{
+ const u32 *supported = (const u32 *)ksettings->link_modes.supported;
+ const u32 *advertising = (const u32 *)ksettings->link_modes.advertising;
+ const u32 *lp_adv = (const u32 *)ksettings->link_modes.lp_advertising;
+ const unsigned int flags = compact ? ETHNL_BITSET_COMPACT : 0;
+ const struct ethtool_link_settings *lsettings = &ksettings->base;
+ struct nlattr *nest;
+ int ret;
+
+ nest = ethnl_nest_start(skb, ETHA_SETTINGS_LINK_MODES);
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_u8(skb, ETHA_LINKMODES_AUTONEG, lsettings->autoneg))
+ goto err;
+
+ ret = ethnl_put_bitset32(skb, ETHA_LINKMODES_OURS,
+ __ETHTOOL_LINK_MODE_MASK_NBITS, advertising,
+ supported, link_mode_names, flags);
+ if (ret < 0)
+ goto err;
+ if (!lpm_empty) {
+ ret = ethnl_put_bitset32(skb, ETHA_LINKMODES_PEER,
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+ lp_adv, NULL, link_mode_names,
+ flags | ETHNL_BITSET_LIST);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (nla_put_u32(skb, ETHA_LINKMODES_SPEED, lsettings->speed) ||
+ nla_put_u8(skb, ETHA_LINKMODES_DUPLEX, lsettings->duplex))
+ goto err;
+
+ nla_nest_end(skb, nest);
+ return 0;
+
+err:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
+static int fill_settings(struct sk_buff *skb,
+ const struct common_req_info *req_info)
+{
+ const struct settings_data *data =
+ container_of(req_info, struct settings_data, reqinfo_base);
+ u32 info_mask = data->repdata_base.info_mask;
+ bool compact = req_info->compact;
+ int ret;
+
+ if (info_mask & ETH_SETTINGS_IM_LINKINFO) {
+ ret = fill_link_info(skb, data->lsettings);
+ if (ret < 0)
+ return ret;
+ }
+ if (info_mask & ETH_SETTINGS_IM_LINKMODES) {
+ ret = fill_link_modes(skb, &data->ksettings, data->lpm_empty,
+ compact);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct get_request_ops settings_request_ops = {
+ .request_cmd = ETHNL_CMD_GET_SETTINGS,
+ .reply_cmd = ETHNL_CMD_SET_SETTINGS,
+ .dev_attrtype = ETHA_SETTINGS_DEV,
+ .data_size = sizeof(struct settings_data),
+ .repdata_offset = offsetof(struct settings_data, repdata_base),
+
+ .parse_request = parse_settings,
+ .prepare_data = prepare_settings,
+ .reply_size = settings_size,
+ .fill_reply = fill_settings,
+};
--
2.21.0


2019-03-25 17:10:20

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 19/22] ethtool: provide WoL information in GET_SETTINGS request

Add information about supported and enabled wake on LAN modes into the
GET_SETTINGS reply when ETH_SETTINGS_IM_WOL flag is set in the request.

The GET_SETTINGS request can be still sent by unprivileged users but in
such case the SecureOn password (if any) is not included in the reply.

Send notification in the same format as reply SET_SETTINGS message when
wake on LAN settings are modified using ioctl interface (ETHTOOL_SWOL
command).

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 13 +++-
include/uapi/linux/ethtool_netlink.h | 14 ++++-
net/ethtool/common.c | 10 ++++
net/ethtool/common.h | 1 +
net/ethtool/ioctl.c | 10 ++--
net/ethtool/settings.c | 63 ++++++++++++++++++++
6 files changed, 105 insertions(+), 6 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index ebd1a0404828..bc7f28f0182f 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -278,6 +278,7 @@ Info mask bits meaning:
ETH_SETTINGS_IM_LINKINFO link settings
ETH_SETTINGS_IM_LINKMODES link modes and related
ETH_SETTINGS_IM_LINKSTATE link state
+ ETH_SETTINGS_IM_WOL struct ethtool_wolinfo

Response contents:

@@ -296,12 +297,22 @@ Response contents:
ETHA_LINKMODES_DUPLEX (u8) duplex mode
ETHA_SETTINGS_LINK_STATE (nested) link state
ETHA_LINKSTATE_LINK (u8) link on/off/unknown
+ ETHA_SETTINGS_WOL (nested) wake on LAN settings
+ ETHA_WOL_MODES (bitfield32) wake on LAN modes
+ ETHA_WOL_SOPASS (binary) SecureOn(tm) password

Most of the attributes and their values have the same meaning as matching
members of the corresponding ioctl structures. For ETHA_LINKMODES_OURS,
value represents advertised modes and mask represents supported modes.
ETHA_LINKMODES_PEER in the reply is a bit list.

+For ETHA_WOL_MODES, selector reports wake on LAN modes supported by the
+device and value enabled modes.
+
+GET_SETTINGS request is allowed for unprivileged user but ETHA_WOL_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.
@@ -348,7 +359,7 @@ ETHTOOL_GSET ETHNL_CMD_GET_SETTINGS
ETHTOOL_SSET ETHNL_CMD_SET_SETTINGS
ETHTOOL_GDRVINFO ETHNL_CMD_GET_INFO
ETHTOOL_GREGS n/a
-ETHTOOL_GWOL n/a
+ETHTOOL_GWOL ETHNL_CMD_GET_SETTINGS
ETHTOOL_SWOL n/a
ETHTOOL_GMSGLVL n/a
ETHTOOL_SMSGLVL n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 45c27c7291d0..532ad11ae87c 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -197,6 +197,7 @@ enum {
ETHA_SETTINGS_LINK_INFO, /* nest - ETHA_LINKINFO_* */
ETHA_SETTINGS_LINK_MODES, /* nest - ETHA_LINKMODES_* */
ETHA_SETTINGS_LINK_STATE, /* nest - ETHA_LINKSTATE_* */
+ ETHA_SETTINGS_WOL, /* nest - ETHA_WOL_* */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -205,10 +206,12 @@ enum {
#define ETH_SETTINGS_IM_LINKINFO (1U << 0)
#define ETH_SETTINGS_IM_LINKMODES (1U << 1)
#define ETH_SETTINGS_IM_LINKSTATE (1U << 2)
+#define ETH_SETTINGS_IM_WOL (1U << 3)

#define ETH_SETTINGS_IM_ALL (ETH_SETTINGS_IM_LINKINFO | \
ETH_SETTINGS_IM_LINKMODES | \
- ETH_SETTINGS_IM_LINKSTATE)
+ ETH_SETTINGS_IM_LINKSTATE | \
+ ETH_SETTINGS_IM_WOL)

enum {
ETHA_LINKINFO_UNSPEC,
@@ -242,6 +245,15 @@ enum {
ETHA_LINKSTATE_MAX = (__ETHA_LINKSTATE_CNT - 1)
};

+enum {
+ ETHA_WOL_UNSPEC,
+ ETHA_WOL_MODES, /* bitfield32 */
+ ETHA_WOL_SOPASS, /* binary */
+
+ __ETHA_WOL_CNT,
+ ETHA_WOL_MAX = (__ETHA_WOL_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index dc907d8b6e43..4b2f08da910a 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -213,3 +213,13 @@ int __ethtool_get_link(struct net_device *dev)

return netif_running(dev) && dev->ethtool_ops->get_link(dev);
}
+
+int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ if (!dev->ethtool_ops->get_wol)
+ return -EOPNOTSUPP;
+
+ dev->ethtool_ops->get_wol(dev, wol);
+
+ return 0;
+}
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index a5ddd7f5cfce..bbe3e51f7308 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_link(struct net_device *dev);
+int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);

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 58669dafeaf9..bba02a218eea 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1242,11 +1242,11 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
{
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+ int rc;

- if (!dev->ethtool_ops->get_wol)
- return -EOPNOTSUPP;
-
- dev->ethtool_ops->get_wol(dev, &wol);
+ rc = __ethtool_get_wol(dev, &wol);
+ if (rc < 0)
+ return rc;

if (copy_to_user(useraddr, &wol, sizeof(wol)))
return -EFAULT;
@@ -1269,6 +1269,8 @@ static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
return ret;

dev->wol_enabled = !!wol.wolopts;
+ ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS, ETH_SETTINGS_IM_WOL,
+ NULL);

return 0;
}
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 7a2729f01f79..56a045e5e916 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -6,10 +6,12 @@

struct settings_data {
struct common_req_info reqinfo_base;
+ bool privileged;

/* everything below here will be reset for each device in dumps */
struct common_reply_data repdata_base;
struct ethtool_link_ksettings ksettings;
+ struct ethtool_wolinfo wolinfo;
struct ethtool_link_settings *lsettings;
int link;
bool lpm_empty;
@@ -114,15 +116,20 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_LINK_INFO] = { .type = NLA_REJECT },
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_REJECT },
[ETHA_SETTINGS_LINK_STATE] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_WOL] = { .type = NLA_REJECT },
};

static int parse_settings(struct common_req_info *req_info,
struct sk_buff *skb, struct genl_info *info,
const struct nlmsghdr *nlhdr)
{
+ struct settings_data *data =
+ container_of(req_info, struct settings_data, reqinfo_base);
struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
int ret;

+ data->privileged = ethnl_is_privileged(skb);
+
ret = ethnlmsg_parse(nlhdr, tb, ETHA_SETTINGS_MAX, get_settings_policy,
info);
if (ret < 0)
@@ -159,6 +166,16 @@ static int ethnl_get_link_ksettings(struct genl_info *info,
return ret;
}

+static int ethnl_get_wol(struct genl_info *info, struct net_device *dev,
+ struct ethtool_wolinfo *wolinfo)
+{
+ int ret = __ethtool_get_wol(dev, wolinfo);
+
+ if (ret < 0)
+ ETHNL_SET_ERRMSG(info, "failed to retrieve wol info");
+ return ret;
+}
+
static int prepare_settings(struct common_req_info *req_info,
struct genl_info *info)
{
@@ -194,6 +211,11 @@ static int prepare_settings(struct common_req_info *req_info,
}
if (req_mask & ETH_SETTINGS_IM_LINKSTATE)
data->link = __ethtool_get_link(dev);
+ if (req_mask & ETH_SETTINGS_IM_WOL) {
+ ret = ethnl_get_wol(info, dev, &data->wolinfo);
+ if (ret < 0)
+ req_mask &= ~ETH_SETTINGS_IM_WOL;
+ }
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -249,6 +271,12 @@ static int link_state_size(int link)
return nla_total_size(nla_total_size(sizeof(u8)));
}

+static int wol_size(void)
+{
+ return nla_total_size(nla_total_size(sizeof(struct nla_bitfield32)) +
+ nla_total_size(SOPASS_MAX));
+}
+
/* 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
@@ -272,6 +300,8 @@ static int settings_size(const struct common_req_info *req_info)
}
if (info_mask & ETH_SETTINGS_IM_LINKSTATE)
len += link_state_size(data->link);
+ if (info_mask & ETH_SETTINGS_IM_WOL)
+ len += wol_size();

return len;
}
@@ -361,6 +391,33 @@ static int fill_link_state(struct sk_buff *skb, u8 link)
return -EMSGSIZE;
}

+static int fill_wolinfo(struct sk_buff *skb,
+ const struct ethtool_wolinfo *wolinfo, bool privileged)
+{
+ struct nlattr *nest;
+
+ nest = ethnl_nest_start(skb, ETHA_SETTINGS_WOL);
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_bitfield32(skb, ETHA_WOL_MODES, wolinfo->wolopts,
+ wolinfo->supported))
+ goto err;
+ /* ioctl() restricts read access to wolinfo but the actual
+ * reason is to hide sopass from unprivileged users; netlink
+ * can show wol modes without sopass
+ */
+ if (privileged &&
+ nla_put(skb, ETHA_WOL_SOPASS, sizeof(wolinfo->sopass),
+ wolinfo->sopass))
+ goto err;
+ nla_nest_end(skb, nest);
+ return 0;
+
+err:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
static int fill_settings(struct sk_buff *skb,
const struct common_req_info *req_info)
{
@@ -386,6 +443,11 @@ static int fill_settings(struct sk_buff *skb,
if (ret < 0)
return ret;
}
+ if (info_mask & ETH_SETTINGS_IM_WOL) {
+ ret = fill_wolinfo(skb, &data->wolinfo, data->privileged);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}
@@ -431,6 +493,7 @@ static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_LINK_INFO] = { .type = NLA_NESTED },
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_NESTED },
[ETHA_SETTINGS_LINK_STATE] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_WOL] = { .type = NLA_REJECT },
};

static int ethnl_set_link_ksettings(struct genl_info *info,
--
2.21.0


2019-03-25 17:10:30

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 21/22] ethtool: provide message level in GET_SETTINGS request

Add information about supported and enabled message levels to the
GET_SETTINGS reply when ETH_SETTINGS_IM_DEBUG flag is set in the
request.

Unlike in the ioctl interface, "message level" is called "message mask" as
it is in fact interpreted as a bit mask. It is put into a nested attribute
ETHA_SETTINGS_DEBUG to allow future extensions (e.g. an actual verbosity
level).

Send notification in the same format as reply when message level is
modified using the ioctl interface (ETHTOOL_SMSGLVL command).

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 9 ++++-
include/linux/netdevice.h | 2 +
include/uapi/linux/ethtool_netlink.h | 13 ++++++-
net/ethtool/ioctl.c | 3 ++
net/ethtool/settings.c | 40 ++++++++++++++++++++
5 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 3568d35ad7ec..603acfbefe29 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -279,6 +279,7 @@ Info mask bits meaning:
ETH_SETTINGS_IM_LINKMODES link modes and related
ETH_SETTINGS_IM_LINKSTATE link state
ETH_SETTINGS_IM_WOL struct ethtool_wolinfo
+ ETH_SETTINGS_IM_DEBUG debugging

Response contents:

@@ -300,6 +301,8 @@ Response contents:
ETHA_SETTINGS_WOL (nested) wake on LAN settings
ETHA_WOL_MODES (bitfield32) wake on LAN modes
ETHA_WOL_SOPASS (binary) SecureOn(tm) password
+ ETHA_SETTINGS_DEBUG (nested) debugging
+ ETHA_DEBUG_MSG_MASK (bitfield32) debug message mask

Most of the attributes and their values have the same meaning as matching
members of the corresponding ioctl structures. For ETHA_LINKMODES_OURS,
@@ -309,6 +312,10 @@ ETHA_LINKMODES_PEER in the reply is a bit list.
For ETHA_WOL_MODES, selector reports wake on LAN modes supported by the
device and value enabled modes.

+ETHA_DEBUG_MSG_MASK corresponds to message level (which is actually a bitfield)
+as reported by ETHTOOL_GMSGLVL. The selector reports all message types
+recognized by kernel and value types enabled for the device.
+
GET_SETTINGS request is allowed for unprivileged user but ETHA_WOL_SOPASS
is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
requests.
@@ -371,7 +378,7 @@ ETHTOOL_GDRVINFO ETHNL_CMD_GET_INFO
ETHTOOL_GREGS n/a
ETHTOOL_GWOL ETHNL_CMD_GET_SETTINGS
ETHTOOL_SWOL ETHNL_CMD_SET_SETTINGS
-ETHTOOL_GMSGLVL n/a
+ETHTOOL_GMSGLVL ETHNL_CMD_GET_SETTINGS
ETHTOOL_SMSGLVL n/a
ETHTOOL_NWAY_RST n/a
ETHTOOL_GLINK ETHNL_CMD_GET_SETTINGS
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index bc761511edb4..23ebb37769c9 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3859,6 +3859,8 @@ enum {
NETIF_MSG_PKTDATA = 0x1000,
NETIF_MSG_HW = 0x2000,
NETIF_MSG_WOL = 0x4000,
+
+ NETIF_MSG_ALL = 0x7fff,
};

#define netif_msg_drv(p) ((p)->msg_enable & NETIF_MSG_DRV)
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 532ad11ae87c..924f7059f944 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -198,6 +198,7 @@ enum {
ETHA_SETTINGS_LINK_MODES, /* nest - ETHA_LINKMODES_* */
ETHA_SETTINGS_LINK_STATE, /* nest - ETHA_LINKSTATE_* */
ETHA_SETTINGS_WOL, /* nest - ETHA_WOL_* */
+ ETHA_SETTINGS_DEBUG, /* nest - ETHA_DEBUG_* */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -207,11 +208,13 @@ enum {
#define ETH_SETTINGS_IM_LINKMODES (1U << 1)
#define ETH_SETTINGS_IM_LINKSTATE (1U << 2)
#define ETH_SETTINGS_IM_WOL (1U << 3)
+#define ETH_SETTINGS_IM_DEBUG (1U << 4)

#define ETH_SETTINGS_IM_ALL (ETH_SETTINGS_IM_LINKINFO | \
ETH_SETTINGS_IM_LINKMODES | \
ETH_SETTINGS_IM_LINKSTATE | \
- ETH_SETTINGS_IM_WOL)
+ ETH_SETTINGS_IM_WOL | \
+ ETH_SETTINGS_IM_DEBUG)

enum {
ETHA_LINKINFO_UNSPEC,
@@ -254,6 +257,14 @@ enum {
ETHA_WOL_MAX = (__ETHA_WOL_CNT - 1)
};

+enum {
+ ETHA_DEBUG_UNSPEC,
+ ETHA_DEBUG_MSG_MASK, /* bitfield32 */
+
+ __ETHA_DEBUG_CNT,
+ ETHA_DEBUG_MAX = (__ETHA_DEBUG_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index bba02a218eea..e94fcd947c87 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -2464,6 +2464,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_SMSGLVL:
rc = ethtool_set_value_void(dev, useraddr,
dev->ethtool_ops->set_msglevel);
+ if (rc >= 0)
+ ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_DEBUG, NULL);
break;
case ETHTOOL_GEEE:
rc = ethtool_get_eee(dev, useraddr);
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index ea5279dad826..53409dd8af34 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -14,6 +14,7 @@ struct settings_data {
struct ethtool_wolinfo wolinfo;
struct ethtool_link_settings *lsettings;
int link;
+ u32 msglevel;
bool lpm_empty;
};

@@ -123,6 +124,7 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_REJECT },
[ETHA_SETTINGS_LINK_STATE] = { .type = NLA_REJECT },
[ETHA_SETTINGS_WOL] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_DEBUG] = { .type = NLA_REJECT },
};

static int parse_settings(struct common_req_info *req_info,
@@ -188,6 +190,7 @@ static int prepare_settings(struct common_req_info *req_info,
struct settings_data *data =
container_of(req_info, struct settings_data, reqinfo_base);
struct net_device *dev = data->repdata_base.dev;
+ const struct ethtool_ops *eops = dev->ethtool_ops;
u32 req_mask = req_info->req_mask;
int ret;

@@ -222,6 +225,12 @@ static int prepare_settings(struct common_req_info *req_info,
if (ret < 0)
req_mask &= ~ETH_SETTINGS_IM_WOL;
}
+ if (req_mask & ETH_SETTINGS_IM_DEBUG) {
+ if (eops->get_msglevel)
+ data->msglevel = eops->get_msglevel(dev);
+ else
+ req_mask &= ~ETH_SETTINGS_IM_DEBUG;
+ }
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -283,6 +292,11 @@ static int wol_size(void)
nla_total_size(SOPASS_MAX));
}

+static int debug_size(void)
+{
+ return nla_total_size(nla_total_size(sizeof(struct nla_bitfield32)));
+}
+
/* 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
@@ -308,6 +322,8 @@ static int settings_size(const struct common_req_info *req_info)
len += link_state_size(data->link);
if (info_mask & ETH_SETTINGS_IM_WOL)
len += wol_size();
+ if (info_mask & ETH_SETTINGS_IM_DEBUG)
+ len += debug_size();

return len;
}
@@ -424,6 +440,24 @@ static int fill_wolinfo(struct sk_buff *skb,
return -EMSGSIZE;
}

+static int fill_debug(struct sk_buff *skb, u32 msglevel)
+{
+ struct nlattr *nest;
+
+ nest = ethnl_nest_start(skb, ETHA_SETTINGS_DEBUG);
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_bitfield32(skb, ETHA_DEBUG_MSG_MASK, msglevel,
+ NETIF_MSG_ALL))
+ goto err;
+ nla_nest_end(skb, nest);
+ return 0;
+
+err:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
static int fill_settings(struct sk_buff *skb,
const struct common_req_info *req_info)
{
@@ -454,6 +488,11 @@ static int fill_settings(struct sk_buff *skb,
if (ret < 0)
return ret;
}
+ if (info_mask & ETH_SETTINGS_IM_DEBUG) {
+ ret = fill_debug(skb, data->msglevel);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}
@@ -508,6 +547,7 @@ static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_NESTED },
[ETHA_SETTINGS_LINK_STATE] = { .type = NLA_REJECT },
[ETHA_SETTINGS_WOL] = { .type = NLA_NESTED },
+ [ETHA_SETTINGS_DEBUG] = { .type = NLA_REJECT },
};

static int ethnl_set_link_ksettings(struct genl_info *info,
--
2.21.0


2019-03-25 17:10:53

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

Add infrastructure for ethtool netlink notifications. There is only one
multicast group "monitor" which is used to notify userspace about changes.
Notifications are supposed to be broadcasted on every configuration change,
whether it is done using the netlink interface or legacy ioctl one.

To trigger an ethtool notification, both ethtool netlink and external code
use ethtool_notify() helper. This helper requires RTNL to be held and may
sleep.

Signed-off-by: Michal Kubecek <[email protected]>
---
include/linux/ethtool_netlink.h | 5 ++++
include/linux/netdevice.h | 12 +++++++++
include/uapi/linux/ethtool_netlink.h | 2 ++
net/ethtool/netlink.c | 37 ++++++++++++++++++++++++++++
net/ethtool/netlink.h | 2 ++
5 files changed, 58 insertions(+)

diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h
index 0412adb4f42f..2a15e64a16f3 100644
--- a/include/linux/ethtool_netlink.h
+++ b/include/linux/ethtool_netlink.h
@@ -5,5 +5,10 @@

#include <uapi/linux/ethtool_netlink.h>
#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+
+enum ethtool_multicast_groups {
+ ETHNL_MCGRP_MONITOR,
+};

#endif /* _LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 166fdc0a78b4..bc761511edb4 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4353,6 +4353,18 @@ struct netdev_notifier_bonding_info {
void netdev_bonding_info_change(struct net_device *dev,
struct netdev_bonding_info *bonding_info);

+#if IS_ENABLED(CONFIG_ETHTOOL_NETLINK)
+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
+ unsigned int cmd, u32 req_mask, const void *data);
+#else
+static inline void ethtool_notify(struct net_device *dev,
+ struct netlink_ext_ack *extack,
+ unsigned int cmd, u32 req_mask,
+ const void *data)
+{
+}
+#endif
+
static inline
struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features)
{
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index de18e076ed69..91e4d117957b 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -65,4 +65,6 @@ enum {
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1

+#define ETHTOOL_MCGRP_MONITOR_NAME "monitor"
+
#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index cc6829ba4331..4a31765165ea 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -4,6 +4,10 @@
#include <linux/ethtool_netlink.h>
#include "netlink.h"

+u32 ethnl_bcast_seq;
+
+static bool ethnl_ok __read_mostly;
+
static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {
[ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
[ETHA_DEV_INDEX] = { .type = NLA_U32 },
@@ -147,11 +151,41 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
return NULL;
}

+/* notifications */
+
+typedef void (*ethnl_notify_handler_t)(struct net_device *dev,
+ struct netlink_ext_ack *extack,
+ unsigned int cmd, u32 req_mask,
+ const void *data);
+
+ethnl_notify_handler_t ethnl_notify_handlers[] = {
+};
+
+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
+ unsigned int cmd, u32 req_mask, const void *data)
+{
+ if (unlikely(!ethnl_ok))
+ return;
+ ASSERT_RTNL();
+
+ if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
+ ethnl_notify_handlers[cmd]))
+ ethnl_notify_handlers[cmd](dev, extack, cmd, req_mask, data);
+ else
+ WARN_ONCE(1, "notification %u not implemented (dev=%s, req_mask=0x%x)\n",
+ cmd, netdev_name(dev), req_mask);
+}
+EXPORT_SYMBOL(ethtool_notify);
+
/* genetlink setup */

static const struct genl_ops ethtool_genl_ops[] = {
};

+static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
+ [ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME },
+};
+
struct genl_family ethtool_genl_family = {
.hdrsize = 0,
.name = ETHTOOL_GENL_NAME,
@@ -160,6 +194,8 @@ struct genl_family ethtool_genl_family = {
.parallel_ops = true,
.ops = ethtool_genl_ops,
.n_ops = ARRAY_SIZE(ethtool_genl_ops),
+ .mcgrps = ethtool_nl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(ethtool_nl_mcgrps),
};

/* module setup */
@@ -171,6 +207,7 @@ static int __init ethnl_init(void)
ret = genl_register_family(&ethtool_genl_family);
if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
return ret;
+ ethnl_ok = true;

return 0;
}
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index b8a6cd3dc3e3..5f2299548915 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -11,6 +11,8 @@
#define ETHNL_SET_ERRMSG(info, msg) \
do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)

+extern u32 ethnl_bcast_seq;
+
extern struct genl_family ethtool_genl_family;

struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest);
--
2.21.0


2019-03-25 17:11:05

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 17/22] ethtool: set link settings and link modes with SET_SETTINGS request

Implement SET_SETTINGS netlink request allowing to set link settings and
advertised link modes as an alternative to ETHTOOL_SLINKSETTINGS ioctl
command.

ETHA_SETTINGS_LINK_MODES attribute is used to set (or modify) advertised
link modes and related settings (autonegotiation, speed and duplex) and
ETHA_SETTINGS_LINK_INFO to set other link settings.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 34 ++-
net/ethtool/netlink.c | 5 +
net/ethtool/netlink.h | 2 +
net/ethtool/settings.c | 305 +++++++++++++++++++
4 files changed, 343 insertions(+), 3 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index d723b3537c46..e79ae9fe01be 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -132,7 +132,7 @@ List of message types
ETHNL_CMD_GET_INFO
ETHNL_CMD_SET_INFO response only
ETHNL_CMD_GET_SETTINGS
- ETHNL_CMD_SET_SETTINGS response only (for now)
+ ETHNL_CMD_SET_SETTINGS

All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
to indicate the type.
@@ -304,6 +304,34 @@ to them are broadcasted as notifications on change of these settings using
netlink or ioctl ethtool interface.


+SET_SETTINGS
+------------
+
+SET_SETTINGS request allows setting some of the data reported by GET_SETTINGS.
+Request flags, info_mask and index are ignored. These attributes are allowed
+to be passed with SET_SETTINGS request:
+
+ ETHA_SETTINGS_DEV (nested) device identification
+ ETHA_SETTINGS_LINK_INFO (nested) link settings
+ ETHA_LINKINFO_PORT (u8) physical port
+ ETHA_LINKINFO_PHYADDR (u8) MDIO address of phy
+ ETHA_LINKINFO_TP_MDIX_CTRL (u8) MDI(-X) control
+ ETHA_SETTINGS_LINK_MODES (nested) link modes
+ ETHA_LINKMODES_AUTONEG (u8) autonegotiation
+ ETHA_LINKMODES_OURS (bitset) advertised link modes
+ ETHA_LINKMODES_SPEED (u32) link speed (Mb/s)
+ ETHA_LINKMODES_DUPLEX (u8) duplex mode
+
+ETHA_LINKMODES_OURS bit set allows setting advertised link modes. If
+autonegotiation is on (either set now or kept from before), advertised modes
+are not changed (no ETHA_LINKMODES_OURS attribute) and at least one of speed
+and duplex is specified, kernel adjusts advertised modes to all supported
+modes matching speed, duplex or both (whatever is specified). This
+autoselection is done on ethtool side with ioctl interface, netlink interface
+is supposed to allow requesting changes without knowing what exactly kernel
+supports.
+
+
Request translation
-------------------

@@ -314,7 +342,7 @@ have their netlink replacement yet.
ioctl command netlink command
---------------------------------------------------------------------
ETHTOOL_GSET ETHNL_CMD_GET_SETTINGS
-ETHTOOL_SSET n/a
+ETHTOOL_SSET ETHNL_CMD_SET_SETTINGS
ETHTOOL_GDRVINFO ETHNL_CMD_GET_INFO
ETHTOOL_GREGS n/a
ETHTOOL_GWOL n/a
@@ -388,7 +416,7 @@ ETHTOOL_STUNABLE n/a
ETHTOOL_GPHYSTATS n/a
ETHTOOL_PERQUEUE n/a
ETHTOOL_GLINKSETTINGS ETHNL_CMD_GET_SETTINGS
-ETHTOOL_SLINKSETTINGS n/a
+ETHTOOL_SLINKSETTINGS ETHNL_CMD_SET_SETTINGS
ETHTOOL_PHY_GTUNABLE n/a
ETHTOOL_PHY_STUNABLE n/a
ETHTOOL_GFECPARAM n/a
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 9b4ad09799c5..50ed82d5f18b 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -663,6 +663,11 @@ static const struct genl_ops ethtool_genl_ops[] = {
.dumpit = ethnl_get_dumpit,
.done = ethnl_get_done,
},
+ {
+ .cmd = ETHNL_CMD_SET_SETTINGS,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .doit = ethnl_set_settings,
+ },
};

static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index fd7a362d79fa..853cd5b23415 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -291,4 +291,6 @@ extern const struct get_request_ops strset_request_ops;
extern const struct get_request_ops info_request_ops;
extern const struct get_request_ops settings_request_ops;

+int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info);
+
#endif /* _NET_ETHTOOL_NETLINK_H */
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 5d0c44a58883..2e0f425029ef 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -14,6 +14,97 @@ struct settings_data {
bool lpm_empty;
};

+struct link_mode_info {
+ int speed;
+ u8 duplex;
+};
+
+#define __DEFINE_LINK_MODE_PARAMS(_speed, _type, _duplex) \
+ [ETHTOOL_LINK_MODE(_speed, _type, _duplex)] = { \
+ .speed = SPEED_ ## _speed, \
+ .duplex = __DUPLEX_ ## _duplex \
+ }
+#define __DUPLEX_Half DUPLEX_HALF
+#define __DUPLEX_Full DUPLEX_FULL
+#define __DEFINE_SPECIAL_MODE_PARAMS(_mode) \
+ [ETHTOOL_LINK_MODE_ ## _mode ## _BIT] = { \
+ .speed = SPEED_UNKNOWN, \
+ .duplex = DUPLEX_UNKNOWN, \
+ }
+
+static const struct link_mode_info link_mode_params[] = {
+ __DEFINE_LINK_MODE_PARAMS(10, T, Half),
+ __DEFINE_LINK_MODE_PARAMS(10, T, Full),
+ __DEFINE_LINK_MODE_PARAMS(100, T, Half),
+ __DEFINE_LINK_MODE_PARAMS(100, T, Full),
+ __DEFINE_LINK_MODE_PARAMS(1000, T, Half),
+ __DEFINE_LINK_MODE_PARAMS(1000, T, Full),
+ __DEFINE_SPECIAL_MODE_PARAMS(Autoneg),
+ __DEFINE_SPECIAL_MODE_PARAMS(TP),
+ __DEFINE_SPECIAL_MODE_PARAMS(AUI),
+ __DEFINE_SPECIAL_MODE_PARAMS(MII),
+ __DEFINE_SPECIAL_MODE_PARAMS(FIBRE),
+ __DEFINE_SPECIAL_MODE_PARAMS(BNC),
+ __DEFINE_LINK_MODE_PARAMS(10000, T, Full),
+ __DEFINE_SPECIAL_MODE_PARAMS(Pause),
+ __DEFINE_SPECIAL_MODE_PARAMS(Asym_Pause),
+ __DEFINE_LINK_MODE_PARAMS(2500, X, Full),
+ __DEFINE_SPECIAL_MODE_PARAMS(Backplane),
+ __DEFINE_LINK_MODE_PARAMS(1000, KX, Full),
+ __DEFINE_LINK_MODE_PARAMS(10000, KX4, Full),
+ __DEFINE_LINK_MODE_PARAMS(10000, KR, Full),
+ [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = {
+ .speed = SPEED_10000,
+ .duplex = DUPLEX_FULL,
+ },
+ __DEFINE_LINK_MODE_PARAMS(20000, MLD2, Full),
+ __DEFINE_LINK_MODE_PARAMS(20000, KR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(40000, KR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(40000, CR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(40000, SR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(40000, LR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(56000, KR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(56000, CR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(56000, SR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(56000, LR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(25000, CR, Full),
+ __DEFINE_LINK_MODE_PARAMS(25000, KR, Full),
+ __DEFINE_LINK_MODE_PARAMS(25000, SR, Full),
+ __DEFINE_LINK_MODE_PARAMS(50000, CR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(50000, KR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, KR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, SR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, CR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, LR4_ER4, Full),
+ __DEFINE_LINK_MODE_PARAMS(50000, SR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(1000, X, Full),
+ __DEFINE_LINK_MODE_PARAMS(10000, CR, Full),
+ __DEFINE_LINK_MODE_PARAMS(10000, SR, Full),
+ __DEFINE_LINK_MODE_PARAMS(10000, LR, Full),
+ __DEFINE_LINK_MODE_PARAMS(10000, LRM, Full),
+ __DEFINE_LINK_MODE_PARAMS(10000, ER, Full),
+ __DEFINE_LINK_MODE_PARAMS(2500, T, Full),
+ __DEFINE_LINK_MODE_PARAMS(5000, T, Full),
+ __DEFINE_SPECIAL_MODE_PARAMS(FEC_NONE),
+ __DEFINE_SPECIAL_MODE_PARAMS(FEC_RS),
+ __DEFINE_SPECIAL_MODE_PARAMS(FEC_BASER),
+ __DEFINE_LINK_MODE_PARAMS(50000, KR, Full),
+ __DEFINE_LINK_MODE_PARAMS(50000, SR, Full),
+ __DEFINE_LINK_MODE_PARAMS(50000, CR, Full),
+ __DEFINE_LINK_MODE_PARAMS(50000, LR_ER_FR, Full),
+ __DEFINE_LINK_MODE_PARAMS(50000, DR, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, KR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, SR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, CR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, LR2_ER2_FR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(100000, DR2, Full),
+ __DEFINE_LINK_MODE_PARAMS(200000, KR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(200000, SR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(200000, LR4_ER4_FR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(200000, DR4, Full),
+ __DEFINE_LINK_MODE_PARAMS(200000, CR4, Full),
+};
+
static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_UNSPEC] = { .type = NLA_REJECT },
[ETHA_SETTINGS_DEV] = { .type = NLA_NESTED },
@@ -275,3 +366,217 @@ const struct get_request_ops settings_request_ops = {
.reply_size = settings_size,
.fill_reply = fill_settings,
};
+
+/* SET_SETTINGS */
+
+static const struct nla_policy set_linkinfo_policy[ETHA_LINKINFO_MAX + 1] = {
+ [ETHA_LINKINFO_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_LINKINFO_PORT] = { .type = NLA_U8 },
+ [ETHA_LINKINFO_PHYADDR] = { .type = NLA_U8 },
+ [ETHA_LINKINFO_TP_MDIX] = { .type = NLA_REJECT },
+ [ETHA_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_U8 },
+ [ETHA_LINKINFO_TRANSCEIVER] = { .type = NLA_REJECT },
+};
+
+static const struct nla_policy set_linkmodes_policy[ETHA_LINKMODES_MAX + 1] = {
+ [ETHA_LINKMODES_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_LINKMODES_AUTONEG] = { .type = NLA_U8 },
+ [ETHA_LINKMODES_OURS] = { .type = NLA_NESTED },
+ [ETHA_LINKMODES_PEER] = { .type = NLA_REJECT },
+ [ETHA_LINKMODES_SPEED] = { .type = NLA_U32 },
+ [ETHA_LINKMODES_DUPLEX] = { .type = NLA_U8 },
+};
+
+static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
+ [ETHA_SETTINGS_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_DEV] = { .type = NLA_NESTED },
+ [ETHA_SETTINGS_INFOMASK] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_COMPACT] = { .type = NLA_FLAG },
+ [ETHA_SETTINGS_LINK_INFO] = { .type = NLA_NESTED },
+ [ETHA_SETTINGS_LINK_MODES] = { .type = NLA_NESTED },
+};
+
+static int ethnl_set_link_ksettings(struct genl_info *info,
+ struct net_device *dev,
+ struct ethtool_link_ksettings *ksettings)
+{
+ int ret = dev->ethtool_ops->set_link_ksettings(dev, ksettings);
+
+ if (ret < 0)
+ ETHNL_SET_ERRMSG(info, "link settings update failed");
+ return ret;
+}
+
+/* Set advertised link modes to all supported modes matching requested speed
+ * and duplex values. Called when autonegotiation is on, speed or duplex is
+ * requested but no link mode change. This is done in userspace with ioctl()
+ * interface, move it into kernel for netlink.
+ * Returns true if advertised modes bitmap was modified.
+ */
+static bool auto_link_modes(struct ethtool_link_ksettings *ksettings,
+ bool req_speed, bool req_duplex)
+{
+ unsigned long *advertising = ksettings->link_modes.advertising;
+ unsigned long *supported = ksettings->link_modes.supported;
+ DECLARE_BITMAP(old_adv, __ETHTOOL_LINK_MODE_MASK_NBITS);
+ unsigned int i;
+
+ bitmap_copy(old_adv, advertising, __ETHTOOL_LINK_MODE_MASK_NBITS);
+
+ for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) {
+ const struct link_mode_info *info = &link_mode_params[i];
+
+ if (info->speed == SPEED_UNKNOWN)
+ continue;
+ if (test_bit(i, supported) &&
+ (!req_speed || info->speed == ksettings->base.speed) &&
+ (!req_duplex || info->duplex == ksettings->base.duplex))
+ set_bit(i, advertising);
+ else
+ clear_bit(i, advertising);
+ }
+
+ return !bitmap_equal(old_adv, advertising,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static int update_linkinfo(struct genl_info *info, struct nlattr *nest,
+ struct ethtool_link_settings *lsettings)
+{
+ struct nlattr *tb[ETHA_LINKINFO_MAX + 1];
+ int ret;
+
+ if (!nest)
+ return 0;
+ ret = nla_parse_nested_strict(tb, ETHA_LINKINFO_MAX, nest,
+ set_linkinfo_policy, info->extack);
+ if (ret < 0)
+ return ret;
+
+ ret = 0;
+ if (ethnl_update_u8(&lsettings->port, tb[ETHA_LINKINFO_PORT]))
+ ret = 1;
+ if (ethnl_update_u8(&lsettings->phy_address, tb[ETHA_LINKINFO_PHYADDR]))
+ ret = 1;
+ if (ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl,
+ tb[ETHA_LINKINFO_TP_MDIX_CTRL]))
+ ret = 1;
+
+ return ret;
+}
+
+static int update_link_modes(struct genl_info *info, const struct nlattr *nest,
+ struct ethtool_link_ksettings *ksettings)
+{
+ struct ethtool_link_settings *lsettings = &ksettings->base;
+ struct nlattr *tb[ETHA_LINKMODES_MAX + 1];
+ bool req_speed, req_duplex;
+ bool mod = false;
+ int ret;
+
+ if (!nest)
+ return 0;
+ ret = nla_parse_nested_strict(tb, ETHA_LINKMODES_MAX, nest,
+ set_linkmodes_policy, info->extack);
+ if (ret < 0)
+ return ret;
+ req_speed = tb[ETHA_LINKMODES_SPEED];
+ req_duplex = tb[ETHA_LINKMODES_DUPLEX];
+
+ if (ethnl_update_u8(&lsettings->autoneg, tb[ETHA_LINKMODES_AUTONEG]))
+ mod = true;
+ if (ethnl_update_bitset(ksettings->link_modes.advertising, NULL,
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+ tb[ETHA_LINKMODES_OURS],
+ &ret, link_mode_names, false, info))
+ mod = true;
+ if (ret < 0)
+ return ret;
+ if (ethnl_update_u32(&lsettings->speed, tb[ETHA_LINKMODES_SPEED]))
+ mod = true;
+ if (ethnl_update_u8(&lsettings->duplex, tb[ETHA_LINKMODES_DUPLEX]))
+ mod = true;
+
+ if (!tb[ETHA_LINKMODES_OURS] && lsettings->autoneg &&
+ (req_speed || req_duplex) &&
+ auto_link_modes(ksettings, req_speed, req_duplex))
+ mod = true;
+
+ return mod;
+}
+
+/* Update device settings using ->set_link_ksettings() callback */
+static int ethnl_update_ksettings(struct genl_info *info, struct nlattr **tb,
+ struct net_device *dev, u32 *req_mask)
+{
+ struct ethtool_link_ksettings ksettings = {};
+ struct ethtool_link_settings *lsettings;
+ u32 mod_mask = 0;
+ int ret;
+
+ ret = ethnl_get_link_ksettings(info, dev, &ksettings);
+ if (ret < 0)
+ return ret;
+ lsettings = &ksettings.base;
+
+ ret = update_linkinfo(info, tb[ETHA_SETTINGS_LINK_INFO], lsettings);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ mod_mask |= ETH_SETTINGS_IM_LINKINFO;
+
+ ret = update_link_modes(info, tb[ETHA_SETTINGS_LINK_MODES], &ksettings);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ mod_mask |= ETH_SETTINGS_IM_LINKMODES;
+
+ if (mod_mask) {
+ ret = ethnl_set_link_ksettings(info, dev, &ksettings);
+ if (ret < 0)
+ return ret;
+ *req_mask |= mod_mask;
+ }
+
+ return 0;
+}
+
+int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
+ struct net_device *dev;
+ u32 req_mask = 0;
+ int ret;
+
+ ret = ethnlmsg_parse(info->nlhdr, tb, ETHA_SETTINGS_MAX,
+ set_settings_policy, info);
+ if (ret < 0)
+ return ret;
+ dev = ethnl_dev_get(info, tb[ETHA_SETTINGS_DEV]);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ rtnl_lock();
+ ret = ethnl_before_ops(dev);
+ if (ret < 0)
+ goto out_rtnl;
+ if (tb[ETHA_SETTINGS_LINK_INFO] || tb[ETHA_SETTINGS_LINK_MODES]) {
+ ret = -EOPNOTSUPP;
+ if (!dev->ethtool_ops->get_link_ksettings)
+ goto out_ops;
+ ret = ethnl_update_ksettings(info, tb, dev, &req_mask);
+ if (ret < 0)
+ goto out_ops;
+ }
+ ret = 0;
+
+out_ops:
+ if (req_mask)
+ ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS, req_mask,
+ NULL);
+ ethnl_after_ops(dev);
+out_rtnl:
+ rtnl_unlock();
+ dev_put(dev);
+ return ret;
+}
--
2.21.0


2019-03-25 17:11:26

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 15/22] ethtool: provide link mode names as a string set

Add table of ethernet link mode names and make it available as a string set
to userspace GET_STRSET requests.

Signed-off-by: Michal Kubecek <[email protected]>
---
include/linux/ethtool.h | 4 ++
include/uapi/linux/ethtool.h | 2 +
net/ethtool/netlink.c | 81 ++++++++++++++++++++++++++++++++++++
net/ethtool/netlink.h | 1 +
net/ethtool/strset.c | 6 +++
5 files changed, 94 insertions(+)

diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index e6ebc9761822..ac3612599450 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -102,6 +102,10 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
#define __ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
DECLARE_BITMAP(name, __ETHTOOL_LINK_MODE_MASK_NBITS)

+/* compose link mode index from speed, type and duplex */
+#define ETHTOOL_LINK_MODE(speed, type, duplex) \
+ ETHTOOL_LINK_MODE_ ## speed ## base ## type ## _ ## duplex ## _BIT
+
/* drivers must ignore base.cmd and base.link_mode_masks_nwords
* fields, but they are allowed to overwrite them (will be ignored).
*/
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 882a542eaaa9..ef1288eb737c 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -566,6 +566,7 @@ struct ethtool_pauseparam {
* @ETH_SS_TSTAMP_SOF: timestamping flag names
* @ETH_SS_TSTAMP_TX_TYPE: timestamping Tx type names
* @ETH_SS_TSTAMP_RX_FILTER: timestamping Rx filter names
+ * @ETH_SS_LINK_MODES: link mode names
*/
enum ethtool_stringset {
ETH_SS_TEST = 0,
@@ -580,6 +581,7 @@ enum ethtool_stringset {
ETH_SS_TSTAMP_SOF,
ETH_SS_TSTAMP_TX_TYPE,
ETH_SS_TSTAMP_RX_FILTER,
+ ETH_SS_LINK_MODES,

ETH_SS_COUNT
};
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 7f9597c0c7a7..f0385c30169b 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -8,6 +8,84 @@ u32 ethnl_bcast_seq;

static bool ethnl_ok __read_mostly;

+#define __LINK_MODE_NAME(speed, type, duplex) \
+ #speed "base" #type "/" #duplex
+#define __DEFINE_LINK_MODE_NAME(speed, type, duplex) \
+ [ETHTOOL_LINK_MODE(speed, type, duplex)] = \
+ __LINK_MODE_NAME(speed, type, duplex)
+#define __DEFINE_SPECIAL_MODE_NAME(_mode, _name) \
+ [ETHTOOL_LINK_MODE_ ## _mode ## _BIT] = _name
+
+const char *const link_mode_names[] = {
+ __DEFINE_LINK_MODE_NAME(10, T, Half),
+ __DEFINE_LINK_MODE_NAME(10, T, Full),
+ __DEFINE_LINK_MODE_NAME(100, T, Half),
+ __DEFINE_LINK_MODE_NAME(100, T, Full),
+ __DEFINE_LINK_MODE_NAME(1000, T, Half),
+ __DEFINE_LINK_MODE_NAME(1000, T, Full),
+ __DEFINE_SPECIAL_MODE_NAME(Autoneg, "Autoneg"),
+ __DEFINE_SPECIAL_MODE_NAME(TP, "TP"),
+ __DEFINE_SPECIAL_MODE_NAME(AUI, "AUI"),
+ __DEFINE_SPECIAL_MODE_NAME(MII, "MII"),
+ __DEFINE_SPECIAL_MODE_NAME(FIBRE, "FIBRE"),
+ __DEFINE_SPECIAL_MODE_NAME(BNC, "BNC"),
+ __DEFINE_LINK_MODE_NAME(10000, T, Full),
+ __DEFINE_SPECIAL_MODE_NAME(Pause, "Pause"),
+ __DEFINE_SPECIAL_MODE_NAME(Asym_Pause, "Asym_Pause"),
+ __DEFINE_LINK_MODE_NAME(2500, X, Full),
+ __DEFINE_SPECIAL_MODE_NAME(Backplane, "Backplane"),
+ __DEFINE_LINK_MODE_NAME(1000, KX, Full),
+ __DEFINE_LINK_MODE_NAME(10000, KX4, Full),
+ __DEFINE_LINK_MODE_NAME(10000, KR, Full),
+ [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baseR_FEC",
+ __DEFINE_LINK_MODE_NAME(20000, MLD2, Full),
+ __DEFINE_LINK_MODE_NAME(20000, KR2, Full),
+ __DEFINE_LINK_MODE_NAME(40000, KR4, Full),
+ __DEFINE_LINK_MODE_NAME(40000, CR4, Full),
+ __DEFINE_LINK_MODE_NAME(40000, SR4, Full),
+ __DEFINE_LINK_MODE_NAME(40000, LR4, Full),
+ __DEFINE_LINK_MODE_NAME(56000, KR4, Full),
+ __DEFINE_LINK_MODE_NAME(56000, CR4, Full),
+ __DEFINE_LINK_MODE_NAME(56000, SR4, Full),
+ __DEFINE_LINK_MODE_NAME(56000, LR4, Full),
+ __DEFINE_LINK_MODE_NAME(25000, CR, Full),
+ __DEFINE_LINK_MODE_NAME(25000, KR, Full),
+ __DEFINE_LINK_MODE_NAME(25000, SR, Full),
+ __DEFINE_LINK_MODE_NAME(50000, CR2, Full),
+ __DEFINE_LINK_MODE_NAME(50000, KR2, Full),
+ __DEFINE_LINK_MODE_NAME(100000, KR4, Full),
+ __DEFINE_LINK_MODE_NAME(100000, SR4, Full),
+ __DEFINE_LINK_MODE_NAME(100000, CR4, Full),
+ __DEFINE_LINK_MODE_NAME(100000, LR4_ER4, Full),
+ __DEFINE_LINK_MODE_NAME(50000, SR2, Full),
+ __DEFINE_LINK_MODE_NAME(1000, X, Full),
+ __DEFINE_LINK_MODE_NAME(10000, CR, Full),
+ __DEFINE_LINK_MODE_NAME(10000, SR, Full),
+ __DEFINE_LINK_MODE_NAME(10000, LR, Full),
+ __DEFINE_LINK_MODE_NAME(10000, LRM, Full),
+ __DEFINE_LINK_MODE_NAME(10000, ER, Full),
+ __DEFINE_LINK_MODE_NAME(2500, T, Full),
+ __DEFINE_LINK_MODE_NAME(5000, T, Full),
+ __DEFINE_SPECIAL_MODE_NAME(FEC_NONE, "None"),
+ __DEFINE_SPECIAL_MODE_NAME(FEC_RS, "RS"),
+ __DEFINE_SPECIAL_MODE_NAME(FEC_BASER, "BASER"),
+ __DEFINE_LINK_MODE_NAME(50000, KR, Full),
+ __DEFINE_LINK_MODE_NAME(50000, SR, Full),
+ __DEFINE_LINK_MODE_NAME(50000, CR, Full),
+ __DEFINE_LINK_MODE_NAME(50000, LR_ER_FR, Full),
+ __DEFINE_LINK_MODE_NAME(50000, DR, Full),
+ __DEFINE_LINK_MODE_NAME(100000, KR2, Full),
+ __DEFINE_LINK_MODE_NAME(100000, SR2, Full),
+ __DEFINE_LINK_MODE_NAME(100000, CR2, Full),
+ __DEFINE_LINK_MODE_NAME(100000, LR2_ER2_FR2, Full),
+ __DEFINE_LINK_MODE_NAME(100000, DR2, Full),
+ __DEFINE_LINK_MODE_NAME(200000, KR4, Full),
+ __DEFINE_LINK_MODE_NAME(200000, SR4, Full),
+ __DEFINE_LINK_MODE_NAME(200000, LR4_ER4_FR4, Full),
+ __DEFINE_LINK_MODE_NAME(200000, DR4, Full),
+ __DEFINE_LINK_MODE_NAME(200000, CR4, Full),
+};
+
static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {
[ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
[ETHA_DEV_INDEX] = { .type = NLA_U32 },
@@ -600,6 +678,9 @@ static int __init ethnl_init(void)
{
int ret;

+ BUILD_BUG_ON(ARRAY_SIZE(link_mode_names) <
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+
ret = genl_register_family(&ethtool_genl_family);
if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
return ret;
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 4ad9a3eee3e5..ce60d7e18651 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -23,6 +23,7 @@ extern struct genl_family ethtool_genl_family;
extern const char *const so_timestamping_labels[];
extern const char *const tstamp_tx_type_labels[];
extern const char *const tstamp_rx_filter_labels[];
+extern const char *const link_mode_names[];

struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest);
int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype);
diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c
index e1b831097ca7..577d1309ba6f 100644
--- a/net/ethtool/strset.c
+++ b/net/ethtool/strset.c
@@ -85,6 +85,12 @@ static const struct strset_info info_template[] = {
.count = __HWTSTAMP_FILTER_COUNT,
.data = { .simple = tstamp_rx_filter_labels },
},
+ [ETH_SS_LINK_MODES] = {
+ .type = ETH_SS_TYPE_SIMPLE,
+ .per_dev = false,
+ .count = __ETHTOOL_LINK_MODE_MASK_NBITS,
+ .data = { .simple = link_mode_names },
+ },
};

struct strset_data {
--
2.21.0


2019-03-25 17:11:27

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

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 <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 43 ++++-
include/uapi/linux/ethtool_netlink.h | 30 ++++
net/ethtool/Makefile | 2 +-
net/ethtool/common.c | 52 ++++++
net/ethtool/common.h | 2 +
net/ethtool/info.c | 158 +++++++++++++++++++
net/ethtool/ioctl.c | 50 +-----
net/ethtool/netlink.c | 8 +
net/ethtool/netlink.h | 1 +
9 files changed, 299 insertions(+), 47 deletions(-)
create mode 100644 net/ethtool/info.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 1508c16a236e..cffa508ca6c6 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -129,6 +129,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.
@@ -209,6 +211,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
-------------------

@@ -220,7 +261,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 66aeb436b822..386d4273ac44 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -16,6 +16,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)
@@ -141,6 +143,34 @@ 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_CNT,
+ ETHA_INFO_MAX = (__ETHA_INFO_CNT - 1)
+};
+
+#define ETH_INFO_IM_DRVINFO (1U << 0)
+
+#define ETH_INFO_IM_ALL (ETH_INFO_IM_DRVINFO)
+
+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..8da992129321 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,53 @@ 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);
+
+ if (!info->fw_version[0])
+ devlink_compat_running_version(dev, info->fw_version,
+ sizeof(info->fw_version));
+
+ 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..cc42993bb05b
--- /dev/null
+++ b/net/ethtool/info.c
@@ -0,0 +1,158 @@
+// 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 },
+};
+
+/* parse_request() handler */
+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 = ethnlmsg_parse(nlhdr, tb, ETHA_INFO_MAX, get_info_policy, info);
+ 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;
+}
+
+/* prepare_data() handler */
+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);
+}
+
+/* reply_size() handler */
+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;
+}
+
+/* fill_reply() handler */
+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 04d747056070..844a4f4b1552 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -685,54 +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);
-
- if (!info.fw_version[0])
- devlink_compat_running_version(dev, info.fw_version,
- sizeof(info.fw_version));
+ 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 63ee0f846b50..7f9597c0c7a7 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -155,6 +155,7 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,

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

/**
@@ -568,6 +569,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[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 32d85bb5c49a..48980ae9e963 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -278,5 +278,6 @@ struct get_request_ops {
/* request handlers */

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

#endif /* _NET_ETHTOOL_NETLINK_H */
--
2.21.0


2019-03-25 17:11:26

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 11/22] ethtool: move string arrays into common file

Introduce file net/ethtool/common.c for code shared by ioctl and netlink
ethtool interface. Move name tables of features, RSS hash functions,
tunables and PHY tunables into this file.

Signed-off-by: Michal Kubecek <[email protected]>
---
net/ethtool/Makefile | 2 +-
net/ethtool/common.c | 83 ++++++++++++++++++++++++++++++++++++++++++++
net/ethtool/common.h | 17 +++++++++
net/ethtool/ioctl.c | 82 ++-----------------------------------------
4 files changed, 103 insertions(+), 81 deletions(-)
create mode 100644 net/ethtool/common.c
create mode 100644 net/ethtool/common.h

diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 482fdb9380fa..11782306593b 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0

-obj-y += ioctl.o
+obj-y += ioctl.o common.o

obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o

diff --git a/net/ethtool/common.c b/net/ethtool/common.c
new file mode 100644
index 000000000000..73f721a1c557
--- /dev/null
+++ b/net/ethtool/common.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+
+#include "common.h"
+
+const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
+ [NETIF_F_SG_BIT] = "tx-scatter-gather",
+ [NETIF_F_IP_CSUM_BIT] = "tx-checksum-ipv4",
+ [NETIF_F_HW_CSUM_BIT] = "tx-checksum-ip-generic",
+ [NETIF_F_IPV6_CSUM_BIT] = "tx-checksum-ipv6",
+ [NETIF_F_HIGHDMA_BIT] = "highdma",
+ [NETIF_F_FRAGLIST_BIT] = "tx-scatter-gather-fraglist",
+ [NETIF_F_HW_VLAN_CTAG_TX_BIT] = "tx-vlan-hw-insert",
+
+ [NETIF_F_HW_VLAN_CTAG_RX_BIT] = "rx-vlan-hw-parse",
+ [NETIF_F_HW_VLAN_CTAG_FILTER_BIT] = "rx-vlan-filter",
+ [NETIF_F_HW_VLAN_STAG_TX_BIT] = "tx-vlan-stag-hw-insert",
+ [NETIF_F_HW_VLAN_STAG_RX_BIT] = "rx-vlan-stag-hw-parse",
+ [NETIF_F_HW_VLAN_STAG_FILTER_BIT] = "rx-vlan-stag-filter",
+ [NETIF_F_VLAN_CHALLENGED_BIT] = "vlan-challenged",
+ [NETIF_F_GSO_BIT] = "tx-generic-segmentation",
+ [NETIF_F_LLTX_BIT] = "tx-lockless",
+ [NETIF_F_NETNS_LOCAL_BIT] = "netns-local",
+ [NETIF_F_GRO_BIT] = "rx-gro",
+ [NETIF_F_GRO_HW_BIT] = "rx-gro-hw",
+ [NETIF_F_LRO_BIT] = "rx-lro",
+
+ [NETIF_F_TSO_BIT] = "tx-tcp-segmentation",
+ [NETIF_F_GSO_ROBUST_BIT] = "tx-gso-robust",
+ [NETIF_F_TSO_ECN_BIT] = "tx-tcp-ecn-segmentation",
+ [NETIF_F_TSO_MANGLEID_BIT] = "tx-tcp-mangleid-segmentation",
+ [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation",
+ [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation",
+ [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation",
+ [NETIF_F_GSO_GRE_CSUM_BIT] = "tx-gre-csum-segmentation",
+ [NETIF_F_GSO_IPXIP4_BIT] = "tx-ipxip4-segmentation",
+ [NETIF_F_GSO_IPXIP6_BIT] = "tx-ipxip6-segmentation",
+ [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
+ [NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT] = "tx-udp_tnl-csum-segmentation",
+ [NETIF_F_GSO_PARTIAL_BIT] = "tx-gso-partial",
+ [NETIF_F_GSO_SCTP_BIT] = "tx-sctp-segmentation",
+ [NETIF_F_GSO_ESP_BIT] = "tx-esp-segmentation",
+ [NETIF_F_GSO_UDP_L4_BIT] = "tx-udp-segmentation",
+
+ [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
+ [NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp",
+ [NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu",
+ [NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter",
+ [NETIF_F_RXHASH_BIT] = "rx-hashing",
+ [NETIF_F_RXCSUM_BIT] = "rx-checksum",
+ [NETIF_F_NOCACHE_COPY_BIT] = "tx-nocache-copy",
+ [NETIF_F_LOOPBACK_BIT] = "loopback",
+ [NETIF_F_RXFCS_BIT] = "rx-fcs",
+ [NETIF_F_RXALL_BIT] = "rx-all",
+ [NETIF_F_HW_L2FW_DOFFLOAD_BIT] = "l2-fwd-offload",
+ [NETIF_F_HW_TC_BIT] = "hw-tc-offload",
+ [NETIF_F_HW_ESP_BIT] = "esp-hw-offload",
+ [NETIF_F_HW_ESP_TX_CSUM_BIT] = "esp-tx-csum-hw-offload",
+ [NETIF_F_RX_UDP_TUNNEL_PORT_BIT] = "rx-udp_tunnel-port-offload",
+ [NETIF_F_HW_TLS_RECORD_BIT] = "tls-hw-record",
+ [NETIF_F_HW_TLS_TX_BIT] = "tls-hw-tx-offload",
+ [NETIF_F_HW_TLS_RX_BIT] = "tls-hw-rx-offload",
+};
+
+const char
+rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN] = {
+ [ETH_RSS_HASH_TOP_BIT] = "toeplitz",
+ [ETH_RSS_HASH_XOR_BIT] = "xor",
+ [ETH_RSS_HASH_CRC32_BIT] = "crc32",
+};
+
+const char
+tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
+ [ETHTOOL_ID_UNSPEC] = "Unspec",
+ [ETHTOOL_RX_COPYBREAK] = "rx-copybreak",
+ [ETHTOOL_TX_COPYBREAK] = "tx-copybreak",
+ [ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout",
+};
+
+const char
+phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
+ [ETHTOOL_ID_UNSPEC] = "Unspec",
+ [ETHTOOL_PHY_DOWNSHIFT] = "phy-downshift",
+};
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
new file mode 100644
index 000000000000..41b2efc1e4e1
--- /dev/null
+++ b/net/ethtool/common.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _ETHTOOL_COMMON_H
+#define _ETHTOOL_COMMON_H
+
+#include <linux/ethtool.h>
+
+extern const char
+netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN];
+extern const char
+rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN];
+extern const char
+tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN];
+extern const char
+phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
+
+#endif /* _ETHTOOL_COMMON_H */
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index b1eb32419732..04d747056070 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -31,6 +31,8 @@
#include <net/xdp_sock.h>
#include <net/flow_offload.h>

+#include "common.h"
+
/*
* Some useful ethtool_ops methods that're device independent.
* If we find that all drivers want to do the same thing here,
@@ -58,86 +60,6 @@ EXPORT_SYMBOL(ethtool_op_get_ts_info);

#define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32)

-static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
- [NETIF_F_SG_BIT] = "tx-scatter-gather",
- [NETIF_F_IP_CSUM_BIT] = "tx-checksum-ipv4",
- [NETIF_F_HW_CSUM_BIT] = "tx-checksum-ip-generic",
- [NETIF_F_IPV6_CSUM_BIT] = "tx-checksum-ipv6",
- [NETIF_F_HIGHDMA_BIT] = "highdma",
- [NETIF_F_FRAGLIST_BIT] = "tx-scatter-gather-fraglist",
- [NETIF_F_HW_VLAN_CTAG_TX_BIT] = "tx-vlan-hw-insert",
-
- [NETIF_F_HW_VLAN_CTAG_RX_BIT] = "rx-vlan-hw-parse",
- [NETIF_F_HW_VLAN_CTAG_FILTER_BIT] = "rx-vlan-filter",
- [NETIF_F_HW_VLAN_STAG_TX_BIT] = "tx-vlan-stag-hw-insert",
- [NETIF_F_HW_VLAN_STAG_RX_BIT] = "rx-vlan-stag-hw-parse",
- [NETIF_F_HW_VLAN_STAG_FILTER_BIT] = "rx-vlan-stag-filter",
- [NETIF_F_VLAN_CHALLENGED_BIT] = "vlan-challenged",
- [NETIF_F_GSO_BIT] = "tx-generic-segmentation",
- [NETIF_F_LLTX_BIT] = "tx-lockless",
- [NETIF_F_NETNS_LOCAL_BIT] = "netns-local",
- [NETIF_F_GRO_BIT] = "rx-gro",
- [NETIF_F_GRO_HW_BIT] = "rx-gro-hw",
- [NETIF_F_LRO_BIT] = "rx-lro",
-
- [NETIF_F_TSO_BIT] = "tx-tcp-segmentation",
- [NETIF_F_GSO_ROBUST_BIT] = "tx-gso-robust",
- [NETIF_F_TSO_ECN_BIT] = "tx-tcp-ecn-segmentation",
- [NETIF_F_TSO_MANGLEID_BIT] = "tx-tcp-mangleid-segmentation",
- [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation",
- [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation",
- [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation",
- [NETIF_F_GSO_GRE_CSUM_BIT] = "tx-gre-csum-segmentation",
- [NETIF_F_GSO_IPXIP4_BIT] = "tx-ipxip4-segmentation",
- [NETIF_F_GSO_IPXIP6_BIT] = "tx-ipxip6-segmentation",
- [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
- [NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT] = "tx-udp_tnl-csum-segmentation",
- [NETIF_F_GSO_PARTIAL_BIT] = "tx-gso-partial",
- [NETIF_F_GSO_SCTP_BIT] = "tx-sctp-segmentation",
- [NETIF_F_GSO_ESP_BIT] = "tx-esp-segmentation",
- [NETIF_F_GSO_UDP_L4_BIT] = "tx-udp-segmentation",
-
- [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
- [NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp",
- [NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu",
- [NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter",
- [NETIF_F_RXHASH_BIT] = "rx-hashing",
- [NETIF_F_RXCSUM_BIT] = "rx-checksum",
- [NETIF_F_NOCACHE_COPY_BIT] = "tx-nocache-copy",
- [NETIF_F_LOOPBACK_BIT] = "loopback",
- [NETIF_F_RXFCS_BIT] = "rx-fcs",
- [NETIF_F_RXALL_BIT] = "rx-all",
- [NETIF_F_HW_L2FW_DOFFLOAD_BIT] = "l2-fwd-offload",
- [NETIF_F_HW_TC_BIT] = "hw-tc-offload",
- [NETIF_F_HW_ESP_BIT] = "esp-hw-offload",
- [NETIF_F_HW_ESP_TX_CSUM_BIT] = "esp-tx-csum-hw-offload",
- [NETIF_F_RX_UDP_TUNNEL_PORT_BIT] = "rx-udp_tunnel-port-offload",
- [NETIF_F_HW_TLS_RECORD_BIT] = "tls-hw-record",
- [NETIF_F_HW_TLS_TX_BIT] = "tls-hw-tx-offload",
- [NETIF_F_HW_TLS_RX_BIT] = "tls-hw-rx-offload",
-};
-
-static const char
-rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN] = {
- [ETH_RSS_HASH_TOP_BIT] = "toeplitz",
- [ETH_RSS_HASH_XOR_BIT] = "xor",
- [ETH_RSS_HASH_CRC32_BIT] = "crc32",
-};
-
-static const char
-tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
- [ETHTOOL_ID_UNSPEC] = "Unspec",
- [ETHTOOL_RX_COPYBREAK] = "rx-copybreak",
- [ETHTOOL_TX_COPYBREAK] = "tx-copybreak",
- [ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout",
-};
-
-static const char
-phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
- [ETHTOOL_ID_UNSPEC] = "Unspec",
- [ETHTOOL_PHY_DOWNSHIFT] = "phy-downshift",
-};
-
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
{
struct ethtool_gfeatures cmd = {
--
2.21.0


2019-03-25 17:11:27

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 14/22] ethtool: provide timestamping information in GET_INFO request

Add timestamping information as provided by ETHTOOL_GET_TS_INFO ioctl
command in GET_INFO reply if ETH_INFO_IM_TSINFO flag is set in the request.

Add constants for counts of HWTSTAMP_TX_* and HWTSTAM_FILTER_* constants
and provide symbolic names for timestamping related values so that they can
be retrieved in GET_STRSET and GET_INFO requests.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 10 +-
include/uapi/linux/ethtool.h | 6 +
include/uapi/linux/ethtool_netlink.h | 16 ++-
include/uapi/linux/net_tstamp.h | 13 ++
net/ethtool/common.c | 24 ++++
net/ethtool/common.h | 2 +
net/ethtool/info.c | 138 +++++++++++++++++++
net/ethtool/ioctl.c | 20 +--
net/ethtool/netlink.h | 9 ++
net/ethtool/strset.c | 18 +++
10 files changed, 236 insertions(+), 20 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index cffa508ca6c6..74c62f11810c 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -240,6 +240,11 @@ Kernel response contents:
ETHA_DRVINFO_FWVERSION (string) firmware version
ETHA_DRVINFO_BUSINFO (string) device bus address
ETHA_DRVINFO_EROM_VER (string) expansion ROM version
+ ETHA_INFO_TSINFO (nested) timestamping information
+ ETHA_TSINFO_TIMESTAMPING (bitset) supported flags
+ ETHA_TSINFO_PHC_INDEX (u32) associated PHC
+ ETHA_TSINFO_TX_TYPES (bitset) Tx types
+ ETHA_TSINFO_RX_FILTERS (bitset) Rx filters

The meaning of DRVINFO attributes follows the corresponding fields of
ETHTOOL_GDRVINFO response. Second part with various counts and sizes is
@@ -247,6 +252,9 @@ 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.

+ETHA_TSINFO_PHC_INDEX can be unsigned as there is no need for special value;
+if no PHC is associated, the attribute is not present.
+
GET_INFO requests allow dumps.


@@ -322,7 +330,7 @@ ETHTOOL_SCHANNELS n/a
ETHTOOL_SET_DUMP n/a
ETHTOOL_GET_DUMP_FLAG n/a
ETHTOOL_GET_DUMP_DATA n/a
-ETHTOOL_GET_TS_INFO n/a
+ETHTOOL_GET_TS_INFO ETHNL_CMD_GET_INFO
ETHTOOL_GMODULEINFO n/a
ETHTOOL_GMODULEEEPROM n/a
ETHTOOL_GEEE n/a
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index eaea804972f6..882a542eaaa9 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -563,6 +563,9 @@ struct ethtool_pauseparam {
* @ETH_SS_RSS_HASH_FUNCS: RSS hush function names
* @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS
* @ETH_SS_PHY_TUNABLES: PHY tunable names
+ * @ETH_SS_TSTAMP_SOF: timestamping flag names
+ * @ETH_SS_TSTAMP_TX_TYPE: timestamping Tx type names
+ * @ETH_SS_TSTAMP_RX_FILTER: timestamping Rx filter names
*/
enum ethtool_stringset {
ETH_SS_TEST = 0,
@@ -574,6 +577,9 @@ enum ethtool_stringset {
ETH_SS_TUNABLES,
ETH_SS_PHY_STATS,
ETH_SS_PHY_TUNABLES,
+ ETH_SS_TSTAMP_SOF,
+ ETH_SS_TSTAMP_TX_TYPE,
+ ETH_SS_TSTAMP_RX_FILTER,

ETH_SS_COUNT
};
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 386d4273ac44..e7a6e813150b 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -151,14 +151,17 @@ enum {
ETHA_INFO_INFOMASK, /* u32 */
ETHA_INFO_COMPACT, /* flag */
ETHA_INFO_DRVINFO, /* nest - ETHA_DRVINFO_* */
+ ETHA_INFO_TSINFO, /* nest - ETHA_TSINFO_* */

__ETHA_INFO_CNT,
ETHA_INFO_MAX = (__ETHA_INFO_CNT - 1)
};

#define ETH_INFO_IM_DRVINFO (1U << 0)
+#define ETH_INFO_IM_TSINFO (1U << 1)

-#define ETH_INFO_IM_ALL (ETH_INFO_IM_DRVINFO)
+#define ETH_INFO_IM_ALL (ETH_INFO_IM_DRVINFO | \
+ ETH_INFO_IM_TSINFO)

enum {
ETHA_DRVINFO_UNSPEC,
@@ -171,6 +174,17 @@ enum {
ETHA_DRVINFO_MAX = (__ETHA_DRVINFO_CNT - 1)
};

+enum {
+ ETHA_TSINFO_UNSPEC,
+ ETHA_TSINFO_TIMESTAMPING, /* bitset */
+ ETHA_TSINFO_PHC_INDEX, /* u32 */
+ ETHA_TSINFO_TX_TYPES, /* bitset */
+ ETHA_TSINFO_RX_FILTERS, /* bitset */
+
+ __ETHA_TSINFO_CNT,
+ ETHA_TSINFO_MAX = (__ETHA_TSINFO_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/include/uapi/linux/net_tstamp.h b/include/uapi/linux/net_tstamp.h
index e5b39721c6e4..e5b0472c4416 100644
--- a/include/uapi/linux/net_tstamp.h
+++ b/include/uapi/linux/net_tstamp.h
@@ -30,6 +30,9 @@ enum {
SOF_TIMESTAMPING_OPT_STATS = (1<<12),
SOF_TIMESTAMPING_OPT_PKTINFO = (1<<13),
SOF_TIMESTAMPING_OPT_TX_SWHW = (1<<14),
+ /* when adding a flag, please update so_timestamping_labels array
+ * in net/ethtool/info.c
+ */

SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_TX_SWHW,
SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) |
@@ -90,6 +93,11 @@ enum hwtstamp_tx_types {
* queue.
*/
HWTSTAMP_TX_ONESTEP_SYNC,
+ /* when adding a value, please update tstamp_tx_type_labels array
+ * in net/ethtool/info.c
+ */
+
+ HWTSTAMP_TX_LAST = HWTSTAMP_TX_ONESTEP_SYNC
};

/* possible values for hwtstamp_config->rx_filter */
@@ -132,6 +140,11 @@ enum hwtstamp_rx_filters {

/* NTP, UDP, all versions and packet modes */
HWTSTAMP_FILTER_NTP_ALL,
+ /* when adding a value, please update tstamp_rx_filter_labels array
+ * in net/ethtool/info.c
+ */
+
+ HWTSTAMP_FILTER_LAST = HWTSTAMP_FILTER_NTP_ALL
};

/* SCM_TIMESTAMPING_PKTINFO control message */
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 8da992129321..7e846f4151f9 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note

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

@@ -133,3 +135,25 @@ int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)

return 0;
}
+
+int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct phy_device *phydev = dev->phydev;
+ int err = 0;
+
+ memset(info, 0, sizeof(*info));
+ info->cmd = ETHTOOL_GET_TS_INFO;
+
+ if (phydev && phydev->drv && phydev->drv->ts_info) {
+ err = phydev->drv->ts_info(phydev, info);
+ } else if (ops->get_ts_info) {
+ err = ops->get_ts_info(dev, info);
+ } else {
+ info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE;
+ info->phc_index = -1;
+ }
+
+ return err;
+}
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index e87e58b3a274..02cbee79da35 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -16,4 +16,6 @@ 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);
+int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info);
+
#endif /* _ETHTOOL_COMMON_H */
diff --git a/net/ethtool/info.c b/net/ethtool/info.c
index cc42993bb05b..68de78533ec7 100644
--- a/net/ethtool/info.c
+++ b/net/ethtool/info.c
@@ -1,15 +1,61 @@
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note

+#include <linux/net_tstamp.h>
+
#include "netlink.h"
#include "common.h"
#include "bitset.h"

+const char *const so_timestamping_labels[] = {
+ "hardware-transmit", /* SOF_TIMESTAMPING_TX_HARDWARE */
+ "software-transmit", /* SOF_TIMESTAMPING_TX_SOFTWARE */
+ "hardware-receive", /* SOF_TIMESTAMPING_RX_HARDWARE */
+ "software-receive", /* SOF_TIMESTAMPING_RX_SOFTWARE */
+ "software-system-clock", /* SOF_TIMESTAMPING_SOFTWARE */
+ "hardware-legacy-clock", /* SOF_TIMESTAMPING_SYS_HARDWARE */
+ "hardware-raw-clock", /* SOF_TIMESTAMPING_RAW_HARDWARE */
+ "option-id", /* SOF_TIMESTAMPING_OPT_ID */
+ "sched-transmit", /* SOF_TIMESTAMPING_TX_SCHED */
+ "ack-transmit", /* SOF_TIMESTAMPING_TX_ACK */
+ "option-cmsg", /* SOF_TIMESTAMPING_OPT_CMSG */
+ "option-tsonly", /* SOF_TIMESTAMPING_OPT_TSONLY */
+ "option-stats", /* SOF_TIMESTAMPING_OPT_STATS */
+ "option-pktinfo", /* SOF_TIMESTAMPING_OPT_PKTINFO */
+ "option-tx-swhw", /* SOF_TIMESTAMPING_OPT_TX_SWHW */
+};
+
+const char *const tstamp_tx_type_labels[] = {
+ [HWTSTAMP_TX_OFF] = "off",
+ [HWTSTAMP_TX_ON] = "on",
+ [HWTSTAMP_TX_ONESTEP_SYNC] = "one-step-sync",
+};
+
+const char *const tstamp_rx_filter_labels[] = {
+ [HWTSTAMP_FILTER_NONE] = "none",
+ [HWTSTAMP_FILTER_ALL] = "all",
+ [HWTSTAMP_FILTER_SOME] = "some",
+ [HWTSTAMP_FILTER_PTP_V1_L4_EVENT] = "ptpv1-l4-event",
+ [HWTSTAMP_FILTER_PTP_V1_L4_SYNC] = "ptpv1-l4-sync",
+ [HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ] = "ptpv1-l4-delay-req",
+ [HWTSTAMP_FILTER_PTP_V2_L4_EVENT] = "ptpv2-l4-event",
+ [HWTSTAMP_FILTER_PTP_V2_L4_SYNC] = "ptpv2-l4-sync",
+ [HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ] = "ptpv2-l4-delay-req",
+ [HWTSTAMP_FILTER_PTP_V2_L2_EVENT] = "ptpv2-l2-event",
+ [HWTSTAMP_FILTER_PTP_V2_L2_SYNC] = "ptpv2-l2-sync",
+ [HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ] = "ptpv2-l2-delay-req",
+ [HWTSTAMP_FILTER_PTP_V2_EVENT] = "ptpv2-event",
+ [HWTSTAMP_FILTER_PTP_V2_SYNC] = "ptpv2-sync",
+ [HWTSTAMP_FILTER_PTP_V2_DELAY_REQ] = "ptpv2-delay-req",
+ [HWTSTAMP_FILTER_NTP_ALL] = "ntp-all",
+};
+
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;
+ struct ethtool_ts_info tsinfo;
};

static const struct nla_policy get_info_policy[ETHA_INFO_MAX + 1] = {
@@ -18,6 +64,7 @@ static const struct nla_policy get_info_policy[ETHA_INFO_MAX + 1] = {
[ETHA_INFO_INFOMASK] = { .type = NLA_U32 },
[ETHA_INFO_COMPACT] = { .type = NLA_FLAG },
[ETHA_INFO_DRVINFO] = { .type = NLA_REJECT },
+ [ETHA_INFO_TSINFO] = { .type = NLA_REJECT },
};

/* parse_request() handler */
@@ -67,6 +114,11 @@ static int prepare_info(struct common_req_info *req_info,
if (ret < 0)
req_mask &= ~ETH_INFO_IM_DRVINFO;
}
+ if (req_mask & ETH_INFO_IM_TSINFO) {
+ ret = __ethtool_get_ts_info(dev, &data->tsinfo);
+ if (ret < 0)
+ req_mask &= ~ETH_INFO_IM_TSINFO;
+ }
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -87,6 +139,42 @@ static int drvinfo_size(const struct ethtool_drvinfo *drvinfo)
return nla_total_size(len);
}

+static int tsinfo_size(const struct ethtool_ts_info *tsinfo, bool compact)
+{
+ const unsigned int flags = compact ? ETHNL_BITSET_COMPACT : 0;
+ int len = 0;
+ int ret;
+
+ /* if any of these exceeds 32, we need a different interface to talk to
+ * NIC drivers anyway
+ */
+ BUILD_BUG_ON(__SOF_TIMESTAMPING_COUNT > 32);
+ BUILD_BUG_ON(__HWTSTAMP_TX_COUNT > 32);
+ BUILD_BUG_ON(__HWTSTAMP_FILTER_COUNT > 32);
+
+ ret = ethnl_bitset32_size(__SOF_TIMESTAMPING_COUNT,
+ &tsinfo->so_timestamping, NULL,
+ so_timestamping_labels, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ ret = ethnl_bitset32_size(__HWTSTAMP_TX_COUNT,
+ &tsinfo->tx_types, NULL,
+ tstamp_tx_type_labels, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ ret = ethnl_bitset32_size(__HWTSTAMP_FILTER_COUNT,
+ &tsinfo->rx_filters, NULL,
+ tstamp_rx_filter_labels, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ len += nla_total_size(sizeof(u32));
+
+ return nla_total_size(len);
+}
+
/* reply_size() handler */
static int info_size(const struct common_req_info *req_info)
{
@@ -98,6 +186,13 @@ static int info_size(const struct common_req_info *req_info)
len += dev_ident_size();
if (info_mask & ETH_INFO_IM_DRVINFO)
len += drvinfo_size(&data->drvinfo);
+ if (info_mask & ETH_INFO_IM_TSINFO) {
+ int ret = tsinfo_size(&data->tsinfo, req_info->compact);
+
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }

return len;
}
@@ -126,6 +221,44 @@ static int fill_drvinfo(struct sk_buff *skb,
return ret;
}

+static int fill_tsinfo(struct sk_buff *skb,
+ const struct ethtool_ts_info *tsinfo, bool compact)
+{
+ const unsigned int flags = compact ? ETHNL_BITSET_COMPACT : 0;
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_INFO_TSINFO);
+ int ret;
+
+ if (!nest)
+ return -EMSGSIZE;
+ ret = ethnl_put_bitset32(skb, ETHA_TSINFO_TIMESTAMPING,
+ __SOF_TIMESTAMPING_COUNT,
+ &tsinfo->so_timestamping, NULL,
+ so_timestamping_labels, flags);
+ if (ret < 0)
+ goto err;
+ ret = -EMSGSIZE;
+ if (tsinfo->phc_index >= 0 &&
+ nla_put_u32(skb, ETHA_TSINFO_PHC_INDEX, tsinfo->phc_index))
+ goto err;
+
+ ret = ethnl_put_bitset32(skb, ETHA_TSINFO_TX_TYPES, __HWTSTAMP_TX_COUNT,
+ &tsinfo->tx_types, NULL, tstamp_tx_type_labels,
+ flags);
+ if (ret < 0)
+ goto err;
+ ret = ethnl_put_bitset32(skb, ETHA_TSINFO_RX_FILTERS,
+ __HWTSTAMP_FILTER_COUNT, &tsinfo->rx_filters,
+ NULL, tstamp_rx_filter_labels, flags);
+ if (ret < 0)
+ goto err;
+
+ nla_nest_end(skb, nest);
+ return 0;
+err:
+ nla_nest_cancel(skb, nest);
+ return ret;
+}
+
/* fill_reply() handler */
static int fill_info(struct sk_buff *skb,
const struct common_req_info *req_info)
@@ -140,6 +273,11 @@ static int fill_info(struct sk_buff *skb,
if (ret < 0)
return ret;
}
+ if (info_mask & ETH_INFO_IM_TSINFO) {
+ ret = fill_tsinfo(skb, &data->tsinfo, req_info->compact);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 844a4f4b1552..18721b5aa353 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -2027,28 +2027,12 @@ static int ethtool_get_dump_data(struct net_device *dev,

static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr)
{
- int err = 0;
+ int err;
struct ethtool_ts_info info;
- const struct ethtool_ops *ops = dev->ethtool_ops;
- struct phy_device *phydev = dev->phydev;
-
- memset(&info, 0, sizeof(info));
- info.cmd = ETHTOOL_GET_TS_INFO;
-
- if (phydev && phydev->drv && phydev->drv->ts_info) {
- err = phydev->drv->ts_info(phydev, &info);
- } else if (ops->get_ts_info) {
- err = ops->get_ts_info(dev, &info);
- } else {
- info.so_timestamping =
- SOF_TIMESTAMPING_RX_SOFTWARE |
- SOF_TIMESTAMPING_SOFTWARE;
- info.phc_index = -1;
- }

+ err = __ethtool_get_ts_info(dev, &info);
if (err)
return err;
-
if (copy_to_user(useraddr, &info, sizeof(info)))
err = -EFAULT;

diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 48980ae9e963..4ad9a3eee3e5 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -7,14 +7,23 @@
#include <linux/netdevice.h>
#include <net/genetlink.h>
#include <net/sock.h>
+#include <linux/net_tstamp.h>

#define ETHNL_SET_ERRMSG(info, msg) \
do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)

+#define __SOF_TIMESTAMPING_COUNT (const_ilog2(SOF_TIMESTAMPING_LAST) + 1)
+#define __HWTSTAMP_TX_COUNT (HWTSTAMP_TX_LAST + 1)
+#define __HWTSTAMP_FILTER_COUNT (HWTSTAMP_FILTER_LAST + 1)
+
extern u32 ethnl_bcast_seq;

extern struct genl_family ethtool_genl_family;

+extern const char *const so_timestamping_labels[];
+extern const char *const tstamp_tx_type_labels[];
+extern const char *const tstamp_rx_filter_labels[];
+
struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest);
int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype);

diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c
index 808ab6b4b387..e1b831097ca7 100644
--- a/net/ethtool/strset.c
+++ b/net/ethtool/strset.c
@@ -67,6 +67,24 @@ static const struct strset_info info_template[] = {
.count = ARRAY_SIZE(phy_tunable_strings),
.data = { .legacy = phy_tunable_strings },
},
+ [ETH_SS_TSTAMP_SOF] = {
+ .type = ETH_SS_TYPE_SIMPLE,
+ .per_dev = false,
+ .count = __SOF_TIMESTAMPING_COUNT,
+ .data = { .simple = so_timestamping_labels },
+ },
+ [ETH_SS_TSTAMP_TX_TYPE] = {
+ .type = ETH_SS_TYPE_SIMPLE,
+ .per_dev = false,
+ .count = __HWTSTAMP_TX_COUNT,
+ .data = { .simple = tstamp_tx_type_labels },
+ },
+ [ETH_SS_TSTAMP_RX_FILTER] = {
+ .type = ETH_SS_TYPE_SIMPLE,
+ .per_dev = false,
+ .count = __HWTSTAMP_FILTER_COUNT,
+ .data = { .simple = tstamp_rx_filter_labels },
+ },
};

struct strset_data {
--
2.21.0


2019-03-25 17:11:27

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

Requests a contents of one or more string sets, i.e. indexed arrays of
strings; this information is provided by ETHTOOL_GSSET_INFO and
ETHTOOL_GSTRINGS commands of ioctl interface. There are three types of
requests:

- no NLM_F_DUMP, no device: get "global" stringsets
- no NLM_F_DUMP, with device: get string sets related to the device
- NLM_F_DUMP, no device: get device related string sets for all devices

It's possible to request all string sets of given type or only specific
sets. With ETHA_STRSET_COUNTS flag, only set sizes (number of strings) are
returned.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 46 +-
include/uapi/linux/ethtool.h | 2 +
include/uapi/linux/ethtool_netlink.h | 43 ++
net/ethtool/Makefile | 2 +-
net/ethtool/netlink.c | 8 +
net/ethtool/netlink.h | 4 +
net/ethtool/strset.c | 447 +++++++++++++++++++
7 files changed, 549 insertions(+), 3 deletions(-)
create mode 100644 net/ethtool/strset.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 5e5d785fe215..1508c16a236e 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -127,6 +127,8 @@ List of message types
---------------------

ETHNL_CMD_EVENT notification only
+ ETHNL_CMD_GET_STRSET
+ ETHNL_CMD_SET_STRSET response only

All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
to indicate the type.
@@ -167,6 +169,46 @@ and also multiple events of the same type (e.g. two or more newly registered
devices).


+GET_STRSET
+----------
+
+Requests contents of a string set as provided by ioctl commands
+ETHTOOL_GSSET_INFO and ETHTOOL_GSTRINGS. String sets are not user writeable so
+that the corresponding SET_STRSET message is only used in kernel replies.
+There are two types of string sets: global (independent of a device, e.g.
+device feature names) and device specific (e.g. device private flags).
+
+Request contents:
+
+ ETHA_STRSET_DEV (nested) device identification
+ ETHA_STRSET_COUNTS (flag) request only string counts
+ ETHA_STRSET_STRINGSET (nested) string set to request
+ ETHA_STRINGSET_ID (u32) set id
+
+Kernel response contents:
+
+ ETHA_STRSET_DEV (nested) device identification
+ ETHA_STRSET_STRINGSET (nested) string set to request
+ ETHA_STRINGSET_ID (u32) set id
+ ETHA_STRINGSET_COUNT (u32) number of strings
+ ETHA_STRINGSET_STRINGS (nested) array of strings
+ ETHA_STRING_INDEX (u32) string index
+ ETHA_STRING_VALUE (string) string value
+
+ETHA_STRSET_DEV, if present, identifies the device to request device specific
+string sets for. Depending on its presence a and NLM_F_DUMP flag, there are
+three type of GET_STRSET requests:
+
+ - no NLM_F_DUMP, no device: get "global" stringsets
+ - no NLM_F_DUMP, with device: get string sets related to the device
+ - NLM_F_DUMP, no device: get device related string sets for all devices
+
+If there is no ETHA_STRSET_STRINGSET attribute, all string sets of requested
+type are returned, otherwise only those specified in the request. Flag
+ETHA_STRSET_COUNTS tells kernel to only return string counts of the sets, not
+the actual strings.
+
+
Request translation
-------------------

@@ -201,7 +243,7 @@ ETHTOOL_STXCSUM n/a
ETHTOOL_GSG n/a
ETHTOOL_SSG n/a
ETHTOOL_TEST n/a
-ETHTOOL_GSTRINGS n/a
+ETHTOOL_GSTRINGS ETHNL_CMD_GET_STRSET
ETHTOOL_PHYS_ID n/a
ETHTOOL_GSTATS n/a
ETHTOOL_GTSO n/a
@@ -229,7 +271,7 @@ ETHTOOL_FLASHDEV n/a
ETHTOOL_RESET n/a
ETHTOOL_SRXNTUPLE n/a
ETHTOOL_GRXNTUPLE n/a
-ETHTOOL_GSSET_INFO n/a
+ETHTOOL_GSSET_INFO ETHNL_CMD_GET_STRSET
ETHTOOL_GRXFHINDIR n/a
ETHTOOL_SRXFHINDIR n/a
ETHTOOL_GFEATURES n/a
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 3652b239dad1..eaea804972f6 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -574,6 +574,8 @@ enum ethtool_stringset {
ETH_SS_TUNABLES,
ETH_SS_PHY_STATS,
ETH_SS_PHY_TUNABLES,
+
+ ETH_SS_COUNT
};

/**
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 988519bc6e37..66aeb436b822 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -14,6 +14,8 @@
enum {
ETHNL_CMD_NOOP,
ETHNL_CMD_EVENT, /* only for notifications */
+ ETHNL_CMD_GET_STRSET,
+ ETHNL_CMD_SET_STRSET, /* only for reply */

__ETHNL_CMD_CNT,
ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
@@ -98,6 +100,47 @@ enum {
ETHA_EVENT_MAX = (__ETHA_EVENT_CNT - 1)
};

+/* string sets */
+
+enum {
+ ETHA_STRING_UNSPEC,
+ ETHA_STRING_INDEX, /* u32 */
+ ETHA_STRING_VALUE, /* string */
+
+ __ETHA_STRING_CNT,
+ ETHA_STRING_MAX = (__ETHA_STRING_CNT - 1)
+};
+
+enum {
+ ETHA_STRINGS_UNSPEC,
+ ETHA_STRINGS_STRING, /* nest - ETHA_STRINGS_* */
+
+ __ETHA_STRINGS_CNT,
+ ETHA_STRINGS_MAX = (__ETHA_STRINGS_CNT - 1)
+};
+
+enum {
+ ETHA_STRINGSET_UNSPEC,
+ ETHA_STRINGSET_ID, /* u32 */
+ ETHA_STRINGSET_COUNT, /* u32 */
+ ETHA_STRINGSET_STRINGS, /* nest - ETHA_STRINGS_* */
+
+ __ETHA_STRINGSET_CNT,
+ ETHA_STRINGSET_MAX = (__ETHA_STRINGSET_CNT - 1)
+};
+
+/* GET_STRINGSET / SET_STRINGSET */
+
+enum {
+ ETHA_STRSET_UNSPEC,
+ ETHA_STRSET_DEV, /* nest - ETHA_DEV_* */
+ ETHA_STRSET_COUNTS, /* flag */
+ ETHA_STRSET_STRINGSET, /* nest - ETHA_STRSET_* */
+
+ __ETHA_STRSET_CNT,
+ ETHA_STRSET_MAX = (__ETHA_STRSET_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 11782306593b..11ceb00821b3 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
+ethtool_nl-y := netlink.o bitset.o strset.o
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 7371cdf98ce9..63ee0f846b50 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -154,6 +154,7 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
/* GET request helpers */

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

/**
@@ -560,6 +561,13 @@ static struct notifier_block ethnl_netdev_notifier = {
/* genetlink setup */

static const struct genl_ops ethtool_genl_ops[] = {
+ {
+ .cmd = ETHNL_CMD_GET_STRSET,
+ .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[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 625f912144b1..32d85bb5c49a 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -275,4 +275,8 @@ struct get_request_ops {
void (*cleanup)(struct common_req_info *req_info);
};

+/* request handlers */
+
+extern const struct get_request_ops strset_request_ops;
+
#endif /* _NET_ETHTOOL_NETLINK_H */
diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c
new file mode 100644
index 000000000000..808ab6b4b387
--- /dev/null
+++ b/net/ethtool/strset.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include "netlink.h"
+#include "common.h"
+
+enum strset_type {
+ ETH_SS_TYPE_NONE,
+ ETH_SS_TYPE_LEGACY,
+ ETH_SS_TYPE_SIMPLE,
+};
+
+struct strset_info {
+ enum strset_type type;
+ bool per_dev;
+ bool free_data;
+ unsigned int count;
+ union {
+ const char (*legacy)[ETH_GSTRING_LEN];
+ const char * const *simple;
+ void *ptr;
+ } data;
+};
+
+static const struct strset_info info_template[] = {
+ [ETH_SS_TEST] = {
+ .type = ETH_SS_TYPE_LEGACY,
+ .per_dev = true,
+ },
+ [ETH_SS_STATS] = {
+ .type = ETH_SS_TYPE_LEGACY,
+ .per_dev = true,
+ },
+ [ETH_SS_PRIV_FLAGS] = {
+ .type = ETH_SS_TYPE_LEGACY,
+ .per_dev = true,
+ },
+ [ETH_SS_NTUPLE_FILTERS] = {
+ .type = ETH_SS_TYPE_NONE,
+ },
+ [ETH_SS_FEATURES] = {
+ .type = ETH_SS_TYPE_LEGACY,
+ .per_dev = false,
+ .count = ARRAY_SIZE(netdev_features_strings),
+ .data = { .legacy = netdev_features_strings },
+ },
+ [ETH_SS_RSS_HASH_FUNCS] = {
+ .type = ETH_SS_TYPE_LEGACY,
+ .per_dev = false,
+ .count = ARRAY_SIZE(rss_hash_func_strings),
+ .data = { .legacy = rss_hash_func_strings },
+ },
+ [ETH_SS_TUNABLES] = {
+ .type = ETH_SS_TYPE_LEGACY,
+ .per_dev = false,
+ .count = ARRAY_SIZE(tunable_strings),
+ .data = { .legacy = tunable_strings },
+ },
+ [ETH_SS_PHY_STATS] = {
+ .type = ETH_SS_TYPE_LEGACY,
+ .per_dev = true,
+ },
+ [ETH_SS_PHY_TUNABLES] = {
+ .type = ETH_SS_TYPE_LEGACY,
+ .per_dev = false,
+ .count = ARRAY_SIZE(phy_tunable_strings),
+ .data = { .legacy = phy_tunable_strings },
+ },
+};
+
+struct strset_data {
+ struct common_req_info reqinfo_base;
+ u32 req_ids;
+ bool counts_only;
+
+ /* everything below here will be reset for each device in dumps */
+ struct common_reply_data repdata_base;
+ struct strset_info info[ETH_SS_COUNT];
+};
+
+static const struct nla_policy get_strset_policy[ETHA_STRSET_MAX + 1] = {
+ [ETHA_STRSET_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_STRSET_DEV] = { .type = NLA_NESTED },
+ [ETHA_STRSET_COUNTS] = { .type = NLA_FLAG },
+ [ETHA_STRSET_STRINGSET] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy get_stringset_policy[ETHA_STRINGSET_MAX + 1] = {
+ [ETHA_STRINGSET_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_STRINGSET_ID] = { .type = NLA_U32 },
+ [ETHA_STRINGSET_COUNT] = { .type = NLA_REJECT },
+ [ETHA_STRINGSET_STRINGS] = { .type = NLA_REJECT },
+};
+
+static bool id_requested(const struct strset_data *data, u32 id)
+{
+ return data->req_ids & (1U << id);
+}
+
+static bool include_set(const struct strset_data *data, u32 id)
+{
+ bool per_dev;
+
+ BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(data->req_ids));
+
+ if (data->req_ids)
+ return id_requested(data, id);
+
+ per_dev = data->info[id].per_dev;
+ if (data->info[id].type == ETH_SS_TYPE_NONE)
+ return false;
+ return data->repdata_base.dev ? per_dev : !per_dev;
+}
+
+const char *str_value(const struct strset_info *info, unsigned int i)
+{
+ switch (info->type) {
+ case ETH_SS_TYPE_LEGACY:
+ return info->data.legacy[i];
+ case ETH_SS_TYPE_SIMPLE:
+ return info->data.simple[i];
+ default:
+ WARN_ONCE(1, "unexpected string set type");
+ return "";
+ }
+}
+
+static int get_strset_id(const struct nlattr *nest, u32 *val,
+ struct genl_info *info)
+{
+ struct nlattr *tb[ETHA_STRINGSET_MAX + 1];
+ int ret;
+
+ ret = nla_parse_nested_strict(tb, ETHA_STRINGSET_MAX, nest,
+ get_stringset_policy,
+ info ? info->extack : NULL);
+ if (ret < 0)
+ return ret;
+ if (!tb[ETHA_STRINGSET_ID])
+ return -EINVAL;
+
+ *val = nla_get_u32(tb[ETHA_STRINGSET_ID]);
+ return 0;
+}
+
+/* parse_request() handler */
+static int parse_strset(struct common_req_info *req_info, struct sk_buff *skb,
+ struct genl_info *info, const struct nlmsghdr *nlhdr)
+{
+ struct strset_data *data =
+ container_of(req_info, struct strset_data, reqinfo_base);
+ struct nlattr *attr;
+ int rem, ret;
+
+ ret = nlmsg_validate(nlhdr, GENL_HDRLEN, ETHA_STRSET_MAX,
+ get_strset_policy, info ? info->extack : NULL);
+ if (ret < 0)
+ return ret;
+
+ nlmsg_for_each_attr(attr, nlhdr, GENL_HDRLEN, rem) {
+ u32 id;
+
+ switch (nla_type(attr)) {
+ case ETHA_STRSET_DEV:
+ req_info->dev = ethnl_dev_get(info, attr);
+ if (IS_ERR(req_info->dev)) {
+ ret = PTR_ERR(req_info->dev);
+ req_info->dev = NULL;
+ return ret;
+ }
+ break;
+ case ETHA_STRSET_COUNTS:
+ data->counts_only = true;
+ break;
+ case ETHA_STRSET_STRINGSET:
+ ret = get_strset_id(attr, &id, info);
+ if (ret < 0)
+ return ret;
+ if (ret >= ETH_SS_COUNT)
+ return -EOPNOTSUPP;
+ data->req_ids |= (1U << id);
+ break;
+ default:
+ ETHNL_SET_ERRMSG(info,
+ "unexpected attribute in ETHNL_CMD_GET_STRSET message");
+ return genl_err_attr(info, -EINVAL, attr);
+ }
+ }
+
+ return 0;
+}
+
+static void free_strset(struct strset_data *data)
+{
+ unsigned int i;
+
+ for (i = 0; i < ETH_SS_COUNT; i++)
+ if (data->info[i].free_data) {
+ kfree(data->info[i].data.ptr);
+ data->info[i].data.ptr = NULL;
+ data->info[i].free_data = false;
+ }
+}
+
+static int prepare_one_stringset(struct strset_info *info,
+ struct net_device *dev, unsigned int id,
+ bool counts_only)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ void *strings;
+ int count, ret;
+
+ if (id == ETH_SS_PHY_STATS && dev->phydev &&
+ !ops->get_ethtool_phy_stats)
+ ret = phy_ethtool_get_sset_count(dev->phydev);
+ else if (ops->get_sset_count && ops->get_strings)
+ ret = ops->get_sset_count(dev, id);
+ else
+ ret = -EOPNOTSUPP;
+ if (ret <= 0) {
+ info->count = 0;
+ return 0;
+ }
+
+ count = ret;
+ if (!counts_only) {
+ strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL);
+ if (!strings)
+ return -ENOMEM;
+ if (id == ETH_SS_PHY_STATS && dev->phydev &&
+ !ops->get_ethtool_phy_stats)
+ phy_ethtool_get_strings(dev->phydev, strings);
+ else
+ ops->get_strings(dev, id, strings);
+ info->data.legacy = strings;
+ info->free_data = true;
+ }
+ info->count = count;
+
+ return 0;
+}
+
+/* prepare_data() handler */
+static int prepare_strset(struct common_req_info *req_info,
+ struct genl_info *info)
+{
+ struct strset_data *data =
+ container_of(req_info, struct strset_data, reqinfo_base);
+ struct net_device *dev = data->repdata_base.dev;
+ unsigned int i;
+ int ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT);
+ memcpy(&data->info, &info_template, sizeof(data->info));
+
+ if (!dev) {
+ for (i = 0; i < ETH_SS_COUNT; i++) {
+ if (id_requested(data, i) &&
+ data->info[i].per_dev) {
+ ETHNL_SET_ERRMSG(info,
+ "requested per device strings without dev");
+ return -EINVAL;
+ }
+ }
+ }
+
+ ret = ethnl_before_ops(dev);
+ if (ret < 0)
+ goto err_strset;
+ for (i = 0; i < ETH_SS_COUNT; i++) {
+ if (!include_set(data, i) || !data->info[i].per_dev)
+ continue;
+ if (WARN_ONCE(data->info[i].type != ETH_SS_TYPE_LEGACY,
+ "unexpected string set type %u",
+ data->info[i].type))
+ goto err_ops;
+
+ ret = prepare_one_stringset(&data->info[i], dev, i,
+ data->counts_only);
+ if (ret < 0)
+ goto err_ops;
+ }
+ ethnl_after_ops(dev);
+
+ return 0;
+err_ops:
+ ethnl_after_ops(dev);
+err_strset:
+ free_strset(data);
+ return ret;
+}
+
+static int legacy_set_size(const char (*set)[ETH_GSTRING_LEN],
+ unsigned int count)
+{
+ unsigned int len = 0;
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ len += nla_total_size(nla_total_size(sizeof(u32)) +
+ ethnl_str_size(set[i]));
+ len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len);
+
+ return nla_total_size(len);
+}
+
+static int simple_set_size(const char * const *set, unsigned int count)
+{
+ unsigned int len = 0;
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ len += nla_total_size(nla_total_size(sizeof(u32)) +
+ ethnl_str_size(set[i]));
+ len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len);
+
+ return nla_total_size(len);
+}
+
+static int set_size(const struct strset_info *info, bool counts_only)
+{
+ if (info->count == 0)
+ return 0;
+ if (counts_only)
+ return nla_total_size(2 * nla_total_size(sizeof(u32)));
+
+ switch (info->type) {
+ case ETH_SS_TYPE_LEGACY:
+ return legacy_set_size(info->data.legacy, info->count);
+ case ETH_SS_TYPE_SIMPLE:
+ return simple_set_size(info->data.simple, info->count);
+ default:
+ return -EINVAL;
+ };
+}
+
+/* reply_size() handler */
+static int strset_size(const struct common_req_info *req_info)
+{
+ const struct strset_data *data =
+ container_of(req_info, struct strset_data, reqinfo_base);
+ unsigned int i;
+ int len = 0;
+ int ret;
+
+ len += dev_ident_size();
+ for (i = 0; i < ETH_SS_COUNT; i++) {
+ const struct strset_info *info = &data->info[i];
+
+ if (!include_set(data, i) || info->type == ETH_SS_TYPE_NONE)
+ continue;
+
+ ret = set_size(info, data->counts_only);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }
+
+ return len;
+}
+
+static int fill_string(struct sk_buff *skb, const struct strset_info *info,
+ u32 idx)
+{
+ struct nlattr *string = ethnl_nest_start(skb, ETHA_STRINGS_STRING);
+
+ if (!string)
+ return -EMSGSIZE;
+ if (nla_put_u32(skb, ETHA_STRING_INDEX, idx) ||
+ nla_put_string(skb, ETHA_STRING_VALUE, str_value(info, idx)))
+ return -EMSGSIZE;
+ nla_nest_end(skb, string);
+
+ return 0;
+}
+
+static int fill_set(struct sk_buff *skb, const struct strset_data *data, u32 id)
+{
+ const struct strset_info *info = &data->info[id];
+ struct nlattr *strings;
+ struct nlattr *nest;
+ unsigned int i = (unsigned int)(-1);
+
+ if (info->type == ETH_SS_TYPE_NONE)
+ return -EOPNOTSUPP;
+ if (info->count == 0)
+ return 0;
+ nest = ethnl_nest_start(skb, ETHA_STRSET_STRINGSET);
+ if (!nest)
+ return -EMSGSIZE;
+
+ if (nla_put_u32(skb, ETHA_STRINGSET_ID, id) ||
+ nla_put_u32(skb, ETHA_STRINGSET_COUNT, info->count))
+ goto err;
+
+ if (!data->counts_only) {
+ strings = ethnl_nest_start(skb, ETHA_STRINGSET_STRINGS);
+ if (!strings)
+ goto err;
+ for (i = 0; i < info->count; i++) {
+ if (fill_string(skb, info, i) < 0)
+ goto err;
+ }
+ nla_nest_end(skb, strings);
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+
+err:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
+/* fill_reply() handler */
+static int fill_strset(struct sk_buff *skb,
+ const struct common_req_info *req_info)
+{
+ const struct strset_data *data =
+ container_of(req_info, struct strset_data, reqinfo_base);
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ETH_SS_COUNT; i++)
+ if (include_set(data, i)) {
+ ret = fill_set(skb, data, i);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct get_request_ops strset_request_ops = {
+ .request_cmd = ETHNL_CMD_GET_STRSET,
+ .reply_cmd = ETHNL_CMD_SET_STRSET,
+ .dev_attrtype = ETHA_STRSET_DEV,
+ .data_size = sizeof(struct strset_data),
+ .repdata_offset = offsetof(struct strset_data, repdata_base),
+ .allow_nodev_do = true,
+
+ .parse_request = parse_strset,
+ .prepare_data = prepare_strset,
+ .reply_size = strset_size,
+ .fill_reply = fill_strset,
+};
--
2.21.0


2019-03-25 17:11:31

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 10/22] ethtool: generic handlers for GET requests

Some parts of processing GET type requests and related notifications are
independent of a command. Provide universal functions so that only four
callbacks need to be defined for each command type:

parse_request() - parse incoming message
prepare_data() - retrieve data from driver or NIC
reply_size() - estimate reply message size
fill_reply() - compose reply message

These callback are defined in an instance of struct get_request_ops.

Signed-off-by: Michal Kubecek <[email protected]>
---
net/ethtool/netlink.c | 319 ++++++++++++++++++++++++++++++++++++++++++
net/ethtool/netlink.h | 111 +++++++++++++++
2 files changed, 430 insertions(+)

diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index d84b9780dfa7..7371cdf98ce9 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -151,6 +151,325 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
return NULL;
}

+/* GET request helpers */
+
+const struct get_request_ops *get_requests[__ETHNL_CMD_CNT] = {
+};
+
+/**
+ * ethnl_alloc_get_data() - Allocate and initialize data for a GET request
+ * @ops: instance of struct get_request_ops describing size and layout
+ *
+ * This initializes only the first part (req_info), second part (reply_data)
+ * is initialized before filling the reply data into it (which is done for
+ * each iteration in dump requests).
+ *
+ * Return: pointer to allocated and initialized data, NULL on error
+ */
+static struct common_req_info *
+ethnl_alloc_get_data(const struct get_request_ops *ops)
+{
+ struct common_req_info *req_info;
+
+ req_info = kmalloc(ops->data_size, GFP_KERNEL);
+ if (!req_info)
+ return NULL;
+
+ memset(req_info, '\0', ops->repdata_offset);
+ req_info->reply_data =
+ (struct common_reply_data *)((char *)req_info +
+ ops->repdata_offset);
+
+ return req_info;
+}
+
+/**
+ * ethnl_free_get_data() - free GET request data
+ * @ops: instance of struct get_request_ops describing the layout
+ * @req_info: pointer to embedded struct common_req_info (at offset 0)
+ *
+ * Calls ->cleanup() handler if defined and frees the data block.
+ */
+static void ethnl_free_get_data(const struct get_request_ops *ops,
+ struct common_req_info *req_info)
+{
+ if (ops->cleanup)
+ ops->cleanup(req_info);
+ kfree(req_info);
+}
+
+/**
+ * ethnl_init_reply_data() - Initialize reply data for GET request
+ * @req_info: pointer to embedded struct common_req_info
+ * @ops: instance of struct get_request_ops describing the layout
+ * @dev: network device to initialize the reply for
+ *
+ * Fills the reply data part with zeros and sets the dev member. Must be called
+ * before calling the ->fill_reply() callback (for each iteration when handling
+ * dump requests).
+ */
+static void ethnl_init_reply_data(const struct common_req_info *req_info,
+ const struct get_request_ops *ops,
+ struct net_device *dev)
+{
+ memset(req_info->reply_data, '\0',
+ ops->data_size - ops->repdata_offset);
+ req_info->reply_data->dev = dev;
+}
+
+/* generic ->doit() handler for GET type requests */
+int ethnl_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ const u8 cmd = info->genlhdr->cmd;
+ struct common_req_info *req_info;
+ const struct get_request_ops *ops;
+ struct sk_buff *rskb;
+ void *reply_payload;
+ int reply_len;
+ int ret;
+
+ ops = get_requests[cmd];
+ if (WARN_ONCE(!ops, "cmd %u has no get_request_ops\n", cmd))
+ return -EOPNOTSUPP;
+ req_info = ethnl_alloc_get_data(ops);
+ if (!req_info)
+ return -ENOMEM;
+ ret = ops->parse_request(req_info, skb, info, info->nlhdr);
+ if (!ops->allow_nodev_do && !req_info->dev) {
+ ETHNL_SET_ERRMSG(info, "device not specified in do request");
+ ret = -EINVAL;
+ }
+ if (ret < 0)
+ goto err_dev;
+ ethnl_init_reply_data(req_info, ops, req_info->dev);
+
+ rtnl_lock();
+ ret = ops->prepare_data(req_info, info);
+ if (ret < 0)
+ goto err_rtnl;
+ reply_len = ops->reply_size(req_info);
+ if (ret < 0)
+ goto err_rtnl;
+ ret = -ENOMEM;
+ rskb = ethnl_reply_init(reply_len, req_info->dev, ops->reply_cmd,
+ ops->dev_attrtype, info, &reply_payload);
+ if (!rskb)
+ goto err_rtnl;
+ ret = ops->fill_reply(rskb, req_info);
+ if (ret < 0)
+ goto err;
+ rtnl_unlock();
+
+ genlmsg_end(rskb, reply_payload);
+ if (req_info->dev)
+ dev_put(req_info->dev);
+ ethnl_free_get_data(ops, req_info);
+ return genlmsg_reply(rskb, info);
+
+err:
+ WARN_ONCE(ret == -EMSGSIZE,
+ "calculated message payload length (%d) not sufficient\n",
+ reply_len);
+ nlmsg_free(rskb);
+ ethnl_free_get_data(ops, req_info);
+err_rtnl:
+ rtnl_unlock();
+err_dev:
+ if (req_info->dev)
+ dev_put(req_info->dev);
+ return ret;
+}
+
+static int ethnl_get_dump_one(struct sk_buff *skb,
+ struct net_device *dev,
+ const struct get_request_ops *ops,
+ struct common_req_info *req_info)
+{
+ int ret;
+
+ ethnl_init_reply_data(req_info, ops, dev);
+ rtnl_lock();
+ ret = ops->prepare_data(req_info, NULL);
+ if (ret < 0)
+ return ret;
+ ret = ethnl_fill_dev(skb, dev, ops->dev_attrtype);
+ if (ret < 0)
+ return ret;
+ ret = ops->fill_reply(skb, req_info);
+ rtnl_unlock();
+
+ req_info->reply_data->dev = NULL;
+ return ret;
+}
+
+/* generic ->dumpit() handler for GET requests; device iteration copied from
+ * rtnl_dump_ifinfo()
+ * cb->args[0]: pointer to struct get_request_ops
+ * cb->args[1]: pointer to request data
+ * cb->args[2]: iteration position - hashbucket
+ * cb->args[3]: iteration position - ifindex
+ */
+int ethnl_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct common_req_info *req_info;
+ const struct get_request_ops *ops;
+ int h, s_h, idx = 0, s_idx;
+ struct hlist_head *head;
+ struct net_device *dev;
+ int ret = 0;
+ void *ehdr;
+
+ ops = (const struct get_request_ops *)cb->args[0];
+ req_info = (struct common_req_info *)cb->args[1];
+ s_h = cb->args[2];
+ s_idx = cb->args[3];
+
+ for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+ idx = 0;
+ head = &net->dev_index_head[h];
+ hlist_for_each_entry(dev, head, index_hlist) {
+ if (idx < s_idx)
+ goto cont;
+ ehdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ &ethtool_genl_family, 0,
+ ops->reply_cmd);
+ ret = ethnl_get_dump_one(skb, dev, ops, req_info);
+ if (ret < 0) {
+ genlmsg_cancel(skb, ehdr);
+ if (ret == -EOPNOTSUPP)
+ goto cont;
+ if (likely(skb->len))
+ goto out;
+ goto out_err;
+ }
+ genlmsg_end(skb, ehdr);
+cont:
+ idx++;
+ }
+ }
+out:
+ ret = skb->len;
+out_err:
+ cb->args[2] = h;
+ cb->args[3] = idx;
+ cb->seq = net->dev_base_seq;
+ nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+
+ return ret;
+}
+
+/* generic ->start() handler for GET requests */
+static int ethnl_get_start(struct netlink_callback *cb)
+{
+ struct common_req_info *req_info;
+ const struct get_request_ops *ops;
+ struct genlmsghdr *ghdr;
+ int ret;
+
+ ghdr = nlmsg_data(cb->nlh);
+ ops = get_requests[ghdr->cmd];
+ if (WARN_ONCE(!ops, "cmd %u has no get_request_ops\n", ghdr->cmd))
+ return -EOPNOTSUPP;
+ req_info = ethnl_alloc_get_data(ops);
+ if (!req_info)
+ return -ENOMEM;
+
+ ret = ops->parse_request(req_info, cb->skb, NULL, cb->nlh);
+ if (req_info->dev) {
+ /* We ignore device specification in dump requests but as the
+ * same parser as for non-dump (doit) requests is used, it
+ * would take reference to the device if it finds one
+ */
+ dev_put(req_info->dev);
+ req_info->dev = NULL;
+ }
+ if (ret < 0)
+ return ret;
+
+ cb->args[0] = (long)ops;
+ cb->args[1] = (long)req_info;
+ cb->args[2] = 0;
+ cb->args[3] = 0;
+
+ return 0;
+}
+
+/* generic ->done() handler for GET requests */
+static int ethnl_get_done(struct netlink_callback *cb)
+{
+ ethnl_free_get_data((const struct get_request_ops *)cb->args[0],
+ (struct common_req_info *)cb->args[1]);
+
+ return 0;
+}
+
+/* generic notification handler */
+static void ethnl_std_notify(struct net_device *dev,
+ struct netlink_ext_ack *extack, unsigned int cmd,
+ u32 req_mask, const void *data)
+{
+ struct common_req_info *req_info;
+ const struct get_request_ops *ops;
+ struct sk_buff *skb;
+ void *reply_payload;
+ int reply_len;
+ int ret;
+
+ ops = get_requests[cmd - 1];
+ if (WARN_ONCE(!ops, "cmd %u has no get_request_ops\n", cmd - 1))
+ return;
+ /* when ethnl_std_notify() is used as notify handler, command id of
+ * corresponding GET request must be one less than cmd argument passed
+ * to ethnl_std_notify()
+ */
+ if (WARN_ONCE(ops->reply_cmd != cmd,
+ "reply_cmd for %u is %u, expected %u\n", cmd - 1,
+ ops->reply_cmd, cmd))
+ return;
+
+ req_info = ethnl_alloc_get_data(ops);
+ if (!req_info)
+ return;
+ req_info->dev = dev;
+ req_info->req_mask = req_mask;
+ req_info->compact = true;
+
+ ethnl_init_reply_data(req_info, ops, dev);
+ ret = ops->prepare_data(req_info, NULL);
+ if (ret < 0)
+ goto err_data;
+ reply_len = ops->reply_size(req_info);
+ if (reply_len < 0)
+ goto err_data;
+ skb = genlmsg_new(reply_len, GFP_KERNEL);
+ if (!skb)
+ goto err_data;
+ reply_payload = genlmsg_put(skb, 0, ++ethnl_bcast_seq,
+ &ethtool_genl_family, 0, ops->reply_cmd);
+ if (!reply_payload)
+ goto err_skb;
+
+ ret = ethnl_fill_dev(skb, dev, ops->dev_attrtype);
+ if (ret < 0)
+ goto err_skb;
+ ret = ops->fill_reply(skb, req_info);
+ if (ret < 0)
+ goto err_skb;
+ ethnl_free_get_data(ops, req_info);
+ genlmsg_end(skb, reply_payload);
+
+ genlmsg_multicast(&ethtool_genl_family, skb, 0, ETHNL_MCGRP_MONITOR,
+ GFP_KERNEL);
+ return;
+
+err_skb:
+ nlmsg_free(skb);
+err_data:
+ ethnl_free_get_data(ops, req_info);
+}
+
/* notifications */

typedef void (*ethnl_notify_handler_t)(struct net_device *dev,
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 5f2299548915..625f912144b1 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -164,4 +164,115 @@ static inline unsigned int dev_ident_size(void)
nla_total_size(IFNAMSIZ));
}

+/* GET request handling */
+
+struct common_reply_data;
+
+/* The structure holding data for unified processing a GET request consists of
+ * two parts: request info and reply data. Request info starts at offset 0 with
+ * embedded struct common_req_info, is usually filled by ->parse_request() and
+ * is common for all reply messages to one request. Reply data start with
+ * embedded struct common_reply_data and contain data specific to a reply
+ * message (usually one per device for dump requests); this part is filled by
+ * ->prepare_data()
+ */
+
+/**
+ * struct common_req_info - base type of request information for GET requests
+ * @reply_data: pointer to reply data within the same block
+ * @dev: network device the request is for (may be null)
+ * @req_mask: request mask, bitmap of requested information
+ * @compact: true if compact format of bitsets in reply is requested
+ *
+ * This is a common base, additional members may follow after this structure.
+ */
+struct common_req_info {
+ struct common_reply_data *reply_data;
+ struct net_device *dev;
+ u32 req_mask;
+ bool compact;
+};
+
+/**
+ * struct common_reply_data - base type of reply data for GET requests
+ * @dev: device for current reply message; in single shot requests it is
+ * equal to &common_req_info.dev; in dumps it's different for each
+ * reply message
+ * @info_mask: bitmap of information actually provided in reply; it is a subset
+ * of &common_req_info.req_mask with cleared bits corresponding to
+ * information which cannot be provided
+ *
+ * This structure is usually followed by additional members filled by
+ * ->prepare_data() and used by ->cleanup().
+ */
+struct common_reply_data {
+ struct net_device *dev;
+ u32 info_mask;
+};
+
+static inline int ethnl_before_ops(struct net_device *dev)
+{
+ if (dev && dev->ethtool_ops->begin)
+ return dev->ethtool_ops->begin(dev);
+ else
+ return 0;
+}
+
+static inline void ethnl_after_ops(struct net_device *dev)
+{
+ if (dev && dev->ethtool_ops->complete)
+ dev->ethtool_ops->complete(dev);
+}
+
+/**
+ * struct get_request_ops - unified handling of GET requests
+ * @request_cmd: command id for request (GET)
+ * @reply_cmd: command id for reply (SET)
+ * @dev_attr: attribute type for device specification
+ * @data_size: total length of data structure
+ * @repdata_offset: offset of "reply data" part (struct common_reply_data)
+ * @allow_nodev_do: do not fail if device is not specified for non-dump request
+ * @parse_request:
+ * parse request message and fill request info; request info is zero
+ * initialized on entry except reply_data pointer (which is initialized)
+ * @prepare_data:
+ * retrieve data needed to compose a reply message; reply data are zero
+ * initialized on entry except for @dev
+ * @reply_size:
+ * return size of reply message payload without device specification;
+ * returned size may be bigger than actual reply size but it must suffice
+ * to hold the reply
+ * @fill_reply:
+ * fill reply message payload using the data prepared by @prepare_data()
+ * @cleanup
+ * (optional) called when data are no longer needed; use e.g. to free
+ * any additional data structures allocated in prepare_data() which are
+ * not part of the main structure
+ *
+ * Description of variable parts of GET request handling when using the unified
+ * infrastructure. When used, a pointer to an instance of this structure is to
+ * be added to &get_requests array, generic handlers ethnl_get_doit(),
+ * ethnl_get_dumpit(), ethnl_get_start() and ethnl_get_done() used in
+ * @ethnl_genl_ops and (optionally) ethnl_std_notify() as notification handler
+ * in &ethnl_notify_handlers.
+ */
+struct get_request_ops {
+ u8 request_cmd;
+ u8 reply_cmd;
+ u16 dev_attrtype;
+ unsigned int data_size;
+ unsigned int repdata_offset;
+ bool allow_nodev_do;
+
+ int (*parse_request)(struct common_req_info *req_info,
+ struct sk_buff *skb, struct genl_info *info,
+ const struct nlmsghdr *nlhdr);
+ int (*prepare_data)(struct common_req_info *req_info,
+ struct genl_info *info);
+ int (*reply_size)(const struct common_req_info *req_info);
+ int (*fill_reply)(struct sk_buff *skb,
+ const struct common_req_info *req_info);
+ void (*cleanup)(struct common_req_info *req_info);
+};
+
#endif /* _NET_ETHTOOL_NETLINK_H */
--
2.21.0


2019-03-25 17:11:33

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 18/22] ethtool: provide link state in GET_SETTINGS request

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

Note: we cannot use NLA_FLAG for link state as we need three states: off,
on and unknown. The attribute is also encapsulated in a nest to allow
future extensions.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 5 ++-
include/uapi/linux/ethtool_netlink.h | 13 ++++++-
net/ethtool/common.c | 8 +++++
net/ethtool/common.h | 1 +
net/ethtool/ioctl.c | 8 ++---
net/ethtool/settings.c | 37 ++++++++++++++++++++
6 files changed, 66 insertions(+), 6 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index e79ae9fe01be..ebd1a0404828 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -277,6 +277,7 @@ Info mask bits meaning:

ETH_SETTINGS_IM_LINKINFO link settings
ETH_SETTINGS_IM_LINKMODES link modes and related
+ ETH_SETTINGS_IM_LINKSTATE link state

Response contents:

@@ -293,6 +294,8 @@ Response contents:
ETHA_LINKMODES_PEER (bitset) partner link modes
ETHA_LINKMODES_SPEED (u32) link speed (Mb/s)
ETHA_LINKMODES_DUPLEX (u8) duplex mode
+ ETHA_SETTINGS_LINK_STATE (nested) link state
+ ETHA_LINKSTATE_LINK (u8) link on/off/unknown

Most of the attributes and their values have the same meaning as matching
members of the corresponding ioctl structures. For ETHA_LINKMODES_OURS,
@@ -350,7 +353,7 @@ ETHTOOL_SWOL n/a
ETHTOOL_GMSGLVL n/a
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 895ba9cd8924..45c27c7291d0 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -196,6 +196,7 @@ enum {
ETHA_SETTINGS_COMPACT, /* flag */
ETHA_SETTINGS_LINK_INFO, /* nest - ETHA_LINKINFO_* */
ETHA_SETTINGS_LINK_MODES, /* nest - ETHA_LINKMODES_* */
+ ETHA_SETTINGS_LINK_STATE, /* nest - ETHA_LINKSTATE_* */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -203,9 +204,11 @@ enum {

#define ETH_SETTINGS_IM_LINKINFO (1U << 0)
#define ETH_SETTINGS_IM_LINKMODES (1U << 1)
+#define ETH_SETTINGS_IM_LINKSTATE (1U << 2)

#define ETH_SETTINGS_IM_ALL (ETH_SETTINGS_IM_LINKINFO | \
- ETH_SETTINGS_IM_LINKMODES)
+ ETH_SETTINGS_IM_LINKMODES | \
+ ETH_SETTINGS_IM_LINKSTATE)

enum {
ETHA_LINKINFO_UNSPEC,
@@ -231,6 +234,14 @@ enum {
ETHA_LINKMODES_MAX = (__ETHA_LINKMODES_CNT - 1)
};

+enum {
+ ETHA_LINKSTATE_UNSPEC,
+ ETHA_LINKSTATE_LINK, /* u8 */
+
+ __ETHA_LINKSTATE_CNT,
+ ETHA_LINKSTATE_MAX = (__ETHA_LINKSTATE_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index a91a4f00d275..dc907d8b6e43 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -205,3 +205,11 @@ convert_legacy_settings_to_link_ksettings(
= legacy_settings->eth_tp_mdix_ctrl;
return retval;
}
+
+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 7a3e0b10e69a..a5ddd7f5cfce 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -17,6 +17,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_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 be3e34ac8cc7..58669dafeaf9 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1318,12 +1318,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 2e0f425029ef..7a2729f01f79 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -11,6 +11,7 @@ struct settings_data {
struct common_reply_data repdata_base;
struct ethtool_link_ksettings ksettings;
struct ethtool_link_settings *lsettings;
+ int link;
bool lpm_empty;
};

@@ -112,6 +113,7 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_COMPACT] = { .type = NLA_FLAG },
[ETHA_SETTINGS_LINK_INFO] = { .type = NLA_REJECT },
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_LINK_STATE] = { .type = NLA_REJECT },
};

static int parse_settings(struct common_req_info *req_info,
@@ -168,6 +170,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)
@@ -189,6 +192,8 @@ static int prepare_settings(struct common_req_info *req_info,
ethnl_bitmap_to_u32(data->ksettings.link_modes.lp_advertising,
__ETHTOOL_LINK_MODE_MASK_NWORDS);
}
+ if (req_mask & ETH_SETTINGS_IM_LINKSTATE)
+ data->link = __ethtool_get_link(dev);
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -237,6 +242,13 @@ static int link_modes_size(const struct ethtool_link_ksettings *ksettings,
return nla_total_size(len);
}

+static int link_state_size(int link)
+{
+ if (link < 0)
+ return nla_total_size(0);
+ return nla_total_size(nla_total_size(sizeof(u8)));
+}
+
/* 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
@@ -258,6 +270,8 @@ static int settings_size(const struct common_req_info *req_info)
return ret;
len += ret;
}
+ if (info_mask & ETH_SETTINGS_IM_LINKSTATE)
+ len += link_state_size(data->link);

return len;
}
@@ -330,6 +344,23 @@ static int fill_link_modes(struct sk_buff *skb,
return -EMSGSIZE;
}

+static int fill_link_state(struct sk_buff *skb, u8 link)
+{
+ struct nlattr *nest;
+
+ nest = ethnl_nest_start(skb, ETHA_SETTINGS_LINK_STATE);
+ if (!nest)
+ return -EMSGSIZE;
+ if (link >=0 && nla_put_u8(skb, ETHA_LINKSTATE_LINK, link))
+ goto err;
+ nla_nest_end(skb, nest);
+ return 0;
+
+err:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
static int fill_settings(struct sk_buff *skb,
const struct common_req_info *req_info)
{
@@ -350,6 +381,11 @@ static int fill_settings(struct sk_buff *skb,
if (ret < 0)
return ret;
}
+ if (info_mask & ETH_SETTINGS_IM_LINKSTATE) {
+ ret = fill_link_state(skb, data->link);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}
@@ -394,6 +430,7 @@ static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_COMPACT] = { .type = NLA_FLAG },
[ETHA_SETTINGS_LINK_INFO] = { .type = NLA_NESTED },
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_NESTED },
+ [ETHA_SETTINGS_LINK_STATE] = { .type = NLA_REJECT },
};

static int ethnl_set_link_ksettings(struct genl_info *info,
--
2.21.0


2019-03-25 17:11:37

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 09/22] ethtool: implement EVENT notifications

Three types of netlink notifications are introduced:

- ETHA_EVENT_NEWDEV to notify about newly registered network devices
- ETHA_EVENT_DELDEV to notify about unregistered network devices
- ETHA_EVENT_RENAMEDEV to notify about renamed network device

The notifications are triggered by NETDEV_REGISTER, NETDEV_UNREGISTER and
NETDEV_CHANGENAME notifiers.

These notifications are intended for applications and daemons monitoring
ethtool events to allow updating the list of existing devices without
having to open another socket for rtnetlink.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 27 ++++++++
include/uapi/linux/ethtool_netlink.h | 37 +++++++++++
net/ethtool/netlink.c | 65 +++++++++++++++++++-
3 files changed, 128 insertions(+), 1 deletion(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index e97218d820c0..5e5d785fe215 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -126,6 +126,8 @@ which the request applies.
List of message types
---------------------

+ ETHNL_CMD_EVENT notification only
+
All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
to indicate the type.

@@ -137,9 +139,34 @@ messages marked as "response only" in the table above. "Get" messages with
NLM_F_DUMP flags and no device identification dump the information for all
devices supporting the request.

+Type ETHNL_CMD_EVENT is special, these messages are never used in userspace
+requests or kernel replies. They are only sent by kernel to sockets listening
+to "monitor" multicast group to inform userspace about certain events.
+
Later sections describe the format and semantics of these request messages.


+EVENT
+-----
+
+EVENT messages are only used in kernel multicast notifications. Atributes
+correspond to specific event types, the same type can appear multiple times.
+
+ ETHA_EVENT_NEWDEV (nested) new device was registered
+ ETHA_NEWDEV_DEV (nested) new device
+ ETHA_EVENT_DELDEV (nested) device was unregistered
+ ETHA_DELDEV_DEV (nested) removed device
+ ETHA_EVENT_RENAMEDEV (nested) device was renamed
+ ETHA_RENAMEDEV_DEV (nested) renamed device
+
+For ETHA_EVENT_RENAMEDEV, the name ETHA_RENAME_DEV/ETHA_DEV_NAME is the new
+name after the rename.
+
+Userspace application must expect multiple events to be present in one message
+and also multiple events of the same type (e.g. two or more newly registered
+devices).
+
+
Request translation
-------------------

diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 91e4d117957b..988519bc6e37 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -13,6 +13,7 @@

enum {
ETHNL_CMD_NOOP,
+ ETHNL_CMD_EVENT, /* only for notifications */

__ETHNL_CMD_CNT,
ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
@@ -61,6 +62,42 @@ enum {
ETHA_BITSET_MAX = (__ETHA_BITSET_CNT - 1)
};

+/* events */
+
+enum {
+ ETHA_NEWDEV_UNSPEC,
+ ETHA_NEWDEV_DEV, /* nest - ETHA_DEV_* */
+
+ __ETHA_NEWDEV_CNT,
+ ETHA_NEWDEV_MAX = (__ETHA_NEWDEV_CNT - 1)
+};
+
+enum {
+ ETHA_DELDEV_UNSPEC,
+ ETHA_DELDEV_DEV, /* nest - ETHA_DEV_* */
+
+ __ETHA_DELDEV_CNT,
+ ETHA_DELDEV_MAX = (__ETHA_DELDEV_CNT - 1)
+};
+
+enum {
+ ETHA_RENAMEDEV_UNSPEC,
+ ETHA_RENAMEDEV_DEV, /* nest - ETHA_DEV_* */
+
+ __ETHA_RENAMEDEV_CNT,
+ ETHA_RENAMEDEV_MAX = (__ETHA_RENAMEDEV_CNT - 1)
+};
+
+enum {
+ ETHA_EVENT_UNSPEC,
+ ETHA_EVENT_NEWDEV, /* nest - ETHA_NEWDEV_* */
+ ETHA_EVENT_DELDEV, /* nest - ETHA_DELDEV_* */
+ ETHA_EVENT_RENAMEDEV, /* nest - ETHA_RENAMEDEV_* */
+
+ __ETHA_EVENT_CNT,
+ ETHA_EVENT_MAX = (__ETHA_EVENT_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 4a31765165ea..d84b9780dfa7 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -177,6 +177,67 @@ void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
}
EXPORT_SYMBOL(ethtool_notify);

+/* size of NEWDEV/DELDEV notification */
+static inline unsigned int dev_notify_size(void)
+{
+ return nla_total_size(dev_ident_size());
+}
+
+static void ethnl_notify_devlist(struct netdev_notifier_info *info,
+ u16 ev_type, u16 dev_attr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(info);
+ struct sk_buff *skb;
+ struct nlattr *nest;
+ void *ehdr;
+ int ret;
+
+ skb = genlmsg_new(dev_notify_size(), GFP_KERNEL);
+ if (!skb)
+ return;
+ ehdr = genlmsg_put(skb, 0, ++ethnl_bcast_seq, &ethtool_genl_family, 0,
+ ETHNL_CMD_EVENT);
+ if (!ehdr)
+ goto out_skb;
+ nest = ethnl_nest_start(skb, ev_type);
+ if (!nest)
+ goto out_skb;
+ ret = ethnl_fill_dev(skb, dev, dev_attr);
+ if (ret < 0)
+ goto out_skb;
+ nla_nest_end(skb, nest);
+ genlmsg_end(skb, ehdr);
+
+ genlmsg_multicast(&ethtool_genl_family, skb, 0, ETHNL_MCGRP_MONITOR,
+ GFP_KERNEL);
+ return;
+out_skb:
+ nlmsg_free(skb);
+}
+
+static int ethnl_netdev_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ switch (event) {
+ case NETDEV_REGISTER:
+ ethnl_notify_devlist(ptr, ETHA_EVENT_NEWDEV, ETHA_NEWDEV_DEV);
+ break;
+ case NETDEV_UNREGISTER:
+ ethnl_notify_devlist(ptr, ETHA_EVENT_DELDEV, ETHA_DELDEV_DEV);
+ break;
+ case NETDEV_CHANGENAME:
+ ethnl_notify_devlist(ptr, ETHA_EVENT_RENAMEDEV,
+ ETHA_RENAMEDEV_DEV);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ethnl_netdev_notifier = {
+ .notifier_call = ethnl_netdev_event,
+};
+
/* genetlink setup */

static const struct genl_ops ethtool_genl_ops[] = {
@@ -209,7 +270,9 @@ static int __init ethnl_init(void)
return ret;
ethnl_ok = true;

- return 0;
+ ret = register_netdevice_notifier(&ethnl_netdev_notifier);
+ WARN(ret < 0, "ethtool: net device notifier registration failed");
+ return ret;
}

subsys_initcall(ethnl_init);
--
2.21.0


2019-03-25 17:11:38

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 20/22] ethtool: set WoL settings with SET_SETTINGS request

Allow enabling and disabling wake on LAN modes using SET_SETTINGS
request.

ETHA_SETTINGS_WOL nested attribute is used to set or modify enabled WoL
modes and SecureOn(tm) password.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 12 +++-
net/ethtool/settings.c | 60 +++++++++++++++++++-
2 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index bc7f28f0182f..3568d35ad7ec 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -335,6 +335,9 @@ to be passed with SET_SETTINGS request:
ETHA_LINKMODES_OURS (bitset) advertised link modes
ETHA_LINKMODES_SPEED (u32) link speed (Mb/s)
ETHA_LINKMODES_DUPLEX (u8) duplex mode
+ ETHA_SETTINGS_WOL (nested) wake on LAN settings
+ ETHA_WOL_MODES (bitfield32) wake on LAN modes
+ ETHA_WOL_SOPASS (binary) SecureOn(tm) password

ETHA_LINKMODES_OURS bit set allows setting advertised link modes. If
autonegotiation is on (either set now or kept from before), advertised modes
@@ -345,6 +348,13 @@ autoselection is done on ethtool side with ioctl interface, netlink interface
is supposed to allow requesting changes without knowing what exactly kernel
supports.

+ETHA_WOL_MODES bitfield is interpreted in the usual way, i.e. bits set in the
+selector are set to 0 or 1 according to value. To allow the semantics of the
+ioctl interface where the whole bitmap is set rather than only modified,
+selectors may have also bits not supported by device set and an error is only
+issued if any of them is also set in the value (i.e. if userspace tries to
+enable mode not supported by device).
+

Request translation
-------------------
@@ -360,7 +370,7 @@ ETHTOOL_SSET ETHNL_CMD_SET_SETTINGS
ETHTOOL_GDRVINFO ETHNL_CMD_GET_INFO
ETHTOOL_GREGS n/a
ETHTOOL_GWOL ETHNL_CMD_GET_SETTINGS
-ETHTOOL_SWOL n/a
+ETHTOOL_SWOL ETHNL_CMD_SET_SETTINGS
ETHTOOL_GMSGLVL n/a
ETHTOOL_SMSGLVL n/a
ETHTOOL_NWAY_RST n/a
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 56a045e5e916..ea5279dad826 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -108,6 +108,12 @@ static const struct link_mode_info link_mode_params[] = {
__DEFINE_LINK_MODE_PARAMS(200000, CR4, Full),
};

+/* We want to allow ~0 as selector for backward compatibility (to just set
+ * given set of modes, whatever kernel supports) so that we allow all bits
+ * on validation and do our own sanity check later.
+ */
+static u32 all_bits = ~(u32)0;
+
static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_UNSPEC] = { .type = NLA_REJECT },
[ETHA_SETTINGS_DEV] = { .type = NLA_NESTED },
@@ -485,6 +491,14 @@ static const struct nla_policy set_linkmodes_policy[ETHA_LINKMODES_MAX + 1] = {
[ETHA_LINKMODES_DUPLEX] = { .type = NLA_U8 },
};

+static const struct nla_policy set_wol_policy[ETHA_LINKINFO_MAX + 1] = {
+ [ETHA_WOL_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_WOL_MODES] = { .type = NLA_BITFIELD32,
+ .validation_data = &all_bits },
+ [ETHA_WOL_SOPASS] = { .type = NLA_BINARY,
+ .len = SOPASS_MAX },
+};
+
static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_UNSPEC] = { .type = NLA_REJECT },
[ETHA_SETTINGS_DEV] = { .type = NLA_NESTED },
@@ -493,7 +507,7 @@ static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_LINK_INFO] = { .type = NLA_NESTED },
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_NESTED },
[ETHA_SETTINGS_LINK_STATE] = { .type = NLA_REJECT },
- [ETHA_SETTINGS_WOL] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_WOL] = { .type = NLA_NESTED },
};

static int ethnl_set_link_ksettings(struct genl_info *info,
@@ -641,6 +655,43 @@ static int ethnl_update_ksettings(struct genl_info *info, struct nlattr **tb,
return 0;
}

+static int update_wol(struct genl_info *info, struct nlattr *nest,
+ struct net_device *dev)
+{
+ struct nlattr *tb[ETHA_WOL_MAX + 1];
+ struct ethtool_wolinfo wolinfo = {};
+ int ret;
+
+ if (!nest)
+ return 0;
+ ret = nla_parse_nested_strict(tb, ETHA_WOL_MAX, nest, set_wol_policy,
+ info->extack);
+ if (ret < 0)
+ return ret;
+
+ ret = ethnl_get_wol(info, dev, &wolinfo);
+ if (ret < 0) {
+ ETHNL_SET_ERRMSG(info, "failed to get wol settings");
+ return ret;
+ }
+
+ ret = 0;
+ if (ethnl_update_bitfield32(&wolinfo.wolopts, tb[ETHA_WOL_MODES]))
+ ret = 1;
+ if (ethnl_update_binary(wolinfo.sopass, SOPASS_MAX,
+ tb[ETHA_WOL_SOPASS]))
+ ret = 1;
+ if (ret) {
+ int ret2 = dev->ethtool_ops->set_wol(dev, &wolinfo);
+ if (ret2 < 0) {
+ ETHNL_SET_ERRMSG(info, "wol info update failed");
+ ret = ret2;
+ }
+ }
+
+ return ret;
+}
+
int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
@@ -668,6 +719,13 @@ int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
if (ret < 0)
goto out_ops;
}
+ if (tb[ETHA_SETTINGS_WOL]) {
+ ret = update_wol(info, tb[ETHA_SETTINGS_WOL], dev);
+ if (ret < 0)
+ goto out_ops;
+ if (ret)
+ req_mask |= ETH_SETTINGS_IM_WOL;
+ }
ret = 0;

out_ops:
--
2.21.0


2019-03-25 17:11:45

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 22/22] ethtool: set message level with SET_SETTINGS request

Allow setting device message level using ETHA_SETTINGS_DEBUG nested
attribute.

Unlike in ioctl interface "message level" is called "message mask" (as it
is in fact used as a bit mask) and put inside a nested attribute to allow
future extensions.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 8 +++-
net/ethtool/settings.c | 45 +++++++++++++++++++-
2 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 603acfbefe29..20e43d42afdd 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -345,6 +345,8 @@ to be passed with SET_SETTINGS request:
ETHA_SETTINGS_WOL (nested) wake on LAN settings
ETHA_WOL_MODES (bitfield32) wake on LAN modes
ETHA_WOL_SOPASS (binary) SecureOn(tm) password
+ ETHA_SETTINGS_DEBUG (nested) debugging
+ ETHA_DEBUG_MSG_MASK (bitfield32) message mask

ETHA_LINKMODES_OURS bit set allows setting advertised link modes. If
autonegotiation is on (either set now or kept from before), advertised modes
@@ -360,7 +362,9 @@ selector are set to 0 or 1 according to value. To allow the semantics of the
ioctl interface where the whole bitmap is set rather than only modified,
selectors may have also bits not supported by device set and an error is only
issued if any of them is also set in the value (i.e. if userspace tries to
-enable mode not supported by device).
+enable mode not supported by device). ETHA_SETTINGS_MSGLEVEL bitfield also
+allows bits not recognized by kernel in selector as long as the request does
+not attempt to enable them.


Request translation
@@ -379,7 +383,7 @@ ETHTOOL_GREGS n/a
ETHTOOL_GWOL ETHNL_CMD_GET_SETTINGS
ETHTOOL_SWOL ETHNL_CMD_SET_SETTINGS
ETHTOOL_GMSGLVL ETHNL_CMD_GET_SETTINGS
-ETHTOOL_SMSGLVL n/a
+ETHTOOL_SMSGLVL ETHNL_CMD_SET_SETTINGS
ETHTOOL_NWAY_RST n/a
ETHTOOL_GLINK ETHNL_CMD_GET_SETTINGS
ETHTOOL_GEEPROM n/a
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 53409dd8af34..bc7ca03aa205 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -538,6 +538,12 @@ static const struct nla_policy set_wol_policy[ETHA_LINKINFO_MAX + 1] = {
.len = SOPASS_MAX },
};

+static const struct nla_policy set_debug_policy[ETHA_DEBUG_MAX + 1] = {
+ [ETHA_DEBUG_UNSPEC] = { .type = NLA_REJECT },
+ [ETHA_DEBUG_MSG_MASK] = { .type = NLA_BITFIELD32,
+ .validation_data = &all_bits },
+};
+
static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_UNSPEC] = { .type = NLA_REJECT },
[ETHA_SETTINGS_DEV] = { .type = NLA_NESTED },
@@ -547,7 +553,7 @@ static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_NESTED },
[ETHA_SETTINGS_LINK_STATE] = { .type = NLA_REJECT },
[ETHA_SETTINGS_WOL] = { .type = NLA_NESTED },
- [ETHA_SETTINGS_DEBUG] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_DEBUG] = { .type = NLA_NESTED },
};

static int ethnl_set_link_ksettings(struct genl_info *info,
@@ -732,6 +738,36 @@ static int update_wol(struct genl_info *info, struct nlattr *nest,
return ret;
}

+static int update_debug(struct genl_info *info, struct nlattr *nest,
+ struct net_device *dev)
+{
+ struct nlattr *tb[ETHA_DEBUG_MAX + 1];
+ u32 msglevel;
+ int ret;
+
+ if (!nest)
+ return 0;
+ ret = nla_parse_nested_strict(tb, ETHA_DEBUG_MAX, nest,
+ set_debug_policy, info->extack);
+ if (ret < 0)
+ return ret;
+
+ if (!dev->ethtool_ops->get_msglevel ||
+ !dev->ethtool_ops->set_msglevel) {
+ ETHNL_SET_ERRMSG(info,
+ "device does not provide msglvl access");
+ return -EOPNOTSUPP;
+ }
+ ret = 0;
+ msglevel = dev->ethtool_ops->get_msglevel(dev);
+ if (ethnl_update_bitfield32(&msglevel, tb[ETHA_DEBUG_MSG_MASK])) {
+ dev->ethtool_ops->set_msglevel(dev, msglevel);
+ ret = 1;
+ }
+
+ return ret;
+}
+
int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
@@ -766,6 +802,13 @@ int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
if (ret)
req_mask |= ETH_SETTINGS_IM_WOL;
}
+ if (tb[ETHA_SETTINGS_DEBUG]) {
+ ret = update_debug(info, tb[ETHA_SETTINGS_DEBUG], dev);
+ if (ret < 0)
+ goto out_ops;
+ if (ret)
+ req_mask |= ETH_SETTINGS_IM_DEBUG;
+ }
ret = 0;

out_ops:
--
2.21.0


2019-03-25 17:12:28

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 02/22] netlink: introduce nla_put_bitfield32()

Similar to other data types, this helper puts NLA_BITFIELD32 attribute into
a netlink message. It takes separate value and selector arguments, if there
is a struct nla_bitfield32 already, one can use nla_put().

Signed-off-by: Michal Kubecek <[email protected]>
---
include/net/netlink.h | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 23f27b0b3cef..bc0497076bec 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -1211,6 +1211,21 @@ static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype,
return nla_put(skb, attrtype, sizeof(*addr), addr);
}

+/**
+ * nla_put_bitfield32 - Add a bitfield32 value/selector attribute to
+ * a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @value: 32-bit value bitmap
+ * @selector: 32-bit selector bitmap
+ */
+static inline int nla_put_bitfield32(struct sk_buff *skb, int attrtype,
+ u32 value, u32 selector)
+{
+ struct nla_bitfield32 tmp = { .value = value, .selector = selector };
+
+ return nla_put(skb, attrtype, sizeof(tmp), &tmp);
+}
+
/**
* nla_get_u32 - return payload of u32 attribute
* @nla: u32 netlink attribute
--
2.21.0


2019-03-25 17:12:39

by Michal Kubecek

[permalink] [raw]
Subject: [PATCH net-next v5 01/22] rtnetlink: provide permanent hardware address in RTM_NEWLINK

Permanent hardware address of a network device was traditionally provided
via ethtool ioctl interface but as Jiri Pirko pointed out in a review of
ethtool netlink interface, rtnetlink is much more suitable for it so let's
add it to the RTM_NEWLINK message.

As permanent address is not modifiable, reject userspace requests
containing IFLA_PERM_ADDRESS attribute.

Note: we already provide permanent hardware address for bond slaves;
unfortunately we cannot drop that attribute for backward compatibility
reasons.

Signed-off-by: Michal Kubecek <[email protected]>
---
include/uapi/linux/if_link.h | 1 +
net/core/rtnetlink.c | 4 ++++
2 files changed, 5 insertions(+)

diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 5b225ff63b48..351ef746b8b0 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -167,6 +167,7 @@ enum {
IFLA_NEW_IFINDEX,
IFLA_MIN_MTU,
IFLA_MAX_MTU,
+ IFLA_PERM_ADDRESS,
__IFLA_MAX
};

diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index a51cab95ba64..a72e8f4d777b 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1026,6 +1026,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ nla_total_size(4) /* IFLA_CARRIER_DOWN_COUNT */
+ nla_total_size(4) /* IFLA_MIN_MTU */
+ nla_total_size(4) /* IFLA_MAX_MTU */
+ + nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */
+ 0;
}

@@ -1683,6 +1684,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
nla_put_s32(skb, IFLA_NEW_IFINDEX, new_ifindex) < 0)
goto nla_put_failure;

+ if (nla_put(skb, IFLA_PERM_ADDRESS, dev->addr_len, dev->perm_addr))
+ goto nla_put_failure;

rcu_read_lock();
if (rtnl_fill_link_af(skb, dev, ext_filter_mask))
@@ -1742,6 +1745,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_CARRIER_DOWN_COUNT] = { .type = NLA_U32 },
[IFLA_MIN_MTU] = { .type = NLA_U32 },
[IFLA_MAX_MTU] = { .type = NLA_U32 },
+ [IFLA_PERM_ADDRESS] = { .type = NLA_REJECT },
};

static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
--
2.21.0


2019-03-26 10:20:26

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 01/22] rtnetlink: provide permanent hardware address in RTM_NEWLINK

Mon, Mar 25, 2019 at 06:07:57PM CET, [email protected] wrote:
>Permanent hardware address of a network device was traditionally provided
>via ethtool ioctl interface but as Jiri Pirko pointed out in a review of
>ethtool netlink interface, rtnetlink is much more suitable for it so let's
>add it to the RTM_NEWLINK message.
>
>As permanent address is not modifiable, reject userspace requests
>containing IFLA_PERM_ADDRESS attribute.
>
>Note: we already provide permanent hardware address for bond slaves;
>unfortunately we cannot drop that attribute for backward compatibility
>reasons.
>
>Signed-off-by: Michal Kubecek <[email protected]>
>---
> include/uapi/linux/if_link.h | 1 +
> net/core/rtnetlink.c | 4 ++++
> 2 files changed, 5 insertions(+)
>
>diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
>index 5b225ff63b48..351ef746b8b0 100644
>--- a/include/uapi/linux/if_link.h
>+++ b/include/uapi/linux/if_link.h
>@@ -167,6 +167,7 @@ enum {
> IFLA_NEW_IFINDEX,
> IFLA_MIN_MTU,
> IFLA_MAX_MTU,
>+ IFLA_PERM_ADDRESS,
> __IFLA_MAX
> };
>
>diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
>index a51cab95ba64..a72e8f4d777b 100644
>--- a/net/core/rtnetlink.c
>+++ b/net/core/rtnetlink.c
>@@ -1026,6 +1026,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
> + nla_total_size(4) /* IFLA_CARRIER_DOWN_COUNT */
> + nla_total_size(4) /* IFLA_MIN_MTU */
> + nla_total_size(4) /* IFLA_MAX_MTU */
>+ + nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */
> + 0;
> }
>
>@@ -1683,6 +1684,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
> nla_put_s32(skb, IFLA_NEW_IFINDEX, new_ifindex) < 0)
> goto nla_put_failure;
>
>+ if (nla_put(skb, IFLA_PERM_ADDRESS, dev->addr_len, dev->perm_addr))
>+ goto nla_put_failure;

I don't think we should put permaddr if driver did not set it. 2
solutions:
1) provide a helper that driver will use to set the perm_addr. This
helper sets a "valid bit". Then you only put IFLA_PERM_ADDRESS
in case the "valid bit" is set.
2) Assuming that no driver would set permaddr to all zeroes,
don't put IFLA_PERM_ADDRESS in case permadd is all zeroes.

>
> rcu_read_lock();
> if (rtnl_fill_link_af(skb, dev, ext_filter_mask))
>@@ -1742,6 +1745,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
> [IFLA_CARRIER_DOWN_COUNT] = { .type = NLA_U32 },
> [IFLA_MIN_MTU] = { .type = NLA_U32 },
> [IFLA_MAX_MTU] = { .type = NLA_U32 },
>+ [IFLA_PERM_ADDRESS] = { .type = NLA_REJECT },
> };
>
> static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
>--
>2.21.0
>

2019-03-26 10:22:44

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 02/22] netlink: introduce nla_put_bitfield32()

Mon, Mar 25, 2019 at 06:08:00PM CET, [email protected] wrote:
>Similar to other data types, this helper puts NLA_BITFIELD32 attribute into
>a netlink message. It takes separate value and selector arguments, if there
>is a struct nla_bitfield32 already, one can use nla_put().
>
>Signed-off-by: Michal Kubecek <[email protected]>

Acked-by: Jiri Pirko <[email protected]>

2019-03-26 10:23:11

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 03/22] netlink: add strict version of nla_parse_nested()

Mon, Mar 25, 2019 at 06:08:03PM CET, [email protected] wrote:
>Similar to nla_parse_strict() and nlmsg_parse_strict(), add also
>nla_parse_nested_strict() as a version of nla_parse_nested() with strict
>policy checking.
>
>Signed-off-by: Michal Kubecek <[email protected]>

Acked-by: Jiri Pirko <[email protected]>


2019-03-26 10:27:02

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 04/22] ethtool: move to its own directory

Mon, Mar 25, 2019 at 06:08:06PM CET, [email protected] wrote:
>The ethtool netlink interface is going to be split into multiple files so
>that it will be more convenient to put all of them in a separate directory
>net/ethtool. Start by moving current ethtool.c with ioctl interface into
>this directory and renaming it to ioctl.c.
>
>Signed-off-by: Michal Kubecek <[email protected]>

Acked-by: Jiri Pirko <[email protected]>

2019-03-26 10:32:20

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 01/22] rtnetlink: provide permanent hardware address in RTM_NEWLINK

On Tue, Mar 26, 2019 at 11:08:36AM +0100, Jiri Pirko wrote:
>
> I don't think we should put permaddr if driver did not set it. 2
> solutions:
> 1) provide a helper that driver will use to set the perm_addr. This
> helper sets a "valid bit". Then you only put IFLA_PERM_ADDRESS
> in case the "valid bit" is set.
> 2) Assuming that no driver would set permaddr to all zeroes,
> don't put IFLA_PERM_ADDRESS in case permadd is all zeroes.

I already replied to similar suggestion in v4 discussion:

http://patchwork.ozlabs.org/patch/1060164/#2117512

But I don't have really strong opinion about this. The problem with not
being able to distinguish between "no/unknown permanent address" and
"old kernel not providing the information" is going to become less
important over time.

Michal

2019-03-26 11:51:24

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 01/22] rtnetlink: provide permanent hardware address in RTM_NEWLINK

Tue, Mar 26, 2019 at 11:31:15AM CET, [email protected] wrote:
>On Tue, Mar 26, 2019 at 11:08:36AM +0100, Jiri Pirko wrote:
>>
>> I don't think we should put permaddr if driver did not set it. 2
>> solutions:
>> 1) provide a helper that driver will use to set the perm_addr. This
>> helper sets a "valid bit". Then you only put IFLA_PERM_ADDRESS
>> in case the "valid bit" is set.
>> 2) Assuming that no driver would set permaddr to all zeroes,
>> don't put IFLA_PERM_ADDRESS in case permadd is all zeroes.
>
>I already replied to similar suggestion in v4 discussion:
>
> http://patchwork.ozlabs.org/patch/1060164/#2117512
>
>But I don't have really strong opinion about this. The problem with not
>being able to distinguish between "no/unknown permanent address" and
>"old kernel not providing the information" is going to become less
>important over time.

If the attribute is sent to userspace, it should mean the permaddr is
there and valid.


>
>Michal

2019-03-26 12:21:14

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Mon, Mar 25, 2019 at 06:08:09PM CET, [email protected] wrote:
>Basic genetlink and init infrastructure for the netlink interface, register
>genetlink family "ethtool". Introduce CONFIG_ETHTOOL_NETLINK Kconfig
>option. Add interface description into Documentation/networking.
>
>Signed-off-by: Michal Kubecek <[email protected]>
>---
> Documentation/networking/ethtool-netlink.txt | 168 +++++++++++++++++++
> include/linux/ethtool_netlink.h | 9 +
> include/uapi/linux/ethtool_netlink.h | 25 +++
> net/Kconfig | 8 +
> net/ethtool/Makefile | 6 +-
> net/ethtool/netlink.c | 34 ++++
> net/ethtool/netlink.h | 12 ++
> 7 files changed, 261 insertions(+), 1 deletion(-)
> create mode 100644 Documentation/networking/ethtool-netlink.txt
> create mode 100644 include/linux/ethtool_netlink.h
> create mode 100644 include/uapi/linux/ethtool_netlink.h
> create mode 100644 net/ethtool/netlink.c
> create mode 100644 net/ethtool/netlink.h
>
>diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
>new file mode 100644
>index 000000000000..377d64c9b7fa
>--- /dev/null
>+++ b/Documentation/networking/ethtool-netlink.txt
>@@ -0,0 +1,168 @@
>+ Netlink interface for ethtool
>+ =============================
>+
>+
>+Basic information
>+-----------------
>+
>+Netlink interface for ethtool uses generic netlink family "ethtool" (userspace
>+application should use macros ETHTOOL_GENL_NAME and ETHTOOL_GENL_VERSION
>+defined in <linux/ethtool_netlink.h> uapi header). This family does not use
>+a specific header, all information in requests and replies is passed using
>+netlink attributes.
>+
>+In requests, device can be identified by ifindex or by name; if both are used,
>+they must match. In replies, kernel fills both. The meaning of flags,
>+info_mask and index fields depends on request type.
>+
>+The ethtool netlink interface uses extended ACK for error and warning
>+reporting, userspace application developers are encouraged to make these
>+messages available to user in a suitable way.
>+
>+Requests can be divided into three categories: "get" (retrieving information),
>+"set" (setting parameters) and "action" (invoking an action).
>+
>+All "set" and "action" type requests require admin privileges (CAP_NET_ADMIN
>+in the namespace). Most "get" type request are allowed for anyone but there

s/request/requests/


>+are exceptions (where the response contains sensitive information). In some
>+cases, the request as such is allowed for anyone but unprivileged users have
>+attributes with sensitive information (e.g. wake-on-lan password) omitted.
>+
>+
>+Conventions
>+-----------
>+
>+Attributes which represent a boolean value usually use u8 type so that we can
>+distinguish three states: "on", "off" and "not present" (meaning the
>+information is not available in "get" requests or value is not to be changed
>+in "set" requests). For these attributes, the "true" value should be passed as
>+number 1 but any non-zero value should be understood as "true" by recipient.
>+
>+Some request types allow passing an attribute named ETHA_*_INFOMASK with
>+a bitmask telling kernel that we are only interested in some parts of the
>+information. If info mask is omitted, all available information is returned.
>+Meaning of info mask bits depends on request type and is listed below.
>+
>+
>+Device identification
>+---------------------
>+
>+When appropriate, network device is identified by a nested attribute named
>+ETHA_*_DEV. This attribute can contain

Isn't it ETHA_DEV_*? I must admit I'm a bit confused.


>+
>+ ETHA_DEV_INDEX (u32) device ifindex
>+ ETHA_DEV_NAME (string) device name
>+
>+In device related requests, one of these is sufficient; if both are used, they
>+must match (i.e. identify the same device). In device related replies both are

You say this now for the second time. First time this was said in second
para.


>+provided by kernel. In dump requests, device is not specified and kernel
>+replies with one message per network device (only those for which the request
>+is supported).
>+
>+
>+List of message types
>+---------------------
>+
>+All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"

Why "usually"? Why not "always"?


>+to indicate the type.
>+
>+Messages of type "get" are used by userspace to request information and
>+usually do not contain any attributes (that may be added later for dump
>+filtering). Kernel response is in the form of corresponding "set" message;

Okay. Do we want reply to "*_cmd_something_get" command to be
"*_cmd_something_set". That sounds odd. Why reply has to be "cmd"? Why
not something like "reply" or "response"?
This should work for both "doit/dumpit" and notifications.


>+the same message can be also used to set (some of) the parameters, except for
>+messages marked as "response only" in the table above. "Get" messages with
>+NLM_F_DUMP flags and no device identification dump the information for all
>+devices supporting the request.
>+
>+Later sections describe the format and semantics of these request messages.
>+
>+
>+Request translation
>+-------------------
>+
>+The following table maps iosctl commands to netlink commands providing their
>+functionality. Entries with "n/a" in right column are commands which do not
>+have their netlink replacement yet.
>+
>+ioctl command netlink command
>+---------------------------------------------------------------------
>+ETHTOOL_GSET n/a
>+ETHTOOL_SSET n/a
>+ETHTOOL_GDRVINFO n/a
>+ETHTOOL_GREGS n/a
>+ETHTOOL_GWOL n/a
>+ETHTOOL_SWOL n/a
>+ETHTOOL_GMSGLVL n/a
>+ETHTOOL_SMSGLVL n/a
>+ETHTOOL_NWAY_RST n/a
>+ETHTOOL_GLINK n/a
>+ETHTOOL_GEEPROM n/a
>+ETHTOOL_SEEPROM n/a
>+ETHTOOL_GCOALESCE n/a
>+ETHTOOL_SCOALESCE n/a
>+ETHTOOL_GRINGPARAM n/a
>+ETHTOOL_SRINGPARAM n/a
>+ETHTOOL_GPAUSEPARAM n/a
>+ETHTOOL_SPAUSEPARAM n/a
>+ETHTOOL_GRXCSUM n/a
>+ETHTOOL_SRXCSUM n/a
>+ETHTOOL_GTXCSUM n/a
>+ETHTOOL_STXCSUM n/a
>+ETHTOOL_GSG n/a
>+ETHTOOL_SSG n/a
>+ETHTOOL_TEST n/a
>+ETHTOOL_GSTRINGS n/a
>+ETHTOOL_PHYS_ID n/a
>+ETHTOOL_GSTATS n/a
>+ETHTOOL_GTSO n/a
>+ETHTOOL_STSO n/a
>+ETHTOOL_GPERMADDR rtnetlink RTM_GETLINK
>+ETHTOOL_GUFO n/a
>+ETHTOOL_SUFO n/a
>+ETHTOOL_GGSO n/a
>+ETHTOOL_SGSO n/a
>+ETHTOOL_GFLAGS n/a
>+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_SGRO n/a
>+ETHTOOL_GRXRINGS n/a
>+ETHTOOL_GRXCLSRLCNT n/a
>+ETHTOOL_GRXCLSRULE n/a
>+ETHTOOL_GRXCLSRLALL n/a
>+ETHTOOL_SRXCLSRLDEL n/a
>+ETHTOOL_SRXCLSRLINS n/a
>+ETHTOOL_FLASHDEV n/a
>+ETHTOOL_RESET n/a
>+ETHTOOL_SRXNTUPLE n/a
>+ETHTOOL_GRXNTUPLE n/a
>+ETHTOOL_GSSET_INFO n/a
>+ETHTOOL_GRXFHINDIR n/a
>+ETHTOOL_SRXFHINDIR n/a
>+ETHTOOL_GFEATURES n/a
>+ETHTOOL_SFEATURES n/a
>+ETHTOOL_GCHANNELS n/a
>+ETHTOOL_SCHANNELS n/a
>+ETHTOOL_SET_DUMP n/a
>+ETHTOOL_GET_DUMP_FLAG n/a
>+ETHTOOL_GET_DUMP_DATA n/a
>+ETHTOOL_GET_TS_INFO n/a
>+ETHTOOL_GMODULEINFO n/a
>+ETHTOOL_GMODULEEEPROM n/a
>+ETHTOOL_GEEE n/a
>+ETHTOOL_SEEE n/a
>+ETHTOOL_GRSSH n/a
>+ETHTOOL_SRSSH n/a
>+ETHTOOL_GTUNABLE n/a
>+ETHTOOL_STUNABLE n/a
>+ETHTOOL_GPHYSTATS n/a
>+ETHTOOL_PERQUEUE n/a
>+ETHTOOL_GLINKSETTINGS n/a
>+ETHTOOL_SLINKSETTINGS n/a
>+ETHTOOL_PHY_GTUNABLE n/a
>+ETHTOOL_PHY_STUNABLE n/a
>+ETHTOOL_GFECPARAM n/a
>+ETHTOOL_SFECPARAM n/a
>diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h
>new file mode 100644
>index 000000000000..0412adb4f42f
>--- /dev/null
>+++ b/include/linux/ethtool_netlink.h
>@@ -0,0 +1,9 @@
>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>+
>+#ifndef _LINUX_ETHTOOL_NETLINK_H_
>+#define _LINUX_ETHTOOL_NETLINK_H_
>+
>+#include <uapi/linux/ethtool_netlink.h>
>+#include <linux/ethtool.h>
>+
>+#endif /* _LINUX_ETHTOOL_NETLINK_H_ */
>diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
>new file mode 100644
>index 000000000000..6aa267451542
>--- /dev/null
>+++ b/include/uapi/linux/ethtool_netlink.h
>@@ -0,0 +1,25 @@
>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>+/*
>+ * include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool
>+ *
>+ * See Documentation/networking/ethtool-netlink.txt in kernel source tree for
>+ * doucumentation of the interface.
>+ */
>+
>+#ifndef _UAPI_LINUX_ETHTOOL_NETLINK_H_
>+#define _UAPI_LINUX_ETHTOOL_NETLINK_H_
>+
>+#include <linux/ethtool.h>
>+
>+enum {
>+ ETHNL_CMD_NOOP,
>+

Usually headers have something like:
/* add new commands above here */
here.


>+ __ETHNL_CMD_CNT,
>+ ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
>+};
>+
>+/* generic netlink info */
>+#define ETHTOOL_GENL_NAME "ethtool"
>+#define ETHTOOL_GENL_VERSION 1
>+
>+#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */
>diff --git a/net/Kconfig b/net/Kconfig
>index 3e8fdd688329..75c600b45775 100644
>--- a/net/Kconfig
>+++ b/net/Kconfig
>@@ -448,6 +448,14 @@ config FAILOVER
> migration of VMs with direct attached VFs by failing over to the
> paravirtual datapath when the VF is unplugged.
>
>+config ETHTOOL_NETLINK
>+ bool "Netlink interface for ethtool"
>+ default y
>+ help
>+ An alternative userspace interface for ethtool based on generic
>+ netlink. It provides better extensibility and some new features,
>+ e.g. notification messages.
>+
> endif # if NET
>
> # Used by archs to tell that they support BPF JIT compiler plus which flavour.
>diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
>index 3ebfab2bca66..f30e0da88be5 100644
>--- a/net/ethtool/Makefile
>+++ b/net/ethtool/Makefile
>@@ -1,3 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0
>
>-obj-y += ioctl.o
>+obj-y += ioctl.o
>+
>+obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o

Why? I believe this should be always build-in same as ioctl.


>+
>+ethtool_nl-y := netlink.o
>diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
>new file mode 100644
>index 000000000000..85dd6dac71a2
>--- /dev/null
>+++ b/net/ethtool/netlink.c
>@@ -0,0 +1,34 @@
>+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
>+
>+#include <linux/ethtool_netlink.h>
>+#include "netlink.h"
>+
>+/* genetlink setup */
>+
>+static const struct genl_ops ethtool_genl_ops[] = {
>+};
>+
>+struct genl_family ethtool_genl_family = {
>+ .hdrsize = 0,

No need to set 0.


>+ .name = ETHTOOL_GENL_NAME,
>+ .version = ETHTOOL_GENL_VERSION,
>+ .netnsok = true,
>+ .parallel_ops = true,
>+ .ops = ethtool_genl_ops,
>+ .n_ops = ARRAY_SIZE(ethtool_genl_ops),
>+};
>+
>+/* module setup */
>+
>+static int __init ethnl_init(void)
>+{
>+ int ret;
>+
>+ ret = genl_register_family(&ethtool_genl_family);
>+ if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
>+ return ret;
>+
>+ return 0;
>+}
>+
>+subsys_initcall(ethnl_init);
>diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
>new file mode 100644
>index 000000000000..63063b582ca2
>--- /dev/null
>+++ b/net/ethtool/netlink.h
>@@ -0,0 +1,12 @@
>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>+
>+#ifndef _NET_ETHTOOL_NETLINK_H
>+#define _NET_ETHTOOL_NETLINK_H
>+
>+#include <linux/ethtool_netlink.h>
>+#include <linux/netdevice.h>
>+#include <net/genetlink.h>
>+
>+extern struct genl_family ethtool_genl_family;

Why? You need this just within "netlink.c", don't you?


>+
>+#endif /* _NET_ETHTOOL_NETLINK_H */
>--
>2.21.0
>

2019-03-26 12:50:05

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 06/22] ethtool: helper functions for netlink interface

Mon, Mar 25, 2019 at 06:08:12PM CET, [email protected] wrote:
>Various helpers used by ethtool netlink code.
>
>Signed-off-by: Michal Kubecek <[email protected]>
>---
> include/uapi/linux/ethtool_netlink.h | 11 ++
> net/ethtool/netlink.c | 144 +++++++++++++++++++++++++++
> net/ethtool/netlink.h | 144 +++++++++++++++++++++++++++
> 3 files changed, 299 insertions(+)
>
>diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
>index 6aa267451542..59240a2cda56 100644
>--- a/include/uapi/linux/ethtool_netlink.h
>+++ b/include/uapi/linux/ethtool_netlink.h
>@@ -18,6 +18,17 @@ enum {
> ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
> };
>
>+/* device specification */
>+
>+enum {
>+ ETHA_DEV_UNSPEC,

ETHNL/ETHA looks odd.
I would prefer prefix "ETHTOOL_* for everything in uapi:
ETHTOOL_CMD_* for commands.
ETHTOOL_ATTR_* for attributes.

Looks much nicer.


>+ ETHA_DEV_INDEX, /* u32 */
>+ ETHA_DEV_NAME, /* string */
>+
>+ __ETHA_DEV_CNT,
>+ ETHA_DEV_MAX = (__ETHA_DEV_CNT - 1)
>+};
>+
> /* generic netlink info */
> #define ETHTOOL_GENL_NAME "ethtool"
> #define ETHTOOL_GENL_VERSION 1
>diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
>index 85dd6dac71a2..cc6829ba4331 100644
>--- a/net/ethtool/netlink.c
>+++ b/net/ethtool/netlink.c
>@@ -1,8 +1,152 @@
> // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
>
>+#include <net/sock.h>
> #include <linux/ethtool_netlink.h>
> #include "netlink.h"
>
>+static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {

ethnl_dev_policy. Please maintain the namespace if possible.
"dev_policy" looks way too generic if anyone sees it in the code.


>+ [ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
>+ [ETHA_DEV_INDEX] = { .type = NLA_U32 },
>+ [ETHA_DEV_NAME] = { .type = NLA_NUL_STRING,
>+ .len = IFNAMSIZ - 1 },
>+};
>+
>+/**
>+ * ethnl_dev_get() - get device identified by nested attribute
>+ * @info: genetlink info (also used for extack error reporting)
>+ * @nest: nest attribute with device identification
>+ *
>+ * Finds the network device identified by ETHA_DEV_INDEX (ifindex) or
>+ * ETHA_DEV_NAME (name) attributes in a nested attribute @nest. If both
>+ * are supplied, they must identify the same device. If successful, takes
>+ * a reference to the device which is to be released by caller.
>+ *
>+ * Return: pointer to the device if successful, ERR_PTR(err) on error
>+ */
>+struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest)

I wonder, why you have this in nested attr? Aren't ifindex/ifname always
used as a handle for commands/notifications? Would be just in toplevel
(see devlink/nl80211)


>+{
>+ struct net *net = genl_info_net(info);
>+ struct nlattr *tb[ETHA_DEV_MAX + 1];
>+ struct net_device *dev;
>+ int ret;
>+
>+ if (!nest) {
>+ ETHNL_SET_ERRMSG(info,
>+ "mandatory device identification missing");

No need to wrap.


>+ return ERR_PTR(-EINVAL);
>+ }
>+ ret = nla_parse_nested_strict(tb, ETHA_DEV_MAX, nest, dev_policy,
>+ info->extack);
>+ if (ret < 0)

"if (ret)" is enough. "Returns 0 on success or a negative error code."


>+ return ERR_PTR(ret);
>+
>+ if (tb[ETHA_DEV_INDEX]) {
>+ dev = dev_get_by_index(net, nla_get_u32(tb[ETHA_DEV_INDEX]));
>+ if (!dev)
>+ return ERR_PTR(-ENODEV);
>+ /* if both ifindex and ifname are passed, they must match */
>+ if (tb[ETHA_DEV_NAME]) {
>+ const char *nl_name = nla_data(tb[ETHA_DEV_NAME]);
>+
>+ if (strncmp(dev->name, nl_name, IFNAMSIZ)) {
>+ dev_put(dev);
>+ ETHNL_SET_ERRMSG(info,
>+ "ifindex and ifname do not match");

No need to wrap.


>+ return ERR_PTR(-ENODEV);
>+ }
>+ }
>+ return dev;
>+ } else if (tb[ETHA_DEV_NAME]) {
>+ dev = dev_get_by_name(net, nla_data(tb[ETHA_DEV_NAME]));
>+ if (!dev)
>+ return ERR_PTR(-ENODEV);
>+ } else {
>+ ETHNL_SET_ERRMSG(info, "either ifindex or ifname required");
>+ return ERR_PTR(-EINVAL);
>+ }
>+
>+ if (!netif_device_present(dev)) {
>+ dev_put(dev);
>+ ETHNL_SET_ERRMSG(info, "device not present");
>+ return ERR_PTR(-ENODEV);
>+ }
>+ return dev;
>+}
>+
>+/**
>+ * ethnl_fill_dev() - Put device identification nest into a message
>+ * @msg: skb with the message
>+ * @dev: network device to describe
>+ * @attrtype: attribute type to use for the nest
>+ *
>+ * Create a nested attribute with attributes describing given network device.
>+ * Clean up on error.
>+ *
>+ * Return: 0 on success, error value (-EMSGSIZE only) on error
>+ */
>+int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype)
>+{
>+ struct nlattr *nest;
>+ int ret = -EMSGSIZE;
>+
>+ nest = ethnl_nest_start(msg, attrtype);
>+ if (!nest)
>+ return -EMSGSIZE;
>+
>+ if (nla_put_u32(msg, ETHA_DEV_INDEX, (u32)dev->ifindex))
>+ goto err;
>+ if (nla_put_string(msg, ETHA_DEV_NAME, dev->name))
>+ goto err;
>+
>+ nla_nest_end(msg, nest);
>+ return 0;
>+err:
Usually this label is called "nla_put_failure". Same for the rest.
"err" could be confused with error return variable.

have "ret = -EMSGSIZE" here. Easier to follow the error path.

>+ nla_nest_cancel(msg, nest);
>+ return ret;
>+}
>+
>+/**
>+ * ethnl_reply_init() - Create skb for a reply and fill device identification
>+ * @payload: payload length (without netlink and genetlink header)
>+ * @dev: device the reply is about (may be null)
>+ * @cmd: ETHNL_CMD_* command for reply
>+ * @info: genetlink info of the received packet we respond to
>+ * @ehdrp: place to store payload pointer returned by genlmsg_new()
>+ *
>+ * Return: pointer to allocated skb on success, NULL on error
>+ */
>+struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
>+ u16 dev_attrtype, struct genl_info *info,
>+ void **ehdrp)
>+{
>+ struct sk_buff *rskb;

Could be just "skb", or "msg". You have "msg" in ethnl_fill_dev().
Please have it consistent.


>+ void *ehdr;
>+
>+ rskb = genlmsg_new(payload, GFP_KERNEL);
>+ if (!rskb) {
>+ ETHNL_SET_ERRMSG(info,
>+ "failed to allocate reply message");

No need to wrap.


>+ return NULL;
>+ }
>+
>+ ehdr = genlmsg_put_reply(rskb, info, &ethtool_genl_family, 0, cmd);
>+ if (!ehdr)
>+ goto err;
>+ if (ehdrp)
>+ *ehdrp = ehdr;
>+ if (dev) {
>+ int ret = ethnl_fill_dev(rskb, dev, dev_attrtype);
>+
>+ if (ret < 0)

"if (ret)" is enough.


>+ goto err;
>+ }
>+
>+ return rskb;
>+err:
>+ nlmsg_free(rskb);
>+ return NULL;
>+}
>+
> /* genetlink setup */
>
> static const struct genl_ops ethtool_genl_ops[] = {
>diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
>index 63063b582ca2..db90d95410b1 100644
>--- a/net/ethtool/netlink.h
>+++ b/net/ethtool/netlink.h
>@@ -6,7 +6,151 @@
> #include <linux/ethtool_netlink.h>
> #include <linux/netdevice.h>
> #include <net/genetlink.h>
>+#include <net/sock.h>
>+
>+#define ETHNL_SET_ERRMSG(info, msg) \
>+ do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)

Why do you need this macro? Can info be null?
In general, macros like this should be avoided.


>
> extern struct genl_family ethtool_genl_family;
>
>+struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest);
>+int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype);
>+
>+struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
>+ u16 dev_attrtype, struct genl_info *info,
>+ void **ehdrp);
>+
>+static inline int ethnl_str_size(const char *s)
>+{
>+ return nla_total_size(strlen(s) + 1);

This looks like more generic helper, not tight to ethtool.


>+}
>+
>+static inline int ethnl_str_ifne_size(const char *s)

Eh? "ifne"? What is this good for?


>+{
>+ return s[0] ? ethnl_str_size(s) : 0;
>+}
>+
>+static inline int ethnl_put_str_ifne(struct sk_buff *skb, int attrtype,
>+ const char *s)
>+{
>+ if (!s[0])
>+ return 0;
>+ return nla_put_string(skb, attrtype, s);

I don't like helpers like this. Do the check in the caller and put or
not put the string there. It's a single if.


>+}
>+
>+static inline struct nlattr *ethnl_nest_start(struct sk_buff *skb,
>+ int attrtype)
>+{
>+ return nla_nest_start(skb, attrtype | NLA_F_NESTED);

Please use nla_nest_start directly and avoid helpers like this.


>+}
>+
>+static inline int ethnlmsg_parse(const struct nlmsghdr *nlh,
>+ struct nlattr *tb[], int maxtype,
>+ const struct nla_policy *policy,
>+ struct genl_info *info)
>+{
>+ return nlmsg_parse_strict(nlh, GENL_HDRLEN, tb, maxtype, policy,
>+ info ? info->extack : NULL);

Same thing, please use nlmsg_parse_strict directly.


>+}
>+
>+/* ethnl_update_* return true if the value is changed */
>+static inline bool ethnl_update_u32(u32 *dst, struct nlattr *attr)
>+{
>+ u32 val;
>+
>+ if (!attr)
>+ return false;
>+ val = nla_get_u32(attr);
>+ if (*dst == val)
>+ return false;
>+
>+ *dst = val;
>+ return true;
>+}
>+
>+static inline bool ethnl_update_u8(u8 *dst, struct nlattr *attr)
>+{
>+ u8 val;
>+
>+ if (!attr)
>+ return false;
>+ val = nla_get_u8(attr);
>+ if (*dst == val)
>+ return false;
>+
>+ *dst = val;
>+ return true;
>+}
>+
>+/* update u32 value used as bool from NLA_U8 */
>+static inline bool ethnl_update_bool32(u32 *dst, struct nlattr *attr)
>+{
>+ u8 val;
>+
>+ if (!attr)
>+ return false;
>+ val = !!nla_get_u8(attr);
>+ if (!!*dst == val)
>+ return false;
>+
>+ *dst = val;
>+ return true;
>+}
>+
>+static inline bool ethnl_update_binary(u8 *dst, unsigned int len,
>+ struct nlattr *attr)
>+{
>+ if (!attr)
>+ return false;
>+ if (nla_len(attr) < len)
>+ len = nla_len(attr);
>+ if (!memcmp(dst, nla_data(attr), len))
>+ return false;
>+
>+ memcpy(dst, nla_data(attr), len);
>+ return true;
>+}
>+
>+static inline bool ethnl_update_bitfield32(u32 *dst, struct nlattr *attr)
>+{
>+ struct nla_bitfield32 change;
>+ u32 newval;
>+
>+ if (!attr)
>+ return false;
>+ change = nla_get_bitfield32(attr);
>+ newval = (*dst & ~change.selector) | (change.value & change.selector);
>+ if (*dst == newval)
>+ return false;
>+
>+ *dst = newval;
>+ return true;
>+}

I don't understand puspose of these "update" helper functions. Try to
avoid them. In general, please try to avoid wrappers around netlink api.



>+
>+static inline void warn_partial_info(struct genl_info *info)
>+{
>+ ETHNL_SET_ERRMSG(info, "not all requested data could be retrieved");
>+}
>+
>+/* Check user privileges explicitly to allow finer access control based on
>+ * context of the request or hiding part of the information from unprivileged
>+ * users
>+ */
>+static inline bool ethnl_is_privileged(struct sk_buff *skb)
>+{
>+ struct net *net = sock_net(skb->sk);
>+
>+ return netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN);

Same here.


>+}
>+
>+/* total size of ETHA_*_DEV nested attribute; this is an upper estimate so that
>+ * we do not need to hold RTNL longer than necessary to prevent rename between
>+ * estimating the size and composing the message
>+ */
>+static inline unsigned int dev_ident_size(void)

You are missing "ethnl_" prefix.


>+{
>+ return nla_total_size(nla_total_size(sizeof(u32)) +
>+ nla_total_size(IFNAMSIZ));
>+}
>+
> #endif /* _NET_ETHTOOL_NETLINK_H */
>--
>2.21.0
>

2019-03-26 13:25:24

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

On Tue, Mar 26, 2019 at 01:09:09PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:09PM CET, [email protected] wrote:
> >+All "set" and "action" type requests require admin privileges (CAP_NET_ADMIN
> >+in the namespace). Most "get" type request are allowed for anyone but there
>
> s/request/requests/

Will fix.

> >+Device identification
> >+---------------------
> >+
> >+When appropriate, network device is identified by a nested attribute named
> >+ETHA_*_DEV. This attribute can contain
>
> Isn't it ETHA_DEV_*? I must admit I'm a bit confused.

ETHA_*_DEV is the nesting attribute (e.g. ETHA_SETTINGS_DEV), ETHA_DEV_*
(ETHA_DEV_INDEX and ETHA_DEV_NAME) are in the nest.

>
>
> >+
> >+ ETHA_DEV_INDEX (u32) device ifindex
> >+ ETHA_DEV_NAME (string) device name
> >+
> >+In device related requests, one of these is sufficient; if both are used, they
> >+must match (i.e. identify the same device). In device related replies both are
>
> You say this now for the second time. First time this was said in second
> para.

I'll drop one of them.

> >+List of message types
> >+---------------------
> >+
> >+All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
>
> Why "usually"? Why not "always"?

Right, it's always. And if it changes one day, the sentence will have to
be rewritten anyway.

> >+Messages of type "get" are used by userspace to request information and
> >+usually do not contain any attributes (that may be added later for dump
> >+filtering). Kernel response is in the form of corresponding "set" message;
>
> Okay. Do we want reply to "*_cmd_something_get" command to be
> "*_cmd_something_set". That sounds odd. Why reply has to be "cmd"? Why
> not something like "reply" or "response"?
> This should work for both "doit/dumpit" and notifications.

As stated right below, the aim is to use the same format for replies to
GET requests as userspace uses for related SET requests. We could use
different id (genlmsghdr::cmd) but that seemed like a waste for no actual
gain.

> >+the same message can be also used to set (some of) the parameters, except for
> >+messages marked as "response only" in the table above. "Get" messages with
> >+NLM_F_DUMP flags and no device identification dump the information for all
> >+devices supporting the request.

> >+
> >+enum {
> >+ ETHNL_CMD_NOOP,
> >+
>
> Usually headers have something like:
> /* add new commands above here */
> here.

OK

> >diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
> >index 3ebfab2bca66..f30e0da88be5 100644
> >--- a/net/ethtool/Makefile
> >+++ b/net/ethtool/Makefile
> >@@ -1,3 +1,7 @@
> > # SPDX-License-Identifier: GPL-2.0
> >
> >-obj-y += ioctl.o
> >+obj-y += ioctl.o
> >+
> >+obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o
>
> Why? I believe this should be always build-in same as ioctl.

I would like to make the ioctl interface optional as well, eventually.
As someone noted in one of the earlier discussions, there may be some
special minimalistic setups where ethtool interface may be of no use.

> >+struct genl_family ethtool_genl_family = {
> >+ .hdrsize = 0,
>
> No need to set 0.

OK

> >+
> >+extern struct genl_family ethtool_genl_family;
>
> Why? You need this just within "netlink.c", don't you?

In the submitted part, yes. But one of the later patches adds specific
notify handler (different from ethnl_std_notify()) which is not in
netlink.c and needs to use pointer to ethtool_genl_family for a call to
genlmsg_put() and genlmsg_multicast().

But I can make it static for now and change to extern when it's needed.

Michal


2019-03-26 13:56:15

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Tue, Mar 26, 2019 at 02:24:27PM CET, [email protected] wrote:
>On Tue, Mar 26, 2019 at 01:09:09PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:09PM CET, [email protected] wrote:
>> >+All "set" and "action" type requests require admin privileges (CAP_NET_ADMIN
>> >+in the namespace). Most "get" type request are allowed for anyone but there
>>
>> s/request/requests/
>
>Will fix.
>
>> >+Device identification
>> >+---------------------
>> >+
>> >+When appropriate, network device is identified by a nested attribute named
>> >+ETHA_*_DEV. This attribute can contain
>>
>> Isn't it ETHA_DEV_*? I must admit I'm a bit confused.
>
>ETHA_*_DEV is the nesting attribute (e.g. ETHA_SETTINGS_DEV), ETHA_DEV_*
>(ETHA_DEV_INDEX and ETHA_DEV_NAME) are in the nest.

Yeah. I wonder why you need to duplicate this. Can this be in top-lever
attr enum that is shared among all commands? It is there anyway and
looks a bit silly to have "DEV" attr separate for every command.
Something like this:

ATTR_IFINDEX
ATTR_IFNAME
ATTR_SOMEOTHER (flags perhaps)
ATTR_CMD_SPECIFIC_NEST_START
ATTR_CMDX_SOMETHING
ATTR_CMDX_SOMETHING2
ATTR_CMDX_SOMETHING3
ATTR_CMD_SPECIFIC_NEST_END

>
>>
>>
>> >+
>> >+ ETHA_DEV_INDEX (u32) device ifindex
>> >+ ETHA_DEV_NAME (string) device name
>> >+
>> >+In device related requests, one of these is sufficient; if both are used, they
>> >+must match (i.e. identify the same device). In device related replies both are
>>
>> You say this now for the second time. First time this was said in second
>> para.
>
>I'll drop one of them.
>
>> >+List of message types
>> >+---------------------
>> >+
>> >+All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
>>
>> Why "usually"? Why not "always"?
>
>Right, it's always. And if it changes one day, the sentence will have to
>be rewritten anyway.

Okay.


>
>> >+Messages of type "get" are used by userspace to request information and
>> >+usually do not contain any attributes (that may be added later for dump
>> >+filtering). Kernel response is in the form of corresponding "set" message;
>>
>> Okay. Do we want reply to "*_cmd_something_get" command to be
>> "*_cmd_something_set". That sounds odd. Why reply has to be "cmd"? Why
>> not something like "reply" or "response"?
>> This should work for both "doit/dumpit" and notifications.
>
>As stated right below, the aim is to use the same format for replies to
>GET requests as userspace uses for related SET requests. We could use
>different id (genlmsghdr::cmd) but that seemed like a waste for no actual
>gain.

I understand. I just wonder if the replies/notifications could use the
same name, not having "set" in it. I know we have it like this in many
netlink ifaces, it is however confusing to users. So once we are doing
this from scratch, we can do it differently.


>
>> >+the same message can be also used to set (some of) the parameters, except for
>> >+messages marked as "response only" in the table above. "Get" messages with
>> >+NLM_F_DUMP flags and no device identification dump the information for all
>> >+devices supporting the request.
>
>> >+
>> >+enum {
>> >+ ETHNL_CMD_NOOP,
>> >+
>>
>> Usually headers have something like:
>> /* add new commands above here */
>> here.
>
>OK
>
>> >diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
>> >index 3ebfab2bca66..f30e0da88be5 100644
>> >--- a/net/ethtool/Makefile
>> >+++ b/net/ethtool/Makefile
>> >@@ -1,3 +1,7 @@
>> > # SPDX-License-Identifier: GPL-2.0
>> >
>> >-obj-y += ioctl.o
>> >+obj-y += ioctl.o
>> >+
>> >+obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o
>>
>> Why? I believe this should be always build-in same as ioctl.
>
>I would like to make the ioctl interface optional as well, eventually.
>As someone noted in one of the earlier discussions, there may be some
>special minimalistic setups where ethtool interface may be of no use.

Okay, fair enough.


>
>> >+struct genl_family ethtool_genl_family = {
>> >+ .hdrsize = 0,
>>
>> No need to set 0.
>
>OK
>
>> >+
>> >+extern struct genl_family ethtool_genl_family;
>>
>> Why? You need this just within "netlink.c", don't you?
>
>In the submitted part, yes. But one of the later patches adds specific
>notify handler (different from ethnl_std_notify()) which is not in
>netlink.c and needs to use pointer to ethtool_genl_family for a call to
>genlmsg_put() and genlmsg_multicast().
>
>But I can make it static for now and change to extern when it's needed.

Please do.


>
>Michal
>

2019-03-26 14:21:17

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 06/22] ethtool: helper functions for netlink interface

On Tue, Mar 26, 2019 at 01:38:15PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:12PM CET, [email protected] wrote:
> >+/* device specification */
> >+
> >+enum {
> >+ ETHA_DEV_UNSPEC,
>
> ETHNL/ETHA looks odd.
> I would prefer prefix "ETHTOOL_* for everything in uapi:
> ETHTOOL_CMD_* for commands.
> ETHTOOL_ATTR_* for attributes.
>
> Looks much nicer.

Even with ETHA_ prefix, there are places where having to fit into
80 columns makes the code hard to read. ETHTOOL_ATTR_ would make it
even harder.

> >+static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {
>
> ethnl_dev_policy. Please maintain the namespace if possible.
> "dev_policy" looks way too generic if anyone sees it in the code.

OK
> >+ [ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
> >+ [ETHA_DEV_INDEX] = { .type = NLA_U32 },
> >+ [ETHA_DEV_NAME] = { .type = NLA_NUL_STRING,
> >+ .len = IFNAMSIZ - 1 },
> >+};
> >+
> >+/**
> >+ * ethnl_dev_get() - get device identified by nested attribute
> >+ * @info: genetlink info (also used for extack error reporting)
> >+ * @nest: nest attribute with device identification
> >+ *
> >+ * Finds the network device identified by ETHA_DEV_INDEX (ifindex) or
> >+ * ETHA_DEV_NAME (name) attributes in a nested attribute @nest. If both
> >+ * are supplied, they must identify the same device. If successful, takes
> >+ * a reference to the device which is to be released by caller.
> >+ *
> >+ * Return: pointer to the device if successful, ERR_PTR(err) on error
> >+ */
> >+struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest)
>
> I wonder, why you have this in nested attr? Aren't ifindex/ifname always
> used as a handle for commands/notifications? Would be just in toplevel
> (see devlink/nl80211)

It makes things only slightly easier now but I would like to make the
API as future proof as possible. If something needs to be added later
(e.g. netns id) or something else changes (e.g. 64-bit ifindex), only
one enum and these two helpers would need to be adjusted.

> >+ if (!nest) {
> >+ ETHNL_SET_ERRMSG(info,
> >+ "mandatory device identification missing");
>
> No need to wrap.

OK

> >+ ret = nla_parse_nested_strict(tb, ETHA_DEV_MAX, nest, dev_policy,
> >+ info->extack);
> >+ if (ret < 0)
>
> "if (ret)" is enough. "Returns 0 on success or a negative error code."

OK

> >+ if (strncmp(dev->name, nl_name, IFNAMSIZ)) {
> >+ dev_put(dev);
> >+ ETHNL_SET_ERRMSG(info,
> >+ "ifindex and ifname do not match");
>
> No need to wrap.

OK

> >+/**
> >+ * ethnl_fill_dev() - Put device identification nest into a message
> >+ * @msg: skb with the message
> >+ * @dev: network device to describe
> >+ * @attrtype: attribute type to use for the nest
> >+ *
> >+ * Create a nested attribute with attributes describing given network device.
> >+ * Clean up on error.
> >+ *
> >+ * Return: 0 on success, error value (-EMSGSIZE only) on error
> >+ */
> >+int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype)
> >+{
> >+ struct nlattr *nest;
> >+ int ret = -EMSGSIZE;
> >+
> >+ nest = ethnl_nest_start(msg, attrtype);
> >+ if (!nest)
> >+ return -EMSGSIZE;
> >+
> >+ if (nla_put_u32(msg, ETHA_DEV_INDEX, (u32)dev->ifindex))
> >+ goto err;
> >+ if (nla_put_string(msg, ETHA_DEV_NAME, dev->name))
> >+ goto err;
> >+
> >+ nla_nest_end(msg, nest);
> >+ return 0;
> >+err:
> Usually this label is called "nla_put_failure". Same for the rest.
> "err" could be confused with error return variable.
>
> have "ret = -EMSGSIZE" here. Easier to follow the error path.

OK

> >+ nla_nest_cancel(msg, nest);
> >+ return ret;
> >+}
> >+
> >+/**
> >+ * ethnl_reply_init() - Create skb for a reply and fill device identification
> >+ * @payload: payload length (without netlink and genetlink header)
> >+ * @dev: device the reply is about (may be null)
> >+ * @cmd: ETHNL_CMD_* command for reply
> >+ * @info: genetlink info of the received packet we respond to
> >+ * @ehdrp: place to store payload pointer returned by genlmsg_new()
> >+ *
> >+ * Return: pointer to allocated skb on success, NULL on error
> >+ */
> >+struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
> >+ u16 dev_attrtype, struct genl_info *info,
> >+ void **ehdrp)
> >+{
> >+ struct sk_buff *rskb;
>
> Could be just "skb", or "msg". You have "msg" in ethnl_fill_dev().
> Please have it consistent.

OK

> >+ rskb = genlmsg_new(payload, GFP_KERNEL);
> >+ if (!rskb) {
> >+ ETHNL_SET_ERRMSG(info,
> >+ "failed to allocate reply message");
>
> No need to wrap.

OK

> >+ int ret = ethnl_fill_dev(rskb, dev, dev_attrtype);
> >+
> >+ if (ret < 0)
>
> "if (ret)" is enough.

OK

> >+
> >+#define ETHNL_SET_ERRMSG(info, msg) \
> >+ do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)
>
> Why do you need this macro? Can info be null?

The same functions are used for generating replies to GET requests (info
is not null) and notifications (info is null, no extack).

> In general, macros like this should be avoided.

At the expense of repeating the same patterns. In this case it would be
acceptable but it would mean one more indentation level for lines with
the error/warning messages.

> >+static inline int ethnl_str_size(const char *s)
> >+{
> >+ return nla_total_size(strlen(s) + 1);
>
> This looks like more generic helper, not tight to ethtool.

OK

> >+static inline int ethnl_str_ifne_size(const char *s)
>
> Eh? "ifne"? What is this good for?

It's for "if not empty". Structures like ethtool_drvinfo use fixed size
char arrays for strings where empty string means the information is not
available. So if the string is empty, netlink attribute is omitted.

> >+{
> >+ return s[0] ? ethnl_str_size(s) : 0;
> >+}
> >+
> >+static inline int ethnl_put_str_ifne(struct sk_buff *skb, int attrtype,
> >+ const char *s)
> >+{
> >+ if (!s[0])
> >+ return 0;
> >+ return nla_put_string(skb, attrtype, s);
>
> I don't like helpers like this. Do the check in the caller and put or
> not put the string there. It's a single if.

OK


> >+static inline struct nlattr *ethnl_nest_start(struct sk_buff *skb,
> >+ int attrtype)
> >+{
> >+ return nla_nest_start(skb, attrtype | NLA_F_NESTED);
>
> Please use nla_nest_start directly and avoid helpers like this.

OK. It's one more thing to check (and forget) in many places, though.

> >+static inline int ethnlmsg_parse(const struct nlmsghdr *nlh,
> >+ struct nlattr *tb[], int maxtype,
> >+ const struct nla_policy *policy,
> >+ struct genl_info *info)
> >+{
> >+ return nlmsg_parse_strict(nlh, GENL_HDRLEN, tb, maxtype, policy,
> >+ info ? info->extack : NULL);
>
> Same thing, please use nlmsg_parse_strict directly.

OK

> >+/* ethnl_update_* return true if the value is changed */
> >+static inline bool ethnl_update_u32(u32 *dst, struct nlattr *attr)
> >+{
> >+ u32 val;
> >+
> >+ if (!attr)
> >+ return false;
> >+ val = nla_get_u32(attr);
> >+ if (*dst == val)
> >+ return false;
> >+
> >+ *dst = val;
> >+ return true;
> >+}
> >+
> >+static inline bool ethnl_update_u8(u8 *dst, struct nlattr *attr)
> >+{
> >+ u8 val;
> >+
> >+ if (!attr)
> >+ return false;
> >+ val = nla_get_u8(attr);
> >+ if (*dst == val)
> >+ return false;
> >+
> >+ *dst = val;
> >+ return true;
> >+}
> >+
> >+/* update u32 value used as bool from NLA_U8 */
> >+static inline bool ethnl_update_bool32(u32 *dst, struct nlattr *attr)
> >+{
> >+ u8 val;
> >+
> >+ if (!attr)
> >+ return false;
> >+ val = !!nla_get_u8(attr);
> >+ if (!!*dst == val)
> >+ return false;
> >+
> >+ *dst = val;
> >+ return true;
> >+}
> >+
> >+static inline bool ethnl_update_binary(u8 *dst, unsigned int len,
> >+ struct nlattr *attr)
> >+{
> >+ if (!attr)
> >+ return false;
> >+ if (nla_len(attr) < len)
> >+ len = nla_len(attr);
> >+ if (!memcmp(dst, nla_data(attr), len))
> >+ return false;
> >+
> >+ memcpy(dst, nla_data(attr), len);
> >+ return true;
> >+}
> >+
> >+static inline bool ethnl_update_bitfield32(u32 *dst, struct nlattr *attr)
> >+{
> >+ struct nla_bitfield32 change;
> >+ u32 newval;
> >+
> >+ if (!attr)
> >+ return false;
> >+ change = nla_get_bitfield32(attr);
> >+ newval = (*dst & ~change.selector) | (change.value & change.selector);
> >+ if (*dst == newval)
> >+ return false;
> >+
> >+ *dst = newval;
> >+ return true;
> >+}
>
> I don't understand puspose of these "update" helper functions. Try to
> avoid them. In general, please try to avoid wrappers around netlink api.

The purpose is to update a value according to a request attribute (if
present) and tell caller if the value was actually modified. They are
used like e.g.

mod = false;
if (ethnl_update_u32(&data.rx_pending, tb[ETHA_RING_RX_PENDING]))
mod = true;
if (ethnl_update_u32(&data.rx_mini_pending,
tb[ETHA_RING_RX_MINI_PENDING]))
mod = true;
if (ethnl_update_u32(&data.rx_jumbo_pending,
tb[ETHA_RING_RX_JUMBO_PENDING]))
mod = true;
if (ethnl_update_u32(&data.tx_pending, tb[ETHA_RING_TX_PENDING]))
mod = true;

if (!mod)
return 0;
/* call ethtool_ops handler and send notification */

so that we can omit calling the handler and sending a notification if
the request was no-op. Expanding the helpers here would make the code
needlessly complicated and error prone.

> >+static inline void warn_partial_info(struct genl_info *info)
> >+{
> >+ ETHNL_SET_ERRMSG(info, "not all requested data could be retrieved");
> >+}
> >+
> >+/* Check user privileges explicitly to allow finer access control based on
> >+ * context of the request or hiding part of the information from unprivileged
> >+ * users
> >+ */
> >+static inline bool ethnl_is_privileged(struct sk_buff *skb)
> >+{
> >+ struct net *net = sock_net(skb->sk);
> >+
> >+ return netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN);
>
> Same here.

It's still the same, helpers allow replacing repeating code patterns
with just saying what you are doing. But this one is called only from
one place so I can live without it.

> >+}
> >+
> >+/* total size of ETHA_*_DEV nested attribute; this is an upper estimate so that
> >+ * we do not need to hold RTNL longer than necessary to prevent rename between
> >+ * estimating the size and composing the message
> >+ */
> >+static inline unsigned int dev_ident_size(void)
>
> You are missing "ethnl_" prefix.

OK

Michal

2019-03-26 16:00:15

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/22] ethtool: netlink bitset handling

Mon, Mar 25, 2019 at 06:08:15PM CET, [email protected] wrote:
>Declare attribute type constants and add helper functions to generate and
>parse arbitrary length bit sets.

Hmm, this looks like a lot of work. Two things:
1) This is generic. Not really related to ethtool in any way. Could this
be done in netlink common code?
2) Did you think about leveraging NLA_BITFIELD32? What I mean is this:
NEST_START
NLA_BITFIELD32 index 0 (bit 0-31)
NLA_BITFIELD32 index 1 (bit 32-63)
NLA_BITFIELD32 index 2 (bit 64-95)
NLA_BITFIELD32 index 3 (bit 96-127)
....
NEST_END
It is basically an array of NLA_BITFIELD32.


2019-03-26 16:23:50

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 06/22] ethtool: helper functions for netlink interface

Tue, Mar 26, 2019 at 03:19:55PM CET, [email protected] wrote:
>On Tue, Mar 26, 2019 at 01:38:15PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:12PM CET, [email protected] wrote:
>> >+/* device specification */
>> >+
>> >+enum {
>> >+ ETHA_DEV_UNSPEC,
>>
>> ETHNL/ETHA looks odd.
>> I would prefer prefix "ETHTOOL_* for everything in uapi:
>> ETHTOOL_CMD_* for commands.
>> ETHTOOL_ATTR_* for attributes.
>>
>> Looks much nicer.
>
>Even with ETHA_ prefix, there are places where having to fit into
>80 columns makes the code hard to read. ETHTOOL_ATTR_ would make it
>even harder.

So cut the line. UAPI consistency is more important than to fit on a
80cols line.

ETH/ETHA is wrong prefix. This is "ETHTOOL". I can live with ETHTOOL_A_*
for attributes.


>
>> >+static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {
>>
>> ethnl_dev_policy. Please maintain the namespace if possible.
>> "dev_policy" looks way too generic if anyone sees it in the code.
>
>OK
>> >+ [ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
>> >+ [ETHA_DEV_INDEX] = { .type = NLA_U32 },
>> >+ [ETHA_DEV_NAME] = { .type = NLA_NUL_STRING,
>> >+ .len = IFNAMSIZ - 1 },
>> >+};
>> >+
>> >+/**
>> >+ * ethnl_dev_get() - get device identified by nested attribute
>> >+ * @info: genetlink info (also used for extack error reporting)
>> >+ * @nest: nest attribute with device identification
>> >+ *
>> >+ * Finds the network device identified by ETHA_DEV_INDEX (ifindex) or
>> >+ * ETHA_DEV_NAME (name) attributes in a nested attribute @nest. If both
>> >+ * are supplied, they must identify the same device. If successful, takes
>> >+ * a reference to the device which is to be released by caller.
>> >+ *
>> >+ * Return: pointer to the device if successful, ERR_PTR(err) on error
>> >+ */
>> >+struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest)
>>
>> I wonder, why you have this in nested attr? Aren't ifindex/ifname always
>> used as a handle for commands/notifications? Would be just in toplevel
>> (see devlink/nl80211)
>
>It makes things only slightly easier now but I would like to make the
>API as future proof as possible. If something needs to be added later
>(e.g. netns id) or something else changes (e.g. 64-bit ifindex), only
>one enum and these two helpers would need to be adjusted.

You can still have the helper without the nest. So if you don't nest
this, everything is the same, both helper and extra attr in enum.


>
>> >+ if (!nest) {
>> >+ ETHNL_SET_ERRMSG(info,
>> >+ "mandatory device identification missing");
>>
>> No need to wrap.
>
>OK
>
>> >+ ret = nla_parse_nested_strict(tb, ETHA_DEV_MAX, nest, dev_policy,
>> >+ info->extack);
>> >+ if (ret < 0)
>>
>> "if (ret)" is enough. "Returns 0 on success or a negative error code."
>
>OK
>
>> >+ if (strncmp(dev->name, nl_name, IFNAMSIZ)) {
>> >+ dev_put(dev);
>> >+ ETHNL_SET_ERRMSG(info,
>> >+ "ifindex and ifname do not match");
>>
>> No need to wrap.
>
>OK
>
>> >+/**
>> >+ * ethnl_fill_dev() - Put device identification nest into a message
>> >+ * @msg: skb with the message
>> >+ * @dev: network device to describe
>> >+ * @attrtype: attribute type to use for the nest
>> >+ *
>> >+ * Create a nested attribute with attributes describing given network device.
>> >+ * Clean up on error.
>> >+ *
>> >+ * Return: 0 on success, error value (-EMSGSIZE only) on error
>> >+ */
>> >+int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype)
>> >+{
>> >+ struct nlattr *nest;
>> >+ int ret = -EMSGSIZE;
>> >+
>> >+ nest = ethnl_nest_start(msg, attrtype);
>> >+ if (!nest)
>> >+ return -EMSGSIZE;
>> >+
>> >+ if (nla_put_u32(msg, ETHA_DEV_INDEX, (u32)dev->ifindex))
>> >+ goto err;
>> >+ if (nla_put_string(msg, ETHA_DEV_NAME, dev->name))
>> >+ goto err;
>> >+
>> >+ nla_nest_end(msg, nest);
>> >+ return 0;
>> >+err:
>> Usually this label is called "nla_put_failure". Same for the rest.
>> "err" could be confused with error return variable.
>>
>> have "ret = -EMSGSIZE" here. Easier to follow the error path.
>
>OK
>
>> >+ nla_nest_cancel(msg, nest);
>> >+ return ret;
>> >+}
>> >+
>> >+/**
>> >+ * ethnl_reply_init() - Create skb for a reply and fill device identification
>> >+ * @payload: payload length (without netlink and genetlink header)
>> >+ * @dev: device the reply is about (may be null)
>> >+ * @cmd: ETHNL_CMD_* command for reply
>> >+ * @info: genetlink info of the received packet we respond to
>> >+ * @ehdrp: place to store payload pointer returned by genlmsg_new()
>> >+ *
>> >+ * Return: pointer to allocated skb on success, NULL on error
>> >+ */
>> >+struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
>> >+ u16 dev_attrtype, struct genl_info *info,
>> >+ void **ehdrp)
>> >+{
>> >+ struct sk_buff *rskb;
>>
>> Could be just "skb", or "msg". You have "msg" in ethnl_fill_dev().
>> Please have it consistent.
>
>OK
>
>> >+ rskb = genlmsg_new(payload, GFP_KERNEL);
>> >+ if (!rskb) {
>> >+ ETHNL_SET_ERRMSG(info,
>> >+ "failed to allocate reply message");
>>
>> No need to wrap.
>
>OK
>
>> >+ int ret = ethnl_fill_dev(rskb, dev, dev_attrtype);
>> >+
>> >+ if (ret < 0)
>>
>> "if (ret)" is enough.
>
>OK
>
>> >+
>> >+#define ETHNL_SET_ERRMSG(info, msg) \
>> >+ do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)
>>
>> Why do you need this macro? Can info be null?
>
>The same functions are used for generating replies to GET requests (info
>is not null) and notifications (info is null, no extack).

So again, could this be generic? GENL_SET_ERR_MSG() could accept null
and ignore.


>
>> In general, macros like this should be avoided.
>
>At the expense of repeating the same patterns. In this case it would be
>acceptable but it would mean one more indentation level for lines with
>the error/warning messages.

No if you move the check to lower layer.


>
>> >+static inline int ethnl_str_size(const char *s)
>> >+{
>> >+ return nla_total_size(strlen(s) + 1);
>>
>> This looks like more generic helper, not tight to ethtool.
>
>OK
>
>> >+static inline int ethnl_str_ifne_size(const char *s)
>>
>> Eh? "ifne"? What is this good for?
>
>It's for "if not empty". Structures like ethtool_drvinfo use fixed size
>char arrays for strings where empty string means the information is not
>available. So if the string is empty, netlink attribute is omitted.
>
>> >+{
>> >+ return s[0] ? ethnl_str_size(s) : 0;
>> >+}
>> >+
>> >+static inline int ethnl_put_str_ifne(struct sk_buff *skb, int attrtype,
>> >+ const char *s)
>> >+{
>> >+ if (!s[0])
>> >+ return 0;
>> >+ return nla_put_string(skb, attrtype, s);
>>
>> I don't like helpers like this. Do the check in the caller and put or
>> not put the string there. It's a single if.
>
>OK
>
>
>> >+static inline struct nlattr *ethnl_nest_start(struct sk_buff *skb,
>> >+ int attrtype)
>> >+{
>> >+ return nla_nest_start(skb, attrtype | NLA_F_NESTED);
>>
>> Please use nla_nest_start directly and avoid helpers like this.
>
>OK. It's one more thing to check (and forget) in many places, though.
>
>> >+static inline int ethnlmsg_parse(const struct nlmsghdr *nlh,
>> >+ struct nlattr *tb[], int maxtype,
>> >+ const struct nla_policy *policy,
>> >+ struct genl_info *info)
>> >+{
>> >+ return nlmsg_parse_strict(nlh, GENL_HDRLEN, tb, maxtype, policy,
>> >+ info ? info->extack : NULL);
>>
>> Same thing, please use nlmsg_parse_strict directly.
>
>OK
>
>> >+/* ethnl_update_* return true if the value is changed */
>> >+static inline bool ethnl_update_u32(u32 *dst, struct nlattr *attr)
>> >+{
>> >+ u32 val;
>> >+
>> >+ if (!attr)
>> >+ return false;
>> >+ val = nla_get_u32(attr);
>> >+ if (*dst == val)
>> >+ return false;
>> >+
>> >+ *dst = val;
>> >+ return true;
>> >+}
>> >+
>> >+static inline bool ethnl_update_u8(u8 *dst, struct nlattr *attr)
>> >+{
>> >+ u8 val;
>> >+
>> >+ if (!attr)
>> >+ return false;
>> >+ val = nla_get_u8(attr);
>> >+ if (*dst == val)
>> >+ return false;
>> >+
>> >+ *dst = val;
>> >+ return true;
>> >+}
>> >+
>> >+/* update u32 value used as bool from NLA_U8 */
>> >+static inline bool ethnl_update_bool32(u32 *dst, struct nlattr *attr)
>> >+{
>> >+ u8 val;
>> >+
>> >+ if (!attr)
>> >+ return false;
>> >+ val = !!nla_get_u8(attr);
>> >+ if (!!*dst == val)
>> >+ return false;
>> >+
>> >+ *dst = val;
>> >+ return true;
>> >+}
>> >+
>> >+static inline bool ethnl_update_binary(u8 *dst, unsigned int len,
>> >+ struct nlattr *attr)
>> >+{
>> >+ if (!attr)
>> >+ return false;
>> >+ if (nla_len(attr) < len)
>> >+ len = nla_len(attr);
>> >+ if (!memcmp(dst, nla_data(attr), len))
>> >+ return false;
>> >+
>> >+ memcpy(dst, nla_data(attr), len);
>> >+ return true;
>> >+}
>> >+
>> >+static inline bool ethnl_update_bitfield32(u32 *dst, struct nlattr *attr)
>> >+{
>> >+ struct nla_bitfield32 change;
>> >+ u32 newval;
>> >+
>> >+ if (!attr)
>> >+ return false;
>> >+ change = nla_get_bitfield32(attr);
>> >+ newval = (*dst & ~change.selector) | (change.value & change.selector);
>> >+ if (*dst == newval)
>> >+ return false;
>> >+
>> >+ *dst = newval;
>> >+ return true;
>> >+}
>>
>> I don't understand puspose of these "update" helper functions. Try to
>> avoid them. In general, please try to avoid wrappers around netlink api.
>
>The purpose is to update a value according to a request attribute (if
>present) and tell caller if the value was actually modified. They are
>used like e.g.
>
> mod = false;
> if (ethnl_update_u32(&data.rx_pending, tb[ETHA_RING_RX_PENDING]))
> mod = true;
> if (ethnl_update_u32(&data.rx_mini_pending,
> tb[ETHA_RING_RX_MINI_PENDING]))
> mod = true;
> if (ethnl_update_u32(&data.rx_jumbo_pending,
> tb[ETHA_RING_RX_JUMBO_PENDING]))
> mod = true;
> if (ethnl_update_u32(&data.tx_pending, tb[ETHA_RING_TX_PENDING]))
> mod = true;
>
> if (!mod)
> return 0;
> /* call ethtool_ops handler and send notification */
>
>so that we can omit calling the handler and sending a notification if
>the request was no-op. Expanding the helpers here would make the code
>needlessly complicated and error prone.

Got it. Makes sense.


>
>> >+static inline void warn_partial_info(struct genl_info *info)
>> >+{
>> >+ ETHNL_SET_ERRMSG(info, "not all requested data could be retrieved");
>> >+}
>> >+
>> >+/* Check user privileges explicitly to allow finer access control based on
>> >+ * context of the request or hiding part of the information from unprivileged
>> >+ * users
>> >+ */
>> >+static inline bool ethnl_is_privileged(struct sk_buff *skb)
>> >+{
>> >+ struct net *net = sock_net(skb->sk);
>> >+
>> >+ return netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN);
>>
>> Same here.
>
>It's still the same, helpers allow replacing repeating code patterns
>with just saying what you are doing. But this one is called only from
>one place so I can live without it.

It is always a tradeoff. We have to be careful not to abuse it.


>
>> >+}
>> >+
>> >+/* total size of ETHA_*_DEV nested attribute; this is an upper estimate so that
>> >+ * we do not need to hold RTNL longer than necessary to prevent rename between
>> >+ * estimating the size and composing the message
>> >+ */
>> >+static inline unsigned int dev_ident_size(void)
>>
>> You are missing "ethnl_" prefix.
>
>OK
>
>Michal

2019-03-26 16:34:58

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

Mon, Mar 25, 2019 at 06:08:18PM CET, [email protected] wrote:
>Add infrastructure for ethtool netlink notifications. There is only one
>multicast group "monitor" which is used to notify userspace about changes.
>Notifications are supposed to be broadcasted on every configuration change,
>whether it is done using the netlink interface or legacy ioctl one.
>
>To trigger an ethtool notification, both ethtool netlink and external code
>use ethtool_notify() helper. This helper requires RTNL to be held and may
>sleep.
>
>Signed-off-by: Michal Kubecek <[email protected]>
>---
> include/linux/ethtool_netlink.h | 5 ++++
> include/linux/netdevice.h | 12 +++++++++
> include/uapi/linux/ethtool_netlink.h | 2 ++
> net/ethtool/netlink.c | 37 ++++++++++++++++++++++++++++
> net/ethtool/netlink.h | 2 ++
> 5 files changed, 58 insertions(+)
>
>diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h
>index 0412adb4f42f..2a15e64a16f3 100644
>--- a/include/linux/ethtool_netlink.h
>+++ b/include/linux/ethtool_netlink.h
>@@ -5,5 +5,10 @@
>
> #include <uapi/linux/ethtool_netlink.h>
> #include <linux/ethtool.h>
>+#include <linux/netdevice.h>
>+
>+enum ethtool_multicast_groups {
>+ ETHNL_MCGRP_MONITOR,
>+};
>
> #endif /* _LINUX_ETHTOOL_NETLINK_H_ */
>diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>index 166fdc0a78b4..bc761511edb4 100644
>--- a/include/linux/netdevice.h
>+++ b/include/linux/netdevice.h
>@@ -4353,6 +4353,18 @@ struct netdev_notifier_bonding_info {
> void netdev_bonding_info_change(struct net_device *dev,
> struct netdev_bonding_info *bonding_info);
>
>+#if IS_ENABLED(CONFIG_ETHTOOL_NETLINK)
>+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
>+ unsigned int cmd, u32 req_mask, const void *data);
>+#else
>+static inline void ethtool_notify(struct net_device *dev,
>+ struct netlink_ext_ack *extack,
>+ unsigned int cmd, u32 req_mask,
>+ const void *data)
>+{
>+}
>+#endif
>+
> static inline
> struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features)
> {
>diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
>index de18e076ed69..91e4d117957b 100644
>--- a/include/uapi/linux/ethtool_netlink.h
>+++ b/include/uapi/linux/ethtool_netlink.h
>@@ -65,4 +65,6 @@ enum {
> #define ETHTOOL_GENL_NAME "ethtool"
> #define ETHTOOL_GENL_VERSION 1
>
>+#define ETHTOOL_MCGRP_MONITOR_NAME "monitor"
>+
> #endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */
>diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
>index cc6829ba4331..4a31765165ea 100644
>--- a/net/ethtool/netlink.c
>+++ b/net/ethtool/netlink.c
>@@ -4,6 +4,10 @@
> #include <linux/ethtool_netlink.h>
> #include "netlink.h"
>
>+u32 ethnl_bcast_seq;
>+
>+static bool ethnl_ok __read_mostly;
>+
> static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {
> [ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
> [ETHA_DEV_INDEX] = { .type = NLA_U32 },
>@@ -147,11 +151,41 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
> return NULL;
> }
>
>+/* notifications */
>+
>+typedef void (*ethnl_notify_handler_t)(struct net_device *dev,
>+ struct netlink_ext_ack *extack,
>+ unsigned int cmd, u32 req_mask,
>+ const void *data);
>+
>+ethnl_notify_handler_t ethnl_notify_handlers[] = {
>+};
>+
>+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
>+ unsigned int cmd, u32 req_mask, const void *data)
>+{
>+ if (unlikely(!ethnl_ok))

Why do you need this?


>+ return;
>+ ASSERT_RTNL();
>+
>+ if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
>+ ethnl_notify_handlers[cmd]))
>+ ethnl_notify_handlers[cmd](dev, extack, cmd, req_mask, data);
>+ else
>+ WARN_ONCE(1, "notification %u not implemented (dev=%s, req_mask=0x%x)\n",
>+ cmd, netdev_name(dev), req_mask);
>+}
>+EXPORT_SYMBOL(ethtool_notify);

Please be consistent with prefixes. Rest of the code (most of it) has
prefix "ethnl".


>+
> /* genetlink setup */
>
> static const struct genl_ops ethtool_genl_ops[] = {
> };
>
>+static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
>+ [ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME },
>+};
>+
> struct genl_family ethtool_genl_family = {
> .hdrsize = 0,
> .name = ETHTOOL_GENL_NAME,
>@@ -160,6 +194,8 @@ struct genl_family ethtool_genl_family = {
> .parallel_ops = true,
> .ops = ethtool_genl_ops,
> .n_ops = ARRAY_SIZE(ethtool_genl_ops),
>+ .mcgrps = ethtool_nl_mcgrps,
>+ .n_mcgrps = ARRAY_SIZE(ethtool_nl_mcgrps),
> };
>
> /* module setup */
>@@ -171,6 +207,7 @@ static int __init ethnl_init(void)
> ret = genl_register_family(&ethtool_genl_family);
> if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
> return ret;
>+ ethnl_ok = true;
>
> return 0;
> }
>diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
>index b8a6cd3dc3e3..5f2299548915 100644
>--- a/net/ethtool/netlink.h
>+++ b/net/ethtool/netlink.h
>@@ -11,6 +11,8 @@
> #define ETHNL_SET_ERRMSG(info, msg) \
> do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)
>
>+extern u32 ethnl_bcast_seq;

Why do you need to have this in header? Second, it is not used by
anything. Please don't introduce variables that are not used. Introduce
them only in patch where you use it.


>+
> extern struct genl_family ethtool_genl_family;
>
> struct net_device *ethnl_dev_get(struct genl_info *info, struct nlattr *nest);
>--
>2.21.0
>

2019-03-26 16:37:53

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Mon, Mar 25, 2019 at 06:08:09PM CET, [email protected] wrote:
>Basic genetlink and init infrastructure for the netlink interface, register
>genetlink family "ethtool". Introduce CONFIG_ETHTOOL_NETLINK Kconfig
>option. Add interface description into Documentation/networking.
>
>Signed-off-by: Michal Kubecek <[email protected]>
>---

[...]


>diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
>new file mode 100644
>index 000000000000..85dd6dac71a2
>--- /dev/null
>+++ b/net/ethtool/netlink.c
>@@ -0,0 +1,34 @@
>+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
>+
>+#include <linux/ethtool_netlink.h>
>+#include "netlink.h"
>+
>+/* genetlink setup */
>+
>+static const struct genl_ops ethtool_genl_ops[] = {

Please be consistent with prefixes. Either use "ethtool_" or "ethnl_"
for all functions and variables in this code.


>+};
>+
>+struct genl_family ethtool_genl_family = {
>+ .hdrsize = 0,
>+ .name = ETHTOOL_GENL_NAME,
>+ .version = ETHTOOL_GENL_VERSION,
>+ .netnsok = true,
>+ .parallel_ops = true,
>+ .ops = ethtool_genl_ops,
>+ .n_ops = ARRAY_SIZE(ethtool_genl_ops),
>+};
>+
>+/* module setup */
>+
>+static int __init ethnl_init(void)
>+{
>+ int ret;
>+
>+ ret = genl_register_family(&ethtool_genl_family);
>+ if (WARN(ret < 0, "ethtool: genetlink family registration failed"))

Why do you need this warning? Please avoid it.


>+ return ret;
>+
>+ return 0;
>+}
>+
>+subsys_initcall(ethnl_init);
>diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
>new file mode 100644
>index 000000000000..63063b582ca2
>--- /dev/null
>+++ b/net/ethtool/netlink.h
>@@ -0,0 +1,12 @@
>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>+
>+#ifndef _NET_ETHTOOL_NETLINK_H
>+#define _NET_ETHTOOL_NETLINK_H
>+
>+#include <linux/ethtool_netlink.h>
>+#include <linux/netdevice.h>
>+#include <net/genetlink.h>
>+
>+extern struct genl_family ethtool_genl_family;
>+
>+#endif /* _NET_ETHTOOL_NETLINK_H */
>--
>2.21.0
>

2019-03-26 17:33:13

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

On Tue, Mar 26, 2019 at 05:36:40PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:09PM CET, [email protected] wrote:
> >+/* genetlink setup */
> >+
> >+static const struct genl_ops ethtool_genl_ops[] = {
>
> Please be consistent with prefixes. Either use "ethtool_" or "ethnl_"
> for all functions and variables in this code.

OK

> >+/* module setup */
> >+
> >+static int __init ethnl_init(void)
> >+{
> >+ int ret;
> >+
> >+ ret = genl_register_family(&ethtool_genl_family);
> >+ if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
>
> Why do you need this warning? Please avoid it.

I'm confused now... few days ago you replied "+1" to the idea:

http://lkml.kernel.org/r/20190321162105.GU2087@nanopsycho

I agreed that panic() (which is what e.g. rtnetlink does) would be an
overkill but I would be definitely opposed to not having anything in the
log at all and just silently going on without the interface (which may
result in misconfigured network). I believe that if this fails, it is
a sign of something going very wrong inside the kernel so that the "W"
taint flag would be appropriate.

Michal

2019-03-26 18:00:42

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/22] ethtool: netlink bitset handling

On Tue, Mar 26, 2019 at 04:59:11PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:15PM CET, [email protected] wrote:
> >Declare attribute type constants and add helper functions to generate and
> >parse arbitrary length bit sets.
>
> Hmm, this looks like a lot of work. Two things:
> 1) This is generic. Not really related to ethtool in any way. Could this
> be done in netlink common code?

I suppose it could if other netlink based APIs would be interested in
using it. The only ethtool specific part is the support for "legacy
style names" (fixed size strings) but that is something I'm not really
happy about. Perhaps it's time to return to the original idea of
supporting only arrays of (char *) and creating them around existing
fixed size ones.

> 2) Did you think about leveraging NLA_BITFIELD32? What I mean is this:
> NEST_START
> NLA_BITFIELD32 index 0 (bit 0-31)
> NLA_BITFIELD32 index 1 (bit 32-63)
> NLA_BITFIELD32 index 2 (bit 64-95)
> NLA_BITFIELD32 index 3 (bit 96-127)
> ....
> NEST_END
> It is basically an array of NLA_BITFIELD32.

That would be similar to compact form but it would introduce the
interleaving and extra struct nlattr header for each block. I don't
think it would make things easier.

The verbose form is meant to allow requests like

ethtool -K eth0 tx-vlan-stag-hw-insert on
ethtool -s eth0 advertise 1000baseT/Full off

without either keeping the table of available flags in sync between
kernel and userspace (all userspace users of the API) or having to ask
for the list first (in case of one shot requests as above; long running
tools like "ethtool --monitor" or config management daemons would keep
the tables and use compact form).

Michal

2019-03-26 18:19:46

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

On Tue, Mar 26, 2019 at 05:34:00PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:18PM CET, [email protected] wrote:
> >+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
> >+ unsigned int cmd, u32 req_mask, const void *data)
> >+{
> >+ if (unlikely(!ethnl_ok))
>
> Why do you need this?

If genetlink family registration fails, ethtool_notify() can be still
called from other code (e.g. the ethtool ioctl interface). In such case,
better bail out right away than fail somewhere later (probably after
preparing the message which can't be sent anyway).

> >+ return;
> >+ ASSERT_RTNL();
> >+
> >+ if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
> >+ ethnl_notify_handlers[cmd]))
> >+ ethnl_notify_handlers[cmd](dev, extack, cmd, req_mask, data);
> >+ else
> >+ WARN_ONCE(1, "notification %u not implemented (dev=%s, req_mask=0x%x)\n",
> >+ cmd, netdev_name(dev), req_mask);
> >+}
> >+EXPORT_SYMBOL(ethtool_notify);
>
> Please be consistent with prefixes. Rest of the code (most of it) has
> prefix "ethnl".

The reason why I used ethtool_ prefix here was that this function is an
entry point which can be called from anywhere and other code doesn't
care if we are using netlink or something else. But as any caller would
need to include <linux/ethtool_netlink.h> anyway to get the constants
for cmd and req_mask, there is no point hiding the fact. I'll rename it
to ethnl_notify().

> >diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
> >index b8a6cd3dc3e3..5f2299548915 100644
> >--- a/net/ethtool/netlink.h
> >+++ b/net/ethtool/netlink.h
> >@@ -11,6 +11,8 @@
> > #define ETHNL_SET_ERRMSG(info, msg) \
> > do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)
> >
> >+extern u32 ethnl_bcast_seq;
>
> Why do you need to have this in header? Second, it is not used by
> anything. Please don't introduce variables that are not used. Introduce
> them only in patch where you use it.

It's the same as with ethtool_genl_family. I'll make it static as well
until it's used in some other file.

Michal

2019-03-26 19:47:44

by David Miller

[permalink] [raw]
Subject: Re: [PATCH net-next v5 01/22] rtnetlink: provide permanent hardware address in RTM_NEWLINK

From: Michal Kubecek <[email protected]>
Date: Mon, 25 Mar 2019 18:07:57 +0100 (CET)

> @@ -1683,6 +1684,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
> nla_put_s32(skb, IFLA_NEW_IFINDEX, new_ifindex) < 0)
> goto nla_put_failure;
>
> + if (nla_put(skb, IFLA_PERM_ADDRESS, dev->addr_len, dev->perm_addr))
> + goto nla_put_failure;
>
> rcu_read_lock();
> if (rtnl_fill_link_af(skb, dev, ext_filter_mask))

I guess, as Jiri says, we really do have to check if the driver
actually set this before pushing the attribute out to userspace.

But seriously, you chould just check for dev_addr_len zeros.

All zeros is not a valid link address on any link type.

2019-03-27 09:26:57

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Tue, Mar 26, 2019 at 06:32:15PM CET, [email protected] wrote:
>On Tue, Mar 26, 2019 at 05:36:40PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:09PM CET, [email protected] wrote:
>> >+/* genetlink setup */
>> >+
>> >+static const struct genl_ops ethtool_genl_ops[] = {
>>
>> Please be consistent with prefixes. Either use "ethtool_" or "ethnl_"
>> for all functions and variables in this code.
>
>OK
>
>> >+/* module setup */
>> >+
>> >+static int __init ethnl_init(void)
>> >+{
>> >+ int ret;
>> >+
>> >+ ret = genl_register_family(&ethtool_genl_family);
>> >+ if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
>>
>> Why do you need this warning? Please avoid it.
>
>I'm confused now... few days ago you replied "+1" to the idea:
>
> http://lkml.kernel.org/r/20190321162105.GU2087@nanopsycho
>
>I agreed that panic() (which is what e.g. rtnetlink does) would be an
>overkill but I would be definitely opposed to not having anything in the
>log at all and just silently going on without the interface (which may
>result in misconfigured network). I believe that if this fails, it is
>a sign of something going very wrong inside the kernel so that the "W"
>taint flag would be appropriate.

Okay. Fair. It's just that I looked over the code and did not find any
other genl_register_family() call with WARN.


>
>Michal

2019-03-27 09:27:08

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

On Tue, Mar 26, 2019 at 02:42:51PM +0100, Jiri Pirko wrote:
> Tue, Mar 26, 2019 at 02:24:27PM CET, [email protected] wrote:
> >On Tue, Mar 26, 2019 at 01:09:09PM +0100, Jiri Pirko wrote:
> >> Mon, Mar 25, 2019 at 06:08:09PM CET, [email protected] wrote:
> >> >+Device identification
> >> >+---------------------
> >> >+
> >> >+When appropriate, network device is identified by a nested attribute named
> >> >+ETHA_*_DEV. This attribute can contain
> >>
> >> Isn't it ETHA_DEV_*? I must admit I'm a bit confused.
> >
> >ETHA_*_DEV is the nesting attribute (e.g. ETHA_SETTINGS_DEV), ETHA_DEV_*
> >(ETHA_DEV_INDEX and ETHA_DEV_NAME) are in the nest.
>
> Yeah. I wonder why you need to duplicate this. Can this be in top-lever
> attr enum that is shared among all commands? It is there anyway and
> looks a bit silly to have "DEV" attr separate for every command.
> Something like this:
>
> ATTR_IFINDEX
> ATTR_IFNAME
> ATTR_SOMEOTHER (flags perhaps)
> ATTR_CMD_SPECIFIC_NEST_START
> ATTR_CMDX_SOMETHING
> ATTR_CMDX_SOMETHING2
> ATTR_CMDX_SOMETHING3
> ATTR_CMD_SPECIFIC_NEST_END

I would rather prefer the opposite:

ATTR_HEADER
ATTR_IFINDEX
ATTR_IFNAME
ATTR_INFO_MASK
ATTR_PER_QUEUE
ATTR_CMDX_SOMETHING
ATTR_CMDX_SOMETHING2
ATTR_CMDX_SOMETHING3
...

This way the "header" with universal attributes (not specfic to
a particular message type) would be kept together at the beginning even
after we need to add some more later and command specific attributes
would still have fixed numbers (starting from 2). I'll think about it
some more and check what would be pros and cons of the two variants
when parsing and generating the messages.

> >> >+Messages of type "get" are used by userspace to request information and
> >> >+usually do not contain any attributes (that may be added later for dump
> >> >+filtering). Kernel response is in the form of corresponding "set" message;
> >>
> >> Okay. Do we want reply to "*_cmd_something_get" command to be
> >> "*_cmd_something_set". That sounds odd. Why reply has to be "cmd"? Why
> >> not something like "reply" or "response"?
> >> This should work for both "doit/dumpit" and notifications.
> >
> >As stated right below, the aim is to use the same format for replies to
> >GET requests as userspace uses for related SET requests. We could use
> >different id (genlmsghdr::cmd) but that seemed like a waste for no actual
> >gain.
>
> I understand. I just wonder if the replies/notifications could use the
> same name, not having "set" in it. I know we have it like this in many
> netlink ifaces, it is however confusing to users. So once we are doing
> this from scratch, we can do it differently.

How about

ETHTOOL_MSG_GET_FOO for get requests
ETHTOOL_MSG_FOO for get replies, notifications and set requests
ETHTOOL_MSG_ACT_FOO for actions (renegotiation, reset, blinking, ...)

?

Michal

2019-03-27 09:39:41

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

Tue, Mar 26, 2019 at 07:17:20PM CET, [email protected] wrote:
>On Tue, Mar 26, 2019 at 05:34:00PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:18PM CET, [email protected] wrote:
>> >+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
>> >+ unsigned int cmd, u32 req_mask, const void *data)
>> >+{
>> >+ if (unlikely(!ethnl_ok))
>>
>> Why do you need this?
>
>If genetlink family registration fails, ethtool_notify() can be still
>called from other code (e.g. the ethtool ioctl interface). In such case,
>better bail out right away than fail somewhere later (probably after
>preparing the message which can't be sent anyway).

Again, haven't seen this in any other gen netlink implementation. Why do
they not need it?


>
>> >+ return;
>> >+ ASSERT_RTNL();
>> >+
>> >+ if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
>> >+ ethnl_notify_handlers[cmd]))
>> >+ ethnl_notify_handlers[cmd](dev, extack, cmd, req_mask, data);
>> >+ else
>> >+ WARN_ONCE(1, "notification %u not implemented (dev=%s, req_mask=0x%x)\n",
>> >+ cmd, netdev_name(dev), req_mask);
>> >+}
>> >+EXPORT_SYMBOL(ethtool_notify);
>>
>> Please be consistent with prefixes. Rest of the code (most of it) has
>> prefix "ethnl".
>
>The reason why I used ethtool_ prefix here was that this function is an
>entry point which can be called from anywhere and other code doesn't
>care if we are using netlink or something else. But as any caller would
>need to include <linux/ethtool_netlink.h> anyway to get the constants
>for cmd and req_mask, there is no point hiding the fact. I'll rename it
>to ethnl_notify().

Okay.


>
>> >diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
>> >index b8a6cd3dc3e3..5f2299548915 100644
>> >--- a/net/ethtool/netlink.h
>> >+++ b/net/ethtool/netlink.h
>> >@@ -11,6 +11,8 @@
>> > #define ETHNL_SET_ERRMSG(info, msg) \
>> > do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)
>> >
>> >+extern u32 ethnl_bcast_seq;
>>
>> Why do you need to have this in header? Second, it is not used by
>> anything. Please don't introduce variables that are not used. Introduce
>> them only in patch where you use it.
>
>It's the same as with ethtool_genl_family. I'll make it static as well
>until it's used in some other file.

Not only static. You should remove it as you are not using it in this
patch at all.


>
>Michal

2019-03-27 09:52:40

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Wed, Mar 27, 2019 at 10:26:04AM CET, [email protected] wrote:
>On Tue, Mar 26, 2019 at 02:42:51PM +0100, Jiri Pirko wrote:
>> Tue, Mar 26, 2019 at 02:24:27PM CET, [email protected] wrote:
>> >On Tue, Mar 26, 2019 at 01:09:09PM +0100, Jiri Pirko wrote:
>> >> Mon, Mar 25, 2019 at 06:08:09PM CET, [email protected] wrote:
>> >> >+Device identification
>> >> >+---------------------
>> >> >+
>> >> >+When appropriate, network device is identified by a nested attribute named
>> >> >+ETHA_*_DEV. This attribute can contain
>> >>
>> >> Isn't it ETHA_DEV_*? I must admit I'm a bit confused.
>> >
>> >ETHA_*_DEV is the nesting attribute (e.g. ETHA_SETTINGS_DEV), ETHA_DEV_*
>> >(ETHA_DEV_INDEX and ETHA_DEV_NAME) are in the nest.
>>
>> Yeah. I wonder why you need to duplicate this. Can this be in top-lever
>> attr enum that is shared among all commands? It is there anyway and
>> looks a bit silly to have "DEV" attr separate for every command.
>> Something like this:
>>
>> ATTR_IFINDEX
>> ATTR_IFNAME
>> ATTR_SOMEOTHER (flags perhaps)
>> ATTR_CMD_SPECIFIC_NEST_START
>> ATTR_CMDX_SOMETHING
>> ATTR_CMDX_SOMETHING2
>> ATTR_CMDX_SOMETHING3
>> ATTR_CMD_SPECIFIC_NEST_END
>
>I would rather prefer the opposite:
>
>ATTR_HEADER
> ATTR_IFINDEX
> ATTR_IFNAME
> ATTR_INFO_MASK
> ATTR_PER_QUEUE
>ATTR_CMDX_SOMETHING
>ATTR_CMDX_SOMETHING2
>ATTR_CMDX_SOMETHING3
>...
>
>This way the "header" with universal attributes (not specfic to
>a particular message type) would be kept together at the beginning even
>after we need to add some more later and command specific attributes
>would still have fixed numbers (starting from 2). I'll think about it
>some more and check what would be pros and cons of the two variants
>when parsing and generating the messages.

Okay, so what you suggest is per-cmd top level attr enum. That leads to
duplications of common attributes:
You would have to have HEADER attr defined in every cmd enum:

enum cmdx {
ATTR_CMDX_HEADER
ATTR_CMDX_SOMETHING
ATTR_CMDX_SOMETHING2
ATTR_CMDX_SOMETHING3
};

enum cmdy {
ATTR_CMDY_HEADER
ATTR_CMDY_SOMETHING
ATTR_CMDY_SOMETHING2
ATTR_CMDY_SOMETHING3
};

That is odd. TC has it and I hate it there :)

I think that the rtnetlink example is better. The generic things are in
generic top level enum. Then you have IFLA_LINKINFO with per-type enums.

>
>> >> >+Messages of type "get" are used by userspace to request information and
>> >> >+usually do not contain any attributes (that may be added later for dump
>> >> >+filtering). Kernel response is in the form of corresponding "set" message;
>> >>
>> >> Okay. Do we want reply to "*_cmd_something_get" command to be
>> >> "*_cmd_something_set". That sounds odd. Why reply has to be "cmd"? Why
>> >> not something like "reply" or "response"?
>> >> This should work for both "doit/dumpit" and notifications.
>> >
>> >As stated right below, the aim is to use the same format for replies to
>> >GET requests as userspace uses for related SET requests. We could use
>> >different id (genlmsghdr::cmd) but that seemed like a waste for no actual
>> >gain.
>>
>> I understand. I just wonder if the replies/notifications could use the
>> same name, not having "set" in it. I know we have it like this in many
>> netlink ifaces, it is however confusing to users. So once we are doing
>> this from scratch, we can do it differently.
>
>How about
>
> ETHTOOL_MSG_GET_FOO for get requests
> ETHTOOL_MSG_FOO for get replies, notifications and set requests
> ETHTOOL_MSG_ACT_FOO for actions (renegotiation, reset, blinking, ...)
>
>?

Why don't you have ETHTOOL_MSG_SET_FOO for set? I think that for
kerne->userspace the ETHTOOL_MSG_FOO if fine. I would change the
ordering of words thought, but it is cosmetics:
ETHTOOL_MSG_FOO /* kernel->userspace messages - replies, notifications */
ETHTOOL_MSG_FOO_GET
ETHTOOL_MSG_FOO_SET
ETHTOOL_MSG_FOO_ACT

What do you think?

>
>Michal

2019-03-27 09:52:51

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

On Wed, Mar 27, 2019 at 10:38:43AM +0100, Jiri Pirko wrote:
> Tue, Mar 26, 2019 at 07:17:20PM CET, [email protected] wrote:
> >On Tue, Mar 26, 2019 at 05:34:00PM +0100, Jiri Pirko wrote:
> >> Mon, Mar 25, 2019 at 06:08:18PM CET, [email protected] wrote:
> >> >+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
> >> >+ unsigned int cmd, u32 req_mask, const void *data)
> >> >+{
> >> >+ if (unlikely(!ethnl_ok))
> >>
> >> Why do you need this?
> >
> >If genetlink family registration fails, ethtool_notify() can be still
> >called from other code (e.g. the ethtool ioctl interface). In such case,
> >better bail out right away than fail somewhere later (probably after
> >preparing the message which can't be sent anyway).
>
> Again, haven't seen this in any other gen netlink implementation. Why do
> they not need it?

Hi Jiri

All other gen netlink kill the machine dead if they cannot register
the socket.

Andrew

2019-03-27 09:58:41

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/22] ethtool: netlink bitset handling

Tue, Mar 26, 2019 at 06:59:32PM CET, [email protected] wrote:
>On Tue, Mar 26, 2019 at 04:59:11PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:15PM CET, [email protected] wrote:
>> >Declare attribute type constants and add helper functions to generate and
>> >parse arbitrary length bit sets.
>>
>> Hmm, this looks like a lot of work. Two things:
>> 1) This is generic. Not really related to ethtool in any way. Could this
>> be done in netlink common code?
>
>I suppose it could if other netlink based APIs would be interested in
>using it. The only ethtool specific part is the support for "legacy
>style names" (fixed size strings) but that is something I'm not really
>happy about. Perhaps it's time to return to the original idea of
>supporting only arrays of (char *) and creating them around existing
>fixed size ones.

Wait, could you please describe this more?


>
>> 2) Did you think about leveraging NLA_BITFIELD32? What I mean is this:
>> NEST_START
>> NLA_BITFIELD32 index 0 (bit 0-31)
>> NLA_BITFIELD32 index 1 (bit 32-63)
>> NLA_BITFIELD32 index 2 (bit 64-95)
>> NLA_BITFIELD32 index 3 (bit 96-127)
>> ....
>> NEST_END
>> It is basically an array of NLA_BITFIELD32.
>
>That would be similar to compact form but it would introduce the
>interleaving and extra struct nlattr header for each block. I don't
>think it would make things easier.

I'm just trying to figure out if it would be possible to use existing
building block instead of introducing new ones....


>
>The verbose form is meant to allow requests like
>
> ethtool -K eth0 tx-vlan-stag-hw-insert on
> ethtool -s eth0 advertise 1000baseT/Full off
>
>without either keeping the table of available flags in sync between
>kernel and userspace (all userspace users of the API) or having to ask
>for the list first (in case of one shot requests as above; long running
>tools like "ethtool --monitor" or config management daemons would keep
>the tables and use compact form).

Okay, I have to go over those patches but this smells.


>
>Michal

2019-03-27 10:02:00

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

On Wed, Mar 27, 2019 at 10:38:43AM +0100, Jiri Pirko wrote:
> Tue, Mar 26, 2019 at 07:17:20PM CET, [email protected] wrote:
> >On Tue, Mar 26, 2019 at 05:34:00PM +0100, Jiri Pirko wrote:
> >> Mon, Mar 25, 2019 at 06:08:18PM CET, [email protected] wrote:
> >> >+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
> >> >+ unsigned int cmd, u32 req_mask, const void *data)
> >> >+{
> >> >+ if (unlikely(!ethnl_ok))
> >>
> >> Why do you need this?
> >
> >If genetlink family registration fails, ethtool_notify() can be still
> >called from other code (e.g. the ethtool ioctl interface). In such case,
> >better bail out right away than fail somewhere later (probably after
> >preparing the message which can't be sent anyway).
>
> Again, haven't seen this in any other gen netlink implementation. Why do
> they not need it?

Do they have notifications triggered from other code by directly calling
a function (i.e. not through e.g. a netdev notifier)?

An alternative to a flag would be using a RCU pointer for this function
(initialized to null and set once the family is registered) which is
what e.g. netfilter is doing with its hooks but that would mean that
each external notification would use an indirect call.

> >> >diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
> >> >index b8a6cd3dc3e3..5f2299548915 100644
> >> >--- a/net/ethtool/netlink.h
> >> >+++ b/net/ethtool/netlink.h
> >> >@@ -11,6 +11,8 @@
> >> > #define ETHNL_SET_ERRMSG(info, msg) \
> >> > do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)
> >> >
> >> >+extern u32 ethnl_bcast_seq;
> >>
> >> Why do you need to have this in header? Second, it is not used by
> >> anything. Please don't introduce variables that are not used. Introduce
> >> them only in patch where you use it.
> >
> >It's the same as with ethtool_genl_family. I'll make it static as well
> >until it's used in some other file.
>
> Not only static. You should remove it as you are not using it in this
> patch at all.

OK

Michal

2019-03-27 10:05:44

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

Wed, Mar 27, 2019 at 10:51:48AM CET, [email protected] wrote:
>On Wed, Mar 27, 2019 at 10:38:43AM +0100, Jiri Pirko wrote:
>> Tue, Mar 26, 2019 at 07:17:20PM CET, [email protected] wrote:
>> >On Tue, Mar 26, 2019 at 05:34:00PM +0100, Jiri Pirko wrote:
>> >> Mon, Mar 25, 2019 at 06:08:18PM CET, [email protected] wrote:
>> >> >+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
>> >> >+ unsigned int cmd, u32 req_mask, const void *data)
>> >> >+{
>> >> >+ if (unlikely(!ethnl_ok))
>> >>
>> >> Why do you need this?
>> >
>> >If genetlink family registration fails, ethtool_notify() can be still
>> >called from other code (e.g. the ethtool ioctl interface). In such case,
>> >better bail out right away than fail somewhere later (probably after
>> >preparing the message which can't be sent anyway).
>>
>> Again, haven't seen this in any other gen netlink implementation. Why do
>> they not need it?
>
>Hi Jiri
>
>All other gen netlink kill the machine dead if they cannot register

Wait, what do you mean be "kill the machine dead"?

>the socket.

Example:
static int __init devlink_init(void)
{
return genl_register_family(&devlink_nl_family);
}


2019-03-27 10:17:42

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

> Wait, what do you mean be "kill the machine dead"?

panic()

Andrew

2019-03-27 10:21:31

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/22] ethtool: netlink bitset handling

On Wed, Mar 27, 2019 at 10:57:49AM +0100, Jiri Pirko wrote:
> Tue, Mar 26, 2019 at 06:59:32PM CET, [email protected] wrote:
> >On Tue, Mar 26, 2019 at 04:59:11PM +0100, Jiri Pirko wrote:
> >> Mon, Mar 25, 2019 at 06:08:15PM CET, [email protected] wrote:
> >> >Declare attribute type constants and add helper functions to generate and
> >> >parse arbitrary length bit sets.
> >>
> >> Hmm, this looks like a lot of work. Two things:
> >> 1) This is generic. Not really related to ethtool in any way. Could this
> >> be done in netlink common code?
> >
> >I suppose it could if other netlink based APIs would be interested in
> >using it. The only ethtool specific part is the support for "legacy
> >style names" (fixed size strings) but that is something I'm not really
> >happy about. Perhaps it's time to return to the original idea of
> >supporting only arrays of (char *) and creating them around existing
> >fixed size ones.
>
> Wait, could you please describe this more?

At the moment, there are two formats of bit name tables. Legacy format
which is used by ioctl string sets, e.g.

const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
...
};

(the same is provided by NIC drivers with ethtool_ops::get_strings())
and simple format used for newly added string sets, e.g.

const char *const link_mode_names[] = {
...
};

If the bitset implementation is to be used by other APIs, legacy format
support would make little sense so I would suggest to drop legacy format
support from bitset code and build a (const char **) "index" for ethtool
legacy string arrays before passing it to bitset related functions.

Michal

2019-03-27 10:42:07

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

Wed, Mar 27, 2019 at 11:16:28AM CET, [email protected] wrote:
>> Wait, what do you mean be "kill the machine dead"?
>
>panic()

I see panic only in net/ipv4/tcp_metrics.c.


>
> Andrew

2019-03-27 10:44:20

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 08/22] ethtool: support for netlink notifications

Wed, Mar 27, 2019 at 10:59:50AM CET, [email protected] wrote:
>On Wed, Mar 27, 2019 at 10:38:43AM +0100, Jiri Pirko wrote:
>> Tue, Mar 26, 2019 at 07:17:20PM CET, [email protected] wrote:
>> >On Tue, Mar 26, 2019 at 05:34:00PM +0100, Jiri Pirko wrote:
>> >> Mon, Mar 25, 2019 at 06:08:18PM CET, [email protected] wrote:
>> >> >+void ethtool_notify(struct net_device *dev, struct netlink_ext_ack *extack,
>> >> >+ unsigned int cmd, u32 req_mask, const void *data)
>> >> >+{
>> >> >+ if (unlikely(!ethnl_ok))
>> >>
>> >> Why do you need this?
>> >
>> >If genetlink family registration fails, ethtool_notify() can be still
>> >called from other code (e.g. the ethtool ioctl interface). In such case,
>> >better bail out right away than fail somewhere later (probably after
>> >preparing the message which can't be sent anyway).
>>
>> Again, haven't seen this in any other gen netlink implementation. Why do
>> they not need it?
>
>Do they have notifications triggered from other code by directly calling
>a function (i.e. not through e.g. a netdev notifier)?

Okay, got it.


>
>An alternative to a flag would be using a RCU pointer for this function
>(initialized to null and set once the family is registered) which is
>what e.g. netfilter is doing with its hooks but that would mean that
>each external notification would use an indirect call.

It's slow path, I don't think this kind of overhead is of any issue.
Sounds good.


>
>> >> >diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
>> >> >index b8a6cd3dc3e3..5f2299548915 100644
>> >> >--- a/net/ethtool/netlink.h
>> >> >+++ b/net/ethtool/netlink.h
>> >> >@@ -11,6 +11,8 @@
>> >> > #define ETHNL_SET_ERRMSG(info, msg) \
>> >> > do { if (info) GENL_SET_ERR_MSG(info, msg); } while (0)
>> >> >
>> >> >+extern u32 ethnl_bcast_seq;
>> >>
>> >> Why do you need to have this in header? Second, it is not used by
>> >> anything. Please don't introduce variables that are not used. Introduce
>> >> them only in patch where you use it.
>> >
>> >It's the same as with ethtool_genl_family. I'll make it static as well
>> >until it's used in some other file.
>>
>> Not only static. You should remove it as you are not using it in this
>> patch at all.
>
>OK
>
>Michal

2019-03-27 13:06:07

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 09/22] ethtool: implement EVENT notifications

Mon, Mar 25, 2019 at 06:08:21PM CET, [email protected] wrote:
>Three types of netlink notifications are introduced:
>
> - ETHA_EVENT_NEWDEV to notify about newly registered network devices
> - ETHA_EVENT_DELDEV to notify about unregistered network devices
> - ETHA_EVENT_RENAMEDEV to notify about renamed network device
>
>The notifications are triggered by NETDEV_REGISTER, NETDEV_UNREGISTER and
>NETDEV_CHANGENAME notifiers.
>
>These notifications are intended for applications and daemons monitoring
>ethtool events to allow updating the list of existing devices without
>having to open another socket for rtnetlink.

Wait. You duplicate events that are already going out through RTNETLINK.
App should open RTNETLINK in order to get those. Other apps are doing
that too. I don't think that duplications like this are desirable :/

2019-03-27 13:10:23

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/22] ethtool netlink interface, part 1

Could you please perhaps split the patchset so it contains max ~15 patches?

2019-03-27 14:17:02

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 09/22] ethtool: implement EVENT notifications

On Wed, Mar 27, 2019 at 02:04:28PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:21PM CET, [email protected] wrote:
> >Three types of netlink notifications are introduced:
> >
> > - ETHA_EVENT_NEWDEV to notify about newly registered network devices
> > - ETHA_EVENT_DELDEV to notify about unregistered network devices
> > - ETHA_EVENT_RENAMEDEV to notify about renamed network device
> >
> >The notifications are triggered by NETDEV_REGISTER, NETDEV_UNREGISTER and
> >NETDEV_CHANGENAME notifiers.
> >
> >These notifications are intended for applications and daemons monitoring
> >ethtool events to allow updating the list of existing devices without
> >having to open another socket for rtnetlink.
>
> Wait. You duplicate events that are already going out through RTNETLINK.
> App should open RTNETLINK in order to get those. Other apps are doing
> that too. I don't think that duplications like this are desirable :/

Is there a way to filter or at least recognize these events when using
rtnetlink? I couldn't find any. The only way seems to be getting every
RTM_NEWLINK message (there can be quite a lot of those), always perform
the lookup in my device list and recognize what happened - only to
almost always find that nothing interesting. It is possible, sure, but
I would really like to avoid it.

Michal

2019-03-27 14:29:08

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/22] ethtool netlink interface, part 1

On Wed, Mar 27, 2019 at 02:09:28PM +0100, Jiri Pirko wrote:
> Could you please perhaps split the patchset so it contains max ~15 patches?

Would 1-17 still be acceptable? The thing is that patch 16 is the first
where some "normal" notification (i.e. not EVENT one) is sent and
patch 17 is the first implementation of a request modifying data.
I believe having at least one example of both querying and setting data
would help the review discussion.

Or perhaps I could leave GET_INFO related patches (at least second and
third) for later.

Michal

2019-03-27 15:21:00

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/22] ethtool netlink interface, part 1

Wed, Mar 27, 2019 at 03:28:06PM CET, [email protected] wrote:
>On Wed, Mar 27, 2019 at 02:09:28PM +0100, Jiri Pirko wrote:
>> Could you please perhaps split the patchset so it contains max ~15 patches?
>
>Would 1-17 still be acceptable? The thing is that patch 16 is the first
>where some "normal" notification (i.e. not EVENT one) is sent and
>patch 17 is the first implementation of a request modifying data.
>I believe having at least one example of both querying and setting data
>would help the review discussion.
>
>Or perhaps I could leave GET_INFO related patches (at least second and
>third) for later.

I think 17 is okay. Please split if possible.

>
>Michal

2019-03-27 16:37:16

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 10/22] ethtool: generic handlers for GET requests

Mon, Mar 25, 2019 at 06:08:24PM CET, [email protected] wrote:
>Some parts of processing GET type requests and related notifications are
>independent of a command. Provide universal functions so that only four
>callbacks need to be defined for each command type:
>
> parse_request() - parse incoming message
> prepare_data() - retrieve data from driver or NIC
> reply_size() - estimate reply message size
> fill_reply() - compose reply message
>
>These callback are defined in an instance of struct get_request_ops.
>
>Signed-off-by: Michal Kubecek <[email protected]>

[...]

>+/**
>+ * struct get_request_ops - unified handling of GET requests
>+ * @request_cmd: command id for request (GET)
>+ * @reply_cmd: command id for reply (SET)
>+ * @dev_attr: attribute type for device specification
>+ * @data_size: total length of data structure
>+ * @repdata_offset: offset of "reply data" part (struct common_reply_data)
>+ * @allow_nodev_do: do not fail if device is not specified for non-dump request
>+ * @parse_request:
>+ * parse request message and fill request info; request info is zero
>+ * initialized on entry except reply_data pointer (which is initialized)
>+ * @prepare_data:
>+ * retrieve data needed to compose a reply message; reply data are zero
>+ * initialized on entry except for @dev
>+ * @reply_size:
>+ * return size of reply message payload without device specification;
>+ * returned size may be bigger than actual reply size but it must suffice
>+ * to hold the reply
>+ * @fill_reply:
>+ * fill reply message payload using the data prepared by @prepare_data()
>+ * @cleanup
>+ * (optional) called when data are no longer needed; use e.g. to free
>+ * any additional data structures allocated in prepare_data() which are
>+ * not part of the main structure
>+ *
>+ * Description of variable parts of GET request handling when using the unified
>+ * infrastructure. When used, a pointer to an instance of this structure is to
>+ * be added to &get_requests array, generic handlers ethnl_get_doit(),
>+ * ethnl_get_dumpit(), ethnl_get_start() and ethnl_get_done() used in
>+ * @ethnl_genl_ops and (optionally) ethnl_std_notify() as notification handler
>+ * in &ethnl_notify_handlers.
>+ */
>+struct get_request_ops {

First of all, please maintain the ethnl prefix. Not only here, but also
for other structs and functions (common_req_info, parse_*, etc).

But I must ask, why do we need this wrappers of ops around ops?
I believe it would be much nicer to use genl ops directly and do the
common work in pre_doit and and post_doit. Please see devlink.c for
examples.

Wrappers are unfortunately quite common in this patchset. Most of the
time, they do things much harder to follow and understand :(


>+ u8 request_cmd;
>+ u8 reply_cmd;
>+ u16 dev_attrtype;
>+ unsigned int data_size;
>+ unsigned int repdata_offset;
>+ bool allow_nodev_do;
>+
>+ int (*parse_request)(struct common_req_info *req_info,
>+ struct sk_buff *skb, struct genl_info *info,
>+ const struct nlmsghdr *nlhdr);
>+ int (*prepare_data)(struct common_req_info *req_info,
>+ struct genl_info *info);
>+ int (*reply_size)(const struct common_req_info *req_info);
>+ int (*fill_reply)(struct sk_buff *skb,
>+ const struct common_req_info *req_info);
>+ void (*cleanup)(struct common_req_info *req_info);
>+};
>+
> #endif /* _NET_ETHTOOL_NETLINK_H */
>--
>2.21.0
>

2019-03-27 19:13:55

by David Miller

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/22] ethtool netlink interface, part 1

From: Michal Kubecek <[email protected]>
Date: Wed, 27 Mar 2019 15:28:06 +0100

> On Wed, Mar 27, 2019 at 02:09:28PM +0100, Jiri Pirko wrote:
>> Could you please perhaps split the patchset so it contains max ~15 patches?
>
> Would 1-17 still be acceptable?

I'm OK with ~17 especially considering the scope of this work.


2019-03-27 20:15:09

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

Mon, Mar 25, 2019 at 06:08:30PM CET, [email protected] wrote:
>Requests a contents of one or more string sets, i.e. indexed arrays of
>strings; this information is provided by ETHTOOL_GSSET_INFO and
>ETHTOOL_GSTRINGS commands of ioctl interface. There are three types of
>requests:
>
> - no NLM_F_DUMP, no device: get "global" stringsets
> - no NLM_F_DUMP, with device: get string sets related to the device
> - NLM_F_DUMP, no device: get device related string sets for all devices
>
>It's possible to request all string sets of given type or only specific
>sets. With ETHA_STRSET_COUNTS flag, only set sizes (number of strings) are
>returned.
>
>Signed-off-by: Michal Kubecek <[email protected]>
>---
> Documentation/networking/ethtool-netlink.txt | 46 +-
> include/uapi/linux/ethtool.h | 2 +
> include/uapi/linux/ethtool_netlink.h | 43 ++
> net/ethtool/Makefile | 2 +-
> net/ethtool/netlink.c | 8 +
> net/ethtool/netlink.h | 4 +
> net/ethtool/strset.c | 447 +++++++++++++++++++
> 7 files changed, 549 insertions(+), 3 deletions(-)
> create mode 100644 net/ethtool/strset.c

First of all, the code is hard to follow. For reasons I mentioned in
other replies (lack of prefixes, wrappers, etc).

More importantly, why do we need this? This concept of having strings in
kernel for various things and features and sending them to userspace is
weird. Certainly not common for Netlink interface. I believe these
strings should be avoided and all should be communicated to userspace
and back in form of well-defined Netlink attributes. We are introducing
new Netlink API, lets do it properly and don't bring baggage from past.

2019-03-27 20:15:09

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

Mon, Mar 25, 2019 at 06:08:33PM CET, [email protected] wrote:
>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 <[email protected]>
>---
> Documentation/networking/ethtool-netlink.txt | 43 ++++-
> include/uapi/linux/ethtool_netlink.h | 30 ++++
> net/ethtool/Makefile | 2 +-
> net/ethtool/common.c | 52 ++++++
> net/ethtool/common.h | 2 +
> net/ethtool/info.c | 158 +++++++++++++++++++
> net/ethtool/ioctl.c | 50 +-----
> net/ethtool/netlink.c | 8 +
> net/ethtool/netlink.h | 1 +
> 9 files changed, 299 insertions(+), 47 deletions(-)
> create mode 100644 net/ethtool/info.c
>
>diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
>index 1508c16a236e..cffa508ca6c6 100644
>--- a/Documentation/networking/ethtool-netlink.txt
>+++ b/Documentation/networking/ethtool-netlink.txt
>@@ -129,6 +129,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.
>@@ -209,6 +211,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

These are already very nicely supported in devlink. No need to duplicate
here.

2019-03-27 21:54:41

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 10/22] ethtool: generic handlers for GET requests

On Wed, Mar 27, 2019 at 05:35:07PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:24PM CET, [email protected] wrote:
> >Some parts of processing GET type requests and related notifications are
> >independent of a command. Provide universal functions so that only four
> >callbacks need to be defined for each command type:
> >
> > parse_request() - parse incoming message
> > prepare_data() - retrieve data from driver or NIC
> > reply_size() - estimate reply message size
> > fill_reply() - compose reply message
> >
> >These callback are defined in an instance of struct get_request_ops.
> >
> >Signed-off-by: Michal Kubecek <[email protected]>
>
> [...]
>
> >+/**
> >+ * struct get_request_ops - unified handling of GET requests
> >+ * @request_cmd: command id for request (GET)
> >+ * @reply_cmd: command id for reply (SET)
> >+ * @dev_attr: attribute type for device specification
> >+ * @data_size: total length of data structure
> >+ * @repdata_offset: offset of "reply data" part (struct common_reply_data)
> >+ * @allow_nodev_do: do not fail if device is not specified for non-dump request
> >+ * @parse_request:
> >+ * parse request message and fill request info; request info is zero
> >+ * initialized on entry except reply_data pointer (which is initialized)
> >+ * @prepare_data:
> >+ * retrieve data needed to compose a reply message; reply data are zero
> >+ * initialized on entry except for @dev
> >+ * @reply_size:
> >+ * return size of reply message payload without device specification;
> >+ * returned size may be bigger than actual reply size but it must suffice
> >+ * to hold the reply
> >+ * @fill_reply:
> >+ * fill reply message payload using the data prepared by @prepare_data()
> >+ * @cleanup
> >+ * (optional) called when data are no longer needed; use e.g. to free
> >+ * any additional data structures allocated in prepare_data() which are
> >+ * not part of the main structure
> >+ *
> >+ * Description of variable parts of GET request handling when using the unified
> >+ * infrastructure. When used, a pointer to an instance of this structure is to
> >+ * be added to &get_requests array, generic handlers ethnl_get_doit(),
> >+ * ethnl_get_dumpit(), ethnl_get_start() and ethnl_get_done() used in
> >+ * @ethnl_genl_ops and (optionally) ethnl_std_notify() as notification handler
> >+ * in &ethnl_notify_handlers.
> >+ */
> >+struct get_request_ops {
>
> First of all, please maintain the ethnl prefix. Not only here, but also
> for other structs and functions (common_req_info, parse_*, etc).
>
> But I must ask, why do we need this wrappers of ops around ops?
> I believe it would be much nicer to use genl ops directly and do the
> common work in pre_doit and and post_doit. Please see devlink.c for
> examples.
>
> Wrappers are unfortunately quite common in this patchset. Most of the
> time, they do things much harder to follow and understand :(

I'm a bit surprised by this position because so far my experience with
linux networking code seemed to suggest that using simple wrappers and
helpers is how things are supposed to be done. And while following a
chain of such wrappers (often each in a different file) in a code I was
reading for the first time could be frustrating at times, mostly I had
to admit that this style has its merits. After all, genetlink itself is
full of simple wrappers around netlink functions.

Let me point out one thing: most of these helpers and wrappers are not
artificial, they haven't been written in advance with an idea that they
might be useful (the patch series does not, of course, reflect the
development history); most of them were written when I realized I'm
writing the same or almost the same code again and again.

So when I caught myself writing

... = nla_nest_start(skb, ... | NLA_F_NESTED);

for the third or fourth time and I realized that every nla_nest_start()
call in the code will have this bitwise or, I felt it would deserve
a helper. (If I expected some objection, it was rather the optical
asymmetry of ethnl_nest_start() being closed with nla_nest_end().)
It would be much nicer to have it in nla_nest_start() but unfortunately
it's too late for that.

And it's exactly the same case with get_request_ops. For quite long
(until after RFC v2), this framework didn't exist and code for get
request processing (both doit and dumpit) and notifications was written
separately for each message type. Realizing that big part of each new
file is in fact an exact copy of the previous one with some string
replacements and that it's going to be like that for most of the future
files, that led me to identifying which parts are specific to message
type and which are generic.

If I have to get rid of get_request_ops, it will only result in having
multiple copies of functions which would replace ethnl_get_doit(),
ethnl_get_dumpit() and ethnl_std_notify(). They would be slightly
simpler but would look the same except for "info" in one being replaced
by "strset" in second, "settings" in third etc. Later, there would be
one more copy for stats, one for tunables etc.

I don't think the generic code can be handled just by pre_doit and
post_doit as the generic and message specific part are interleaved and
the generic parts are also different for do requests, dump requests and
notifications.

Michal

2019-03-27 22:27:50

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

On Wed, Mar 27, 2019 at 09:14:11PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:33PM CET, [email protected] wrote:
> >+
> >+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
>
> These are already very nicely supported in devlink. No need to duplicate
> here.

They are supported by devlink as an interface. But devlink itself is
only supported by few NIC drivers at the moment:

mike@unicorn:~/work/git/net-next> grep -r devlink_ops drivers/net/
drivers/net/ethernet/mellanox/mlx4/main.c:static const struct devlink_ops mlx4_devlink_ops = {
drivers/net/ethernet/mellanox/mlx4/main.c: devlink = devlink_alloc(&mlx4_devlink_ops, sizeof(*priv));
drivers/net/ethernet/mellanox/mlxsw/core.c:static const struct devlink_ops mlxsw_devlink_ops = {
drivers/net/ethernet/mellanox/mlxsw/core.c: devlink = devlink_alloc(&mlxsw_devlink_ops, alloc_size);
drivers/net/ethernet/mellanox/mlx5/core/main.c:static const struct devlink_ops mlx5_devlink_ops = {
drivers/net/ethernet/mellanox/mlx5/core/main.c: devlink = devlink_alloc(&mlx5_devlink_ops, sizeof(*dev));
drivers/net/ethernet/cavium/liquidio/lio_main.c:static const struct devlink_ops liquidio_devlink_ops = {
drivers/net/ethernet/cavium/liquidio/lio_main.c: devlink = devlink_alloc(&liquidio_devlink_ops,
drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c:static const struct devlink_ops bnxt_dl_ops = {
drivers/net/ethernet/netronome/nfp/nfp_main.c: devlink = devlink_alloc(&nfp_devlink_ops, sizeof(*pf));
drivers/net/ethernet/netronome/nfp/nfp_main.h:extern const struct devlink_ops nfp_devlink_ops;
drivers/net/ethernet/netronome/nfp/nfp_devlink.c:const struct devlink_ops nfp_devlink_ops = {
drivers/net/netdevsim/devlink.c:static const struct devlink_ops nsim_devlink_ops = {
drivers/net/netdevsim/devlink.c: devlink = devlink_alloc(&nsim_devlink_ops, 0);

That's 6 drivers from 4 vendors (if I don't count netdevsim). And I did
not check if all of them do actually provide the information shown
above. On the other hand:

mike@unicorn:~/work/git/net-next> egrep -r '\.get_drvinfo' drivers/net/ | wc -l
240

Some of these 240 lines assign the same handler but not enough to make
me optimistic about being able to implement "ethtool -i <dev>" using
devlink interface in near future (say few months or one year).

I'm all for implementing new features which are are related to physical
device (ASIC) rather than network interface only in devlink (at the
level of kernel-userspace interface). But for features already provided
by ethtool (userspace utility) I can't help seeing the state of devlink
support in NIC drivers as a serious blocker.

Michal

2019-03-27 22:57:09

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

On Wed, Mar 27, 2019 at 09:12:37PM +0100, Jiri Pirko wrote:
> Mon, Mar 25, 2019 at 06:08:30PM CET, [email protected] wrote:
> >Requests a contents of one or more string sets, i.e. indexed arrays of
> >strings; this information is provided by ETHTOOL_GSSET_INFO and
> >ETHTOOL_GSTRINGS commands of ioctl interface. There are three types of
> >requests:
> >
> > - no NLM_F_DUMP, no device: get "global" stringsets
> > - no NLM_F_DUMP, with device: get string sets related to the device
> > - NLM_F_DUMP, no device: get device related string sets for all devices
> >
> >It's possible to request all string sets of given type or only specific
> >sets. With ETHA_STRSET_COUNTS flag, only set sizes (number of strings) are
> >returned.
> >
> >Signed-off-by: Michal Kubecek <[email protected]>
> >---
> > Documentation/networking/ethtool-netlink.txt | 46 +-
> > include/uapi/linux/ethtool.h | 2 +
> > include/uapi/linux/ethtool_netlink.h | 43 ++
> > net/ethtool/Makefile | 2 +-
> > net/ethtool/netlink.c | 8 +
> > net/ethtool/netlink.h | 4 +
> > net/ethtool/strset.c | 447 +++++++++++++++++++
> > 7 files changed, 549 insertions(+), 3 deletions(-)
> > create mode 100644 net/ethtool/strset.c
>
> First of all, the code is hard to follow. For reasons I mentioned in
> other replies (lack of prefixes, wrappers, etc).
>
> More importantly, why do we need this? This concept of having strings in
> kernel for various things and features and sending them to userspace is
> weird. Certainly not common for Netlink interface. I believe these
> strings should be avoided and all should be communicated to userspace
> and back in form of well-defined Netlink attributes. We are introducing
> new Netlink API, lets do it properly and don't bring baggage from past.

For some of the global string sets where the values have fixed meaning
and new values are only appended to, it would be possible. But even for
those, keeping the list in sync between kernel and ethtool is often less
than perfect. And ethtool is only one of the tools using the interface.
So even in this case, I don't see string identifiers (or tags or names
or whatever we call them) as a baggage from past, rather as a solution
to a problem some of the existing interfaces have.

Then e.g. netdev features are not fixed in this sense: the same bit
index represents different feature in different kernels. I guess we
could introduce some fixed numeric identifiers for them and map between
those and actual indices but I don't see an advantage of such approach.

But the really important question is how would you handle what is
currently described by "per device" string sets, i.e. private flags,
(ethtool) statistics, tests, ...? For these, the list depends on the
driver or even device.

Michal

2019-03-28 01:57:52

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 02/22] netlink: introduce nla_put_bitfield32()



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Similar to other data types, this helper puts NLA_BITFIELD32 attribute into
> a netlink message. It takes separate value and selector arguments, if there
> is a struct nla_bitfield32 already, one can use nla_put().
>
> Signed-off-by: Michal Kubecek <[email protected]>

Reviewed-by: Florian Fainelli <[email protected]>
--
Florian

2019-03-28 01:58:22

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 03/22] netlink: add strict version of nla_parse_nested()



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Similar to nla_parse_strict() and nlmsg_parse_strict(), add also
> nla_parse_nested_strict() as a version of nla_parse_nested() with strict
> policy checking.
>
> Signed-off-by: Michal Kubecek <[email protected]>

Reviewed-by: Florian Fainelli <[email protected]>
--
Florian

2019-03-28 02:00:23

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 04/22] ethtool: move to its own directory



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> The ethtool netlink interface is going to be split into multiple files so
> that it will be more convenient to put all of them in a separate directory
> net/ethtool. Start by moving current ethtool.c with ioctl interface into
> this directory and renaming it to ioctl.c.
>
> Signed-off-by: Michal Kubecek <[email protected]>

Reviewed-by: Florian Fainelli <[email protected]>
--
Florian

2019-03-28 02:07:50

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface



On 3/27/2019 2:50 AM, Jiri Pirko wrote:
>
> Why don't you have ETHTOOL_MSG_SET_FOO for set? I think that for
> kerne->userspace the ETHTOOL_MSG_FOO if fine. I would change the
> ordering of words thought, but it is cosmetics:
> ETHTOOL_MSG_FOO /* kernel->userspace messages - replies, notifications */
> ETHTOOL_MSG_FOO_GET
> ETHTOOL_MSG_FOO_SET
> ETHTOOL_MSG_FOO_ACT
>
> What do you think?

We could even name the notification explicitly with: ETHTOOL_MSG_NOTIF
or ETHTOOL_MSG_NTF just so we spell out exactly what those messages are.
--
Florian

2019-03-28 02:15:41

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 09/22] ethtool: implement EVENT notifications



On 3/27/2019 7:14 AM, Michal Kubecek wrote:
> On Wed, Mar 27, 2019 at 02:04:28PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:21PM CET, [email protected] wrote:
>>> Three types of netlink notifications are introduced:
>>>
>>> - ETHA_EVENT_NEWDEV to notify about newly registered network devices
>>> - ETHA_EVENT_DELDEV to notify about unregistered network devices
>>> - ETHA_EVENT_RENAMEDEV to notify about renamed network device
>>>
>>> The notifications are triggered by NETDEV_REGISTER, NETDEV_UNREGISTER and
>>> NETDEV_CHANGENAME notifiers.
>>>
>>> These notifications are intended for applications and daemons monitoring
>>> ethtool events to allow updating the list of existing devices without
>>> having to open another socket for rtnetlink.
>>
>> Wait. You duplicate events that are already going out through RTNETLINK.
>> App should open RTNETLINK in order to get those. Other apps are doing
>> that too. I don't think that duplications like this are desirable :/
>
> Is there a way to filter or at least recognize these events when using
> rtnetlink? I couldn't find any. The only way seems to be getting every
> RTM_NEWLINK message (there can be quite a lot of those), always perform
> the lookup in my device list and recognize what happened - only to
> almost always find that nothing interesting. It is possible, sure, but
> I would really like to avoid it.

I am afraid you are right about this, would adding a filtering
capability specifically for this in rtnetlink be a better route?
--
Florian

2019-03-28 02:18:11

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 11/22] ethtool: move string arrays into common file



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Introduce file net/ethtool/common.c for code shared by ioctl and netlink
> ethtool interface. Move name tables of features, RSS hash functions,
> tunables and PHY tunables into this file.
>
> Signed-off-by: Michal Kubecek <[email protected]>

Reviewed-by: Florian Fainelli <[email protected]>
--
Florian

2019-03-28 02:26:45

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Requests a contents of one or more string sets, i.e. indexed arrays of
> strings; this information is provided by ETHTOOL_GSSET_INFO and
> ETHTOOL_GSTRINGS commands of ioctl interface. There are three types of
> requests:
>
> - no NLM_F_DUMP, no device: get "global" stringsets
> - no NLM_F_DUMP, with device: get string sets related to the device
> - NLM_F_DUMP, no device: get device related string sets for all devices
>
> It's possible to request all string sets of given type or only specific
> sets. With ETHA_STRSET_COUNTS flag, only set sizes (number of strings) are
> returned.
>
> Signed-off-by: Michal Kubecek <[email protected]>
> ---

[snip]

> diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
> index 5e5d785fe215..1508c16a236e 100644
> --- a/Documentation/networking/ethtool-netlink.txt
> +++ b/Documentation/networking/ethtool-netlink.txt
> @@ -127,6 +127,8 @@ List of message types
> ---------------------
>
> ETHNL_CMD_EVENT notification only
> + ETHNL_CMD_GET_STRSET
> + ETHNL_CMD_SET_STRSET response only
>
> All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
> to indicate the type.
> @@ -167,6 +169,46 @@ and also multiple events of the same type (e.g. two or more newly registered
> devices).
>
>
> +GET_STRSET
> +----------
> +
> +Requests contents of a string set as provided by ioctl commands
> +ETHTOOL_GSSET_INFO and ETHTOOL_GSTRINGS. String sets are not user writeable so
> +that the corresponding SET_STRSET message is only used in kernel replies.
> +There are two types of string sets: global (independent of a device, e.g.
> +device feature names) and device specific (e.g. device private flags).
> +
> +Request contents:
> +
> + ETHA_STRSET_DEV (nested) device identification
> + ETHA_STRSET_COUNTS (flag) request only string counts

Should not that be part of the nested attribute under
ETHA_STRSET_STRINGSET. We should probably think about adding another
flag which indicates that we want to get the stringset associated data,
see below why.

> + ETHA_STRSET_STRINGSET (nested) string set to request
> + ETHA_STRINGSET_ID (u32) set id
> +
> +Kernel response contents:
> +
> + ETHA_STRSET_DEV (nested) device identification
> + ETHA_STRSET_STRINGSET (nested) string set to request

string set requested?

> + ETHA_STRINGSET_ID (u32) set id
> + ETHA_STRINGSET_COUNT (u32) number of strings
> + ETHA_STRINGSET_STRINGS (nested) array of strings
> + ETHA_STRING_INDEX (u32) string index
> + ETHA_STRING_VALUE (string) string value

This is one of the areas where the legacy ethtool ioctl() is painful
because we need to request a string set to know how many of those exist
to allocate space for those in both kernel and user space.

If we could find a way to have a single command that allows us to dump
stringset (count, values) and associated data, then we save ourselves a
context switch and having to pre-allocate memory accordingly.
--
Florian

2019-03-28 02:31:13

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request



On 3/27/2019 3:25 PM, Michal Kubecek wrote:
> On Wed, Mar 27, 2019 at 09:14:11PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:33PM CET, [email protected] wrote:
>>> +
>>> +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
>>
>> These are already very nicely supported in devlink. No need to duplicate
>> here.
>
> They are supported by devlink as an interface. But devlink itself is
> only supported by few NIC drivers at the moment:
>
> mike@unicorn:~/work/git/net-next> grep -r devlink_ops drivers/net/

You might want to include net/ in your list too.

> drivers/net/ethernet/mellanox/mlx4/main.c:static const struct devlink_ops mlx4_devlink_ops = {
> drivers/net/ethernet/mellanox/mlx4/main.c: devlink = devlink_alloc(&mlx4_devlink_ops, sizeof(*priv));
> drivers/net/ethernet/mellanox/mlxsw/core.c:static const struct devlink_ops mlxsw_devlink_ops = {
> drivers/net/ethernet/mellanox/mlxsw/core.c: devlink = devlink_alloc(&mlxsw_devlink_ops, alloc_size);
> drivers/net/ethernet/mellanox/mlx5/core/main.c:static const struct devlink_ops mlx5_devlink_ops = {
> drivers/net/ethernet/mellanox/mlx5/core/main.c: devlink = devlink_alloc(&mlx5_devlink_ops, sizeof(*dev));
> drivers/net/ethernet/cavium/liquidio/lio_main.c:static const struct devlink_ops liquidio_devlink_ops = {
> drivers/net/ethernet/cavium/liquidio/lio_main.c: devlink = devlink_alloc(&liquidio_devlink_ops,
> drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c:static const struct devlink_ops bnxt_dl_ops = {
> drivers/net/ethernet/netronome/nfp/nfp_main.c: devlink = devlink_alloc(&nfp_devlink_ops, sizeof(*pf));
> drivers/net/ethernet/netronome/nfp/nfp_main.h:extern const struct devlink_ops nfp_devlink_ops;
> drivers/net/ethernet/netronome/nfp/nfp_devlink.c:const struct devlink_ops nfp_devlink_ops = {
> drivers/net/netdevsim/devlink.c:static const struct devlink_ops nsim_devlink_ops = {
> drivers/net/netdevsim/devlink.c: devlink = devlink_alloc(&nsim_devlink_ops, 0);
>
> That's 6 drivers from 4 vendors (if I don't count netdevsim). And I did
> not check if all of them do actually provide the information shown
> above. On the other hand:
>
> mike@unicorn:~/work/git/net-next> egrep -r '\.get_drvinfo' drivers/net/ | wc -l
> 240
>
> Some of these 240 lines assign the same handler but not enough to make
> me optimistic about being able to implement "ethtool -i <dev>" using
> devlink interface in near future (say few months or one year).
>
> I'm all for implementing new features which are are related to physical
> device (ASIC) rather than network interface only in devlink (at the
> level of kernel-userspace interface). But for features already provided
> by ethtool (userspace utility) I can't help seeing the state of devlink
> support in NIC drivers as a serious blocker.

Can't we just interrogate devlink first if there is a devlink <->
net_device mapping and fallback to ethtool_ops::get_drvinfo if that did
not work? Maybe issue a (rate-limited) warning "use devlink instead".
--
Florian

2019-03-28 03:37:57

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 14/22] ethtool: provide timestamping information in GET_INFO request



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Add timestamping information as provided by ETHTOOL_GET_TS_INFO ioctl
> command in GET_INFO reply if ETH_INFO_IM_TSINFO flag is set in the request.
>
> Add constants for counts of HWTSTAMP_TX_* and HWTSTAM_FILTER_* constants
> and provide symbolic names for timestamping related values so that they can
> be retrieved in GET_STRSET and GET_INFO requests.
>
> Signed-off-by: Michal Kubecek <[email protected]>
> ---

[snip]

> +int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
> +{
> + const struct ethtool_ops *ops = dev->ethtool_ops;
> + struct phy_device *phydev = dev->phydev;
> + int err = 0;
> +
> + memset(info, 0, sizeof(*info));
> + info->cmd = ETHTOOL_GET_TS_INFO;
> +
> + if (phydev && phydev->drv && phydev->drv->ts_info) {
> + err = phydev->drv->ts_info(phydev, info);
> + } else if (ops->get_ts_info) {
> + err = ops->get_ts_info(dev, info);
> + } else {
> + info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
> + SOF_TIMESTAMPING_SOFTWARE;
> + info->phc_index = -1;
> + }

We don't have to solve this just yet, but this is the type of logic that
we want to be able to change in the future, there are a number of things
in the current ethtool code where, say, we always prefer PHY time
stamping over MAC time stamping when there should be a choice if both
are supported.

I don't know if you maintain a TODO for after when this patch series get
merged, but we may want to start one :)
--
Florian

2019-03-28 03:43:22

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 19/22] ethtool: provide WoL information in GET_SETTINGS request



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Add information about supported and enabled wake on LAN modes into the
> GET_SETTINGS reply when ETH_SETTINGS_IM_WOL flag is set in the request.
>
> The GET_SETTINGS request can be still sent by unprivileged users but in
> such case the SecureOn password (if any) is not included in the reply.
>
> Send notification in the same format as reply SET_SETTINGS message when
> wake on LAN settings are modified using ioctl interface (ETHTOOL_SWOL
> command).

Looks good to me, just one question below:

>
> Signed-off-by: Michal Kubecek <[email protected]>
> ---

[snip]
>
> + data->privileged = ethnl_is_privileged(skb);

Is not this potentially creating a time of check/time of use issue? Can
you check for ethnl_is_privileged() at the time where you fill in the
reply skb?
--
Florian

2019-03-28 03:46:05

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 16/22] ethtool: provide link settings and link modes in GET_SETTINGS request



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Implement GET_SETTINGS netlink request to get link settings and link mode
> information provided by ETHTOOL_GLINKSETTINGS ioctl command.
>
> The information in SET_SETTINGS message sent as reply is divided into two
> parts: autonegotiation, speed, duplex and supported, advertised and peer
> advertised link modes when ETH_SETTINGS_IM_LINKMODES flag is set in the
> request and other settings when ETH_SETTINGS_IM_LINKINFO is set.
>
> Send notification in the same format as the reply message when relevant
> fields are modified using the ioctl interface (ETHTOOL_SLINKSETTINGS or
> ETHTOOL_SSET command).
>
> Signed-off-by: Michal Kubecek <[email protected]>
> ---

[snip]

> + ETHA_SETTINGS_DEV (nested) device identification
> + ETHA_SETTINGS_LINK_INFO (nested) link settings
> + ETHA_LINKINFO_PORT (u8) physical port
> + ETHA_LINKINFO_PHYADDR (u8) MDIO address of phy
> + ETHA_LINKINFO_TP_MDIX (u8) MDI(-X) status
> + ETHA_LINKINFO_TP_MDIX_CTRL (u8) MDI(-X) control
> + ETHA_LINKINFO_TRANSCEIVER (u8) transceiver
> + ETHA_SETTINGS_LINK_MODES (nested) link modes
> + ETHA_LINKMODES_AUTONEG (u8) autoneotiation status

Typo: auto-negotiation status.
--
Florian

2019-03-28 03:47:41

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 21/22] ethtool: provide message level in GET_SETTINGS request



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Add information about supported and enabled message levels to the
> GET_SETTINGS reply when ETH_SETTINGS_IM_DEBUG flag is set in the
> request.
>
> Unlike in the ioctl interface, "message level" is called "message mask" as
> it is in fact interpreted as a bit mask. It is put into a nested attribute
> ETHA_SETTINGS_DEBUG to allow future extensions (e.g. an actual verbosity
> level).
>
> Send notification in the same format as reply when message level is
> modified using the ioctl interface (ETHTOOL_SMSGLVL command).
>
> Signed-off-by: Michal Kubecek <[email protected]>

Reviewed-by: Florian Fainelli <[email protected]>
--
Florian

2019-03-28 03:49:31

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 22/22] ethtool: set message level with SET_SETTINGS request



On 3/25/2019 10:09 AM, Michal Kubecek wrote:
> Allow setting device message level using ETHA_SETTINGS_DEBUG nested
> attribute.
>
> Unlike in ioctl interface "message level" is called "message mask" (as it
> is in fact used as a bit mask) and put inside a nested attribute to allow
> future extensions.
>
> Signed-off-by: Michal Kubecek <[email protected]>

Reviewed-by: Florian Fainelli <[email protected]>
--
Florian

2019-03-28 03:50:27

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 20/22] ethtool: set WoL settings with SET_SETTINGS request



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Allow enabling and disabling wake on LAN modes using SET_SETTINGS
> request.
>
> ETHA_SETTINGS_WOL nested attribute is used to set or modify enabled WoL
> modes and SecureOn(tm) password.
>
> Signed-off-by: Michal Kubecek <[email protected]>

Reviewed-by: Florian Fainelli <[email protected]>
--
Florian

2019-03-28 03:53:00

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 15/22] ethtool: provide link mode names as a string set



On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> Add table of ethernet link mode names and make it available as a string set
> to userspace GET_STRSET requests.
>
> Signed-off-by: Michal Kubecek <[email protected]>

Reviewed-by: Florian Fainelli <[email protected]>
--
Florian

2019-03-28 06:42:57

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 09/22] ethtool: implement EVENT notifications

On Wed, Mar 27, 2019 at 07:14:43PM -0700, Florian Fainelli wrote:
>
>
> On 3/27/2019 7:14 AM, Michal Kubecek wrote:
> > On Wed, Mar 27, 2019 at 02:04:28PM +0100, Jiri Pirko wrote:
> >> Mon, Mar 25, 2019 at 06:08:21PM CET, [email protected] wrote:
> >>> Three types of netlink notifications are introduced:
> >>>
> >>> - ETHA_EVENT_NEWDEV to notify about newly registered network devices
> >>> - ETHA_EVENT_DELDEV to notify about unregistered network devices
> >>> - ETHA_EVENT_RENAMEDEV to notify about renamed network device
> >>>
> >>> The notifications are triggered by NETDEV_REGISTER, NETDEV_UNREGISTER and
> >>> NETDEV_CHANGENAME notifiers.
> >>>
> >>> These notifications are intended for applications and daemons monitoring
> >>> ethtool events to allow updating the list of existing devices without
> >>> having to open another socket for rtnetlink.
> >>
> >> Wait. You duplicate events that are already going out through RTNETLINK.
> >> App should open RTNETLINK in order to get those. Other apps are doing
> >> that too. I don't think that duplications like this are desirable :/
> >
> > Is there a way to filter or at least recognize these events when using
> > rtnetlink? I couldn't find any. The only way seems to be getting every
> > RTM_NEWLINK message (there can be quite a lot of those), always perform
> > the lookup in my device list and recognize what happened - only to
> > almost always find that nothing interesting. It is possible, sure, but
> > I would really like to avoid it.
>
> I am afraid you are right about this, would adding a filtering
> capability specifically for this in rtnetlink be a better route?

Maybe we could add new IFLA_EVENT_* values and use IFLA_EVENT to mark
RTM_NEWLINK messages announcing "new device" and "device rename". That
way, monitoring application would still need to parse all RTM_NEWLINK
messages but it would be able to recognize which announce a change in
device list without a lookup in its structures.

Michal

2019-03-28 07:19:50

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

On Wed, Mar 27, 2019 at 07:25:47PM -0700, Florian Fainelli wrote:
> > +GET_STRSET
> > +----------
> > +
> > +Requests contents of a string set as provided by ioctl commands
> > +ETHTOOL_GSSET_INFO and ETHTOOL_GSTRINGS. String sets are not user writeable so
> > +that the corresponding SET_STRSET message is only used in kernel replies.
> > +There are two types of string sets: global (independent of a device, e.g.
> > +device feature names) and device specific (e.g. device private flags).
> > +
> > +Request contents:
> > +
> > + ETHA_STRSET_DEV (nested) device identification
> > + ETHA_STRSET_COUNTS (flag) request only string counts
>
> Should not that be part of the nested attribute under
> ETHA_STRSET_STRINGSET. We should probably think about adding another
> flag which indicates that we want to get the stringset associated data,
> see below why.
>
> > + ETHA_STRSET_STRINGSET (nested) string set to request
> > + ETHA_STRINGSET_ID (u32) set id
> > +
> > +Kernel response contents:
> > +
> > + ETHA_STRSET_DEV (nested) device identification
> > + ETHA_STRSET_STRINGSET (nested) string set to request
>
> string set requested?
>
> > + ETHA_STRINGSET_ID (u32) set id
> > + ETHA_STRINGSET_COUNT (u32) number of strings
> > + ETHA_STRINGSET_STRINGS (nested) array of strings
> > + ETHA_STRING_INDEX (u32) string index
> > + ETHA_STRING_VALUE (string) string value
>
> This is one of the areas where the legacy ethtool ioctl() is painful
> because we need to request a string set to know how many of those exist
> to allocate space for those in both kernel and user space.
>
> If we could find a way to have a single command that allows us to dump
> stringset (count, values) and associated data, then we save ourselves a
> context switch and having to pre-allocate memory accordingly.

The way the proposed interface is designed, we can get both without an
extra roundtrip but it's done the other way around, i.e. passing the
strings with the request for data. The API allows two modes of
operation:

1. One shot requests, e.g. typical ethtool use. We use "verbose" mode
(no ETHA_*_COMPACT flag in the request) and data is provided together
with the names. E.g. "ethtool eth2" request would look like

ETHA_SETTINGS_DEV
ETHA_DEV_NAME = "eth2"
ETHA_SETTINGS_INFO_MASK = ... | ETH_SETTINGS_IM_LINKMODES | ...

and the reply would be

ETHA_SETTINGS_DEV = {
ETHA_DEV_INDEX = 4
ETHA_DEV_NAME = "eth2"
}
ETHA_SETTINGS_LINK_INFO = {
...
}
ETHA_SETTRINGS_LINK_MODES = {
ETHA_LINKMODES_AUTONEG = 1 (AUTONEG_ENABLE)
ETHA_LINKMODES_OURS = {
ETHA_BITSET_SIZE = 67
ETHA_BITSET_BITS = {
ETHA_BITS_BIT = {
ETHA_BIT_INDEX = 0 (ETHTOOL_LINK_MODE_10baseT_Half_BIT)
ETHA_BIT_NAME = "10baseT/Half"
}
ETHA_BITS_BIT = {
ETHA_BIT_INDEX = 1 (ETHTOOL_LINK_MODE_10baseT_Full_BIT)
ETHA_BIT_NAME = "10baseT/Full"
}
ETHA_BITS_BIT = {
ETHA_BIT_INDEX = 2 (ETHTOOL_LINK_MODE_100baseT_Half_BIT)
ETHA_BIT_NAME = "100baseT/Half"
ETHA_BIT_VALUE
}
ETHA_BITS_BIT = {
ETHA_BIT_INDEX = 3 (ETHTOOL_LINK_MODE_100baseT_Full_BIT)
ETHA_BIT_NAME = "100baseT/Full"
ETHA_BIT_VALUE
}
ETHA_BITS_BIT = {
ETHA_BIT_INDEX = 5 (ETHTOOL_LINK_MODE_1000baseT_Full_BIT)
ETHA_BIT_NAME = "1000baseT/Full"
ETHA_BIT_VALUE
}
}
}
ETHA_LINKMODES_PEER = {
...
}
ETHA_LINKMODES_SPEED = 1000 (SPEED_1000)
ETHA_LINKMODES_DUPLEX = 1 (DUPLEX_FULL)
}
...

2. Long time running applications, e.g. "ethtool --monitor", wicked,
systemd-networkd, ... For these, it would make sense to cache the
strings and either retrieve them in advance (on start and when new
device appears) or when they are needed for the first time. The request
would then include the ETHA_SETTINGS_COMPACT flag and reply would be

...
ETHA_LINKMODES_OURS = {
ETHA_BITSET_SIZE = 67
ETHA_BITSET_VALUE = 0x0000002c 0x00000000 0x00000000
ETHA_BITSET_MASK = 0x0000002f 0x00000000 0x00000000
}

For "set" type requests, it's up to the application if it uses verbose
or compact form. The verbose form is most useful when we want e.g. to
set the values of one or two bits which may be identified by their names
(on command line or in config file).

Michal

2019-03-28 08:11:05

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Thu, Mar 28, 2019 at 03:05:14AM CET, [email protected] wrote:
>
>
>On 3/27/2019 2:50 AM, Jiri Pirko wrote:
>>
>> Why don't you have ETHTOOL_MSG_SET_FOO for set? I think that for
>> kerne->userspace the ETHTOOL_MSG_FOO if fine. I would change the
>> ordering of words thought, but it is cosmetics:
>> ETHTOOL_MSG_FOO /* kernel->userspace messages - replies, notifications */
>> ETHTOOL_MSG_FOO_GET
>> ETHTOOL_MSG_FOO_SET
>> ETHTOOL_MSG_FOO_ACT
>>
>> What do you think?
>
>We could even name the notification explicitly with: ETHTOOL_MSG_NOTIF
>or ETHTOOL_MSG_NTF just so we spell out exactly what those messages are.

Sound good. Something like:

ETHTOOL_MSG_FOO_GET
ETHTOOL_MSG_FOO_GET_RPLY /* kernel->userspace replies to get */
ETHTOOL_MSG_FOO_SET
ETHTOOL_MSG_FOO_ACT
ETHTOOL_MSG_FOO_NTF /* kernel->userspace async messages - notifications */

2019-03-28 09:17:36

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 09/22] ethtool: implement EVENT notifications

Thu, Mar 28, 2019 at 07:41:43AM CET, [email protected] wrote:
>On Wed, Mar 27, 2019 at 07:14:43PM -0700, Florian Fainelli wrote:
>>
>>
>> On 3/27/2019 7:14 AM, Michal Kubecek wrote:
>> > On Wed, Mar 27, 2019 at 02:04:28PM +0100, Jiri Pirko wrote:
>> >> Mon, Mar 25, 2019 at 06:08:21PM CET, [email protected] wrote:
>> >>> Three types of netlink notifications are introduced:
>> >>>
>> >>> - ETHA_EVENT_NEWDEV to notify about newly registered network devices
>> >>> - ETHA_EVENT_DELDEV to notify about unregistered network devices
>> >>> - ETHA_EVENT_RENAMEDEV to notify about renamed network device
>> >>>
>> >>> The notifications are triggered by NETDEV_REGISTER, NETDEV_UNREGISTER and
>> >>> NETDEV_CHANGENAME notifiers.
>> >>>
>> >>> These notifications are intended for applications and daemons monitoring
>> >>> ethtool events to allow updating the list of existing devices without
>> >>> having to open another socket for rtnetlink.
>> >>
>> >> Wait. You duplicate events that are already going out through RTNETLINK.
>> >> App should open RTNETLINK in order to get those. Other apps are doing
>> >> that too. I don't think that duplications like this are desirable :/
>> >
>> > Is there a way to filter or at least recognize these events when using
>> > rtnetlink? I couldn't find any. The only way seems to be getting every
>> > RTM_NEWLINK message (there can be quite a lot of those), always perform
>> > the lookup in my device list and recognize what happened - only to
>> > almost always find that nothing interesting. It is possible, sure, but
>> > I would really like to avoid it.
>>
>> I am afraid you are right about this, would adding a filtering
>> capability specifically for this in rtnetlink be a better route?
>
>Maybe we could add new IFLA_EVENT_* values and use IFLA_EVENT to mark
>RTM_NEWLINK messages announcing "new device" and "device rename". That
>way, monitoring application would still need to parse all RTM_NEWLINK
>messages but it would be able to recognize which announce a change in
>device list without a lookup in its structures.

Hmm, that sounds good.

>
>Michal

2019-03-28 09:22:22

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

Wed, Mar 27, 2019 at 11:25:54PM CET, [email protected] wrote:
>On Wed, Mar 27, 2019 at 09:14:11PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:33PM CET, [email protected] wrote:
>> >+
>> >+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
>>
>> These are already very nicely supported in devlink. No need to duplicate
>> here.
>
>They are supported by devlink as an interface. But devlink itself is
>only supported by few NIC drivers at the moment:
>
>mike@unicorn:~/work/git/net-next> grep -r devlink_ops drivers/net/
>drivers/net/ethernet/mellanox/mlx4/main.c:static const struct devlink_ops mlx4_devlink_ops = {
>drivers/net/ethernet/mellanox/mlx4/main.c: devlink = devlink_alloc(&mlx4_devlink_ops, sizeof(*priv));
>drivers/net/ethernet/mellanox/mlxsw/core.c:static const struct devlink_ops mlxsw_devlink_ops = {
>drivers/net/ethernet/mellanox/mlxsw/core.c: devlink = devlink_alloc(&mlxsw_devlink_ops, alloc_size);
>drivers/net/ethernet/mellanox/mlx5/core/main.c:static const struct devlink_ops mlx5_devlink_ops = {
>drivers/net/ethernet/mellanox/mlx5/core/main.c: devlink = devlink_alloc(&mlx5_devlink_ops, sizeof(*dev));
>drivers/net/ethernet/cavium/liquidio/lio_main.c:static const struct devlink_ops liquidio_devlink_ops = {
>drivers/net/ethernet/cavium/liquidio/lio_main.c: devlink = devlink_alloc(&liquidio_devlink_ops,
>drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c:static const struct devlink_ops bnxt_dl_ops = {
>drivers/net/ethernet/netronome/nfp/nfp_main.c: devlink = devlink_alloc(&nfp_devlink_ops, sizeof(*pf));
>drivers/net/ethernet/netronome/nfp/nfp_main.h:extern const struct devlink_ops nfp_devlink_ops;
>drivers/net/ethernet/netronome/nfp/nfp_devlink.c:const struct devlink_ops nfp_devlink_ops = {
>drivers/net/netdevsim/devlink.c:static const struct devlink_ops nsim_devlink_ops = {
>drivers/net/netdevsim/devlink.c: devlink = devlink_alloc(&nsim_devlink_ops, 0);
>
>That's 6 drivers from 4 vendors (if I don't count netdevsim). And I did
>not check if all of them do actually provide the information shown
>above. On the other hand:
>
>mike@unicorn:~/work/git/net-next> egrep -r '\.get_drvinfo' drivers/net/ | wc -l
>240
>
>Some of these 240 lines assign the same handler but not enough to make
>me optimistic about being able to implement "ethtool -i <dev>" using
>devlink interface in near future (say few months or one year).
>
>I'm all for implementing new features which are are related to physical
>device (ASIC) rather than network interface only in devlink (at the
>level of kernel-userspace interface). But for features already provided
>by ethtool (userspace utility) I can't help seeing the state of devlink
>support in NIC drivers as a serious blocker.

What I'm thinking about at for some time now would be en explicit
default devlink and devlink_port registration for every netdev for
drivers that does not support devlink themselves. I need to think about
it more, but it seems doable. Then we can hang appropriate things there
and make the ethtoolnl/devlink separation clear. I believe we need to do
it.


>
>Michal

2019-03-28 09:38:42

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

On Thu, Mar 28, 2019 at 09:10:10AM +0100, Jiri Pirko wrote:
> Thu, Mar 28, 2019 at 03:05:14AM CET, [email protected] wrote:
> >
> >
> >On 3/27/2019 2:50 AM, Jiri Pirko wrote:
> >>
> >> Why don't you have ETHTOOL_MSG_SET_FOO for set? I think that for
> >> kerne->userspace the ETHTOOL_MSG_FOO if fine. I would change the
> >> ordering of words thought, but it is cosmetics:
> >> ETHTOOL_MSG_FOO /* kernel->userspace messages - replies, notifications */
> >> ETHTOOL_MSG_FOO_GET
> >> ETHTOOL_MSG_FOO_SET
> >> ETHTOOL_MSG_FOO_ACT
> >>
> >> What do you think?
> >
> >We could even name the notification explicitly with: ETHTOOL_MSG_NOTIF
> >or ETHTOOL_MSG_NTF just so we spell out exactly what those messages are.
>
> Sound good. Something like:
>
> ETHTOOL_MSG_FOO_GET
> ETHTOOL_MSG_FOO_GET_RPLY /* kernel->userspace replies to get */
> ETHTOOL_MSG_FOO_SET
> ETHTOOL_MSG_FOO_ACT
> ETHTOOL_MSG_FOO_NTF /* kernel->userspace async messages - notifications */

The names sound fine to me and having different message ids would still
allow processing messages by the same handler easily.

But there is one potential issue I would like to point out: this way we
spend 4 message ids for a get/set pair rather than 2. These message ids
(genlmsghdr::cmd) are u8, i.e. the resource is not as infinite as one
would wish. There are 80 ioctl commands (43 "get" and 29 "set") at the
moment.

Netlink API should be less greedy in general. I already combined some
ioctl commands into one netlink request type and with an easy way to add
new attributes to existing commands, we won't need to add new commands
as often (certainly not in a way which left us with 9 "get" and 9 "set"
ioctl commands for netdev features). So even with 4 ids per get/set
pair, we might be safe for reasonably long time. But it's still
something to keep in mind.

Michal

2019-03-28 09:56:06

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

On Thu, Mar 28, 2019 at 10:21:26AM +0100, Jiri Pirko wrote:
> Wed, Mar 27, 2019 at 11:25:54PM CET, [email protected] wrote:
> >
> >I'm all for implementing new features which are are related to physical
> >device (ASIC) rather than network interface only in devlink (at the
> >level of kernel-userspace interface). But for features already provided
> >by ethtool (userspace utility) I can't help seeing the state of devlink
> >support in NIC drivers as a serious blocker.
>
> What I'm thinking about at for some time now would be en explicit
> default devlink and devlink_port registration for every netdev for
> drivers that does not support devlink themselves. I need to think about
> it more, but it seems doable. Then we can hang appropriate things there
> and make the ethtoolnl/devlink separation clear. I believe we need to do
> it.

That sounds great, such "generic devlink" implementation would be a way
around. Kernel could then emulate features which are not implemented by
an actual devlink handler (i.e. "generic devlink" is used or particular
handler is missing) by falling back to ethtool_ops handler so that
userspace could rely on devlink API for things like device information,
various dumps, flashing etc. without losing anything.

Michal

2019-03-28 10:04:44

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 14/22] ethtool: provide timestamping information in GET_INFO request

On Wed, Mar 27, 2019 at 08:36:53PM -0700, Florian Fainelli wrote:
>
>
> On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> > Add timestamping information as provided by ETHTOOL_GET_TS_INFO ioctl
> > command in GET_INFO reply if ETH_INFO_IM_TSINFO flag is set in the request.
> >
> > Add constants for counts of HWTSTAMP_TX_* and HWTSTAM_FILTER_* constants
> > and provide symbolic names for timestamping related values so that they can
> > be retrieved in GET_STRSET and GET_INFO requests.
> >
> > Signed-off-by: Michal Kubecek <[email protected]>
> > ---
>
> [snip]
>
> > +int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
> > +{
> > + const struct ethtool_ops *ops = dev->ethtool_ops;
> > + struct phy_device *phydev = dev->phydev;
> > + int err = 0;
> > +
> > + memset(info, 0, sizeof(*info));
> > + info->cmd = ETHTOOL_GET_TS_INFO;
> > +
> > + if (phydev && phydev->drv && phydev->drv->ts_info) {
> > + err = phydev->drv->ts_info(phydev, info);
> > + } else if (ops->get_ts_info) {
> > + err = ops->get_ts_info(dev, info);
> > + } else {
> > + info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
> > + SOF_TIMESTAMPING_SOFTWARE;
> > + info->phc_index = -1;
> > + }
>
> We don't have to solve this just yet, but this is the type of logic that
> we want to be able to change in the future, there are a number of things
> in the current ethtool code where, say, we always prefer PHY time
> stamping over MAC time stamping when there should be a choice if both
> are supported.

For the record, this is just a code block carved out of existing
ethtool_ts_info() where the only difference is that info is a pointer
rather than a structure itself.

> I don't know if you maintain a TODO for after when this patch series get
> merged, but we may want to start one :)

I have some informal TODO locally; some ideas are in the cover letter,
I also mentioned some in my talk last week. Perhaps it is time to make
it a publicly visible document and start collecting ideas.

Michal

2019-03-28 10:06:34

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 16/22] ethtool: provide link settings and link modes in GET_SETTINGS request

On Wed, Mar 27, 2019 at 08:44:58PM -0700, Florian Fainelli wrote:
>
>
> On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> > Implement GET_SETTINGS netlink request to get link settings and link mode
> > information provided by ETHTOOL_GLINKSETTINGS ioctl command.
> >
> > The information in SET_SETTINGS message sent as reply is divided into two
> > parts: autonegotiation, speed, duplex and supported, advertised and peer
> > advertised link modes when ETH_SETTINGS_IM_LINKMODES flag is set in the
> > request and other settings when ETH_SETTINGS_IM_LINKINFO is set.
> >
> > Send notification in the same format as the reply message when relevant
> > fields are modified using the ioctl interface (ETHTOOL_SLINKSETTINGS or
> > ETHTOOL_SSET command).
> >
> > Signed-off-by: Michal Kubecek <[email protected]>
> > ---
>
> [snip]
>
> > + ETHA_SETTINGS_DEV (nested) device identification
> > + ETHA_SETTINGS_LINK_INFO (nested) link settings
> > + ETHA_LINKINFO_PORT (u8) physical port
> > + ETHA_LINKINFO_PHYADDR (u8) MDIO address of phy
> > + ETHA_LINKINFO_TP_MDIX (u8) MDI(-X) status
> > + ETHA_LINKINFO_TP_MDIX_CTRL (u8) MDI(-X) control
> > + ETHA_LINKINFO_TRANSCEIVER (u8) transceiver
> > + ETHA_SETTINGS_LINK_MODES (nested) link modes
> > + ETHA_LINKMODES_AUTONEG (u8) autoneotiation status
>
> Typo: auto-negotiation status.

Will fix, thanks.

Michal

2019-03-28 10:13:49

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 19/22] ethtool: provide WoL information in GET_SETTINGS request

On Wed, Mar 27, 2019 at 08:42:26PM -0700, Florian Fainelli wrote:
> On 3/25/2019 10:08 AM, Michal Kubecek wrote:
> >
> > + data->privileged = ethnl_is_privileged(skb);
>
> Is not this potentially creating a time of check/time of use issue? Can
> you check for ethnl_is_privileged() at the time where you fill in the
> reply skb?

I'm not sure what kind of race you have in mind but if this is a
problem, we might have even bigger one for any "SET" request where the
privilege check is performed even earlier by the genetlink dispatcher
(by setting GENL_UNS_ADMIN_PERM or GENL_ADMIN_PERM flag in genl_ops).

Michal

2019-03-28 13:24:13

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Thu, Mar 28, 2019 at 10:37:46AM CET, [email protected] wrote:
>On Thu, Mar 28, 2019 at 09:10:10AM +0100, Jiri Pirko wrote:
>> Thu, Mar 28, 2019 at 03:05:14AM CET, [email protected] wrote:
>> >
>> >
>> >On 3/27/2019 2:50 AM, Jiri Pirko wrote:
>> >>
>> >> Why don't you have ETHTOOL_MSG_SET_FOO for set? I think that for
>> >> kerne->userspace the ETHTOOL_MSG_FOO if fine. I would change the
>> >> ordering of words thought, but it is cosmetics:
>> >> ETHTOOL_MSG_FOO /* kernel->userspace messages - replies, notifications */
>> >> ETHTOOL_MSG_FOO_GET
>> >> ETHTOOL_MSG_FOO_SET
>> >> ETHTOOL_MSG_FOO_ACT
>> >>
>> >> What do you think?
>> >
>> >We could even name the notification explicitly with: ETHTOOL_MSG_NOTIF
>> >or ETHTOOL_MSG_NTF just so we spell out exactly what those messages are.
>>
>> Sound good. Something like:
>>
>> ETHTOOL_MSG_FOO_GET
>> ETHTOOL_MSG_FOO_GET_RPLY /* kernel->userspace replies to get */
>> ETHTOOL_MSG_FOO_SET
>> ETHTOOL_MSG_FOO_ACT
>> ETHTOOL_MSG_FOO_NTF /* kernel->userspace async messages - notifications */
>
>The names sound fine to me and having different message ids would still
>allow processing messages by the same handler easily.
>
>But there is one potential issue I would like to point out: this way we
>spend 4 message ids for a get/set pair rather than 2. These message ids
>(genlmsghdr::cmd) are u8, i.e. the resource is not as infinite as one
>would wish. There are 80 ioctl commands (43 "get" and 29 "set") at the
>moment.
>
>Netlink API should be less greedy in general. I already combined some
>ioctl commands into one netlink request type and with an easy way to add
>new attributes to existing commands, we won't need to add new commands
>as often (certainly not in a way which left us with 9 "get" and 9 "set"
>ioctl commands for netdev features). So even with 4 ids per get/set
>pair, we might be safe for reasonably long time. But it's still
>something to keep in mind.

There are still 16 bits reserve in genl msg header:
struct genlmsghdr {
__u8 cmd;
__u8 version;
__u16 reserved;
};


2019-03-28 13:34:51

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 10/22] ethtool: generic handlers for GET requests

Wed, Mar 27, 2019 at 10:53:42PM CET, [email protected] wrote:
>On Wed, Mar 27, 2019 at 05:35:07PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:24PM CET, [email protected] wrote:
>> >Some parts of processing GET type requests and related notifications are
>> >independent of a command. Provide universal functions so that only four
>> >callbacks need to be defined for each command type:
>> >
>> > parse_request() - parse incoming message
>> > prepare_data() - retrieve data from driver or NIC
>> > reply_size() - estimate reply message size
>> > fill_reply() - compose reply message
>> >
>> >These callback are defined in an instance of struct get_request_ops.
>> >
>> >Signed-off-by: Michal Kubecek <[email protected]>
>>
>> [...]
>>
>> >+/**
>> >+ * struct get_request_ops - unified handling of GET requests
>> >+ * @request_cmd: command id for request (GET)
>> >+ * @reply_cmd: command id for reply (SET)
>> >+ * @dev_attr: attribute type for device specification
>> >+ * @data_size: total length of data structure
>> >+ * @repdata_offset: offset of "reply data" part (struct common_reply_data)
>> >+ * @allow_nodev_do: do not fail if device is not specified for non-dump request
>> >+ * @parse_request:
>> >+ * parse request message and fill request info; request info is zero
>> >+ * initialized on entry except reply_data pointer (which is initialized)
>> >+ * @prepare_data:
>> >+ * retrieve data needed to compose a reply message; reply data are zero
>> >+ * initialized on entry except for @dev
>> >+ * @reply_size:
>> >+ * return size of reply message payload without device specification;
>> >+ * returned size may be bigger than actual reply size but it must suffice
>> >+ * to hold the reply
>> >+ * @fill_reply:
>> >+ * fill reply message payload using the data prepared by @prepare_data()
>> >+ * @cleanup
>> >+ * (optional) called when data are no longer needed; use e.g. to free
>> >+ * any additional data structures allocated in prepare_data() which are
>> >+ * not part of the main structure
>> >+ *
>> >+ * Description of variable parts of GET request handling when using the unified
>> >+ * infrastructure. When used, a pointer to an instance of this structure is to
>> >+ * be added to &get_requests array, generic handlers ethnl_get_doit(),
>> >+ * ethnl_get_dumpit(), ethnl_get_start() and ethnl_get_done() used in
>> >+ * @ethnl_genl_ops and (optionally) ethnl_std_notify() as notification handler
>> >+ * in &ethnl_notify_handlers.
>> >+ */
>> >+struct get_request_ops {
>>
>> First of all, please maintain the ethnl prefix. Not only here, but also
>> for other structs and functions (common_req_info, parse_*, etc).
>>
>> But I must ask, why do we need this wrappers of ops around ops?
>> I believe it would be much nicer to use genl ops directly and do the
>> common work in pre_doit and and post_doit. Please see devlink.c for
>> examples.
>>
>> Wrappers are unfortunately quite common in this patchset. Most of the
>> time, they do things much harder to follow and understand :(
>
>I'm a bit surprised by this position because so far my experience with
>linux networking code seemed to suggest that using simple wrappers and
>helpers is how things are supposed to be done. And while following a
>chain of such wrappers (often each in a different file) in a code I was
>reading for the first time could be frustrating at times, mostly I had
>to admit that this style has its merits. After all, genetlink itself is
>full of simple wrappers around netlink functions.
>
>Let me point out one thing: most of these helpers and wrappers are not
>artificial, they haven't been written in advance with an idea that they
>might be useful (the patch series does not, of course, reflect the
>development history); most of them were written when I realized I'm
>writing the same or almost the same code again and again.
>
>So when I caught myself writing
>
> ... = nla_nest_start(skb, ... | NLA_F_NESTED);
>
>for the third or fourth time and I realized that every nla_nest_start()
>call in the code will have this bitwise or, I felt it would deserve
>a helper. (If I expected some objection, it was rather the optical
>asymmetry of ethnl_nest_start() being closed with nla_nest_end().)
>It would be much nicer to have it in nla_nest_start() but unfortunately
>it's too late for that.

Okay, this wrapper I can understand, but it certainly isn't ethnl
specific wrapper. It is generic, many others might use it:
$ git grep NLA_F_NESTED |grep nla_nest_start


>
>And it's exactly the same case with get_request_ops. For quite long
>(until after RFC v2), this framework didn't exist and code for get
>request processing (both doit and dumpit) and notifications was written
>separately for each message type. Realizing that big part of each new
>file is in fact an exact copy of the previous one with some string
>replacements and that it's going to be like that for most of the future
>files, that led me to identifying which parts are specific to message
>type and which are generic.
>
>If I have to get rid of get_request_ops, it will only result in having
>multiple copies of functions which would replace ethnl_get_doit(),
>ethnl_get_dumpit() and ethnl_std_notify(). They would be slightly
>simpler but would look the same except for "info" in one being replaced
>by "strset" in second, "settings" in third etc. Later, there would be
>one more copy for stats, one for tunables etc.
>
>I don't think the generic code can be handled just by pre_doit and
>post_doit as the generic and message specific part are interleaved and
>the generic parts are also different for do requests, dump requests and
>notifications.

What do you mean by "interleaved"? I guess you can make the attrs to be
formatted in the way it is not interleaved. Like what I suggested, to
have top-level generic attr enum and one of the generic attrs would nest
the cmd-specific attrs. That way, pre_doit can work with the generic
attrs, doit op can work with cmd-specific attrs. Similar to devlink
code.

>
>Michal

2019-03-28 13:45:48

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

Thu, Mar 28, 2019 at 08:18:53AM CET, [email protected] wrote:
>On Wed, Mar 27, 2019 at 07:25:47PM -0700, Florian Fainelli wrote:
>> > +GET_STRSET
>> > +----------
>> > +
>> > +Requests contents of a string set as provided by ioctl commands
>> > +ETHTOOL_GSSET_INFO and ETHTOOL_GSTRINGS. String sets are not user writeable so
>> > +that the corresponding SET_STRSET message is only used in kernel replies.
>> > +There are two types of string sets: global (independent of a device, e.g.
>> > +device feature names) and device specific (e.g. device private flags).
>> > +
>> > +Request contents:
>> > +
>> > + ETHA_STRSET_DEV (nested) device identification
>> > + ETHA_STRSET_COUNTS (flag) request only string counts
>>
>> Should not that be part of the nested attribute under
>> ETHA_STRSET_STRINGSET. We should probably think about adding another
>> flag which indicates that we want to get the stringset associated data,
>> see below why.
>>
>> > + ETHA_STRSET_STRINGSET (nested) string set to request
>> > + ETHA_STRINGSET_ID (u32) set id
>> > +
>> > +Kernel response contents:
>> > +
>> > + ETHA_STRSET_DEV (nested) device identification
>> > + ETHA_STRSET_STRINGSET (nested) string set to request
>>
>> string set requested?
>>
>> > + ETHA_STRINGSET_ID (u32) set id
>> > + ETHA_STRINGSET_COUNT (u32) number of strings
>> > + ETHA_STRINGSET_STRINGS (nested) array of strings
>> > + ETHA_STRING_INDEX (u32) string index
>> > + ETHA_STRING_VALUE (string) string value
>>
>> This is one of the areas where the legacy ethtool ioctl() is painful
>> because we need to request a string set to know how many of those exist
>> to allocate space for those in both kernel and user space.
>>
>> If we could find a way to have a single command that allows us to dump
>> stringset (count, values) and associated data, then we save ourselves a
>> context switch and having to pre-allocate memory accordingly.
>
>The way the proposed interface is designed, we can get both without an
>extra roundtrip but it's done the other way around, i.e. passing the
>strings with the request for data. The API allows two modes of
>operation:
>
>1. One shot requests, e.g. typical ethtool use. We use "verbose" mode
>(no ETHA_*_COMPACT flag in the request) and data is provided together
>with the names. E.g. "ethtool eth2" request would look like
>
> ETHA_SETTINGS_DEV
> ETHA_DEV_NAME = "eth2"
> ETHA_SETTINGS_INFO_MASK = ... | ETH_SETTINGS_IM_LINKMODES | ...
>
>and the reply would be
>
> ETHA_SETTINGS_DEV = {
> ETHA_DEV_INDEX = 4
> ETHA_DEV_NAME = "eth2"
> }
> ETHA_SETTINGS_LINK_INFO = {
> ...
> }
> ETHA_SETTRINGS_LINK_MODES = {
> ETHA_LINKMODES_AUTONEG = 1 (AUTONEG_ENABLE)
> ETHA_LINKMODES_OURS = {
> ETHA_BITSET_SIZE = 67
> ETHA_BITSET_BITS = {
> ETHA_BITS_BIT = {
> ETHA_BIT_INDEX = 0 (ETHTOOL_LINK_MODE_10baseT_Half_BIT)
> ETHA_BIT_NAME = "10baseT/Half"
> }
> ETHA_BITS_BIT = {
> ETHA_BIT_INDEX = 1 (ETHTOOL_LINK_MODE_10baseT_Full_BIT)
> ETHA_BIT_NAME = "10baseT/Full"
> }
> ETHA_BITS_BIT = {
> ETHA_BIT_INDEX = 2 (ETHTOOL_LINK_MODE_100baseT_Half_BIT)
> ETHA_BIT_NAME = "100baseT/Half"
> ETHA_BIT_VALUE
> }
> ETHA_BITS_BIT = {
> ETHA_BIT_INDEX = 3 (ETHTOOL_LINK_MODE_100baseT_Full_BIT)
> ETHA_BIT_NAME = "100baseT/Full"
> ETHA_BIT_VALUE
> }
> ETHA_BITS_BIT = {
> ETHA_BIT_INDEX = 5 (ETHTOOL_LINK_MODE_1000baseT_Full_BIT)
> ETHA_BIT_NAME = "1000baseT/Full"
> ETHA_BIT_VALUE

I don't like this. This should not be bitfield/set. This should be
simply nested array of enum values:

enum ethtool_link_mode {
ETHTOOL_LINK_MODE_10baseT_Half,
ETHTOOL_LINK_MODE_10baseT_Full,
ETHTOOL_LINK_MODE_100baseT_Half,
ETHTOOL_LINK_MODE_100baseT_Full,
ETHTOOL_LINK_MODE_1000baseT_Full,
};

and then there should be 2 attrs:
ETHTOOL_A_LINK_MODE_LIST_OUR /* nest */
ETHTOOL_A_LINK_MODE_LIST_PEER /* nest */
ETHTOOL_A_LINK_MODE /* u32 */

and then the message should look like:

ETHTOOL_A_LINK_MODE_LIST_OUR start nest
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Half
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Full
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Half
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Full
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_1000baseT_Full
ETHTOOL_A_LINK_MODE_LIST_OUR end nest
ETHTOOL_A_LINK_MODE_LIST_PEER start nest
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Half
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Full
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Half
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Full
ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_1000baseT_Full
ETHTOOL_A_LINK_MODE_LIST_PEER end nest

Nice and simple. No bits, no strings.



> }
> }
> }
> ETHA_LINKMODES_PEER = {
> ...
> }
> ETHA_LINKMODES_SPEED = 1000 (SPEED_1000)
> ETHA_LINKMODES_DUPLEX = 1 (DUPLEX_FULL)
> }
> ...
>
>2. Long time running applications, e.g. "ethtool --monitor", wicked,
>systemd-networkd, ... For these, it would make sense to cache the
>strings and either retrieve them in advance (on start and when new
>device appears) or when they are needed for the first time. The request
>would then include the ETHA_SETTINGS_COMPACT flag and reply would be
>
> ...
> ETHA_LINKMODES_OURS = {
> ETHA_BITSET_SIZE = 67
> ETHA_BITSET_VALUE = 0x0000002c 0x00000000 0x00000000
> ETHA_BITSET_MASK = 0x0000002f 0x00000000 0x00000000
> }
>
>For "set" type requests, it's up to the application if it uses verbose
>or compact form. The verbose form is most useful when we want e.g. to
>set the values of one or two bits which may be identified by their names
>(on command line or in config file).
>
>Michal

2019-03-28 14:05:43

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

On Thu, Mar 28, 2019 at 02:43:13PM +0100, Jiri Pirko wrote:
>
> I don't like this. This should not be bitfield/set. This should be
> simply nested array of enum values:
>
> enum ethtool_link_mode {
> ETHTOOL_LINK_MODE_10baseT_Half,
> ETHTOOL_LINK_MODE_10baseT_Full,
> ETHTOOL_LINK_MODE_100baseT_Half,
> ETHTOOL_LINK_MODE_100baseT_Full,
> ETHTOOL_LINK_MODE_1000baseT_Full,
> };

We already have such enum. The problem with your "no string" approach is
that it requires all userspace applications to (1) keep this enum in
sync with kernel and (2) maintain their our tables of names. Experience
shows we are not very good and satisfying these conditions even for the
one which should be best at keeping up.

> and then there should be 2 attrs:
> ETHTOOL_A_LINK_MODE_LIST_OUR /* nest */
> ETHTOOL_A_LINK_MODE_LIST_PEER /* nest */
> ETHTOOL_A_LINK_MODE /* u32 */
>
> and then the message should look like:
>
> ETHTOOL_A_LINK_MODE_LIST_OUR start nest
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Half
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Full
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Half
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Full
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_1000baseT_Full
> ETHTOOL_A_LINK_MODE_LIST_OUR end nest
> ETHTOOL_A_LINK_MODE_LIST_PEER start nest
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Half
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Full
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Half
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Full
> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_1000baseT_Full
> ETHTOOL_A_LINK_MODE_LIST_PEER end nest
>
> Nice and simple. No bits, no strings.

A bit too simple, actually. You would need third nest to distinguish
supported and advertised modes. And for setting, you would also need two
arrays if you want to set only some of the modes (unless you introduce
something that would be similar to mine except for omitting the names).

More important: you still didn't explain how is your "no strings"
approach supposed to work for bit sets where userspace cannot possibly
know the set of available flags (e.g. the private flags).

Michal

2019-03-28 16:37:00

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

Thu, Mar 28, 2019 at 10:53:47AM CET, [email protected] wrote:
>On Thu, Mar 28, 2019 at 10:21:26AM +0100, Jiri Pirko wrote:
>> Wed, Mar 27, 2019 at 11:25:54PM CET, [email protected] wrote:
>> >
>> >I'm all for implementing new features which are are related to physical
>> >device (ASIC) rather than network interface only in devlink (at the
>> >level of kernel-userspace interface). But for features already provided
>> >by ethtool (userspace utility) I can't help seeing the state of devlink
>> >support in NIC drivers as a serious blocker.
>>
>> What I'm thinking about at for some time now would be en explicit
>> default devlink and devlink_port registration for every netdev for
>> drivers that does not support devlink themselves. I need to think about
>> it more, but it seems doable. Then we can hang appropriate things there
>> and make the ethtoolnl/devlink separation clear. I believe we need to do
>> it.
>
>That sounds great, such "generic devlink" implementation would be a way
>around. Kernel could then emulate features which are not implemented by
>an actual devlink handler (i.e. "generic devlink" is used or particular
>handler is missing) by falling back to ethtool_ops handler so that
>userspace could rely on devlink API for things like device information,
>various dumps, flashing etc. without losing anything.

Yep. Plan to do that next week.

>
>Michal

2019-03-28 17:02:47

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

On 3/28/19 6:23 AM, Jiri Pirko wrote:
> Thu, Mar 28, 2019 at 10:37:46AM CET, [email protected] wrote:
>> On Thu, Mar 28, 2019 at 09:10:10AM +0100, Jiri Pirko wrote:
>>> Thu, Mar 28, 2019 at 03:05:14AM CET, [email protected] wrote:
>>>>
>>>>
>>>> On 3/27/2019 2:50 AM, Jiri Pirko wrote:
>>>>>
>>>>> Why don't you have ETHTOOL_MSG_SET_FOO for set? I think that for
>>>>> kerne->userspace the ETHTOOL_MSG_FOO if fine. I would change the
>>>>> ordering of words thought, but it is cosmetics:
>>>>> ETHTOOL_MSG_FOO /* kernel->userspace messages - replies, notifications */
>>>>> ETHTOOL_MSG_FOO_GET
>>>>> ETHTOOL_MSG_FOO_SET
>>>>> ETHTOOL_MSG_FOO_ACT
>>>>>
>>>>> What do you think?
>>>>
>>>> We could even name the notification explicitly with: ETHTOOL_MSG_NOTIF
>>>> or ETHTOOL_MSG_NTF just so we spell out exactly what those messages are.
>>>
>>> Sound good. Something like:
>>>
>>> ETHTOOL_MSG_FOO_GET
>>> ETHTOOL_MSG_FOO_GET_RPLY /* kernel->userspace replies to get */
>>> ETHTOOL_MSG_FOO_SET
>>> ETHTOOL_MSG_FOO_ACT
>>> ETHTOOL_MSG_FOO_NTF /* kernel->userspace async messages - notifications */
>>
>> The names sound fine to me and having different message ids would still
>> allow processing messages by the same handler easily.
>>
>> But there is one potential issue I would like to point out: this way we
>> spend 4 message ids for a get/set pair rather than 2. These message ids
>> (genlmsghdr::cmd) are u8, i.e. the resource is not as infinite as one
>> would wish. There are 80 ioctl commands (43 "get" and 29 "set") at the
>> moment.
>>
>> Netlink API should be less greedy in general. I already combined some
>> ioctl commands into one netlink request type and with an easy way to add
>> new attributes to existing commands, we won't need to add new commands
>> as often (certainly not in a way which left us with 9 "get" and 9 "set"
>> ioctl commands for netdev features). So even with 4 ids per get/set
>> pair, we might be safe for reasonably long time. But it's still
>> something to keep in mind.
>
> There are still 16 bits reserve in genl msg header:
> struct genlmsghdr {
> __u8 cmd;
> __u8 version;
> __u16 reserved;
> };
>

And you know not all message IDs will be making sense depending on the
direction, so aliasing specific message IDs to an existing value should
be fine?
--
Florian

2019-03-28 17:29:34

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/22] ethtool: introduce ethtool netlink interface

Thu, Mar 28, 2019 at 06:00:20PM CET, [email protected] wrote:
>On 3/28/19 6:23 AM, Jiri Pirko wrote:
>> Thu, Mar 28, 2019 at 10:37:46AM CET, [email protected] wrote:
>>> On Thu, Mar 28, 2019 at 09:10:10AM +0100, Jiri Pirko wrote:
>>>> Thu, Mar 28, 2019 at 03:05:14AM CET, [email protected] wrote:
>>>>>
>>>>>
>>>>> On 3/27/2019 2:50 AM, Jiri Pirko wrote:
>>>>>>
>>>>>> Why don't you have ETHTOOL_MSG_SET_FOO for set? I think that for
>>>>>> kerne->userspace the ETHTOOL_MSG_FOO if fine. I would change the
>>>>>> ordering of words thought, but it is cosmetics:
>>>>>> ETHTOOL_MSG_FOO /* kernel->userspace messages - replies, notifications */
>>>>>> ETHTOOL_MSG_FOO_GET
>>>>>> ETHTOOL_MSG_FOO_SET
>>>>>> ETHTOOL_MSG_FOO_ACT
>>>>>>
>>>>>> What do you think?
>>>>>
>>>>> We could even name the notification explicitly with: ETHTOOL_MSG_NOTIF
>>>>> or ETHTOOL_MSG_NTF just so we spell out exactly what those messages are.
>>>>
>>>> Sound good. Something like:
>>>>
>>>> ETHTOOL_MSG_FOO_GET
>>>> ETHTOOL_MSG_FOO_GET_RPLY /* kernel->userspace replies to get */
>>>> ETHTOOL_MSG_FOO_SET
>>>> ETHTOOL_MSG_FOO_ACT
>>>> ETHTOOL_MSG_FOO_NTF /* kernel->userspace async messages - notifications */
>>>
>>> The names sound fine to me and having different message ids would still
>>> allow processing messages by the same handler easily.
>>>
>>> But there is one potential issue I would like to point out: this way we
>>> spend 4 message ids for a get/set pair rather than 2. These message ids
>>> (genlmsghdr::cmd) are u8, i.e. the resource is not as infinite as one
>>> would wish. There are 80 ioctl commands (43 "get" and 29 "set") at the
>>> moment.
>>>
>>> Netlink API should be less greedy in general. I already combined some
>>> ioctl commands into one netlink request type and with an easy way to add
>>> new attributes to existing commands, we won't need to add new commands
>>> as often (certainly not in a way which left us with 9 "get" and 9 "set"
>>> ioctl commands for netdev features). So even with 4 ids per get/set
>>> pair, we might be safe for reasonably long time. But it's still
>>> something to keep in mind.
>>
>> There are still 16 bits reserve in genl msg header:
>> struct genlmsghdr {
>> __u8 cmd;
>> __u8 version;
>> __u16 reserved;
>> };
>>
>
>And you know not all message IDs will be making sense depending on the
>direction, so aliasing specific message IDs to an existing value should
>be fine?

You are right, good idea. There can be 2 enums one for in, one for out.

2019-03-28 17:36:18

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

Thu, Mar 28, 2019 at 03:04:28PM CET, [email protected] wrote:
>On Thu, Mar 28, 2019 at 02:43:13PM +0100, Jiri Pirko wrote:
>>
>> I don't like this. This should not be bitfield/set. This should be
>> simply nested array of enum values:
>>
>> enum ethtool_link_mode {
>> ETHTOOL_LINK_MODE_10baseT_Half,
>> ETHTOOL_LINK_MODE_10baseT_Full,
>> ETHTOOL_LINK_MODE_100baseT_Half,
>> ETHTOOL_LINK_MODE_100baseT_Full,
>> ETHTOOL_LINK_MODE_1000baseT_Full,
>> };
>
>We already have such enum. The problem with your "no string" approach is
>that it requires all userspace applications to (1) keep this enum in

That is how it is usually done. UAPI defines ATTRS and values, userspace
assigns appropriate strings.


>sync with kernel and (2) maintain their our tables of names. Experience
>shows we are not very good and satisfying these conditions even for the
>one which should be best at keeping up.

Moreover, I think that speed, duplex and type should be sent
separatelly:

ETHTOOL_A_LINK_MODE_LIST_OUR start nest
ETHTOOL_A_LINK_MODE start nest
ETHTOOL_A_LINK_MODE_SPEED = 100 /* this should be u64 */
ETHTOOL_A_LINK_MODE_DUPLEX = ETHTOOL_LINK_MODE_DUPLEX_FULL
ETHTOOL_A_LINK_MODE_TYPE = ETHTOOL_LINK_MODE_TYPE_BASET
ETHTOOL_A_LINK_MODE start end
ETHTOOL_A_LINK_MODE start nest
ETHTOOL_A_LINK_MODE_SPEED = 10
ETHTOOL_A_LINK_MODE_DUPLEX = ETHTOOL_LINK_MODE_DUPLEX_HALF
ETHTOOL_A_LINK_MODE_TYPE = ETHTOOL_LINK_MODE_TYPE_BASET
ETHTOOL_A_LINK_MODE start end
ETHTOOL_A_LINK_MODE_LIST_PEER end nest

Does not really make sense to combine those 3 attributes together.


>
>> and then there should be 2 attrs:
>> ETHTOOL_A_LINK_MODE_LIST_OUR /* nest */
>> ETHTOOL_A_LINK_MODE_LIST_PEER /* nest */
>> ETHTOOL_A_LINK_MODE /* u32 */
>>
>> and then the message should look like:
>>
>> ETHTOOL_A_LINK_MODE_LIST_OUR start nest
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Half
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Full
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Half
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Full
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_1000baseT_Full
>> ETHTOOL_A_LINK_MODE_LIST_OUR end nest
>> ETHTOOL_A_LINK_MODE_LIST_PEER start nest
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Half
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_10baseT_Full
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Half
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_100baseT_Full
>> ETHTOOL_A_LINK_MODE = ETHTOOL_LINK_MODE_1000baseT_Full
>> ETHTOOL_A_LINK_MODE_LIST_PEER end nest
>>
>> Nice and simple. No bits, no strings.
>
>A bit too simple, actually. You would need third nest to distinguish

It is just an example.


>supported and advertised modes. And for setting, you would also need two
>arrays if you want to set only some of the modes (unless you introduce
>something that would be similar to mine except for omitting the names).
>
>More important: you still didn't explain how is your "no strings"
>approach supposed to work for bit sets where userspace cannot possibly
>know the set of available flags (e.g. the private flags).

I know, that is a different part of this thread.

>
>Michal

2019-03-28 18:54:00

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

On Thu, 28 Mar 2019 18:35:24 +0100, Jiri Pirko wrote:
> Thu, Mar 28, 2019 at 03:04:28PM CET, [email protected] wrote:
> >On Thu, Mar 28, 2019 at 02:43:13PM +0100, Jiri Pirko wrote:
> >>
> >> I don't like this. This should not be bitfield/set. This should be
> >> simply nested array of enum values:
> >>
> >> enum ethtool_link_mode {
> >> ETHTOOL_LINK_MODE_10baseT_Half,
> >> ETHTOOL_LINK_MODE_10baseT_Full,
> >> ETHTOOL_LINK_MODE_100baseT_Half,
> >> ETHTOOL_LINK_MODE_100baseT_Full,
> >> ETHTOOL_LINK_MODE_1000baseT_Full,
> >> };
> >
> >We already have such enum. The problem with your "no string" approach is
> >that it requires all userspace applications to (1) keep this enum in
>
> That is how it is usually done. UAPI defines ATTRS and values, userspace
> assigns appropriate strings.

+1 FWIW, I'm with Jiri on the string situation.

2019-03-28 20:10:44

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

On Thu, 28 Mar 2019 17:34:39 +0100, Jiri Pirko wrote:
> Thu, Mar 28, 2019 at 10:53:47AM CET, [email protected] wrote:
> >On Thu, Mar 28, 2019 at 10:21:26AM +0100, Jiri Pirko wrote:
> >> Wed, Mar 27, 2019 at 11:25:54PM CET, [email protected] wrote:
> >> >
> >> >I'm all for implementing new features which are are related to physical
> >> >device (ASIC) rather than network interface only in devlink (at the
> >> >level of kernel-userspace interface). But for features already provided
> >> >by ethtool (userspace utility) I can't help seeing the state of devlink
> >> >support in NIC drivers as a serious blocker.
> >>
> >> What I'm thinking about at for some time now would be en explicit
> >> default devlink and devlink_port registration for every netdev for
> >> drivers that does not support devlink themselves. I need to think about
> >> it more, but it seems doable. Then we can hang appropriate things there
> >> and make the ethtoolnl/devlink separation clear. I believe we need to do
> >> it.
> >
> >That sounds great, such "generic devlink" implementation would be a way
> >around. Kernel could then emulate features which are not implemented by
> >an actual devlink handler (i.e. "generic devlink" is used or particular
> >handler is missing) by falling back to ethtool_ops handler so that
> >userspace could rely on devlink API for things like device information,
> >various dumps, flashing etc. without losing anything.
>
> Yep. Plan to do that next week.

The problem with the main part of dev info - fw_version - is that it is
often overloaded in drivers and becomes impossible to parse for users.
I'd rather we didn't dump that nasty chaos in devlink info and let it
die with ethtool IOCTL.

Flashing can also be handled at user space tool level.

2019-03-28 20:44:49

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

On Thu, Mar 28, 2019 at 11:52:56AM -0700, Jakub Kicinski wrote:
> On Thu, 28 Mar 2019 18:35:24 +0100, Jiri Pirko wrote:
> > Thu, Mar 28, 2019 at 03:04:28PM CET, [email protected] wrote:
> > >On Thu, Mar 28, 2019 at 02:43:13PM +0100, Jiri Pirko wrote:
> > >>
> > >> I don't like this. This should not be bitfield/set. This should be
> > >> simply nested array of enum values:
> > >>
> > >> enum ethtool_link_mode {
> > >> ETHTOOL_LINK_MODE_10baseT_Half,
> > >> ETHTOOL_LINK_MODE_10baseT_Full,
> > >> ETHTOOL_LINK_MODE_100baseT_Half,
> > >> ETHTOOL_LINK_MODE_100baseT_Full,
> > >> ETHTOOL_LINK_MODE_1000baseT_Full,
> > >> };
> > >
> > >We already have such enum. The problem with your "no string" approach is
> > >that it requires all userspace applications to (1) keep this enum in
> >
> > That is how it is usually done. UAPI defines ATTRS and values, userspace
> > assigns appropriate strings.
>
> +1 FWIW, I'm with Jiri on the string situation.

And I'm still waiting for any of you to tell me how would you handle
private flags, stats, tests etc. without the string sets.

Ditching the verbose form of bit sets would be a nuisance for userspace
using the interface but compared to e.g. having to mix three different
kernel interfaces, it's just a minor problem. Ditching the static string
sets would mean giving up the opportunity to get rid of having to sync
all kinds of tables with every userspace consumer whenever a new flag is
introduced. Pity... but still doable. But how do you want to do without
the string sets which are provided by drivers?

Michal

2019-03-28 21:07:20

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

On Thu, 28 Mar 2019 21:43:29 +0100, Michal Kubecek wrote:
> On Thu, Mar 28, 2019 at 11:52:56AM -0700, Jakub Kicinski wrote:
> > On Thu, 28 Mar 2019 18:35:24 +0100, Jiri Pirko wrote:
> > > Thu, Mar 28, 2019 at 03:04:28PM CET, [email protected] wrote:
> > > >On Thu, Mar 28, 2019 at 02:43:13PM +0100, Jiri Pirko wrote:
> > > >>
> > > >> I don't like this. This should not be bitfield/set. This should be
> > > >> simply nested array of enum values:
> > > >>
> > > >> enum ethtool_link_mode {
> > > >> ETHTOOL_LINK_MODE_10baseT_Half,
> > > >> ETHTOOL_LINK_MODE_10baseT_Full,
> > > >> ETHTOOL_LINK_MODE_100baseT_Half,
> > > >> ETHTOOL_LINK_MODE_100baseT_Full,
> > > >> ETHTOOL_LINK_MODE_1000baseT_Full,
> > > >> };
> > > >
> > > >We already have such enum. The problem with your "no string" approach is
> > > >that it requires all userspace applications to (1) keep this enum in
> > >
> > > That is how it is usually done. UAPI defines ATTRS and values, userspace
> > > assigns appropriate strings.
> >
> > +1 FWIW, I'm with Jiri on the string situation.
>
> And I'm still waiting for any of you to tell me how would you handle
> private flags, stats, tests etc. without the string sets.
>
> Ditching the verbose form of bit sets would be a nuisance for userspace
> using the interface but compared to e.g. having to mix three different
> kernel interfaces, it's just a minor problem. Ditching the static string
> sets would mean giving up the opportunity to get rid of having to sync
> all kinds of tables with every userspace consumer whenever a new flag is
> introduced. Pity... but still doable. But how do you want to do without
> the string sets which are provided by drivers?

I don't think there is a disagreement about stuff which is private to
the driver. The strings have to be exposed there.

2019-03-28 22:29:39

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

On Thu, Mar 28, 2019 at 06:35:24PM +0100, Jiri Pirko wrote:
>
> Moreover, I think that speed, duplex and type should be sent
> separatelly:
>
> ETHTOOL_A_LINK_MODE_LIST_OUR start nest
> ETHTOOL_A_LINK_MODE start nest
> ETHTOOL_A_LINK_MODE_SPEED = 100 /* this should be u64 */
> ETHTOOL_A_LINK_MODE_DUPLEX = ETHTOOL_LINK_MODE_DUPLEX_FULL
> ETHTOOL_A_LINK_MODE_TYPE = ETHTOOL_LINK_MODE_TYPE_BASET
> ETHTOOL_A_LINK_MODE start end
> ETHTOOL_A_LINK_MODE start nest
> ETHTOOL_A_LINK_MODE_SPEED = 10
> ETHTOOL_A_LINK_MODE_DUPLEX = ETHTOOL_LINK_MODE_DUPLEX_HALF
> ETHTOOL_A_LINK_MODE_TYPE = ETHTOOL_LINK_MODE_TYPE_BASET
> ETHTOOL_A_LINK_MODE start end
> ETHTOOL_A_LINK_MODE_LIST_PEER end nest
>
> Does not really make sense to combine those 3 attributes together.

This helped me to realize the primary source of misunderstanding: as I'm
working on kernel and ethtool implementation simultaneously, I don't
look only at the API itself and don't consider only if its design is
clean and logical. I always think about the actual userspace programs
wanting to use the interface and try to imagine what the design means
for them.

So when you say e.g. "this belongs to rtnetlink", I have to admit that
from strictly logical point of view you are right but at the same time,
chain of thoughts starts: "that means two sockets; we need a table which
command needs which, some might need both, monitor will have to listen
at two sockets, that's fork/pthread/poll..."

From this point of view, the scheme above which, on its own, makes
perfect sense (there might be a bit of a problem with 10000baseR_FEC
mode but that one is a trouble in any scheme), means that one side will
translate the link mode number to the three parameters and the other
will look the triplet out in its own table to get the link mode number.
On userspace side, there will be another translation between link mode
number and name. What exactly is the gain from such representation?
No idea.

There is one crucial difference between ethtool and devlink. You are
building devlink from scratch so you define the logic, define the API
based on that logic and make both NIC drivers and userspace conform to
it. With ethtool, the situation is exactly the opposite: on one side,
there are ethtool_ops as a constraint, on the other, it's ethtool with
its feature set and users used to it. Changing ethtool_ops is going to
be slow and painful process. Changing the users and their habits...
we already know how that works.

Michal

2019-03-28 22:47:14

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

On Thu, Mar 28, 2019 at 01:09:04PM -0700, Jakub Kicinski wrote:
> On Thu, 28 Mar 2019 17:34:39 +0100, Jiri Pirko wrote:
> > Thu, Mar 28, 2019 at 10:53:47AM CET, [email protected] wrote:
> > >On Thu, Mar 28, 2019 at 10:21:26AM +0100, Jiri Pirko wrote:
> > >> Wed, Mar 27, 2019 at 11:25:54PM CET, [email protected] wrote:
> > >> >
> > >> >I'm all for implementing new features which are are related to physical
> > >> >device (ASIC) rather than network interface only in devlink (at the
> > >> >level of kernel-userspace interface). But for features already provided
> > >> >by ethtool (userspace utility) I can't help seeing the state of devlink
> > >> >support in NIC drivers as a serious blocker.
> > >>
> > >> What I'm thinking about at for some time now would be en explicit
> > >> default devlink and devlink_port registration for every netdev for
> > >> drivers that does not support devlink themselves. I need to think about
> > >> it more, but it seems doable. Then we can hang appropriate things there
> > >> and make the ethtoolnl/devlink separation clear. I believe we need to do
> > >> it.
> > >
> > >That sounds great, such "generic devlink" implementation would be a way
> > >around. Kernel could then emulate features which are not implemented by
> > >an actual devlink handler (i.e. "generic devlink" is used or particular
> > >handler is missing) by falling back to ethtool_ops handler so that
> > >userspace could rely on devlink API for things like device information,
> > >various dumps, flashing etc. without losing anything.
> >
> > Yep. Plan to do that next week.
>
> The problem with the main part of dev info - fw_version - is that it is
> often overloaded in drivers and becomes impossible to parse for users.
> I'd rather we didn't dump that nasty chaos in devlink info and let it
> die with ethtool IOCTL.

If I understand you correctly, what you are suggesting would result in
"ethtool -i" through netlink not showing firmware version for NICs
without actual devlink implementation (which is vast majority of NICs at
the moment). I'm afraid doing that would make sure ioctl ethtool
wouldn't die any time soon.

> Flashing can also be handled at user space tool level.

I'm not sure how to understand this.

Michal

2019-03-29 06:54:24

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

Thu, Mar 28, 2019 at 10:06:07PM CET, [email protected] wrote:
>On Thu, 28 Mar 2019 21:43:29 +0100, Michal Kubecek wrote:
>> On Thu, Mar 28, 2019 at 11:52:56AM -0700, Jakub Kicinski wrote:
>> > On Thu, 28 Mar 2019 18:35:24 +0100, Jiri Pirko wrote:
>> > > Thu, Mar 28, 2019 at 03:04:28PM CET, [email protected] wrote:
>> > > >On Thu, Mar 28, 2019 at 02:43:13PM +0100, Jiri Pirko wrote:
>> > > >>
>> > > >> I don't like this. This should not be bitfield/set. This should be
>> > > >> simply nested array of enum values:
>> > > >>
>> > > >> enum ethtool_link_mode {
>> > > >> ETHTOOL_LINK_MODE_10baseT_Half,
>> > > >> ETHTOOL_LINK_MODE_10baseT_Full,
>> > > >> ETHTOOL_LINK_MODE_100baseT_Half,
>> > > >> ETHTOOL_LINK_MODE_100baseT_Full,
>> > > >> ETHTOOL_LINK_MODE_1000baseT_Full,
>> > > >> };
>> > > >
>> > > >We already have such enum. The problem with your "no string" approach is
>> > > >that it requires all userspace applications to (1) keep this enum in
>> > >
>> > > That is how it is usually done. UAPI defines ATTRS and values, userspace
>> > > assigns appropriate strings.
>> >
>> > +1 FWIW, I'm with Jiri on the string situation.
>>
>> And I'm still waiting for any of you to tell me how would you handle
>> private flags, stats, tests etc. without the string sets.
>>
>> Ditching the verbose form of bit sets would be a nuisance for userspace
>> using the interface but compared to e.g. having to mix three different
>> kernel interfaces, it's just a minor problem. Ditching the static string
>> sets would mean giving up the opportunity to get rid of having to sync
>> all kinds of tables with every userspace consumer whenever a new flag is
>> introduced. Pity... but still doable. But how do you want to do without
>> the string sets which are provided by drivers?
>
>I don't think there is a disagreement about stuff which is private to
>the driver. The strings have to be exposed there.

Yeah, there is no other way of doing that. However, please check if some
driver-specific setting cannot be moved to devlink with use of devlink
params (we have both devlink driver specific params and per-port
devlink driver specific params).

2019-03-29 08:44:42

by Jiri Pirko

[permalink] [raw]
Subject: Re: [PATCH net-next v5 12/22] ethtool: provide string sets with GET_STRSET request

Wed, Mar 27, 2019 at 11:56:08PM CET, [email protected] wrote:
>On Wed, Mar 27, 2019 at 09:12:37PM +0100, Jiri Pirko wrote:
>> Mon, Mar 25, 2019 at 06:08:30PM CET, [email protected] wrote:
>> >Requests a contents of one or more string sets, i.e. indexed arrays of
>> >strings; this information is provided by ETHTOOL_GSSET_INFO and
>> >ETHTOOL_GSTRINGS commands of ioctl interface. There are three types of
>> >requests:
>> >
>> > - no NLM_F_DUMP, no device: get "global" stringsets
>> > - no NLM_F_DUMP, with device: get string sets related to the device
>> > - NLM_F_DUMP, no device: get device related string sets for all devices
>> >
>> >It's possible to request all string sets of given type or only specific
>> >sets. With ETHA_STRSET_COUNTS flag, only set sizes (number of strings) are
>> >returned.
>> >
>> >Signed-off-by: Michal Kubecek <[email protected]>
>> >---
>> > Documentation/networking/ethtool-netlink.txt | 46 +-
>> > include/uapi/linux/ethtool.h | 2 +
>> > include/uapi/linux/ethtool_netlink.h | 43 ++
>> > net/ethtool/Makefile | 2 +-
>> > net/ethtool/netlink.c | 8 +
>> > net/ethtool/netlink.h | 4 +
>> > net/ethtool/strset.c | 447 +++++++++++++++++++
>> > 7 files changed, 549 insertions(+), 3 deletions(-)
>> > create mode 100644 net/ethtool/strset.c
>>
>> First of all, the code is hard to follow. For reasons I mentioned in
>> other replies (lack of prefixes, wrappers, etc).
>>
>> More importantly, why do we need this? This concept of having strings in
>> kernel for various things and features and sending them to userspace is
>> weird. Certainly not common for Netlink interface. I believe these
>> strings should be avoided and all should be communicated to userspace
>> and back in form of well-defined Netlink attributes. We are introducing
>> new Netlink API, lets do it properly and don't bring baggage from past.
>
>For some of the global string sets where the values have fixed meaning
>and new values are only appended to, it would be possible. But even for
>those, keeping the list in sync between kernel and ethtool is often less
>than perfect. And ethtool is only one of the tools using the interface.
>So even in this case, I don't see string identifiers (or tags or names
>or whatever we call them) as a baggage from past, rather as a solution
>to a problem some of the existing interfaces have.
>
>Then e.g. netdev features are not fixed in this sense: the same bit
>index represents different feature in different kernels. I guess we

Should not be exposed in bits in first place. See devlink params for
example.


>could introduce some fixed numeric identifiers for them and map between
>those and actual indices but I don't see an advantage of such approach.
>
>But the really important question is how would you handle what is
>currently described by "per device" string sets, i.e. private flags,
>(ethtool) statistics, tests, ...? For these, the list depends on the
>driver or even device.

Yes, those per-driver things have to be strings. That's what we did for
devlink params.

Tests also could be handled in similar way as devlink params.

For stats, I believe that uint->string per-driver mapping
needs to be exposed.


2019-03-29 18:49:15

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

On Thu, 28 Mar 2019 23:46:18 +0100, Michal Kubecek wrote:
> On Thu, Mar 28, 2019 at 01:09:04PM -0700, Jakub Kicinski wrote:
> > On Thu, 28 Mar 2019 17:34:39 +0100, Jiri Pirko wrote:
> > > Thu, Mar 28, 2019 at 10:53:47AM CET, [email protected] wrote:
> > > >On Thu, Mar 28, 2019 at 10:21:26AM +0100, Jiri Pirko wrote:
> > > >> Wed, Mar 27, 2019 at 11:25:54PM CET, [email protected] wrote:
> > > >> >
> > > >> >I'm all for implementing new features which are are related to physical
> > > >> >device (ASIC) rather than network interface only in devlink (at the
> > > >> >level of kernel-userspace interface). But for features already provided
> > > >> >by ethtool (userspace utility) I can't help seeing the state of devlink
> > > >> >support in NIC drivers as a serious blocker.
> > > >>
> > > >> What I'm thinking about at for some time now would be en explicit
> > > >> default devlink and devlink_port registration for every netdev for
> > > >> drivers that does not support devlink themselves. I need to think about
> > > >> it more, but it seems doable. Then we can hang appropriate things there
> > > >> and make the ethtoolnl/devlink separation clear. I believe we need to do
> > > >> it.
> > > >
> > > >That sounds great, such "generic devlink" implementation would be a way
> > > >around. Kernel could then emulate features which are not implemented by
> > > >an actual devlink handler (i.e. "generic devlink" is used or particular
> > > >handler is missing) by falling back to ethtool_ops handler so that
> > > >userspace could rely on devlink API for things like device information,
> > > >various dumps, flashing etc. without losing anything.
> > >
> > > Yep. Plan to do that next week.
> >
> > The problem with the main part of dev info - fw_version - is that it is
> > often overloaded in drivers and becomes impossible to parse for users.
> > I'd rather we didn't dump that nasty chaos in devlink info and let it
> > die with ethtool IOCTL.
>
> If I understand you correctly, what you are suggesting would result in
> "ethtool -i" through netlink not showing firmware version for NICs
> without actual devlink implementation (which is vast majority of NICs at
> the moment). I'm afraid doing that would make sure ioctl ethtool
> wouldn't die any time soon.

I was trying to say that'd be opposed to showing the conglomerate
fw_version from ethtool in devlink. I'm not opposed to showing it
in ethnl or having ethnl call into devlink to produce the conglomerate,
but I'd rather not see the legacy of ethtool creeping back into devlink.

> > Flashing can also be handled at user space tool level.
>
> I'm not sure how to understand this.

If we want ethnl user space command to provide flashing, it can just
send a devlink CMD behind the scenes.

2019-03-29 18:54:14

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 13/22] ethtool: provide driver/device information in GET_INFO request

On 3/28/19 1:09 PM, Jakub Kicinski wrote:
> On Thu, 28 Mar 2019 17:34:39 +0100, Jiri Pirko wrote:
>> Thu, Mar 28, 2019 at 10:53:47AM CET, [email protected] wrote:
>>> On Thu, Mar 28, 2019 at 10:21:26AM +0100, Jiri Pirko wrote:
>>>> Wed, Mar 27, 2019 at 11:25:54PM CET, [email protected] wrote:
>>>>>
>>>>> I'm all for implementing new features which are are related to physical
>>>>> device (ASIC) rather than network interface only in devlink (at the
>>>>> level of kernel-userspace interface). But for features already provided
>>>>> by ethtool (userspace utility) I can't help seeing the state of devlink
>>>>> support in NIC drivers as a serious blocker.
>>>>
>>>> What I'm thinking about at for some time now would be en explicit
>>>> default devlink and devlink_port registration for every netdev for
>>>> drivers that does not support devlink themselves. I need to think about
>>>> it more, but it seems doable. Then we can hang appropriate things there
>>>> and make the ethtoolnl/devlink separation clear. I believe we need to do
>>>> it.
>>>
>>> That sounds great, such "generic devlink" implementation would be a way
>>> around. Kernel could then emulate features which are not implemented by
>>> an actual devlink handler (i.e. "generic devlink" is used or particular
>>> handler is missing) by falling back to ethtool_ops handler so that
>>> userspace could rely on devlink API for things like device information,
>>> various dumps, flashing etc. without losing anything.
>>
>> Yep. Plan to do that next week.
>
> The problem with the main part of dev info - fw_version - is that it is
> often overloaded in drivers and becomes impossible to parse for users.
> I'd rather we didn't dump that nasty chaos in devlink info and let it
> die with ethtool IOCTL.
>
> Flashing can also be handled at user space tool level.
>

Yes, I really like how sfc does it which is to expose MTD devices and
certainly have a custom flashing tool, that should be the way to go IMHO.
--
Florian