summaryrefslogtreecommitdiff
path: root/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'ethtool.c')
-rw-r--r--ethtool.c400
1 files changed, 227 insertions, 173 deletions
diff --git a/ethtool.c b/ethtool.c
index 24d4e4f..2e04d87 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -6,6 +6,7 @@
* Kernel 2.4 update Copyright 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
* Wake-on-LAN,natsemi,misc support by Tim Hockin <thockin@sun.com>
* Portions Copyright 2002 Intel
+ * Portions Copyright (C) Sun Microsystems 2008
* do_test support by Eli Kupermann <eli.kupermann@intel.com>
* ETHTOOL_PHYS_ID support by Chris Leech <christopher.leech@intel.com>
* e1000 support by Scott Feldman <scott.feldman@intel.com>
@@ -14,6 +15,7 @@
* amd8111e support by Reeja John <reeja.john@amd.com>
* long arguments by Andi Kleen.
* SMSC LAN911x support by Steve Glendinning <steve.glendinning@smsc.com>
+ * Rx Network Flow Control configuration support <santwona.behera@sun.com>
* Various features by Ben Hutchings <bhutchings@solarflare.com>;
* Copyright 2009, 2010 Solarflare Communications
*
@@ -85,14 +87,13 @@ 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 void parse_rxntupleopts(int argc, char **argp, int first_arg);
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_srxclsrule(int fd, struct ifreq *ifr);
+static int do_grxclsrule(int fd, struct ifreq *ifr);
static int do_flash(int fd, struct ifreq *ifr);
static int do_permaddr(int fd, struct ifreq *ifr);
@@ -123,8 +124,8 @@ static enum {
MODE_SNFC,
MODE_GRXFHINDIR,
MODE_SRXFHINDIR,
- MODE_SNTUPLE,
- MODE_GNTUPLE,
+ MODE_SCLSRULE,
+ MODE_GCLSRULE,
MODE_FLASHDEV,
MODE_PERMADDR,
} mode = MODE_GSET;
@@ -230,22 +231,28 @@ static struct option {
"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 "
+ { "-U", "--config-ntuple", MODE_SCLSRULE, "Configure Rx ntuple filters "
"and actions",
- " { flow-type tcp4|udp4|sctp4\n"
- " [ src-ip ADDR [src-ip-mask MASK] ]\n"
- " [ dst-ip ADDR [dst-ip-mask MASK] ]\n"
- " [ src-port PORT [src-port-mask MASK] ]\n"
- " [ dst-port PORT [dst-port-mask MASK] ]\n"
- " | flow-type ether\n"
- " [ src MAC-ADDR [src-mask MASK] ]\n"
- " [ dst MAC-ADDR [dst-mask MASK] ]\n"
- " [ proto N [proto-mask MASK] ] }\n"
- " [ vlan VLAN-TAG [vlan-mask MASK] ]\n"
- " [ user-def DATA [user-def-mask MASK] ]\n"
- " action N\n" },
- { "-u", "--show-ntuple", MODE_GNTUPLE,
- "Get Rx ntuple filters and actions\n" },
+ " [ delete %d ] |\n"
+ " [ flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4\n"
+ " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+ " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+ " [ proto %d [m %x] ]\n"
+ " [ src-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
+ " [ dst-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
+ " [ tos %d [m %x] ]\n"
+ " [ l4proto %d [m %x] ]\n"
+ " [ src-port %d [m %x] ]\n"
+ " [ dst-port %d [m %x] ]\n"
+ " [ spi %d [m %x] ]\n"
+ " [ vlan-etype %x [m %x] ]\n"
+ " [ vlan %x [m %x] ]\n"
+ " [ user-def %x [m %x] ]\n"
+ " [ action %d ]\n"
+ " [ loc %d]]\n" },
+ { "-u", "--show-ntuple", MODE_GCLSRULE,
+ "Get Rx ntuple filters and actions",
+ " [ rule %d ]\n"},
{ "-P", "--show-permaddr", MODE_PERMADDR,
"Show permanent hardware address" },
{ "-h", "--help", MODE_HELP, "Show this help" },
@@ -371,26 +378,6 @@ 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 int ntuple_ip4src_seen = 0;
-static int ntuple_ip4src_mask_seen = 0;
-static int ntuple_ip4dst_seen = 0;
-static int ntuple_ip4dst_mask_seen = 0;
-static int ntuple_psrc_seen = 0;
-static int ntuple_psrc_mask_seen = 0;
-static int ntuple_pdst_seen = 0;
-static int ntuple_pdst_mask_seen = 0;
-static int ntuple_ether_dst_seen = 0;
-static int ntuple_ether_dst_mask_seen = 0;
-static int ntuple_ether_src_seen = 0;
-static int ntuple_ether_src_mask_seen = 0;
-static int ntuple_ether_proto_seen = 0;
-static int ntuple_ether_proto_mask_seen = 0;
-static int ntuple_vlan_tag_seen = 0;
-static int ntuple_vlan_tag_mask_seen = 0;
-static int ntuple_user_def_seen = 0;
-static int ntuple_user_def_mask_seen = 0;
static char *flash_file = NULL;
static int flash = -1;
static int flash_region = -1;
@@ -399,6 +386,11 @@ static int msglvl_changed;
static u32 msglvl_wanted = 0;
static u32 msglvl_mask = 0;
+static int rx_class_rule_get = -1;
+static int rx_class_rule_del = -1;
+static int rx_class_rule_added = 0;
+static struct ethtool_rx_flow_spec rx_rule_fs;
+
static enum {
ONLINE=0,
OFFLINE,
@@ -511,58 +503,6 @@ static struct cmdline_info cmdline_coalesce[] = {
{ "tx-frames-high", CMDL_S32, &coal_tx_frames_high_wanted, &ecoal.tx_max_coalesced_frames_high },
};
-static struct cmdline_info cmdline_ntuple_tcp_ip4[] = {
- { "src-ip", CMDL_IP4, &ntuple_fs.h_u.tcp_ip4_spec.ip4src, NULL,
- 0, &ntuple_ip4src_seen },
- { "src-ip-mask", CMDL_IP4, &ntuple_fs.m_u.tcp_ip4_spec.ip4src, NULL,
- 0, &ntuple_ip4src_mask_seen },
- { "dst-ip", CMDL_IP4, &ntuple_fs.h_u.tcp_ip4_spec.ip4dst, NULL,
- 0, &ntuple_ip4dst_seen },
- { "dst-ip-mask", CMDL_IP4, &ntuple_fs.m_u.tcp_ip4_spec.ip4dst, NULL,
- 0, &ntuple_ip4dst_mask_seen },
- { "src-port", CMDL_BE16, &ntuple_fs.h_u.tcp_ip4_spec.psrc, NULL,
- 0, &ntuple_psrc_seen },
- { "src-port-mask", CMDL_BE16, &ntuple_fs.m_u.tcp_ip4_spec.psrc, NULL,
- 0, &ntuple_psrc_mask_seen },
- { "dst-port", CMDL_BE16, &ntuple_fs.h_u.tcp_ip4_spec.pdst, NULL,
- 0, &ntuple_pdst_seen },
- { "dst-port-mask", CMDL_BE16, &ntuple_fs.m_u.tcp_ip4_spec.pdst, NULL,
- 0, &ntuple_pdst_mask_seen },
- { "vlan", CMDL_U16, &ntuple_fs.vlan_tag, NULL,
- 0, &ntuple_vlan_tag_seen },
- { "vlan-mask", CMDL_U16, &ntuple_fs.vlan_tag_mask, NULL,
- 0, &ntuple_vlan_tag_mask_seen },
- { "user-def", CMDL_U64, &ntuple_fs.data, NULL,
- 0, &ntuple_user_def_seen },
- { "user-def-mask", CMDL_U64, &ntuple_fs.data_mask, NULL,
- 0, &ntuple_user_def_mask_seen },
- { "action", CMDL_S32, &ntuple_fs.action, NULL },
-};
-
-static struct cmdline_info cmdline_ntuple_ether[] = {
- { "dst", CMDL_MAC, ntuple_fs.h_u.ether_spec.h_dest, NULL,
- 0, &ntuple_ether_dst_seen },
- { "dst-mask", CMDL_MAC, ntuple_fs.m_u.ether_spec.h_dest, NULL,
- 0, &ntuple_ether_dst_mask_seen },
- { "src", CMDL_MAC, ntuple_fs.h_u.ether_spec.h_source, NULL,
- 0, &ntuple_ether_src_seen },
- { "src-mask", CMDL_MAC, ntuple_fs.m_u.ether_spec.h_source, NULL,
- 0, &ntuple_ether_src_mask_seen },
- { "proto", CMDL_BE16, &ntuple_fs.h_u.ether_spec.h_proto, NULL,
- 0, &ntuple_ether_proto_seen },
- { "proto-mask", CMDL_BE16, &ntuple_fs.m_u.ether_spec.h_proto, NULL,
- 0, &ntuple_ether_proto_mask_seen },
- { "vlan", CMDL_U16, &ntuple_fs.vlan_tag, NULL,
- 0, &ntuple_vlan_tag_seen },
- { "vlan-mask", CMDL_U16, &ntuple_fs.vlan_tag_mask, NULL,
- 0, &ntuple_vlan_tag_mask_seen },
- { "user-def", CMDL_U64, &ntuple_fs.data, NULL,
- 0, &ntuple_user_def_seen },
- { "user-def-mask", CMDL_U64, &ntuple_fs.data_mask, NULL,
- 0, &ntuple_user_def_mask_seen },
- { "action", CMDL_S32, &ntuple_fs.action, NULL },
-};
-
static struct cmdline_info cmdline_msglvl[] = {
{ "drv", CMDL_FLAG, &msglvl_wanted, NULL,
NETIF_MSG_DRV, &msglvl_mask },
@@ -833,8 +773,8 @@ static void parse_cmdline(int argc, char **argp)
(mode == MODE_SNFC) ||
(mode == MODE_GRXFHINDIR) ||
(mode == MODE_SRXFHINDIR) ||
- (mode == MODE_SNTUPLE) ||
- (mode == MODE_GNTUPLE) ||
+ (mode == MODE_SCLSRULE) ||
+ (mode == MODE_GCLSRULE) ||
(mode == MODE_PHYS_ID) ||
(mode == MODE_FLASHDEV) ||
(mode == MODE_PERMADDR)) {
@@ -918,16 +858,45 @@ static void parse_cmdline(int argc, char **argp)
i = argc;
break;
}
- if (mode == MODE_SNTUPLE) {
+ if (mode == MODE_SCLSRULE) {
if (!strcmp(argp[i], "flow-type")) {
i += 1;
if (i >= argc) {
exit_bad_args();
break;
}
- parse_rxntupleopts(argc, argp, i);
- i = argc;
- break;
+ if (rxclass_parse_ruleopts(&argp[i],
+ argc - i,
+ &rx_rule_fs) < 0) {
+ exit_bad_args();
+ } else {
+ i = argc;
+ rx_class_rule_added = 1;
+ }
+ } else if (!strcmp(argp[i], "delete")) {
+ i += 1;
+ if (i >= argc) {
+ exit_bad_args();
+ break;
+ }
+ rx_class_rule_del =
+ get_uint_range(argp[i], 0,
+ INT_MAX);
+ } else {
+ exit_bad_args();
+ }
+ break;
+ }
+ if (mode == MODE_GCLSRULE) {
+ if (!strcmp(argp[i], "rule")) {
+ i += 1;
+ if (i >= argc) {
+ exit_bad_args();
+ break;
+ }
+ rx_class_rule_get =
+ get_uint_range(argp[i], 0,
+ INT_MAX);
} else {
exit_bad_args();
}
@@ -1598,66 +1567,6 @@ static char *unparse_rxfhashopts(u64 opts)
return buf;
}
-static void parse_rxntupleopts(int argc, char **argp, int i)
-{
- ntuple_fs.flow_type = rxflow_str_to_type(argp[i]);
-
- switch (ntuple_fs.flow_type) {
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- case SCTP_V4_FLOW:
- parse_generic_cmdline(argc, argp, i + 1,
- &sntuple_changed,
- cmdline_ntuple_tcp_ip4,
- ARRAY_SIZE(cmdline_ntuple_tcp_ip4));
- if (!ntuple_ip4src_seen)
- ntuple_fs.m_u.tcp_ip4_spec.ip4src = 0xffffffff;
- if (!ntuple_ip4dst_seen)
- ntuple_fs.m_u.tcp_ip4_spec.ip4dst = 0xffffffff;
- if (!ntuple_psrc_seen)
- ntuple_fs.m_u.tcp_ip4_spec.psrc = 0xffff;
- if (!ntuple_pdst_seen)
- ntuple_fs.m_u.tcp_ip4_spec.pdst = 0xffff;
- ntuple_fs.m_u.tcp_ip4_spec.tos = 0xff;
- break;
- case ETHER_FLOW:
- parse_generic_cmdline(argc, argp, i + 1,
- &sntuple_changed,
- cmdline_ntuple_ether,
- ARRAY_SIZE(cmdline_ntuple_ether));
- if (!ntuple_ether_dst_seen)
- memset(ntuple_fs.m_u.ether_spec.h_dest, 0xff, ETH_ALEN);
- if (!ntuple_ether_src_seen)
- memset(ntuple_fs.m_u.ether_spec.h_source, 0xff,
- ETH_ALEN);
- if (!ntuple_ether_proto_seen)
- ntuple_fs.m_u.ether_spec.h_proto = 0xffff;
- break;
- default:
- fprintf(stderr, "Unsupported flow type \"%s\"\n", argp[i]);
- exit(106);
- break;
- }
-
- if (!ntuple_vlan_tag_seen)
- ntuple_fs.vlan_tag_mask = 0xffff;
- if (!ntuple_user_def_seen)
- ntuple_fs.data_mask = 0xffffffffffffffffULL;
-
- if ((ntuple_ip4src_mask_seen && !ntuple_ip4src_seen) ||
- (ntuple_ip4dst_mask_seen && !ntuple_ip4dst_seen) ||
- (ntuple_psrc_mask_seen && !ntuple_psrc_seen) ||
- (ntuple_pdst_mask_seen && !ntuple_pdst_seen) ||
- (ntuple_ether_dst_mask_seen && !ntuple_ether_dst_seen) ||
- (ntuple_ether_src_mask_seen && !ntuple_ether_src_seen) ||
- (ntuple_ether_proto_mask_seen && !ntuple_ether_proto_seen) ||
- (ntuple_vlan_tag_mask_seen && !ntuple_vlan_tag_seen) ||
- (ntuple_user_def_mask_seen && !ntuple_user_def_seen)) {
- fprintf(stderr, "Cannot specify mask without value\n");
- exit(107);
- }
-}
-
static struct {
const char *name;
int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
@@ -2019,10 +1928,10 @@ static int doit(void)
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) {
- return do_grxntuple(fd, &ifr);
+ } else if (mode == MODE_SCLSRULE) {
+ return do_srxclsrule(fd, &ifr);
+ } else if (mode == MODE_GCLSRULE) {
+ return do_grxclsrule(fd, &ifr);
} else if (mode == MODE_FLASHDEV) {
return do_flash(fd, &ifr);
} else if (mode == MODE_PERMADDR) {
@@ -3155,21 +3064,143 @@ static int do_permaddr(int fd, struct ifreq *ifr)
return err;
}
+static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
+ struct ethtool_rx_ntuple_flow_spec *ntuple)
+{
+ size_t i;
+
+ /* verify location is not specified */
+ if (fsp->location != RX_CLS_LOC_UNSPEC)
+ return -1;
+
+ /* verify ring cookie can transfer to action */
+ if (fsp->ring_cookie > INT_MAX && fsp->ring_cookie < (u64)(-2))
+ return -1;
+
+ /* verify only one field is setting data field */
+ if ((fsp->flow_type & FLOW_EXT) &&
+ (fsp->m_ext.data[0] || fsp->m_ext.data[1]) &&
+ fsp->m_ext.vlan_etype)
+ return -1;
+
+ /* Set entire ntuple to ~0 to guarantee all masks are set */
+ memset(ntuple, ~0, sizeof(*ntuple));
+
+ /* set non-filter values */
+ ntuple->flow_type = fsp->flow_type;
+ ntuple->action = fsp->ring_cookie;
+
+ /*
+ * Copy over header union, they are identical in layout however
+ * the ntuple union contains additional padding on the end
+ */
+ memcpy(&ntuple->h_u, &fsp->h_u, sizeof(fsp->h_u));
+
+ /*
+ * The same rule mentioned above applies to the mask union. However,
+ * in addition we need to invert the mask bits to match the ntuple
+ * mask which is 1 for masked, versus 0 for masked as seen in nfc.
+ */
+ memcpy(&ntuple->m_u, &fsp->m_u, sizeof(fsp->m_u));
+ for (i = 0; i < sizeof(fsp->m_u); i++)
+ ntuple->m_u.hdata[i] ^= 0xFF;
+
+ /* copy extended fields */
+ if (fsp->flow_type & FLOW_EXT) {
+ ntuple->vlan_tag =
+ ntohs(fsp->h_ext.vlan_tci);
+ ntuple->vlan_tag_mask =
+ ~ntohs(fsp->m_ext.vlan_tci);
+ if (fsp->m_ext.vlan_etype) {
+ /*
+ * vlan_etype and user data are mutually exclusive
+ * in ntuple configuration as they occupy the same
+ * space.
+ */
+ if (fsp->m_ext.data[0] || fsp->m_ext.data[1])
+ return -1;
+ ntuple->data =
+ ntohl(fsp->h_ext.vlan_etype);
+ ntuple->data_mask =
+ ~(u64)ntohl(fsp->m_ext.vlan_etype);
+ } else {
+ ntuple->data =
+ (u64)ntohl(fsp->h_ext.data[0]) << 32;
+ ntuple->data |=
+ (u64)ntohl(fsp->h_ext.data[1]);
+ ntuple->data_mask =
+ (u64)ntohl(~fsp->m_ext.data[0]) << 32;
+ ntuple->data_mask |=
+ (u64)ntohl(~fsp->m_ext.data[1]);
+ }
+ }
+
+ return 0;
+}
+
static int do_srxntuple(int fd, struct ifreq *ifr)
{
+ struct ethtool_rx_ntuple ntuplecmd;
+ struct ethtool_value eval;
int err;
- if (sntuple_changed) {
- struct ethtool_rx_ntuple ntuplecmd;
+ /* attempt to convert the flow classifier to an ntuple classifier */
+ err = flow_spec_to_ntuple(&rx_rule_fs, &ntuplecmd.fs);
+ if (err)
+ return -1;
- ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;
- memcpy(&ntuplecmd.fs, &ntuple_fs,
- sizeof(struct ethtool_rx_ntuple_flow_spec));
+ /*
+ * Check to see if the flag is set for N-tuple, this allows
+ * us to avoid the possible EINVAL response for the N-tuple
+ * flag not being set on the device
+ */
+ eval.cmd = ETHTOOL_GFLAGS;
+ ifr->ifr_data = (caddr_t)&eval;
+ err = ioctl(fd, SIOCETHTOOL, ifr);
+ if (err || !(eval.data & ETH_FLAG_NTUPLE))
+ return -1;
- ifr->ifr_data = (caddr_t)&ntuplecmd;
- err = ioctl(fd, SIOCETHTOOL, ifr);
- if (err < 0)
- perror("Cannot add new RX n-tuple filter");
+ /* send rule via N-tuple */
+ ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;
+ ifr->ifr_data = (caddr_t)&ntuplecmd;
+ err = ioctl(fd, SIOCETHTOOL, ifr);
+
+ /*
+ * Display error only if reponse is something other than op not
+ * supported. It is possible that the interface uses the network
+ * flow classifier interface instead of N-tuple.
+ */
+ if (err && errno != EOPNOTSUPP)
+ perror("Cannot add new rule via N-tuple");
+
+ return err;
+}
+
+static int do_srxclsrule(int fd, struct ifreq *ifr)
+{
+ int err;
+
+ if (rx_class_rule_added) {
+ /* attempt to add rule via N-tuple specifier */
+ err = do_srxntuple(fd, ifr);
+ if (!err)
+ return 0;
+
+ /* attempt to add rule via network flow classifier */
+ err = rxclass_rule_ins(fd, ifr, &rx_rule_fs);
+ if (err < 0) {
+ fprintf(stderr, "Cannot insert"
+ " classification rule\n");
+ return 1;
+ }
+ } else if (rx_class_rule_del >= 0) {
+ err = rxclass_rule_del(fd, ifr, rx_class_rule_del);
+
+ if (err < 0) {
+ fprintf(stderr, "Cannot delete"
+ " classification rule\n");
+ return 1;
+ }
} else {
exit_bad_args();
}
@@ -3177,9 +3208,32 @@ static int do_srxntuple(int fd, struct ifreq *ifr)
return 0;
}
-static int do_grxntuple(int fd, struct ifreq *ifr)
+static int do_grxclsrule(int fd, struct ifreq *ifr)
{
- return 0;
+ struct ethtool_rxnfc nfccmd;
+ int err;
+
+ if (rx_class_rule_get >= 0) {
+ err = rxclass_rule_get(fd, ifr, rx_class_rule_get);
+ if (err < 0)
+ fprintf(stderr, "Cannot get RX classification rule\n");
+ return err ? 1 : 0;
+ }
+
+ nfccmd.cmd = ETHTOOL_GRXRINGS;
+ ifr->ifr_data = (caddr_t)&nfccmd;
+ err = ioctl(fd, SIOCETHTOOL, ifr);
+ if (err < 0)
+ perror("Cannot get RX rings");
+ else
+ fprintf(stdout, "%d RX rings available\n",
+ (int)nfccmd.data);
+
+ err = rxclass_rule_getall(fd, ifr);
+ if (err < 0)
+ fprintf(stderr, "RX classification rule retrieval failed\n");
+
+ return err ? 1 : 0;
}
static int send_ioctl(int fd, struct ifreq *ifr)