All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michal Kubecek <mkubecek@suse.cz>
To: David Miller <davem@davemloft.net>, netdev@vger.kernel.org
Cc: Jakub Kicinski <jakub.kicinski@netronome.com>,
	Jiri Pirko <jiri@resnulli.us>, Andrew Lunn <andrew@lunn.ch>,
	Florian Fainelli <f.fainelli@gmail.com>,
	John Linville <linville@tuxdriver.com>,
	linux-kernel@vger.kernel.org
Subject: [PATCH net-next v4 12/22] ethtool: provide string sets with GET_STRSET request
Date: Thu, 21 Mar 2019 14:40:54 +0100 (CET)	[thread overview]
Message-ID: <2b8f7ef8e08653d3ce0520c0642cd27995d0c79b.1553170807.git.mkubecek@suse.cz> (raw)
In-Reply-To: <cover.1553170807.git.mkubecek@suse.cz>

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 <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  46 +-
 include/uapi/linux/ethtool.h                 |   2 +
 include/uapi/linux/ethtool_netlink.h         |  43 ++
 net/ethtool/Makefile                         |   2 +-
 net/ethtool/netlink.c                        |   8 +
 net/ethtool/netlink.h                        |   4 +
 net/ethtool/strset.c                         | 447 +++++++++++++++++++
 7 files changed, 549 insertions(+), 3 deletions(-)
 create mode 100644 net/ethtool/strset.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 5e5d785fe215..1508c16a236e 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -127,6 +127,8 @@ List of message types
 ---------------------
 
     ETHNL_CMD_EVENT			notification only
+    ETHNL_CMD_GET_STRSET
+    ETHNL_CMD_SET_STRSET		response only
 
 All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
 to indicate the type.
@@ -167,6 +169,46 @@ and also multiple events of the same type (e.g. two or more newly registered
 devices).
 
 
+GET_STRSET
+----------
+
+Requests contents of a string set as provided by ioctl commands
+ETHTOOL_GSSET_INFO and ETHTOOL_GSTRINGS. String sets are not user writeable so
+that the corresponding SET_STRSET message is only used in kernel replies.
+There are two types of string sets: global (independent of a device, e.g.
+device feature names) and device specific (e.g. device private flags).
+
+Request contents:
+
+    ETHA_STRSET_DEV		(nested)	device identification
+    ETHA_STRSET_COUNTS		(flag)		request only string counts
+    ETHA_STRSET_STRINGSET	(nested)	string set to request
+        ETHA_STRINGSET_ID		(u32)		set id
+
+Kernel response contents:
+
+    ETHA_STRSET_DEV		(nested)	device identification
+    ETHA_STRSET_STRINGSET	(nested)	string set to request
+        ETHA_STRINGSET_ID		(u32)		set id
+        ETHA_STRINGSET_COUNT		(u32)		number of strings
+        ETHA_STRINGSET_STRINGS		(nested)	array of strings
+            ETHA_STRING_INDEX			(u32)		string index
+            ETHA_STRING_VALUE			(string)	string value
+
+ETHA_STRSET_DEV, if present, identifies the device to request device specific
+string sets for. Depending on its presence a and NLM_F_DUMP flag, there are
+three type of GET_STRSET requests:
+
+ - no NLM_F_DUMP, no device: get "global" stringsets
+ - no NLM_F_DUMP, with device: get string sets related to the device
+ - NLM_F_DUMP, no device: get device related string sets for all devices
+
+If there is no ETHA_STRSET_STRINGSET attribute, all string sets of requested
+type are returned, otherwise only those specified in the request. Flag
+ETHA_STRSET_COUNTS tells kernel to only return string counts of the sets, not
+the actual strings.
+
+
 Request translation
 -------------------
 
@@ -201,7 +243,7 @@ ETHTOOL_STXCSUM			n/a
 ETHTOOL_GSG			n/a
 ETHTOOL_SSG			n/a
 ETHTOOL_TEST			n/a
-ETHTOOL_GSTRINGS		n/a
+ETHTOOL_GSTRINGS		ETHNL_CMD_GET_STRSET
 ETHTOOL_PHYS_ID			n/a
 ETHTOOL_GSTATS			n/a
 ETHTOOL_GTSO			n/a
@@ -229,7 +271,7 @@ ETHTOOL_FLASHDEV		n/a
 ETHTOOL_RESET			n/a
 ETHTOOL_SRXNTUPLE		n/a
 ETHTOOL_GRXNTUPLE		n/a
-ETHTOOL_GSSET_INFO		n/a
+ETHTOOL_GSSET_INFO		ETHNL_CMD_GET_STRSET
 ETHTOOL_GRXFHINDIR		n/a
 ETHTOOL_SRXFHINDIR		n/a
 ETHTOOL_GFEATURES		n/a
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 3652b239dad1..eaea804972f6 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -574,6 +574,8 @@ enum ethtool_stringset {
 	ETH_SS_TUNABLES,
 	ETH_SS_PHY_STATS,
 	ETH_SS_PHY_TUNABLES,
+
+	ETH_SS_COUNT
 };
 
 /**
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 988519bc6e37..66aeb436b822 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -14,6 +14,8 @@
 enum {
 	ETHNL_CMD_NOOP,
 	ETHNL_CMD_EVENT,		/* only for notifications */
+	ETHNL_CMD_GET_STRSET,
+	ETHNL_CMD_SET_STRSET,		/* only for reply */
 
 	__ETHNL_CMD_CNT,
 	ETHNL_CMD_MAX = (__ETHNL_CMD_CNT - 1)
@@ -98,6 +100,47 @@ enum {
 	ETHA_EVENT_MAX = (__ETHA_EVENT_CNT - 1)
 };
 
+/* string sets */
+
+enum {
+	ETHA_STRING_UNSPEC,
+	ETHA_STRING_INDEX,			/* u32 */
+	ETHA_STRING_VALUE,			/* string */
+
+	__ETHA_STRING_CNT,
+	ETHA_STRING_MAX = (__ETHA_STRING_CNT - 1)
+};
+
+enum {
+	ETHA_STRINGS_UNSPEC,
+	ETHA_STRINGS_STRING,			/* nest - ETHA_STRINGS_* */
+
+	__ETHA_STRINGS_CNT,
+	ETHA_STRINGS_MAX = (__ETHA_STRINGS_CNT - 1)
+};
+
+enum {
+	ETHA_STRINGSET_UNSPEC,
+	ETHA_STRINGSET_ID,			/* u32 */
+	ETHA_STRINGSET_COUNT,			/* u32 */
+	ETHA_STRINGSET_STRINGS,			/* nest - ETHA_STRINGS_* */
+
+	__ETHA_STRINGSET_CNT,
+	ETHA_STRINGSET_MAX = (__ETHA_STRINGSET_CNT - 1)
+};
+
+/* GET_STRINGSET / SET_STRINGSET */
+
+enum {
+	ETHA_STRSET_UNSPEC,
+	ETHA_STRSET_DEV,			/* nest - ETHA_DEV_* */
+	ETHA_STRSET_COUNTS,			/* flag */
+	ETHA_STRSET_STRINGSET,			/* nest - ETHA_STRSET_* */
+
+	__ETHA_STRSET_CNT,
+	ETHA_STRSET_MAX = (__ETHA_STRSET_CNT - 1)
+};
+
 /* generic netlink info */
 #define ETHTOOL_GENL_NAME "ethtool"
 #define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 11782306593b..11ceb00821b3 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -4,4 +4,4 @@ obj-y				+= ioctl.o common.o
 
 obj-$(CONFIG_ETHTOOL_NETLINK)	+= ethtool_nl.o
 
-ethtool_nl-y	:= netlink.o bitset.o
+ethtool_nl-y	:= netlink.o bitset.o strset.o
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 18f300e42d21..3559f5c7040f 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -152,6 +152,7 @@ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
 /* GET request helpers */
 
 const struct get_request_ops *get_requests[__ETHNL_CMD_CNT] = {
+	[ETHNL_CMD_GET_STRSET]		= &strset_request_ops,
 };
 
 /**
@@ -556,6 +557,13 @@ static struct notifier_block ethnl_netdev_notifier = {
 /* genetlink setup */
 
 static const struct genl_ops ethtool_genl_ops[] = {
+	{
+		.cmd	= ETHNL_CMD_GET_STRSET,
+		.doit	= ethnl_get_doit,
+		.start	= ethnl_get_start,
+		.dumpit	= ethnl_get_dumpit,
+		.done	= ethnl_get_done,
+	},
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 625f912144b1..32d85bb5c49a 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -275,4 +275,8 @@ struct get_request_ops {
 	void (*cleanup)(struct common_req_info *req_info);
 };
 
+/* request handlers */
+
+extern const struct get_request_ops strset_request_ops;
+
 #endif /* _NET_ETHTOOL_NETLINK_H */
diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c
new file mode 100644
index 000000000000..808ab6b4b387
--- /dev/null
+++ b/net/ethtool/strset.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include "netlink.h"
+#include "common.h"
+
+enum strset_type {
+	ETH_SS_TYPE_NONE,
+	ETH_SS_TYPE_LEGACY,
+	ETH_SS_TYPE_SIMPLE,
+};
+
+struct strset_info {
+	enum strset_type type;
+	bool per_dev;
+	bool free_data;
+	unsigned int count;
+	union {
+		const char (*legacy)[ETH_GSTRING_LEN];
+		const char * const *simple;
+		void *ptr;
+	} data;
+};
+
+static const struct strset_info info_template[] = {
+	[ETH_SS_TEST] = {
+		.type		= ETH_SS_TYPE_LEGACY,
+		.per_dev	= true,
+	},
+	[ETH_SS_STATS] = {
+		.type		= ETH_SS_TYPE_LEGACY,
+		.per_dev	= true,
+	},
+	[ETH_SS_PRIV_FLAGS] = {
+		.type		= ETH_SS_TYPE_LEGACY,
+		.per_dev	= true,
+	},
+	[ETH_SS_NTUPLE_FILTERS] = {
+		.type		= ETH_SS_TYPE_NONE,
+	},
+	[ETH_SS_FEATURES] = {
+		.type		= ETH_SS_TYPE_LEGACY,
+		.per_dev	= false,
+		.count		= ARRAY_SIZE(netdev_features_strings),
+		.data		= { .legacy = netdev_features_strings },
+	},
+	[ETH_SS_RSS_HASH_FUNCS] = {
+		.type		= ETH_SS_TYPE_LEGACY,
+		.per_dev	= false,
+		.count		= ARRAY_SIZE(rss_hash_func_strings),
+		.data		= { .legacy = rss_hash_func_strings },
+	},
+	[ETH_SS_TUNABLES] = {
+		.type		= ETH_SS_TYPE_LEGACY,
+		.per_dev	= false,
+		.count		= ARRAY_SIZE(tunable_strings),
+		.data		= { .legacy = tunable_strings },
+	},
+	[ETH_SS_PHY_STATS] = {
+		.type		= ETH_SS_TYPE_LEGACY,
+		.per_dev	= true,
+	},
+	[ETH_SS_PHY_TUNABLES] = {
+		.type		= ETH_SS_TYPE_LEGACY,
+		.per_dev	= false,
+		.count		= ARRAY_SIZE(phy_tunable_strings),
+		.data		= { .legacy = phy_tunable_strings },
+	},
+};
+
+struct strset_data {
+	struct common_req_info		reqinfo_base;
+	u32				req_ids;
+	bool				counts_only;
+
+	/* everything below here will be reset for each device in dumps */
+	struct common_reply_data	repdata_base;
+	struct strset_info		info[ETH_SS_COUNT];
+};
+
+static const struct nla_policy get_strset_policy[ETHA_STRSET_MAX + 1] = {
+	[ETHA_STRSET_UNSPEC]		= { .type = NLA_REJECT },
+	[ETHA_STRSET_DEV]		= { .type = NLA_NESTED },
+	[ETHA_STRSET_COUNTS]		= { .type = NLA_FLAG },
+	[ETHA_STRSET_STRINGSET]		= { .type = NLA_NESTED },
+};
+
+static const struct nla_policy get_stringset_policy[ETHA_STRINGSET_MAX + 1] = {
+	[ETHA_STRINGSET_UNSPEC]		= { .type = NLA_REJECT },
+	[ETHA_STRINGSET_ID]		= { .type = NLA_U32 },
+	[ETHA_STRINGSET_COUNT]		= { .type = NLA_REJECT },
+	[ETHA_STRINGSET_STRINGS]	= { .type = NLA_REJECT },
+};
+
+static bool id_requested(const struct strset_data *data, u32 id)
+{
+	return data->req_ids & (1U << id);
+}
+
+static bool include_set(const struct strset_data *data, u32 id)
+{
+	bool per_dev;
+
+	BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(data->req_ids));
+
+	if (data->req_ids)
+		return id_requested(data, id);
+
+	per_dev = data->info[id].per_dev;
+	if (data->info[id].type == ETH_SS_TYPE_NONE)
+		return false;
+	return data->repdata_base.dev ? per_dev : !per_dev;
+}
+
+const char *str_value(const struct strset_info *info, unsigned int i)
+{
+	switch (info->type) {
+	case ETH_SS_TYPE_LEGACY:
+		return info->data.legacy[i];
+	case ETH_SS_TYPE_SIMPLE:
+		return info->data.simple[i];
+	default:
+		WARN_ONCE(1, "unexpected string set type");
+		return "";
+	}
+}
+
+static int get_strset_id(const struct nlattr *nest, u32 *val,
+			 struct genl_info *info)
+{
+	struct nlattr *tb[ETHA_STRINGSET_MAX + 1];
+	int ret;
+
+	ret = nla_parse_nested_strict(tb, ETHA_STRINGSET_MAX, nest,
+				      get_stringset_policy,
+				      info ? info->extack : NULL);
+	if (ret < 0)
+		return ret;
+	if (!tb[ETHA_STRINGSET_ID])
+		return -EINVAL;
+
+	*val = nla_get_u32(tb[ETHA_STRINGSET_ID]);
+	return 0;
+}
+
+/* parse_request() handler */
+static int parse_strset(struct common_req_info *req_info, struct sk_buff *skb,
+			struct genl_info *info, const struct nlmsghdr *nlhdr)
+{
+	struct strset_data *data =
+		container_of(req_info, struct strset_data, reqinfo_base);
+	struct nlattr *attr;
+	int rem, ret;
+
+	ret = nlmsg_validate(nlhdr, GENL_HDRLEN, ETHA_STRSET_MAX,
+			     get_strset_policy, info ? info->extack : NULL);
+	if (ret < 0)
+		return ret;
+
+	nlmsg_for_each_attr(attr, nlhdr, GENL_HDRLEN, rem) {
+		u32 id;
+
+		switch (nla_type(attr)) {
+		case ETHA_STRSET_DEV:
+			req_info->dev = ethnl_dev_get(info, attr);
+			if (IS_ERR(req_info->dev)) {
+				ret = PTR_ERR(req_info->dev);
+				req_info->dev = NULL;
+				return ret;
+			}
+			break;
+		case ETHA_STRSET_COUNTS:
+			data->counts_only = true;
+			break;
+		case ETHA_STRSET_STRINGSET:
+			ret = get_strset_id(attr, &id, info);
+			if (ret < 0)
+				return ret;
+			if (ret >= ETH_SS_COUNT)
+				return -EOPNOTSUPP;
+			data->req_ids |= (1U << id);
+			break;
+		default:
+			ETHNL_SET_ERRMSG(info,
+					 "unexpected attribute in ETHNL_CMD_GET_STRSET message");
+			return genl_err_attr(info, -EINVAL, attr);
+		}
+	}
+
+	return 0;
+}
+
+static void free_strset(struct strset_data *data)
+{
+	unsigned int i;
+
+	for (i = 0; i < ETH_SS_COUNT; i++)
+		if (data->info[i].free_data) {
+			kfree(data->info[i].data.ptr);
+			data->info[i].data.ptr = NULL;
+			data->info[i].free_data = false;
+		}
+}
+
+static int prepare_one_stringset(struct strset_info *info,
+				 struct net_device *dev, unsigned int id,
+				 bool counts_only)
+{
+	const struct ethtool_ops *ops = dev->ethtool_ops;
+	void *strings;
+	int count, ret;
+
+	if (id == ETH_SS_PHY_STATS && dev->phydev &&
+	    !ops->get_ethtool_phy_stats)
+		ret = phy_ethtool_get_sset_count(dev->phydev);
+	else if (ops->get_sset_count && ops->get_strings)
+		ret = ops->get_sset_count(dev, id);
+	else
+		ret = -EOPNOTSUPP;
+	if (ret <= 0) {
+		info->count = 0;
+		return 0;
+	}
+
+	count = ret;
+	if (!counts_only) {
+		strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL);
+		if (!strings)
+			return -ENOMEM;
+		if (id == ETH_SS_PHY_STATS && dev->phydev &&
+		    !ops->get_ethtool_phy_stats)
+			phy_ethtool_get_strings(dev->phydev, strings);
+		else
+			ops->get_strings(dev, id, strings);
+		info->data.legacy = strings;
+		info->free_data = true;
+	}
+	info->count = count;
+
+	return 0;
+}
+
+/* prepare_data() handler */
+static int prepare_strset(struct common_req_info *req_info,
+			  struct genl_info *info)
+{
+	struct strset_data *data =
+		container_of(req_info, struct strset_data, reqinfo_base);
+	struct net_device *dev = data->repdata_base.dev;
+	unsigned int i;
+	int ret;
+
+	BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT);
+	memcpy(&data->info, &info_template, sizeof(data->info));
+
+	if (!dev) {
+		for (i = 0; i < ETH_SS_COUNT; i++) {
+			if (id_requested(data, i) &&
+			    data->info[i].per_dev) {
+				ETHNL_SET_ERRMSG(info,
+						 "requested per device strings without dev");
+				return -EINVAL;
+			}
+		}
+	}
+
+	ret = ethnl_before_ops(dev);
+	if (ret < 0)
+		goto err_strset;
+	for (i = 0; i < ETH_SS_COUNT; i++) {
+		if (!include_set(data, i) || !data->info[i].per_dev)
+			continue;
+		if (WARN_ONCE(data->info[i].type != ETH_SS_TYPE_LEGACY,
+			      "unexpected string set type %u",
+			      data->info[i].type))
+			goto err_ops;
+
+		ret = prepare_one_stringset(&data->info[i], dev, i,
+					    data->counts_only);
+		if (ret < 0)
+			goto err_ops;
+	}
+	ethnl_after_ops(dev);
+
+	return 0;
+err_ops:
+	ethnl_after_ops(dev);
+err_strset:
+	free_strset(data);
+	return ret;
+}
+
+static int legacy_set_size(const char (*set)[ETH_GSTRING_LEN],
+			   unsigned int count)
+{
+	unsigned int len = 0;
+	unsigned int i;
+
+	for (i = 0; i < count; i++)
+		len += nla_total_size(nla_total_size(sizeof(u32)) +
+				      ethnl_str_size(set[i]));
+	len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len);
+
+	return nla_total_size(len);
+}
+
+static int simple_set_size(const char * const *set, unsigned int count)
+{
+	unsigned int len = 0;
+	unsigned int i;
+
+	for (i = 0; i < count; i++)
+		len += nla_total_size(nla_total_size(sizeof(u32)) +
+				      ethnl_str_size(set[i]));
+	len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len);
+
+	return nla_total_size(len);
+}
+
+static int set_size(const struct strset_info *info, bool counts_only)
+{
+	if (info->count == 0)
+		return 0;
+	if (counts_only)
+		return nla_total_size(2 * nla_total_size(sizeof(u32)));
+
+	switch (info->type) {
+	case ETH_SS_TYPE_LEGACY:
+		return legacy_set_size(info->data.legacy, info->count);
+	case ETH_SS_TYPE_SIMPLE:
+		return simple_set_size(info->data.simple, info->count);
+	default:
+		return -EINVAL;
+	};
+}
+
+/* reply_size() handler */
+static int strset_size(const struct common_req_info *req_info)
+{
+	const struct strset_data *data =
+		container_of(req_info, struct strset_data, reqinfo_base);
+	unsigned int i;
+	int len = 0;
+	int ret;
+
+	len += dev_ident_size();
+	for (i = 0; i < ETH_SS_COUNT; i++) {
+		const struct strset_info *info = &data->info[i];
+
+		if (!include_set(data, i) || info->type == ETH_SS_TYPE_NONE)
+			continue;
+
+		ret = set_size(info, data->counts_only);
+		if (ret < 0)
+			return ret;
+		len += ret;
+	}
+
+	return len;
+}
+
+static int fill_string(struct sk_buff *skb, const struct strset_info *info,
+		       u32 idx)
+{
+	struct nlattr *string = ethnl_nest_start(skb, ETHA_STRINGS_STRING);
+
+	if (!string)
+		return -EMSGSIZE;
+	if (nla_put_u32(skb, ETHA_STRING_INDEX, idx) ||
+	    nla_put_string(skb, ETHA_STRING_VALUE, str_value(info, idx)))
+		return -EMSGSIZE;
+	nla_nest_end(skb, string);
+
+	return 0;
+}
+
+static int fill_set(struct sk_buff *skb, const struct strset_data *data, u32 id)
+{
+	const struct strset_info *info = &data->info[id];
+	struct nlattr *strings;
+	struct nlattr *nest;
+	unsigned int i = (unsigned int)(-1);
+
+	if (info->type == ETH_SS_TYPE_NONE)
+		return -EOPNOTSUPP;
+	if (info->count == 0)
+		return 0;
+	nest = ethnl_nest_start(skb, ETHA_STRSET_STRINGSET);
+	if (!nest)
+		return -EMSGSIZE;
+
+	if (nla_put_u32(skb, ETHA_STRINGSET_ID, id) ||
+	    nla_put_u32(skb, ETHA_STRINGSET_COUNT, info->count))
+		goto err;
+
+	if (!data->counts_only) {
+		strings = ethnl_nest_start(skb, ETHA_STRINGSET_STRINGS);
+		if (!strings)
+			goto err;
+		for (i = 0; i < info->count; i++) {
+			if (fill_string(skb, info, i) < 0)
+				goto err;
+		}
+		nla_nest_end(skb, strings);
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+
+err:
+	nla_nest_cancel(skb, nest);
+	return -EMSGSIZE;
+}
+
+/* fill_reply() handler */
+static int fill_strset(struct sk_buff *skb,
+		       const struct common_req_info *req_info)
+{
+	const struct strset_data *data =
+		container_of(req_info, struct strset_data, reqinfo_base);
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ETH_SS_COUNT; i++)
+		if (include_set(data, i)) {
+			ret = fill_set(skb, data, i);
+			if (ret < 0)
+				return ret;
+		}
+
+	return 0;
+}
+
+const struct get_request_ops strset_request_ops = {
+	.request_cmd		= ETHNL_CMD_GET_STRSET,
+	.reply_cmd		= ETHNL_CMD_SET_STRSET,
+	.dev_attrtype		= ETHA_STRSET_DEV,
+	.data_size		= sizeof(struct strset_data),
+	.repdata_offset		= offsetof(struct strset_data, repdata_base),
+	.allow_nodev_do		= true,
+
+	.parse_request		= parse_strset,
+	.prepare_data		= prepare_strset,
+	.reply_size		= strset_size,
+	.fill_reply		= fill_strset,
+};
-- 
2.21.0


  parent reply	other threads:[~2019-03-21 13:42 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-03-21 13:40 [PATCH net-next v4 00/22] ethtool netlink interface, part 1 Michal Kubecek
2019-03-21 13:31 ` Michal Kubecek
2019-03-21 13:56   ` Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 01/22] rtnetlink: provide permanent hardware address in RTM_NEWLINK Michal Kubecek
2019-03-21 15:47   ` Stephen Hemminger
2019-03-21 20:35   ` Jakub Kicinski
2019-03-22  6:32     ` Michal Kubecek
2019-03-21 21:58   ` David Miller
2019-03-21 13:40 ` [PATCH net-next v4 02/22] netlink: introduce nla_put_bitfield32() Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 03/22] netlink: add strict version of nla_parse_nested() Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 04/22] ethtool: move to its own directory Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 05/22] ethtool: introduce ethtool netlink interface Michal Kubecek
2019-03-21 13:57   ` Andrew Lunn
2019-03-21 14:13     ` Michal Kubecek
2019-03-21 15:25       ` Andrew Lunn
2019-03-21 16:21         ` Jiri Pirko
2019-03-21 16:47         ` Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 06/22] ethtool: helper functions for " Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 07/22] ethtool: netlink bitset handling Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 08/22] ethtool: support for netlink notifications Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 09/22] ethtool: implement EVENT notifications Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 10/22] ethtool: generic handlers for GET requests Michal Kubecek
2019-03-21 13:40 ` [PATCH net-next v4 11/22] ethtool: move string arrays into common file Michal Kubecek
2019-03-21 13:40 ` Michal Kubecek [this message]
2019-03-21 13:40 ` [PATCH net-next v4 13/22] ethtool: provide driver/device information in GET_INFO request Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 14/22] ethtool: provide timestamping " Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 15/22] ethtool: provide link mode names as a string set Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 16/22] ethtool: provide link settings and link modes in GET_SETTINGS request Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 17/22] ethtool: set link settings and link modes with SET_SETTINGS request Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 18/22] ethtool: provide link state in GET_SETTINGS request Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 19/22] ethtool: provide WoL information " Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 20/22] ethtool: set WoL settings with SET_SETTINGS request Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 21/22] ethtool: provide message level in GET_SETTINGS request Michal Kubecek
2019-03-21 13:41 ` [PATCH net-next v4 22/22] ethtool: set message level with SET_SETTINGS request Michal Kubecek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2b8f7ef8e08653d3ce0520c0642cd27995d0c79b.1553170807.git.mkubecek@suse.cz \
    --to=mkubecek@suse.cz \
    --cc=andrew@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=f.fainelli@gmail.com \
    --cc=jakub.kicinski@netronome.com \
    --cc=jiri@resnulli.us \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linville@tuxdriver.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.