summaryrefslogtreecommitdiff
path: root/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'ethtool.c')
-rw-r--r--ethtool.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/ethtool.c b/ethtool.c
index 4073745..49c8ac1 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -98,6 +98,8 @@ static char *unparse_rxfhashopts(u64 opts);
static int dump_rxfhash(int fhash, u64 val);
static int do_srxclass(int fd, struct ifreq *ifr);
static int do_grxclass(int fd, struct ifreq *ifr);
+static int do_grxfhindir(int fd, struct ifreq *ifr);
+static int do_srxfhindir(int fd, struct ifreq *ifr);
static int do_srxntuple(int fd, struct ifreq *ifr);
static int do_grxntuple(int fd, struct ifreq *ifr);
static int do_flash(int fd, struct ifreq *ifr);
@@ -125,6 +127,8 @@ static enum {
MODE_GSTATS,
MODE_GNFC,
MODE_SNFC,
+ MODE_GRXFHINDIR,
+ MODE_SRXFHINDIR,
MODE_SNTUPLE,
MODE_GNTUPLE,
MODE_FLASHDEV,
@@ -225,6 +229,10 @@ static struct option {
"classification options",
" [ rx-flow-hash tcp4|udp4|ah4|sctp4|"
"tcp6|udp6|ah6|sctp6 m|v|t|s|d|f|n|r... ]\n" },
+ { "-x", "--show-rxfh-indir", MODE_GRXFHINDIR, "Show Rx flow hash "
+ "indirection" },
+ { "-X", "--set-rxfh-indir", MODE_SRXFHINDIR, "Set Rx flow hash indirection",
+ " equal N | weight W0 W1 ...\n" },
{ "-U", "--config-ntuple", MODE_SNTUPLE, "Configure Rx ntuple filters "
"and actions",
" [ flow-type tcp4|udp4|sctp4 src-ip <addr> "
@@ -350,6 +358,8 @@ static int rx_fhash_get = 0;
static int rx_fhash_set = 0;
static u32 rx_fhash_val = 0;
static int rx_fhash_changed = 0;
+static int rxfhindir_equal = 0;
+static char **rxfhindir_weight = NULL;
static int sntuple_changed = 0;
static struct ethtool_rx_ntuple_flow_spec ntuple_fs;
static char *flash_file = NULL;
@@ -549,6 +559,11 @@ static int get_int(char *str, int base)
return get_int_range(str, base, INT_MIN, INT_MAX);
}
+static u32 get_u32(char *str, int base)
+{
+ return get_uint_range(str, base, 0xffffffff);
+}
+
static void parse_generic_cmdline(int argc, char **argp,
int first_arg, int *changed,
struct cmdline_info *info,
@@ -725,6 +740,8 @@ static void parse_cmdline(int argc, char **argp)
(mode == MODE_GSTATS) ||
(mode == MODE_GNFC) ||
(mode == MODE_SNFC) ||
+ (mode == MODE_GRXFHINDIR) ||
+ (mode == MODE_SRXFHINDIR) ||
(mode == MODE_SNTUPLE) ||
(mode == MODE_GNTUPLE) ||
(mode == MODE_PHYS_ID) ||
@@ -878,6 +895,30 @@ static void parse_cmdline(int argc, char **argp)
show_usage(1);
break;
}
+ if (mode == MODE_SRXFHINDIR) {
+ if (!strcmp(argp[i], "equal")) {
+ if (argc != i + 2) {
+ show_usage(1);
+ break;
+ }
+ i += 1;
+ rxfhindir_equal =
+ get_int_range(argp[i], 0, 1,
+ INT_MAX);
+ i += 1;
+ } else if (!strcmp(argp[i], "weight")) {
+ i += 1;
+ if (i >= argc) {
+ show_usage(1);
+ break;
+ }
+ rxfhindir_weight = argp + i;
+ i = argc;
+ } else {
+ show_usage(1);
+ }
+ break;
+ }
if (mode != MODE_SSET)
show_usage(1);
if (!strcmp(argp[i], "speed")) {
@@ -1812,6 +1853,10 @@ static int doit(void)
return do_grxclass(fd, &ifr);
} else if (mode == MODE_SNFC) {
return do_srxclass(fd, &ifr);
+ } else if (mode == MODE_GRXFHINDIR) {
+ return do_grxfhindir(fd, &ifr);
+ } else if (mode == MODE_SRXFHINDIR) {
+ return do_srxfhindir(fd, &ifr);
} else if (mode == MODE_SNTUPLE) {
return do_srxntuple(fd, &ifr);
} else if (mode == MODE_GNTUPLE) {
@@ -2751,6 +2796,123 @@ static int do_grxclass(int fd, struct ifreq *ifr)
return 0;
}
+static int do_grxfhindir(int fd, struct ifreq *ifr)
+{
+ struct ethtool_rxnfc ring_count;
+ struct ethtool_rxfh_indir indir_head;
+ struct ethtool_rxfh_indir *indir;
+ u32 i;
+ int err;
+
+ ring_count.cmd = ETHTOOL_GRXRINGS;
+ ifr->ifr_data = (caddr_t) &ring_count;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot get RX ring count");
+ return 102;
+ }
+
+ indir_head.cmd = ETHTOOL_GRXFHINDIR;
+ indir_head.size = 0;
+ ifr->ifr_data = (caddr_t) &indir_head;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot get RX flow hash indirection table size");
+ return 103;
+ }
+
+ indir = malloc(sizeof(*indir) +
+ indir_head.size * sizeof(*indir->ring_index));
+ indir->cmd = ETHTOOL_GRXFHINDIR;
+ indir->size = indir_head.size;
+ ifr->ifr_data = (caddr_t) indir;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot get RX flow hash indirection table");
+ return 103;
+ }
+
+ printf("RX flow hash indirection table for %s with %llu RX ring(s):\n",
+ devname, ring_count.data);
+ for (i = 0; i < indir->size; i++) {
+ if (i % 8 == 0)
+ printf("%5u: ", i);
+ printf(" %5u", indir->ring_index[i]);
+ if (i % 8 == 7)
+ fputc('\n', stdout);
+ }
+ return 0;
+}
+
+static int do_srxfhindir(int fd, struct ifreq *ifr)
+{
+ struct ethtool_rxfh_indir indir_head;
+ struct ethtool_rxfh_indir *indir;
+ u32 i;
+ int err;
+
+ if (!rxfhindir_equal && !rxfhindir_weight)
+ show_usage(1);
+
+ indir_head.cmd = ETHTOOL_GRXFHINDIR;
+ indir_head.size = 0;
+ ifr->ifr_data = (caddr_t) &indir_head;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot get RX flow hash indirection table size");
+ return 104;
+ }
+
+ indir = malloc(sizeof(*indir) +
+ indir_head.size * sizeof(*indir->ring_index));
+ indir->cmd = ETHTOOL_SRXFHINDIR;
+ indir->size = indir_head.size;
+
+ if (rxfhindir_equal) {
+ for (i = 0; i < indir->size; i++)
+ indir->ring_index[i] = i % rxfhindir_equal;
+ } else {
+ u32 j, weight, sum = 0, partial = 0;
+
+ for (j = 0; rxfhindir_weight[j]; j++) {
+ weight = get_u32(rxfhindir_weight[j], 0);
+ sum += weight;
+ }
+
+ if (sum == 0) {
+ fprintf(stderr,
+ "At least one weight must be non-zero\n");
+ exit(1);
+ }
+
+ if (sum > indir->size) {
+ fprintf(stderr,
+ "Total weight exceeds the size of the "
+ "indirection table\n");
+ exit(1);
+ }
+
+ j = -1;
+ for (i = 0; i < indir->size; i++) {
+ if (i >= indir->size * partial / sum) {
+ j += 1;
+ weight = get_u32(rxfhindir_weight[j], 0);
+ partial += weight;
+ }
+ indir->ring_index[i] = j;
+ }
+ }
+
+ ifr->ifr_data = (caddr_t) indir;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot set RX flow hash indirection table");
+ return 105;
+ }
+
+ return 0;
+}
+
static int do_flash(int fd, struct ifreq *ifr)
{
struct ethtool_flash efl;