All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michał Mirosław" <mirq-linux@rere.qmqm.pl>
To: Ben Hutchings <bhutchings@solarflare.com>
Cc: netdev@vger.kernel.org, David Miller <davem@davemloft.net>
Subject: [RFC PATCH v3 ethtool] ethtool: implement [GS]FEATURES calls
Date: Thu, 19 May 2011 15:25:59 +0200 (CEST)	[thread overview]
Message-ID: <feef455dde958796b578892a48d26461e9d75fd9.1305811240.git.mirq-linux@rere.qmqm.pl> (raw)
In-Reply-To: <20110519091833.GA24484@rere.qmqm.pl>

This is all-in-one PoC patch for [GS]FEATURES support and checking of
all feature changes when altering some.

Example result:

icybox:~# ./ethtool -K ge0 tx_checksum-ipv6 on
feature group tx is enabled (expected: disabled)
feature group sg is enabled (expected: disabled)
feature group gso is enabled (expected: disabled)
feature tx-scatter-gather is enabled (expected: disabled, saved: enabled)
feature tx-generic-segmentation is enabled (expected: disabled, saved: enabled)

Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
 ethtool.c |  583 +++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 494 insertions(+), 89 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index 34fe107..40456bb 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -33,6 +33,7 @@
 #include <limits.h>
 #include <ctype.h>
 
+#include <unistd.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -83,6 +84,8 @@ static int do_gcoalesce(int fd, struct ifreq *ifr);
 static int do_scoalesce(int fd, struct ifreq *ifr);
 static int do_goffload(int fd, struct ifreq *ifr);
 static int do_soffload(int fd, struct ifreq *ifr);
+static void parse_sfeatures_args(int argc, char **argp, int argi);
+static int do_gfeatures(int fd, struct ifreq *ifr);
 static int do_gstats(int fd, struct ifreq *ifr);
 static int rxflow_str_to_type(const char *str);
 static int parse_rxfhashopts(char *optstr, u32 *data);
@@ -196,7 +199,8 @@ static struct option {
 		"		[ txvlan on|off ]\n"
 		"		[ ntuple on|off ]\n"
 		"		[ rxhash on|off ]\n"
-    },
+		"		[ feature-name on|off [...] ]\n"
+		"		see --show-offload output for feature-name strings\n" },
     { "-i", "--driver", MODE_GDRV, "Show driver information" },
     { "-d", "--register-dump", MODE_GREGS, "Do a register dump",
 		"		[ raw on|off ]\n"
@@ -296,7 +300,6 @@ static void show_usage(void)
 
 static char *devname = NULL;
 
-static int goffload_changed = 0;
 static int off_csum_rx_wanted = -1;
 static int off_csum_tx_wanted = -1;
 static int off_sg_wanted = -1;
@@ -306,6 +309,9 @@ static int off_gso_wanted = -1;
 static u32 off_flags_wanted = 0;
 static u32 off_flags_mask = 0;
 static int off_gro_wanted = -1;
+static int n_feature_strings;
+static const char **feature_strings;
+static struct ethtool_sfeatures *features_req;
 
 static struct ethtool_pauseparam epause;
 static int gpause_changed = 0;
@@ -851,10 +857,7 @@ static void parse_cmdline(int argc, char **argp)
 				break;
 			}
 			if (mode == MODE_SOFFLOAD) {
-				parse_generic_cmdline(argc, argp, i,
-					&goffload_changed,
-			      		cmdline_offload,
-			      		ARRAY_SIZE(cmdline_offload));
+				parse_sfeatures_args(argc, argp, i);
 				i = argc;
 				break;
 			}
@@ -1788,9 +1791,15 @@ static int dump_coalesce(void)
 	return 0;
 }
 
-static int dump_offload(int rx, int tx, int sg, int tso, int ufo, int gso,
-			int gro, int lro, int rxvlan, int txvlan, int ntuple,
-			int rxhash)
+struct offload_state {
+	int rx, tx, sg, tso, ufo, gso, gro, lro, rxvlan, txvlan, ntuple, rxhash;
+};
+
+const char *const old_feature_names[] = {
+	"rx", "tx", "sg", "tso", "ufo", "gso", "gro", "lro", "rxvlan", "txvlan", "ntuple", "rxhash"
+};
+
+static int dump_offload(const struct offload_state *offload)
 {
 	fprintf(stdout,
 		"rx-checksumming: %s\n"
@@ -1805,18 +1814,18 @@ static int dump_offload(int rx, int tx, int sg, int tso, int ufo, int gso,
 		"tx-vlan-offload: %s\n"
 		"ntuple-filters: %s\n"
 		"receive-hashing: %s\n",
-		rx ? "on" : "off",
-		tx ? "on" : "off",
-		sg ? "on" : "off",
-		tso ? "on" : "off",
-		ufo ? "on" : "off",
-		gso ? "on" : "off",
-		gro ? "on" : "off",
-		lro ? "on" : "off",
-		rxvlan ? "on" : "off",
-		txvlan ? "on" : "off",
-		ntuple ? "on" : "off",
-		rxhash ? "on" : "off");
+		offload->rx ? "on" : "off",
+		offload->tx ? "on" : "off",
+		offload->sg ? "on" : "off",
+		offload->tso ? "on" : "off",
+		offload->ufo ? "on" : "off",
+		offload->gso ? "on" : "off",
+		offload->gro ? "on" : "off",
+		offload->lro ? "on" : "off",
+		offload->rxvlan ? "on" : "off",
+		offload->txvlan ? "on" : "off",
+		offload->ntuple ? "on" : "off",
+		offload->rxhash ? "on" : "off");
 
 	return 0;
 }
@@ -1867,21 +1876,33 @@ static int dump_rxfhash(int fhash, u64 val)
 	return 0;
 }
 
-static int doit(void)
-{
-	struct ifreq ifr;
-	int fd;
+static int control_fd = -1;
 
+static int get_control_socket(struct ifreq *ifr)
+{
 	/* Setup our control structures. */
-	memset(&ifr, 0, sizeof(ifr));
-	strcpy(ifr.ifr_name, devname);
+	memset(ifr, 0, sizeof(*ifr));
+	strcpy(ifr->ifr_name, devname);
+
+	if (control_fd >= 0)
+		return control_fd;
 
 	/* Open control socket. */
-	fd = socket(AF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
+	control_fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (control_fd < 0)
 		perror("Cannot get control socket");
+
+	return control_fd;
+}
+
+static int doit(void)
+{
+	struct ifreq ifr;
+	int fd;
+
+	fd = get_control_socket(&ifr);
+	if (fd < 0)
 		return 70;
-	}
 
 	/* all of these are expected to populate ifr->ifr_data as needed */
 	if (mode == MODE_GDRV) {
@@ -2139,14 +2160,13 @@ static int do_scoalesce(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_goffload(int fd, struct ifreq *ifr)
+static int send_goffloads(int fd, struct ifreq *ifr,
+	struct offload_state *offload)
 {
 	struct ethtool_value eval;
-	int err, allfail = 1, rx = 0, tx = 0, sg = 0;
-	int tso = 0, ufo = 0, gso = 0, gro = 0, lro = 0, rxvlan = 0, txvlan = 0,
-	    ntuple = 0, rxhash = 0;
+	int err, allfail = 1;
 
-	fprintf(stdout, "Offload parameters for %s:\n", devname);
+	memset(offload, 0, sizeof(*offload));
 
 	eval.cmd = ETHTOOL_GRXCSUM;
 	ifr->ifr_data = (caddr_t)&eval;
@@ -2154,7 +2174,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	if (err)
 		perror("Cannot get device rx csum settings");
 	else {
-		rx = eval.data;
+		offload->rx = eval.data;
 		allfail = 0;
 	}
 
@@ -2164,7 +2184,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	if (err)
 		perror("Cannot get device tx csum settings");
 	else {
-		tx = eval.data;
+		offload->tx = eval.data;
 		allfail = 0;
 	}
 
@@ -2174,7 +2194,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	if (err)
 		perror("Cannot get device scatter-gather settings");
 	else {
-		sg = eval.data;
+		offload->sg = eval.data;
 		allfail = 0;
 	}
 
@@ -2184,7 +2204,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	if (err)
 		perror("Cannot get device tcp segmentation offload settings");
 	else {
-		tso = eval.data;
+		offload->tso = eval.data;
 		allfail = 0;
 	}
 
@@ -2194,7 +2214,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	if (err)
 		perror("Cannot get device udp large send offload settings");
 	else {
-		ufo = eval.data;
+		offload->ufo = eval.data;
 		allfail = 0;
 	}
 
@@ -2204,7 +2224,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	if (err)
 		perror("Cannot get device generic segmentation offload settings");
 	else {
-		gso = eval.data;
+		offload->gso = eval.data;
 		allfail = 0;
 	}
 
@@ -2214,11 +2234,11 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	if (err) {
 		perror("Cannot get device flags");
 	} else {
-		lro = (eval.data & ETH_FLAG_LRO) != 0;
-		rxvlan = (eval.data & ETH_FLAG_RXVLAN) != 0;
-		txvlan = (eval.data & ETH_FLAG_TXVLAN) != 0;
-		ntuple = (eval.data & ETH_FLAG_NTUPLE) != 0;
-		rxhash = (eval.data & ETH_FLAG_RXHASH) != 0;
+		offload->lro = (eval.data & ETH_FLAG_LRO) != 0;
+		offload->rxvlan = (eval.data & ETH_FLAG_RXVLAN) != 0;
+		offload->txvlan = (eval.data & ETH_FLAG_TXVLAN) != 0;
+		offload->ntuple = (eval.data & ETH_FLAG_NTUPLE) != 0;
+		offload->rxhash = (eval.data & ETH_FLAG_RXHASH) != 0;
 		allfail = 0;
 	}
 
@@ -2228,130 +2248,515 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	if (err)
 		perror("Cannot get device GRO settings");
 	else {
-		gro = eval.data;
+		offload->gro = eval.data;
 		allfail = 0;
 	}
 
+	return -allfail;
+}
+
+static int do_goffload(int fd, struct ifreq *ifr)
+{
+	struct offload_state offload;
+	int err, allfail;
+
+	allfail = send_goffloads(fd, ifr, &offload);
+
+	if (!allfail) {
+		fprintf(stdout, "Offload parameters for %s:\n", devname);
+
+		dump_offload(&offload);
+	}
+
+	err = do_gfeatures(fd, ifr);
+	if (!err)
+		allfail = 0;
+
 	if (allfail) {
 		fprintf(stdout, "no offload info available\n");
 		return 83;
 	}
 
-	return dump_offload(rx, tx, sg, tso, ufo, gso, gro, lro, rxvlan, txvlan,
-			    ntuple, rxhash);
+	return 0;
 }
 
-static int do_soffload(int fd, struct ifreq *ifr)
+static int send_soffloads(int fd, struct ifreq *ifr)
 {
 	struct ethtool_value eval;
 	int err, changed = 0;
 
 	if (off_csum_rx_wanted >= 0) {
-		changed = 1;
 		eval.cmd = ETHTOOL_SRXCSUM;
 		eval.data = (off_csum_rx_wanted == 1);
 		ifr->ifr_data = (caddr_t)&eval;
 		err = send_ioctl(fd, ifr);
-		if (err) {
+		if (err)
 			perror("Cannot set device rx csum settings");
-			return 84;
-		}
+		else
+			changed = 1;
 	}
 
 	if (off_csum_tx_wanted >= 0) {
-		changed = 1;
 		eval.cmd = ETHTOOL_STXCSUM;
 		eval.data = (off_csum_tx_wanted == 1);
 		ifr->ifr_data = (caddr_t)&eval;
 		err = send_ioctl(fd, ifr);
-		if (err) {
+		if (err)
 			perror("Cannot set device tx csum settings");
-			return 85;
-		}
+		else
+			changed = 1;
 	}
 
 	if (off_sg_wanted >= 0) {
-		changed = 1;
 		eval.cmd = ETHTOOL_SSG;
 		eval.data = (off_sg_wanted == 1);
 		ifr->ifr_data = (caddr_t)&eval;
 		err = send_ioctl(fd, ifr);
-		if (err) {
+		if (err)
 			perror("Cannot set device scatter-gather settings");
-			return 86;
-		}
+		else
+			changed = 1;
 	}
 
 	if (off_tso_wanted >= 0) {
-		changed = 1;
 		eval.cmd = ETHTOOL_STSO;
 		eval.data = (off_tso_wanted == 1);
 		ifr->ifr_data = (caddr_t)&eval;
 		err = send_ioctl(fd, ifr);
-		if (err) {
+		if (err)
 			perror("Cannot set device tcp segmentation offload settings");
-			return 88;
-		}
+		else
+			changed = 1;
 	}
 	if (off_ufo_wanted >= 0) {
-		changed = 1;
 		eval.cmd = ETHTOOL_SUFO;
 		eval.data = (off_ufo_wanted == 1);
 		ifr->ifr_data = (caddr_t)&eval;
 		err = ioctl(fd, SIOCETHTOOL, ifr);
-		if (err) {
+		if (err)
 			perror("Cannot set device udp large send offload settings");
-			return 89;
-		}
+		else
+			changed = 1;
 	}
 	if (off_gso_wanted >= 0) {
-		changed = 1;
 		eval.cmd = ETHTOOL_SGSO;
 		eval.data = (off_gso_wanted == 1);
 		ifr->ifr_data = (caddr_t)&eval;
 		err = ioctl(fd, SIOCETHTOOL, ifr);
-		if (err) {
+		if (err)
 			perror("Cannot set device generic segmentation offload settings");
-			return 90;
-		}
+		else
+			changed = 1;
 	}
 	if (off_flags_mask) {
-		changed = 1;
 		eval.cmd = ETHTOOL_GFLAGS;
 		eval.data = 0;
 		ifr->ifr_data = (caddr_t)&eval;
 		err = ioctl(fd, SIOCETHTOOL, ifr);
 		if (err) {
 			perror("Cannot get device flag settings");
-			return 91;
-		}
-
-		eval.cmd = ETHTOOL_SFLAGS;
-		eval.data = ((eval.data & ~off_flags_mask) |
-			     off_flags_wanted);
-
-		err = ioctl(fd, SIOCETHTOOL, ifr);
-		if (err) {
-			perror("Cannot set device flag settings");
-			return 92;
+		} else {
+			eval.cmd = ETHTOOL_SFLAGS;
+			eval.data = ((eval.data & ~off_flags_mask) |
+				     off_flags_wanted);
+
+			err = ioctl(fd, SIOCETHTOOL, ifr);
+			if (err)
+				perror("Cannot set device flag settings");
+			else
+				changed = 1;
 		}
 	}
 	if (off_gro_wanted >= 0) {
-		changed = 1;
 		eval.cmd = ETHTOOL_SGRO;
 		eval.data = (off_gro_wanted == 1);
 		ifr->ifr_data = (caddr_t)&eval;
 		err = ioctl(fd, SIOCETHTOOL, ifr);
-		if (err) {
+		if (err)
 			perror("Cannot set device GRO settings");
-			return 93;
+		else
+			changed = 1;
+	}
+
+	return changed;
+}
+
+static int get_feature_strings(int fd, struct ifreq *ifr,
+	struct ethtool_gstrings **strs)
+{
+	struct ethtool_sset_info *sset_info;
+	struct ethtool_gstrings *strings;
+	int sz_str, n_strings, err;
+
+	sset_info = malloc(sizeof(struct ethtool_sset_info) + sizeof(u32));
+	sset_info->cmd = ETHTOOL_GSSET_INFO;
+	sset_info->sset_mask = (1ULL << ETH_SS_FEATURES);
+	ifr->ifr_data = (caddr_t)sset_info;
+	err = send_ioctl(fd, ifr);
+
+	n_strings = sset_info->data[0];
+	free(sset_info);
+
+	if ((err < 0) ||
+	    (!(sset_info->sset_mask & (1ULL << ETH_SS_FEATURES))) ||
+	    (n_strings == 0)) {
+		return -100;
+	}
+
+	sz_str = n_strings * ETH_GSTRING_LEN;
+	strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings));
+	if (!strings) {
+		fprintf(stderr, "no memory available\n");
+		exit(95);
+	}
+
+	strings->cmd = ETHTOOL_GSTRINGS;
+	strings->string_set = ETH_SS_FEATURES;
+	strings->len = n_strings;
+	ifr->ifr_data = (caddr_t) strings;
+	err = send_ioctl(fd, ifr);
+	if (err < 0) {
+		perror("Cannot get feature strings information");
+		free(strings);
+		exit(96);
+	}
+
+	*strs = strings;
+	return n_strings;
+}
+
+static int init_feature_strings(void)
+{
+	struct ethtool_gstrings *strings;
+	struct ifreq ifr;
+	int fd, i, n;
+
+	if (feature_strings)
+		return n_feature_strings;
+
+	fd = get_control_socket(&ifr);
+	if (fd < 0)
+		exit(100);
+
+	n = get_feature_strings(fd, &ifr, &strings);
+
+	if (n < 0)
+		return n;
+
+	n_feature_strings = n;
+	feature_strings = calloc(n, sizeof(*feature_strings));
+	if (!feature_strings) {
+		fprintf(stderr, "no memory available for string table [size=%d]\n", n);
+		exit(95);
+	}
+
+	for (i = 0; i < n; ++i) {
+		if (!strings->data[i*ETH_GSTRING_LEN])
+			continue;
+
+		feature_strings[i] = strndup(
+			(const char *)&strings->data[i * ETH_GSTRING_LEN],
+			ETH_GSTRING_LEN);
+
+		if (!feature_strings[i]) {
+			fprintf(stderr, "no memory available for a string\n");
+			exit(95);
 		}
 	}
 
+	free(strings);
+	return n;
+}
+
+static void parse_sfeatures_args(int argc, char **argp, int argi)
+{
+	struct cmdline_info *cmdline_desc, *cp;
+	int sz_features, i;
+	int changed = 0;
+
+	if (init_feature_strings() < 0) {
+		/* ETHTOOL_GFEATURES unavailable */
+		parse_generic_cmdline(argc, argp, argi, &changed,
+			cmdline_offload, ARRAY_SIZE(cmdline_offload));
+		return;
+	}
+
+	sz_features = sizeof(*features_req->features) * ((n_feature_strings + 31) / 32);
+
+	cp = cmdline_desc = calloc(n_feature_strings + ARRAY_SIZE(cmdline_offload),
+		sizeof(*cmdline_desc));
+	memcpy(cp, cmdline_offload, sizeof(cmdline_offload));
+	cp += ARRAY_SIZE(cmdline_offload);
+
+	features_req = calloc(1, sizeof(*features_req) + sz_features);
+	if (!cmdline_desc || !features_req) {
+		fprintf(stderr, "no memory available\n");
+		exit(95);
+	}
+
+	features_req->size = (n_feature_strings + 31) / 32;
+
+	for (i = 0; i < n_feature_strings; ++i) {
+		if (!feature_strings[i])
+			continue;
+
+		cp->name = feature_strings[i];
+		cp->type = CMDL_FLAG;
+		cp->flag_val = 1 << (i % 32);
+		cp->wanted_val = &features_req->features[i / 32].requested;
+		cp->seen_val = &features_req->features[i / 32].valid;
+		++cp;
+	}
+
+	parse_generic_cmdline(argc, argp, argi, &changed,
+		cmdline_desc, cp - cmdline_desc);
+
+	free(cmdline_desc);
+
+	if (!changed) {
+		free(features_req);
+		features_req = NULL;
+	}
+}
+
+static int send_gfeatures(int fd, struct ifreq *ifr,
+	struct ethtool_gfeatures **features_p)
+{
+	struct ethtool_gfeatures *features;
+	int err, sz_features;
+
+	sz_features = sizeof(*features->features) * ((n_feature_strings + 31) / 32);
+	features = calloc(1, sz_features + sizeof(*features));
+	if (!features) {
+		fprintf(stderr, "no memory available\n");
+		return 95;
+	}
+
+	features->cmd = ETHTOOL_GFEATURES;
+	features->size = (n_feature_strings + 31) / 32;
+	ifr->ifr_data = (caddr_t) features;
+	err = send_ioctl(fd, ifr);
+
+	if (err < 0) {
+		perror("Cannot get feature status");
+		free(features);
+		return 97;
+	}
+
+	*features_p = features;
+	return 0;
+}
+
+static int do_gfeatures(int fd, struct ifreq *ifr)
+{
+	struct ethtool_gfeatures *features;
+	int err, i;
+
+	err = init_feature_strings();
+	if (err < 0)
+		return -err;
+
+	err = send_gfeatures(fd, ifr, &features);
+	if (err)
+		return err;
+
+	fprintf(stdout, "\nFull offload state:  (feature-name: active,wanted,changable)\n");
+	for (i = 0; i < n_feature_strings; i++) {
+		if (!feature_strings[i])
+			continue;	/* empty */
+#define P_FLAG(f) \
+	(features->features[i / 32].f & (1 << (i % 32))) ? "yes" : " no"
+#define PA_FLAG(f) \
+	(features->features[i / 32].available & (1 << (i % 32))) ? P_FLAG(f) : "---"
+#define PN_FLAG(f) \
+	(features->features[i / 32].never_changed & (1 << (i % 32))) ? "---" : P_FLAG(f)
+		fprintf(stdout, "     %-*.*s %s,%s,%s\n",
+			ETH_GSTRING_LEN, ETH_GSTRING_LEN, feature_strings[i],
+			P_FLAG(active), PA_FLAG(requested), PN_FLAG(available));
+#undef P_FLAG
+#undef PA_FLAG
+#undef PN_FLAG
+	}
+	free(features);
+
+	return 0;
+}
+
+static void print_gfeatures_diff(
+	const struct ethtool_get_features_block *expected,
+	const struct ethtool_get_features_block *set,
+	const char **strings, int n_strings)
+{
+	int i;
+
+	if (n_strings > 32)
+		n_strings = 32;
+
+	for (i = 0; i < n_strings; ++i) {
+		u32 mask = 1 << i;
+
+		if (!strings[i])
+			continue;
+
+		if (!((expected->active ^ set->active) & mask))
+			continue;
+
+		fprintf(stderr, "feature %.*s is %s (expected: %s, saved: %s)\n",
+			ETH_GSTRING_LEN, strings[i],
+			set->active & mask ? "enabled" : "disabled",
+			expected->active & mask ? "enabled" : "disabled",
+			!(set->available & mask) ? "not user-changeable" :
+				set->requested & mask ? "enabled" : "disabled"
+		);
+	}
+}
+
+static int get_offload_state(int fd, struct ifreq *ifr,
+	struct ethtool_gfeatures **features,
+	struct offload_state *offload)
+{
+	int err, allfail;
+
+	allfail = send_goffloads(fd, ifr, offload);
+
+	err = init_feature_strings();
+	if (err < 0)
+		return allfail ? err : 0;
+
+	err = send_gfeatures(fd, ifr, features);
+	if (err)
+		perror("Cannot read features");
+
+	return allfail ? -err : 0;
+}
+
+static int send_sfeatures(int fd, struct ifreq *ifr)
+{
+	int err;
+
+	features_req->cmd = ETHTOOL_SFEATURES;
+	ifr->ifr_data = (caddr_t) features_req;
+	err = send_ioctl(fd, ifr);
+	if (err < 0) {
+		perror("Cannot change features");
+		return 97;
+	}
+
+	return 0;
+}
+
+static void compare_offload_state(struct offload_state *offload0,
+	struct offload_state *offload1)
+{
+	int *expected = (int *)offload0, *new = (int *)offload1;
+	int i;
+
+	if (off_csum_rx_wanted >= 0)
+		offload0->rx = off_csum_rx_wanted;
+
+	if (off_csum_tx_wanted >= 0)
+		offload0->tx = off_csum_tx_wanted;
+
+	if (off_sg_wanted >= 0)
+		offload0->sg = off_sg_wanted;
+
+	if (off_tso_wanted >= 0)
+		offload0->tso = off_tso_wanted;
+
+	if (off_ufo_wanted >= 0)
+		offload0->ufo = off_ufo_wanted;
+
+	if (off_gso_wanted >= 0)
+		offload0->gso = off_gso_wanted;
+
+	if (off_gro_wanted >= 0)
+		offload0->gro = off_gro_wanted;
+
+	if (off_flags_mask & ETH_FLAG_LRO)
+		offload0->lro = !!(off_flags_wanted & ETH_FLAG_LRO);
+
+	if (off_flags_mask & ETH_FLAG_RXVLAN)
+		offload0->rxvlan = !!(off_flags_wanted & ETH_FLAG_RXVLAN);
+
+	if (off_flags_mask & ETH_FLAG_TXVLAN)
+		offload0->txvlan = !!(off_flags_wanted & ETH_FLAG_TXVLAN);
+
+	if (off_flags_mask & ETH_FLAG_NTUPLE)
+		offload0->ntuple = !!(off_flags_wanted & ETH_FLAG_NTUPLE);
+
+	if (off_flags_mask & ETH_FLAG_RXHASH)
+		offload0->rxhash = !!(off_flags_wanted & ETH_FLAG_RXHASH);
+
+	for (i = 0; i < ARRAY_SIZE(old_feature_names); i++) {
+		if (expected[i] == new[i])
+			continue;
+
+		fprintf(stderr, "feature group %s is %s (expected: %s)\n",
+			old_feature_names[i],
+			new[i] ? "enabled" : "disabled",
+			expected[i] ? "enabled" : "disabled"
+		);
+	}
+}
+
+static void compare_features(struct ethtool_gfeatures *features0,
+	struct ethtool_gfeatures *features1)
+{
+	int i;
+
+	/* make features0 .active what we expect to be set */
+	i = (n_feature_strings + 31) / 32;
+	while (i--) {
+		features0->features[i].active &= ~features_req->features[i].valid;
+		features0->features[i].active |=
+			features_req->features[i].requested &
+			features_req->features[i].valid;
+	}
+
+	for (i = 0; i < n_feature_strings; i += 32)
+		print_gfeatures_diff(&features0->features[i / 32],
+			&features1->features[i / 32],
+			feature_strings + i,
+			n_feature_strings - i);
+}
+
+static int do_soffload(int fd, struct ifreq *ifr)
+{
+	struct ethtool_gfeatures *features_old, *features_new;
+	struct offload_state offload_old, offload_new;
+	int err, changed;
+
+	err = get_offload_state(fd, ifr, &features_old, &offload_old);
+	if (err)
+		return -err;
+
+	changed = send_soffloads(fd, ifr);
+
+	if (features_req) {
+		err = send_sfeatures(fd, ifr);
+		if (!err)
+			changed = 1;
+	}
+
 	if (!changed) {
 		fprintf(stdout, "no offload settings changed\n");
+		return err;
+	}
+
+	err = get_offload_state(fd, ifr, &features_new, &offload_new);
+	if (err) {
+		perror("can't verify offload setting");
+		return 101;
+	}
+	if ((!features_old) ^ (!features_new)) {
+		fprintf(stderr, "can't compare features (one GFEATURES failed)\n");
+		features_old = NULL;
 	}
 
+	compare_offload_state(&offload_old, &offload_new);
+	if (features_old)
+		compare_features(features_old, features_new);
+
 	return 0;
 }
 


  reply	other threads:[~2011-05-19 13:26 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-05-14  1:05 [PATCH net-2.6] ethtool: Remove fallback to old ethtool operations for ETHTOOL_SFEATURES Ben Hutchings
2011-05-14  9:54 ` Michał Mirosław
2011-05-14 20:08   ` Ben Hutchings
2011-05-14 10:31 ` [PATCH] net: fix ETHTOOL_SFEATURES compatibility with old ethtool_ops.set_flags Michał Mirosław
2011-05-14 10:35 ` [PATCH net-2.6] ethtool: Remove fallback to old ethtool operations for ETHTOOL_SFEATURES Michał Mirosław
2011-05-16  2:45   ` Ben Hutchings
2011-05-16 12:13     ` Michał Mirosław
2011-05-16 13:28     ` [PATCH] ethtool: ETHTOOL_SFEATURES: remove NETIF_F_COMPAT return Michał Mirosław
2011-05-16 13:37       ` Ben Hutchings
2011-05-16 14:23         ` Michał Mirosław
2011-05-16 14:53           ` Ben Hutchings
2011-05-16 15:01             ` Michał Mirosław
2011-05-16 15:57               ` [RFC PATCH ethtool 1/3] ethtool: Regularise offload feature settings Ben Hutchings
2011-05-16 15:57               ` [RFC PATCH ethtool 2/3] ethtool: Report any consequential offload feature changes Ben Hutchings
2011-05-16 15:58               ` [RFC PATCH ethtool 3/3] ethtool: Use ETHTOOL_{G,S}FEATURES where available Ben Hutchings
2011-05-16 20:51             ` [PATCH] ethtool: ETHTOOL_SFEATURES: remove NETIF_F_COMPAT return Michał Mirosław
2011-05-16 21:08               ` Ben Hutchings
2011-05-16 21:50                 ` Michał Mirosław
2011-05-16 22:09                   ` Ben Hutchings
2011-05-17  8:45                     ` Michał Mirosław
2011-05-17 20:33                     ` [RFC PATCH ethtool] ethtool: merge ETHTOOL_[GS]FEATURES support to -k/-K modes Michał Mirosław
2011-05-18 19:02                     ` [PATCH] ethtool: ETHTOOL_SFEATURES: remove NETIF_F_COMPAT return Ben Hutchings
2011-05-19  9:18                       ` Michał Mirosław
2011-05-19 13:25                         ` Michał Mirosław [this message]
2011-05-16 20:54             ` [RFC PATCH ethtool] ethtool: implement G/SFEATURES calls Michał Mirosław
2011-05-16 18:09           ` [PATCH] ethtool: ETHTOOL_SFEATURES: remove NETIF_F_COMPAT return David Miller
2011-05-19 10:03             ` Michał Mirosław
2011-05-24  9:14               ` Michał Mirosław
2011-05-24 19:39                 ` David Miller
2011-05-24 21:59                   ` Michał Mirosław
2011-05-27 14:13                     ` Ben Hutchings
2011-05-27 15:28                       ` Michał Mirosław
2011-05-27 15:45                         ` Ben Hutchings
2011-05-27 16:34                           ` Michał Mirosław
2011-05-27 23:25                             ` Ben Hutchings
2011-05-28  7:35                               ` Michał Mirosław
     [not found]                                 ` <20110528073525.GA19033-CoA6ZxLDdyEEUmgCuDUIdw@public.gmane.org>
2011-05-28 10:07                                   ` [Xen-devel] " Ian Campbell
     [not found]                                     ` <1306577228.23577.17.camel-ztPmHsLffjjnO4AKDKe2m+kiAK3p4hvP@public.gmane.org>
2011-05-28 17:31                                       ` Jesse Gross
     [not found]                                         ` <BANLkTime8PHYe+BFELt92gg7SZ91xKvAwA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2011-05-29  9:38                                           ` Michał Mirosław
     [not found]                                             ` <20110529093849.GA5245-CoA6ZxLDdyEEUmgCuDUIdw@public.gmane.org>
2011-05-31 18:43                                               ` Jesse Gross
2011-05-26 10:42                   ` [RESEND PATCH net] net: fix ETHTOOL_SFEATURES compatibility with old ethtool_ops.set_flags Michał Mirosław
2011-05-26 18:14                     ` David Miller
2011-05-14 10:41 ` [PATCH v2] " Michał Mirosław

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=feef455dde958796b578892a48d26461e9d75fd9.1305811240.git.mirq-linux@rere.qmqm.pl \
    --to=mirq-linux@rere.qmqm.pl \
    --cc=bhutchings@solarflare.com \
    --cc=davem@davemloft.net \
    --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.