summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c59
-rw-r--r--drivers/net/ethernet/sfc/filter.c196
-rw-r--r--drivers/net/ethernet/sfc/filter.h6
3 files changed, 214 insertions, 47 deletions
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index f887f65e418..83191151b65 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -808,11 +808,16 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
return efx_reset(efx, rc);
}
+/* MAC address mask including only MC flag */
+static const u8 mac_addr_mc_mask[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
+
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 ethhdr *mac_entry = &rule->h_u.ether_spec;
+ struct ethhdr *mac_mask = &rule->m_u.ether_spec;
struct efx_filter_spec spec;
u16 vid;
u8 proto;
@@ -828,11 +833,18 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
else
rule->ring_cookie = spec.dmaq_id;
- rc = efx_filter_get_eth_local(&spec, &vid,
- rule->h_u.ether_spec.h_dest);
+ if (spec.type == EFX_FILTER_MC_DEF || spec.type == EFX_FILTER_UC_DEF) {
+ rule->flow_type = ETHER_FLOW;
+ memcpy(mac_mask->h_dest, mac_addr_mc_mask, ETH_ALEN);
+ if (spec.type == EFX_FILTER_MC_DEF)
+ memcpy(mac_entry->h_dest, mac_addr_mc_mask, ETH_ALEN);
+ return 0;
+ }
+
+ rc = efx_filter_get_eth_local(&spec, &vid, mac_entry->h_dest);
if (rc == 0) {
rule->flow_type = ETHER_FLOW;
- memset(rule->m_u.ether_spec.h_dest, ~0, ETH_ALEN);
+ memset(mac_mask->h_dest, ~0, ETH_ALEN);
if (vid != EFX_FILTER_VID_UNSPEC) {
rule->flow_type |= FLOW_EXT;
rule->h_ext.vlan_tci = htons(vid);
@@ -1001,27 +1013,40 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx,
}
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;
- case ETHER_FLOW:
- /* Must match all of destination */
- if (!is_broadcast_ether_addr(mac_mask->h_dest))
- return -EINVAL;
- /* and nothing else */
+ 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;
- rc = efx_filter_set_eth_local(
- &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);
+ /* Is it a default UC or MC filter? */
+ if (!compare_ether_addr(mac_mask->h_dest, mac_addr_mc_mask) &&
+ vlan_tag_mask == 0) {
+ if (is_multicast_ether_addr(mac_entry->h_dest))
+ rc = efx_filter_set_mc_def(&spec);
+ else
+ rc = efx_filter_set_uc_def(&spec);
+ }
+ /* Otherwise, it must match all of destination and all
+ * or none of VID.
+ */
+ else if (is_broadcast_ether_addr(mac_mask->h_dest) &&
+ (vlan_tag_mask == 0xfff || vlan_tag_mask == 0)) {
+ 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)
return rc;
break;
+ }
default:
return -EINVAL;
diff --git a/drivers/net/ethernet/sfc/filter.c b/drivers/net/ethernet/sfc/filter.c
index 1fbbbee7b1a..92517ea4271 100644
--- a/drivers/net/ethernet/sfc/filter.c
+++ b/drivers/net/ethernet/sfc/filter.c
@@ -35,9 +35,16 @@
enum efx_filter_table_id {
EFX_FILTER_TABLE_RX_IP = 0,
EFX_FILTER_TABLE_RX_MAC,
+ EFX_FILTER_TABLE_RX_DEF,
EFX_FILTER_TABLE_COUNT,
};
+enum efx_filter_index {
+ EFX_FILTER_INDEX_UC_DEF,
+ EFX_FILTER_INDEX_MC_DEF,
+ EFX_FILTER_SIZE_RX_DEF,
+};
+
struct efx_filter_table {
enum efx_filter_table_id id;
u32 offset; /* address of table relative to BAR */
@@ -109,7 +116,7 @@ static void efx_filter_table_reset_search_depth(struct efx_filter_table *table)
memset(table->search_depth, 0, sizeof(table->search_depth));
}
-static void efx_filter_push_rx_limits(struct efx_nic *efx)
+static void efx_filter_push_rx_config(struct efx_nic *efx)
{
struct efx_filter_state *state = efx->filter_state;
struct efx_filter_table *table;
@@ -143,6 +150,32 @@ static void efx_filter_push_rx_limits(struct efx_nic *efx)
FILTER_CTL_SRCH_FUDGE_WILD);
}
+ table = &state->table[EFX_FILTER_TABLE_RX_DEF];
+ if (table->size) {
+ EFX_SET_OWORD_FIELD(
+ filter_ctl, FRF_CZ_UNICAST_NOMATCH_Q_ID,
+ table->spec[EFX_FILTER_INDEX_UC_DEF].dmaq_id);
+ EFX_SET_OWORD_FIELD(
+ filter_ctl, FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED,
+ !!(table->spec[EFX_FILTER_INDEX_UC_DEF].flags &
+ EFX_FILTER_FLAG_RX_RSS));
+ EFX_SET_OWORD_FIELD(
+ filter_ctl, FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE,
+ !!(table->spec[EFX_FILTER_INDEX_UC_DEF].flags &
+ EFX_FILTER_FLAG_RX_OVERRIDE_IP));
+ EFX_SET_OWORD_FIELD(
+ filter_ctl, FRF_CZ_MULTICAST_NOMATCH_Q_ID,
+ table->spec[EFX_FILTER_INDEX_MC_DEF].dmaq_id);
+ EFX_SET_OWORD_FIELD(
+ filter_ctl, FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED,
+ !!(table->spec[EFX_FILTER_INDEX_MC_DEF].flags &
+ EFX_FILTER_FLAG_RX_RSS));
+ EFX_SET_OWORD_FIELD(
+ filter_ctl, FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE,
+ !!(table->spec[EFX_FILTER_INDEX_MC_DEF].flags &
+ EFX_FILTER_FLAG_RX_OVERRIDE_IP));
+ }
+
efx_writeo(efx, &filter_ctl, FR_BZ_RX_FILTER_CTL);
}
@@ -319,6 +352,52 @@ int efx_filter_set_eth_local(struct efx_filter_spec *spec,
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;
+}
+
+/**
+ * efx_filter_set_mc_def - specify matching otherwise-unmatched multicast
+ * @spec: Specification to initialise
+ */
+int efx_filter_set_mc_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_MC_DEF;
+ memset(spec->data, 0, sizeof(spec->data)); /* ensure equality */
+ return 0;
+}
+
+static void efx_filter_reset_rx_def(struct efx_nic *efx, unsigned filter_idx)
+{
+ struct efx_filter_state *state = efx->filter_state;
+ struct efx_filter_table *table = &state->table[EFX_FILTER_TABLE_RX_DEF];
+ struct efx_filter_spec *spec = &table->spec[filter_idx];
+
+ efx_filter_init_rx(spec, EFX_FILTER_PRI_MANUAL,
+ EFX_FILTER_FLAG_RX_RSS, 0);
+ spec->type = EFX_FILTER_UC_DEF + 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)
{
@@ -366,6 +445,13 @@ static u32 efx_filter_build(efx_oword_t *filter, struct efx_filter_spec *spec)
break;
}
+ case EFX_FILTER_TABLE_RX_DEF:
+ /* One filter spec per type */
+ BUILD_BUG_ON(EFX_FILTER_INDEX_UC_DEF != 0);
+ BUILD_BUG_ON(EFX_FILTER_INDEX_MC_DEF !=
+ EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF);
+ return spec->type - EFX_FILTER_UC_DEF;
+
case EFX_FILTER_TABLE_RX_MAC: {
bool is_wild = spec->type == EFX_FILTER_MAC_WILD;
EFX_POPULATE_OWORD_8(
@@ -448,23 +534,40 @@ static int efx_filter_search(struct efx_filter_table *table,
* MAC filters without overriding behaviour.
*/
+#define EFX_FILTER_MATCH_PRI_RX_MAC_OVERRIDE_IP 0
+#define EFX_FILTER_MATCH_PRI_RX_DEF_OVERRIDE_IP 1
+#define EFX_FILTER_MATCH_PRI_NORMAL_BASE 2
+
#define EFX_FILTER_INDEX_WIDTH 13
#define EFX_FILTER_INDEX_MASK ((1 << EFX_FILTER_INDEX_WIDTH) - 1)
static inline u32 efx_filter_make_id(enum efx_filter_table_id table_id,
unsigned int index, u8 flags)
{
- return (table_id == EFX_FILTER_TABLE_RX_MAC &&
- flags & EFX_FILTER_FLAG_RX_OVERRIDE_IP) ?
- index :
- (table_id + 1) << EFX_FILTER_INDEX_WIDTH | index;
+ unsigned int match_pri = EFX_FILTER_MATCH_PRI_NORMAL_BASE + table_id;
+
+ if (flags & EFX_FILTER_FLAG_RX_OVERRIDE_IP) {
+ if (table_id == EFX_FILTER_TABLE_RX_MAC)
+ match_pri = EFX_FILTER_MATCH_PRI_RX_MAC_OVERRIDE_IP;
+ else if (table_id == EFX_FILTER_TABLE_RX_DEF)
+ match_pri = EFX_FILTER_MATCH_PRI_RX_DEF_OVERRIDE_IP;
+ }
+
+ return match_pri << EFX_FILTER_INDEX_WIDTH | index;
}
static inline enum efx_filter_table_id efx_filter_id_table_id(u32 id)
{
- return (id <= EFX_FILTER_INDEX_MASK) ?
- EFX_FILTER_TABLE_RX_MAC :
- (id >> EFX_FILTER_INDEX_WIDTH) - 1;
+ unsigned int match_pri = id >> EFX_FILTER_INDEX_WIDTH;
+
+ switch (match_pri) {
+ case EFX_FILTER_MATCH_PRI_RX_MAC_OVERRIDE_IP:
+ return EFX_FILTER_TABLE_RX_MAC;
+ case EFX_FILTER_MATCH_PRI_RX_DEF_OVERRIDE_IP:
+ return EFX_FILTER_TABLE_RX_DEF;
+ default:
+ return match_pri - EFX_FILTER_MATCH_PRI_NORMAL_BASE;
+ }
}
static inline unsigned int efx_filter_id_index(u32 id)
@@ -474,23 +577,27 @@ static inline unsigned int efx_filter_id_index(u32 id)
static inline u8 efx_filter_id_flags(u32 id)
{
- return (id <= EFX_FILTER_INDEX_MASK) ?
- EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_RX_OVERRIDE_IP :
- EFX_FILTER_FLAG_RX;
+ unsigned int match_pri = id >> EFX_FILTER_INDEX_WIDTH;
+
+ if (match_pri < EFX_FILTER_MATCH_PRI_NORMAL_BASE)
+ return EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_RX_OVERRIDE_IP;
+ else
+ return EFX_FILTER_FLAG_RX;
}
u32 efx_filter_get_rx_id_limit(struct efx_nic *efx)
{
struct efx_filter_state *state = efx->filter_state;
+ unsigned int table_id = EFX_FILTER_TABLE_RX_DEF;
- if (state->table[EFX_FILTER_TABLE_RX_MAC].size != 0)
- return ((EFX_FILTER_TABLE_RX_MAC + 1) << EFX_FILTER_INDEX_WIDTH)
- + state->table[EFX_FILTER_TABLE_RX_MAC].size;
- else if (state->table[EFX_FILTER_TABLE_RX_IP].size != 0)
- return ((EFX_FILTER_TABLE_RX_IP + 1) << EFX_FILTER_INDEX_WIDTH)
- + state->table[EFX_FILTER_TABLE_RX_IP].size;
- else
- return 0;
+ do {
+ if (state->table[table_id].size != 0)
+ return ((EFX_FILTER_MATCH_PRI_NORMAL_BASE + table_id)
+ << EFX_FILTER_INDEX_WIDTH) +
+ state->table[table_id].size;
+ } while (table_id--);
+
+ return 0;
}
/**
@@ -548,12 +655,17 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec,
}
*saved_spec = *spec;
- if (table->search_depth[spec->type] < depth) {
- table->search_depth[spec->type] = depth;
- efx_filter_push_rx_limits(efx);
- }
+ if (table->id == EFX_FILTER_TABLE_RX_DEF) {
+ efx_filter_push_rx_config(efx);
+ } else {
+ if (table->search_depth[spec->type] < depth) {
+ table->search_depth[spec->type] = depth;
+ efx_filter_push_rx_config(efx);
+ }
- efx_writeo(efx, &filter, table->offset + table->step * filter_idx);
+ efx_writeo(efx, &filter,
+ table->offset + table->step * filter_idx);
+ }
netif_vdbg(efx, hw, efx->net_dev,
"%s: filter type %d index %d rxq %u set",
@@ -571,7 +683,11 @@ static void efx_filter_table_clear_entry(struct efx_nic *efx,
{
static efx_oword_t filter;
- if (test_bit(filter_idx, table->used_bitmap)) {
+ if (table->id == EFX_FILTER_TABLE_RX_DEF) {
+ /* RX default filters must always exist */
+ efx_filter_reset_rx_def(efx, filter_idx);
+ efx_filter_push_rx_config(efx);
+ } else if (test_bit(filter_idx, table->used_bitmap)) {
__clear_bit(filter_idx, table->used_bitmap);
--table->used;
memset(&table->spec[filter_idx], 0, sizeof(table->spec[0]));
@@ -617,7 +733,8 @@ int efx_filter_remove_id_safe(struct efx_nic *efx,
spin_lock_bh(&state->lock);
if (test_bit(filter_idx, table->used_bitmap) &&
- spec->priority == priority && spec->flags == filter_flags) {
+ spec->priority == priority &&
+ !((spec->flags ^ filter_flags) & EFX_FILTER_FLAG_RX_OVERRIDE_IP)) {
efx_filter_table_clear_entry(efx, table, filter_idx);
if (table->used == 0)
efx_filter_table_reset_search_depth(table);
@@ -668,7 +785,8 @@ int efx_filter_get_filter_safe(struct efx_nic *efx,
spin_lock_bh(&state->lock);
if (test_bit(filter_idx, table->used_bitmap) &&
- spec->priority == priority && spec->flags == filter_flags) {
+ spec->priority == priority &&
+ !((spec->flags ^ filter_flags) & EFX_FILTER_FLAG_RX_OVERRIDE_IP)) {
*spec_buf = *spec;
rc = 0;
} else {
@@ -722,7 +840,7 @@ u32 efx_filter_count_rx_used(struct efx_nic *efx,
spin_lock_bh(&state->lock);
for (table_id = EFX_FILTER_TABLE_RX_IP;
- table_id <= EFX_FILTER_TABLE_RX_MAC;
+ table_id <= EFX_FILTER_TABLE_RX_DEF;
table_id++) {
table = &state->table[table_id];
for (filter_idx = 0; filter_idx < table->size; filter_idx++) {
@@ -750,7 +868,7 @@ s32 efx_filter_get_rx_ids(struct efx_nic *efx,
spin_lock_bh(&state->lock);
for (table_id = EFX_FILTER_TABLE_RX_IP;
- table_id <= EFX_FILTER_TABLE_RX_MAC;
+ table_id <= EFX_FILTER_TABLE_RX_DEF;
table_id++) {
table = &state->table[table_id];
for (filter_idx = 0; filter_idx < table->size; filter_idx++) {
@@ -785,6 +903,11 @@ void efx_restore_filters(struct efx_nic *efx)
for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) {
table = &state->table[table_id];
+
+ /* Check whether this is a regular register table */
+ if (table->step == 0)
+ continue;
+
for (filter_idx = 0; filter_idx < table->size; filter_idx++) {
if (!test_bit(filter_idx, table->used_bitmap))
continue;
@@ -794,7 +917,7 @@ void efx_restore_filters(struct efx_nic *efx)
}
}
- efx_filter_push_rx_limits(efx);
+ efx_filter_push_rx_config(efx);
spin_unlock_bh(&state->lock);
}
@@ -833,6 +956,10 @@ int efx_probe_filters(struct efx_nic *efx)
table->offset = FR_CZ_RX_MAC_FILTER_TBL0;
table->size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS;
table->step = FR_CZ_RX_MAC_FILTER_TBL0_STEP;
+
+ table = &state->table[EFX_FILTER_TABLE_RX_DEF];
+ table->id = EFX_FILTER_TABLE_RX_DEF;
+ table->size = EFX_FILTER_SIZE_RX_DEF;
}
for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) {
@@ -849,6 +976,15 @@ int efx_probe_filters(struct efx_nic *efx)
goto fail;
}
+ if (state->table[EFX_FILTER_TABLE_RX_DEF].size) {
+ /* RX default filters must always exist */
+ unsigned i;
+ for (i = 0; i < EFX_FILTER_SIZE_RX_DEF; i++)
+ efx_filter_reset_rx_def(efx, i);
+ }
+
+ efx_filter_push_rx_config(efx);
+
return 0;
fail:
diff --git a/drivers/net/ethernet/sfc/filter.h b/drivers/net/ethernet/sfc/filter.h
index 3d4108cd90c..451997841dc 100644
--- a/drivers/net/ethernet/sfc/filter.h
+++ b/drivers/net/ethernet/sfc/filter.h
@@ -20,6 +20,8 @@
* @EFX_FILTER_UDP_WILD: Matching UDP/IPv4 destination (host, port)
* @EFX_FILTER_MAC_FULL: Matching Ethernet destination MAC address, VID
* @EFX_FILTER_MAC_WILD: Matching Ethernet destination MAC address
+ * @EFX_FILTER_UC_DEF: Matching all otherwise unmatched unicast
+ * @EFX_FILTER_MC_DEF: Matching all otherwise unmatched multicast
* @EFX_FILTER_UNSPEC: Match type is unspecified
*
* Falcon NICs only support the TCP/IPv4 and UDP/IPv4 filter types.
@@ -31,6 +33,8 @@ enum efx_filter_type {
EFX_FILTER_UDP_WILD,
EFX_FILTER_MAC_FULL = 4,
EFX_FILTER_MAC_WILD,
+ EFX_FILTER_UC_DEF = 8,
+ EFX_FILTER_MC_DEF,
EFX_FILTER_TYPE_COUNT, /* number of specific types */
EFX_FILTER_UNSPEC = 0xf,
};
@@ -117,6 +121,8 @@ extern int efx_filter_set_eth_local(struct efx_filter_spec *spec,
u16 vid, const u8 *addr);
extern int efx_filter_get_eth_local(const struct efx_filter_spec *spec,
u16 *vid, u8 *addr);
+extern int efx_filter_set_uc_def(struct efx_filter_spec *spec);
+extern int efx_filter_set_mc_def(struct efx_filter_spec *spec);
enum {
EFX_FILTER_VID_UNSPEC = 0xffff,
};