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

Commit b2bb7b77 authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller
Browse files

sfc: Implement ethtool RX NFC rules API instead of n-tuple API

parent 1a6281ac
Loading
Loading
Loading
Loading
+149 −39
Original line number Diff line number Diff line
@@ -818,9 +818,58 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
	return efx_reset(efx, rc);
}

static int efx_ethtool_get_class_rule(struct efx_nic *efx,
				      struct ethtool_rx_flow_spec *rule)
{
	struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
	struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
	struct efx_filter_spec spec;
	u16 vid;
	u8 proto;
	int rc;

	rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL,
					rule->location, &spec);
	if (rc)
		return rc;

	if (spec.dmaq_id == 0xfff)
		rule->ring_cookie = RX_CLS_FLOW_DISC;
	else
		rule->ring_cookie = spec.dmaq_id;

	rc = efx_filter_get_eth_local(&spec, &vid,
				      rule->h_u.ether_spec.h_dest);
	if (rc == 0) {
		rule->flow_type = ETHER_FLOW;
		memset(rule->m_u.ether_spec.h_dest, ~0, ETH_ALEN);
		if (vid != EFX_FILTER_VID_UNSPEC) {
			rule->flow_type |= FLOW_EXT;
			rule->h_ext.vlan_tci = htons(vid);
			rule->m_ext.vlan_tci = htons(0xfff);
		}
		return 0;
	}

	rc = efx_filter_get_ipv4_local(&spec, &proto,
				       &ip_entry->ip4dst, &ip_entry->pdst);
	if (rc != 0) {
		rc = efx_filter_get_ipv4_full(
			&spec, &proto, &ip_entry->ip4src, &ip_entry->psrc,
			&ip_entry->ip4dst, &ip_entry->pdst);
		EFX_WARN_ON_PARANOID(rc);
		ip_mask->ip4src = ~0;
		ip_mask->psrc = ~0;
	}
	rule->flow_type = (proto == IPPROTO_TCP) ? TCP_V4_FLOW : UDP_V4_FLOW;
	ip_mask->ip4dst = ~0;
	ip_mask->pdst = ~0;
	return rc;
}

static int
efx_ethtool_get_rxnfc(struct net_device *net_dev,
		      struct ethtool_rxnfc *info, u32 *rules __always_unused)
		      struct ethtool_rxnfc *info, u32 *rule_locs)
{
	struct efx_nic *efx = netdev_priv(net_dev);

@@ -862,42 +911,80 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
		return 0;
	}

	case ETHTOOL_GRXCLSRLCNT:
		info->data = efx_filter_get_rx_id_limit(efx);
		if (info->data == 0)
			return -EOPNOTSUPP;
		info->data |= RX_CLS_LOC_SPECIAL;
		info->rule_cnt =
			efx_filter_count_rx_used(efx, EFX_FILTER_PRI_MANUAL);
		return 0;

	case ETHTOOL_GRXCLSRULE:
		if (efx_filter_get_rx_id_limit(efx) == 0)
			return -EOPNOTSUPP;
		return efx_ethtool_get_class_rule(efx, &info->fs);

	case ETHTOOL_GRXCLSRLALL: {
		s32 rc;
		info->data = efx_filter_get_rx_id_limit(efx);
		if (info->data == 0)
			return -EOPNOTSUPP;
		rc = efx_filter_get_rx_ids(efx, EFX_FILTER_PRI_MANUAL,
					   rule_locs, info->rule_cnt);
		if (rc < 0)
			return rc;
		info->rule_cnt = rc;
		return 0;
	}

	default:
		return -EOPNOTSUPP;
	}
}

static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
				     struct ethtool_rx_ntuple *ntuple)
static int efx_ethtool_set_class_rule(struct efx_nic *efx,
				      struct ethtool_rx_flow_spec *rule)
{
	struct efx_nic *efx = netdev_priv(net_dev);
	struct ethtool_tcpip4_spec *ip_entry = &ntuple->fs.h_u.tcp_ip4_spec;
	struct ethtool_tcpip4_spec *ip_mask = &ntuple->fs.m_u.tcp_ip4_spec;
	struct ethhdr *mac_entry = &ntuple->fs.h_u.ether_spec;
	struct ethhdr *mac_mask = &ntuple->fs.m_u.ether_spec;
	struct efx_filter_spec filter;
	struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
	struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
	struct ethhdr *mac_entry = &rule->h_u.ether_spec;
	struct ethhdr *mac_mask = &rule->m_u.ether_spec;
	struct efx_filter_spec spec;
	int rc;

	/* Range-check action */
	if (ntuple->fs.action < ETHTOOL_RXNTUPLE_ACTION_CLEAR ||
	    ntuple->fs.action >= (s32)efx->n_rx_channels)
	/* Check that user wants us to choose the location */
	if (rule->location != RX_CLS_LOC_ANY &&
	    rule->location != RX_CLS_LOC_FIRST &&
	    rule->location != RX_CLS_LOC_LAST)
		return -EINVAL;

	if (~ntuple->fs.data_mask)
	/* Range-check ring_cookie */
	if (rule->ring_cookie >= efx->n_rx_channels &&
	    rule->ring_cookie != RX_CLS_FLOW_DISC)
		return -EINVAL;

	efx_filter_init_rx(&filter, EFX_FILTER_PRI_MANUAL, 0,
			   (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) ?
			   0xfff : ntuple->fs.action);
	/* Check for unsupported extensions */
	if ((rule->flow_type & FLOW_EXT) &&
	    (rule->m_ext.vlan_etype | rule->m_ext.data[0] |
	     rule->m_ext.data[1]))
		return -EINVAL;

	efx_filter_init_rx(&spec, EFX_FILTER_PRI_MANUAL,
			   (rule->location == RX_CLS_LOC_FIRST) ?
			   EFX_FILTER_FLAG_RX_OVERRIDE_IP : 0,
			   (rule->ring_cookie == RX_CLS_FLOW_DISC) ?
			   0xfff : rule->ring_cookie);

	switch (ntuple->fs.flow_type) {
	switch (rule->flow_type) {
	case TCP_V4_FLOW:
	case UDP_V4_FLOW: {
		u8 proto = (ntuple->fs.flow_type == TCP_V4_FLOW ?
		u8 proto = (rule->flow_type == TCP_V4_FLOW ?
			    IPPROTO_TCP : IPPROTO_UDP);

		/* Must match all of destination, */
		if (ip_mask->ip4dst | ip_mask->pdst)
		if ((__force u32)~ip_mask->ip4dst |
		    (__force u16)~ip_mask->pdst)
			return -EINVAL;
		/* all or none of source, */
		if ((ip_mask->ip4src | ip_mask->psrc) &&
@@ -905,17 +992,17 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
		     (__force u16)~ip_mask->psrc))
			return -EINVAL;
		/* and nothing else */
		if ((u8)~ip_mask->tos | (u16)~ntuple->fs.vlan_tag_mask)
		if (ip_mask->tos | rule->m_ext.vlan_tci)
			return -EINVAL;

		if (!ip_mask->ip4src)
			rc = efx_filter_set_ipv4_full(&filter, proto,
		if (ip_mask->ip4src)
			rc = efx_filter_set_ipv4_full(&spec, proto,
						      ip_entry->ip4dst,
						      ip_entry->pdst,
						      ip_entry->ip4src,
						      ip_entry->psrc);
		else
			rc = efx_filter_set_ipv4_local(&filter, proto,
			rc = efx_filter_set_ipv4_local(&spec, proto,
						       ip_entry->ip4dst,
						       ip_entry->pdst);
		if (rc)
@@ -923,23 +1010,24 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
		break;
	}

	case ETHER_FLOW:
		/* Must match all of destination, */
		if (!is_zero_ether_addr(mac_mask->h_dest))
	case ETHER_FLOW | FLOW_EXT:
		/* Must match all or none of VID */
		if (rule->m_ext.vlan_tci != htons(0xfff) &&
		    rule->m_ext.vlan_tci != 0)
			return -EINVAL;
		/* all or none of VID, */
		if (ntuple->fs.vlan_tag_mask != 0xf000 &&
		    ntuple->fs.vlan_tag_mask != 0xffff)
	case ETHER_FLOW:
		/* Must match all of destination */
		if (!is_broadcast_ether_addr(mac_mask->h_dest))
			return -EINVAL;
		/* and nothing else */
		if (!is_broadcast_ether_addr(mac_mask->h_source) ||
		    mac_mask->h_proto != htons(0xffff))
		if (!is_zero_ether_addr(mac_mask->h_source) ||
		    mac_mask->h_proto)
			return -EINVAL;

		rc = efx_filter_set_eth_local(
			&filter,
			(ntuple->fs.vlan_tag_mask == 0xf000) ?
			ntuple->fs.vlan_tag : EFX_FILTER_VID_UNSPEC,
			&spec,
			(rule->flow_type & FLOW_EXT && rule->m_ext.vlan_tci) ?
			ntohs(rule->h_ext.vlan_tci) : EFX_FILTER_VID_UNSPEC,
			mac_entry->h_dest);
		if (rc)
			return rc;
@@ -949,11 +1037,33 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
		return -EINVAL;
	}

	if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_CLEAR)
		return efx_filter_remove_filter(efx, &filter);
	rc = efx_filter_insert_filter(efx, &spec, true);
	if (rc < 0)
		return rc;

	rule->location = rc;
	return 0;
}

static int efx_ethtool_set_rxnfc(struct net_device *net_dev,
				 struct ethtool_rxnfc *info)
{
	struct efx_nic *efx = netdev_priv(net_dev);

	if (efx_filter_get_rx_id_limit(efx) == 0)
		return -EOPNOTSUPP;

	switch (info->cmd) {
	case ETHTOOL_SRXCLSRLINS:
		return efx_ethtool_set_class_rule(efx, &info->fs);

	case ETHTOOL_SRXCLSRLDEL:
		return efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_MANUAL,
						 info->fs.location);

	rc = efx_filter_insert_filter(efx, &filter, true);
	return rc < 0 ? rc : 0;
	default:
		return -EOPNOTSUPP;
	}
}

static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
@@ -1007,7 +1117,7 @@ const struct ethtool_ops efx_ethtool_ops = {
	.set_wol                = efx_ethtool_set_wol,
	.reset			= efx_ethtool_reset,
	.get_rxnfc		= efx_ethtool_get_rxnfc,
	.set_rx_ntuple		= efx_ethtool_set_rx_ntuple,
	.set_rxnfc		= efx_ethtool_set_rxnfc,
	.get_rxfh_indir_size	= efx_ethtool_get_rxfh_indir_size,
	.get_rxfh_indir		= efx_ethtool_get_rxfh_indir,
	.set_rxfh_indir		= efx_ethtool_set_rxfh_indir,