Commit 7c460d9b authored by Ben Hutchings's avatar Ben Hutchings

sfc: Extend and abstract efx_filter_spec to cover Huntington/EF10

Replace type field with match_flags.  Add rss_context and match values
covering of most of what is now in the MCDI protocol.

Change some fields into bitfields so that the structure size doesn't grow
beyond 64 bytes.

Ditch the filter decoding functions as it is now easier to pick apart
the abstract structure.

Rewrite ethtool NFC rule functions to set/get filter match flags and
values directly.
Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
parent f26e958c
...@@ -799,11 +799,12 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) ...@@ -799,11 +799,12 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
return efx_reset(efx, rc); return efx_reset(efx, rc);
} }
/* MAC address mask including only MC flag */ /* MAC address mask including only I/G bit */
static const u8 mac_addr_mc_mask[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; static const u8 mac_addr_ig_mask[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
#define IP4_ADDR_FULL_MASK ((__force __be32)~0) #define IP4_ADDR_FULL_MASK ((__force __be32)~0)
#define PORT_FULL_MASK ((__force __be16)~0) #define PORT_FULL_MASK ((__force __be16)~0)
#define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
static int efx_ethtool_get_class_rule(struct efx_nic *efx, static int efx_ethtool_get_class_rule(struct efx_nic *efx,
struct ethtool_rx_flow_spec *rule) struct ethtool_rx_flow_spec *rule)
...@@ -813,8 +814,6 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx, ...@@ -813,8 +814,6 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
struct ethhdr *mac_entry = &rule->h_u.ether_spec; struct ethhdr *mac_entry = &rule->h_u.ether_spec;
struct ethhdr *mac_mask = &rule->m_u.ether_spec; struct ethhdr *mac_mask = &rule->m_u.ether_spec;
struct efx_filter_spec spec; struct efx_filter_spec spec;
u16 vid;
u8 proto;
int rc; int rc;
rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL, rc = efx_filter_get_filter_safe(efx, EFX_FILTER_PRI_MANUAL,
...@@ -827,39 +826,67 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx, ...@@ -827,39 +826,67 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
else else
rule->ring_cookie = spec.dmaq_id; rule->ring_cookie = spec.dmaq_id;
if (spec.type == EFX_FILTER_MC_DEF || spec.type == EFX_FILTER_UC_DEF) { if ((spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) &&
rule->flow_type = ETHER_FLOW; spec.ether_type == htons(ETH_P_IP) &&
memcpy(mac_mask->h_dest, mac_addr_mc_mask, ETH_ALEN); (spec.match_flags & EFX_FILTER_MATCH_IP_PROTO) &&
if (spec.type == EFX_FILTER_MC_DEF) (spec.ip_proto == IPPROTO_TCP || spec.ip_proto == IPPROTO_UDP) &&
memcpy(mac_entry->h_dest, mac_addr_mc_mask, ETH_ALEN); !(spec.match_flags &
return 0; ~(EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST |
EFX_FILTER_MATCH_IP_PROTO |
EFX_FILTER_MATCH_LOC_PORT | EFX_FILTER_MATCH_REM_PORT))) {
rule->flow_type = ((spec.ip_proto == IPPROTO_TCP) ?
TCP_V4_FLOW : UDP_V4_FLOW);
if (spec.match_flags & EFX_FILTER_MATCH_LOC_HOST) {
ip_entry->ip4dst = spec.loc_host[0];
ip_mask->ip4dst = IP4_ADDR_FULL_MASK;
} }
if (spec.match_flags & EFX_FILTER_MATCH_REM_HOST) {
rc = efx_filter_get_eth_local(&spec, &vid, mac_entry->h_dest); ip_entry->ip4src = spec.rem_host[0];
if (rc == 0) { ip_mask->ip4src = IP4_ADDR_FULL_MASK;
}
if (spec.match_flags & EFX_FILTER_MATCH_LOC_PORT) {
ip_entry->pdst = spec.loc_port;
ip_mask->pdst = PORT_FULL_MASK;
}
if (spec.match_flags & EFX_FILTER_MATCH_REM_PORT) {
ip_entry->psrc = spec.rem_port;
ip_mask->psrc = PORT_FULL_MASK;
}
} else if (!(spec.match_flags &
~(EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG |
EFX_FILTER_MATCH_REM_MAC | EFX_FILTER_MATCH_ETHER_TYPE |
EFX_FILTER_MATCH_OUTER_VID))) {
rule->flow_type = ETHER_FLOW; rule->flow_type = ETHER_FLOW;
if (spec.match_flags &
(EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG)) {
memcpy(mac_entry->h_dest, spec.loc_mac, ETH_ALEN);
if (spec.match_flags & EFX_FILTER_MATCH_LOC_MAC)
memset(mac_mask->h_dest, ~0, ETH_ALEN); memset(mac_mask->h_dest, ~0, ETH_ALEN);
if (vid != EFX_FILTER_VID_UNSPEC) { else
rule->flow_type |= FLOW_EXT; memcpy(mac_mask->h_dest, mac_addr_ig_mask,
rule->h_ext.vlan_tci = htons(vid); ETH_ALEN);
rule->m_ext.vlan_tci = htons(0xfff);
} }
return 0; if (spec.match_flags & EFX_FILTER_MATCH_REM_MAC) {
memcpy(mac_entry->h_source, spec.rem_mac, ETH_ALEN);
memset(mac_mask->h_source, ~0, ETH_ALEN);
}
if (spec.match_flags & EFX_FILTER_MATCH_ETHER_TYPE) {
mac_entry->h_proto = spec.ether_type;
mac_mask->h_proto = ETHER_TYPE_FULL_MASK;
}
} else {
/* The above should handle all filters that we insert */
WARN_ON(1);
return -EINVAL;
} }
rc = efx_filter_get_ipv4_local(&spec, &proto, if (spec.match_flags & EFX_FILTER_MATCH_OUTER_VID) {
&ip_entry->ip4dst, &ip_entry->pdst); rule->flow_type |= FLOW_EXT;
if (rc != 0) { rule->h_ext.vlan_tci = spec.outer_vid;
rc = efx_filter_get_ipv4_full( rule->m_ext.vlan_tci = htons(0xfff);
&spec, &proto, &ip_entry->ip4dst, &ip_entry->pdst,
&ip_entry->ip4src, &ip_entry->psrc);
EFX_WARN_ON_PARANOID(rc);
ip_mask->ip4src = IP4_ADDR_FULL_MASK;
ip_mask->psrc = PORT_FULL_MASK;
} }
rule->flow_type = (proto == IPPROTO_TCP) ? TCP_V4_FLOW : UDP_V4_FLOW;
ip_mask->ip4dst = IP4_ADDR_FULL_MASK;
ip_mask->pdst = PORT_FULL_MASK;
return rc; return rc;
} }
...@@ -969,80 +996,78 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx, ...@@ -969,80 +996,78 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
(rule->ring_cookie == RX_CLS_FLOW_DISC) ? (rule->ring_cookie == RX_CLS_FLOW_DISC) ?
EFX_FILTER_RX_DMAQ_ID_DROP : rule->ring_cookie); EFX_FILTER_RX_DMAQ_ID_DROP : rule->ring_cookie);
switch (rule->flow_type) { switch (rule->flow_type & ~FLOW_EXT) {
case TCP_V4_FLOW: case TCP_V4_FLOW:
case UDP_V4_FLOW: { case UDP_V4_FLOW:
u8 proto = (rule->flow_type == TCP_V4_FLOW ? spec.match_flags = (EFX_FILTER_MATCH_ETHER_TYPE |
EFX_FILTER_MATCH_IP_PROTO);
spec.ether_type = htons(ETH_P_IP);
spec.ip_proto = ((rule->flow_type & ~FLOW_EXT) == TCP_V4_FLOW ?
IPPROTO_TCP : IPPROTO_UDP); IPPROTO_TCP : IPPROTO_UDP);
if (ip_mask->ip4dst) {
/* Must match all of destination, */ if (ip_mask->ip4dst != IP4_ADDR_FULL_MASK)
if (!(ip_mask->ip4dst == IP4_ADDR_FULL_MASK &&
ip_mask->pdst == PORT_FULL_MASK))
return -EINVAL; return -EINVAL;
/* all or none of source, */ spec.match_flags |= EFX_FILTER_MATCH_LOC_HOST;
if ((ip_mask->ip4src || ip_mask->psrc) && spec.loc_host[0] = ip_entry->ip4dst;
!(ip_mask->ip4src == IP4_ADDR_FULL_MASK && }
ip_mask->psrc == PORT_FULL_MASK)) if (ip_mask->ip4src) {
if (ip_mask->ip4src != IP4_ADDR_FULL_MASK)
return -EINVAL; return -EINVAL;
/* and nothing else */ spec.match_flags |= EFX_FILTER_MATCH_REM_HOST;
if (ip_mask->tos || rule->m_ext.vlan_tci) spec.rem_host[0] = ip_entry->ip4src;
}
if (ip_mask->pdst) {
if (ip_mask->pdst != PORT_FULL_MASK)
return -EINVAL; return -EINVAL;
spec.match_flags |= EFX_FILTER_MATCH_LOC_PORT;
if (ip_mask->ip4src) spec.loc_port = ip_entry->pdst;
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(&spec, proto,
ip_entry->ip4dst,
ip_entry->pdst);
if (rc)
return rc;
break;
} }
if (ip_mask->psrc) {
case ETHER_FLOW | FLOW_EXT: if (ip_mask->psrc != PORT_FULL_MASK)
case ETHER_FLOW: {
u16 vlan_tag_mask = (rule->flow_type & FLOW_EXT ?
ntohs(rule->m_ext.vlan_tci) : 0);
/* Must not match on source address or Ethertype */
if (!is_zero_ether_addr(mac_mask->h_source) ||
mac_mask->h_proto)
return -EINVAL; return -EINVAL;
spec.match_flags |= EFX_FILTER_MATCH_REM_PORT;
spec.rem_port = ip_entry->psrc;
}
if (ip_mask->tos)
return -EINVAL;
break;
/* Is it a default UC or MC filter? */ case ETHER_FLOW:
if (ether_addr_equal(mac_mask->h_dest, mac_addr_mc_mask) && if (!is_zero_ether_addr(mac_mask->h_dest)) {
vlan_tag_mask == 0) { if (ether_addr_equal(mac_mask->h_dest,
if (is_multicast_ether_addr(mac_entry->h_dest)) mac_addr_ig_mask))
rc = efx_filter_set_mc_def(&spec); spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG;
else if (is_broadcast_ether_addr(mac_mask->h_dest))
spec.match_flags |= EFX_FILTER_MATCH_LOC_MAC;
else else
rc = efx_filter_set_uc_def(&spec); return -EINVAL;
memcpy(spec.loc_mac, mac_entry->h_dest, ETH_ALEN);
} }
/* Otherwise, it must match all of destination and all if (!is_zero_ether_addr(mac_mask->h_source)) {
* or none of VID. if (!is_broadcast_ether_addr(mac_mask->h_source))
*/ return -EINVAL;
else if (is_broadcast_ether_addr(mac_mask->h_dest) && spec.match_flags |= EFX_FILTER_MATCH_REM_MAC;
(vlan_tag_mask == 0xfff || vlan_tag_mask == 0)) { memcpy(spec.rem_mac, mac_entry->h_source, ETH_ALEN);
rc = efx_filter_set_eth_local(
&spec,
vlan_tag_mask ?
ntohs(rule->h_ext.vlan_tci) : EFX_FILTER_VID_UNSPEC,
mac_entry->h_dest);
} else {
rc = -EINVAL;
} }
if (rc) if (mac_mask->h_proto) {
return rc; if (mac_mask->h_proto != ETHER_TYPE_FULL_MASK)
break; return -EINVAL;
spec.match_flags |= EFX_FILTER_MATCH_ETHER_TYPE;
spec.ether_type = mac_entry->h_proto;
} }
break;
default: default:
return -EINVAL; return -EINVAL;
} }
if ((rule->flow_type & FLOW_EXT) && rule->m_ext.vlan_tci) {
if (rule->m_ext.vlan_tci != htons(0xfff))
return -EINVAL;
spec.match_flags |= EFX_FILTER_MATCH_OUTER_VID;
spec.outer_vid = rule->h_ext.vlan_tci;
}
rc = efx_filter_insert_filter(efx, &spec, true); rc = efx_filter_insert_filter(efx, &spec, true);
if (rc < 0) if (rc < 0)
return rc; return rc;
......
...@@ -32,6 +32,18 @@ ...@@ -32,6 +32,18 @@
* counter-productive. */ * counter-productive. */
#define EFX_FARCH_FILTER_CTL_SRCH_HINT_MAX 5 #define EFX_FARCH_FILTER_CTL_SRCH_HINT_MAX 5
enum efx_farch_filter_type {
EFX_FARCH_FILTER_TCP_FULL = 0,
EFX_FARCH_FILTER_TCP_WILD,
EFX_FARCH_FILTER_UDP_FULL,
EFX_FARCH_FILTER_UDP_WILD,
EFX_FARCH_FILTER_MAC_FULL = 4,
EFX_FARCH_FILTER_MAC_WILD,
EFX_FARCH_FILTER_UC_DEF = 8,
EFX_FARCH_FILTER_MC_DEF,
EFX_FARCH_FILTER_TYPE_COUNT, /* number of specific types */
};
enum efx_farch_filter_table_id { enum efx_farch_filter_table_id {
EFX_FARCH_FILTER_TABLE_RX_IP = 0, EFX_FARCH_FILTER_TABLE_RX_IP = 0,
EFX_FARCH_FILTER_TABLE_RX_MAC, EFX_FARCH_FILTER_TABLE_RX_MAC,
...@@ -62,7 +74,7 @@ struct efx_farch_filter_table { ...@@ -62,7 +74,7 @@ struct efx_farch_filter_table {
unsigned used; /* number currently used */ unsigned used; /* number currently used */
unsigned long *used_bitmap; unsigned long *used_bitmap;
struct efx_farch_filter_spec *spec; struct efx_farch_filter_spec *spec;
unsigned search_depth[EFX_FILTER_TYPE_COUNT]; unsigned search_depth[EFX_FARCH_FILTER_TYPE_COUNT];
}; };
struct efx_filter_state { struct efx_filter_state {
...@@ -106,33 +118,22 @@ static enum efx_farch_filter_table_id ...@@ -106,33 +118,22 @@ static enum efx_farch_filter_table_id
efx_farch_filter_spec_table_id(const struct efx_farch_filter_spec *spec) efx_farch_filter_spec_table_id(const struct efx_farch_filter_spec *spec)
{ {
BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP != BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP !=
(EFX_FILTER_TCP_FULL >> 2)); (EFX_FARCH_FILTER_TCP_FULL >> 2));
BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP != BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP !=
(EFX_FILTER_TCP_WILD >> 2)); (EFX_FARCH_FILTER_TCP_WILD >> 2));
BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP != BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP !=
(EFX_FILTER_UDP_FULL >> 2)); (EFX_FARCH_FILTER_UDP_FULL >> 2));
BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP != BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_IP !=
(EFX_FILTER_UDP_WILD >> 2)); (EFX_FARCH_FILTER_UDP_WILD >> 2));
BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_MAC != BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_MAC !=
(EFX_FILTER_MAC_FULL >> 2)); (EFX_FARCH_FILTER_MAC_FULL >> 2));
BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_MAC != BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_RX_MAC !=
(EFX_FILTER_MAC_WILD >> 2)); (EFX_FARCH_FILTER_MAC_WILD >> 2));
BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_TX_MAC != BUILD_BUG_ON(EFX_FARCH_FILTER_TABLE_TX_MAC !=
EFX_FARCH_FILTER_TABLE_RX_MAC + 2); EFX_FARCH_FILTER_TABLE_RX_MAC + 2);
EFX_BUG_ON_PARANOID(spec->type == EFX_FILTER_UNSPEC);
return (spec->type >> 2) + ((spec->flags & EFX_FILTER_FLAG_TX) ? 2 : 0); return (spec->type >> 2) + ((spec->flags & EFX_FILTER_FLAG_TX) ? 2 : 0);
} }
static struct efx_farch_filter_table *
efx_farch_filter_spec_table(struct efx_filter_state *state,
const struct efx_farch_filter_spec *spec)
{
if (spec->type == EFX_FILTER_UNSPEC)
return NULL;
else
return &state->table[efx_farch_filter_spec_table_id(spec)];
}
static void static void
efx_farch_filter_table_reset_search_depth(struct efx_farch_filter_table *table) efx_farch_filter_table_reset_search_depth(struct efx_farch_filter_table *table)
{ {
...@@ -149,27 +150,27 @@ static void efx_farch_filter_push_rx_config(struct efx_nic *efx) ...@@ -149,27 +150,27 @@ static void efx_farch_filter_push_rx_config(struct efx_nic *efx)
table = &state->table[EFX_FARCH_FILTER_TABLE_RX_IP]; table = &state->table[EFX_FARCH_FILTER_TABLE_RX_IP];
EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_FULL_SRCH_LIMIT, EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_FULL_SRCH_LIMIT,
table->search_depth[EFX_FILTER_TCP_FULL] + table->search_depth[EFX_FARCH_FILTER_TCP_FULL] +
EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL); EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL);
EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_WILD_SRCH_LIMIT, EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_TCP_WILD_SRCH_LIMIT,
table->search_depth[EFX_FILTER_TCP_WILD] + table->search_depth[EFX_FARCH_FILTER_TCP_WILD] +
EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD); EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD);
EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_FULL_SRCH_LIMIT, EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_FULL_SRCH_LIMIT,
table->search_depth[EFX_FILTER_UDP_FULL] + table->search_depth[EFX_FARCH_FILTER_UDP_FULL] +
EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL); EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL);
EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_WILD_SRCH_LIMIT, EFX_SET_OWORD_FIELD(filter_ctl, FRF_BZ_UDP_WILD_SRCH_LIMIT,
table->search_depth[EFX_FILTER_UDP_WILD] + table->search_depth[EFX_FARCH_FILTER_UDP_WILD] +
EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD); EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD);
table = &state->table[EFX_FARCH_FILTER_TABLE_RX_MAC]; table = &state->table[EFX_FARCH_FILTER_TABLE_RX_MAC];
if (table->size) { if (table->size) {
EFX_SET_OWORD_FIELD( EFX_SET_OWORD_FIELD(
filter_ctl, FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT, filter_ctl, FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT,
table->search_depth[EFX_FILTER_MAC_FULL] + table->search_depth[EFX_FARCH_FILTER_MAC_FULL] +
EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL); EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL);
EFX_SET_OWORD_FIELD( EFX_SET_OWORD_FIELD(
filter_ctl, FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT, filter_ctl, FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT,
table->search_depth[EFX_FILTER_MAC_WILD] + table->search_depth[EFX_FARCH_FILTER_MAC_WILD] +
EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD); EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD);
} }
...@@ -225,64 +226,57 @@ static void efx_farch_filter_push_tx_limits(struct efx_nic *efx) ...@@ -225,64 +226,57 @@ static void efx_farch_filter_push_tx_limits(struct efx_nic *efx)
if (table->size) { if (table->size) {
EFX_SET_OWORD_FIELD( EFX_SET_OWORD_FIELD(
tx_cfg, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE, tx_cfg, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
table->search_depth[EFX_FILTER_MAC_FULL] + table->search_depth[EFX_FARCH_FILTER_MAC_FULL] +
EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL); EFX_FARCH_FILTER_CTL_SRCH_FUDGE_FULL);
EFX_SET_OWORD_FIELD( EFX_SET_OWORD_FIELD(
tx_cfg, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE, tx_cfg, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
table->search_depth[EFX_FILTER_MAC_WILD] + table->search_depth[EFX_FARCH_FILTER_MAC_WILD] +
EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD); EFX_FARCH_FILTER_CTL_SRCH_FUDGE_WILD);
} }
efx_writeo(efx, &tx_cfg, FR_AZ_TX_CFG); efx_writeo(efx, &tx_cfg, FR_AZ_TX_CFG);
} }
static inline void __efx_filter_set_ipv4(struct efx_filter_spec *spec, static int
__be32 host1, __be16 port1, efx_farch_filter_from_gen_spec(struct efx_farch_filter_spec *spec,
__be32 host2, __be16 port2) const struct efx_filter_spec *gen_spec)
{ {
spec->data[0] = ntohl(host1) << 16 | ntohs(port1); bool is_full = false;
spec->data[1] = ntohs(port2) << 16 | ntohl(host1) >> 16;
spec->data[2] = ntohl(host2);
}
static inline void __efx_filter_get_ipv4(const struct efx_filter_spec *spec,
__be32 *host1, __be16 *port1,
__be32 *host2, __be16 *port2)
{
*host1 = htonl(spec->data[0] >> 16 | spec->data[1] << 16);
*port1 = htons(spec->data[0]);
*host2 = htonl(spec->data[2]);
*port2 = htons(spec->data[1] >> 16);
}
/** if ((gen_spec->flags & EFX_FILTER_FLAG_RX_RSS) &&
* efx_filter_set_ipv4_local - specify IPv4 host, transport protocol and port gen_spec->rss_context != EFX_FILTER_RSS_CONTEXT_DEFAULT)
* @spec: Specification to initialise return -EINVAL;
* @proto: Transport layer protocol number
* @host: Local host address (network byte order)
* @port: Local port (network byte order)
*/
int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
__be32 host, __be16 port)
{
__be32 host1;
__be16 port1;
EFX_BUG_ON_PARANOID(!(spec->flags & EFX_FILTER_FLAG_RX)); spec->priority = gen_spec->priority;
spec->flags = gen_spec->flags;
spec->dmaq_id = gen_spec->dmaq_id;
/* This cannot currently be combined with other filtering */ switch (gen_spec->match_flags) {
if (spec->type != EFX_FILTER_UNSPEC) case (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
return -EPROTONOSUPPORT; EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT):
is_full = true;
/* fall through */
case (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT): {
__be32 rhost, host1, host2;
__be16 rport, port1, port2;
if (port == 0) EFX_BUG_ON_PARANOID(!(gen_spec->flags & EFX_FILTER_FLAG_RX));
return -EINVAL;
switch (proto) { if (gen_spec->ether_type != htons(ETH_P_IP))
return -EPROTONOSUPPORT;
if (gen_spec->loc_port == 0 ||
(is_full && gen_spec->rem_port == 0))
return -EADDRNOTAVAIL;
switch (gen_spec->ip_proto) {
case IPPROTO_TCP: case IPPROTO_TCP:
spec->type = EFX_FILTER_TCP_WILD; spec->type = (is_full ? EFX_FARCH_FILTER_TCP_FULL :
EFX_FARCH_FILTER_TCP_WILD);
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
spec->type = EFX_FILTER_UDP_WILD; spec->type = (is_full ? EFX_FARCH_FILTER_UDP_FULL :
EFX_FARCH_FILTER_UDP_WILD);
break; break;
default: default:
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
...@@ -293,155 +287,141 @@ int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto, ...@@ -293,155 +287,141 @@ int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
* wildcard filter. We need to convert from local and remote * wildcard filter. We need to convert from local and remote
* (= zero for wildcard) addresses. * (= zero for wildcard) addresses.
*/ */
host1 = 0; rhost = is_full ? gen_spec->rem_host[0] : 0;
if (proto != IPPROTO_UDP) { rport = is_full ? gen_spec->rem_port : 0;
port1 = 0; host1 = rhost;
host2 = gen_spec->loc_host[0];
if (!is_full && gen_spec->ip_proto == IPPROTO_UDP) {
port1 = gen_spec->loc_port;
port2 = rport;
} else { } else {
port1 = port; port1 = rport;
port = 0; port2 = gen_spec->loc_port;
} }
spec->data[0] = ntohl(host1) << 16 | ntohs(port1);
spec->data[1] = ntohs(port2) << 16 | ntohl(host1) >> 16;
spec->data[2] = ntohl(host2);
__efx_filter_set_ipv4(spec, host1, port1, host, port); break;
return 0;
}
int efx_filter_get_ipv4_local(const struct efx_filter_spec *spec,
u8 *proto, __be32 *host, __be16 *port)
{
__be32 host1;
__be16 port1;
switch (spec->type) {
case EFX_FILTER_TCP_WILD:
*proto = IPPROTO_TCP;
__efx_filter_get_ipv4(spec, &host1, &port1, host, port);
return 0;
case EFX_FILTER_UDP_WILD:
*proto = IPPROTO_UDP;
__efx_filter_get_ipv4(spec, &host1, port, host, &port1);
return 0;
default:
return -EINVAL;
} }
}
/**
* efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports
* @spec: Specification to initialise
* @proto: Transport layer protocol number
* @host: Local host address (network byte order)
* @port: Local port (network byte order)
* @rhost: Remote host address (network byte order)
* @rport: Remote port (network byte order)
*/
int efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto,
__be32 host, __be16 port,
__be32 rhost, __be16 rport)
{
EFX_BUG_ON_PARANOID(!(spec->flags & EFX_FILTER_FLAG_RX));
/* This cannot currently be combined with other filtering */ case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID:
if (spec->type != EFX_FILTER_UNSPEC) is_full = true;
return -EPROTONOSUPPORT; /* fall through */
case EFX_FILTER_MATCH_LOC_MAC:
if (port == 0 || rport == 0) spec->type = (is_full ? EFX_FARCH_FILTER_MAC_FULL :
return -EINVAL; EFX_FARCH_FILTER_MAC_WILD);
spec->data[0] = is_full ? ntohs(gen_spec->outer_vid) : 0;
switch (proto) { spec->data[1] = (gen_spec->loc_mac[2] << 24 |
case IPPROTO_TCP: gen_spec->loc_mac[3] << 16 |
spec->type = EFX_FILTER_TCP_FULL; gen_spec->loc_mac[4] << 8 |
gen_spec->loc_mac[5]);
spec->data[2] = (gen_spec->loc_mac[0] << 8 |
gen_spec->loc_mac[1]);
break; break;
case IPPROTO_UDP:
spec->type = EFX_FILTER_UDP_FULL; case EFX_FILTER_MATCH_LOC_MAC_IG:
spec->type = (is_multicast_ether_addr(gen_spec->loc_mac) ?
EFX_FARCH_FILTER_MC_DEF :
EFX_FARCH_FILTER_UC_DEF);
memset(spec->data, 0, sizeof(spec->data)); /* ensure equality */
break; break;
default: default:
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
} }
__efx_filter_set_ipv4(spec, rhost, rport, host, port);
return 0; return 0;
} }
int efx_filter_get_ipv4_full(const struct efx_filter_spec *spec, static void
u8 *proto, __be32 *host, __be16 *port, efx_farch_filter_to_gen_spec(struct efx_filter_spec *gen_spec,
__be32 *rhost, __be16 *rport) const struct efx_farch_filter_spec *spec)
{ {
switch (spec->type) { bool is_full = false;
case EFX_FILTER_TCP_FULL:
*proto = IPPROTO_TCP;
break;
case EFX_FILTER_UDP_FULL:
*proto = IPPROTO_UDP;
break;
default:
return -EINVAL;
}
__efx_filter_get_ipv4(spec, rhost, rport, host, port);
return 0;
}
/** /* *gen_spec should be completely initialised, to be consistent
* efx_filter_set_eth_local - specify local Ethernet address and optional VID * with efx_filter_init_{rx,tx}() and in case we want to copy
* @spec: Specification to initialise * it back to userland.
* @vid: VLAN ID to match, or %EFX_FILTER_VID_UNSPEC
* @addr: Local Ethernet MAC address
*/ */
int efx_filter_set_eth_local(struct efx_filter_spec *spec, memset(gen_spec, 0, sizeof(*gen_spec));
u16 vid, const u8 *addr)
{
EFX_BUG_ON_PARANOID(!(spec->flags &
(EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)));
/* This cannot currently be combined with other filtering */ gen_spec->priority = spec->priority;
if (spec->type != EFX_FILTER_UNSPEC) gen_spec->flags = spec->flags;
return -EPROTONOSUPPORT; gen_spec->dmaq_id = spec->dmaq_id;
if (vid == EFX_FILTER_VID_UNSPEC) { switch (spec->type) {
spec->type = EFX_FILTER_MAC_WILD; case EFX_FARCH_FILTER_TCP_FULL:
spec->data[0] = 0; case EFX_FARCH_FILTER_UDP_FULL:
is_full = true;
/* fall through */
case EFX_FARCH_FILTER_TCP_WILD:
case EFX_FARCH_FILTER_UDP_WILD: {
__be32 host1, host2;
__be16 port1, port2;
gen_spec->match_flags =
EFX_FILTER_MATCH_ETHER_TYPE |
EFX_FILTER_MATCH_IP_PROTO |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
if (is_full)
gen_spec->match_flags |= (EFX_FILTER_MATCH_REM_HOST |
EFX_FILTER_MATCH_REM_PORT);
gen_spec->ether_type = htons(ETH_P_IP);
gen_spec->ip_proto =
(spec->type == EFX_FARCH_FILTER_TCP_FULL ||
spec->type == EFX_FARCH_FILTER_TCP_WILD) ?
IPPROTO_TCP : IPPROTO_UDP;
host1 = htonl(spec->data[0] >> 16 | spec->data[1] << 16);
port1 = htons(spec->data[0]);
host2 = htonl(spec->data[2]);
port2 = htons(spec->data[1] >> 16);
if (spec->flags & EFX_FILTER_FLAG_TX) {
gen_spec->loc_host[0] = host1;
gen_spec->rem_host[0] = host2;
} else { } else {
spec->type = EFX_FILTER_MAC_FULL; gen_spec->loc_host[0] = host2;
spec->data[0] = vid; gen_spec->rem_host[0] = host1;
}
if (!!(gen_spec->flags & EFX_FILTER_FLAG_TX) ^
(!is_full && gen_spec->ip_proto == IPPROTO_UDP)) {
gen_spec->loc_port = port1;
gen_spec->rem_port = port2;
} else {
gen_spec->loc_port = port2;
gen_spec->rem_port = port1;
} }
spec->data[1] = addr[2] << 24 | addr[3] << 16 | addr[4] << 8 | addr[5]; break;
spec->data[2] = addr[0] << 8 | addr[1]; }
return 0;
}
/**
* efx_filter_set_uc_def - specify matching otherwise-unmatched unicast
* @spec: Specification to initialise
*/
int efx_filter_set_uc_def(struct efx_filter_spec *spec)
{
EFX_BUG_ON_PARANOID(!(spec->flags &
(EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)));
if (spec->type != EFX_FILTER_UNSPEC)
return -EINVAL;
spec->type = EFX_FILTER_UC_DEF;
memset(spec->data, 0, sizeof(spec->data)); /* ensure equality */
return 0;
}
/** case EFX_FARCH_FILTER_MAC_FULL:
* efx_filter_set_mc_def - specify matching otherwise-unmatched multicast is_full = true;
* @spec: Specification to initialise /* fall through */
*/ case EFX_FARCH_FILTER_MAC_WILD:
int efx_filter_set_mc_def(struct efx_filter_spec *spec) gen_spec->match_flags = EFX_FILTER_MATCH_LOC_MAC;
{ if (is_full)
EFX_BUG_ON_PARANOID(!(spec->flags & gen_spec->match_flags |= EFX_FILTER_MATCH_OUTER_VID;
(EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX))); gen_spec->loc_mac[0] = spec->data[2] >> 8;
gen_spec->loc_mac[1] = spec->data[2];
gen_spec->loc_mac[2] = spec->data[1] >> 24;
gen_spec->loc_mac[3] = spec->data[1] >> 16;
gen_spec->loc_mac[4] = spec->data[1] >> 8;
gen_spec->loc_mac[5] = spec->data[1];
gen_spec->outer_vid = htons(spec->data[0]);
break;
if (spec->type != EFX_FILTER_UNSPEC) case EFX_FARCH_FILTER_UC_DEF:
return -EINVAL; case EFX_FARCH_FILTER_MC_DEF:
gen_spec->match_flags = EFX_FILTER_MATCH_LOC_MAC_IG;
gen_spec->loc_mac[0] = spec->type == EFX_FARCH_FILTER_MC_DEF;
break;
spec->type = EFX_FILTER_MC_DEF; default:
memset(spec->data, 0, sizeof(spec->data)); /* ensure equality */ WARN_ON(1);
return 0; break;
}
} }
static void static void
...@@ -455,7 +435,7 @@ efx_farch_filter_reset_rx_def(struct efx_nic *efx, unsigned filter_idx) ...@@ -455,7 +435,7 @@ efx_farch_filter_reset_rx_def(struct efx_nic *efx, unsigned filter_idx)
/* If there's only one channel then disable RSS for non VF /* If there's only one channel then disable RSS for non VF
* traffic, thereby allowing VFs to use RSS when the PF can't. * traffic, thereby allowing VFs to use RSS when the PF can't.
*/ */
spec->type = EFX_FILTER_UC_DEF + filter_idx; spec->type = EFX_FARCH_FILTER_UC_DEF + filter_idx;
spec->priority = EFX_FILTER_PRI_MANUAL; spec->priority = EFX_FILTER_PRI_MANUAL;
spec->flags = (EFX_FILTER_FLAG_RX | spec->flags = (EFX_FILTER_FLAG_RX |
(efx->n_rx_channels > 1 ? EFX_FILTER_FLAG_RX_RSS : 0) | (efx->n_rx_channels > 1 ? EFX_FILTER_FLAG_RX_RSS : 0) |
...@@ -464,29 +444,6 @@ efx_farch_filter_reset_rx_def(struct efx_nic *efx, unsigned filter_idx) ...@@ -464,29 +444,6 @@ efx_farch_filter_reset_rx_def(struct efx_nic *efx, unsigned filter_idx)
table->used_bitmap[0] |= 1 << filter_idx; table->used_bitmap[0] |= 1 << filter_idx;
} }
int efx_filter_get_eth_local(const struct efx_filter_spec *spec,
u16 *vid, u8 *addr)
{
switch (spec->type) {
case EFX_FILTER_MAC_WILD:
*vid = EFX_FILTER_VID_UNSPEC;
break;
case EFX_FILTER_MAC_FULL:
*vid = spec->data[0];
break;
default:
return -EINVAL;
}
addr[0] = spec->data[2] >> 8;
addr[1] = spec->data[2];
addr[2] = spec->data[1] >> 24;
addr[3] = spec->data[1] >> 16;
addr[4] = spec->data[1] >> 8;
addr[5] = spec->data[1];
return 0;
}
/* Build a filter entry and return its n-tuple key. */ /* Build a filter entry and return its n-tuple key. */
static u32 efx_farch_filter_build(efx_oword_t *filter, static u32 efx_farch_filter_build(efx_oword_t *filter,
struct efx_farch_filter_spec *spec) struct efx_farch_filter_spec *spec)
...@@ -495,8 +452,8 @@ static u32 efx_farch_filter_build(efx_oword_t *filter, ...@@ -495,8 +452,8 @@ static u32 efx_farch_filter_build(efx_oword_t *filter,
switch (efx_farch_filter_spec_table_id(spec)) { switch (efx_farch_filter_spec_table_id(spec)) {
case EFX_FARCH_FILTER_TABLE_RX_IP: { case EFX_FARCH_FILTER_TABLE_RX_IP: {
bool is_udp = (spec->type == EFX_FILTER_UDP_FULL || bool is_udp = (spec->type == EFX_FARCH_FILTER_UDP_FULL ||
spec->type == EFX_FILTER_UDP_WILD); spec->type == EFX_FARCH_FILTER_UDP_WILD);
EFX_POPULATE_OWORD_7( EFX_POPULATE_OWORD_7(
*filter, *filter,
FRF_BZ_RSS_EN, FRF_BZ_RSS_EN,
...@@ -513,7 +470,7 @@ static u32 efx_farch_filter_build(efx_oword_t *filter, ...@@ -513,7 +470,7 @@ static u32 efx_farch_filter_build(efx_oword_t *filter,
} }
case EFX_FARCH_FILTER_TABLE_RX_MAC: { case EFX_FARCH_FILTER_TABLE_RX_MAC: {
bool is_wild = spec->type == EFX_FILTER_MAC_WILD; bool is_wild = spec->type == EFX_FARCH_FILTER_MAC_WILD;
EFX_POPULATE_OWORD_7( EFX_POPULATE_OWORD_7(
*filter, *filter,
FRF_CZ_RMFT_RSS_EN, FRF_CZ_RMFT_RSS_EN,
...@@ -530,7 +487,7 @@ static u32 efx_farch_filter_build(efx_oword_t *filter, ...@@ -530,7 +487,7 @@ static u32 efx_farch_filter_build(efx_oword_t *filter,
} }
case EFX_FARCH_FILTER_TABLE_TX_MAC: { case EFX_FARCH_FILTER_TABLE_TX_MAC: {
bool is_wild = spec->type == EFX_FILTER_MAC_WILD; bool is_wild = spec->type == EFX_FARCH_FILTER_MAC_WILD;
EFX_POPULATE_OWORD_5(*filter, EFX_POPULATE_OWORD_5(*filter,
FRF_CZ_TMFT_TXQ_ID, spec->dmaq_id, FRF_CZ_TMFT_TXQ_ID, spec->dmaq_id,
FRF_CZ_TMFT_WILDCARD_MATCH, is_wild, FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
...@@ -573,15 +530,15 @@ static bool efx_farch_filter_equal(const struct efx_farch_filter_spec *left, ...@@ -573,15 +530,15 @@ static bool efx_farch_filter_equal(const struct efx_farch_filter_spec *left,
#define EFX_FARCH_FILTER_MATCH_PRI_COUNT 5 #define EFX_FARCH_FILTER_MATCH_PRI_COUNT 5
static const u8 efx_farch_filter_type_match_pri[EFX_FILTER_TYPE_COUNT] = { static const u8 efx_farch_filter_type_match_pri[EFX_FARCH_FILTER_TYPE_COUNT] = {
[EFX_FILTER_TCP_FULL] = 0, [EFX_FARCH_FILTER_TCP_FULL] = 0,
[EFX_FILTER_UDP_FULL] = 0, [EFX_FARCH_FILTER_UDP_FULL] = 0,
[EFX_FILTER_TCP_WILD] = 1, [EFX_FARCH_FILTER_TCP_WILD] = 1,
[EFX_FILTER_UDP_WILD] = 1, [EFX_FARCH_FILTER_UDP_WILD] = 1,
[EFX_FILTER_MAC_FULL] = 2, [EFX_FARCH_FILTER_MAC_FULL] = 2,
[EFX_FILTER_MAC_WILD] = 3, [EFX_FARCH_FILTER_MAC_WILD] = 3,
[EFX_FILTER_UC_DEF] = 4, [EFX_FARCH_FILTER_UC_DEF] = 4,
[EFX_FILTER_MC_DEF] = 4, [EFX_FARCH_FILTER_MC_DEF] = 4,
}; };
static const enum efx_farch_filter_table_id efx_farch_filter_range_table[] = { static const enum efx_farch_filter_table_id efx_farch_filter_range_table[] = {
...@@ -672,11 +629,12 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, ...@@ -672,11 +629,12 @@ s32 efx_filter_insert_filter(struct efx_nic *efx,
unsigned int depth = 0; unsigned int depth = 0;
int rc; int rc;
/* XXX efx_farch_filter_spec and efx_filter_spec will diverge in future */ rc = efx_farch_filter_from_gen_spec(&spec, gen_spec);
memcpy(&spec, gen_spec, sizeof(*gen_spec)); if (rc)
return rc;
table = efx_farch_filter_spec_table(state, &spec); table = &state->table[efx_farch_filter_spec_table_id(&spec)];
if (!table || table->size == 0) if (table->size == 0)
return -EINVAL; return -EINVAL;
netif_vdbg(efx, hw, efx->net_dev, netif_vdbg(efx, hw, efx->net_dev,
...@@ -687,8 +645,8 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, ...@@ -687,8 +645,8 @@ s32 efx_filter_insert_filter(struct efx_nic *efx,
/* One filter spec per type */ /* One filter spec per type */
BUILD_BUG_ON(EFX_FARCH_FILTER_INDEX_UC_DEF != 0); BUILD_BUG_ON(EFX_FARCH_FILTER_INDEX_UC_DEF != 0);
BUILD_BUG_ON(EFX_FARCH_FILTER_INDEX_MC_DEF != BUILD_BUG_ON(EFX_FARCH_FILTER_INDEX_MC_DEF !=
EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF); EFX_FARCH_FILTER_MC_DEF - EFX_FARCH_FILTER_UC_DEF);
rep_index = spec.type - EFX_FILTER_UC_DEF; rep_index = spec.type - EFX_FARCH_FILTER_UC_DEF;
ins_index = rep_index; ins_index = rep_index;
spin_lock_bh(&state->lock); spin_lock_bh(&state->lock);
...@@ -911,8 +869,7 @@ int efx_filter_get_filter_safe(struct efx_nic *efx, ...@@ -911,8 +869,7 @@ int efx_filter_get_filter_safe(struct efx_nic *efx,
if (test_bit(filter_idx, table->used_bitmap) && if (test_bit(filter_idx, table->used_bitmap) &&
spec->priority == priority) { spec->priority == priority) {
/* XXX efx_farch_filter_spec and efx_filter_spec will diverge */ efx_farch_filter_to_gen_spec(spec_buf, spec);
memcpy(spec_buf, spec, sizeof(*spec));
rc = 0; rc = 0;
} else { } else {
rc = -ENOENT; rc = -ENOENT;
......
...@@ -11,32 +11,49 @@ ...@@ -11,32 +11,49 @@
#define EFX_FILTER_H #define EFX_FILTER_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/if_ether.h>
#include <asm/byteorder.h>
/** /**
* enum efx_filter_type - type of hardware filter * enum efx_filter_match_flags - Flags for hardware filter match type
* @EFX_FILTER_TCP_FULL: Matching TCP/IPv4 4-tuple * @EFX_FILTER_MATCH_REM_HOST: Match by remote IP host address
* @EFX_FILTER_TCP_WILD: Matching TCP/IPv4 destination (host, port) * @EFX_FILTER_MATCH_LOC_HOST: Match by local IP host address
* @EFX_FILTER_UDP_FULL: Matching UDP/IPv4 4-tuple * @EFX_FILTER_MATCH_REM_MAC: Match by remote MAC address
* @EFX_FILTER_UDP_WILD: Matching UDP/IPv4 destination (host, port) * @EFX_FILTER_MATCH_REM_PORT: Match by remote TCP/UDP port
* @EFX_FILTER_MAC_FULL: Matching Ethernet destination MAC address, VID * @EFX_FILTER_MATCH_LOC_MAC: Match by local MAC address
* @EFX_FILTER_MAC_WILD: Matching Ethernet destination MAC address * @EFX_FILTER_MATCH_LOC_PORT: Match by local TCP/UDP port
* @EFX_FILTER_UC_DEF: Matching all otherwise unmatched unicast * @EFX_FILTER_MATCH_ETHER_TYPE: Match by Ether-type
* @EFX_FILTER_MC_DEF: Matching all otherwise unmatched multicast * @EFX_FILTER_MATCH_INNER_VID: Match by inner VLAN ID
* @EFX_FILTER_UNSPEC: Match type is unspecified * @EFX_FILTER_MATCH_OUTER_VID: Match by outer VLAN ID
* @EFX_FILTER_MATCH_IP_PROTO: Match by IP transport protocol
* @EFX_FILTER_MATCH_LOC_MAC_IG: Match by local MAC address I/G bit.
* Used for RX default unicast and multicast/broadcast filters.
* *
* Falcon NICs only support the TCP/IPv4 and UDP/IPv4 filter types. * Only some combinations are supported, depending on NIC type:
*
* - Falcon supports RX filters matching by {TCP,UDP}/IPv4 4-tuple or
* local 2-tuple (only implemented for Falcon B0)
*
* - Siena supports RX and TX filters matching by {TCP,UDP}/IPv4 4-tuple
* or local 2-tuple, or local MAC with or without outer VID, and RX
* default filters
*
* - Huntington supports filter matching controlled by firmware, potentially
* using {TCP,UDP}/IPv{4,6} 4-tuple or local 2-tuple, local MAC or I/G bit,
* with or without outer and inner VID
*/ */
enum efx_filter_type { enum efx_filter_match_flags {
EFX_FILTER_TCP_FULL = 0, EFX_FILTER_MATCH_REM_HOST = 0x0001,
EFX_FILTER_TCP_WILD, EFX_FILTER_MATCH_LOC_HOST = 0x0002,
EFX_FILTER_UDP_FULL, EFX_FILTER_MATCH_REM_MAC = 0x0004,
EFX_FILTER_UDP_WILD, EFX_FILTER_MATCH_REM_PORT = 0x0008,
EFX_FILTER_MAC_FULL = 4, EFX_FILTER_MATCH_LOC_MAC = 0x0010,
EFX_FILTER_MAC_WILD, EFX_FILTER_MATCH_LOC_PORT = 0x0020,
EFX_FILTER_UC_DEF = 8, EFX_FILTER_MATCH_ETHER_TYPE = 0x0040,
EFX_FILTER_MC_DEF, EFX_FILTER_MATCH_INNER_VID = 0x0080,
EFX_FILTER_TYPE_COUNT, /* number of specific types */ EFX_FILTER_MATCH_OUTER_VID = 0x0100,
EFX_FILTER_UNSPEC = 0xf, EFX_FILTER_MATCH_IP_PROTO = 0x0200,
EFX_FILTER_MATCH_LOC_MAC_IG = 0x0400,
}; };
/** /**
...@@ -73,29 +90,55 @@ enum efx_filter_flags { ...@@ -73,29 +90,55 @@ enum efx_filter_flags {
/** /**
* struct efx_filter_spec - specification for a hardware filter * struct efx_filter_spec - specification for a hardware filter
* @type: Type of match to be performed, from &enum efx_filter_type * @match_flags: Match type flags, from &enum efx_filter_match_flags
* @priority: Priority of the filter, from &enum efx_filter_priority * @priority: Priority of the filter, from &enum efx_filter_priority
* @flags: Miscellaneous flags, from &enum efx_filter_flags * @flags: Miscellaneous flags, from &enum efx_filter_flags
* @rss_context: RSS context to use, if %EFX_FILTER_FLAG_RX_RSS is set
* @dmaq_id: Source/target queue index, or %EFX_FILTER_RX_DMAQ_ID_DROP for * @dmaq_id: Source/target queue index, or %EFX_FILTER_RX_DMAQ_ID_DROP for
* an RX drop filter * an RX drop filter
* @data: Match data (type-dependent) * @outer_vid: Outer VLAN ID to match, if %EFX_FILTER_MATCH_OUTER_VID is set
* @inner_vid: Inner VLAN ID to match, if %EFX_FILTER_MATCH_INNER_VID is set
* @loc_mac: Local MAC address to match, if %EFX_FILTER_MATCH_LOC_MAC or
* %EFX_FILTER_MATCH_LOC_MAC_IG is set
* @rem_mac: Remote MAC address to match, if %EFX_FILTER_MATCH_REM_MAC is set
* @ether_type: Ether-type to match, if %EFX_FILTER_MATCH_ETHER_TYPE is set
* @ip_proto: IP transport protocol to match, if %EFX_FILTER_MATCH_IP_PROTO
* is set
* @loc_host: Local IP host to match, if %EFX_FILTER_MATCH_LOC_HOST is set
* @rem_host: Remote IP host to match, if %EFX_FILTER_MATCH_REM_HOST is set
* @loc_port: Local TCP/UDP port to match, if %EFX_FILTER_MATCH_LOC_PORT is set
* @rem_port: Remote TCP/UDP port to match, if %EFX_FILTER_MATCH_REM_PORT is set
* *
* Use the efx_filter_set_*() functions to initialise the @type and * The efx_filter_init_rx() or efx_filter_init_tx() function *must* be
* @data fields. * used to initialise the structure. The efx_filter_set_*() functions
* may then be used to set @rss_context, @match_flags and related
* fields.
* *
* The @priority field is used by software to determine whether a new * The @priority field is used by software to determine whether a new
* filter may replace an old one. The hardware priority of a filter * filter may replace an old one. The hardware priority of a filter
* depends on the filter type. * depends on which fields are matched.
*/ */
struct efx_filter_spec { struct efx_filter_spec {
u8 type:4; u32 match_flags:12;
u8 priority:4; u32 priority:2;
u8 flags; u32 flags:6;
u16 dmaq_id; u32 dmaq_id:12;
u32 data[3]; u32 rss_context;
__be16 outer_vid __aligned(4); /* allow jhash2() of match values */
__be16 inner_vid;
u8 loc_mac[ETH_ALEN];
u8 rem_mac[ETH_ALEN];
__be16 ether_type;
u8 ip_proto;
__be32 loc_host[4];
__be32 rem_host[4];
__be16 loc_port;
__be16 rem_port;
/* total 64 bytes */
}; };
enum { enum {
EFX_FILTER_RSS_CONTEXT_DEFAULT = 0xffffffff,
EFX_FILTER_RX_DMAQ_ID_DROP = 0xfff EFX_FILTER_RX_DMAQ_ID_DROP = 0xfff
}; };
...@@ -104,39 +147,116 @@ static inline void efx_filter_init_rx(struct efx_filter_spec *spec, ...@@ -104,39 +147,116 @@ static inline void efx_filter_init_rx(struct efx_filter_spec *spec,
enum efx_filter_flags flags, enum efx_filter_flags flags,
unsigned rxq_id) unsigned rxq_id)
{ {
spec->type = EFX_FILTER_UNSPEC; memset(spec, 0, sizeof(*spec));
spec->priority = priority; spec->priority = priority;
spec->flags = EFX_FILTER_FLAG_RX | flags; spec->flags = EFX_FILTER_FLAG_RX | flags;
spec->rss_context = EFX_FILTER_RSS_CONTEXT_DEFAULT;
spec->dmaq_id = rxq_id; spec->dmaq_id = rxq_id;
} }
static inline void efx_filter_init_tx(struct efx_filter_spec *spec, static inline void efx_filter_init_tx(struct efx_filter_spec *spec,
unsigned txq_id) unsigned txq_id)
{ {
spec->type = EFX_FILTER_UNSPEC; memset(spec, 0, sizeof(*spec));
spec->priority = EFX_FILTER_PRI_REQUIRED; spec->priority = EFX_FILTER_PRI_REQUIRED;
spec->flags = EFX_FILTER_FLAG_TX; spec->flags = EFX_FILTER_FLAG_TX;
spec->dmaq_id = txq_id; spec->dmaq_id = txq_id;
} }
extern int efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto, /**
__be32 host, __be16 port); * efx_filter_set_ipv4_local - specify IPv4 host, transport protocol and port
extern int efx_filter_get_ipv4_local(const struct efx_filter_spec *spec, * @spec: Specification to initialise
u8 *proto, __be32 *host, __be16 *port); * @proto: Transport layer protocol number
extern int efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto, * @host: Local host address (network byte order)
__be32 host, __be16 port, * @port: Local port (network byte order)
__be32 rhost, __be16 rport); */
extern int efx_filter_get_ipv4_full(const struct efx_filter_spec *spec, static inline int
u8 *proto, __be32 *host, __be16 *port, efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto,
__be32 *rhost, __be16 *rport); __be32 host, __be16 port)
extern int efx_filter_set_eth_local(struct efx_filter_spec *spec, {
u16 vid, const u8 *addr); spec->match_flags |=
extern int efx_filter_get_eth_local(const struct efx_filter_spec *spec, EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
u16 *vid, u8 *addr); EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
extern int efx_filter_set_uc_def(struct efx_filter_spec *spec); spec->ether_type = htons(ETH_P_IP);
extern int efx_filter_set_mc_def(struct efx_filter_spec *spec); spec->ip_proto = proto;
spec->loc_host[0] = host;
spec->loc_port = port;
return 0;
}
/**
* efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports
* @spec: Specification to initialise
* @proto: Transport layer protocol number
* @lhost: Local host address (network byte order)
* @lport: Local port (network byte order)
* @rhost: Remote host address (network byte order)
* @rport: Remote port (network byte order)
*/
static inline int
efx_filter_set_ipv4_full(struct efx_filter_spec *spec, u8 proto,
__be32 lhost, __be16 lport,
__be32 rhost, __be16 rport)
{
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 = htons(ETH_P_IP);
spec->ip_proto = proto;
spec->loc_host[0] = lhost;
spec->loc_port = lport;
spec->rem_host[0] = rhost;
spec->rem_port = rport;
return 0;
}
enum { enum {
EFX_FILTER_VID_UNSPEC = 0xffff, EFX_FILTER_VID_UNSPEC = 0xffff,
}; };
/**
* efx_filter_set_eth_local - specify local Ethernet address and/or VID
* @spec: Specification to initialise
* @vid: Outer VLAN ID to match, or %EFX_FILTER_VID_UNSPEC
* @addr: Local Ethernet MAC address, or %NULL
*/
static inline int efx_filter_set_eth_local(struct efx_filter_spec *spec,
u16 vid, const u8 *addr)
{
if (vid == EFX_FILTER_VID_UNSPEC && addr == NULL)
return -EINVAL;
if (vid != EFX_FILTER_VID_UNSPEC) {
spec->match_flags |= EFX_FILTER_MATCH_OUTER_VID;
spec->outer_vid = htons(vid);
}
if (addr != NULL) {
spec->match_flags |= EFX_FILTER_MATCH_LOC_MAC;
memcpy(spec->loc_mac, addr, ETH_ALEN);
}
return 0;
}
/**
* efx_filter_set_uc_def - specify matching otherwise-unmatched unicast
* @spec: Specification to initialise
*/
static inline int efx_filter_set_uc_def(struct efx_filter_spec *spec)
{
spec->match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG;
return 0;
}
/**
* efx_filter_set_mc_def - specify matching otherwise-unmatched multicast
* @spec: Specification to initialise
*/
static inline int efx_filter_set_mc_def(struct efx_filter_spec *spec)
{
spec->match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG;
spec->loc_mac[0] = 1;
return 0;
}
#endif /* EFX_FILTER_H */ #endif /* EFX_FILTER_H */
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