* [PATCH ethtool 0/7] Generic netdev features support
@ 2012-06-02 0:33 Ben Hutchings
2012-06-02 0:35 ` [PATCH ethtool 1/7] Run tests in-process Ben Hutchings
` (7 more replies)
0 siblings, 8 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 0:33 UTC (permalink / raw)
To: netdev; +Cc: Michał Mirosław, Mahesh Bandewar
This series adds support for the not-so-new generic netdev features
ethtool API, along with some test infrastructure and test cases to check
that it works with old and new kernel versions. I'm still not convinced
that there are enough test cases, but I've held this back for too damn
long already.
I'd like to include these changes in an ethtool 3.4 release by the end
of next week, so let me know if there's anything wrong with them.
Ben.
Ben Hutchings (7):
Run tests in-process
Add output file parameter to dump_hex() and make it extern
test: Add test_ioctl() function to support a mock send_ioctl()
Add unit tests for setting offload features (old API)
Regularise offload feature settings
Report when offload feature changes are not exactly as requested
Change -k/-K options to use ETHTOOL_{G,S}FEATURES
.gitignore | 2 +-
Makefile.am | 10 +-
ethtool.8.in | 31 +--
ethtool.c | 668 +++++++++++++++++++++++++++++++++++--------------------
internal.h | 72 ++++++
test-cmdline.c | 14 +-
test-common.c | 357 +++++++++++++++++++++++++++---
test-features.c | 516 ++++++++++++++++++++++++++++++++++++++++++
8 files changed, 1358 insertions(+), 312 deletions(-)
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH ethtool 1/7] Run tests in-process
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
@ 2012-06-02 0:35 ` Ben Hutchings
2012-06-02 0:36 ` [PATCH ethtool 2/7] Add output file parameter to dump_hex() and make it extern Ben Hutchings
` (6 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 0:35 UTC (permalink / raw)
To: netdev; +Cc: Michał Mirosław, Mahesh Bandewar
Wrap main(), exit(), and resource management so that ethtool commands
can be tested without starting a new process and without leaking.
This will allow deeper teesting that covers ioctl requests and
responses.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
.gitignore | 1 -
Makefile.am | 6 +-
ethtool.c | 9 +-
internal.h | 42 ++++++++
test-cmdline.c | 7 ++
test-common.c | 311 ++++++++++++++++++++++++++++++++++++++++++++++++++------
6 files changed, 332 insertions(+), 44 deletions(-)
diff --git a/.gitignore b/.gitignore
index e10c4ef..62bf520 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,6 @@ ethtool.spec
ethtool.8
ethtool
test-cmdline
-test-one-cmdline
stamp-h1
config.*
aclocal.m4
diff --git a/Makefile.am b/Makefile.am
index 789cd9a..d1aec43 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,11 +12,9 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
rxclass.c sfpid.c
TESTS = test-cmdline
-check_PROGRAMS = test-cmdline test-one-cmdline
-test_cmdline_SOURCES = test-cmdline.c test-common.c
+check_PROGRAMS = test-cmdline
+test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES)
test_cmdline_CFLAGS = -DTEST_ETHTOOL
-test_one_cmdline_SOURCES = $(ethtool_SOURCES)
-test_one_cmdline_CFLAGS = -DTEST_ETHTOOL
dist-hook:
cp $(top_srcdir)/ethtool.spec $(distdir)
diff --git a/ethtool.c b/ethtool.c
index f18f611..65bdd38 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3261,16 +3261,13 @@ static int do_getmodule(struct cmd_context *ctx)
return 0;
}
+#ifndef TEST_ETHTOOL
int send_ioctl(struct cmd_context *ctx, void *cmd)
{
-#ifndef TEST_ETHTOOL
ctx->ifr.ifr_data = cmd;
return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
-#else
- /* If we get this far then parsing succeeded */
- exit(0);
-#endif
}
+#endif
static int show_usage(struct cmd_context *ctx);
@@ -3451,7 +3448,7 @@ static int show_usage(struct cmd_context *ctx)
return 0;
}
-int main(int argc, char **argp, char **envp)
+int main(int argc, char **argp)
{
int (*func)(struct cmd_context *);
int want_device;
diff --git a/internal.h b/internal.h
index 55f0d8a..10abe25 100644
--- a/internal.h
+++ b/internal.h
@@ -6,7 +6,11 @@
#ifdef HAVE_CONFIG_H
#include "ethtool-config.h"
#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
+#include <unistd.h>
#include <endian.h>
#include <sys/ioctl.h>
#include <net/if.h>
@@ -98,6 +102,44 @@ struct cmd_context {
#ifdef TEST_ETHTOOL
int test_cmdline(const char *args);
+
+#ifndef TEST_NO_WRAPPERS
+int test_main(int argc, char **argp);
+#define main(...) test_main(__VA_ARGS__)
+void test_exit(int rc) __attribute__((noreturn));
+#undef exit
+#define exit(rc) test_exit(rc)
+void *test_malloc(size_t size);
+#undef malloc
+#define malloc(size) test_malloc(size)
+void *test_calloc(size_t nmemb, size_t size);
+#undef calloc
+#define calloc(nmemb, size) test_calloc(nmemb, size)
+char *test_strdup(const char *s);
+#undef strdup
+#define strdup(s) test_strdup(s)
+void *test_free(void *ptr);
+#undef free
+#define free(ptr) test_free(ptr)
+void *test_realloc(void *ptr, size_t size);
+#undef realloc
+#define realloc(ptr, size) test_realloc(ptr, size)
+int test_open(const char *pathname, int flag, ...);
+#undef open
+#define open(...) test_open(__VA_ARGS__)
+int test_socket(int domain, int type, int protocol);
+#undef socket
+#define socket(...) test_socket(__VA_ARGS__)
+int test_close(int fd);
+#undef close
+#define close(fd) test_close(fd)
+FILE *test_fopen(const char *path, const char *mode);
+#undef fopen
+#define fopen(path, mode) test_fopen(path, mode)
+int test_fclose(FILE *fh);
+#undef fclose
+#define fclose(fh) test_fclose(fh)
+#endif
#endif
int send_ioctl(struct cmd_context *ctx, void *cmd);
diff --git a/test-cmdline.c b/test-cmdline.c
index c30b0e6..c62bb97 100644
--- a/test-cmdline.c
+++ b/test-cmdline.c
@@ -9,6 +9,7 @@
#include <stdio.h>
#include <stdlib.h>
+#define TEST_NO_WRAPPERS
#include "internal.h"
static struct test_case {
@@ -232,6 +233,12 @@ static struct test_case {
{ 1, "-0" },
};
+int send_ioctl(struct cmd_context *ctx, void *cmd)
+{
+ /* If we get this far then parsing succeeded */
+ test_exit(0);
+}
+
int main(void)
{
struct test_case *tc;
diff --git a/test-common.c b/test-common.c
index 4ea84c8..69853aa 100644
--- a/test-common.c
+++ b/test-common.c
@@ -2,27 +2,268 @@
* Common test functions for ethtool
* Copyright 2011 Solarflare Communications Inc.
*
+ * Partly derived from kernel <linux/list.h>.
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
+#include <assert.h>
+#include <setjmp.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/fcntl.h>
-#include <sys/wait.h>
#include <unistd.h>
+#define TEST_NO_WRAPPERS
#include "internal.h"
+/* List utilities */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+static void init_list_head(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static void list_add(struct list_head *new, struct list_head *head)
+{
+ head->next->prev = new;
+ new->next = head->next;
+ new->prev = head;
+ head->next = new;
+}
+
+static void list_del(struct list_head *entry)
+{
+ entry->next->prev = entry->prev;
+ entry->prev->next = entry->next;
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/* Free memory at end of test */
+
+static struct list_head malloc_list = LIST_HEAD_INIT(malloc_list);
+
+void *test_malloc(size_t size)
+{
+ struct list_head *block = malloc(sizeof(*block) + size);
+
+ if (!block)
+ return NULL;
+ list_add(block, &malloc_list);
+ return block + 1;
+}
+
+void *test_calloc(size_t nmemb, size_t size)
+{
+ void *ptr = test_malloc(nmemb * size);
+
+ if (ptr)
+ memset(ptr, 0, nmemb * size);
+ return ptr;
+}
+
+char *test_strdup(const char *s)
+{
+ size_t size = strlen(s) + 1;
+ char *dup = test_malloc(size);
+
+ if (dup)
+ memcpy(dup, s, size);
+ return dup;
+}
+
+void test_free(void *ptr)
+{
+ struct list_head *block;
+
+ if (!ptr)
+ return;
+ block = (struct list_head *)ptr - 1;
+ list_del(block);
+ free(block);
+}
+
+void *test_realloc(void *ptr, size_t size)
+{
+ struct list_head *block;
+
+ if (ptr) {
+ block = (struct list_head *)ptr - 1;
+ list_del(block);
+ }
+ block = realloc(block, sizeof(*block) + size);
+ if (!block)
+ return NULL;
+ list_add(block, &malloc_list);
+ return block + 1;
+}
+
+static void test_free_all(void)
+{
+ struct list_head *block, *next;
+
+ list_for_each_safe(block, next, &malloc_list)
+ free(block);
+ init_list_head(&malloc_list);
+}
+
+/* Close files at end of test */
+
+struct file_node {
+ struct list_head link;
+ FILE *fh;
+ int fd;
+};
+
+static struct list_head file_list = LIST_HEAD_INIT(file_list);
+
+int test_open(const char *pathname, int flag, ...)
+{
+ struct file_node *node;
+ mode_t mode;
+
+ if (flag & O_CREAT) {
+ va_list ap;
+ va_start(ap, flag);
+ mode = va_arg(ap, mode_t);
+ va_end(ap);
+ } else {
+ mode = 0;
+ }
+
+ node = malloc(sizeof(*node));
+ if (!node)
+ return -1;
+
+ node->fd = open(pathname, flag, mode);
+ if (node->fd < 0) {
+ free(node);
+ return -1;
+ }
+
+ node->fh = NULL;
+ list_add(&node->link, &file_list);
+ return node->fd;
+}
+
+int test_socket(int domain, int type, int protocol)
+{
+ struct file_node *node;
+
+ node = malloc(sizeof(*node));
+ if (!node)
+ return -1;
+
+ node->fd = socket(domain, type, protocol);
+ if (node->fd < 0) {
+ free(node);
+ return -1;
+ }
+
+ node->fh = NULL;
+ list_add(&node->link, &file_list);
+ return node->fd;
+}
+
+int test_close(int fd)
+{
+ struct list_head *head, *next;
+
+ if (fd >= 0) {
+ list_for_each_safe(head, next, &file_list) {
+ if (((struct file_node *)head)->fd == fd) {
+ list_del(head);
+ free(head);
+ break;
+ }
+ }
+ }
+
+ return close(fd);
+}
+
+FILE *test_fopen(const char *path, const char *mode)
+{
+ struct file_node *node;
+
+ node = malloc(sizeof(*node));
+ if (!node)
+ return NULL;
+
+ node->fh = fopen(path, mode);
+ if (!node->fh) {
+ free(node);
+ return NULL;
+ }
+
+ node->fd = -1;
+ list_add(&node->link, &file_list);
+ return node->fh;
+}
+
+int test_fclose(FILE *fh)
+{
+ struct list_head *head, *next;
+
+ assert(fh);
+
+ list_for_each_safe(head, next, &file_list) {
+ if (((struct file_node *)head)->fh == fh) {
+ list_del(head);
+ free(head);
+ break;
+ }
+ }
+
+ return fclose(fh);
+}
+
+static void test_close_all(void)
+{
+ struct list_head *head, *next;
+ struct file_node *node;
+
+ list_for_each_safe(head, next, &file_list) {
+ node = (struct file_node *)head;
+ if (node->fh)
+ fclose(node->fh);
+ else
+ close(node->fd);
+ free(node);
+ }
+ init_list_head(&file_list);
+}
+
+/* Wrap test main function */
+
+static jmp_buf test_return;
+
+void test_exit(int rc)
+{
+ longjmp(test_return, rc + 1);
+}
+
int test_cmdline(const char *args)
{
int argc, i;
char **argv;
const char *arg;
size_t len;
- pid_t pid;
- int dev_null;
- int status;
+ int dev_null = -1, old_stdout = -1, old_stderr = -1;
int rc;
/* Convert line to argv */
@@ -37,12 +278,12 @@ int test_cmdline(const char *args)
break;
arg += len + 1;
}
- argv = calloc(argc + 1, sizeof(argv[0]));
- argv[0] = strdup("ethtool");
+ argv = test_calloc(argc + 1, sizeof(argv[0]));
+ argv[0] = test_strdup("ethtool");
arg = args;
for (i = 1; i < argc; i++) {
len = strcspn(arg, " ");
- argv[i] = malloc(len + 1);
+ argv[i] = test_malloc(len + 1);
memcpy(argv[i], arg, len);
argv[i][len] = 0;
arg += len + 1;
@@ -56,37 +297,41 @@ int test_cmdline(const char *args)
}
fflush(NULL);
- pid = fork();
-
- /* Child */
- if (pid == 0) {
- dup2(dev_null, STDIN_FILENO);
- if (!getenv("ETHTOOL_TEST_VERBOSE")) {
- dup2(dev_null, STDOUT_FILENO);
- dup2(dev_null, STDERR_FILENO);
+ dup2(dev_null, STDIN_FILENO);
+ if (!getenv("TEST_TEST_VERBOSE")) {
+ old_stdout = dup(STDOUT_FILENO);
+ if (old_stdout < 0) {
+ perror("dup stdout");
+ rc = -1;
+ goto out;
}
- execv("./test-one-cmdline", argv);
- _exit(126);
+ dup2(dev_null, STDOUT_FILENO);
+ old_stderr = dup(STDERR_FILENO);
+ if (old_stderr < 0) {
+ perror("dup stderr");
+ rc = -1;
+ goto out;
+ }
+ dup2(dev_null, STDERR_FILENO);
}
- /* Parent */
- if (pid < 0) {
- perror("fork");
- close(dev_null);
- rc = -1;
- goto out;
+ rc = setjmp(test_return);
+ rc = rc ? rc - 1 : test_main(argc, argv);
+
+out:
+ fflush(NULL);
+ if (old_stderr >= 0) {
+ dup2(old_stderr, STDERR_FILENO);
+ close(old_stderr);
}
- close(dev_null);
- if (waitpid(pid, &status, 0) < 0) {
- perror("waitpid");
- rc = -1;
- goto out;
+ if (old_stdout >= 0) {
+ dup2(old_stdout, STDOUT_FILENO);
+ close(old_stdout);
}
- rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ if (dev_null >= 0)
+ close(dev_null);
-out:
- for (i = 0; i < argc; i++)
- free(argv[i]);
- free(argv);
+ test_free_all();
+ test_close_all();
return rc;
}
--
1.7.7.6
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ethtool 2/7] Add output file parameter to dump_hex() and make it extern
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
2012-06-02 0:35 ` [PATCH ethtool 1/7] Run tests in-process Ben Hutchings
@ 2012-06-02 0:36 ` Ben Hutchings
2012-06-02 0:37 ` [PATCH ethtool 3/7] test: Add test_ioctl() function to support a mock send_ioctl() Ben Hutchings
` (5 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 0:36 UTC (permalink / raw)
To: netdev; +Cc: Michał Mirosław, Mahesh Bandewar
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
ethtool.c | 19 ++++++++++---------
internal.h | 2 ++
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/ethtool.c b/ethtool.c
index 65bdd38..9d42ca8 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -787,18 +787,18 @@ static const struct {
{ "st_gmac", st_gmac_dump_regs },
};
-static void dump_hex(__u8 *data, int len, int offset)
+void dump_hex(FILE *file, const u8 *data, int len, int offset)
{
int i;
- fprintf(stdout, "Offset\t\tValues\n");
- fprintf(stdout, "------\t\t------");
+ fprintf(file, "Offset\t\tValues\n");
+ fprintf(file, "------\t\t------");
for (i = 0; i < len; i++) {
if (i % 16 == 0)
- fprintf(stdout, "\n0x%04x:\t\t", i + offset);
- fprintf(stdout, "%02x ", data[i]);
+ fprintf(file, "\n0x%04x:\t\t", i + offset);
+ fprintf(file, "%02x ", data[i]);
}
- fprintf(stdout, "\n");
+ fprintf(file, "\n");
}
static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
@@ -834,7 +834,7 @@ static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
ETHTOOL_BUSINFO_LEN))
return driver_list[i].func(info, regs);
- dump_hex(regs->data, regs->len, 0);
+ dump_hex(stdout, regs->data, regs->len, 0);
return 0;
}
@@ -853,7 +853,7 @@ static int dump_eeprom(int geeprom_dump_raw, struct ethtool_drvinfo *info,
return tg3_dump_eeprom(info, ee);
}
- dump_hex(ee->data, ee->len, ee->offset);
+ dump_hex(stdout, ee->data, ee->len, ee->offset);
return 0;
}
@@ -3253,7 +3253,8 @@ static int do_getmodule(struct cmd_context *ctx)
}
}
if (geeprom_dump_hex)
- dump_hex(eeprom->data, eeprom->len, eeprom->offset);
+ dump_hex(stdout, eeprom->data,
+ eeprom->len, eeprom->offset);
}
free(eeprom);
diff --git a/internal.h b/internal.h
index 10abe25..b013783 100644
--- a/internal.h
+++ b/internal.h
@@ -144,6 +144,8 @@ int test_fclose(FILE *fh);
int send_ioctl(struct cmd_context *ctx, void *cmd);
+void dump_hex(FILE *f, const u8 *data, int len, int offset);
+
/* National Semiconductor DP83815, DP83816 */
int natsemi_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
int natsemi_dump_eeprom(struct ethtool_drvinfo *info,
--
1.7.7.6
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ethtool 3/7] test: Add test_ioctl() function to support a mock send_ioctl()
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
2012-06-02 0:35 ` [PATCH ethtool 1/7] Run tests in-process Ben Hutchings
2012-06-02 0:36 ` [PATCH ethtool 2/7] Add output file parameter to dump_hex() and make it extern Ben Hutchings
@ 2012-06-02 0:37 ` Ben Hutchings
2012-06-02 0:37 ` [PATCH ethtool 4/7] Add unit tests for setting offload features (old API) Ben Hutchings
` (4 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 0:37 UTC (permalink / raw)
To: netdev; +Cc: Michał Mirosław, Mahesh Bandewar
test_ioctl() verifies an ethtool ioctl structure and either returns a
canned response (if it was as expected) or aborts the test.
Adjust the file redirection in test_cmdline() so that test_ioctl() can
always print error messages.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
internal.h | 10 ++++++++
test-common.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 68 insertions(+), 12 deletions(-)
diff --git a/internal.h b/internal.h
index b013783..0fff2a5 100644
--- a/internal.h
+++ b/internal.h
@@ -103,6 +103,16 @@ struct cmd_context {
#ifdef TEST_ETHTOOL
int test_cmdline(const char *args);
+struct cmd_expect {
+ const void *cmd; /* expected command; NULL at end of list */
+ size_t cmd_len; /* length to match (might be < sizeof struct) */
+ int rc; /* kernel return code */
+ const void *resp; /* response to write back; may be NULL */
+ size_t resp_len; /* length to write back */
+};
+int test_ioctl(const struct cmd_expect *expect, void *cmd);
+#define TEST_IOCTL_MISMATCH (-2)
+
#ifndef TEST_NO_WRAPPERS
int test_main(int argc, char **argp);
#define main(...) test_main(__VA_ARGS__)
diff --git a/test-common.c b/test-common.c
index 69853aa..adc3cd4 100644
--- a/test-common.c
+++ b/test-common.c
@@ -10,6 +10,7 @@
*/
#include <assert.h>
+#include <errno.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdlib.h>
@@ -251,19 +252,52 @@ static void test_close_all(void)
/* Wrap test main function */
static jmp_buf test_return;
+static FILE *orig_stderr;
void test_exit(int rc)
{
longjmp(test_return, rc + 1);
}
+int test_ioctl(const struct cmd_expect *expect, void *cmd)
+{
+ int rc;
+
+ if (!expect->cmd || *(u32 *)cmd != *(const u32 *)expect->cmd) {
+ /* We have no idea how long this command structure is */
+ fprintf(orig_stderr, "Unexpected ioctl: cmd=%#10x\n",
+ *(u32 *)cmd);
+ return TEST_IOCTL_MISMATCH;
+ }
+
+ if (memcmp(cmd, expect->cmd, expect->cmd_len)) {
+ fprintf(orig_stderr, "Expected ioctl structure:\n");
+ dump_hex(orig_stderr, expect->cmd, expect->cmd_len, 0);
+ fprintf(orig_stderr, "Actual ioctl structure:\n");
+ dump_hex(orig_stderr, cmd, expect->cmd_len, 0);
+ return TEST_IOCTL_MISMATCH;
+ }
+
+ if (expect->resp)
+ memcpy(cmd, expect->resp, expect->resp_len);
+ rc = expect->rc;
+
+ /* Convert kernel return code according to libc convention */
+ if (rc >= 0) {
+ return rc;
+ } else {
+ errno = -rc;
+ return -1;
+ }
+}
+
int test_cmdline(const char *args)
{
int argc, i;
char **argv;
const char *arg;
size_t len;
- int dev_null = -1, old_stdout = -1, old_stderr = -1;
+ int dev_null = -1, orig_stdout_fd = -1, orig_stderr_fd = -1;
int rc;
/* Convert line to argv */
@@ -298,20 +332,28 @@ int test_cmdline(const char *args)
fflush(NULL);
dup2(dev_null, STDIN_FILENO);
- if (!getenv("TEST_TEST_VERBOSE")) {
- old_stdout = dup(STDOUT_FILENO);
- if (old_stdout < 0) {
+ if (getenv("TEST_TEST_VERBOSE")) {
+ orig_stderr = stderr;
+ } else {
+ orig_stdout_fd = dup(STDOUT_FILENO);
+ if (orig_stdout_fd < 0) {
perror("dup stdout");
rc = -1;
goto out;
}
dup2(dev_null, STDOUT_FILENO);
- old_stderr = dup(STDERR_FILENO);
- if (old_stderr < 0) {
+ orig_stderr_fd = dup(STDERR_FILENO);
+ if (orig_stderr_fd < 0) {
perror("dup stderr");
rc = -1;
goto out;
}
+ orig_stderr = fdopen(orig_stderr_fd, "w");
+ if (orig_stderr == NULL) {
+ perror("fdopen orig_stderr_fd");
+ rc = -1;
+ goto out;
+ }
dup2(dev_null, STDERR_FILENO);
}
@@ -320,13 +362,17 @@ int test_cmdline(const char *args)
out:
fflush(NULL);
- if (old_stderr >= 0) {
- dup2(old_stderr, STDERR_FILENO);
- close(old_stderr);
+ if (orig_stderr_fd >= 0) {
+ dup2(orig_stderr_fd, STDERR_FILENO);
+ if (orig_stderr)
+ fclose(orig_stderr);
+ else
+ close(orig_stderr_fd);
}
- if (old_stdout >= 0) {
- dup2(old_stdout, STDOUT_FILENO);
- close(old_stdout);
+ orig_stderr = NULL;
+ if (orig_stdout_fd >= 0) {
+ dup2(orig_stdout_fd, STDOUT_FILENO);
+ close(orig_stdout_fd);
}
if (dev_null >= 0)
close(dev_null);
--
1.7.7.6
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ethtool 4/7] Add unit tests for setting offload features (old API)
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
` (2 preceding siblings ...)
2012-06-02 0:37 ` [PATCH ethtool 3/7] test: Add test_ioctl() function to support a mock send_ioctl() Ben Hutchings
@ 2012-06-02 0:37 ` Ben Hutchings
2012-06-02 0:38 ` [PATCH ethtool 5/7] Regularise offload feature settings Ben Hutchings
` (3 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 0:37 UTC (permalink / raw)
To: netdev; +Cc: Michał Mirosław, Mahesh Bandewar
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
.gitignore | 1 +
Makefile.am | 6 ++-
test-features.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 158 insertions(+), 2 deletions(-)
create mode 100644 test-features.c
diff --git a/.gitignore b/.gitignore
index 62bf520..45c9501 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ ethtool.spec
ethtool.8
ethtool
test-cmdline
+test-features
stamp-h1
config.*
aclocal.m4
diff --git a/Makefile.am b/Makefile.am
index d1aec43..1b96b98 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,10 +11,12 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
smsc911x.c at76c50x-usb.c sfc.c stmmac.c \
rxclass.c sfpid.c
-TESTS = test-cmdline
-check_PROGRAMS = test-cmdline
+TESTS = test-cmdline test-features
+check_PROGRAMS = test-cmdline test-features
test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES)
test_cmdline_CFLAGS = -DTEST_ETHTOOL
+test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES)
+test_features_CFLAGS = -DTEST_ETHTOOL
dist-hook:
cp $(top_srcdir)/ethtool.spec $(distdir)
diff --git a/test-features.c b/test-features.c
new file mode 100644
index 0000000..4d8680a
--- /dev/null
+++ b/test-features.c
@@ -0,0 +1,153 @@
+/****************************************************************************
+ * Test cases for ethtool features
+ * Copyright 2012 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define TEST_NO_WRAPPERS
+#include "internal.h"
+
+static const struct ethtool_value
+cmd_grxcsum_off = { ETHTOOL_GRXCSUM, 0 },
+cmd_grxcsum_on = { ETHTOOL_GRXCSUM, 1 },
+cmd_srxcsum_off = { ETHTOOL_SRXCSUM, 0 },
+cmd_srxcsum_on = { ETHTOOL_SRXCSUM, 1 },
+cmd_gtxcsum_off = { ETHTOOL_GTXCSUM, 0 },
+cmd_gtxcsum_on = { ETHTOOL_GTXCSUM, 1 },
+cmd_stxcsum_off = { ETHTOOL_STXCSUM, 0 },
+cmd_stxcsum_on = { ETHTOOL_STXCSUM, 1 },
+cmd_gsg_off = { ETHTOOL_GSG, 0 },
+cmd_gsg_on = { ETHTOOL_GSG, 1 },
+cmd_ssg_off = { ETHTOOL_SSG, 0 },
+cmd_ssg_on = { ETHTOOL_SSG, 1 },
+cmd_gtso_off = { ETHTOOL_GTSO, 0 },
+cmd_gtso_on = { ETHTOOL_GTSO, 1 },
+cmd_stso_off = { ETHTOOL_STSO, 0 },
+cmd_stso_on = { ETHTOOL_STSO, 1 },
+cmd_gufo_off = { ETHTOOL_GUFO, 0 },
+cmd_gufo_on = { ETHTOOL_GUFO, 1 },
+cmd_sufo_off = { ETHTOOL_SUFO, 0 },
+cmd_sufo_on = { ETHTOOL_SUFO, 1 },
+cmd_ggso_off = { ETHTOOL_GGSO, 0 },
+cmd_ggso_on = { ETHTOOL_GGSO, 1 },
+cmd_sgso_off = { ETHTOOL_SGSO, 0 },
+cmd_sgso_on = { ETHTOOL_SGSO, 1 },
+cmd_ggro_off = { ETHTOOL_GGRO, 0 },
+cmd_ggro_on = { ETHTOOL_GGRO, 1 },
+cmd_sgro_off = { ETHTOOL_SGRO, 0 },
+cmd_sgro_on = { ETHTOOL_SGRO, 1 },
+cmd_gflags_off = { ETHTOOL_GFLAGS, 0 },
+cmd_gflags_on = { ETHTOOL_GFLAGS,
+ ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
+ ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH },
+cmd_sflags_off = { ETHTOOL_SFLAGS, 0 },
+cmd_sflags_ntuple = { ETHTOOL_GFLAGS, ETH_FLAG_NTUPLE },
+cmd_sflags_on = { ETHTOOL_SFLAGS,
+ ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
+ ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH },
+cmd_sflags_not_rxhash = { ETHTOOL_SFLAGS,
+ ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
+ ETH_FLAG_NTUPLE };
+
+static const struct cmd_expect cmd_expect_get_features_off[] = {
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_off[] = {
+ { &cmd_srxcsum_off, sizeof(cmd_srxcsum_off), 0, 0, 0 },
+ { &cmd_stxcsum_off, sizeof(cmd_stxcsum_off), 0, 0, 0 },
+ { &cmd_ssg_off, sizeof(cmd_ssg_off), 0, 0, 0 },
+ { &cmd_stso_off, sizeof(cmd_stso_off), 0, 0, 0 },
+ { &cmd_sufo_off, sizeof(cmd_sufo_off), 0, 0, 0 },
+ { &cmd_sgso_off, sizeof(cmd_sgso_off), 0, 0, 0 },
+ { &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
+ { &cmd_sflags_off, sizeof(cmd_sflags_off), 0, 0, 0 },
+ { &cmd_sgro_off, sizeof(cmd_sgro_off), 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_on[] = {
+ { &cmd_srxcsum_on, sizeof(cmd_srxcsum_on), 0, 0, 0 },
+ { &cmd_stxcsum_on, sizeof(cmd_stxcsum_on), 0, 0, 0 },
+ { &cmd_ssg_on, sizeof(cmd_ssg_on), 0, 0, 0 },
+ { &cmd_stso_on, sizeof(cmd_stso_on), 0, 0, 0 },
+ { &cmd_sufo_on, sizeof(cmd_sufo_on), 0, 0, 0 },
+ { &cmd_sgso_on, sizeof(cmd_sgso_on), 0, 0, 0 },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_off) },
+ { &cmd_sflags_on, sizeof(cmd_sflags_on), 0, 0, 0 },
+ { &cmd_sgro_on, sizeof(cmd_sgro_on), 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+static struct test_case {
+ int rc;
+ const char *args;
+ const struct cmd_expect *expect;
+} const test_cases[] = {
+ { 0, "-k devname", cmd_expect_get_features_off },
+ { 0, "-K devname rx off tx off sg off tso off ufo off gso off lro off rxvlan off txvlan off ntuple off rxhash off gro off",
+ cmd_expect_set_features_off },
+ { 0, "-K devname rx on tx on sg on tso on ufo on gso on lro on rxvlan on txvlan on ntuple on rxhash on gro on",
+ cmd_expect_set_features_on },
+};
+
+static int expect_matched;
+static const struct cmd_expect *expect_next;
+
+int send_ioctl(struct cmd_context *ctx, void *cmd)
+{
+ int rc = test_ioctl(expect_next, cmd);
+
+ if (rc == TEST_IOCTL_MISMATCH) {
+ expect_matched = 0;
+ test_exit(0);
+ }
+ expect_next++;
+ return rc;
+}
+
+int main(void)
+{
+ const struct test_case *tc;
+ int test_rc;
+ int rc = 0;
+
+ for (tc = test_cases; tc < test_cases + ARRAY_SIZE(test_cases); tc++) {
+ if (getenv("ETHTOOL_TEST_VERBOSE"))
+ printf("I: Test command line: ethtool %s\n", tc->args);
+ expect_matched = 1;
+ expect_next = tc->expect;
+ test_rc = test_cmdline(tc->args);
+
+ /* If we found a mismatch, or there is still another
+ * expected ioctl to match, the test failed.
+ */
+ if (!expect_matched || expect_next->cmd) {
+ fprintf(stderr,
+ "E: ethtool %s deviated from the expected "
+ "ioctl sequence after %zu calls\n",
+ tc->args, expect_next - tc->expect);
+ rc = 1;
+ } else if (test_rc != tc->rc) {
+ fprintf(stderr, "E: ethtool %s returns %d\n",
+ tc->args, test_rc);
+ rc = 1;
+ }
+ }
+
+ return rc;
+}
--
1.7.7.6
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ethtool 5/7] Regularise offload feature settings
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
` (3 preceding siblings ...)
2012-06-02 0:37 ` [PATCH ethtool 4/7] Add unit tests for setting offload features (old API) Ben Hutchings
@ 2012-06-02 0:38 ` Ben Hutchings
2012-06-02 0:39 ` [PATCH ethtool 6/7] Report when offload feature changes are not exactly as requested Ben Hutchings
` (2 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 0:38 UTC (permalink / raw)
To: netdev; +Cc: Michał Mirosław, Mahesh Bandewar
This is partly preparation for use of the new net device features API,
but is useful in its own right.
Replace repetitive code for getting/setting offload flags with data-
driven loops.
This changes error messages to use the same long names for offload
flags as in dump_offload(), and changes various exit codes to 1.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
ethtool.c | 291 +++++++++++++++++--------------------------------------
internal.h | 18 ++++
test-features.c | 6 +-
3 files changed, 110 insertions(+), 205 deletions(-)
diff --git a/ethtool.c b/ethtool.c
index 9d42ca8..34d8b90 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -128,6 +128,38 @@ static const struct flag_info flags_msglvl[] = {
{ "wol", NETIF_MSG_WOL },
};
+static const struct {
+ const char *short_name;
+ const char *long_name;
+ u32 get_cmd, set_cmd;
+ u32 value;
+} off_flag_def[] = {
+ { "rx", "rx-checksumming",
+ ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM },
+ { "tx", "tx-checksumming",
+ ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM },
+ { "sg", "scatter-gather",
+ ETHTOOL_GSG, ETHTOOL_SSG, ETH_FLAG_SG },
+ { "tso", "tcp-segmentation-offload",
+ ETHTOOL_GTSO, ETHTOOL_STSO, ETH_FLAG_TSO },
+ { "ufo", "udp-fragmentation-offload",
+ ETHTOOL_GUFO, ETHTOOL_SUFO, ETH_FLAG_UFO },
+ { "gso", "generic-segmentation-offload",
+ ETHTOOL_GGSO, ETHTOOL_SGSO, ETH_FLAG_GSO },
+ { "gro", "generic-receive-offload",
+ ETHTOOL_GGRO, ETHTOOL_SGRO, ETH_FLAG_GRO },
+ { "lro", "large-receive-offload",
+ 0, 0, ETH_FLAG_LRO },
+ { "rxvlan", "rx-vlan-offload",
+ 0, 0, ETH_FLAG_RXVLAN },
+ { "txvlan", "tx-vlan-offload",
+ 0, 0, ETH_FLAG_TXVLAN },
+ { "ntuple", "ntuple-filters",
+ 0, 0, ETH_FLAG_NTUPLE },
+ { "rxhash", "receive-hashing",
+ 0, 0, ETH_FLAG_RXHASH },
+};
+
static long long
get_int_range(char *str, int base, long long min, long long max)
{
@@ -1037,35 +1069,17 @@ static int dump_coalesce(const struct ethtool_coalesce *ecoal)
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)
+static int dump_offload(u32 active)
{
- fprintf(stdout,
- "rx-checksumming: %s\n"
- "tx-checksumming: %s\n"
- "scatter-gather: %s\n"
- "tcp-segmentation-offload: %s\n"
- "udp-fragmentation-offload: %s\n"
- "generic-segmentation-offload: %s\n"
- "generic-receive-offload: %s\n"
- "large-receive-offload: %s\n"
- "rx-vlan-offload: %s\n"
- "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");
+ u32 value;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+ value = off_flag_def[i].value;
+ printf("%s: %s\n",
+ off_flag_def[i].long_name,
+ (active & value) ? "on" : "off");
+ }
return 0;
}
@@ -1636,67 +1650,30 @@ static int do_scoalesce(struct cmd_context *ctx)
static int do_goffload(struct cmd_context *ctx)
{
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;
+ u32 flags = 0, value;
+ int i;
if (ctx->argc != 0)
exit_bad_args();
fprintf(stdout, "Offload parameters for %s:\n", ctx->devname);
- eval.cmd = ETHTOOL_GRXCSUM;
- err = send_ioctl(ctx, &eval);
- if (err)
- perror("Cannot get device rx csum settings");
- else {
- rx = eval.data;
- allfail = 0;
- }
-
- eval.cmd = ETHTOOL_GTXCSUM;
- err = send_ioctl(ctx, &eval);
- if (err)
- perror("Cannot get device tx csum settings");
- else {
- tx = eval.data;
- allfail = 0;
- }
-
- eval.cmd = ETHTOOL_GSG;
- err = send_ioctl(ctx, &eval);
- if (err)
- perror("Cannot get device scatter-gather settings");
- else {
- sg = eval.data;
- allfail = 0;
- }
-
- eval.cmd = ETHTOOL_GTSO;
- err = send_ioctl(ctx, &eval);
- if (err)
- perror("Cannot get device tcp segmentation offload settings");
- else {
- tso = eval.data;
- allfail = 0;
- }
-
- eval.cmd = ETHTOOL_GUFO;
- err = send_ioctl(ctx, &eval);
- if (err)
- perror("Cannot get device udp large send offload settings");
- else {
- ufo = eval.data;
- allfail = 0;
- }
-
- eval.cmd = ETHTOOL_GGSO;
- err = send_ioctl(ctx, &eval);
- if (err)
- perror("Cannot get device generic segmentation offload settings");
- else {
- gso = eval.data;
- allfail = 0;
+ for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+ value = off_flag_def[i].value;
+ if (!off_flag_def[i].get_cmd)
+ continue;
+ eval.cmd = off_flag_def[i].get_cmd;
+ err = send_ioctl(ctx, &eval);
+ if (err) {
+ fprintf(stderr,
+ "Cannot get device %s settings: %m\n",
+ off_flag_def[i].long_name);
+ } else {
+ if (eval.data)
+ flags |= value;
+ allfail = 0;
+ }
}
eval.cmd = ETHTOOL_GFLAGS;
@@ -1704,20 +1681,7 @@ static int do_goffload(struct cmd_context *ctx)
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;
- allfail = 0;
- }
-
- eval.cmd = ETHTOOL_GGRO;
- err = send_ioctl(ctx, &eval);
- if (err)
- perror("Cannot get device GRO settings");
- else {
- gro = eval.data;
+ flags |= eval.data & ETH_FLAG_EXT_MASK;
allfail = 0;
}
@@ -1726,112 +1690,45 @@ static int do_goffload(struct cmd_context *ctx)
return 83;
}
- return dump_offload(rx, tx, sg, tso, ufo, gso, gro, lro, rxvlan, txvlan,
- ntuple, rxhash);
+ return dump_offload(flags);
}
static int do_soffload(struct cmd_context *ctx)
{
int goffload_changed = 0;
- int off_csum_rx_wanted = -1;
- int off_csum_tx_wanted = -1;
- int off_sg_wanted = -1;
- int off_tso_wanted = -1;
- int off_ufo_wanted = -1;
- int off_gso_wanted = -1;
u32 off_flags_wanted = 0;
u32 off_flags_mask = 0;
- int off_gro_wanted = -1;
- struct cmdline_info cmdline_offload[] = {
- { "rx", CMDL_BOOL, &off_csum_rx_wanted, NULL },
- { "tx", CMDL_BOOL, &off_csum_tx_wanted, NULL },
- { "sg", CMDL_BOOL, &off_sg_wanted, NULL },
- { "tso", CMDL_BOOL, &off_tso_wanted, NULL },
- { "ufo", CMDL_BOOL, &off_ufo_wanted, NULL },
- { "gso", CMDL_BOOL, &off_gso_wanted, NULL },
- { "lro", CMDL_FLAG, &off_flags_wanted, NULL,
- ETH_FLAG_LRO, &off_flags_mask },
- { "gro", CMDL_BOOL, &off_gro_wanted, NULL },
- { "rxvlan", CMDL_FLAG, &off_flags_wanted, NULL,
- ETH_FLAG_RXVLAN, &off_flags_mask },
- { "txvlan", CMDL_FLAG, &off_flags_wanted, NULL,
- ETH_FLAG_TXVLAN, &off_flags_mask },
- { "ntuple", CMDL_FLAG, &off_flags_wanted, NULL,
- ETH_FLAG_NTUPLE, &off_flags_mask },
- { "rxhash", CMDL_FLAG, &off_flags_wanted, NULL,
- ETH_FLAG_RXHASH, &off_flags_mask },
- };
+ struct cmdline_info cmdline_offload[ARRAY_SIZE(off_flag_def)];
struct ethtool_value eval;
- int err, changed = 0;
+ int err;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(off_flag_def); i++)
+ flag_to_cmdline_info(off_flag_def[i].short_name,
+ off_flag_def[i].value,
+ &off_flags_wanted, &off_flags_mask,
+ &cmdline_offload[i]);
parse_generic_cmdline(ctx, &goffload_changed,
cmdline_offload, ARRAY_SIZE(cmdline_offload));
- if (off_csum_rx_wanted >= 0) {
- changed = 1;
- eval.cmd = ETHTOOL_SRXCSUM;
- eval.data = (off_csum_rx_wanted == 1);
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot set device rx csum settings");
- return 84;
- }
- }
-
- if (off_csum_tx_wanted >= 0) {
- changed = 1;
- eval.cmd = ETHTOOL_STXCSUM;
- eval.data = (off_csum_tx_wanted == 1);
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot set device tx csum settings");
- return 85;
- }
- }
-
- if (off_sg_wanted >= 0) {
- changed = 1;
- eval.cmd = ETHTOOL_SSG;
- eval.data = (off_sg_wanted == 1);
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot set device scatter-gather settings");
- return 86;
- }
- }
-
- if (off_tso_wanted >= 0) {
- changed = 1;
- eval.cmd = ETHTOOL_STSO;
- eval.data = (off_tso_wanted == 1);
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot set device tcp segmentation offload settings");
- return 88;
- }
- }
- if (off_ufo_wanted >= 0) {
- changed = 1;
- eval.cmd = ETHTOOL_SUFO;
- eval.data = (off_ufo_wanted == 1);
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot set device udp large send offload settings");
- return 89;
- }
- }
- if (off_gso_wanted >= 0) {
- changed = 1;
- eval.cmd = ETHTOOL_SGSO;
- eval.data = (off_gso_wanted == 1);
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot set device generic segmentation offload settings");
- return 90;
+ for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+ if (!off_flag_def[i].set_cmd)
+ continue;
+ if (off_flags_mask & off_flag_def[i].value) {
+ eval.cmd = off_flag_def[i].set_cmd;
+ eval.data = !!(off_flags_wanted &
+ off_flag_def[i].value);
+ err = send_ioctl(ctx, &eval);
+ if (err) {
+ fprintf(stderr,
+ "Cannot set device %s settings: %m\n",
+ off_flag_def[i].long_name);
+ return 1;
+ }
}
}
- if (off_flags_mask) {
- changed = 1;
+ if (off_flags_mask & ETH_FLAG_EXT_MASK) {
eval.cmd = ETHTOOL_GFLAGS;
eval.data = 0;
err = send_ioctl(ctx, &eval);
@@ -1841,8 +1738,8 @@ static int do_soffload(struct cmd_context *ctx)
}
eval.cmd = ETHTOOL_SFLAGS;
- eval.data = ((eval.data & ~off_flags_mask) |
- off_flags_wanted);
+ eval.data &= ~(off_flags_mask & ETH_FLAG_EXT_MASK);
+ eval.data |= off_flags_wanted & ETH_FLAG_EXT_MASK;
err = send_ioctl(ctx, &eval);
if (err) {
@@ -1850,18 +1747,8 @@ static int do_soffload(struct cmd_context *ctx)
return 92;
}
}
- if (off_gro_wanted >= 0) {
- changed = 1;
- eval.cmd = ETHTOOL_SGRO;
- eval.data = (off_gro_wanted == 1);
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot set device GRO settings");
- return 93;
- }
- }
- if (!changed) {
+ if (off_flags_mask == 0) {
fprintf(stdout, "no offload settings changed\n");
}
diff --git a/internal.h b/internal.h
index 0fff2a5..deb7868 100644
--- a/internal.h
+++ b/internal.h
@@ -91,6 +91,24 @@ static inline int test_bit(unsigned int nr, const unsigned long *addr)
#define SIOCETHTOOL 0x8946
#endif
+/* Internal values for old-style offload flags. Values and names
+ * must not clash with the flags defined for ETHTOOL_{G,S}FLAGS.
+ */
+#define ETH_FLAG_RXCSUM (1 << 0)
+#define ETH_FLAG_TXCSUM (1 << 1)
+#define ETH_FLAG_SG (1 << 2)
+#define ETH_FLAG_TSO (1 << 3)
+#define ETH_FLAG_UFO (1 << 4)
+#define ETH_FLAG_GSO (1 << 5)
+#define ETH_FLAG_GRO (1 << 6)
+#define ETH_FLAG_INT_MASK (ETH_FLAG_RXCSUM | ETH_FLAG_TXCSUM | \
+ ETH_FLAG_SG | ETH_FLAG_TSO | ETH_FLAG_UFO | \
+ ETH_FLAG_GSO | ETH_FLAG_GRO),
+/* Mask of all flags defined for ETHTOOL_{G,S}FLAGS. */
+#define ETH_FLAG_EXT_MASK (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | \
+ ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE | \
+ ETH_FLAG_RXHASH)
+
/* Context for sub-commands */
struct cmd_context {
const char *devname; /* net device name */
diff --git a/test-features.c b/test-features.c
index 4d8680a..349bc23 100644
--- a/test-features.c
+++ b/test-features.c
@@ -62,8 +62,8 @@ static const struct cmd_expect cmd_expect_get_features_off[] = {
{ &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
{ &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
{ &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
- { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
{ &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
{ 0, 0, 0, 0, 0 }
};
@@ -74,9 +74,9 @@ static const struct cmd_expect cmd_expect_set_features_off[] = {
{ &cmd_stso_off, sizeof(cmd_stso_off), 0, 0, 0 },
{ &cmd_sufo_off, sizeof(cmd_sufo_off), 0, 0, 0 },
{ &cmd_sgso_off, sizeof(cmd_sgso_off), 0, 0, 0 },
+ { &cmd_sgro_off, sizeof(cmd_sgro_off), 0, 0, 0 },
{ &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
{ &cmd_sflags_off, sizeof(cmd_sflags_off), 0, 0, 0 },
- { &cmd_sgro_off, sizeof(cmd_sgro_off), 0, 0, 0 },
{ 0, 0, 0, 0, 0 }
};
@@ -87,9 +87,9 @@ static const struct cmd_expect cmd_expect_set_features_on[] = {
{ &cmd_stso_on, sizeof(cmd_stso_on), 0, 0, 0 },
{ &cmd_sufo_on, sizeof(cmd_sufo_on), 0, 0, 0 },
{ &cmd_sgso_on, sizeof(cmd_sgso_on), 0, 0, 0 },
+ { &cmd_sgro_on, sizeof(cmd_sgro_on), 0, 0, 0 },
{ &cmd_gflags_off, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_off) },
{ &cmd_sflags_on, sizeof(cmd_sflags_on), 0, 0, 0 },
- { &cmd_sgro_on, sizeof(cmd_sgro_on), 0, 0, 0 },
{ 0, 0, 0, 0, 0 }
};
--
1.7.7.6
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ethtool 6/7] Report when offload feature changes are not exactly as requested
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
` (4 preceding siblings ...)
2012-06-02 0:38 ` [PATCH ethtool 5/7] Regularise offload feature settings Ben Hutchings
@ 2012-06-02 0:39 ` Ben Hutchings
2012-06-02 0:40 ` [PATCH ethtool 7/7] Change -k/-K options to use ETHTOOL_{G,S}FEATURES Ben Hutchings
2012-06-02 0:45 ` [PATCH ethtool 0/7] Generic netdev features support Stephen Hemminger
7 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 0:39 UTC (permalink / raw)
To: netdev; +Cc: Michał Mirosław, Mahesh Bandewar
When an offload feature is enabled or disabled, this can change the
state of other features that depend on it, or may itself be deferred
if it depends on a feature that is disabled. Report when this
happens, and fail if no offload features could be changed.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
ethtool.c | 65 ++++++++++++++++++++++++++++++++++++++-----------------
test-features.c | 34 +++++++++++++++++++++++++++-
2 files changed, 77 insertions(+), 22 deletions(-)
diff --git a/ethtool.c b/ethtool.c
index 34d8b90..1cb708f 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -1069,13 +1069,15 @@ static int dump_coalesce(const struct ethtool_coalesce *ecoal)
return 0;
}
-static int dump_offload(u32 active)
+static int dump_offload(u32 active, u32 mask)
{
u32 value;
int i;
for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
value = off_flag_def[i].value;
+ if (!(mask & value))
+ continue;
printf("%s: %s\n",
off_flag_def[i].long_name,
(active & value) ? "on" : "off");
@@ -1647,17 +1649,14 @@ static int do_scoalesce(struct cmd_context *ctx)
return 0;
}
-static int do_goffload(struct cmd_context *ctx)
+static int get_offload(struct cmd_context *ctx, u32 *flags)
{
struct ethtool_value eval;
int err, allfail = 1;
- u32 flags = 0, value;
+ u32 value;
int i;
- if (ctx->argc != 0)
- exit_bad_args();
-
- fprintf(stdout, "Offload parameters for %s:\n", ctx->devname);
+ *flags = 0;
for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
value = off_flag_def[i].value;
@@ -1671,7 +1670,7 @@ static int do_goffload(struct cmd_context *ctx)
off_flag_def[i].long_name);
} else {
if (eval.data)
- flags |= value;
+ *flags |= value;
allfail = 0;
}
}
@@ -1681,16 +1680,28 @@ static int do_goffload(struct cmd_context *ctx)
if (err) {
perror("Cannot get device flags");
} else {
- flags |= eval.data & ETH_FLAG_EXT_MASK;
+ *flags |= eval.data & ETH_FLAG_EXT_MASK;
allfail = 0;
}
- if (allfail) {
+ return allfail;
+}
+
+static int do_goffload(struct cmd_context *ctx)
+{
+ u32 flags;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ fprintf(stdout, "Offload parameters for %s:\n", ctx->devname);
+
+ if (get_offload(ctx, &flags)) {
fprintf(stdout, "no offload info available\n");
return 83;
}
- return dump_offload(flags);
+ return dump_offload(flags, ~(u32)0);
}
static int do_soffload(struct cmd_context *ctx)
@@ -1699,6 +1710,7 @@ static int do_soffload(struct cmd_context *ctx)
u32 off_flags_wanted = 0;
u32 off_flags_mask = 0;
struct cmdline_info cmdline_offload[ARRAY_SIZE(off_flag_def)];
+ u32 old_flags, new_flags, diff;
struct ethtool_value eval;
int err;
int i;
@@ -1712,6 +1724,11 @@ static int do_soffload(struct cmd_context *ctx)
parse_generic_cmdline(ctx, &goffload_changed,
cmdline_offload, ARRAY_SIZE(cmdline_offload));
+ if (get_offload(ctx, &old_flags)) {
+ fprintf(stderr, "no offload info available\n");
+ return 1;
+ }
+
for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
if (!off_flag_def[i].set_cmd)
continue;
@@ -1729,16 +1746,8 @@ static int do_soffload(struct cmd_context *ctx)
}
}
if (off_flags_mask & ETH_FLAG_EXT_MASK) {
- eval.cmd = ETHTOOL_GFLAGS;
- eval.data = 0;
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot get device flag settings");
- return 91;
- }
-
eval.cmd = ETHTOOL_SFLAGS;
- eval.data &= ~(off_flags_mask & ETH_FLAG_EXT_MASK);
+ eval.data = old_flags & ~off_flags_mask & ETH_FLAG_EXT_MASK;
eval.data |= off_flags_wanted & ETH_FLAG_EXT_MASK;
err = send_ioctl(ctx, &eval);
@@ -1750,6 +1759,22 @@ static int do_soffload(struct cmd_context *ctx)
if (off_flags_mask == 0) {
fprintf(stdout, "no offload settings changed\n");
+ return 0;
+ }
+
+ /* Compare new state with requested state */
+ if (get_offload(ctx, &new_flags)) {
+ fprintf(stderr, "no offload info available\n");
+ return 1;
+ }
+ if (new_flags != ((old_flags & ~off_flags_mask) | off_flags_wanted)) {
+ if (new_flags == old_flags) {
+ fprintf(stderr,
+ "Could not change any device offload settings\n");
+ return 1;
+ }
+ printf("Actual changes:\n");
+ dump_offload(new_flags, new_flags ^ old_flags);
}
return 0;
diff --git a/test-features.c b/test-features.c
index 349bc23..409b3d2 100644
--- a/test-features.c
+++ b/test-features.c
@@ -68,6 +68,14 @@ static const struct cmd_expect cmd_expect_get_features_off[] = {
};
static const struct cmd_expect cmd_expect_set_features_off[] = {
+ { &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
+ { &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
+ { &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
+ { &cmd_gtso_on, 4, 0, &cmd_gtso_on, sizeof(cmd_gtso_on) },
+ { &cmd_gufo_on, 4, 0, &cmd_gufo_on, sizeof(cmd_gufo_on) },
+ { &cmd_ggso_on, 4, 0, &cmd_ggso_on, sizeof(cmd_ggso_on) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
{ &cmd_srxcsum_off, sizeof(cmd_srxcsum_off), 0, 0, 0 },
{ &cmd_stxcsum_off, sizeof(cmd_stxcsum_off), 0, 0, 0 },
{ &cmd_ssg_off, sizeof(cmd_ssg_off), 0, 0, 0 },
@@ -75,12 +83,27 @@ static const struct cmd_expect cmd_expect_set_features_off[] = {
{ &cmd_sufo_off, sizeof(cmd_sufo_off), 0, 0, 0 },
{ &cmd_sgso_off, sizeof(cmd_sgso_off), 0, 0, 0 },
{ &cmd_sgro_off, sizeof(cmd_sgro_off), 0, 0, 0 },
- { &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
{ &cmd_sflags_off, sizeof(cmd_sflags_off), 0, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_sflags_off) },
{ 0, 0, 0, 0, 0 }
};
static const struct cmd_expect cmd_expect_set_features_on[] = {
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
{ &cmd_srxcsum_on, sizeof(cmd_srxcsum_on), 0, 0, 0 },
{ &cmd_stxcsum_on, sizeof(cmd_stxcsum_on), 0, 0, 0 },
{ &cmd_ssg_on, sizeof(cmd_ssg_on), 0, 0, 0 },
@@ -88,8 +111,15 @@ static const struct cmd_expect cmd_expect_set_features_on[] = {
{ &cmd_sufo_on, sizeof(cmd_sufo_on), 0, 0, 0 },
{ &cmd_sgso_on, sizeof(cmd_sgso_on), 0, 0, 0 },
{ &cmd_sgro_on, sizeof(cmd_sgro_on), 0, 0, 0 },
- { &cmd_gflags_off, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_off) },
{ &cmd_sflags_on, sizeof(cmd_sflags_on), 0, 0, 0 },
+ { &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
+ { &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
+ { &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
+ { &cmd_gtso_on, 4, 0, &cmd_gtso_on, sizeof(cmd_gtso_on) },
+ { &cmd_gufo_on, 4, 0, &cmd_gufo_on, sizeof(cmd_gufo_on) },
+ { &cmd_ggso_on, 4, 0, &cmd_ggso_on, sizeof(cmd_ggso_on) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
{ 0, 0, 0, 0, 0 }
};
--
1.7.7.6
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH ethtool 7/7] Change -k/-K options to use ETHTOOL_{G,S}FEATURES
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
` (5 preceding siblings ...)
2012-06-02 0:39 ` [PATCH ethtool 6/7] Report when offload feature changes are not exactly as requested Ben Hutchings
@ 2012-06-02 0:40 ` Ben Hutchings
2012-06-02 0:45 ` [PATCH ethtool 0/7] Generic netdev features support Stephen Hemminger
7 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 0:40 UTC (permalink / raw)
To: netdev; +Cc: Michał Mirosław, Mahesh Bandewar
Rewrite the offload get and set functions to use the generic features
API where available, while maintaining a similar output format.
Add the long options --show-features and --features as additional
aliases for -k and -K.
Where there is exactly one named feature corresponding to an old
offload name, show the feature using the old offload name. Where
there are multiple features corresponding to an old offload name,
show the features as a group, indented underneath it.
Add some test cases to check that this works properly with or without
the generic features API. (These may well be insufficient.)
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
ethtool.8.in | 31 ++---
ethtool.c | 448 +++++++++++++++++++++++++++++++++++++++++++-----------
test-cmdline.c | 7 -
test-features.c | 345 +++++++++++++++++++++++++++++++++++++++++-
4 files changed, 707 insertions(+), 124 deletions(-)
diff --git a/ethtool.8.in b/ethtool.8.in
index 523b737..70ae31d 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -199,23 +199,13 @@ ethtool \- query or control network driver and hardware settings
.BN length
.BN value
.HP
-.B ethtool \-k|\-\-show\-offload
+.B ethtool \-k|\-\-show\-features|\-\-show\-offload
.I devname
.HP
-.B ethtool \-K|\-\-offload
-.I devname
-.B2 rx on off
-.B2 tx on off
-.B2 sg on off
-.B2 tso on off
-.B2 ufo on off
-.B2 gso on off
-.B2 gro on off
-.B2 lro on off
-.B2 rxvlan on off
-.B2 txvlan on off
-.B2 ntuple on off
-.B2 rxhash on off
+.B ethtool \-K|\-\-features|\-\-offload
+.I devname feature
+.A1 on off
+.RB ...
.HP
.B ethtool \-p|\-\-identify
.I devname
@@ -428,11 +418,14 @@ parameters allow writing to certain portions of the EEPROM.
Because of the persistent nature of writing to the EEPROM, a device-specific
magic key must be specified to prevent the accidental writing to the EEPROM.
.TP
-.B \-k \-\-show\-offload
-Queries the specified network device for offload information.
+.B \-k \-\-show\-features \-\-show\-offload
+Queries the specified network device for the state of protocol
+offload and other features.
.TP
-.B \-K \-\-offload
-Changes the offload parameters of the specified network device.
+.B \-K \-\-features \-\-offload
+Changes the offload parameters and other features of the specified
+network device. The following feature names are built-in and others
+may be defined by the kernel.
.TP
.A2 rx on off
Specifies whether RX checksumming should be enabled.
diff --git a/ethtool.c b/ethtool.c
index 1cb708f..984273b 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -128,38 +128,63 @@ static const struct flag_info flags_msglvl[] = {
{ "wol", NETIF_MSG_WOL },
};
-static const struct {
+struct off_flag_def {
const char *short_name;
const char *long_name;
+ const char *kernel_name;
u32 get_cmd, set_cmd;
u32 value;
-} off_flag_def[] = {
- { "rx", "rx-checksumming",
+};
+static const struct off_flag_def off_flag_def[] = {
+ { "rx", "rx-checksumming", "rx-checksum",
ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM },
- { "tx", "tx-checksumming",
+ { "tx", "tx-checksumming", "tx-checksum-*",
ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM },
- { "sg", "scatter-gather",
+ { "sg", "scatter-gather", "tx-scatter-gather*",
ETHTOOL_GSG, ETHTOOL_SSG, ETH_FLAG_SG },
- { "tso", "tcp-segmentation-offload",
+ { "tso", "tcp-segmentation-offload", "tx-tcp*-segmentation",
ETHTOOL_GTSO, ETHTOOL_STSO, ETH_FLAG_TSO },
- { "ufo", "udp-fragmentation-offload",
+ { "ufo", "udp-fragmentation-offload", "tx-udp-fragmentation",
ETHTOOL_GUFO, ETHTOOL_SUFO, ETH_FLAG_UFO },
- { "gso", "generic-segmentation-offload",
+ { "gso", "generic-segmentation-offload", "tx-generic-segmentation",
ETHTOOL_GGSO, ETHTOOL_SGSO, ETH_FLAG_GSO },
- { "gro", "generic-receive-offload",
+ { "gro", "generic-receive-offload", "rx-gro",
ETHTOOL_GGRO, ETHTOOL_SGRO, ETH_FLAG_GRO },
- { "lro", "large-receive-offload",
+ { "lro", "large-receive-offload", "rx-lro",
0, 0, ETH_FLAG_LRO },
- { "rxvlan", "rx-vlan-offload",
+ { "rxvlan", "rx-vlan-offload", "rx-vlan-hw-parse",
0, 0, ETH_FLAG_RXVLAN },
- { "txvlan", "tx-vlan-offload",
+ { "txvlan", "tx-vlan-offload", "tx-vlan-hw-insert",
0, 0, ETH_FLAG_TXVLAN },
- { "ntuple", "ntuple-filters",
+ { "ntuple", "ntuple-filters", "rx-ntuple-filter",
0, 0, ETH_FLAG_NTUPLE },
- { "rxhash", "receive-hashing",
+ { "rxhash", "receive-hashing", "rx-hashing",
0, 0, ETH_FLAG_RXHASH },
};
+struct feature_def {
+ char name[ETH_GSTRING_LEN];
+ int off_flag_index; /* index in off_flag_def; negative if none match */
+};
+
+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)];
+ /* Name and offload flag index for each feature */
+ struct feature_def def[0];
+};
+
+#define FEATURE_BITS_TO_BLOCKS(n_bits) DIV_ROUND_UP(n_bits, 32U)
+#define FEATURE_WORD(blocks, index, field) ((blocks)[(index) / 32U].field)
+#define FEATURE_FIELD_FLAG(index) (1U << (index) % 32U)
+#define FEATURE_BIT_SET(blocks, index, field) \
+ (FEATURE_WORD(blocks, index, field) |= FEATURE_FIELD_FLAG(index))
+#define FEATURE_BIT_CLEAR(blocks, index, field) \
+ (FEATURE_WORD(blocks, index, filed) &= ~FEATURE_FIELD_FLAG(index))
+#define FEATURE_BIT_IS_SET(blocks, index, field) \
+ (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
+
static long long
get_int_range(char *str, int base, long long min, long long max)
{
@@ -1069,21 +1094,84 @@ static int dump_coalesce(const struct ethtool_coalesce *ecoal)
return 0;
}
-static int dump_offload(u32 active, u32 mask)
+struct feature_state {
+ u32 off_flags;
+ struct ethtool_gfeatures features;
+};
+
+static void dump_one_feature(const char *indent, const char *name,
+ const struct feature_state *state,
+ const struct feature_state *ref_state,
+ u32 index)
+{
+ if (ref_state &&
+ !(FEATURE_BIT_IS_SET(state->features.features, index, active) ^
+ FEATURE_BIT_IS_SET(ref_state->features.features, index, active)))
+ return;
+
+ printf("%s%s: %s%s\n",
+ indent, name,
+ FEATURE_BIT_IS_SET(state->features.features, index, active) ?
+ "on" : "off",
+ (!FEATURE_BIT_IS_SET(state->features.features, index, available)
+ || FEATURE_BIT_IS_SET(state->features.features, index,
+ never_changed))
+ ? " [fixed]"
+ : (FEATURE_BIT_IS_SET(state->features.features, index, requested)
+ ^ FEATURE_BIT_IS_SET(state->features.features, index, active))
+ ? (FEATURE_BIT_IS_SET(state->features.features, index, requested)
+ ? " [requested on]" : " [requested off]")
+ : "");
+}
+
+static void dump_features(const struct feature_defs *defs,
+ const struct feature_state *state,
+ const struct feature_state *ref_state)
{
u32 value;
- int i;
+ int indent;
+ int i, j;
for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
value = off_flag_def[i].value;
- if (!(mask & value))
- continue;
- printf("%s: %s\n",
- off_flag_def[i].long_name,
- (active & value) ? "on" : "off");
+
+ /* If this offload flag matches exactly one generic
+ * feature then it's redundant to show the flag and
+ * feature states separately. Otherwise, show the
+ * flag state first.
+ */
+ if (defs->off_flag_matched[i] != 1 &&
+ (!ref_state ||
+ (state->off_flags ^ ref_state->off_flags) & value)) {
+ printf("%s: %s\n",
+ off_flag_def[i].long_name,
+ (state->off_flags & value) ? "on" : "off");
+ indent = 1;
+ } else {
+ indent = 0;
+ }
+
+ /* Show matching features */
+ for (j = 0; j < defs->n_features; j++) {
+ if (defs->def[j].off_flag_index != i)
+ continue;
+ if (defs->off_flag_matched[i] != 1)
+ /* Show all matching feature states */
+ dump_one_feature(indent ? "\t" : "",
+ defs->def[j].name,
+ state, ref_state, j);
+ else
+ /* Show full state with the old flag name */
+ dump_one_feature("", off_flag_def[i].long_name,
+ state, ref_state, j);
+ }
}
- return 0;
+ /* Show all unmatched features that have non-null names */
+ for (j = 0; j < defs->n_features; j++)
+ if (defs->def[j].off_flag_index < 0 && defs->def[j].name[0])
+ dump_one_feature("", defs->def[j].name,
+ state, ref_state, j);
}
static int dump_rxfhash(int fhash, u64 val)
@@ -1259,6 +1347,72 @@ get_stringset(struct cmd_context *ctx, enum ethtool_stringset set_id,
return strings;
}
+static struct feature_defs *get_feature_defs(struct cmd_context *ctx)
+{
+ struct ethtool_gstrings *names;
+ struct feature_defs *defs;
+ u32 n_features;
+ int i, j;
+
+ names = get_stringset(ctx, ETH_SS_FEATURES, 0);
+ if (names) {
+ n_features = names->len;
+ } else if (errno == EOPNOTSUPP || errno == EINVAL) {
+ /* Kernel doesn't support named features; not an error */
+ n_features = 0;
+ } else {
+ return NULL;
+ }
+
+ defs = malloc(sizeof(*defs) + sizeof(defs->def[0]) * n_features);
+ if (!defs)
+ return NULL;
+
+ defs->n_features = n_features;
+ memset(defs->off_flag_matched, 0, sizeof(defs->off_flag_matched));
+
+ /* Copy out feature names and find those associated with legacy flags */
+ for (i = 0; i < defs->n_features; i++) {
+ memcpy(defs->def[i].name, names->data + i * ETH_GSTRING_LEN,
+ ETH_GSTRING_LEN);
+ defs->def[i].off_flag_index = -1;
+
+ for (j = 0;
+ j < ARRAY_SIZE(off_flag_def) &&
+ defs->def[i].off_flag_index < 0;
+ j++) {
+ const char *pattern =
+ off_flag_def[j].kernel_name;
+ const char *name = defs->def[i].name;
+ for (;;) {
+ if (*pattern == '*') {
+ /* There is only one wildcard; so
+ * switch to a suffix comparison */
+ size_t pattern_len =
+ strlen(pattern + 1);
+ size_t name_len = strlen(name);
+ if (name_len < pattern_len)
+ break; /* name is too short */
+ name += name_len - pattern_len;
+ ++pattern;
+ } else if (*pattern != *name) {
+ break; /* mismatch */
+ } else if (*pattern == 0) {
+ defs->def[i].off_flag_index = j;
+ defs->off_flag_matched[j]++;
+ break;
+ } else {
+ ++name;
+ ++pattern;
+ }
+ }
+ }
+ }
+
+ free(names);
+ return defs;
+}
+
static int do_gdrv(struct cmd_context *ctx)
{
int err;
@@ -1649,14 +1803,22 @@ static int do_scoalesce(struct cmd_context *ctx)
return 0;
}
-static int get_offload(struct cmd_context *ctx, u32 *flags)
+static struct feature_state *
+get_features(struct cmd_context *ctx, const struct feature_defs *defs)
{
+ struct feature_state *state;
struct ethtool_value eval;
int err, allfail = 1;
u32 value;
int i;
- *flags = 0;
+ state = malloc(sizeof(*state) +
+ FEATURE_BITS_TO_BLOCKS(defs->n_features) *
+ sizeof(state->features.features[0]));
+ if (!state)
+ return NULL;
+
+ state->off_flags = 0;
for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
value = off_flag_def[i].value;
@@ -1670,7 +1832,7 @@ static int get_offload(struct cmd_context *ctx, u32 *flags)
off_flag_def[i].long_name);
} else {
if (eval.data)
- *flags |= value;
+ state->off_flags |= value;
allfail = 0;
}
}
@@ -1680,101 +1842,214 @@ static int get_offload(struct cmd_context *ctx, u32 *flags)
if (err) {
perror("Cannot get device flags");
} else {
- *flags |= eval.data & ETH_FLAG_EXT_MASK;
+ state->off_flags |= eval.data & ETH_FLAG_EXT_MASK;
allfail = 0;
}
- return allfail;
+ if (defs->n_features) {
+ state->features.cmd = ETHTOOL_GFEATURES;
+ state->features.size = FEATURE_BITS_TO_BLOCKS(defs->n_features);
+ err = send_ioctl(ctx, &state->features);
+ if (err)
+ perror("Cannot get device generic features");
+ else
+ allfail = 0;
+ }
+
+ if (allfail) {
+ free(state);
+ return NULL;
+ }
+
+ return state;
}
-static int do_goffload(struct cmd_context *ctx)
+static int do_gfeatures(struct cmd_context *ctx)
{
- u32 flags;
+ struct feature_defs *defs;
+ struct feature_state *features;
if (ctx->argc != 0)
exit_bad_args();
- fprintf(stdout, "Offload parameters for %s:\n", ctx->devname);
+ defs = get_feature_defs(ctx);
+ if (!defs)
+ return 1;
+
+ fprintf(stdout, "Features for %s:\n", ctx->devname);
- if (get_offload(ctx, &flags)) {
- fprintf(stdout, "no offload info available\n");
- return 83;
+ features = get_features(ctx, defs);
+ if (!features) {
+ fprintf(stdout, "no feature info available\n");
+ return 1;
}
- return dump_offload(flags, ~(u32)0);
+ dump_features(defs, features, NULL);
+ return 0;
}
-static int do_soffload(struct cmd_context *ctx)
+static int do_sfeatures(struct cmd_context *ctx)
{
- int goffload_changed = 0;
+ struct feature_defs *defs;
+ int any_changed = 0, any_mismatch = 0;
u32 off_flags_wanted = 0;
u32 off_flags_mask = 0;
- struct cmdline_info cmdline_offload[ARRAY_SIZE(off_flag_def)];
- u32 old_flags, new_flags, diff;
+ struct ethtool_sfeatures *efeatures;
+ struct cmdline_info *cmdline_features;
+ struct feature_state *old_state, *new_state;
struct ethtool_value eval;
int err;
- int i;
+ int i, j;
+
+ defs = get_feature_defs(ctx);
+ if (!defs)
+ return 1;
+ if (defs->n_features) {
+ efeatures = malloc(sizeof(*efeatures) +
+ FEATURE_BITS_TO_BLOCKS(defs->n_features) *
+ sizeof(efeatures->features[0]));
+ if (!efeatures) {
+ perror("Cannot parse arguments");
+ return 1;
+ }
+ efeatures->cmd = ETHTOOL_SFEATURES;
+ efeatures->size = FEATURE_BITS_TO_BLOCKS(defs->n_features);
+ memset(efeatures->features, 0,
+ FEATURE_BITS_TO_BLOCKS(defs->n_features) *
+ sizeof(efeatures->features[0]));
+ } else {
+ efeatures = NULL;
+ }
+ /* 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,
+ sizeof(cmdline_features[0]));
+ if (!cmdline_features) {
+ perror("Cannot parse arguments");
+ return 1;
+ }
for (i = 0; i < ARRAY_SIZE(off_flag_def); i++)
flag_to_cmdline_info(off_flag_def[i].short_name,
off_flag_def[i].value,
&off_flags_wanted, &off_flags_mask,
- &cmdline_offload[i]);
-
- parse_generic_cmdline(ctx, &goffload_changed,
- cmdline_offload, ARRAY_SIZE(cmdline_offload));
+ &cmdline_features[i]);
+ for (i = 0; i < defs->n_features; i++)
+ flag_to_cmdline_info(
+ 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]);
+ parse_generic_cmdline(ctx, &any_changed, cmdline_features,
+ ARRAY_SIZE(off_flag_def) + defs->n_features);
+ free(cmdline_features);
+
+ if (!any_changed) {
+ fprintf(stdout, "no features changed\n");
+ return 0;
+ }
- if (get_offload(ctx, &old_flags)) {
- fprintf(stderr, "no offload info available\n");
+ old_state = get_features(ctx, defs);
+ if (!old_state)
return 1;
- }
+ /* For each offload that the user specified, update any
+ * 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++) {
- if (!off_flag_def[i].set_cmd)
+ int fixed = 1;
+
+ if (!(off_flags_mask & off_flag_def[i].value))
continue;
- if (off_flags_mask & off_flag_def[i].value) {
- eval.cmd = off_flag_def[i].set_cmd;
- eval.data = !!(off_flags_wanted &
- off_flag_def[i].value);
- err = send_ioctl(ctx, &eval);
- if (err) {
- fprintf(stderr,
- "Cannot set device %s settings: %m\n",
- off_flag_def[i].long_name);
- return 1;
+
+ for (j = 0; j < defs->n_features; j++) {
+ if (defs->def[j].off_flag_index != i ||
+ !FEATURE_BIT_IS_SET(old_state->features.features,
+ j, available) ||
+ FEATURE_BIT_IS_SET(old_state->features.features,
+ j, never_changed))
+ continue;
+
+ fixed = 0;
+ if (!FEATURE_BIT_IS_SET(efeatures->features, j, valid)) {
+ FEATURE_BIT_SET(efeatures->features, j, valid);
+ if (off_flags_wanted & off_flag_def[i].value)
+ FEATURE_BIT_SET(efeatures->features, j,
+ requested);
}
}
+
+ if (fixed)
+ fprintf(stderr, "Cannot change %s\n",
+ off_flag_def[i].long_name);
}
- if (off_flags_mask & ETH_FLAG_EXT_MASK) {
- eval.cmd = ETHTOOL_SFLAGS;
- eval.data = old_flags & ~off_flags_mask & ETH_FLAG_EXT_MASK;
- eval.data |= off_flags_wanted & ETH_FLAG_EXT_MASK;
- err = send_ioctl(ctx, &eval);
- if (err) {
- perror("Cannot set device flag settings");
- return 92;
+ if (efeatures) {
+ err = send_ioctl(ctx, efeatures);
+ if (err < 0) {
+ perror("Cannot set device feature settings");
+ return 1;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) {
+ if (!off_flag_def[i].set_cmd)
+ continue;
+ if (off_flags_mask & off_flag_def[i].value) {
+ eval.cmd = off_flag_def[i].set_cmd;
+ eval.data = !!(off_flags_wanted &
+ off_flag_def[i].value);
+ err = send_ioctl(ctx, &eval);
+ if (err) {
+ fprintf(stderr,
+ "Cannot set device %s settings: %m\n",
+ off_flag_def[i].long_name);
+ return 1;
+ }
+ }
}
- }
- if (off_flags_mask == 0) {
- fprintf(stdout, "no offload settings changed\n");
- return 0;
+ if (off_flags_mask & ETH_FLAG_EXT_MASK) {
+ eval.cmd = ETHTOOL_SFLAGS;
+ eval.data = (old_state->off_flags & ~off_flags_mask &
+ ETH_FLAG_EXT_MASK);
+ eval.data |= off_flags_wanted & ETH_FLAG_EXT_MASK;
+
+ err = send_ioctl(ctx, &eval);
+ if (err) {
+ perror("Cannot set device flag settings");
+ return 92;
+ }
+ }
}
/* Compare new state with requested state */
- if (get_offload(ctx, &new_flags)) {
- fprintf(stderr, "no offload info available\n");
+ new_state = get_features(ctx, defs);
+ if (!new_state)
return 1;
- }
- if (new_flags != ((old_flags & ~off_flags_mask) | off_flags_wanted)) {
- if (new_flags == old_flags) {
+ any_changed = new_state->off_flags != old_state->off_flags;
+ any_mismatch = (new_state->off_flags !=
+ ((old_state->off_flags & ~off_flags_mask) |
+ off_flags_wanted));
+ for (i = 0; i < FEATURE_BITS_TO_BLOCKS(defs->n_features); i++) {
+ if (new_state->features.features[i].active !=
+ old_state->features.features[i].active)
+ any_changed = 1;
+ if (new_state->features.features[i].active !=
+ ((old_state->features.features[i].active &
+ ~efeatures->features[i].valid) |
+ efeatures->features[i].requested))
+ any_mismatch = 1;
+ }
+ if (any_mismatch) {
+ if (!any_changed) {
fprintf(stderr,
- "Could not change any device offload settings\n");
+ "Could not change any device features\n");
return 1;
}
printf("Actual changes:\n");
- dump_offload(new_flags, new_flags ^ old_flags);
+ dump_features(defs, new_state, old_state);
}
return 0;
@@ -3237,22 +3512,11 @@ static const struct option {
" [ rx-mini N ]\n"
" [ rx-jumbo N ]\n"
" [ tx N ]\n" },
- { "-k|--show-offload", 1, do_goffload,
- "Get protocol offload information" },
- { "-K|--offload", 1, do_soffload, "Set protocol offload",
- " [ rx on|off ]\n"
- " [ tx on|off ]\n"
- " [ sg on|off ]\n"
- " [ tso on|off ]\n"
- " [ ufo on|off ]\n"
- " [ gso on|off ]\n"
- " [ gro on|off ]\n"
- " [ lro on|off ]\n"
- " [ rxvlan on|off ]\n"
- " [ txvlan on|off ]\n"
- " [ ntuple on|off ]\n"
- " [ rxhash on|off ]\n"
- },
+ { "-k|--show-features|--show-offload", 1, do_gfeatures,
+ "Get state of protocol offload and other features" },
+ { "-K|--features|--offload", 1, do_sfeatures,
+ "Set protocol offload and other features",
+ " FEATURE on|off ...\n" },
{ "-i|--driver", 1, do_gdrv, "Show driver information" },
{ "-d|--register-dump", 1, do_gregs, "Do a register dump",
" [ raw on|off ]\n"
diff --git a/test-cmdline.c b/test-cmdline.c
index c62bb97..978c312 100644
--- a/test-cmdline.c
+++ b/test-cmdline.c
@@ -86,14 +86,7 @@ static struct test_case {
{ 1, "--set-ring devname rx" },
{ 1, "-G devname foo 1" },
{ 1, "-G" },
- { 0, "-k devname" },
- { 0, "--show-offload devname" },
{ 1, "-k" },
- { 0, "-K devname rx on tx off sg on tso off ufo on gso off gro on" },
- { 0, "--offload devname lro off rxvlan on txvlan off ntuple on rxhash off" },
- { 1, "-K devname rx foo" },
- { 1, "--offload devname rx" },
- { 1, "-K devname foo on" },
{ 1, "-K" },
{ 0, "-i devname" },
{ 0, "--driver devname" },
diff --git a/test-features.c b/test-features.c
index 409b3d2..a48c701 100644
--- a/test-features.c
+++ b/test-features.c
@@ -7,12 +7,19 @@
* by the Free Software Foundation, incorporated herein by reference.
*/
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TEST_NO_WRAPPERS
#include "internal.h"
+static const struct {
+ struct ethtool_sset_info cmd;
+ u32 data[1];
+}
+cmd_gssetinfo = { { ETHTOOL_GSSET_INFO, 0, 1ULL << ETH_SS_FEATURES }, 34 };
+
static const struct ethtool_value
cmd_grxcsum_off = { ETHTOOL_GRXCSUM, 0 },
cmd_grxcsum_on = { ETHTOOL_GRXCSUM, 1 },
@@ -55,7 +62,13 @@ cmd_sflags_not_rxhash = { ETHTOOL_SFLAGS,
ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
ETH_FLAG_NTUPLE };
-static const struct cmd_expect cmd_expect_get_features_off[] = {
+static const struct cmd_expect cmd_expect_get_strings_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_get_features_off_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
{ &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
{ &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
{ &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
@@ -67,7 +80,8 @@ static const struct cmd_expect cmd_expect_get_features_off[] = {
{ 0, 0, 0, 0, 0 }
};
-static const struct cmd_expect cmd_expect_set_features_off[] = {
+static const struct cmd_expect cmd_expect_set_features_off_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
{ &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
{ &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
{ &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
@@ -95,7 +109,8 @@ static const struct cmd_expect cmd_expect_set_features_off[] = {
{ 0, 0, 0, 0, 0 }
};
-static const struct cmd_expect cmd_expect_set_features_on[] = {
+static const struct cmd_expect cmd_expect_set_features_on_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
{ &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
{ &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
{ &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
@@ -123,16 +138,334 @@ static const struct cmd_expect cmd_expect_set_features_on[] = {
{ 0, 0, 0, 0, 0 }
};
+static const struct cmd_expect cmd_expect_set_features_unsup_on_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_stxcsum_on, sizeof(cmd_stxcsum_on), -EOPNOTSUPP },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct {
+ struct ethtool_gstrings cmd;
+ u8 data[34][ETH_GSTRING_LEN];
+}
+cmd_gstrings = {
+ { ETHTOOL_GSTRINGS, ETH_SS_FEATURES, 34 },
+ {
+ "tx-scatter-gather",
+ "tx-checksum-ipv4",
+ "",
+ "tx-checksum-ip-generic",
+ "tx-checksum-ipv6",
+ "highdma",
+ "tx-scatter-gather-fraglist",
+ "tx-vlan-hw-insert",
+ "rx-vlan-hw-parse",
+ "rx-vlan-filter",
+ "vlan-challenged",
+ "tx-generic-segmentation",
+ "tx-lockless",
+ "netns-local",
+ "rx-gro",
+ "rx-lro",
+ "tx-tcp-segmentation",
+ "tx-udp-fragmentation",
+ "tx-gso-robust",
+ "tx-tcp-ecn-segmentation",
+ "tx-tcp6-segmentation",
+ "tx-fcoe-segmentation",
+ "",
+ "",
+ "tx-checksum-fcoe-crc",
+ "tx-checksum-sctp",
+ "fcoe-mtu",
+ "rx-ntuple-filter",
+ "rx-hashing",
+ "rx-checksum",
+ "tx-nocache-copy",
+ "loopback",
+ "rx-fcs",
+ "rx-all",
+ }
+};
+
+static const struct {
+ struct ethtool_gfeatures cmd;
+ struct ethtool_get_features_block data[2];
+}
+ /* available requested active never_changed */
+/* minimal: only GRO and GSO are available (and GSO won't work) */
+cmd_gfeatures_min_off = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0x00004800, 0x00000000, 0x00000000, 0x00003400},
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000}}
+},
+cmd_gfeatures_min_on = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0x00004800, 0x00004800, 0x00004000, 0x00003400},
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000}}
+},
+/* maximal: everything that isn't never-changed is available */
+cmd_gfeatures_max_off = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0xffffcbff, 0x00000000, 0x00000000, 0x00003400 },
+ { 0x00000003, 0x00000000, 0x00000000, 0x00000000 }}
+},
+cmd_gfeatures_max_on = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0xffffcbff, 0xffffcbff, 0xffffcbff, 0x00003400 },
+ { 0x00000003, 0x00000003, 0x00000003, 0x00000000 }}
+},
+/* IPv4: GRO, GSO, SG and some IPv4-specific offloads are available */
+cmd_gfeatures_ipv4_off = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0x00014803, 0x00000000, 0x00000000, 0x00003400 },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }}
+},
+cmd_gfeatures_ipv4_on = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0x00014803, 0x00014803, 0x00014803, 0x00003400 },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }}
+};
+
+static const struct {
+ struct ethtool_sfeatures cmd;
+ struct ethtool_set_features_block data[2];
+}
+ /* valid requested */
+cmd_sfeatures_min_on = { { ETHTOOL_SFEATURES, 2 },
+ {{ 0x00004800, 0x00004800 },
+ { 0x00000000, 0x00000000 }} },
+cmd_sfeatures_min_off = { { ETHTOOL_SFEATURES, 2 },
+ {{ 0x00004800, 0x00000000 },
+ { 0x00000000, 0x00000000 }} },
+cmd_sfeatures_noop = { { ETHTOOL_SFEATURES, 2 },
+ {{ 0x00000000, 0x00000000 },
+ { 0x00000000, 0x00000000 }} },
+cmd_sfeatures_ipv4_on = { { ETHTOOL_SFEATURES, 2 },
+ {{ 0x00014803, 0x00014803 },
+ { 0x00000000, 0x00000000 }} };
+
+static const struct cmd_expect cmd_expect_get_strings[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_get_features_min_off[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_get_features_max_on[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
+ { &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
+ { &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
+ { &cmd_gtso_on, 4, 0, &cmd_gtso_on, sizeof(cmd_gtso_on) },
+ { &cmd_gufo_on, 4, 0, &cmd_gufo_on, sizeof(cmd_gufo_on) },
+ { &cmd_ggso_on, 4, 0, &cmd_ggso_on, sizeof(cmd_ggso_on) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
+ { &cmd_gfeatures_max_on, sizeof(cmd_gfeatures_max_on.cmd),
+ 0, &cmd_gfeatures_max_on, sizeof(cmd_gfeatures_max_on) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_min_off_min_on[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { &cmd_sfeatures_min_on, sizeof(cmd_sfeatures_min_on),
+ ETHTOOL_F_WISH, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_on, sizeof(cmd_gfeatures_min_on.cmd),
+ 0, &cmd_gfeatures_min_on, sizeof(cmd_gfeatures_min_on) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_min_off_min_off[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { &cmd_sfeatures_min_off, sizeof(cmd_sfeatures_min_off), 0, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_min_on_min_off[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_on, sizeof(cmd_gfeatures_min_on.cmd),
+ 0, &cmd_gfeatures_min_on, sizeof(cmd_gfeatures_min_on) },
+ { &cmd_sfeatures_min_off, sizeof(cmd_sfeatures_min_off), 0, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_min_off_unsup_on[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { &cmd_sfeatures_noop, sizeof(cmd_sfeatures_noop), 0, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_ipv4_off_many_on[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_ipv4_off, sizeof(cmd_gfeatures_ipv4_off.cmd),
+ 0, &cmd_gfeatures_ipv4_off, sizeof(cmd_gfeatures_ipv4_off) },
+ { &cmd_sfeatures_ipv4_on, sizeof(cmd_sfeatures_ipv4_on), 0, 0, 0 },
+ { &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
+ { &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
+ { &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
+ { &cmd_gtso_on, 4, 0, &cmd_gtso_on, sizeof(cmd_gtso_on) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_on, 4, 0, &cmd_ggso_on, sizeof(cmd_ggso_on) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_ipv4_on, sizeof(cmd_gfeatures_ipv4_on.cmd),
+ 0, &cmd_gfeatures_ipv4_on, sizeof(cmd_gfeatures_ipv4_on) },
+ { 0, 0, 0, 0, 0 }
+};
+
static struct test_case {
int rc;
const char *args;
const struct cmd_expect *expect;
} const test_cases[] = {
- { 0, "-k devname", cmd_expect_get_features_off },
+ { 0, "-k devname", cmd_expect_get_features_off_old },
{ 0, "-K devname rx off tx off sg off tso off ufo off gso off lro off rxvlan off txvlan off ntuple off rxhash off gro off",
- cmd_expect_set_features_off },
+ cmd_expect_set_features_off_old },
{ 0, "-K devname rx on tx on sg on tso on ufo on gso on lro on rxvlan on txvlan on ntuple on rxhash on gro on",
- cmd_expect_set_features_on },
+ cmd_expect_set_features_on_old },
+ { 1, "-K devname tx on sg on", cmd_expect_set_features_unsup_on_old },
+ { 0, "--show-offload devname", cmd_expect_get_features_min_off },
+ { 0, "--show-features devname", cmd_expect_get_features_max_on },
+ { 0, "-K devname rx on tx on sg on tso on ufo on gso on gro on",
+ cmd_expect_set_features_min_off_min_on },
+ { 0, "-K devname rx off tx off sg off tso off ufo off gso off gro off",
+ cmd_expect_set_features_min_off_min_off },
+ { 0, "-K devname rx off tx off sg off tso off ufo off gso off gro off",
+ cmd_expect_set_features_min_on_min_off },
+ { 1, "-K devname tx on sg on",
+ cmd_expect_set_features_min_off_unsup_on },
+ { 0, "--features devname rx on tx on sg on tso on gso on gro on",
+ cmd_expect_set_features_ipv4_off_many_on },
+ { 1, "-K devname rx foo", cmd_expect_get_strings_old },
+ { 1, "-K devname rx foo", cmd_expect_get_strings },
+ { 1, "--offload devname rx", cmd_expect_get_strings_old },
+ { 1, "--features devname rx", cmd_expect_get_strings },
+ { 1, "--features devname foo on", cmd_expect_get_strings_old },
+ { 1, "--offload devname foo on", cmd_expect_get_strings },
};
static int expect_matched;
--
1.7.7.6
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH ethtool 0/7] Generic netdev features support
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
` (6 preceding siblings ...)
2012-06-02 0:40 ` [PATCH ethtool 7/7] Change -k/-K options to use ETHTOOL_{G,S}FEATURES Ben Hutchings
@ 2012-06-02 0:45 ` Stephen Hemminger
2012-06-02 1:31 ` Ben Hutchings
7 siblings, 1 reply; 10+ messages in thread
From: Stephen Hemminger @ 2012-06-02 0:45 UTC (permalink / raw)
To: Ben Hutchings; +Cc: netdev, Michał Mirosław, Mahesh Bandewar
On Sat, 2 Jun 2012 01:33:47 +0100
Ben Hutchings <bhutchings@solarflare.com> wrote:
> This series adds support for the not-so-new generic netdev features
> ethtool API, along with some test infrastructure and test cases to check
> that it works with old and new kernel versions. I'm still not convinced
> that there are enough test cases, but I've held this back for too damn
> long already.
>
> I'd like to include these changes in an ethtool 3.4 release by the end
> of next week, so let me know if there's anything wrong with them.
>
> Ben.
Slightly off-topic, is anyone working on doing ethtool setting over netlink?
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH ethtool 0/7] Generic netdev features support
2012-06-02 0:45 ` [PATCH ethtool 0/7] Generic netdev features support Stephen Hemminger
@ 2012-06-02 1:31 ` Ben Hutchings
0 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2012-06-02 1:31 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev, Michał Mirosław, Mahesh Bandewar
On Fri, 2012-06-01 at 17:45 -0700, Stephen Hemminger wrote:
> On Sat, 2 Jun 2012 01:33:47 +0100
> Ben Hutchings <bhutchings@solarflare.com> wrote:
>
> > This series adds support for the not-so-new generic netdev features
> > ethtool API, along with some test infrastructure and test cases to check
> > that it works with old and new kernel versions. I'm still not convinced
> > that there are enough test cases, but I've held this back for too damn
> > long already.
> >
> > I'd like to include these changes in an ethtool 3.4 release by the end
> > of next week, so let me know if there's anything wrong with them.
> >
> > Ben.
>
> Slightly off-topic, is anyone working on doing ethtool setting over netlink?
I'm not. I seem to remember you brought this up at netconf 2010 but I
don't remember what your motivation was. Were you thinking about being
able to monitor for changes, for example?
Ben.
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2012-06-02 1:31 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-02 0:33 [PATCH ethtool 0/7] Generic netdev features support Ben Hutchings
2012-06-02 0:35 ` [PATCH ethtool 1/7] Run tests in-process Ben Hutchings
2012-06-02 0:36 ` [PATCH ethtool 2/7] Add output file parameter to dump_hex() and make it extern Ben Hutchings
2012-06-02 0:37 ` [PATCH ethtool 3/7] test: Add test_ioctl() function to support a mock send_ioctl() Ben Hutchings
2012-06-02 0:37 ` [PATCH ethtool 4/7] Add unit tests for setting offload features (old API) Ben Hutchings
2012-06-02 0:38 ` [PATCH ethtool 5/7] Regularise offload feature settings Ben Hutchings
2012-06-02 0:39 ` [PATCH ethtool 6/7] Report when offload feature changes are not exactly as requested Ben Hutchings
2012-06-02 0:40 ` [PATCH ethtool 7/7] Change -k/-K options to use ETHTOOL_{G,S}FEATURES Ben Hutchings
2012-06-02 0:45 ` [PATCH ethtool 0/7] Generic netdev features support Stephen Hemminger
2012-06-02 1:31 ` Ben Hutchings
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox