[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <feef455dde958796b578892a48d26461e9d75fd9.1305811240.git.mirq-linux@rere.qmqm.pl>
Date: Thu, 19 May 2011 15:25:59 +0200 (CEST)
From: Michał Mirosław <mirq-linux@...e.qmqm.pl>
To: Ben Hutchings <bhutchings@...arflare.com>
Cc: netdev@...r.kernel.org, David Miller <davem@...emloft.net>
Subject: [RFC PATCH v3 ethtool] ethtool: implement [GS]FEATURES calls
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@...e.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;
}
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists