diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2013-09-03 12:22:23 -0400 |
---|---|---|
committer | Ben Hutchings <bhutchings@solarflare.com> | 2013-09-20 14:32:00 -0400 |
commit | c47b2d9d56832e7ff1a20bd598623de42701a3a3 (patch) | |
tree | e2efb45c2cc51557d4970bf90f3367a88adfb6ed | |
parent | ee45fd92c739db5b7950163d91dfe5f016af6d24 (diff) |
sfc: Support ARFS for IPv6 flows
Extend efx_filter_rfs() to map TCP/IPv6 and UDP/IPv6 flows into
efx_filter_spec. These are only supported on EF10; on Falcon and
Siena they will be rejected by efx_farch_filter_from_gen_spec().
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
-rw-r--r-- | drivers/net/ethernet/sfc/rx.c | 90 |
1 files changed, 62 insertions, 28 deletions
diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 4a596725023f..8f09e686fc23 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/in.h> | 12 | #include <linux/in.h> |
13 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
14 | #include <linux/ip.h> | 14 | #include <linux/ip.h> |
15 | #include <linux/ipv6.h> | ||
15 | #include <linux/tcp.h> | 16 | #include <linux/tcp.h> |
16 | #include <linux/udp.h> | 17 | #include <linux/udp.h> |
17 | #include <linux/prefetch.h> | 18 | #include <linux/prefetch.h> |
@@ -818,44 +819,70 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, | |||
818 | struct efx_nic *efx = netdev_priv(net_dev); | 819 | struct efx_nic *efx = netdev_priv(net_dev); |
819 | struct efx_channel *channel; | 820 | struct efx_channel *channel; |
820 | struct efx_filter_spec spec; | 821 | struct efx_filter_spec spec; |
821 | const struct iphdr *ip; | ||
822 | const __be16 *ports; | 822 | const __be16 *ports; |
823 | __be16 ether_type; | ||
823 | int nhoff; | 824 | int nhoff; |
824 | int rc; | 825 | int rc; |
825 | 826 | ||
826 | nhoff = skb_network_offset(skb); | 827 | /* The core RPS/RFS code has already parsed and validated |
828 | * VLAN, IP and transport headers. We assume they are in the | ||
829 | * header area. | ||
830 | */ | ||
827 | 831 | ||
828 | if (skb->protocol == htons(ETH_P_8021Q)) { | 832 | if (skb->protocol == htons(ETH_P_8021Q)) { |
829 | EFX_BUG_ON_PARANOID(skb_headlen(skb) < | 833 | const struct vlan_hdr *vh = |
830 | nhoff + sizeof(struct vlan_hdr)); | 834 | (const struct vlan_hdr *)skb->data; |
831 | if (((const struct vlan_hdr *)skb->data + nhoff)-> | ||
832 | h_vlan_encapsulated_proto != htons(ETH_P_IP)) | ||
833 | return -EPROTONOSUPPORT; | ||
834 | 835 | ||
835 | /* This is IP over 802.1q VLAN. We can't filter on the | 836 | /* We can't filter on the IP 5-tuple and the vlan |
836 | * IP 5-tuple and the vlan together, so just strip the | 837 | * together, so just strip the vlan header and filter |
837 | * vlan header and filter on the IP part. | 838 | * on the IP part. |
838 | */ | 839 | */ |
839 | nhoff += sizeof(struct vlan_hdr); | 840 | EFX_BUG_ON_PARANOID(skb_headlen(skb) < sizeof(*vh)); |
840 | } else if (skb->protocol != htons(ETH_P_IP)) { | 841 | ether_type = vh->h_vlan_encapsulated_proto; |
841 | return -EPROTONOSUPPORT; | 842 | nhoff = sizeof(struct vlan_hdr); |
843 | } else { | ||
844 | ether_type = skb->protocol; | ||
845 | nhoff = 0; | ||
842 | } | 846 | } |
843 | 847 | ||
844 | /* RFS must validate the IP header length before calling us */ | 848 | if (ether_type != htons(ETH_P_IP) && ether_type != htons(ETH_P_IPV6)) |
845 | EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip)); | ||
846 | ip = (const struct iphdr *)(skb->data + nhoff); | ||
847 | if (ip_is_fragment(ip)) | ||
848 | return -EPROTONOSUPPORT; | 849 | return -EPROTONOSUPPORT; |
849 | EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4); | ||
850 | ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl); | ||
851 | 850 | ||
852 | efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT, | 851 | efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT, |
853 | efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0, | 852 | efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0, |
854 | rxq_index); | 853 | rxq_index); |
855 | rc = efx_filter_set_ipv4_full(&spec, ip->protocol, | 854 | spec.match_flags = |
856 | ip->daddr, ports[1], ip->saddr, ports[0]); | 855 | EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | |
857 | if (rc) | 856 | EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT | |
858 | return rc; | 857 | EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT; |
858 | spec.ether_type = ether_type; | ||
859 | |||
860 | if (ether_type == htons(ETH_P_IP)) { | ||
861 | const struct iphdr *ip = | ||
862 | (const struct iphdr *)(skb->data + nhoff); | ||
863 | |||
864 | EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip)); | ||
865 | if (ip_is_fragment(ip)) | ||
866 | return -EPROTONOSUPPORT; | ||
867 | spec.ip_proto = ip->protocol; | ||
868 | spec.rem_host[0] = ip->saddr; | ||
869 | spec.loc_host[0] = ip->daddr; | ||
870 | EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4); | ||
871 | ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl); | ||
872 | } else { | ||
873 | const struct ipv6hdr *ip6 = | ||
874 | (const struct ipv6hdr *)(skb->data + nhoff); | ||
875 | |||
876 | EFX_BUG_ON_PARANOID(skb_headlen(skb) < | ||
877 | nhoff + sizeof(*ip6) + 4); | ||
878 | spec.ip_proto = ip6->nexthdr; | ||
879 | memcpy(spec.rem_host, &ip6->saddr, sizeof(ip6->saddr)); | ||
880 | memcpy(spec.loc_host, &ip6->daddr, sizeof(ip6->daddr)); | ||
881 | ports = (const __be16 *)(ip6 + 1); | ||
882 | } | ||
883 | |||
884 | spec.rem_port = ports[0]; | ||
885 | spec.loc_port = ports[1]; | ||
859 | 886 | ||
860 | rc = efx->type->filter_rfs_insert(efx, &spec); | 887 | rc = efx->type->filter_rfs_insert(efx, &spec); |
861 | if (rc < 0) | 888 | if (rc < 0) |
@@ -866,11 +893,18 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, | |||
866 | channel = efx_get_channel(efx, skb_get_rx_queue(skb)); | 893 | channel = efx_get_channel(efx, skb_get_rx_queue(skb)); |
867 | ++channel->rfs_filters_added; | 894 | ++channel->rfs_filters_added; |
868 | 895 | ||
869 | netif_info(efx, rx_status, efx->net_dev, | 896 | if (ether_type == htons(ETH_P_IP)) |
870 | "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n", | 897 | netif_info(efx, rx_status, efx->net_dev, |
871 | (ip->protocol == IPPROTO_TCP) ? "TCP" : "UDP", | 898 | "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n", |
872 | &ip->saddr, ntohs(ports[0]), &ip->daddr, ntohs(ports[1]), | 899 | (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", |
873 | rxq_index, flow_id, rc); | 900 | spec.rem_host, ntohs(ports[0]), spec.loc_host, |
901 | ntohs(ports[1]), rxq_index, flow_id, rc); | ||
902 | else | ||
903 | netif_info(efx, rx_status, efx->net_dev, | ||
904 | "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d]\n", | ||
905 | (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", | ||
906 | spec.rem_host, ntohs(ports[0]), spec.loc_host, | ||
907 | ntohs(ports[1]), rxq_index, flow_id, rc); | ||
874 | 908 | ||
875 | return rc; | 909 | return rc; |
876 | } | 910 | } |