diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2010-09-20 04:43:42 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-09-21 17:58:00 -0400 |
commit | b4187e4277b13d7bc4acc3c953b3cab0137b14b2 (patch) | |
tree | 83647558702b6051491f04c029a7eea53147f00b /drivers/net/sfc/ethtool.c | |
parent | 64eebcfd899a5d2ebe211a593ec13ec24630f1a3 (diff) |
sfc: Implement the ethtool RX n-tuple control functions
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc/ethtool.c')
-rw-r--r-- | drivers/net/sfc/ethtool.c | 118 |
1 files changed, 116 insertions, 2 deletions
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 7f735d804801..c95328fa3ee8 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include "workarounds.h" | 15 | #include "workarounds.h" |
16 | #include "selftest.h" | 16 | #include "selftest.h" |
17 | #include "efx.h" | 17 | #include "efx.h" |
18 | #include "filter.h" | ||
18 | #include "nic.h" | 19 | #include "nic.h" |
19 | #include "spi.h" | 20 | #include "spi.h" |
20 | #include "mdio_10g.h" | 21 | #include "mdio_10g.h" |
@@ -551,9 +552,22 @@ static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev) | |||
551 | static int efx_ethtool_set_flags(struct net_device *net_dev, u32 data) | 552 | static int efx_ethtool_set_flags(struct net_device *net_dev, u32 data) |
552 | { | 553 | { |
553 | struct efx_nic *efx = netdev_priv(net_dev); | 554 | struct efx_nic *efx = netdev_priv(net_dev); |
554 | u32 supported = efx->type->offload_features & ETH_FLAG_RXHASH; | 555 | u32 supported = (efx->type->offload_features & |
556 | (ETH_FLAG_RXHASH | ETH_FLAG_NTUPLE)); | ||
557 | int rc; | ||
558 | |||
559 | rc = ethtool_op_set_flags(net_dev, data, supported); | ||
560 | if (rc) | ||
561 | return rc; | ||
562 | |||
563 | if (!(data & ETH_FLAG_NTUPLE)) { | ||
564 | efx_filter_table_clear(efx, EFX_FILTER_TABLE_RX_IP, | ||
565 | EFX_FILTER_PRI_MANUAL); | ||
566 | efx_filter_table_clear(efx, EFX_FILTER_TABLE_RX_MAC, | ||
567 | EFX_FILTER_PRI_MANUAL); | ||
568 | } | ||
555 | 569 | ||
556 | return ethtool_op_set_flags(net_dev, data, supported); | 570 | return 0; |
557 | } | 571 | } |
558 | 572 | ||
559 | static void efx_ethtool_self_test(struct net_device *net_dev, | 573 | static void efx_ethtool_self_test(struct net_device *net_dev, |
@@ -955,6 +969,105 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, | |||
955 | } | 969 | } |
956 | } | 970 | } |
957 | 971 | ||
972 | static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, | ||
973 | struct ethtool_rx_ntuple *ntuple) | ||
974 | { | ||
975 | struct efx_nic *efx = netdev_priv(net_dev); | ||
976 | struct ethtool_tcpip4_spec *ip_entry = &ntuple->fs.h_u.tcp_ip4_spec; | ||
977 | struct ethtool_tcpip4_spec *ip_mask = &ntuple->fs.m_u.tcp_ip4_spec; | ||
978 | struct ethhdr *mac_entry = &ntuple->fs.h_u.ether_spec; | ||
979 | struct ethhdr *mac_mask = &ntuple->fs.m_u.ether_spec; | ||
980 | struct efx_filter_spec filter; | ||
981 | |||
982 | /* Range-check action */ | ||
983 | if (ntuple->fs.action < ETHTOOL_RXNTUPLE_ACTION_CLEAR || | ||
984 | ntuple->fs.action >= (s32)efx->n_rx_channels) | ||
985 | return -EINVAL; | ||
986 | |||
987 | if (~ntuple->fs.data_mask) | ||
988 | return -EINVAL; | ||
989 | |||
990 | switch (ntuple->fs.flow_type) { | ||
991 | case TCP_V4_FLOW: | ||
992 | case UDP_V4_FLOW: | ||
993 | /* Must match all of destination, */ | ||
994 | if (ip_mask->ip4dst | ip_mask->pdst) | ||
995 | return -EINVAL; | ||
996 | /* all or none of source, */ | ||
997 | if ((ip_mask->ip4src | ip_mask->psrc) && | ||
998 | ((__force u32)~ip_mask->ip4src | | ||
999 | (__force u16)~ip_mask->psrc)) | ||
1000 | return -EINVAL; | ||
1001 | /* and nothing else */ | ||
1002 | if ((u8)~ip_mask->tos | (u16)~ntuple->fs.vlan_tag_mask) | ||
1003 | return -EINVAL; | ||
1004 | break; | ||
1005 | case ETHER_FLOW: | ||
1006 | /* Must match all of destination, */ | ||
1007 | if (!is_zero_ether_addr(mac_mask->h_dest)) | ||
1008 | return -EINVAL; | ||
1009 | /* all or none of VID, */ | ||
1010 | if (ntuple->fs.vlan_tag_mask != 0xf000 && | ||
1011 | ntuple->fs.vlan_tag_mask != 0xffff) | ||
1012 | return -EINVAL; | ||
1013 | /* and nothing else */ | ||
1014 | if (!is_broadcast_ether_addr(mac_mask->h_source) || | ||
1015 | mac_mask->h_proto != htons(0xffff)) | ||
1016 | return -EINVAL; | ||
1017 | break; | ||
1018 | default: | ||
1019 | return -EINVAL; | ||
1020 | } | ||
1021 | |||
1022 | filter.priority = EFX_FILTER_PRI_MANUAL; | ||
1023 | filter.flags = 0; | ||
1024 | |||
1025 | switch (ntuple->fs.flow_type) { | ||
1026 | case TCP_V4_FLOW: | ||
1027 | if (!ip_mask->ip4src) | ||
1028 | efx_filter_set_rx_tcp_full(&filter, | ||
1029 | htonl(ip_entry->ip4src), | ||
1030 | htons(ip_entry->psrc), | ||
1031 | htonl(ip_entry->ip4dst), | ||
1032 | htons(ip_entry->pdst)); | ||
1033 | else | ||
1034 | efx_filter_set_rx_tcp_wild(&filter, | ||
1035 | htonl(ip_entry->ip4dst), | ||
1036 | htons(ip_entry->pdst)); | ||
1037 | break; | ||
1038 | case UDP_V4_FLOW: | ||
1039 | if (!ip_mask->ip4src) | ||
1040 | efx_filter_set_rx_udp_full(&filter, | ||
1041 | htonl(ip_entry->ip4src), | ||
1042 | htons(ip_entry->psrc), | ||
1043 | htonl(ip_entry->ip4dst), | ||
1044 | htons(ip_entry->pdst)); | ||
1045 | else | ||
1046 | efx_filter_set_rx_udp_wild(&filter, | ||
1047 | htonl(ip_entry->ip4dst), | ||
1048 | htons(ip_entry->pdst)); | ||
1049 | break; | ||
1050 | case ETHER_FLOW: | ||
1051 | if (ntuple->fs.vlan_tag_mask == 0xf000) | ||
1052 | efx_filter_set_rx_mac_full(&filter, | ||
1053 | ntuple->fs.vlan_tag & 0xfff, | ||
1054 | mac_entry->h_dest); | ||
1055 | else | ||
1056 | efx_filter_set_rx_mac_wild(&filter, mac_entry->h_dest); | ||
1057 | break; | ||
1058 | } | ||
1059 | |||
1060 | if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_CLEAR) { | ||
1061 | return efx_filter_remove_filter(efx, &filter); | ||
1062 | } else { | ||
1063 | if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) | ||
1064 | filter.dmaq_id = 0xfff; | ||
1065 | else | ||
1066 | filter.dmaq_id = ntuple->fs.action; | ||
1067 | return efx_filter_insert_filter(efx, &filter, true); | ||
1068 | } | ||
1069 | } | ||
1070 | |||
958 | static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, | 1071 | static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, |
959 | struct ethtool_rxfh_indir *indir) | 1072 | struct ethtool_rxfh_indir *indir) |
960 | { | 1073 | { |
@@ -1033,6 +1146,7 @@ const struct ethtool_ops efx_ethtool_ops = { | |||
1033 | .set_wol = efx_ethtool_set_wol, | 1146 | .set_wol = efx_ethtool_set_wol, |
1034 | .reset = efx_ethtool_reset, | 1147 | .reset = efx_ethtool_reset, |
1035 | .get_rxnfc = efx_ethtool_get_rxnfc, | 1148 | .get_rxnfc = efx_ethtool_get_rxnfc, |
1149 | .set_rx_ntuple = efx_ethtool_set_rx_ntuple, | ||
1036 | .get_rxfh_indir = efx_ethtool_get_rxfh_indir, | 1150 | .get_rxfh_indir = efx_ethtool_get_rxfh_indir, |
1037 | .set_rxfh_indir = efx_ethtool_set_rxfh_indir, | 1151 | .set_rxfh_indir = efx_ethtool_set_rxfh_indir, |
1038 | }; | 1152 | }; |