sfc: Implement ethtool RX NFC rules API instead of n-tuple API
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1a6281ac5c
commit
b2bb7b776a
|
@ -818,9 +818,58 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
|
||||||
return efx_reset(efx, rc);
|
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
|
static int
|
||||||
efx_ethtool_get_rxnfc(struct net_device *net_dev,
|
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);
|
struct efx_nic *efx = netdev_priv(net_dev);
|
||||||
|
|
||||||
|
@ -862,42 +911,80 @@ efx_ethtool_get_rxnfc(struct net_device *net_dev,
|
||||||
return 0;
|
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:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
|
static int efx_ethtool_set_class_rule(struct efx_nic *efx,
|
||||||
struct ethtool_rx_ntuple *ntuple)
|
struct ethtool_rx_flow_spec *rule)
|
||||||
{
|
{
|
||||||
struct efx_nic *efx = netdev_priv(net_dev);
|
struct ethtool_tcpip4_spec *ip_entry = &rule->h_u.tcp_ip4_spec;
|
||||||
struct ethtool_tcpip4_spec *ip_entry = &ntuple->fs.h_u.tcp_ip4_spec;
|
struct ethtool_tcpip4_spec *ip_mask = &rule->m_u.tcp_ip4_spec;
|
||||||
struct ethtool_tcpip4_spec *ip_mask = &ntuple->fs.m_u.tcp_ip4_spec;
|
struct ethhdr *mac_entry = &rule->h_u.ether_spec;
|
||||||
struct ethhdr *mac_entry = &ntuple->fs.h_u.ether_spec;
|
struct ethhdr *mac_mask = &rule->m_u.ether_spec;
|
||||||
struct ethhdr *mac_mask = &ntuple->fs.m_u.ether_spec;
|
struct efx_filter_spec spec;
|
||||||
struct efx_filter_spec filter;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Range-check action */
|
/* Check that user wants us to choose the location */
|
||||||
if (ntuple->fs.action < ETHTOOL_RXNTUPLE_ACTION_CLEAR ||
|
if (rule->location != RX_CLS_LOC_ANY &&
|
||||||
ntuple->fs.action >= (s32)efx->n_rx_channels)
|
rule->location != RX_CLS_LOC_FIRST &&
|
||||||
|
rule->location != RX_CLS_LOC_LAST)
|
||||||
return -EINVAL;
|
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;
|
return -EINVAL;
|
||||||
|
|
||||||
efx_filter_init_rx(&filter, EFX_FILTER_PRI_MANUAL, 0,
|
/* Check for unsupported extensions */
|
||||||
(ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) ?
|
if ((rule->flow_type & FLOW_EXT) &&
|
||||||
0xfff : ntuple->fs.action);
|
(rule->m_ext.vlan_etype | rule->m_ext.data[0] |
|
||||||
|
rule->m_ext.data[1]))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
switch (ntuple->fs.flow_type) {
|
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 (rule->flow_type) {
|
||||||
case TCP_V4_FLOW:
|
case TCP_V4_FLOW:
|
||||||
case UDP_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);
|
IPPROTO_TCP : IPPROTO_UDP);
|
||||||
|
|
||||||
/* Must match all of destination, */
|
/* 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;
|
return -EINVAL;
|
||||||
/* all or none of source, */
|
/* all or none of source, */
|
||||||
if ((ip_mask->ip4src | ip_mask->psrc) &&
|
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))
|
(__force u16)~ip_mask->psrc))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
/* and nothing else */
|
/* 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;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!ip_mask->ip4src)
|
if (ip_mask->ip4src)
|
||||||
rc = efx_filter_set_ipv4_full(&filter, proto,
|
rc = efx_filter_set_ipv4_full(&spec, proto,
|
||||||
ip_entry->ip4dst,
|
ip_entry->ip4dst,
|
||||||
ip_entry->pdst,
|
ip_entry->pdst,
|
||||||
ip_entry->ip4src,
|
ip_entry->ip4src,
|
||||||
ip_entry->psrc);
|
ip_entry->psrc);
|
||||||
else
|
else
|
||||||
rc = efx_filter_set_ipv4_local(&filter, proto,
|
rc = efx_filter_set_ipv4_local(&spec, proto,
|
||||||
ip_entry->ip4dst,
|
ip_entry->ip4dst,
|
||||||
ip_entry->pdst);
|
ip_entry->pdst);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -923,23 +1010,24 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ETHER_FLOW:
|
case ETHER_FLOW | FLOW_EXT:
|
||||||
/* Must match all of destination, */
|
/* Must match all or none of VID */
|
||||||
if (!is_zero_ether_addr(mac_mask->h_dest))
|
if (rule->m_ext.vlan_tci != htons(0xfff) &&
|
||||||
|
rule->m_ext.vlan_tci != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
/* all or none of VID, */
|
case ETHER_FLOW:
|
||||||
if (ntuple->fs.vlan_tag_mask != 0xf000 &&
|
/* Must match all of destination */
|
||||||
ntuple->fs.vlan_tag_mask != 0xffff)
|
if (!is_broadcast_ether_addr(mac_mask->h_dest))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
/* and nothing else */
|
/* and nothing else */
|
||||||
if (!is_broadcast_ether_addr(mac_mask->h_source) ||
|
if (!is_zero_ether_addr(mac_mask->h_source) ||
|
||||||
mac_mask->h_proto != htons(0xffff))
|
mac_mask->h_proto)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
rc = efx_filter_set_eth_local(
|
rc = efx_filter_set_eth_local(
|
||||||
&filter,
|
&spec,
|
||||||
(ntuple->fs.vlan_tag_mask == 0xf000) ?
|
(rule->flow_type & FLOW_EXT && rule->m_ext.vlan_tci) ?
|
||||||
ntuple->fs.vlan_tag : EFX_FILTER_VID_UNSPEC,
|
ntohs(rule->h_ext.vlan_tci) : EFX_FILTER_VID_UNSPEC,
|
||||||
mac_entry->h_dest);
|
mac_entry->h_dest);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -949,11 +1037,33 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ntuple->fs.action == ETHTOOL_RXNTUPLE_ACTION_CLEAR)
|
rc = efx_filter_insert_filter(efx, &spec, true);
|
||||||
return efx_filter_remove_filter(efx, &filter);
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
rc = efx_filter_insert_filter(efx, &filter, true);
|
rule->location = rc;
|
||||||
return rc < 0 ? rc : 0;
|
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);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
|
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,
|
.set_wol = efx_ethtool_set_wol,
|
||||||
.reset = efx_ethtool_reset,
|
.reset = efx_ethtool_reset,
|
||||||
.get_rxnfc = efx_ethtool_get_rxnfc,
|
.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_size = efx_ethtool_get_rxfh_indir_size,
|
||||||
.get_rxfh_indir = efx_ethtool_get_rxfh_indir,
|
.get_rxfh_indir = efx_ethtool_get_rxfh_indir,
|
||||||
.set_rxfh_indir = efx_ethtool_set_rxfh_indir,
|
.set_rxfh_indir = efx_ethtool_set_rxfh_indir,
|
||||||
|
|
Loading…
Reference in New Issue