linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michal Kubecek <mkubecek@suse.cz>
To: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
	"John W. Linville" <linville@tuxdriver.com>
Subject: [RFC PATCH ethtool v2 10/23] netlink: add netlink handler for gfeatures (-k)
Date: Mon, 30 Jul 2018 14:56:34 +0200 (CEST)	[thread overview]
Message-ID: <184c7cae56aa3fa9a2116874cd7024ec45f4d05e.1532954671.git.mkubecek@suse.cz> (raw)
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>

Implement "ethtool -k <dev>" subcommand using netlink interface command
ETHNL_CMD_GET_SETTINGS with ETH_SETTINGS_IM_FEATURES mask.

The implementation tries to emulate legacy flags but the result is not
exactly equal to the ioctl code. In particular, we map netdev features to
legacy flags based on name patterns in off_flag_def[] but this mapping is
slightly different from mapping used by kernel ioctl code.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 common.c           |  30 ++++++
 common.h           |  21 +++++
 ethtool.c          |  70 +++-----------
 netlink/extapi.h   |   1 +
 netlink/monitor.c  |   6 ++
 netlink/settings.c | 226 +++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 296 insertions(+), 58 deletions(-)

diff --git a/common.c b/common.c
index 59897acd2512..db9a360ffae6 100644
--- a/common.c
+++ b/common.c
@@ -283,3 +283,33 @@ void dump_mdix(u8 mdix, u8 mdix_ctrl)
 		fprintf(stdout, "\n");
 	}
 }
+
+const struct off_flag_def off_flag_def[OFF_FLAG_DEF_SIZE] = {
+	{ "rx",     "rx-checksumming",		    "rx-checksum",
+	  ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM,	0 },
+	{ "tx",     "tx-checksumming",		    "tx-checksum-*",
+	  ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM,	0 },
+	{ "sg",     "scatter-gather",		    "tx-scatter-gather*",
+	  ETHTOOL_GSG,	   ETHTOOL_SSG,     ETH_FLAG_SG,	0 },
+	{ "tso",    "tcp-segmentation-offload",	    "tx-tcp*-segmentation",
+	  ETHTOOL_GTSO,	   ETHTOOL_STSO,    ETH_FLAG_TSO,	0 },
+	{ "ufo",    "udp-fragmentation-offload",    "tx-udp-fragmentation",
+	  ETHTOOL_GUFO,	   ETHTOOL_SUFO,    ETH_FLAG_UFO,	0 },
+	{ "gso",    "generic-segmentation-offload", "tx-generic-segmentation",
+	  ETHTOOL_GGSO,	   ETHTOOL_SGSO,    ETH_FLAG_GSO,	0 },
+	{ "gro",    "generic-receive-offload",	    "rx-gro",
+	  ETHTOOL_GGRO,	   ETHTOOL_SGRO,    ETH_FLAG_GRO,	0 },
+	{ "lro",    "large-receive-offload",	    "rx-lro",
+	  0,		   0,		    ETH_FLAG_LRO,
+	  KERNEL_VERSION(2,6,24) },
+	{ "rxvlan", "rx-vlan-offload",		    "rx-vlan-hw-parse",
+	  0,		   0,		    ETH_FLAG_RXVLAN,
+	  KERNEL_VERSION(2,6,37) },
+	{ "txvlan", "tx-vlan-offload",		    "tx-vlan-hw-insert",
+	  0,		   0,		    ETH_FLAG_TXVLAN,
+	  KERNEL_VERSION(2,6,37) },
+	{ "ntuple", "ntuple-filters",		    "rx-ntuple-filter",
+	  0,		   0,		    ETH_FLAG_NTUPLE,	0 },
+	{ "rxhash", "receive-hashing",		    "rx-hashing",
+	  0,		   0,		    ETH_FLAG_RXHASH,	0 },
+};
diff --git a/common.h b/common.h
index a980b844a0ed..36f96ddaa3bd 100644
--- a/common.h
+++ b/common.h
@@ -1,6 +1,8 @@
 #ifndef ETHTOOL_COMMON_H__
 #define ETHTOOL_COMMON_H__
 
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
 struct flag_info {
 	const char *name;
 	u32 value;
@@ -25,6 +27,25 @@ struct link_mode_info {
 	u8			duplex;
 };
 
+struct off_flag_def {
+	const char *short_name;
+	const char *long_name;
+	const char *kernel_name;
+	u32 get_cmd, set_cmd;
+	u32 value;
+	/* For features exposed through ETHTOOL_GFLAGS, the oldest
+	 * kernel version for which we can trust the result.  Where
+	 * the flag was added at the same time the kernel started
+	 * supporting the feature, this is 0 (to allow for backports).
+	 * Where the feature was supported before the flag was added,
+	 * it is the version that introduced the flag.
+	 */
+	u32 min_kernel_ver;
+};
+
+#define OFF_FLAG_DEF_SIZE 12
+extern const struct off_flag_def off_flag_def[OFF_FLAG_DEF_SIZE];
+
 extern const struct link_mode_info link_modes[];
 extern const unsigned int link_modes_count;
 
diff --git a/ethtool.c b/ethtool.c
index 0b40b96cf9bf..b29086bbabde 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -61,8 +61,6 @@
 #define NETLINK_GENERIC	16
 #endif
 
-#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
-
 static void exit_bad_args(void) __attribute__((noreturn));
 
 static void exit_bad_args(void)
@@ -104,51 +102,6 @@ struct cmdline_info {
 	void *seen_val;
 };
 
-struct off_flag_def {
-	const char *short_name;
-	const char *long_name;
-	const char *kernel_name;
-	u32 get_cmd, set_cmd;
-	u32 value;
-	/* For features exposed through ETHTOOL_GFLAGS, the oldest
-	 * kernel version for which we can trust the result.  Where
-	 * the flag was added at the same time the kernel started
-	 * supporting the feature, this is 0 (to allow for backports).
-	 * Where the feature was supported before the flag was added,
-	 * it is the version that introduced the flag.
-	 */
-	u32 min_kernel_ver;
-};
-static const struct off_flag_def off_flag_def[] = {
-	{ "rx",     "rx-checksumming",		    "rx-checksum",
-	  ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM,	0 },
-	{ "tx",     "tx-checksumming",		    "tx-checksum-*",
-	  ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM,	0 },
-	{ "sg",     "scatter-gather",		    "tx-scatter-gather*",
-	  ETHTOOL_GSG,	   ETHTOOL_SSG,     ETH_FLAG_SG,	0 },
-	{ "tso",    "tcp-segmentation-offload",	    "tx-tcp*-segmentation",
-	  ETHTOOL_GTSO,	   ETHTOOL_STSO,    ETH_FLAG_TSO,	0 },
-	{ "ufo",    "udp-fragmentation-offload",    "tx-udp-fragmentation",
-	  ETHTOOL_GUFO,	   ETHTOOL_SUFO,    ETH_FLAG_UFO,	0 },
-	{ "gso",    "generic-segmentation-offload", "tx-generic-segmentation",
-	  ETHTOOL_GGSO,	   ETHTOOL_SGSO,    ETH_FLAG_GSO,	0 },
-	{ "gro",    "generic-receive-offload",	    "rx-gro",
-	  ETHTOOL_GGRO,	   ETHTOOL_SGRO,    ETH_FLAG_GRO,	0 },
-	{ "lro",    "large-receive-offload",	    "rx-lro",
-	  0,		   0,		    ETH_FLAG_LRO,
-	  KERNEL_VERSION(2,6,24) },
-	{ "rxvlan", "rx-vlan-offload",		    "rx-vlan-hw-parse",
-	  0,		   0,		    ETH_FLAG_RXVLAN,
-	  KERNEL_VERSION(2,6,37) },
-	{ "txvlan", "tx-vlan-offload",		    "tx-vlan-hw-insert",
-	  0,		   0,		    ETH_FLAG_TXVLAN,
-	  KERNEL_VERSION(2,6,37) },
-	{ "ntuple", "ntuple-filters",		    "rx-ntuple-filter",
-	  0,		   0,		    ETH_FLAG_NTUPLE,	0 },
-	{ "rxhash", "receive-hashing",		    "rx-hashing",
-	  0,		   0,		    ETH_FLAG_RXHASH,	0 },
-};
-
 struct feature_def {
 	char name[ETH_GSTRING_LEN];
 	int off_flag_index; /* index in off_flag_def; negative if none match */
@@ -157,7 +110,7 @@ struct feature_def {
 struct feature_defs {
 	size_t n_features;
 	/* Number of features each offload flag is associated with */
-	unsigned int off_flag_matched[ARRAY_SIZE(off_flag_def)];
+	unsigned int off_flag_matched[OFF_FLAG_DEF_SIZE];
 	/* Name and offload flag index for each feature */
 	struct feature_def def[0];
 };
@@ -1325,7 +1278,7 @@ static void dump_features(const struct feature_defs *defs,
 	int indent;
 	int i, j;
 
-	for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+	for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
 		/* Don't show features whose state is unknown on this
 		 * kernel version
 		 */
@@ -1640,7 +1593,7 @@ static struct feature_defs *get_feature_defs(struct cmd_context *ctx)
 		defs->def[i].off_flag_index = -1;
 
 		for (j = 0;
-		     j < ARRAY_SIZE(off_flag_def) &&
+		     j < OFF_FLAG_DEF_SIZE &&
 			     defs->def[i].off_flag_index < 0;
 		     j++) {
 			const char *pattern =
@@ -2082,7 +2035,7 @@ get_features(struct cmd_context *ctx, const struct feature_defs *defs)
 
 	state->off_flags = 0;
 
-	for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+	for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
 		value = off_flag_def[i].value;
 		if (!off_flag_def[i].get_cmd)
 			continue;
@@ -2198,14 +2151,14 @@ static int do_sfeatures(struct cmd_context *ctx)
 	/* Generate cmdline_info for legacy flags and kernel-named
 	 * features, and parse our arguments.
 	 */
-	cmdline_features = calloc(ARRAY_SIZE(off_flag_def) + defs->n_features,
+	cmdline_features = calloc(OFF_FLAG_DEF_SIZE + defs->n_features,
 				  sizeof(cmdline_features[0]));
 	if (!cmdline_features) {
 		perror("Cannot parse arguments");
 		rc = 1;
 		goto err;
 	}
-	for (i = 0; i < ARRAY_SIZE(off_flag_def); i++)
+	for (i = 0; i < OFF_FLAG_DEF_SIZE; i++)
 		flag_to_cmdline_info(off_flag_def[i].short_name,
 				     off_flag_def[i].value,
 				     &off_flags_wanted, &off_flags_mask,
@@ -2215,9 +2168,9 @@ static int do_sfeatures(struct cmd_context *ctx)
 			defs->def[i].name, FEATURE_FIELD_FLAG(i),
 			&FEATURE_WORD(efeatures->features, i, requested),
 			&FEATURE_WORD(efeatures->features, i, valid),
-			&cmdline_features[ARRAY_SIZE(off_flag_def) + i]);
+			&cmdline_features[OFF_FLAG_DEF_SIZE + i]);
 	parse_generic_cmdline(ctx, &any_changed, cmdline_features,
-			      ARRAY_SIZE(off_flag_def) + defs->n_features);
+			      OFF_FLAG_DEF_SIZE + defs->n_features);
 	free(cmdline_features);
 
 	if (!any_changed) {
@@ -2237,7 +2190,7 @@ static int do_sfeatures(struct cmd_context *ctx)
 		 * related features that the user did not specify and that
 		 * are not fixed.  Warn if all related features are fixed.
 		 */
-		for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+		for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
 			int fixed = 1;
 
 			if (!(off_flags_mask & off_flag_def[i].value))
@@ -2278,7 +2231,7 @@ static int do_sfeatures(struct cmd_context *ctx)
 			goto err;
 		}
 	} else {
-		for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+		for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
 			if (!off_flag_def[i].set_cmd)
 				continue;
 			if (off_flags_mask & off_flag_def[i].value) {
@@ -4924,6 +4877,7 @@ static int show_usage(struct cmd_context *ctx);
 #define nl_gdrv		NULL
 #define nl_gset		NULL
 #define nl_sset		NULL
+#define nl_gfeatures	NULL
 #endif
 
 static const struct option {
@@ -4988,7 +4942,7 @@ static const struct option {
 	  "		[ rx-mini N ]\n"
 	  "		[ rx-jumbo N ]\n"
 	  "		[ tx N ]\n" },
-	{ "-k|--show-features|--show-offload", 1, do_gfeatures, NULL,
+	{ "-k|--show-features|--show-offload", 1, do_gfeatures, nl_gfeatures,
 	  "Get state of protocol offload and other features" },
 	{ "-K|--features|--offload", 1, do_sfeatures, NULL,
 	  "Set protocol offload and other features",
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 66573f0b4304..c656ff862687 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -16,6 +16,7 @@ int netlink_done(struct cmd_context *ctx);
 int nl_gdrv(struct cmd_context *ctx);
 int nl_gset(struct cmd_context *ctx);
 int nl_sset(struct cmd_context *ctx);
+int nl_gfeatures(struct cmd_context *ctx);
 int nl_monitor(struct cmd_context *ctx);
 
 void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index 32d842611011..e3b8bb46f561 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -127,6 +127,12 @@ static struct monitor_option monitor_opts[] = {
 				  ETH_SETTINGS_IM_WOLINFO |
 				  ETH_SETTINGS_IM_LINK,
 	},
+	{
+		.pattern	= "-k|--show-features|--show-offload"
+				  "|-K|--features|--offload",
+		.cmd		= ETHNL_CMD_SET_SETTINGS,
+		.info_mask	= ETH_SETTINGS_IM_FEATURES,
+	},
 };
 
 static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/settings.c b/netlink/settings.c
index b077a96325e5..51a01b400224 100644
--- a/netlink/settings.c
+++ b/netlink/settings.c
@@ -5,6 +5,7 @@
 #include "../internal.h"
 #include "../common.h"
 #include "netlink.h"
+#include "strset.h"
 #include "parser.h"
 
 /* GET_SETTINGS */
@@ -34,6 +35,219 @@ err:
 	return ret;
 }
 
+uint32_t *get_compact_bitset_value(const struct nlattr *bitset)
+{
+        const struct nlattr *tb[ETHA_BITSET_MAX + 1] = {};
+        DECLARE_ATTR_TB_INFO(tb);
+	unsigned int count;
+        int ret;
+
+        ret = mnl_attr_parse_nested(bitset, attr_cb, &tb_info);
+        if (ret < 0 || !tb[ETHA_BITSET_SIZE] || !tb[ETHA_BITSET_VALUE])
+		return NULL;
+	count = mnl_attr_get_u32(tb[ETHA_BITSET_SIZE]);
+	if (32 * mnl_attr_get_payload_len(tb[ETHA_BITSET_VALUE]) < count)
+		return NULL;
+
+	return mnl_attr_get_payload(tb[ETHA_BITSET_VALUE]);
+}
+
+uint32_t *get_compact_bitset_mask(const struct nlattr *bitset)
+{
+        const struct nlattr *tb[ETHA_BITSET_MAX + 1] = {};
+        DECLARE_ATTR_TB_INFO(tb);
+	unsigned int count;
+        int ret;
+
+        ret = mnl_attr_parse_nested(bitset, attr_cb, &tb_info);
+        if (ret < 0 || !tb[ETHA_BITSET_SIZE] || !tb[ETHA_BITSET_MASK])
+		return NULL;
+	count = mnl_attr_get_u32(tb[ETHA_BITSET_SIZE]);
+	if (32 * mnl_attr_get_payload_len(tb[ETHA_BITSET_MASK]) < count)
+		return NULL;
+
+	return mnl_attr_get_payload(tb[ETHA_BITSET_MASK]);
+}
+
+struct feature_results {
+	uint32_t	*hw;
+	uint32_t	*wanted;
+	uint32_t	*active;
+	uint32_t	*nochange;
+	unsigned int	count;
+	unsigned int	words;
+};
+
+static int prepare_feature_results(const struct nlattr *bitset,
+				   struct feature_results *dest)
+{
+	const struct nlattr *tb[ETHA_FEATURES_MAX + 1] = {};
+	DECLARE_ATTR_TB_INFO(tb);
+	unsigned int count;
+	int ret;
+
+	memset(dest, '\0', sizeof(*dest));
+	ret = mnl_attr_parse_nested(bitset, attr_cb, &tb_info);
+	if (ret < 0)
+		return ret;
+	if (!tb[ETHA_FEATURES_HW] || !tb[ETHA_FEATURES_WANTED] ||
+	    !tb[ETHA_FEATURES_ACTIVE] || !tb[ETHA_FEATURES_NOCHANGE])
+		return -EFAULT;
+	count = bitset_get_count(tb[ETHA_FEATURES_HW], &ret);
+	if (ret < 0)
+		return -EFAULT;
+	if ((bitset_get_count(tb[ETHA_FEATURES_WANTED], &ret) != count) ||
+	    (bitset_get_count(tb[ETHA_FEATURES_ACTIVE], &ret) != count) ||
+	    (bitset_get_count(tb[ETHA_FEATURES_NOCHANGE], &ret) != count))
+		return -EFAULT;
+	dest->hw = get_compact_bitset_value(tb[ETHA_FEATURES_HW]);
+	dest->wanted = get_compact_bitset_value(tb[ETHA_FEATURES_WANTED]);
+	dest->active = get_compact_bitset_value(tb[ETHA_FEATURES_ACTIVE]);
+	dest->nochange = get_compact_bitset_value(tb[ETHA_FEATURES_NOCHANGE]);
+	if (!dest->hw || !dest->wanted || !dest->active || !dest->nochange)
+		return -EFAULT;
+	dest->count = count;
+	dest->words = (count + 31) / 32;
+
+	return 0;
+}
+
+static bool feature_on(const uint32_t *bitmap, unsigned int idx)
+{
+	return bitmap[idx / 32] & (1 << (idx % 32));
+}
+
+static void dump_feature(const struct feature_results *results,
+			 const uint32_t *ref, const uint32_t *ref_mask,
+			 unsigned int idx, const char *name, const char *prefix)
+{
+	const char *suffix = "";
+
+	if (!name || !*name)
+		return;
+	if (ref) {
+		if (ref_mask && !feature_on(ref_mask, idx))
+			return;
+		if ((!ref_mask || feature_on(ref_mask, idx)) &&
+		    (feature_on(results->active, idx) == feature_on(ref, idx)))
+			return;
+	}
+
+	if (!feature_on(results->hw, idx) || feature_on(results->nochange, idx))
+		suffix = " [fixed]";
+	else if (feature_on(results->active, idx) !=
+		 feature_on(results->wanted, idx))
+		suffix = feature_on(results->wanted, idx) ?
+			 " [requested on]" : " [requested off]";
+	printf("%s%s: %s%s\n", prefix, name,
+	       feature_on(results->active, idx) ? "on" : "off", suffix);
+}
+
+/* this assumes pattern contains no more than one asterisk */
+static bool _flag_pattern_match(const char *name, const char *pattern)
+{
+	const char *p_ast = strchr(pattern, '*');
+
+	if (p_ast) {
+		size_t name_len = strlen(name);
+		size_t pattern_len = strlen(pattern);
+
+		if (name_len + 1 < pattern_len)
+			return false;
+		if (strncmp(name, pattern, p_ast - pattern))
+			return false;
+		pattern_len -= (p_ast - pattern) + 1;
+		name += name_len  - pattern_len;
+		pattern = p_ast + 1;
+	}
+	return !strcmp(name, pattern);
+}
+
+static bool flag_pattern_match(const char *name, const char *pattern)
+{
+	bool ret = _flag_pattern_match(name, pattern);
+
+	return ret;
+}
+
+int dump_features(const struct nlattr *bitset)
+{
+	const struct stringset *feature_names;
+	struct feature_results results;
+	unsigned int i, j;
+	int *feature_flags = NULL;
+	int ret;
+
+	ret = prepare_feature_results(bitset, &results);
+	if (ret < 0)
+		return -EFAULT;
+
+	ret = -ENOMEM;
+	feature_flags = calloc(results.count, sizeof(feature_flags[0]));
+	if (!feature_flags)
+	       goto out_free;
+	feature_names = global_stringset(ETH_SS_FEATURES);
+
+	/* map netdev features to legacy flags */
+	for (i = 0; i < results.count; i++) {
+		const char *name = get_string(feature_names, i);
+		feature_flags[i] = -1;
+
+		if (!name || !*name)
+			continue;
+		for (j = 0; j < OFF_FLAG_DEF_SIZE; j++) {
+			const char *flag_name = off_flag_def[j].kernel_name;
+
+			if (flag_pattern_match(name, flag_name)) {
+				feature_flags[i] = j;
+				break;
+			}
+		}
+	}
+	/* show legacy flags and their matching features first */
+	for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+		unsigned int n_match = 0;
+		bool flag_value = false;
+
+		for (j = 0; j < results.count; j++) {
+			if (feature_flags[j] == i) {
+				n_match++;
+				flag_value = flag_value ||
+					     feature_on(results.active, j);
+			}
+		}
+		if (n_match != 1)
+			printf("%s: %s\n", off_flag_def[i].long_name,
+			       flag_value ? "on" : "off");
+		if (n_match == 0)
+			continue;
+		for (j = 0; j < results.count; j++) {
+			const char *name = get_string(feature_names, j);
+
+			if (feature_flags[j] != i)
+				continue;
+			if (n_match == 1)
+				dump_feature(&results, NULL, NULL, j,
+					     off_flag_def[i].long_name, "");
+			else
+				dump_feature(&results, NULL, NULL, j, name,
+					     "\t");
+		}
+	}
+	/* and, finally, remaining netdev_features not matching legacy flags */
+	for (i = 0; i < results.count; i++) {
+		const char *name = get_string(feature_names, i);
+
+		if (!name || !*name || feature_flags[i] >= 0)
+			continue;
+		dump_feature(&results, NULL, NULL, i, name, "");
+	}
+
+out_free:
+	free(feature_flags);
+	return 0;
+}
+
 int settings_reply_cb(const struct nlmsghdr *nlhdr, void *data)
 {
 	const struct nlattr *tb[ETHA_SETTINGS_MAX + 1] = {};
@@ -262,6 +476,15 @@ int settings_reply_cb(const struct nlmsghdr *nlhdr, void *data)
 		printf("\tLink detected: %s\n", link ? "yes" : "no");
 		allfail = false;
 	};
+	if (tb[ETHA_SETTINGS_FEATURES] &&
+	    mask_ok(nlctx, ETH_SETTINGS_IM_FEATURES)) {
+		int ret;
+
+		printf("Features for %s:\n", nlctx->devname);
+		ret = dump_features(tb[ETHA_SETTINGS_FEATURES]);
+		if (ret == 0)
+			allfail = false;
+	}
 
 	if (allfail && !nlctx->is_monitor) {
 		fputs("No data available\n", stdout);
@@ -299,6 +522,9 @@ int nl_gset(struct cmd_context *ctx)
 				     ETH_SETTINGS_IM_LINK);
 }
 
+int nl_gfeatures(struct cmd_context *ctx)
+{
+	return settings_request(ctx, ETH_SETTINGS_IM_FEATURES);
 }
 
 /* SET_SETTINGS */
-- 
2.18.0


  parent reply	other threads:[~2018-07-30 12:56 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-30 12:55 [RFC PATCH ethtool v2 00/23] ethtool netlink interface (userspace side) (WiP) Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 01/23] move UAPI header copies to a separate directory Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 02/23] update UAPI header copies Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 03/23] netlink: add netlink interface Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 04/23] netlink: add support for string sets Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 05/23] netlink: add notification monitor Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 06/23] netlink: add netlink handler for gdrv (-i) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 07/23] netlink: add netlink handler for gset (no option) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 08/23] netlink: add helpers for command line parsing Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 09/23] netlink: add netlink handler for sset (-s) Michal Kubecek
2018-07-30 12:56 ` Michal Kubecek [this message]
2018-07-30 12:56 ` [RFC PATCH ethtool v2 11/23] netlink: add netlink handler for sfeatures (-K) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 12/23] netlink: add netlink handler for gcoalesce (-c) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 13/23] netlink: add netlink handler for gring (-g) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 14/23] netlink: add netlink handler for gpause (-a) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 15/23] netlink: add netlink handler for gchannels (-l) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 16/23] netlink: add netlink handler for geee (--show-eee) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 17/23] netlink: add netlink handler for gfec (--show-fec) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 18/23] netlink: add netlink handler for scoalesce (-C) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 19/23] netlink: add netlink handler for sring (-G) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 20/23] netlink: add netlink handler for spause (-A) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 21/23] netlink: add netlink handler for schannels (-L) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 22/23] netlink: add netlink handler for seee (--set-eee) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 23/23] netlink: add netlink handler for sfec (--set-fec) 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=184c7cae56aa3fa9a2116874cd7024ec45f4d05e.1532954671.git.mkubecek@suse.cz \
    --to=mkubecek@suse.cz \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).