aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/sfc
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2013-09-03 12:22:23 -0400
committerBen Hutchings <bhutchings@solarflare.com>2013-09-20 14:32:00 -0400
commitc47b2d9d56832e7ff1a20bd598623de42701a3a3 (patch)
treee2efb45c2cc51557d4970bf90f3367a88adfb6ed /drivers/net/ethernet/sfc
parentee45fd92c739db5b7950163d91dfe5f016af6d24 (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>
Diffstat (limited to 'drivers/net/ethernet/sfc')
-rw-r--r--drivers/net/ethernet/sfc/rx.c90
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}