Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c47b2d9d authored by Ben Hutchings's avatar Ben Hutchings
Browse files

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: default avatarBen Hutchings <bhutchings@solarflare.com>
parent ee45fd92
Loading
Loading
Loading
Loading
+62 −28
Original line number Original line Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/in.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/ip.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/udp.h>
#include <linux/prefetch.h>
#include <linux/prefetch.h>
@@ -818,44 +819,70 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
	struct efx_nic *efx = netdev_priv(net_dev);
	struct efx_nic *efx = netdev_priv(net_dev);
	struct efx_channel *channel;
	struct efx_channel *channel;
	struct efx_filter_spec spec;
	struct efx_filter_spec spec;
	const struct iphdr *ip;
	const __be16 *ports;
	const __be16 *ports;
	__be16 ether_type;
	int nhoff;
	int nhoff;
	int rc;
	int rc;


	nhoff = skb_network_offset(skb);
	/* The core RPS/RFS code has already parsed and validated
	 * VLAN, IP and transport headers.  We assume they are in the
	 * header area.
	 */


	if (skb->protocol == htons(ETH_P_8021Q)) {
	if (skb->protocol == htons(ETH_P_8021Q)) {
		EFX_BUG_ON_PARANOID(skb_headlen(skb) <
		const struct vlan_hdr *vh =
				    nhoff + sizeof(struct vlan_hdr));
			(const struct vlan_hdr *)skb->data;
		if (((const struct vlan_hdr *)skb->data + nhoff)->
		    h_vlan_encapsulated_proto != htons(ETH_P_IP))
			return -EPROTONOSUPPORT;


		/* This is IP over 802.1q VLAN.  We can't filter on the
		/* We can't filter on the IP 5-tuple and the vlan
		 * IP 5-tuple and the vlan together, so just strip the
		 * together, so just strip the vlan header and filter
		 * vlan header and filter on the IP part.
		 * on the IP part.
		 */
		 */
		nhoff += sizeof(struct vlan_hdr);
		EFX_BUG_ON_PARANOID(skb_headlen(skb) < sizeof(*vh));
	} else if (skb->protocol != htons(ETH_P_IP)) {
		ether_type = vh->h_vlan_encapsulated_proto;
		return -EPROTONOSUPPORT;
		nhoff = sizeof(struct vlan_hdr);
	} else {
		ether_type = skb->protocol;
		nhoff = 0;
	}
	}


	/* RFS must validate the IP header length before calling us */
	if (ether_type != htons(ETH_P_IP) && ether_type != htons(ETH_P_IPV6))
		return -EPROTONOSUPPORT;

	efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT,
			   efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
			   rxq_index);
	spec.match_flags =
		EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
		EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
		EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
	spec.ether_type = ether_type;

	if (ether_type == htons(ETH_P_IP)) {
		const struct iphdr *ip =
			(const struct iphdr *)(skb->data + nhoff);

		EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
		EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
	ip = (const struct iphdr *)(skb->data + nhoff);
		if (ip_is_fragment(ip))
		if (ip_is_fragment(ip))
			return -EPROTONOSUPPORT;
			return -EPROTONOSUPPORT;
		spec.ip_proto = ip->protocol;
		spec.rem_host[0] = ip->saddr;
		spec.loc_host[0] = ip->daddr;
		EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4);
		EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4);
		ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
		ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
	} else {
		const struct ipv6hdr *ip6 =
			(const struct ipv6hdr *)(skb->data + nhoff);


	efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT,
		EFX_BUG_ON_PARANOID(skb_headlen(skb) <
			   efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
				    nhoff + sizeof(*ip6) + 4);
			   rxq_index);
		spec.ip_proto = ip6->nexthdr;
	rc = efx_filter_set_ipv4_full(&spec, ip->protocol,
		memcpy(spec.rem_host, &ip6->saddr, sizeof(ip6->saddr));
				      ip->daddr, ports[1], ip->saddr, ports[0]);
		memcpy(spec.loc_host, &ip6->daddr, sizeof(ip6->daddr));
	if (rc)
		ports = (const __be16 *)(ip6 + 1);
		return rc;
	}

	spec.rem_port = ports[0];
	spec.loc_port = ports[1];


	rc = efx->type->filter_rfs_insert(efx, &spec);
	rc = efx->type->filter_rfs_insert(efx, &spec);
	if (rc < 0)
	if (rc < 0)
@@ -866,11 +893,18 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
	channel = efx_get_channel(efx, skb_get_rx_queue(skb));
	channel = efx_get_channel(efx, skb_get_rx_queue(skb));
	++channel->rfs_filters_added;
	++channel->rfs_filters_added;


	if (ether_type == htons(ETH_P_IP))
		netif_info(efx, rx_status, efx->net_dev,
		netif_info(efx, rx_status, efx->net_dev,
			   "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n",
			   "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n",
		   (ip->protocol == IPPROTO_TCP) ? "TCP" : "UDP",
			   (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
		   &ip->saddr, ntohs(ports[0]), &ip->daddr, ntohs(ports[1]),
			   spec.rem_host, ntohs(ports[0]), spec.loc_host,
		   rxq_index, flow_id, rc);
			   ntohs(ports[1]), rxq_index, flow_id, rc);
	else
		netif_info(efx, rx_status, efx->net_dev,
			   "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d]\n",
			   (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
			   spec.rem_host, ntohs(ports[0]), spec.loc_host,
			   ntohs(ports[1]), rxq_index, flow_id, rc);


	return rc;
	return rc;
}
}