From: Santwona Behera Added new interfaces to ethtool to configure receive network flow distribution across multiple rx rings using hashing. Patch is against ethtool version 6. Signed-off-by: Santwona Behera --- ethtool-copy.h | 33 +++++++ ethtool.8 | 80 ++++++++++++++++++ ethtool.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+), 0 deletions(-) diff --git a/ethtool-copy.h b/ethtool-copy.h index 3a63224..e2a72f1 100644 --- a/ethtool-copy.h +++ b/ethtool-copy.h @@ -256,6 +256,12 @@ struct ethtool_perm_addr { __u8 data[0]; }; +struct ethtool_rxnfc { + __u32 cmd; + __u32 flow_type; + __u64 data; +}; + #ifdef __KERNEL__ struct net_device; @@ -373,6 +379,8 @@ struct ethtool_ops { void (*complete)(struct net_device *); u32 (*get_ufo)(struct net_device *); int (*set_ufo)(struct net_device *, u32); + int (*get_rxhash)(struct net_device *, struct ethtool_rxnfc *); + int (*set_rxhash)(struct net_device *, struct ethtool_rxnfc *); }; #endif /* __KERNEL__ */ @@ -415,6 +423,9 @@ struct ethtool_ops { #define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (ethtool_value) */ #define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */ +#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */ +#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ + /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET #define SPARC_ETH_SSET ETHTOOL_SSET @@ -501,4 +512,26 @@ struct ethtool_ops { #define WAKE_MAGIC (1 << 5) #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ +/* L3-L4 network traffic flow types */ +#define TCP_V4_FLOW 0x01 +#define UDP_V4_FLOW 0x02 +#define SCTP_V4_FLOW 0x03 +#define AH_ESP_V4_FLOW 0x04 +#define TCP_V6_FLOW 0x05 +#define UDP_V6_FLOW 0x06 +#define SCTP_V6_FLOW 0x07 +#define AH_ESP_V6_FLOW 0x08 + +/* L3-L4 network traffic flow hash options */ +#define RXH_DEV_PORT (1 << 0) +#define RXH_L2DA (1 << 1) +#define RXH_VLAN (1 << 2) +#define RXH_L3_PROTO (1 << 3) +#define RXH_IP_SRC (1 << 4) +#define RXH_IP_DST (1 << 5) +#define RXH_L4_BYTES_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ +#define RXH_L4_BYTES_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ +#define RXH_DISCARD (1 << 31) + + #endif /* _LINUX_ETHTOOL_H */ diff --git a/ethtool.8 b/ethtool.8 index cc6a46e..1beb387 100644 --- a/ethtool.8 +++ b/ethtool.8 @@ -40,6 +40,14 @@ .\" \(*WO - wol flags .\" .ds WO \fBp\fP|\fBu\fP|\fBm\fP|\fBb\fP|\fBa\fP|\fBg\fP|\fBs\fP|\fBd\fP... +.\" +.\" \(*FL - flow type values +.\" +.ds FL \fBtcp4\fP|\fBudp4\fP|\fBah4\fP|\fBsctp4\fP|\fBtcp6\fP|\fBudp6\fP|\fBah6\fP|\fBsctp6\fP +.\" +.\" \(*HO - hash options +.\" +.ds HO \fBp\fP|\fm\fP|\fBv\fP|\fBt\fP|\fBs\fP|\fBd\fP|\fBf\fP|\fBn\fP|\fBr\fP... .TH ETHTOOL 8 "July 2007" "Ethtool version 6" .SH NAME ethtool \- Display or change ethernet card settings @@ -188,6 +196,15 @@ ethtool \- Display or change ethernet card settings .RB [ sopass \ \*(MA] .RB [ msglvl .IR N ] + +.B ethtool \-n +.I ethX +.RB [ rx-flow-hash \ \*(FL] + +.B ethtool \-N +.I ethX +.RB [ rx-flow-hash \ \*(FL +.RB \ \*(HO] .SH DESCRIPTION .BI ethtool is used for querying settings of an ethernet device and changing them. @@ -413,6 +430,69 @@ bytes in ethernet MAC hex format (\*(MA). .TP .BI msglvl \ N Sets the driver message level. Meanings differ per driver. +.TP +.B \-n \-\-show-nfc +Retrieves the receive network flow classification configurations. +.TP +.BR rx-flow-hash \ \*(FL +Retrieves the hash options for the specified network traffic type. +.RS +.PD 0 +.TP 3 +.BR "tcp4" " TCP over IPv4" +.TP 3 +.BR "udp4" " UDP over IPv4" +.TP 3 +.BR "ah4" " IPSEC AH/ESP over IPv4" +.TP 3 +.BR "sctp4" " SCTP over IPv4" +.TP 3 +.BR "tcp6" " TCP over IPv6" +.TP 3 +.BR "udp6" " UDP over IPv6" +.TP 3 +.BR "ah6" " IPSEC AH/ESP over IPv6" +.TP 3 +.BR "sctp6" " SCTP over IPv6" +.PD +.RE +.TP +.B \-N \-\-config-nfc +Configures the receive network flow classification. +.TP +.BR rx-flow-hash \ \*(FL \ \*(HO +Configures the hash options for the specified network traffic type. +.RS +.PD 0 +.TP 3 +.B p +Hash on the device port number on which the packet was received. +.TP 3 +.B m +Hash on the Layer 2 destination address of the rx packet. +.TP 3 +.B v +Hash on the VLAN tag of the rx packet. +.TP 3 +.B t +Hash on the Layer 3 protocol field of the rx packet. +.TP 3 +.B s +Hash on the IP source address of the rx packet. +.TP 3 +.B d +Hash on the IP destination address of the rx packet. +.TP 3 +.B f +Hash on bytes 0 and 1 of the Layer 4 header of the rx packet. +.TP 3 +.B n +Hash on bytes 2 and 3 of the Layer 4 header of the rx packet. +.TP 3 +.B r +Discard all packets of this flow type. When this option is set, all other options are ignored. +.PD +.RE .SH BUGS Not supported (in part or whole) on all ethernet drivers. .SH AUTHOR diff --git a/ethtool.c b/ethtool.c index a668b49..46a63bd 100644 --- a/ethtool.c +++ b/ethtool.c @@ -69,6 +69,12 @@ static int do_scoalesce(int fd, struct ifreq *ifr); static int do_goffload(int fd, struct ifreq *ifr); static int do_soffload(int fd, struct ifreq *ifr); static int do_gstats(int fd, struct ifreq *ifr); +static int rxflow_str_to_type(const char *str); +static int parse_rxfhashopts(char *optstr, u32 *data); +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 enum { MODE_HELP = -1, @@ -90,6 +96,8 @@ static enum { MODE_GOFFLOAD, MODE_SOFFLOAD, MODE_GSTATS, + MODE_GNFC, + MODE_SNFC, } mode = MODE_GSET; static struct option { @@ -170,6 +178,14 @@ static struct option { { "-t", "--test", MODE_TEST, "Execute adapter self test", " [ online | offline ]\n" }, { "-S", "--statistics", MODE_GSTATS, "Show adapter statistics" }, + { "-n", "--show-nfc", MODE_GNFC, "Show Rx network flow classification" + "options", + " [ rx-flow-hash tcp4|udp4|ah4|sctp4|" + "tcp6|udp6|ah6|sctp6 ]\n" }, + { "-N", "--config-nfc", MODE_SNFC, "Configure Rx network flow " + "classification options", + " [ rx-flow-hash tcp4|udp4|ah4|sctp4|" + "tcp6|udp6|ah6|sctp6 p|m|v|t|s|d|f|n|r... ]\n" }, { "-h", "--help", MODE_HELP, "Show this help" }, {} }; @@ -266,6 +282,10 @@ static int seeprom_changed = 0; static int seeprom_magic = 0; static int seeprom_offset = -1; static int seeprom_value = 0; +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 enum { ONLINE=0, OFFLINE, @@ -394,6 +414,30 @@ static void parse_generic_cmdline(int argc, char **argp, } } +static int rxflow_str_to_type(const char *str) +{ + int flow_type = 0; + + if (!strcmp(str, "tcp4")) + flow_type = TCP_V4_FLOW; + else if (!strcmp(str, "udp4")) + flow_type = UDP_V4_FLOW; + else if (!strcmp(str, "ah4")) + flow_type = AH_ESP_V4_FLOW; + else if (!strcmp(str, "sctp4")) + flow_type = SCTP_V4_FLOW; + else if (!strcmp(str, "tcp6")) + flow_type = TCP_V6_FLOW; + else if (!strcmp(str, "udp6")) + flow_type = UDP_V6_FLOW; + else if (!strcmp(str, "ah6")) + flow_type = AH_ESP_V6_FLOW; + else if (!strcmp(str, "sctp6")) + flow_type = SCTP_V6_FLOW; + + return flow_type; +} + static void parse_cmdline(int argc, char **argp) { int i, k; @@ -430,6 +474,8 @@ static void parse_cmdline(int argc, char **argp) (mode == MODE_GOFFLOAD) || (mode == MODE_SOFFLOAD) || (mode == MODE_GSTATS) || + (mode == MODE_GNFC) || + (mode == MODE_SNFC) || (mode == MODE_PHYS_ID)) { devname = argp[i]; break; @@ -509,6 +555,48 @@ static void parse_cmdline(int argc, char **argp) i = argc; break; } + if (mode == MODE_GNFC) { + if (!strcmp(argp[i], "rx-flow-hash")) { + i += 1; + if (i >= argc) { + show_usage(1); + break; + } + rx_fhash_get = + rxflow_str_to_type(argp[i]); + if (!rx_fhash_get) + show_usage(1); + } else + show_usage(1); + break; + } + if (mode == MODE_SNFC) { + if (!strcmp(argp[i], "rx-flow-hash")) { + i += 1; + if (i >= argc) { + show_usage(1); + break; + } + rx_fhash_set = + rxflow_str_to_type(argp[i]); + if (!rx_fhash_set) { + show_usage(1); + break; + } + i += 1; + if (i >= argc) { + show_usage(1); + break; + } + if (parse_rxfhashopts(argp[i], + &rx_fhash_val) < 0) + show_usage(1); + else + rx_fhash_changed = 1; + } else + show_usage(1); + break; + } if (mode != MODE_SSET) show_usage(1); if (!strcmp(argp[i], "speed")) { @@ -1000,6 +1088,84 @@ static int parse_sopass(char *src, unsigned char *dest) return 0; } +static int parse_rxfhashopts(char *optstr, u32 *data) +{ + *data = 0; + while (*optstr) { + switch (*optstr) { + case 'p': + *data |= RXH_DEV_PORT; + break; + case 'm': + *data |= RXH_L2DA; + break; + case 'v': + *data |= RXH_VLAN; + break; + case 't': + *data |= RXH_L3_PROTO; + break; + case 's': + *data |= RXH_IP_SRC; + break; + case 'd': + *data |= RXH_IP_DST; + break; + case 'f': + *data |= RXH_L4_BYTES_0_1; + break; + case 'n': + *data |= RXH_L4_BYTES_2_3; + break; + case 'r': + *data |= RXH_DISCARD; + break; + default: + return -1; + } + optstr++; + } + return 0; +} + +static char *unparse_rxfhashopts(u64 opts) +{ + static char buf[300]; + + memset(buf, 0, sizeof(buf)); + + if (opts) { + if (opts & RXH_DEV_PORT) { + strcat(buf, "Dev port\n"); + } + if (opts & RXH_L2DA) { + strcat(buf, "L2DA\n"); + } + if (opts & RXH_VLAN) { + strcat(buf, "VLAN tag\n"); + } + if (opts & RXH_L3_PROTO) { + strcat(buf, "L3 proto\n"); + } + if (opts & RXH_IP_SRC) { + strcat(buf, "IP SA\n"); + } + if (opts & RXH_IP_DST) { + strcat(buf, "IP DA\n"); + } + if (opts & RXH_L4_BYTES_0_1) { + strcat(buf, "L4 bytes 0 & 1 [TCP/UDP src port]\n"); + } + if (opts & RXH_L4_BYTES_2_3) { + strcat(buf, "L4 bytes 2 & 3 [TCP/UDP dst port]\n"); + } + } else { + sprintf(buf, "None"); + } + + return buf; +} + static struct { const char *name; int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs); @@ -1236,6 +1402,48 @@ static int dump_offload (int rx, int tx, int sg, int tso, int ufo, int gso) return 0; } +static int dump_rxfhash(int fhash, u64 val) +{ + switch (fhash) { + case TCP_V4_FLOW: + fprintf(stdout, "TCP over IPV4 flows"); + break; + case UDP_V4_FLOW: + fprintf(stdout, "UDP over IPV4 flows"); + break; + case SCTP_V4_FLOW: + fprintf(stdout, "SCTP over IPV4 flows"); + break; + case AH_ESP_V4_FLOW: + fprintf(stdout, "IPSEC AH over IPV4 flows"); + break; + case TCP_V6_FLOW: + fprintf(stdout, "TCP over IPV6 flows"); + break; + case UDP_V6_FLOW: + fprintf(stdout, "UDP over IPV6 flows"); + break; + case SCTP_V6_FLOW: + fprintf(stdout, "SCTP over IPV6 flows"); + break; + case AH_ESP_V6_FLOW: + fprintf(stdout, "IPSEC AH over IPV6 flows"); + break; + default: + break; + } + + if (val & RXH_DISCARD) { + fprintf(stdout, " - All matching flows discarded on RX\n"); + return 0; + } + fprintf(stdout, " use these fields for computing Hash flow key:\n"); + + fprintf(stdout, "%s\n", unparse_rxfhashopts(val)); + + return 0; +} + static int doit(void) { struct ifreq ifr; @@ -1289,6 +1497,10 @@ static int doit(void) return do_soffload(fd, &ifr); } else if (mode == MODE_GSTATS) { return do_gstats(fd, &ifr); + } else if (mode == MODE_GNFC) { + return do_grxclass(fd, &ifr); + } else if (mode == MODE_SNFC) { + return do_srxclass(fd, &ifr); } return 69; @@ -2085,6 +2297,48 @@ static int do_gstats(int fd, struct ifreq *ifr) return 0; } + +static int do_srxclass(int fd, struct ifreq *ifr) +{ + int err; + + if (rx_fhash_changed) { + struct ethtool_rxnfc nfccmd; + + nfccmd.cmd = ETHTOOL_SRXFH; + nfccmd.flow_type = rx_fhash_set; + nfccmd.data = rx_fhash_val; + + ifr->ifr_data = (caddr_t)&nfccmd; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) + perror("Cannot change RX network flow hashing options"); + + } + + return 0; +} + +static int do_grxclass(int fd, struct ifreq *ifr) +{ + int err; + + if (rx_fhash_get) { + struct ethtool_rxnfc nfccmd; + + nfccmd.cmd = ETHTOOL_GRXFH; + nfccmd.flow_type = rx_fhash_get; + ifr->ifr_data = (caddr_t)&nfccmd; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) + perror("Cannot get RX network flow hashing options"); + else + dump_rxfhash(rx_fhash_get, nfccmd.data); + } + + return 0; +} + int main(int argc, char **argp, char **envp) { parse_cmdline(argc, argp); -- 1.5.2