diff options
Diffstat (limited to 'drivers/net/ethernet/sfc/rx.c')
| -rw-r--r-- | drivers/net/ethernet/sfc/rx.c | 102 | 
1 files changed, 41 insertions, 61 deletions
| diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 8956995b2fe7..02b0b5272c14 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -842,33 +842,18 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,  	struct efx_nic *efx = netdev_priv(net_dev);  	struct efx_channel *channel;  	struct efx_filter_spec spec; -	const __be16 *ports; -	__be16 ether_type; -	int nhoff; +	struct flow_keys fk;  	int rc; -	/* 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)) { -		const struct vlan_hdr *vh = -			(const struct vlan_hdr *)skb->data; +	if (flow_id == RPS_FLOW_ID_INVALID) +		return -EINVAL; -		/* We can't filter on the IP 5-tuple and the vlan -		 * together, so just strip the vlan header and filter -		 * on the IP part. -		 */ -		EFX_BUG_ON_PARANOID(skb_headlen(skb) < sizeof(*vh)); -		ether_type = vh->h_vlan_encapsulated_proto; -		nhoff = sizeof(struct vlan_hdr); -	} else { -		ether_type = skb->protocol; -		nhoff = 0; -	} +	if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) +		return -EPROTONOSUPPORT; -	if (ether_type != htons(ETH_P_IP) && ether_type != htons(ETH_P_IPV6)) +	if (fk.basic.n_proto != htons(ETH_P_IP) && fk.basic.n_proto != htons(ETH_P_IPV6)) +		return -EPROTONOSUPPORT; +	if (fk.control.flags & FLOW_DIS_IS_FRAGMENT)  		return -EPROTONOSUPPORT;  	efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT, @@ -878,56 +863,41 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,  		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)); -		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); +	spec.ether_type = fk.basic.n_proto; +	spec.ip_proto = fk.basic.ip_proto; + +	if (fk.basic.n_proto == htons(ETH_P_IP)) { +		spec.rem_host[0] = fk.addrs.v4addrs.src; +		spec.loc_host[0] = fk.addrs.v4addrs.dst;  	} 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); +		memcpy(spec.rem_host, &fk.addrs.v6addrs.src, sizeof(struct in6_addr)); +		memcpy(spec.loc_host, &fk.addrs.v6addrs.dst, sizeof(struct in6_addr));  	} -	spec.rem_port = ports[0]; -	spec.loc_port = ports[1]; +	spec.rem_port = fk.ports.src; +	spec.loc_port = fk.ports.dst;  	rc = efx->type->filter_rfs_insert(efx, &spec);  	if (rc < 0)  		return rc;  	/* Remember this so we can check whether to expire the filter later */ -	efx->rps_flow_id[rc] = flow_id; -	channel = efx_get_channel(efx, skb_get_rx_queue(skb)); +	channel = efx_get_channel(efx, rxq_index); +	channel->rps_flow_id[rc] = flow_id;  	++channel->rfs_filters_added; -	if (ether_type == htons(ETH_P_IP)) +	if (spec.ether_type == htons(ETH_P_IP))  		netif_info(efx, rx_status, efx->net_dev,  			   "steering %s %pI4:%u:%pI4:%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); +			   spec.rem_host, ntohs(spec.rem_port), spec.loc_host, +			   ntohs(spec.loc_port), 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); +			   spec.rem_host, ntohs(spec.rem_port), spec.loc_host, +			   ntohs(spec.loc_port), rxq_index, flow_id, rc);  	return rc;  } @@ -935,24 +905,34 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,  bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned int quota)  {  	bool (*expire_one)(struct efx_nic *efx, u32 flow_id, unsigned int index); -	unsigned int index, size; +	unsigned int channel_idx, index, size;  	u32 flow_id;  	if (!spin_trylock_bh(&efx->filter_lock))  		return false;  	expire_one = efx->type->filter_rfs_expire_one; +	channel_idx = efx->rps_expire_channel;  	index = efx->rps_expire_index;  	size = efx->type->max_rx_ip_filters;  	while (quota--) { -		flow_id = efx->rps_flow_id[index]; -		if (expire_one(efx, flow_id, index)) +		struct efx_channel *channel = efx_get_channel(efx, channel_idx); +		flow_id = channel->rps_flow_id[index]; + +		if (flow_id != RPS_FLOW_ID_INVALID && +		    expire_one(efx, flow_id, index)) {  			netif_info(efx, rx_status, efx->net_dev, -				   "expired filter %d [flow %u]\n", -				   index, flow_id); -		if (++index == size) +				   "expired filter %d [queue %u flow %u]\n", +				   index, channel_idx, flow_id); +			channel->rps_flow_id[index] = RPS_FLOW_ID_INVALID; +		} +		if (++index == size) { +			if (++channel_idx == efx->n_channels) +				channel_idx = 0;  			index = 0; +		}  	} +	efx->rps_expire_channel = channel_idx;  	efx->rps_expire_index = index;  	spin_unlock_bh(&efx->filter_lock); |