[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <feef455dde958796b578892a48d26461e9d75fd9.1305576698.git.mirq-linux@rere.qmqm.pl>
Date: Mon, 16 May 2011 22:54:20 +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 ethtool] ethtool: implement G/SFEATURES calls
Signed-off-by: Michał Mirosław <mirq-linux@...e.qmqm.pl>
---
ethtool.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 293 insertions(+), 6 deletions(-)
diff --git a/ethtool.c b/ethtool.c
index 34fe107..86a5a8b 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,9 @@ 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_sfeatures(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);
@@ -119,6 +123,8 @@ static enum {
MODE_SRING,
MODE_GOFFLOAD,
MODE_SOFFLOAD,
+ MODE_GFEATURES,
+ MODE_SFEATURES,
MODE_GSTATS,
MODE_GNFC,
MODE_SNFC,
@@ -197,6 +203,10 @@ static struct option {
" [ ntuple on|off ]\n"
" [ rxhash on|off ]\n"
},
+ { "-w", "--show-features", MODE_GFEATURES, "Get offload status" },
+ { "-W", "--request-features", MODE_SFEATURES, "Set requested offload",
+ " [ feature-name on|off [...] ]\n"
+ " see --show-features 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"
@@ -768,6 +778,8 @@ static void parse_cmdline(int argc, char **argp)
(mode == MODE_SRING) ||
(mode == MODE_GOFFLOAD) ||
(mode == MODE_SOFFLOAD) ||
+ (mode == MODE_GFEATURES) ||
+ (mode == MODE_SFEATURES) ||
(mode == MODE_GSTATS) ||
(mode == MODE_GNFC) ||
(mode == MODE_SNFC) ||
@@ -858,6 +870,11 @@ static void parse_cmdline(int argc, char **argp)
i = argc;
break;
}
+ if (mode == MODE_SFEATURES) {
+ parse_sfeatures_args(argc, argp, i);
+ i = argc;
+ break;
+ }
if (mode == MODE_SCLSRULE) {
if (!strcmp(argp[i], "flow-type")) {
i += 1;
@@ -1867,21 +1884,30 @@ static int dump_rxfhash(int fhash, u64 val)
return 0;
}
-static int doit(void)
+static int get_control_socket(struct ifreq *ifr)
{
- struct ifreq ifr;
int fd;
/* Setup our control structures. */
- memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, devname);
+ memset(ifr, 0, sizeof(*ifr));
+ strcpy(ifr->ifr_name, devname);
/* Open control socket. */
fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (fd < 0) {
+ if (fd < 0)
perror("Cannot get control socket");
+
+ return 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) {
@@ -1918,6 +1944,10 @@ static int doit(void)
return do_goffload(fd, &ifr);
} else if (mode == MODE_SOFFLOAD) {
return do_soffload(fd, &ifr);
+ } else if (mode == MODE_GFEATURES) {
+ return do_gfeatures(fd, &ifr);
+ } else if (mode == MODE_SFEATURES) {
+ return do_sfeatures(fd, &ifr);
} else if (mode == MODE_GSTATS) {
return do_gstats(fd, &ifr);
} else if (mode == MODE_GNFC) {
@@ -2355,6 +2385,263 @@ static int do_soffload(int fd, struct ifreq *ifr)
return 0;
}
+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);
+
+ if ((err < 0) ||
+ (!(sset_info->sset_mask & (1ULL << ETH_SS_FEATURES)))) {
+ perror("Cannot get driver strings info");
+ return -100;
+ }
+
+ n_strings = sset_info->data[0];
+ free(sset_info);
+ sz_str = n_strings * ETH_GSTRING_LEN;
+
+ strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings));
+ if (!strings) {
+ fprintf(stderr, "no memory available\n");
+ return -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);
+ return -96;
+ }
+
+ *strs = strings;
+ return n_strings;
+}
+
+struct ethtool_sfeatures *features_req;
+
+static void parse_sfeatures_args(int argc, char **argp, int argi)
+{
+ struct cmdline_info *cmdline_desc, *cp;
+ struct ethtool_gstrings *strings;
+ struct ifreq ifr;
+ int n_strings, sz_features, i;
+ int fd, changed = 0;
+
+ fd = get_control_socket(&ifr);
+ if (fd < 0)
+ exit(100);
+
+ n_strings = get_feature_strings(fd, &ifr, &strings);
+ if (n_strings < 0)
+ exit(-n_strings);
+
+ sz_features = sizeof(*features_req->features) * ((n_strings + 31) / 32);
+
+ cp = cmdline_desc = calloc(n_strings, sizeof(*cmdline_desc));
+ 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_strings + 31) / 32;
+
+ for (i = 0; i < n_strings; ++i) {
+ if (!strings->data[i*ETH_GSTRING_LEN])
+ continue;
+
+ strings->data[i*ETH_GSTRING_LEN + ETH_GSTRING_LEN-1] = 0;
+ cp->name = (const char *)strings->data + i * ETH_GSTRING_LEN;
+ 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);
+ free(strings);
+ close(fd);
+
+ if (!changed) {
+ free(features_req);
+ features_req = NULL;
+ }
+}
+
+static int send_gfeatures(int fd, struct ifreq *ifr, int n_strings,
+ struct ethtool_gfeatures **features_p)
+{
+ struct ethtool_gfeatures *features;
+ int err, sz_features;
+
+ sz_features = sizeof(*features->features) * ((n_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_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_gstrings *strings;
+ struct ethtool_gfeatures *features;
+ int n_strings, err, i;
+
+ n_strings = get_feature_strings(fd, ifr, &strings);
+ if (n_strings < 0)
+ return -n_strings;
+
+ err = send_gfeatures(fd, ifr, n_strings, &features);
+ if (err) {
+ free(strings);
+ return err;
+ }
+
+ fprintf(stdout, "Offload state: (name: enabled,wanted,changable)\n");
+ for (i = 0; i < n_strings; i++) {
+ if (!strings->data[i * ETH_GSTRING_LEN])
+ 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,
+ &strings->data[i * ETH_GSTRING_LEN],
+ P_FLAG(active), PA_FLAG(requested), PN_FLAG(available));
+#undef P_FLAG
+#undef PA_FLAG
+#undef PN_FLAG
+ }
+ free(strings);
+ 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) {
+ const char *name = &strings[i * ETH_GSTRING_LEN];
+ u32 mask = 1 << i;
+
+ if (!((expected->active ^ set->active) & mask))
+ continue;
+
+ fprintf(stderr, "feature %.*s is %s (expected: %s, saved: %s)\n",
+ ETH_GSTRING_LEN, name,
+ set->active & mask ? "enabled" : "disabled",
+ expected->active & mask ? "enabled" : "disabled",
+ !(set->available & mask) ? "not user-changeable" :
+ set->requested & mask ? "enabled" : "disabled"
+ );
+ }
+}
+
+static int do_sfeatures(int fd, struct ifreq *ifr)
+{
+ struct ethtool_gstrings *strings;
+ struct ethtool_gfeatures *features0, *features1;
+ int n_strings, err, i;
+
+ if (!features_req) {
+ fprintf(stderr, "no features changed\n");
+ return 97;
+ }
+
+ n_strings = get_feature_strings(fd, ifr, &strings);
+ if (n_strings < 0) {
+ free(features_req);
+ return -n_strings;
+ }
+
+ err = send_gfeatures(fd, ifr, n_strings, &features0);
+ if (err) {
+ perror("Cannot read features");
+ goto free_strings;
+ }
+
+ features_req->cmd = ETHTOOL_SFEATURES;
+ ifr->ifr_data = (caddr_t) features_req;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot change features");
+ err = 97;
+ goto free_features;
+ }
+
+ err = send_gfeatures(fd, ifr, n_strings, &features1);
+ if (err) {
+ perror("Cannot verify features");
+ goto free_features;
+ }
+
+ /* make features0 .active what we expect to be set */
+ i = (n_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_strings; i += 32)
+ print_gfeatures_diff(&features0->features[i / 32],
+ &features1->features[i / 32],
+ (char *)&strings->data[i * ETH_GSTRING_LEN],
+ n_strings - i);
+
+ free(features1);
+free_features:
+ free(features0);
+free_strings:
+ free(strings);
+ free(features_req);
+ return err;
+}
+
+
static int do_gset(int fd, struct ifreq *ifr)
{
int err;
--
1.7.2.5
--
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