2019-02-18 19:17:08

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 00/21] ethtool netlink interface, part 1

Note: this is marked as RFC because it's rather late in the cycle; the plan
is to make a regular submission (with changes based on review) once
net-next reopens after the 5.1 merge window. The full (work in progress)
series, together with the (userspace) ethtool counterpart can be found at
https://github.com/mkubecek/ethnl

This is first part of 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 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

Basic concepts:

- the goal is to provide all features of ioctl interface but allow
easier future extensions; at some point, it should be possible to have
full ethtool functionality without using the ioctl interface
- inextensibility of ioctl interface resulted in way too many commands,
many of them obsoleted by newer ones; reduce the number by ignoring the
obsolete commands and grouping some together
- for "set" type commands, allows passing only the attributes to be
changed; therefore we don't need a get-modify-set cycle (which is
inherently racy), userspace can simply say what it wants to change
- provide notifications to multicast group "monitor" like rtnetlink does,
i.e. in the form of messages close to replies to "get" requests
- allow dump requests to get some information about all network devices
providing it
- be less dependent on ethtool and kernel being in sync; allow e.g. saying
"ethtool -s eth0 advertise foo off" without ethtool knowing what "foo"
means; it's kernel's job to know what mode "xyz" is and if it exists and
is supported

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 (21):
netlink: introduce nla_put_bitfield32()
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 permanent hardware address 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: provide WoL information in GET_SETTINGS request
ethtool: provide message level in GET_SETTINGS request
ethtool: provide link state in GET_SETTINGS request
ethtool: provide device features in GET_SETTINGS request
ethtool: provide private flags in GET_SETTINGS request
ethtool: send netlink notifications about setting changes

Documentation/networking/ethtool-netlink.txt | 441 +++++++++++++
include/linux/ethtool_netlink.h | 17 +
include/linux/netdevice.h | 14 +
include/net/netlink.h | 15 +
include/uapi/linux/ethtool.h | 10 +
include/uapi/linux/ethtool_netlink.h | 265 ++++++++
include/uapi/linux/net_tstamp.h | 13 +
net/Kconfig | 8 +
net/Makefile | 2 +-
net/core/Makefile | 2 +-
net/ethtool/Makefile | 7 +
net/ethtool/bitset.c | 572 +++++++++++++++++
net/ethtool/bitset.h | 40 ++
net/ethtool/common.c | 227 +++++++
net/ethtool/common.h | 29 +
net/ethtool/info.c | 332 ++++++++++
net/{core/ethtool.c => ethtool/ioctl.c} | 244 ++-----
net/ethtool/netlink.c | 634 +++++++++++++++++++
net/ethtool/netlink.h | 252 ++++++++
net/ethtool/settings.c | 559 ++++++++++++++++
net/ethtool/strset.c | 461 ++++++++++++++
21 files changed, 3937 insertions(+), 207 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.20.1



2019-02-18 19:15:57

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 01/21] 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.20.1


2019-02-18 19:16:02

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 02/21] 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 ioct.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 bdaf53925acd..6c68520e5eb9 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.20.1


2019-02-18 19:16:22

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 06/21] 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 | 32 ++++++++++++++++++++++++++++
net/ethtool/netlink.h | 2 ++
5 files changed, 53 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 aab4d9f6613d..9a50a67f328f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4340,6 +4340,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 7f3d401977b4..b662d75a0636 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -59,4 +59,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 ffdcc7c9d4bc..a5fa54c2b743 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -4,6 +4,8 @@
#include <linux/ethtool_netlink.h>
#include "netlink.h"

+u32 ethnl_bcast_seq;
+
static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {
[ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
[ETHA_DEV_INDEX] = { .type = NLA_U32 },
@@ -122,11 +124,39 @@ 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)
+{
+ 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,
@@ -135,6 +165,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 */
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index ba638eb5a99a..78385baeaec0 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.20.1


2019-02-18 19:16:29

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 08/21] 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 | 286 ++++++++++++++++++++++++++++++++++++++++++
net/ethtool/netlink.h | 84 +++++++++++++
2 files changed, 370 insertions(+)

diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index ee3424cd1f90..8cdb6f52cb4a 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -84,6 +84,41 @@ int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype)
return ret;
}

+/* GET request helpers */
+
+const struct get_request_ops *get_requests[__ETHNL_CMD_CNT] = {
+};
+
+static struct common_req_info *alloc_get_data(const struct get_request_ops *ops)
+{
+ struct common_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;
+}
+
+static void 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);
+}
+
+static void 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;
+}
+
/* 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)
@@ -124,6 +159,257 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
return NULL;
}

+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 = 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;
+ 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);
+ 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);
+ 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;
+
+ 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; 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 = 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)
+{
+ 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 = alloc_get_data(ops);
+ if (!req_info)
+ return;
+ req_info->dev = dev;
+ req_info->req_mask = req_mask;
+ req_info->compact = true;
+
+ 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;
+ 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:
+ 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 78385baeaec0..7141ec71a6d3 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -155,4 +155,88 @@ static inline unsigned int dev_ident_size(void)
nla_total_size(IFNAMSIZ));
}

+/* GET request handling */
+
+struct common_reply_data;
+
+/* 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()
+ *
+ * @reply_data: pointer to corresponding struct common_reply_data
+ * @dev: requested device; may be null (dumps), if not, reference is held
+ * @req_mask: bit mask of parts of information requested
+ * @compact: use compact format for bitsets
+ */
+struct common_req_info {
+ struct common_reply_data *reply_data;
+ struct net_device *dev;
+ u32 req_mask;
+ bool compact;
+};
+
+/* @dev: device for current reply message
+ * @info_mask: subset of req_mask (without information which is not available)
+ */
+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);
+}
+
+/* parameters and callbacks for unified handling of GET requests
+ * @request_cmd: command id for request (GET)
+ * @reply_cmd: command id for reply (SET)
+ * @dev_attr: attr 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 set)
+ * @prepare_data: retrieve data needed to compose a reply message; reply data
+ * is zero initialized on entry except @dev
+ * @reply_size: return size of reply message payload without device
+ * specification; reported size may be slightly bigger than actual reply
+ * but must not be smaller
+ * @fill_reply: fill reply message payload
+ * @cleanup: (optional) called when data are no longer needed; use e.g. to free
+ * any additional data allocated in prepare_data() which are not part
+ * of the main structure
+ */
+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.20.1


2019-02-18 19:16:32

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 09/21] 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 1320e8dce559..71a1643adb2b 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.20.1


2019-02-18 19:16:37

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 10/21] 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 | 10 +
net/ethtool/strset.c | 437 +++++++++++++++++++
6 files changed, 537 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 b79c26b5e92b..f0fe4f50db9f 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -126,6 +126,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.
@@ -166,6 +168,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
-------------------

@@ -200,7 +242,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
@@ -228,7 +270,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 17be76aeb468..9ea278961d80 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 7e192ad8ce3a..630b66732dc9 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -8,6 +8,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)
@@ -92,6 +94,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 8cdb6f52cb4a..082a9f2aa0a7 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -86,7 +86,10 @@ int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype)

/* GET request helpers */

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

static struct common_req_info *alloc_get_data(const struct get_request_ops *ops)
@@ -498,6 +501,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/strset.c b/net/ethtool/strset.c
new file mode 100644
index 000000000000..a7d0ec2865fb
--- /dev/null
+++ b/net/ethtool/strset.c
@@ -0,0 +1,437 @@
+// 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 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(tb, ETHA_STRINGSET_MAX, nest, 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;
+}
+
+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;
+ }
+ }
+
+ 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;
+}
+
+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;
+
+ 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;
+ };
+}
+
+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;
+}
+
+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.20.1


2019-02-18 19:16:43

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 12/21] ethtool: provide permanent hardware address in GET_INFO request

Add information about permanent hadware address of a device (as provided by
ETHTOOL_GPERMADDR ioctl command) in GET_INFO reply if ETH_INFO_IM_PERMADDR
flag is set in the request.

There is no separate attribute for hardware address length as nla_len gives
this information. The reply also provides address type (net_device::type).

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 9 ++++-
include/uapi/linux/ethtool_netlink.h | 12 +++++-
net/ethtool/info.c | 39 ++++++++++++++++++++
3 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index b6999a2167e8..1e615e111262 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -239,6 +239,9 @@ 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_PERMADDR (nested)
+ ETHA_PERMADDR_ADDRESS (binary) permanent HW address
+ ETHA_PERMADDR_TYPE (u16) dev->type

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

+There is no separate attribute for permanent address length as the length can
+be determined from attribute length (nla_len()). ETHA_PERMADDR_TYPE provides
+net_device::type value to give a hint about what kind of address device has.
+
GET_INFO requests allow dumps.


@@ -288,7 +295,7 @@ ETHTOOL_PHYS_ID n/a
ETHTOOL_GSTATS n/a
ETHTOOL_GTSO n/a
ETHTOOL_STSO n/a
-ETHTOOL_GPERMADDR n/a
+ETHTOOL_GPERMADDR ETHNL_CMD_GET_INFO
ETHTOOL_GUFO n/a
ETHTOOL_SUFO n/a
ETHTOOL_GGSO n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index fdae12b6c6b6..fb756b6a8592 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -153,8 +153,9 @@ enum {
};

#define ETH_INFO_IM_DRVINFO 0x01
+#define ETH_INFO_IM_PERMADDR 0x02

-#define ETH_INFO_IM_ALL 0x01
+#define ETH_INFO_IM_ALL 0x03

enum {
ETHA_DRVINFO_UNSPEC,
@@ -167,6 +168,15 @@ enum {
ETHA_DRVINFO_MAX = (__ETHA_DRVINFO_CNT - 1)
};

+enum {
+ ETHA_PERMADDR_UNSPEC,
+ ETHA_PERMADDR_ADDRESS, /* binary */
+ ETHA_PERMADDR_TYPE, /* u16 */
+
+ __ETHA_PERMADDR_CNT,
+ ETHA_PERMADDR_MAX = (__ETHA_PERMADDR_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/info.c b/net/ethtool/info.c
index 1a2e78d238e3..05dbd87ebc41 100644
--- a/net/ethtool/info.c
+++ b/net/ethtool/info.c
@@ -18,6 +18,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_PERMADDR] = { .type = NLA_REJECT },
};

static int parse_info(struct common_req_info *req_info, struct sk_buff *skb,
@@ -86,16 +87,29 @@ static int drvinfo_size(const struct ethtool_drvinfo *drvinfo)
return nla_total_size(len);
}

+static int permaddr_size(const struct net_device *dev)
+{
+ int len = 0;
+
+ len += nla_total_size(dev->addr_len);
+ len += nla_total_size(sizeof(u16));
+
+ return nla_total_size(len);
+}
+
static int info_size(const struct common_req_info *req_info)
{
const struct info_data *data =
container_of(req_info, struct info_data, reqinfo_base);
+ const struct net_device *dev = data->repdata_base.dev;
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);
+ if (info_mask & ETH_INFO_IM_PERMADDR)
+ len += permaddr_size(dev);

return len;
}
@@ -124,6 +138,26 @@ static int fill_drvinfo(struct sk_buff *skb,
return ret;
}

+static int fill_permaddr(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct nlattr *nest = ethnl_nest_start(skb, ETHA_INFO_PERMADDR);
+ int ret;
+
+ if (!nest)
+ return -EMSGSIZE;
+ ret = -EMSGSIZE;
+ if (nla_put(skb, ETHA_PERMADDR_ADDRESS, dev->addr_len, dev->perm_addr))
+ goto err;
+ if (nla_put_u16(skb, ETHA_PERMADDR_TYPE, dev->type))
+ goto err;
+
+ nla_nest_end(skb, nest);
+ return 0;
+err:
+ nla_nest_cancel(skb, nest);
+ return ret;
+}
+
static int fill_info(struct sk_buff *skb,
const struct common_req_info *req_info)
{
@@ -137,6 +171,11 @@ static int fill_info(struct sk_buff *skb,
if (ret < 0)
return ret;
}
+ if (info_mask & ETH_INFO_IM_PERMADDR) {
+ ret = fill_permaddr(skb, data->repdata_base.dev);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}
--
2.20.1


2019-02-18 19:16:47

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 13/21] 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 | 14 +-
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, 234 insertions(+), 20 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 1e615e111262..c6c7475340e2 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -242,6 +242,11 @@ Kernel response contents:
ETHA_INFO_PERMADDR (nested)
ETHA_PERMADDR_ADDRESS (binary) permanent HW address
ETHA_PERMADDR_TYPE (u16) dev->type
+ 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
@@ -253,6 +258,9 @@ There is no separate attribute for permanent address length as the length can
be determined from attribute length (nla_len()). ETHA_PERMADDR_TYPE provides
net_device::type value to give a hint about what kind of address device has.

+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.


@@ -328,7 +336,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 9ea278961d80..1b58637d3a4d 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 fb756b6a8592..8ab2b7454e81 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -154,8 +154,9 @@ enum {

#define ETH_INFO_IM_DRVINFO 0x01
#define ETH_INFO_IM_PERMADDR 0x02
+#define ETH_INFO_IM_TSINFO 0x04

-#define ETH_INFO_IM_ALL 0x03
+#define ETH_INFO_IM_ALL 0x07

enum {
ETHA_DRVINFO_UNSPEC,
@@ -177,6 +178,17 @@ enum {
ETHA_PERMADDR_MAX = (__ETHA_PERMADDR_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 e0bd7c6c5874..4616816861cc 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"

@@ -135,3 +137,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 05dbd87ebc41..838257db1d31 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] = {
@@ -19,6 +65,7 @@ static const struct nla_policy get_info_policy[ETHA_INFO_MAX + 1] = {
[ETHA_INFO_COMPACT] = { .type = NLA_FLAG },
[ETHA_INFO_DRVINFO] = { .type = NLA_REJECT },
[ETHA_INFO_PERMADDR] = { .type = NLA_REJECT },
+ [ETHA_INFO_TSINFO] = { .type = NLA_REJECT },
};

static int parse_info(struct common_req_info *req_info, struct sk_buff *skb,
@@ -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;
@@ -97,6 +149,42 @@ static int permaddr_size(const struct net_device *dev)
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);
+}
+
static int info_size(const struct common_req_info *req_info)
{
const struct info_data *data =
@@ -110,6 +198,13 @@ static int info_size(const struct common_req_info *req_info)
len += drvinfo_size(&data->drvinfo);
if (info_mask & ETH_INFO_IM_PERMADDR)
len += permaddr_size(dev);
+ 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;
}
@@ -158,6 +253,44 @@ static int fill_permaddr(struct sk_buff *skb, const struct net_device *dev)
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;
+}
+
static int fill_info(struct sk_buff *skb,
const struct common_req_info *req_info)
{
@@ -176,6 +309,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 c883239001a4..0837849156d3 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -2034,28 +2034,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 7141ec71a6d3..82a4c1f398d8 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 a7d0ec2865fb..5c74498d9c72 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.20.1


2019-02-18 19:17:02

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 15/21] 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 is divided into two parts: 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.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 49 +++-
include/linux/ethtool_netlink.h | 3 +
include/uapi/linux/ethtool_netlink.h | 37 +++
net/ethtool/Makefile | 2 +-
net/ethtool/common.c | 48 ++++
net/ethtool/common.h | 4 +
net/ethtool/ioctl.c | 48 ----
net/ethtool/netlink.c | 9 +
net/ethtool/settings.c | 265 +++++++++++++++++++
9 files changed, 414 insertions(+), 51 deletions(-)
create mode 100644 net/ethtool/settings.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index c6c7475340e2..0ea7d89c6052 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -130,6 +130,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.
@@ -264,6 +266,49 @@ 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_ksettings except link modes
+ ETH_SETTINGS_IM_LINKMODES link modes from link_ksettings
+
+Response contents:
+
+ ETHA_SETTINGS_DEV (nested) device identification
+ ETHA_SETTINGS_LINK_INFO (nested) link settings
+ ETHA_LINKINFO_SPEED (u32) link speed (Mb/s)
+ ETHA_LINKINFO_DUPLEX (u8) duplex mode
+ ETHA_LINKINFO_PORT (u8) physical port
+ ETHA_LINKINFO_PHYADDR (u8) MDIO address of phy
+ ETHA_LINKINFO_AUTONEG (u8) autoneotiation status
+ 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 (bitset) device link modes
+ ETHA_SETTINGS_PEER_MODES (bitset) link partner link modes
+
+Most of the attributes and their values have the same meaning as matching
+members of the corresponding ioctl structures. For ETHA_SETTINGS_LINK_MODES,
+value represents advertised modes and mask represents supported modes.
+ETHA_SETTINGS_PEER_MODES 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
-------------------

@@ -273,7 +318,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
@@ -347,7 +392,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 8ab2b7454e81..4e1fa4a06aac 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -12,6 +12,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)
@@ -189,6 +191,41 @@ 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, /* nested */
+ ETHA_SETTINGS_LINK_MODES, /* bitset */
+ ETHA_SETTINGS_PEER_MODES, /* bitset */
+
+ __ETHA_SETTINGS_CNT,
+ ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
+};
+
+#define ETH_SETTINGS_IM_LINKINFO 0x01
+#define ETH_SETTINGS_IM_LINKMODES 0x02
+
+#define ETH_SETTINGS_IM_ALL 0x03
+
+enum {
+ ETHA_LINKINFO_UNSPEC,
+ ETHA_LINKINFO_SPEED, /* u32 */
+ ETHA_LINKINFO_DUPLEX, /* u8 */
+ ETHA_LINKINFO_PORT, /* u8 */
+ ETHA_LINKINFO_PHYADDR, /* u8 */
+ ETHA_LINKINFO_AUTONEG, /* 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)
+};
+
/* 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 4616816861cc..3316e9e403c9 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -159,3 +159,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 0837849156d3..5e878cf6f418 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -356,54 +356,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)
*/
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 1ff6696ad716..5166b0c28288 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -143,10 +143,12 @@ int ethnl_fill_dev(struct sk_buff *msg, struct net_device *dev, u16 attrtype)

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;

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,
};

static struct common_req_info *alloc_get_data(const struct get_request_ops *ops)
@@ -572,6 +574,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/settings.c b/net/ethtool/settings.c
new file mode 100644
index 000000000000..4f2858fe1a7d
--- /dev/null
+++ b/net/ethtool/settings.c
@@ -0,0 +1,265 @@
+// 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 },
+ [ETHA_SETTINGS_PEER_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 = genlmsg_parse(nlhdr, &ethtool_genl_family, tb,
+ ETHA_SETTINGS_MAX, get_settings_policy,
+ info ? info->extack : NULL);
+ 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;
+
+ /* speed */
+ len += nla_total_size(sizeof(u32));
+ /* duplex, autoneg, port, phyaddr, mdix, mdixctrl, transcvr */
+ len += 7 * 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;
+
+ 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;
+
+ return 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_u32(skb, ETHA_LINKINFO_SPEED, lsettings->speed) ||
+ nla_put_u8(skb, ETHA_LINKINFO_DUPLEX, lsettings->duplex) ||
+ 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_AUTONEG,
+ lsettings->autoneg) ||
+ 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;
+ int ret;
+
+ ret = ethnl_put_bitset32(skb, ETHA_SETTINGS_LINK_MODES,
+ __ETHTOOL_LINK_MODE_MASK_NBITS, advertising,
+ supported, link_mode_names, flags);
+ if (ret < 0)
+ return ret;
+ if (!lpm_empty) {
+ ret = ethnl_put_bitset32(skb, ETHA_SETTINGS_PEER_MODES,
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+ lp_adv, NULL, link_mode_names,
+ flags | ETHNL_BITSET_LIST);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+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.20.1


2019-02-18 19:17:08

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 16/21] 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_WOLINFO 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.

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

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 0ea7d89c6052..913df35cb762 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -283,6 +283,7 @@ Info mask bits meaning:

ETH_SETTINGS_IM_LINKINFO link_ksettings except link modes
ETH_SETTINGS_IM_LINKMODES link modes from link_ksettings
+ ETH_SETTINGS_IM_WOLINFO struct ethtool_wolinfo

Response contents:

@@ -298,12 +299,22 @@ Response contents:
ETHA_LINKINFO_TRANSCEIVER (u8) transceiver
ETHA_SETTINGS_LINK_MODES (bitset) device link modes
ETHA_SETTINGS_PEER_MODES (bitset) link partner link modes
+ 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_SETTINGS_LINK_MODES,
value represents advertised modes and mask represents supported modes.
ETHA_SETTINGS_PEER_MODES 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_SETTINGS_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.
@@ -322,7 +333,7 @@ ETHTOOL_GSET ETHNL_CMD_GET_SETTINGS
ETHTOOL_SSET n/a
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 4e1fa4a06aac..ce9d6b48f814 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -201,6 +201,7 @@ enum {
ETHA_SETTINGS_LINK_INFO, /* nested */
ETHA_SETTINGS_LINK_MODES, /* bitset */
ETHA_SETTINGS_PEER_MODES, /* bitset */
+ ETHA_SETTINGS_WOL, /* nested */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -208,8 +209,9 @@ enum {

#define ETH_SETTINGS_IM_LINKINFO 0x01
#define ETH_SETTINGS_IM_LINKMODES 0x02
+#define ETH_SETTINGS_IM_WOLINFO 0x04

-#define ETH_SETTINGS_IM_ALL 0x03
+#define ETH_SETTINGS_IM_ALL 0x07

enum {
ETHA_LINKINFO_UNSPEC,
@@ -226,6 +228,15 @@ enum {
ETHA_LINKINFO_MAX = (__ETHA_LINKINFO_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 3316e9e403c9..9fba57f554dd 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -207,3 +207,13 @@ convert_legacy_settings_to_link_ksettings(
= legacy_settings->eth_tp_mdix_ctrl;
return retval;
}
+
+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 7a3e0b10e69a..ed3f1ca54660 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_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 5e878cf6f418..945eaf551a4c 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1230,11 +1230,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;
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 4f2858fe1a7d..d296625edb2b 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -6,11 +6,13 @@

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_link_settings *lsettings;
+ struct ethtool_wolinfo wolinfo;
bool lpm_empty;
};

@@ -22,15 +24,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_PEER_MODES] = { .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 = genlmsg_parse(nlhdr, &ethtool_genl_family, tb,
ETHA_SETTINGS_MAX, get_settings_policy,
info ? info->extack : NULL);
@@ -68,6 +75,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)
{
@@ -100,6 +117,11 @@ 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_WOLINFO) {
+ ret = ethnl_get_wol(info, dev, &data->wolinfo);
+ if (ret < 0)
+ req_mask &= ~ETH_SETTINGS_IM_WOLINFO;
+ }
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -147,6 +169,12 @@ static int link_modes_size(const struct ethtool_link_ksettings *ksettings,
return len;
}

+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
@@ -168,6 +196,8 @@ static int settings_size(const struct common_req_info *req_info)
return ret;
len += ret;
}
+ if (info_mask & ETH_SETTINGS_IM_WOLINFO)
+ len += wol_size();

return len;
}
@@ -227,6 +257,32 @@ static int fill_link_modes(struct sk_buff *skb,
return 0;
}

+static int fill_wolinfo(struct sk_buff *skb,
+ const struct ethtool_wolinfo *wolinfo, bool privileged)
+{
+ struct nlattr *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)
{
@@ -247,6 +303,11 @@ static int fill_settings(struct sk_buff *skb,
if (ret < 0)
return ret;
}
+ if (info_mask & ETH_SETTINGS_IM_WOLINFO) {
+ ret = fill_wolinfo(skb, &data->wolinfo, data->privileged);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}
--
2.20.1


2019-02-18 19:17:15

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 17/21] 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_MSGLEVEL flag is set in the
request.

Signed-off-by: Michal Kubecek <[email protected]>
---
Documentation/networking/ethtool-netlink.txt | 7 ++++++-
include/linux/netdevice.h | 2 ++
include/uapi/linux/ethtool_netlink.h | 4 +++-
net/ethtool/settings.c | 16 ++++++++++++++++
4 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 913df35cb762..057eb3213cfe 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -284,6 +284,7 @@ Info mask bits meaning:
ETH_SETTINGS_IM_LINKINFO link_ksettings except link modes
ETH_SETTINGS_IM_LINKMODES link modes from link_ksettings
ETH_SETTINGS_IM_WOLINFO struct ethtool_wolinfo
+ ETH_SETTINGS_IM_MSGLEVEL msglevel

Response contents:

@@ -302,6 +303,7 @@ 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_MSGLEVEL (bitfield32) debug level

Most of the attributes and their values have the same meaning as matching
members of the corresponding ioctl structures. For ETHA_SETTINGS_LINK_MODES,
@@ -311,6 +313,9 @@ ETHA_SETTINGS_PEER_MODES 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.

+For ETHA_SETTINGS_MSGLEVEL, selector reports all flags supported by kernel and
+value flags enabled for the device.
+
GET_SETTINGS request is allowed for unprivileged user but ETHA_SETTINGS_SOPASS
is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
requests.
@@ -335,7 +340,7 @@ ETHTOOL_GDRVINFO ETHNL_CMD_GET_INFO
ETHTOOL_GREGS n/a
ETHTOOL_GWOL ETHNL_CMD_GET_SETTINGS
ETHTOOL_SWOL n/a
-ETHTOOL_GMSGLVL n/a
+ETHTOOL_GMSGLVL ETHNL_CMD_GET_SETTINGS
ETHTOOL_SMSGLVL n/a
ETHTOOL_NWAY_RST n/a
ETHTOOL_GLINK n/a
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9a50a67f328f..a2ddfeb381bb 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3846,6 +3846,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 ce9d6b48f814..360e20a73f19 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -202,6 +202,7 @@ enum {
ETHA_SETTINGS_LINK_MODES, /* bitset */
ETHA_SETTINGS_PEER_MODES, /* bitset */
ETHA_SETTINGS_WOL, /* nested */
+ ETHA_SETTINGS_MSGLEVEL, /* bitfield32 */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -210,8 +211,9 @@ enum {
#define ETH_SETTINGS_IM_LINKINFO 0x01
#define ETH_SETTINGS_IM_LINKMODES 0x02
#define ETH_SETTINGS_IM_WOLINFO 0x04
+#define ETH_SETTINGS_IM_MSGLEVEL 0x08

-#define ETH_SETTINGS_IM_ALL 0x07
+#define ETH_SETTINGS_IM_ALL 0x0f

enum {
ETHA_LINKINFO_UNSPEC,
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index d296625edb2b..58cd2d19a75d 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -13,6 +13,7 @@ struct settings_data {
struct ethtool_link_ksettings ksettings;
struct ethtool_link_settings *lsettings;
struct ethtool_wolinfo wolinfo;
+ u32 msglevel;
bool lpm_empty;
};

@@ -25,6 +26,7 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_LINK_MODES] = { .type = NLA_REJECT },
[ETHA_SETTINGS_PEER_MODES] = { .type = NLA_REJECT },
[ETHA_SETTINGS_WOL] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_MSGLEVEL] = { .type = NLA_REJECT },
};

static int parse_settings(struct common_req_info *req_info,
@@ -91,6 +93,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;

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

data->repdata_base.info_mask = req_mask;
@@ -198,6 +207,8 @@ static int settings_size(const struct common_req_info *req_info)
}
if (info_mask & ETH_SETTINGS_IM_WOLINFO)
len += wol_size();
+ if (info_mask & ETH_SETTINGS_IM_MSGLEVEL)
+ len += nla_total_size(sizeof(struct nla_bitfield32));

return len;
}
@@ -308,6 +319,11 @@ static int fill_settings(struct sk_buff *skb,
if (ret < 0)
return ret;
}
+ if (info_mask & ETH_SETTINGS_IM_MSGLEVEL) {
+ if (nla_put_bitfield32(skb, ETHA_SETTINGS_MSGLEVEL,
+ data->msglevel, NETIF_MSG_ALL))
+ return -EMSGSIZE;
+ }

return 0;
}
--
2.20.1


2019-02-18 19:17:17

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 18/21] 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_LINK flag is set
in the request.

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

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

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 057eb3213cfe..538dad93009f 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -285,6 +285,7 @@ Info mask bits meaning:
ETH_SETTINGS_IM_LINKMODES link modes from link_ksettings
ETH_SETTINGS_IM_WOLINFO struct ethtool_wolinfo
ETH_SETTINGS_IM_MSGLEVEL msglevel
+ ETH_SETTINGS_IM_LINK link state

Response contents:

@@ -304,6 +305,7 @@ Response contents:
ETHA_WOL_MODES (bitfield32) wake on LAN modes
ETHA_WOL_SOPASS (binary) SecureOn(tm) password
ETHA_SETTINGS_MSGLEVEL (bitfield32) debug level
+ ETHA_SETTINGS_LINK (u8) link state

Most of the attributes and their values have the same meaning as matching
members of the corresponding ioctl structures. For ETHA_SETTINGS_LINK_MODES,
@@ -343,7 +345,7 @@ ETHTOOL_SWOL n/a
ETHTOOL_GMSGLVL ETHNL_CMD_GET_SETTINGS
ETHTOOL_SMSGLVL n/a
ETHTOOL_NWAY_RST n/a
-ETHTOOL_GLINK n/a
+ETHTOOL_GLINK ETHNL_CMD_GET_SETTINGS
ETHTOOL_GEEPROM n/a
ETHTOOL_SEEPROM n/a
ETHTOOL_GCOALESCE n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 360e20a73f19..06e78d94cacc 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -203,6 +203,7 @@ enum {
ETHA_SETTINGS_PEER_MODES, /* bitset */
ETHA_SETTINGS_WOL, /* nested */
ETHA_SETTINGS_MSGLEVEL, /* bitfield32 */
+ ETHA_SETTINGS_LINK, /* u8 */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -212,8 +213,9 @@ enum {
#define ETH_SETTINGS_IM_LINKMODES 0x02
#define ETH_SETTINGS_IM_WOLINFO 0x04
#define ETH_SETTINGS_IM_MSGLEVEL 0x08
+#define ETH_SETTINGS_IM_LINK 0x10

-#define ETH_SETTINGS_IM_ALL 0x0f
+#define ETH_SETTINGS_IM_ALL 0x1f

enum {
ETHA_LINKINFO_UNSPEC,
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 9fba57f554dd..a188f07bcb4c 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -217,3 +217,11 @@ int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)

return 0;
}
+
+int __ethtool_get_link(struct net_device *dev)
+{
+ if (!dev->ethtool_ops->get_link)
+ return -EOPNOTSUPP;
+
+ return netif_running(dev) && dev->ethtool_ops->get_link(dev);
+}
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index ed3f1ca54660..e5b5c5c2a4b9 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -18,6 +18,7 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info);
int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
+int __ethtool_get_link(struct net_device *dev);

bool convert_legacy_settings_to_link_ksettings(
struct ethtool_link_ksettings *link_ksettings,
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 945eaf551a4c..63662a3fa2ae 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1306,12 +1306,12 @@ static int ethtool_nway_reset(struct net_device *dev)
static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
{
struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
+ int link = __ethtool_get_link(dev);

- if (!dev->ethtool_ops->get_link)
- return -EOPNOTSUPP;
-
- edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev);
+ if (link < 0)
+ return link;

+ edata.data = link;
if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 58cd2d19a75d..099300901c12 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -14,6 +14,7 @@ struct settings_data {
struct ethtool_link_settings *lsettings;
struct ethtool_wolinfo wolinfo;
u32 msglevel;
+ int link;
bool lpm_empty;
};

@@ -27,6 +28,7 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_PEER_MODES] = { .type = NLA_REJECT },
[ETHA_SETTINGS_WOL] = { .type = NLA_REJECT },
[ETHA_SETTINGS_MSGLEVEL] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_LINK] = { .type = NLA_REJECT },
};

static int parse_settings(struct common_req_info *req_info,
@@ -99,6 +101,7 @@ static int prepare_settings(struct common_req_info *req_info,

data->lsettings = &data->ksettings.base;
data->lpm_empty = true;
+ data->link = -EOPNOTSUPP;

ret = ethnl_before_ops(dev);
if (ret < 0)
@@ -131,6 +134,8 @@ static int prepare_settings(struct common_req_info *req_info,
else
req_mask &= ~ETH_SETTINGS_IM_MSGLEVEL;
}
+ if (req_mask & ETH_SETTINGS_IM_LINK)
+ data->link = __ethtool_get_link(dev);
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -209,6 +214,8 @@ static int settings_size(const struct common_req_info *req_info)
len += wol_size();
if (info_mask & ETH_SETTINGS_IM_MSGLEVEL)
len += nla_total_size(sizeof(struct nla_bitfield32));
+ if (info_mask & ETH_SETTINGS_IM_LINK)
+ len += nla_total_size(sizeof(u32));

return len;
}
@@ -324,6 +331,10 @@ static int fill_settings(struct sk_buff *skb,
data->msglevel, NETIF_MSG_ALL))
return -EMSGSIZE;
}
+ if (info_mask & ETH_SETTINGS_IM_LINK && data->link >= 0) {
+ if (nla_put_u8(skb, ETHA_SETTINGS_LINK, data->link))
+ return -EMSGSIZE;
+ }

return 0;
}
--
2.20.1


2019-02-18 19:17:18

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 03/21] 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 | 167 +++++++++++++++++++
include/linux/ethtool_netlink.h | 9 +
include/uapi/linux/ethtool_netlink.h | 19 +++
net/Kconfig | 8 +
net/ethtool/Makefile | 6 +-
net/ethtool/netlink.c | 34 ++++
net/ethtool/netlink.h | 12 ++
7 files changed, 254 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..09c36e0392a8
--- /dev/null
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -0,0 +1,167 @@
+ 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 n/a
+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..d8a8d3c72c2f
--- /dev/null
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#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 62da6148e9f8..383fe04ddf07 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -460,6 +460,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..5fe3e5b68e81
--- /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 (ret < 0)
+ panic("ethtool: could not register genetlink family\n");
+
+ 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.20.1


2019-02-18 19:17:23

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 14/21] 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/uapi/linux/ethtool.h | 2 ++
net/ethtool/netlink.c | 58 ++++++++++++++++++++++++++++++++++++
net/ethtool/netlink.h | 1 +
net/ethtool/strset.c | 6 ++++
4 files changed, 67 insertions(+)

diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 1b58637d3a4d..ba96a691bfd4 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 e27dec427414..1ff6696ad716 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -6,6 +6,61 @@

u32 ethnl_bcast_seq;

+const char *const link_mode_names[] = {
+ [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = "10baseT/Half",
+ [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = "10baseT/Full",
+ [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = "100baseT/Half",
+ [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = "100baseT/Full",
+ [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = "1000baseT/Half",
+ [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = "1000baseT/Full",
+ [ETHTOOL_LINK_MODE_Autoneg_BIT] = "Autoneg",
+ [ETHTOOL_LINK_MODE_TP_BIT] = "TP",
+ [ETHTOOL_LINK_MODE_AUI_BIT] = "AUI",
+ [ETHTOOL_LINK_MODE_MII_BIT] = "MII",
+ [ETHTOOL_LINK_MODE_FIBRE_BIT] = "FIBRE",
+ [ETHTOOL_LINK_MODE_BNC_BIT] = "BNC",
+ [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = "10000baseT/Full",
+ [ETHTOOL_LINK_MODE_Pause_BIT] = "Pause",
+ [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = "Asym_Pause",
+ [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = "2500baseX/Full",
+ [ETHTOOL_LINK_MODE_Backplane_BIT] = "Backplane",
+ [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = "1000baseKX/Full",
+ [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = "10000baseKX4/Full",
+ [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = "10000baseKR/Full",
+ [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baseR/FEC",
+ [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = "20000baseMLD2/Full",
+ [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = "20000baseKR2/Full",
+ [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = "40000baseKR4/Full",
+ [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = "40000baseCR4/Full",
+ [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = "40000baseSR4/Full",
+ [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = "40000baseLR4/Full",
+ [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = "56000baseKR4/Full",
+ [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = "56000baseCR4/Full",
+ [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = "56000baseSR4/Full",
+ [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = "56000baseLR4/Full",
+ [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = "25000baseCR/Full",
+ [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = "25000baseKR/Full",
+ [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = "25000baseSR/Full",
+ [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = "50000baseCR2/Full",
+ [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = "50000baseKR2/Full",
+ [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = "100000baseKR4/Full",
+ [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = "100000baseSR4/Full",
+ [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = "100000baseCR4/Full",
+ [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = "100000baseLR4/ER4_Full",
+ [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = "50000baseSR2/Full",
+ [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = "1000baseX/Full",
+ [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = "10000baseCR/Full",
+ [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = "10000baseSR/Full",
+ [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = "10000baseLR/Full",
+ [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = "10000baseLRM/Full",
+ [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = "10000baseER/Full",
+ [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = "2500baseT/Full",
+ [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = "5000baseT/Full",
+ [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = "None",
+ [ETHTOOL_LINK_MODE_FEC_RS_BIT] = "RS",
+ [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "BASER",
+};
+
static const struct nla_policy dev_policy[ETHA_DEV_MAX + 1] = {
[ETHA_DEV_UNSPEC] = { .type = NLA_REJECT },
[ETHA_DEV_INDEX] = { .type = NLA_U32 },
@@ -541,6 +596,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 (ret < 0)
panic("ethtool: could not register genetlink family\n");
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 82a4c1f398d8..800ea57ab0de 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 5c74498d9c72..dd87d7db8b61 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.20.1


2019-02-18 19:17:25

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 04/21] 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 | 119 +++++++++++++++++++++++
net/ethtool/netlink.h | 135 +++++++++++++++++++++++++++
3 files changed, 265 insertions(+)

diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index d8a8d3c72c2f..01896a1ee21a 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -12,6 +12,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 5fe3e5b68e81..ffdcc7c9d4bc 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -1,8 +1,127 @@
// 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 },
+};
+
+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(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;
+}
+
+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;
+}
+
+/* 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: info for the received packet we respond to
+ * ehdrp: place to store payload pointer returned by genlmsg_new()
+ * returns: skb or 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)
+{
+ void *ehdr;
+ struct sk_buff *rskb;
+
+ 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..36a397656127 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -6,7 +6,142 @@
#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);
+}
+
+/* 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.20.1


2019-02-18 19:17:30

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 05/21] 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 | 572 +++++++++++++++++++
net/ethtool/bitset.h | 40 ++
net/ethtool/netlink.h | 9 +
6 files changed, 717 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 09c36e0392a8..205ae4462e9e 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -59,6 +59,69 @@ 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).

+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 01896a1ee21a..7f3d401977b4 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -23,6 +23,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..8f5c60999a96
--- /dev/null
+++ b/net/ethtool/bitset.c
@@ -0,0 +1,572 @@
+// 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);
+}
+
+/* put bitset into a message
+ * skb: skb with the message
+ * attrtype: attribute type for the bitset
+ * 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
+ */
+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(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(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);
+}
+
+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(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 (!tb[ETHA_BITSET_VALUE])
+ goto out_free;
+ 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 36a397656127..ba638eb5a99a 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.20.1


2019-02-18 19:17:40

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 19/21] ethtool: provide device features in GET_SETTINGS request

Add information about network device features (as provided by
ETHTOOL_GFEATURES ioctl command) in GET_SETTINGS reply when
ETH_SETTINGS_IM_FEATURES flag is set in the request.

This request also provides information provided by ETHTOOL_GRXCSUM,
ETHTOOL_GTXCSUM, ETHTOOL_GSG, ETHTOOL_GTSO, ETHTOOL_GUFO, ETHTOOL_GGSO,
ETHTOOL_GFLAGS and ETHTOOL_GGRO ioctl commands.

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

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 538dad93009f..664c922a05eb 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -286,6 +286,7 @@ Info mask bits meaning:
ETH_SETTINGS_IM_WOLINFO struct ethtool_wolinfo
ETH_SETTINGS_IM_MSGLEVEL msglevel
ETH_SETTINGS_IM_LINK link state
+ ETH_SETTINGS_IM_FEATURES features

Response contents:

@@ -306,6 +307,11 @@ Response contents:
ETHA_WOL_SOPASS (binary) SecureOn(tm) password
ETHA_SETTINGS_MSGLEVEL (bitfield32) debug level
ETHA_SETTINGS_LINK (u8) link state
+ ETHA_SETTINGS_FEATURES (nested) device features
+ ETHA_FEATURES_HW (bitset) dev->hw_features
+ ETHA_FEATURES_WANTED (bitset) dev->wanted_features
+ ETHA_FEATURES_ACTIVE (bitset) dev->features
+ ETHA_FEATURES_NOCHANGE (bitset) NETIF_F_NEVER_CHANGE

Most of the attributes and their values have the same meaning as matching
members of the corresponding ioctl structures. For ETHA_SETTINGS_LINK_MODES,
@@ -318,13 +324,23 @@ device and value enabled modes.
For ETHA_SETTINGS_MSGLEVEL, selector reports all flags supported by kernel and
value flags enabled for the device.

+Bitmaps contained in ETHA_SETTINGS_FEATURES have the same meaning as bitmaps
+used in ioctl interference but attribute names are different (they are based
+on corresponding members of struct net_device). Legacy "flags" are not
+provided, if userspace needs them (most likely only ethtool for backward
+compatibility), it can calculate their values from related feature bits
+itself. ETHA_FEATURES_HW uses mask consisting of all features recognized by
+kernel (to provide all names when using verbose bitmap format), remaining
+three use mask equal to value (to save space).
+
GET_SETTINGS request is allowed for unprivileged user but ETHA_SETTINGS_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.
+netlink or ioctl ethtool interface; feature notifications are also sent
+whenever netdev_update_features() or netdev_change_features() is called.


Request translation
@@ -354,30 +370,30 @@ ETHTOOL_GRINGPARAM n/a
ETHTOOL_SRINGPARAM n/a
ETHTOOL_GPAUSEPARAM n/a
ETHTOOL_SPAUSEPARAM n/a
-ETHTOOL_GRXCSUM n/a
+ETHTOOL_GRXCSUM ETHNL_CMD_GET_SETTINGS
ETHTOOL_SRXCSUM n/a
-ETHTOOL_GTXCSUM n/a
+ETHTOOL_GTXCSUM ETHNL_CMD_GET_SETTINGS
ETHTOOL_STXCSUM n/a
-ETHTOOL_GSG n/a
+ETHTOOL_GSG ETHNL_CMD_GET_SETTINGS
ETHTOOL_SSG n/a
ETHTOOL_TEST n/a
ETHTOOL_GSTRINGS ETHNL_CMD_GET_STRSET
ETHTOOL_PHYS_ID n/a
ETHTOOL_GSTATS n/a
-ETHTOOL_GTSO n/a
+ETHTOOL_GTSO ETHNL_CMD_GET_SETTINGS
ETHTOOL_STSO n/a
ETHTOOL_GPERMADDR ETHNL_CMD_GET_INFO
-ETHTOOL_GUFO n/a
+ETHTOOL_GUFO ETHNL_CMD_GET_SETTINGS
ETHTOOL_SUFO n/a
-ETHTOOL_GGSO n/a
+ETHTOOL_GGSO ETHNL_CMD_GET_SETTINGS
ETHTOOL_SGSO n/a
-ETHTOOL_GFLAGS n/a
+ETHTOOL_GFLAGS ETHNL_CMD_GET_SETTINGS
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_GGRO ETHNL_CMD_GET_SETTINGS
ETHTOOL_SGRO n/a
ETHTOOL_GRXRINGS n/a
ETHTOOL_GRXCLSRLCNT n/a
@@ -392,7 +408,7 @@ ETHTOOL_GRXNTUPLE n/a
ETHTOOL_GSSET_INFO ETHNL_CMD_GET_STRSET
ETHTOOL_GRXFHINDIR n/a
ETHTOOL_SRXFHINDIR n/a
-ETHTOOL_GFEATURES n/a
+ETHTOOL_GFEATURES ETHNL_CMD_GET_SETTINGS
ETHTOOL_SFEATURES n/a
ETHTOOL_GCHANNELS n/a
ETHTOOL_SCHANNELS n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 06e78d94cacc..154d7e6a59dd 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -204,6 +204,7 @@ enum {
ETHA_SETTINGS_WOL, /* nested */
ETHA_SETTINGS_MSGLEVEL, /* bitfield32 */
ETHA_SETTINGS_LINK, /* u8 */
+ ETHA_SETTINGS_FEATURES, /* nest - ETHA_FEATURES_* */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -214,8 +215,20 @@ enum {
#define ETH_SETTINGS_IM_WOLINFO 0x04
#define ETH_SETTINGS_IM_MSGLEVEL 0x08
#define ETH_SETTINGS_IM_LINK 0x10
+#define ETH_SETTINGS_IM_FEATURES 0x20

-#define ETH_SETTINGS_IM_ALL 0x1f
+#define ETH_SETTINGS_IM_ALL 0x3f
+
+enum {
+ ETHA_FEATURES_UNSPEC,
+ ETHA_FEATURES_HW, /* bitset */
+ ETHA_FEATURES_WANTED, /* bitset */
+ ETHA_FEATURES_ACTIVE, /* bitset */
+ ETHA_FEATURES_NOCHANGE, /* bitset */
+
+ __ETHA_FEATURES_CNT,
+ ETHA_FEATURES_MAX = (__ETHA_FEATURES_CNT - 1)
+};

enum {
ETHA_LINKINFO_UNSPEC,
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index e5b5c5c2a4b9..cf0b81af2d9f 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -6,6 +6,8 @@
#include <linux/netdevice.h>
#include <linux/ethtool.h>

+#define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32)
+
extern const char
netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN];
extern const char
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 63662a3fa2ae..6c3b492a88fe 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -58,8 +58,6 @@ EXPORT_SYMBOL(ethtool_op_get_ts_info);

/* Handlers for each ethtool command */

-#define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32)
-
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
{
struct ethtool_gfeatures cmd = {
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 099300901c12..32ee273de879 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -16,6 +16,12 @@ struct settings_data {
u32 msglevel;
int link;
bool lpm_empty;
+ struct {
+ u32 hw[ETHTOOL_DEV_FEATURE_WORDS];
+ u32 wanted[ETHTOOL_DEV_FEATURE_WORDS];
+ u32 active[ETHTOOL_DEV_FEATURE_WORDS];
+ u32 nochange[ETHTOOL_DEV_FEATURE_WORDS];
+ } features;
};

static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
@@ -29,6 +35,7 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_WOL] = { .type = NLA_REJECT },
[ETHA_SETTINGS_MSGLEVEL] = { .type = NLA_REJECT },
[ETHA_SETTINGS_LINK] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_FEATURES] = { .type = NLA_REJECT },
};

static int parse_settings(struct common_req_info *req_info,
@@ -89,6 +96,24 @@ static int ethnl_get_wol(struct genl_info *info, struct net_device *dev,
return ret;
}

+static void features_to_bitmap(u32 *dest, netdev_features_t src)
+{
+ unsigned int i;
+
+ for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++)
+ dest[i] = (u32)(src >> (32 * i));
+}
+
+static int ethnl_get_features(struct net_device *dev,
+ struct settings_data *data)
+{
+ features_to_bitmap(data->features.hw, dev->hw_features);
+ features_to_bitmap(data->features.wanted, dev->wanted_features);
+ features_to_bitmap(data->features.active, dev->features);
+ features_to_bitmap(data->features.nochange, NETIF_F_NEVER_CHANGE);
+ return 0;
+}
+
static int prepare_settings(struct common_req_info *req_info,
struct genl_info *info)
{
@@ -136,6 +161,8 @@ static int prepare_settings(struct common_req_info *req_info,
}
if (req_mask & ETH_SETTINGS_IM_LINK)
data->link = __ethtool_get_link(dev);
+ if (req_mask & ETH_SETTINGS_IM_FEATURES)
+ ethnl_get_features(dev, data);
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -189,6 +216,38 @@ static int wol_size(void)
nla_total_size(SOPASS_MAX));
}

+static int features_size(const struct settings_data *data)
+{
+ unsigned int flags =
+ (data->reqinfo_base.compact ? ETHNL_BITSET_COMPACT : 0) |
+ ETHNL_BITSET_LEGACY_NAMES;
+ int len = 0, ret;
+
+ ret = ethnl_bitset32_size(NETDEV_FEATURE_COUNT, data->features.hw,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ flags |= ETHNL_BITSET_LIST;
+ ret = ethnl_bitset32_size(NETDEV_FEATURE_COUNT, data->features.wanted,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ ret = ethnl_bitset32_size(NETDEV_FEATURE_COUNT, data->features.active,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ ret = ethnl_bitset32_size(NETDEV_FEATURE_COUNT, data->features.nochange,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+
+ return 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
@@ -216,6 +275,12 @@ static int settings_size(const struct common_req_info *req_info)
len += nla_total_size(sizeof(struct nla_bitfield32));
if (info_mask & ETH_SETTINGS_IM_LINK)
len += nla_total_size(sizeof(u32));
+ if (info_mask & ETH_SETTINGS_IM_FEATURES) {
+ ret = features_size(data);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }

return len;
}
@@ -301,6 +366,44 @@ static int fill_wolinfo(struct sk_buff *skb,
return -EMSGSIZE;
}

+static int fill_features(struct sk_buff *skb, const struct settings_data *data)
+{
+ unsigned int flags =
+ (data->reqinfo_base.compact ? ETHNL_BITSET_COMPACT : 0) |
+ ETHNL_BITSET_LEGACY_NAMES;
+ struct nlattr *feat_attr;
+ int ret;
+
+ feat_attr = ethnl_nest_start(skb, ETHA_SETTINGS_FEATURES);
+ if (!feat_attr)
+ return -EMSGSIZE;
+
+ ret = ethnl_put_bitset32(skb, ETHA_FEATURES_HW, NETDEV_FEATURE_COUNT,
+ data->features.hw, NULL,
+ netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ flags |= ETHNL_BITSET_LIST;
+ ret = ethnl_put_bitset32(skb, ETHA_FEATURES_WANTED,
+ NETDEV_FEATURE_COUNT, data->features.wanted,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ ret = ethnl_put_bitset32(skb, ETHA_FEATURES_ACTIVE,
+ NETDEV_FEATURE_COUNT, data->features.active,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+ ret = ethnl_put_bitset32(skb, ETHA_FEATURES_NOCHANGE,
+ NETDEV_FEATURE_COUNT, data->features.nochange,
+ NULL, netdev_features_strings, flags);
+ if (ret < 0)
+ return ret;
+
+ nla_nest_end(skb, feat_attr);
+ return 0;
+}
+
static int fill_settings(struct sk_buff *skb,
const struct common_req_info *req_info)
{
@@ -335,6 +438,11 @@ static int fill_settings(struct sk_buff *skb,
if (nla_put_u8(skb, ETHA_SETTINGS_LINK, data->link))
return -EMSGSIZE;
}
+ if (info_mask & ETH_SETTINGS_IM_FEATURES) {
+ ret = fill_features(skb, data);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}
--
2.20.1


2019-02-18 19:17:49

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 11/21] 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 | 32 ++++
net/ethtool/Makefile | 2 +-
net/ethtool/common.c | 54 +++++++
net/ethtool/common.h | 2 +
net/ethtool/info.c | 155 +++++++++++++++++++
net/ethtool/ioctl.c | 52 +------
net/ethtool/netlink.c | 9 ++
8 files changed, 300 insertions(+), 49 deletions(-)
create mode 100644 net/ethtool/info.c

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

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


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

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

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

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

obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o

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

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

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

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

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

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

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

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

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

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

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

static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
--
2.20.1


2019-02-18 19:18:19

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 21/21] ethtool: send netlink notifications about setting changes

SET_SETTINGS notification message has the same format as response to
GET_SETTINGS request and is broadcasted on change of relevant fields. Info
mask can be used to limit the information passed to userspace.

Send the notification on changes performed via the legacy ioctl interface;
feature notifications are also sent whenever netdev_update_features() or
netdev_change_features() is called, even if the change was not performed
using the ethtool interface.

Signed-off-by: Michal Kubecek <[email protected]>
---
net/ethtool/ioctl.c | 24 ++++++++++++++++++++++--
net/ethtool/netlink.c | 12 ++++++++++++
2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 6c3b492a88fe..17ccca14f5c3 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"

@@ -567,7 +568,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.
@@ -616,6 +622,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();

@@ -628,7 +635,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,
@@ -1255,6 +1267,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_WOLINFO, NULL);

return 0;
}
@@ -2453,6 +2467,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_MSGLEVEL, NULL);
break;
case ETHTOOL_GEEE:
rc = ethtool_get_eee(dev, useraddr);
@@ -2519,6 +2536,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_SPFLAGS:
rc = ethtool_set_value(dev, useraddr,
dev->ethtool_ops->set_priv_flags);
+ if (rc == 0)
+ ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_PRIVFLAGS, NULL);
break;
case ETHTOOL_GRXFH:
case ETHTOOL_GRXRINGS:
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 5166b0c28288..6f94aec6fa69 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -480,6 +480,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,
@@ -534,6 +535,14 @@ static void ethnl_notify_devlist(struct netdev_notifier_info *info,
nlmsg_free(skb);
}

+static void ethnl_notify_features(struct netdev_notifier_info *info)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(info);
+
+ ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS,
+ ETH_SETTINGS_IM_FEATURES, NULL);
+}
+
static int ethnl_netdev_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
@@ -548,6 +557,9 @@ static int ethnl_netdev_event(struct notifier_block *this, unsigned long event,
ethnl_notify_devlist(ptr, ETHA_EVENT_RENAMEDEV,
ETHA_RENAMEDEV_DEV);
break;
+ case NETDEV_FEAT_CHANGE:
+ ethnl_notify_features(ptr);
+ break;
}

return NOTIFY_DONE;
--
2.20.1


2019-02-18 19:18:54

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 20/21] ethtool: provide private flags in GET_SETTINGS request

Add information about device private flags (as provided by
ETHTOOL_GPFLAGS ioctl command) in GET_SETTINGS reply when
ETH_SETTINGS_IM_PRIVFLAGS flag is set in the request.

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

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 664c922a05eb..290008aaed0a 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -287,6 +287,7 @@ Info mask bits meaning:
ETH_SETTINGS_IM_MSGLEVEL msglevel
ETH_SETTINGS_IM_LINK link state
ETH_SETTINGS_IM_FEATURES features
+ ETH_SETTINGS_IM_PRIVFLAGS device private flags

Response contents:

@@ -312,6 +313,7 @@ Response contents:
ETHA_FEATURES_WANTED (bitset) dev->wanted_features
ETHA_FEATURES_ACTIVE (bitset) dev->features
ETHA_FEATURES_NOCHANGE (bitset) NETIF_F_NEVER_CHANGE
+ ETHA_SETTINGS_PRIV_FLAGS (bitset) device private flags

Most of the attributes and their values have the same meaning as matching
members of the corresponding ioctl structures. For ETHA_SETTINGS_LINK_MODES,
@@ -333,6 +335,11 @@ itself. ETHA_FEATURES_HW uses mask consisting of all features recognized by
kernel (to provide all names when using verbose bitmap format), remaining
three use mask equal to value (to save space).

+ETHA_SETTINGS_PRIV_FLAGS is a bitset with values of device private flags.
+These flags are defined by driver, their number and names (as well as meaning)
+are device dependent. For compact bitset format, names can be retrieved as
+ETH_SS_PRIV_FLAGS string set.
+
GET_SETTINGS request is allowed for unprivileged user but ETHA_SETTINGS_SOPASS
is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
requests.
@@ -389,7 +396,7 @@ ETHTOOL_GGSO ETHNL_CMD_GET_SETTINGS
ETHTOOL_SGSO n/a
ETHTOOL_GFLAGS ETHNL_CMD_GET_SETTINGS
ETHTOOL_SFLAGS n/a
-ETHTOOL_GPFLAGS n/a
+ETHTOOL_GPFLAGS ETHNL_CMD_GET_SETTINGS
ETHTOOL_SPFLAGS n/a
ETHTOOL_GRXFH n/a
ETHTOOL_SRXFH n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 154d7e6a59dd..afd61d36bcf8 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -205,6 +205,7 @@ enum {
ETHA_SETTINGS_MSGLEVEL, /* bitfield32 */
ETHA_SETTINGS_LINK, /* u8 */
ETHA_SETTINGS_FEATURES, /* nest - ETHA_FEATURES_* */
+ ETHA_SETTINGS_PRIV_FLAGS, /* nest - ETHA_BITSET_* */

__ETHA_SETTINGS_CNT,
ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -216,8 +217,9 @@ enum {
#define ETH_SETTINGS_IM_MSGLEVEL 0x08
#define ETH_SETTINGS_IM_LINK 0x10
#define ETH_SETTINGS_IM_FEATURES 0x20
+#define ETH_SETTINGS_IM_PRIVFLAGS 0x40

-#define ETH_SETTINGS_IM_ALL 0x3f
+#define ETH_SETTINGS_IM_ALL 0x7f

enum {
ETHA_FEATURES_UNSPEC,
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 32ee273de879..7f72f4250306 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -22,6 +22,9 @@ struct settings_data {
u32 active[ETHTOOL_DEV_FEATURE_WORDS];
u32 nochange[ETHTOOL_DEV_FEATURE_WORDS];
} features;
+ char (*priv_flag_names)[ETH_GSTRING_LEN];
+ u32 priv_flags;
+ unsigned int n_priv_flags;
};

static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
@@ -36,6 +39,7 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
[ETHA_SETTINGS_MSGLEVEL] = { .type = NLA_REJECT },
[ETHA_SETTINGS_LINK] = { .type = NLA_REJECT },
[ETHA_SETTINGS_FEATURES] = { .type = NLA_REJECT },
+ [ETHA_SETTINGS_PRIV_FLAGS] = { .type = NLA_REJECT },
};

static int parse_settings(struct common_req_info *req_info,
@@ -114,6 +118,58 @@ static int ethnl_get_features(struct net_device *dev,
return 0;
}

+static int get_priv_flags_info(struct net_device *dev, unsigned int *count,
+ void **names)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ int nflags;
+
+ if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings)
+ return -EOPNOTSUPP;
+ nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
+ if (nflags < 0)
+ return nflags;
+
+ if (names) {
+ *names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL);
+ if (!*names)
+ return -ENOMEM;
+ ops->get_strings(dev, ETH_SS_PRIV_FLAGS, *names);
+ }
+
+ /* We can easily pass more than 32 private flags to userspace via
+ * netlink but we cannot get more with ethtool_ops::get_priv_flags().
+ * Note that we must not adjust nflags before allocating the space
+ * for flag names as the buffer must be large enough for all flags.
+ */
+ if (WARN_ONCE(nflags > 32,
+ "device %s reports more than 32 private flags (%d)\n",
+ netdev_name(dev), nflags))
+ nflags = 32;
+
+ *count = nflags;
+ return 0;
+}
+
+static int ethnl_get_priv_flags(struct genl_info *info,
+ struct settings_data *data)
+{
+ struct net_device *dev = data->repdata_base.dev;
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ unsigned int nflags;
+ void *names;
+ int ret;
+
+ ret = get_priv_flags_info(dev, &nflags, &names);
+ if (ret < 0)
+ return ret;
+
+ data->priv_flags = ops->get_priv_flags(dev);
+ data->priv_flag_names = names;
+ data->n_priv_flags = nflags;
+ return 0;
+}
+
static int prepare_settings(struct common_req_info *req_info,
struct genl_info *info)
{
@@ -163,6 +219,11 @@ static int prepare_settings(struct common_req_info *req_info,
data->link = __ethtool_get_link(dev);
if (req_mask & ETH_SETTINGS_IM_FEATURES)
ethnl_get_features(dev, data);
+ if (req_mask & ETH_SETTINGS_IM_PRIVFLAGS) {
+ ret = ethnl_get_priv_flags(info, data);
+ if (ret < 0)
+ req_mask &= ~ETH_SETTINGS_IM_PRIVFLAGS;
+ }
ethnl_after_ops(dev);

data->repdata_base.info_mask = req_mask;
@@ -281,6 +342,17 @@ static int settings_size(const struct common_req_info *req_info)
return ret;
len += ret;
}
+ if (info_mask & ETH_SETTINGS_IM_PRIVFLAGS) {
+ const unsigned int flags =
+ (compact ? ETHNL_BITSET_COMPACT : 0) |
+ ETHNL_BITSET_LEGACY_NAMES;
+
+ ret = ethnl_bitset32_size(data->n_priv_flags, &data->priv_flags,
+ NULL, data->priv_flag_names, flags);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }

return len;
}
@@ -404,6 +476,18 @@ static int fill_features(struct sk_buff *skb, const struct settings_data *data)
return 0;
}

+static int fill_priv_flags(struct sk_buff *skb,
+ const struct settings_data *data)
+{
+ const unsigned int bitset_flags =
+ (data->reqinfo_base.compact ? ETHNL_BITSET_COMPACT : 0) |
+ ETHNL_BITSET_LEGACY_NAMES;
+
+ return ethnl_put_bitset32(skb, ETHA_SETTINGS_PRIV_FLAGS,
+ data->n_priv_flags, &data->priv_flags, NULL,
+ data->priv_flag_names, bitset_flags);
+}
+
static int fill_settings(struct sk_buff *skb,
const struct common_req_info *req_info)
{
@@ -443,10 +527,23 @@ static int fill_settings(struct sk_buff *skb,
if (ret < 0)
return ret;
}
+ if (info_mask & ETH_SETTINGS_IM_PRIVFLAGS) {
+ ret = fill_priv_flags(skb, data);
+ if (ret < 0)
+ return ret;
+ }

return 0;
}

+static void settings_cleanup(struct common_req_info *req_info)
+{
+ const struct settings_data *data =
+ container_of(req_info, struct settings_data, reqinfo_base);
+
+ kfree(data->priv_flag_names);
+}
+
const struct get_request_ops settings_request_ops = {
.request_cmd = ETHNL_CMD_GET_SETTINGS,
.reply_cmd = ETHNL_CMD_SET_SETTINGS,
@@ -458,4 +555,5 @@ const struct get_request_ops settings_request_ops = {
.prepare_data = prepare_settings,
.reply_size = settings_size,
.fill_reply = fill_settings,
+ .cleanup = settings_cleanup,
};
--
2.20.1


2019-02-18 19:19:12

by Michal Kubecek

[permalink] [raw]
Subject: [RFC PATCH net-next v3 07/21] 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, 129 insertions(+)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 205ae4462e9e..b79c26b5e92b 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -125,6 +125,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.

@@ -136,9 +138,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 b662d75a0636..7e192ad8ce3a 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -7,6 +7,7 @@

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

__ETHNL_CMD_CNT,
ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
@@ -55,6 +56,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 a5fa54c2b743..ee3424cd1f90 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -148,6 +148,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[] = {
@@ -179,6 +240,10 @@ static int __init ethnl_init(void)
if (ret < 0)
panic("ethtool: could not register genetlink family\n");

+ ret = register_netdevice_notifier(&ethnl_netdev_notifier);
+ if (ret < 0)
+ panic("ethtool: could not register netdev notifier\n");
+
return 0;
}

--
2.20.1


2019-02-18 20:01:56

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 02/21] ethtool: move to its own directory

On Mon, 18 Feb 2019 19:21:34 +0100 (CET), 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 ioct.c.

nit: s/ioct.c/ioctl.c/

2019-02-18 20:15:53

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 04/21] ethtool: helper functions for netlink interface

On Mon, 18 Feb 2019 19:21:44 +0100 (CET), Michal Kubecek wrote:
> +/* 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: info for the received packet we respond to
> + * ehdrp: place to store payload pointer returned by genlmsg_new()
> + * returns: skb or null on error
> + */

nit: why not correct kdoc?

> +struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
> + u16 dev_attrtype, struct genl_info *info,
> + void **ehdrp)
> +{
> + void *ehdr;
> + struct sk_buff *rskb;

nit: reverse xmas tree

2019-02-19 02:45:43

by Andrew Lunn

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 07/21] ethtool: implement EVENT notifications

On Mon, Feb 18, 2019 at 07:21:59PM +0100, Michal Kubecek 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.

Hi Michal

Does ETHA_EVENT_RENAMEDEV actually contain enough information to avoid
needing a rtnetlink lookup? If i understand the code correctly, all
you have is the new name. You don't know the old name?

Having said that, i don't see an easy way to get access to the old
name when handling the NETDEV_CHANGENAME.

Andrew

2019-02-19 07:03:16

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 07/21] ethtool: implement EVENT notifications

On Tue, Feb 19, 2019 at 12:46:08AM +0100, Andrew Lunn wrote:
> On Mon, Feb 18, 2019 at 07:21:59PM +0100, Michal Kubecek 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.
>
> Hi Michal
>
> Does ETHA_EVENT_RENAMEDEV actually contain enough information to avoid
> needing a rtnetlink lookup? If i understand the code correctly, all
> you have is the new name. You don't know the old name?
>
> Having said that, i don't see an easy way to get access to the old
> name when handling the NETDEV_CHANGENAME.

We don't have the old name and without modifying the NETDEV_CHANGENAME
notifier to pass it, there is probably no way to get it. But the ethtool
notification also contains ifindex so that userspace applications which
want to track the configuration can remember both and identify the
renamed device by ifindex.

Michal

2019-02-19 10:34:40

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 12/21] ethtool: provide permanent hardware address in GET_INFO request

Mon, Feb 18, 2019 at 07:22:24PM CET, [email protected] wrote:
>Add information about permanent hadware address of a device (as provided by
>ETHTOOL_GPERMADDR ioctl command) in GET_INFO reply if ETH_INFO_IM_PERMADDR
>flag is set in the request.
>
>There is no separate attribute for hardware address length as nla_len gives
>this information. The reply also provides address type (net_device::type).
>
>Signed-off-by: Michal Kubecek <[email protected]>
>---
> Documentation/networking/ethtool-netlink.txt | 9 ++++-
> include/uapi/linux/ethtool_netlink.h | 12 +++++-
> net/ethtool/info.c | 39 ++++++++++++++++++++
> 3 files changed, 58 insertions(+), 2 deletions(-)
>
>diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
>index b6999a2167e8..1e615e111262 100644
>--- a/Documentation/networking/ethtool-netlink.txt
>+++ b/Documentation/networking/ethtool-netlink.txt
>@@ -239,6 +239,9 @@ 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_PERMADDR (nested)
>+ ETHA_PERMADDR_ADDRESS (binary) permanent HW address

I think this is a nice example of thing that should not be exposed with
ethtool but rather via rtnetlink, alongside with the actual hw address.

[...]

2019-02-19 10:45:39

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 00/21] ethtool netlink interface, part 1

Mon, Feb 18, 2019 at 07:21:24PM CET, [email protected] wrote:
>Note: this is marked as RFC because it's rather late in the cycle; the plan
>is to make a regular submission (with changes based on review) once
>net-next reopens after the 5.1 merge window. The full (work in progress)
>series, together with the (userspace) ethtool counterpart can be found at
>https://github.com/mkubecek/ethnl
>
>This is first part of 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 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
>
>Basic concepts:
>
>- the goal is to provide all features of ioctl interface but allow
> easier future extensions; at some point, it should be possible to have
> full ethtool functionality without using the ioctl interface

I'm not sure it is a good idea to map the existing iface to netlink
fully. There are things in ethtool which are not really unique for
Ethernet. Those things should be put somewhere else.


>- inextensibility of ioctl interface resulted in way too many commands,
> many of them obsoleted by newer ones; reduce the number by ignoring the
> obsolete commands and grouping some together
>- for "set" type commands, allows passing only the attributes to be
> changed; therefore we don't need a get-modify-set cycle (which is
> inherently racy), userspace can simply say what it wants to change
>- provide notifications to multicast group "monitor" like rtnetlink does,
> i.e. in the form of messages close to replies to "get" requests
>- allow dump requests to get some information about all network devices
> providing it
>- be less dependent on ethtool and kernel being in sync; allow e.g. saying
> "ethtool -s eth0 advertise foo off" without ethtool knowing what "foo"
> means; it's kernel's job to know what mode "xyz" is and if it exists and
> is supported
>
>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

Could you explain why please?


>
>- 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 (21):
> netlink: introduce nla_put_bitfield32()
> 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 permanent hardware address 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: provide WoL information in GET_SETTINGS request
> ethtool: provide message level in GET_SETTINGS request
> ethtool: provide link state in GET_SETTINGS request
> ethtool: provide device features in GET_SETTINGS request
> ethtool: provide private flags in GET_SETTINGS request
> ethtool: send netlink notifications about setting changes
>
> Documentation/networking/ethtool-netlink.txt | 441 +++++++++++++
> include/linux/ethtool_netlink.h | 17 +
> include/linux/netdevice.h | 14 +
> include/net/netlink.h | 15 +
> include/uapi/linux/ethtool.h | 10 +
> include/uapi/linux/ethtool_netlink.h | 265 ++++++++
> include/uapi/linux/net_tstamp.h | 13 +
> net/Kconfig | 8 +
> net/Makefile | 2 +-
> net/core/Makefile | 2 +-
> net/ethtool/Makefile | 7 +
> net/ethtool/bitset.c | 572 +++++++++++++++++
> net/ethtool/bitset.h | 40 ++
> net/ethtool/common.c | 227 +++++++
> net/ethtool/common.h | 29 +
> net/ethtool/info.c | 332 ++++++++++
> net/{core/ethtool.c => ethtool/ioctl.c} | 244 ++-----
> net/ethtool/netlink.c | 634 +++++++++++++++++++
> net/ethtool/netlink.h | 252 ++++++++
> net/ethtool/settings.c | 559 ++++++++++++++++
> net/ethtool/strset.c | 461 ++++++++++++++
> 21 files changed, 3937 insertions(+), 207 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.20.1
>

2019-02-19 11:37:48

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 12/21] ethtool: provide permanent hardware address in GET_INFO request

On Tue, Feb 19, 2019 at 11:24:00AM +0100, Jiri Pirko wrote:
> Mon, Feb 18, 2019 at 07:22:24PM CET, [email protected] wrote:
> >Add information about permanent hadware address of a device (as provided by
> >ETHTOOL_GPERMADDR ioctl command) in GET_INFO reply if ETH_INFO_IM_PERMADDR
> >flag is set in the request.
> >
> >There is no separate attribute for hardware address length as nla_len gives
> >this information. The reply also provides address type (net_device::type).
> >
> >Signed-off-by: Michal Kubecek <[email protected]>
> >---
> > Documentation/networking/ethtool-netlink.txt | 9 ++++-
> > include/uapi/linux/ethtool_netlink.h | 12 +++++-
> > net/ethtool/info.c | 39 ++++++++++++++++++++
> > 3 files changed, 58 insertions(+), 2 deletions(-)
> >
> >diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
> >index b6999a2167e8..1e615e111262 100644
> >--- a/Documentation/networking/ethtool-netlink.txt
> >+++ b/Documentation/networking/ethtool-netlink.txt
> >@@ -239,6 +239,9 @@ 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_PERMADDR (nested)
> >+ ETHA_PERMADDR_ADDRESS (binary) permanent HW address
>
> I think this is a nice example of thing that should not be exposed with
> ethtool but rather via rtnetlink, alongside with the actual hw address.
>
> [...]

I guess you are right. As we don't have to query the driver and just
read the information from struct net_device, rtnetlink does indeed seem
more appropriate.

Michal

2019-02-19 11:58:19

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 00/21] ethtool netlink interface, part 1

On Tue, Feb 19, 2019 at 11:35:08AM +0100, Jiri Pirko wrote:
> >- 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
>
> Could you explain why please?

What I mean is the problem discussed under Jakub's devlink flash
patchset: that he couldn't implement only the devlink callback in nfp
and rely on the generic fallback to devlink because it wouldn't work if
devlink is built as a module.

But I think this should be addressed. If we agree that flashing (and
other features provided by ethtool at the moment) rather belongs to
devlink (which nobody seems to oppose), we should rather try to make it
possible for drivers to provide only the devlink callback and gradually
move all in-tree drivers to doing so. (And one day, remove it from
ethtool_ops.) It doesn't seem to make much sense to have devlink as
a module in such scenario.

Michal

2019-02-19 12:39:08

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 00/21] ethtool netlink interface, part 1

Tue, Feb 19, 2019 at 12:57:27PM CET, [email protected] wrote:
>On Tue, Feb 19, 2019 at 11:35:08AM +0100, Jiri Pirko wrote:
>> >- 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
>>
>> Could you explain why please?
>
>What I mean is the problem discussed under Jakub's devlink flash
>patchset: that he couldn't implement only the devlink callback in nfp
>and rely on the generic fallback to devlink because it wouldn't work if
>devlink is built as a module.

So let's fix that.


>
>But I think this should be addressed. If we agree that flashing (and
>other features provided by ethtool at the moment) rather belongs to
>devlink (which nobody seems to oppose), we should rather try to make it
>possible for drivers to provide only the devlink callback and gradually
>move all in-tree drivers to doing so. (And one day, remove it from
>ethtool_ops.) It doesn't seem to make much sense to have devlink as
>a module in such scenario.

Agreed.

>
>Michal

2019-02-19 13:09:24

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 04/21] ethtool: helper functions for netlink interface

On Mon, Feb 18, 2019 at 12:15:17PM -0800, Jakub Kicinski wrote:
> On Mon, 18 Feb 2019 19:21:44 +0100 (CET), Michal Kubecek wrote:
> > +/* 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: info for the received packet we respond to
> > + * ehdrp: place to store payload pointer returned by genlmsg_new()
> > + * returns: skb or null on error
> > + */
>
> nit: why not correct kdoc?

Good point. I'll fix that, together with the other things.

Michal

>
> > +struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
> > + u16 dev_attrtype, struct genl_info *info,
> > + void **ehdrp)
> > +{
> > + void *ehdr;
> > + struct sk_buff *rskb;
>
> nit: reverse xmas tree

2019-02-20 02:28:48

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 05/21] ethtool: netlink bitset handling

On Mon, 18 Feb 2019 19:21:49 +0100 (CET), Michal Kubecek wrote:
> + 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 (!tb[ETHA_BITSET_VALUE])
> + goto out_free;

!tb[ETHA_BITSET_BITS] && !tb[ETHA_BITSET_VALUE] is already rejected
above.

> + 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;
> + }

2019-02-20 02:44:09

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 08/21] ethtool: generic handlers for GET requests

On Mon, 18 Feb 2019 19:22:04 +0100 (CET), Michal Kubecek wrote:
> +static struct common_req_info *alloc_get_data(const struct get_request_ops *ops)

nit: IMHO prefixing all structures and functions with a common phrase
(ethnl) makes the code easier to navigate.

> +{
> + struct common_req_info *req_info = kmalloc(ops->data_size, GFP_KERNEL);
> +
> + if (!req_info)
> + return NULL;

nit: please avoid calling complex functions inline, especially if the
result has to be error checked

2019-02-20 02:56:58

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 10/21] ethtool: provide string sets with GET_STRSET request

On Mon, 18 Feb 2019 19:22:14 +0100 (CET), 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.

> +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

Is it common to put the device information outside of the main
attribute nest?

> + 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.
> +
> +

> +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(tb, ETHA_STRINGSET_MAX, nest, stringset_policy,
> + info ? info->extack : NULL);

Would it make sense to use strict parsing everywhere from the start?
You seem to add REJECTS, but won't attributes > max get ignored?

> + if (ret < 0)
> + return ret;
> + if (!tb[ETHA_STRINGSET_ID])
> + return -EINVAL;
> +
> + *val = nla_get_u32(tb[ETHA_STRINGSET_ID]);
> + return 0;
> +}
> +
> +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;


2019-02-20 03:01:48

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 13/21] ethtool: provide timestamping information in GET_INFO request

On Mon, 18 Feb 2019 19:22:29 +0100 (CET), 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.

What's the reason for providing the symbolic names?

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

2019-02-20 08:17:27

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 05/21] ethtool: netlink bitset handling

On Tue, Feb 19, 2019 at 06:27:59PM -0800, Jakub Kicinski wrote:
> On Mon, 18 Feb 2019 19:21:49 +0100 (CET), Michal Kubecek wrote:
> > + 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 (!tb[ETHA_BITSET_VALUE])
> > + goto out_free;
>
> !tb[ETHA_BITSET_BITS] && !tb[ETHA_BITSET_VALUE] is already rejected
> above.

Thank you. I'll drop this test.

Michal

2019-02-20 12:37:30

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 10/21] ethtool: provide string sets with GET_STRSET request

On Tue, Feb 19, 2019 at 06:56:10PM -0800, Jakub Kicinski wrote:
> On Mon, 18 Feb 2019 19:22:14 +0100 (CET), 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.
>
> > +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
>
> Is it common to put the device information outside of the main
> attribute nest?

There may be multiple string sets (ETHA_STRSET_STIRNGSET nests) in one
message but they would be either all global or all related to the same
device. If string sets for multiple devices are sent, it would be in
response to a dump request and there would be one message per device.

>
> > + 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.
> > +
> > +
>
> > +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(tb, ETHA_STRINGSET_MAX, nest, stringset_policy,
> > + info ? info->extack : NULL);
>
> Would it make sense to use strict parsing everywhere from the start?
> You seem to add REJECTS, but won't attributes > max get ignored?

Good point, I forgot about this when the strict checking helpers were
added. I'll change the uses of nlmsg_parse() and nla_parse_nested() to
their strict variants (and add nla_parse_nested_strict()).

Michal

2019-02-20 13:00:58

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 13/21] ethtool: provide timestamping information in GET_INFO request

On Tue, Feb 19, 2019 at 07:00:48PM -0800, Jakub Kicinski wrote:
> On Mon, 18 Feb 2019 19:22:29 +0100 (CET), 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.
>
> What's the reason for providing the symbolic names?

One of the the goals I had was to reduce the need to keep the lists of
possible values in sync between kernel and userspace ethtool and other
users of the interface so that when a new value is added, we don't have
to update all userspace tools to be able to use or present it.

This already works in ethtool for some newer commands (e.g. features)
and obviously for those where the list of available options depends on
the device (e.g. private flags or statistics). I would like to extend
the principle also to older commands and new ones which do not work like
this (e.g. device reset).

Michal

2019-02-20 18:38:26

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 13/21] ethtool: provide timestamping information in GET_INFO request

On Wed, 20 Feb 2019 14:00:07 +0100, Michal Kubecek wrote:
> On Tue, Feb 19, 2019 at 07:00:48PM -0800, Jakub Kicinski wrote:
> > On Mon, 18 Feb 2019 19:22:29 +0100 (CET), 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.
> >
> > What's the reason for providing the symbolic names?
>
> One of the the goals I had was to reduce the need to keep the lists of
> possible values in sync between kernel and userspace ethtool and other
> users of the interface so that when a new value is added, we don't have
> to update all userspace tools to be able to use or present it.
>
> This already works in ethtool for some newer commands (e.g. features)
> and obviously for those where the list of available options depends on
> the device (e.g. private flags or statistics). I would like to extend
> the principle also to older commands and new ones which do not work like
> this (e.g. device reset).

Let me try to argue that's the wrong direction. People should learn to
update their user space tooling if they want access to new features.
In my (limited) experience trying to solve forward compatibility leads
to short term gains, and long term warts in the APIs and increased
maintenance burden in the kernel.

2019-02-21 03:16:57

by Florian Fainelli

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 15/21] ethtool: provide link settings and link modes in GET_SETTINGS request



On 2/18/2019 10:22 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 is divided into two parts: 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.
>
> Signed-off-by: Michal Kubecek <[email protected]>
> ---

[snip]

> +#define ETH_SETTINGS_IM_LINKINFO 0x01
> +#define ETH_SETTINGS_IM_LINKMODES 0x02
> +
> +#define ETH_SETTINGS_IM_ALL 0x03

You could define ETH_SETTINGS_IM_ALL as:

#define ETH_SETTING_IM_ALL \
(ETH_SETTINGS_IM_LINKINFO |
ETH_SETTINGS_IM_LINMODES)

that would scale better IMHO, especially given that you have to keep
bumping that mask with new bits in subsequent patches.

[snip]

> + 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;

What if userland is newer than the kernel and specifies a req_mask with
bits set that you don't support? Should not you always do an &
ETH_SETTINGS_IM_ALL here?

[snip]
--
Florian

2019-02-21 03:22:13

by Florian Fainelli

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 00/21] ethtool netlink interface, part 1

Hi Michal,

Let me start with a big thank you for tackling this humongous amount of
work!

On 2/18/2019 10:21 AM, Michal Kubecek wrote:
> Note: this is marked as RFC because it's rather late in the cycle; the plan
> is to make a regular submission (with changes based on review) once
> net-next reopens after the 5.1 merge window. The full (work in progress)
> series, together with the (userspace) ethtool counterpart can be found at
> https://github.com/mkubecek/ethnl
>
> This is first part of 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 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
>
> Basic concepts:
>
> - the goal is to provide all features of ioctl interface but allow
> easier future extensions; at some point, it should be possible to have
> full ethtool functionality without using the ioctl interface

+1

> - inextensibility of ioctl interface resulted in way too many commands,
> many of them obsoleted by newer ones; reduce the number by ignoring the
> obsolete commands and grouping some together
> - for "set" type commands, allows passing only the attributes to be
> changed; therefore we don't need a get-modify-set cycle (which is
> inherently racy), userspace can simply say what it wants to change
> - provide notifications to multicast group "monitor" like rtnetlink does,
> i.e. in the form of messages close to replies to "get" requests

+1

> - allow dump requests to get some information about all network devices
> providing it

And it seems like you have also added some filtering capability with at
least the settings dump, right? That is also highly welcome.

> - be less dependent on ethtool and kernel being in sync; allow e.g. saying
> "ethtool -s eth0 advertise foo off" without ethtool knowing what "foo"
> means; it's kernel's job to know what mode "xyz" is and if it exists and
> is supported
>
> Main changes between RFC v2 and RFC v3:
>
> - do not allow building as a module (no netdev notifiers needed)

I would very much prefer that we could build this as a module. Some
systems might be memory constrained or opt to disable ethtool entirely
(security?). If this is not too much trouble, can we try to maintain that?

> - 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

Should we just move everything under devlink given that ethtool over
netlink or devlink are essentially manipulating the same (or close to)
objects? It seems to me that at least now is the right time to make
decisions about what should stay in ethtool and be offered via netlink,
what should be migrated to something else (e.g.: Jiri mentioned
rtnetlink), and what is a devlink candidate.

How do we proceed to easily triage which of these netlink facilities
should things be routed to/from?

>
> - 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

Right, so as a driver writer, one thing I kind of hate is having to fill
a netlink skb myself because that's a lot of work, and it makes it
fairly hard to do mass conversion of internal APIs due to the increased
complexity in auditing what drivers do.

>
> - 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

Historically you listen for a link event and you determine the link
parameters from there, I don't know if there is much value in giving the
full link parameter list, especially given those could theoretically
change depending on your medium (e.g.: wireless), or there could be a
lot to query at that time...

>
> Michal Kubecek (21):
> netlink: introduce nla_put_bitfield32()
> 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 permanent hardware address 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: provide WoL information in GET_SETTINGS request
> ethtool: provide message level in GET_SETTINGS request
> ethtool: provide link state in GET_SETTINGS request
> ethtool: provide device features in GET_SETTINGS request
> ethtool: provide private flags in GET_SETTINGS request
> ethtool: send netlink notifications about setting changes
>
> Documentation/networking/ethtool-netlink.txt | 441 +++++++++++++
> include/linux/ethtool_netlink.h | 17 +
> include/linux/netdevice.h | 14 +
> include/net/netlink.h | 15 +
> include/uapi/linux/ethtool.h | 10 +
> include/uapi/linux/ethtool_netlink.h | 265 ++++++++
> include/uapi/linux/net_tstamp.h | 13 +
> net/Kconfig | 8 +
> net/Makefile | 2 +-
> net/core/Makefile | 2 +-
> net/ethtool/Makefile | 7 +
> net/ethtool/bitset.c | 572 +++++++++++++++++
> net/ethtool/bitset.h | 40 ++
> net/ethtool/common.c | 227 +++++++
> net/ethtool/common.h | 29 +
> net/ethtool/info.c | 332 ++++++++++
> net/{core/ethtool.c => ethtool/ioctl.c} | 244 ++-----
> net/ethtool/netlink.c | 634 +++++++++++++++++++
> net/ethtool/netlink.h | 252 ++++++++
> net/ethtool/settings.c | 559 ++++++++++++++++
> net/ethtool/strset.c | 461 ++++++++++++++
> 21 files changed, 3937 insertions(+), 207 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
>

--
Florian

2019-02-21 03:23:10

by Florian Fainelli

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 14/21] ethtool: provide link mode names as a string set



On 2/18/2019 10:22 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]>
> ---

[snip]

>
> +const char *const link_mode_names[] = {

Maybe you can define a macro (totally untested) to build the table an
avoid some of the repetition, something like:

#define ETHTOOL_LINK_NAME(speed, duplex) \
[ETHTOOL_LINK_MODE_##speed_##duplex_BIT] = stringify(speed ## "/" ##
duplex)

--
Florian

2019-02-21 09:55:18

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 00/21] ethtool netlink interface, part 1

On Wed, Feb 20, 2019 at 07:21:18PM -0800, Florian Fainelli wrote:
> > Main changes between RFC v2 and RFC v3:
> >
> > - do not allow building as a module (no netdev notifiers needed)
>
> I would very much prefer that we could build this as a module. Some
> systems might be memory constrained or opt to disable ethtool entirely
> (security?). If this is not too much trouble, can we try to maintain that?

There were two reasons why I agreed so quickly to the suggestion to make
CONFIG_ETHTOOL a bool:

1. I didn't really think there would be anyone who would benefit from
having the interface as a loadable module and the only reason why I made
it possible originally was that it was easier to test changes that way.
That was rather poor reason for keeping it once submitted but if there
is an actual demand from real life users, it's something different.

2. Using of ethtool netdev notifiers for triggering netlink
notifications from other code (mostly ioctl interface) was rather
clumsy. However, it could be worked around using an RCU pointer and some
simple helpers (as discussed in the devlink thread) so this not not
a serious problem either.

> > - 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
>
> Should we just move everything under devlink given that ethtool over
> netlink or devlink are essentially manipulating the same (or close to)
> objects? It seems to me that at least now is the right time to make
> decisions about what should stay in ethtool and be offered via netlink,
> what should be migrated to something else (e.g.: Jiri mentioned
> rtnetlink), and what is a devlink candidate.
>
> How do we proceed to easily triage which of these netlink facilities
> should things be routed to/from?

At the moment I would say the likely candidates for rtnetlink are
permanent hw address (ethtool -P), netdev features (-k / -K) and link
state (part of "ethtool <dev>"). (I'm not very happy about the prospect
of moving netdev features to rtnetlink but it seems hard to argue why
they shouldn't belong there.) As for devlink, I would see eeprom dump
(-e) and write (-E), register dump (-d), firmware dump (-w / -W) and
flash (-f), and module eeprom dump (-m) as candidates. Maybe also device
reset (--reset) (which was put into ethtool_ops, though, even if added
only recently).

There is also another question: at which level to do the split.

I would like to avoid doint it between user and ethtool utility, i.e.
dropping some functions from ethtool and pointing people to iproute2
tools (or even replacing ethtool with some new tool). There is the
"ifconfig memento": 20 years after it became obsolete, I still keep
hearing people say "I don't want to learn some new tool with unfamiliar
syntax". (Some of them learning to use ifconfig few years ago doesn't
really help. Neither them saying the same about ss which copied the most
of netstat's basic options.) I'm afraid the result would be exactly the
same with ethtool.

Then there is an option of providing the functions in ethtool but using
both ethtool netlink and devlink (and probably also rtnetlink) to talk
to kernel. This means a bit more complicated userspace code but nothing
serious. The bigger problem I see with this approach is that so far only
few NIC drivers have any devlink support. Unless we would want to add
ethtool_ops fallback for devlink, we would have to wait until the
relevant features are available via devlink in all in-tree NIC drivers
which provide them via ethtool_ops now.

Providing the features via ethtool netlink interface but having them
backed up by devlink would mean we would have two userspace interfaces
for the same (or almost the same) function. It's something I could live
with but I understand that not everyone would like this. The advantage
is that it would allow migrating the features from ethtool_ops to
devlink gradually.

> > - 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
>
> Right, so as a driver writer, one thing I kind of hate is having to fill
> a netlink skb myself because that's a lot of work, and it makes it
> fairly hard to do mass conversion of internal APIs due to the increased
> complexity in auditing what drivers do.

I would also prefer to avoid parsing and generating netlink messages in
drivers for regular operations.

Michal

2019-02-21 09:58:40

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 14/21] ethtool: provide link mode names as a string set

On Wed, Feb 20, 2019 at 07:21:58PM -0800, Florian Fainelli wrote:
> > +const char *const link_mode_names[] = {
>
> Maybe you can define a macro (totally untested) to build the table an
> avoid some of the repetition, something like:
>
> #define ETHTOOL_LINK_NAME(speed, duplex) \
> [ETHTOOL_LINK_MODE_##speed_##duplex_BIT] = stringify(speed ## "/" ##
> duplex)

Thanks for the tip, that should work for most of the modes.

Michal

2019-02-21 10:15:33

by Michal Kubecek

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 15/21] ethtool: provide link settings and link modes in GET_SETTINGS request

On Wed, Feb 20, 2019 at 07:14:50PM -0800, Florian Fainelli wrote:
> On 2/18/2019 10:22 AM, Michal Kubecek wrote:
> > +#define ETH_SETTINGS_IM_LINKINFO 0x01
> > +#define ETH_SETTINGS_IM_LINKMODES 0x02
> > +
> > +#define ETH_SETTINGS_IM_ALL 0x03
>
> You could define ETH_SETTINGS_IM_ALL as:
>
> #define ETH_SETTING_IM_ALL \
> (ETH_SETTINGS_IM_LINKINFO |
> ETH_SETTINGS_IM_LINMODES)
>
> that would scale better IMHO, especially given that you have to keep
> bumping that mask with new bits in subsequent patches.

I'm considering going even further and using something similar to what
is used for NETIF_F_* constants so that the *_ALL value would be
calculated automatically. But I'm not sure if it's not too fancy for
a uapi header file.

> > + 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;
>
> What if userland is newer than the kernel and specifies a req_mask with
> bits set that you don't support? Should not you always do an &
> ETH_SETTINGS_IM_ALL here?

In that case only known bits would be handled and the check at the end
of prepare_info() would add a warning to extack that part of the
information couldn't be provided (same as if some of the recognized
parts didn't have necessary ethtool_ops handlers or if they failed).

Michal

2019-02-21 17:42:24

by Florian Fainelli

[permalink] [raw]
Subject: Re: [RFC PATCH net-next v3 15/21] ethtool: provide link settings and link modes in GET_SETTINGS request

On 2/21/19 2:14 AM, Michal Kubecek wrote:
> On Wed, Feb 20, 2019 at 07:14:50PM -0800, Florian Fainelli wrote:
>> On 2/18/2019 10:22 AM, Michal Kubecek wrote:
>>> +#define ETH_SETTINGS_IM_LINKINFO 0x01
>>> +#define ETH_SETTINGS_IM_LINKMODES 0x02
>>> +
>>> +#define ETH_SETTINGS_IM_ALL 0x03
>>
>> You could define ETH_SETTINGS_IM_ALL as:
>>
>> #define ETH_SETTING_IM_ALL \
>> (ETH_SETTINGS_IM_LINKINFO |
>> ETH_SETTINGS_IM_LINMODES)
>>
>> that would scale better IMHO, especially given that you have to keep
>> bumping that mask with new bits in subsequent patches.
>
> I'm considering going even further and using something similar to what
> is used for NETIF_F_* constants so that the *_ALL value would be
> calculated automatically. But I'm not sure if it's not too fancy for
> a uapi header file.

Adding new netdev features still requires defining two constants: one in
the enumeration, and one that resolves the bit to bitmask constant, so
this would amount to the same possible mistakes/errors being made here
by changing two lines.

>
>>> + 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;
>>
>> What if userland is newer than the kernel and specifies a req_mask with
>> bits set that you don't support? Should not you always do an &
>> ETH_SETTINGS_IM_ALL here?
>
> In that case only known bits would be handled and the check at the end
> of prepare_info() would add a warning to extack that part of the
> information couldn't be provided (same as if some of the recognized
> parts didn't have necessary ethtool_ops handlers or if they failed).

I guess that is fair, I was just wondering if it made sense for the
kernel to report that there is one particular bitmask of settings that
it does not support at all and report that through netlink extended ack,
as opposed to silently not processing the bits it does not know.
--
Florian