Commit c47b2d9d authored by Ben Hutchings's avatar Ben Hutchings

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
...@@ -12,6 +12,7 @@ ...@@ -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, ...@@ -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))
EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
ip = (const struct iphdr *)(skb->data + nhoff);
if (ip_is_fragment(ip))
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4);
ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT, efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT,
efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0, efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0,
rxq_index); rxq_index);
rc = efx_filter_set_ipv4_full(&spec, ip->protocol, spec.match_flags =
ip->daddr, ports[1], ip->saddr, ports[0]); EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
if (rc) EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
return rc; 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));
if (ip_is_fragment(ip))
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);
ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
} else {
const struct ipv6hdr *ip6 =
(const struct ipv6hdr *)(skb->data + nhoff);
EFX_BUG_ON_PARANOID(skb_headlen(skb) <
nhoff + sizeof(*ip6) + 4);
spec.ip_proto = ip6->nexthdr;
memcpy(spec.rem_host, &ip6->saddr, sizeof(ip6->saddr));
memcpy(spec.loc_host, &ip6->daddr, sizeof(ip6->daddr));
ports = (const __be16 *)(ip6 + 1);
}
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, ...@@ -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;
netif_info(efx, rx_status, efx->net_dev, if (ether_type == htons(ETH_P_IP))
"steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n", netif_info(efx, rx_status, efx->net_dev,
(ip->protocol == IPPROTO_TCP) ? "TCP" : "UDP", "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n",
&ip->saddr, ntohs(ports[0]), &ip->daddr, ntohs(ports[1]), (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP",
rxq_index, flow_id, rc); spec.rem_host, ntohs(ports[0]), spec.loc_host,
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;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment