diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2012-01-03 07:05:47 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-01-04 14:10:18 -0500 |
commit | b2bb7b776a9b16508641a0854642d9737d7621eb (patch) | |
tree | fed1d91b3981dbf5dc2977fcb5d6dd1705cb6a59 /drivers/net/ethernet/sfc | |
parent | 1a6281ac5cf7285cbc2b1f9725dcf1a2461eac83 (diff) |
sfc: Implement ethtool RX NFC rules API instead of n-tuple API
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/sfc')
-rw-r--r-- | drivers/net/ethernet/sfc/ethtool.c | 188 |
1 files changed, 149 insertions, 39 deletions
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 1be51b2bfa42..29b2ebfef19f 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c | |||
@@ -818,9 +818,58 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) | |||
818 | return efx_reset(efx, rc); | 818 | return efx_reset(efx, rc); |
819 | } | 819 | } |
820 | 820 | ||
821 | static int efx_ethtool_get_class_rule(struct efx_nic *efx, | ||
822 | struct ethtool_rx_flow_spec *rule) | ||
823 | { | ||
824 | struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; | ||
825 | struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; | ||
826 | struct efx_filter_spec spec; | ||
827 | u16 vid; | ||
828 | u8 proto; | ||
829 | int rc; | ||
830 | |||
831 | rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL, | ||
832 | rule->location, &spec); | ||
833 | if (rc) | ||
834 | return rc; | ||
835 | |||
836 | if (spec.dmaq_id == 0xfff) | ||
837 | rule->ring_cookie = RX_CLS_FLOW_DISC; | ||
838 | else | ||
839 | rule->ring_cookie = spec.dmaq_id; | ||
840 | |||
841 | rc = efx_filter_get_eth_local(&spec, &vid, | ||
842 | rule->h_u.ether_spec.h_dest); | ||
843 | if (rc == 0) { | ||
844 | rule->flow_type = ETHER_FLOW; | ||
845 | memset(rule->m_u.ether_spec.h_dest, ~0, ETH_ALEN); | ||
846 | if (vid != EFX_FILTER_VID_UNSPEC) { | ||
847 | rule->flow_type |= FLOW_EXT; | ||
848 | rule->h_ext.vlan_tci = htons(vid); | ||
849 | rule->m_ext.vlan_tci = htons(0xfff); | ||
850 | } | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | rc = efx_filter_get_ipv4_local(&spec, &proto, | ||
855 | &ip_entry->ip4dst, &ip_entry->pdst); | ||
856 | if (rc != 0) { | ||
857 | rc = efx_filter_get_ipv4_full( | ||
858 | &spec, &proto, &ip_entry->ip4src, &ip_entry->psrc, | ||
859 | &ip_entry->ip4dst, &ip_entry->pdst); | ||
860 | EFX_WARN_ON_PARANOID(rc); | ||
861 | ip_mask->ip4src = ~0; | ||
862 | ip_mask->psrc = ~0; | ||
863 | } | ||
864 | rule->flow_type = (proto == IPPROTO_TCP) ? TCP_V4_FLOW : UDP_V4_FLOW; | ||
865 | ip_mask->ip4dst = ~0; | ||
866 | ip_mask->pdst = ~0; | ||
867 | return rc; | ||
868 | } | ||
869 | |||
821 | static int | 870 | static int |
822 | efx_ethtool_get_rxnfc(struct net_device *net_dev, | 871 | efx_ethtool_get_rxnfc(struct net_device *net_dev, |
823 | struct ethtool_rxnfc *info, u32 *rules __always_unused) | 872 | struct ethtool_rxnfc *info, u32 *rule_locs) |
824 | { | 873 | { |
825 | struct efx_nic *efx = netdev_priv(net_dev); | 874 | struct efx_nic *efx = netdev_priv(net_dev); |
826 | 875 | ||
@@ -862,42 +911,80 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev, | |||
862 | return 0; | 911 | return 0; |
863 | } | 912 | } |
864 | 913 | ||
914 | case ETHTOOL_GRXCLSRLCNT: | ||
915 | info->data = efx_filter_get_rx_id_limit(efx); | ||
916 | if (info->data == 0) | ||
917 | return -EOPNOTSUPP; | ||
918 | info->data |= RX_CLS_LOC_SPECIAL; | ||
919 | info->rule_cnt = | ||
920 | efx_filter_count_rx_used(efx, EFX_FILTER_PRI_MANUAL); | ||
921 | return 0; | ||
922 | |||
923 | case ETHTOOL_GRXCLSRULE: | ||
924 | if (efx_filter_get_rx_id_limit(efx) == 0) | ||
925 | return -EOPNOTSUPP; | ||
926 | return efx_ethtool_get_class_rule(efx, &info->fs); | ||
927 | |||
928 | case ETHTOOL_GRXCLSRLALL: { | ||
929 | s32 rc; | ||
930 | info->data = efx_filter_get_rx_id_limit(efx); | ||
931 | if (info->data == 0) | ||
932 | return -EOPNOTSUPP; | ||
933 | rc = efx_filter_get_rx_ids(efx, EFX_FILTER_PRI_MANUAL, | ||
934 | rule_locs, info->rule_cnt); | ||
935 | if (rc < 0) | ||
936 | return rc; | ||
937 | info->rule_cnt = rc; | ||
938 | return 0; | ||
939 | } | ||
940 | |||
865 | default: | 941 | default: |
866 | return -EOPNOTSUPP; | 942 | return -EOPNOTSUPP; |
867 | } | 943 | } |
868 | } | 944 | } |
869 | 945 | ||
870 | static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, | 946 | static int efx_ethtool_set_class_rule(struct efx_nic *efx, |
871 | struct ethtool_rx_ntuple *ntuple) | 947 | struct ethtool_rx_flow_spec *rule) |
872 | { | 948 | { |
873 | struct efx_nic *efx = netdev_priv(net_dev); | 949 | struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec; |
874 | struct ethtool_tcpip4_spec *ip_entry = &ntuple->fs.h_u.tcp_ip4_spec; | 950 | struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec; |
875 | struct ethtool_tcpip4_spec *ip_mask = &ntuple->fs.m_u.tcp_ip4_spec; | 951 | struct ethhdr *mac_entry = &rule->h_u.ether_spec; |
876 | struct ethhdr *mac_entry = &ntuple->fs.h_u.ether_spec; | 952 | struct ethhdr *mac_mask = &rule->m_u.ether_spec; |
877 | struct ethhdr *mac_mask = &ntuple->fs.m_u.ether_spec; | 953 | struct efx_filter_spec spec; |
878 | struct efx_filter_spec filter; | ||
879 | int rc; | 954 | int rc; |
880 | 955 | ||
881 | /* Range-check action */ | 956 | /* Check that user wants us to choose the location */ |
882 | if (ntuple->fs.action < ETHTOOL_RXNTUPLE_ACTION_CLEAR || | 957 | if (rule->location != RX_CLS_LOC_ANY && |
883 | ntuple->fs.action >= (s32)efx->n_rx_channels) | 958 | rule->location != RX_CLS_LOC_FIRST && |
959 | rule->location != RX_CLS_LOC_LAST) | ||
884 | return -EINVAL; | 960 | return -EINVAL; |
885 | 961 | ||
886 | if (~ntuple->fs.data_mask) | 962 | /* Range-check ring_cookie */ |
963 | if (rule->ring_cookie >= efx->n_rx_channels && | ||
964 | rule->ring_cookie != RX_CLS_FLOW_DISC) | ||
887 | return -EINVAL; | 965 | return -EINVAL; |
888 | 966 | ||
889 | efx_filter_init_rx(&filter, EFX_FILTER_PRI_MANUAL, 0, | 967 | /* Check for unsupported extensions */ |
890 | (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) ? | 968 | if ((rule->flow_type & FLOW_EXT) && |
891 | 0xfff : ntuple->fs.action); | 969 | (rule->m_ext.vlan_etype | rule->m_ext.data[0] | |
970 | rule->m_ext.data[1])) | ||
971 | return -EINVAL; | ||
972 | |||
973 | efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL, | ||
974 | (rule->location == RX_CLS_LOC_FIRST) ? | ||
975 | EFX_FILTER_FLAG_RX_OVERRIDE_IP : 0, | ||
976 | (rule->ring_cookie == RX_CLS_FLOW_DISC) ? | ||
977 | 0xfff : rule->ring_cookie); | ||
892 | 978 | ||
893 | switch (ntuple->fs.flow_type) { | 979 | switch (rule->flow_type) { |
894 | case TCP_V4_FLOW: | 980 | case TCP_V4_FLOW: |
895 | case UDP_V4_FLOW: { | 981 | case UDP_V4_FLOW: { |
896 | u8 proto = (ntuple->fs.flow_type == TCP_V4_FLOW ? | 982 | u8 proto = (rule->flow_type == TCP_V4_FLOW ? |
897 | IPPROTO_TCP : IPPROTO_UDP); | 983 | IPPROTO_TCP : IPPROTO_UDP); |
898 | 984 | ||
899 | /* Must match all of destination, */ | 985 | /* Must match all of destination, */ |
900 | if (ip_mask->ip4dst | ip_mask->pdst) | 986 | if ((__force u32)~ip_mask->ip4dst | |
987 | (__force u16)~ip_mask->pdst) | ||
901 | return -EINVAL; | 988 | return -EINVAL; |
902 | /* all or none of source, */ | 989 | /* all or none of source, */ |
903 | if ((ip_mask->ip4src | ip_mask->psrc) && | 990 | if ((ip_mask->ip4src | ip_mask->psrc) && |
@@ -905,17 +992,17 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, | |||
905 | (__force u16)~ip_mask->psrc)) | 992 | (__force u16)~ip_mask->psrc)) |
906 | return -EINVAL; | 993 | return -EINVAL; |
907 | /* and nothing else */ | 994 | /* and nothing else */ |
908 | if ((u8)~ip_mask->tos | (u16)~ntuple->fs.vlan_tag_mask) | 995 | if (ip_mask->tos | rule->m_ext.vlan_tci) |
909 | return -EINVAL; | 996 | return -EINVAL; |
910 | 997 | ||
911 | if (!ip_mask->ip4src) | 998 | if (ip_mask->ip4src) |
912 | rc = efx_filter_set_ipv4_full(&filter, proto, | 999 | rc = efx_filter_set_ipv4_full(&spec, proto, |
913 | ip_entry->ip4dst, | 1000 | ip_entry->ip4dst, |
914 | ip_entry->pdst, | 1001 | ip_entry->pdst, |
915 | ip_entry->ip4src, | 1002 | ip_entry->ip4src, |
916 | ip_entry->psrc); | 1003 | ip_entry->psrc); |
917 | else | 1004 | else |
918 | rc = efx_filter_set_ipv4_local(&filter, proto, | 1005 | rc = efx_filter_set_ipv4_local(&spec, proto, |
919 | ip_entry->ip4dst, | 1006 | ip_entry->ip4dst, |
920 | ip_entry->pdst); | 1007 | ip_entry->pdst); |
921 | if (rc) | 1008 | if (rc) |
@@ -923,23 +1010,24 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, | |||
923 | break; | 1010 | break; |
924 | } | 1011 | } |
925 | 1012 | ||
926 | case ETHER_FLOW: | 1013 | case ETHER_FLOW | FLOW_EXT: |
927 | /* Must match all of destination, */ | 1014 | /* Must match all or none of VID */ |
928 | if (!is_zero_ether_addr(mac_mask->h_dest)) | 1015 | if (rule->m_ext.vlan_tci != htons(0xfff) && |
1016 | rule->m_ext.vlan_tci != 0) | ||
929 | return -EINVAL; | 1017 | return -EINVAL; |
930 | /* all or none of VID, */ | 1018 | case ETHER_FLOW: |
931 | if (ntuple->fs.vlan_tag_mask != 0xf000 && | 1019 | /* Must match all of destination */ |
932 | ntuple->fs.vlan_tag_mask != 0xffff) | 1020 | if (!is_broadcast_ether_addr(mac_mask->h_dest)) |
933 | return -EINVAL; | 1021 | return -EINVAL; |
934 | /* and nothing else */ | 1022 | /* and nothing else */ |
935 | if (!is_broadcast_ether_addr(mac_mask->h_source) || | 1023 | if (!is_zero_ether_addr(mac_mask->h_source) || |
936 | mac_mask->h_proto != htons(0xffff)) | 1024 | mac_mask->h_proto) |
937 | return -EINVAL; | 1025 | return -EINVAL; |
938 | 1026 | ||
939 | rc = efx_filter_set_eth_local( | 1027 | rc = efx_filter_set_eth_local( |
940 | &filter, | 1028 | &spec, |
941 | (ntuple->fs.vlan_tag_mask == 0xf000) ? | 1029 | (rule->flow_type & FLOW_EXT && rule->m_ext.vlan_tci) ? |
942 | ntuple->fs.vlan_tag : EFX_FILTER_VID_UNSPEC, | 1030 | ntohs(rule->h_ext.vlan_tci) : EFX_FILTER_VID_UNSPEC, |
943 | mac_entry->h_dest); | 1031 | mac_entry->h_dest); |
944 | if (rc) | 1032 | if (rc) |
945 | return rc; | 1033 | return rc; |
@@ -949,11 +1037,33 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, | |||
949 | return -EINVAL; | 1037 | return -EINVAL; |
950 | } | 1038 | } |
951 | 1039 | ||
952 | if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_CLEAR) | 1040 | rc = efx_filter_insert_filter(efx, &spec, true); |
953 | return efx_filter_remove_filter(efx, &filter); | 1041 | if (rc < 0) |
1042 | return rc; | ||
954 | 1043 | ||
955 | rc = efx_filter_insert_filter(efx, &filter, true); | 1044 | rule->location = rc; |
956 | return rc < 0 ? rc : 0; | 1045 | return 0; |
1046 | } | ||
1047 | |||
1048 | static int efx_ethtool_set_rxnfc(struct net_device *net_dev, | ||
1049 | struct ethtool_rxnfc *info) | ||
1050 | { | ||
1051 | struct efx_nic *efx = netdev_priv(net_dev); | ||
1052 | |||
1053 | if (efx_filter_get_rx_id_limit(efx) == 0) | ||
1054 | return -EOPNOTSUPP; | ||
1055 | |||
1056 | switch (info->cmd) { | ||
1057 | case ETHTOOL_SRXCLSRLINS: | ||
1058 | return efx_ethtool_set_class_rule(efx, &info->fs); | ||
1059 | |||
1060 | case ETHTOOL_SRXCLSRLDEL: | ||
1061 | return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL, | ||
1062 | info->fs.location); | ||
1063 | |||
1064 | default: | ||
1065 | return -EOPNOTSUPP; | ||
1066 | } | ||
957 | } | 1067 | } |
958 | 1068 | ||
959 | static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev) | 1069 | static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev) |
@@ -1007,7 +1117,7 @@ const struct ethtool_ops efx_ethtool_ops = { | |||
1007 | .set_wol = efx_ethtool_set_wol, | 1117 | .set_wol = efx_ethtool_set_wol, |
1008 | .reset = efx_ethtool_reset, | 1118 | .reset = efx_ethtool_reset, |
1009 | .get_rxnfc = efx_ethtool_get_rxnfc, | 1119 | .get_rxnfc = efx_ethtool_get_rxnfc, |
1010 | .set_rx_ntuple = efx_ethtool_set_rx_ntuple, | 1120 | .set_rxnfc = efx_ethtool_set_rxnfc, |
1011 | .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size, | 1121 | .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size, |
1012 | .get_rxfh_indir = efx_ethtool_get_rxfh_indir, | 1122 | .get_rxfh_indir = efx_ethtool_get_rxfh_indir, |
1013 | .set_rxfh_indir = efx_ethtool_set_rxfh_indir, | 1123 | .set_rxfh_indir = efx_ethtool_set_rxfh_indir, |