Merge branch 'slab/for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/penber...
[cascardo/linux.git] / drivers / net / ethernet / sfc / filter.c
index 1fbbbee..fea7f73 100644 (file)
 enum efx_filter_table_id {
        EFX_FILTER_TABLE_RX_IP = 0,
        EFX_FILTER_TABLE_RX_MAC,
+       EFX_FILTER_TABLE_RX_DEF,
+       EFX_FILTER_TABLE_TX_MAC,
        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 */
@@ -90,8 +98,9 @@ efx_filter_spec_table_id(const struct efx_filter_spec *spec)
        BUILD_BUG_ON(EFX_FILTER_TABLE_RX_IP != (EFX_FILTER_UDP_WILD >> 2));
        BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_MAC_FULL >> 2));
        BUILD_BUG_ON(EFX_FILTER_TABLE_RX_MAC != (EFX_FILTER_MAC_WILD >> 2));
+       BUILD_BUG_ON(EFX_FILTER_TABLE_TX_MAC != EFX_FILTER_TABLE_RX_MAC + 2);
        EFX_BUG_ON_PARANOID(spec->type == EFX_FILTER_UNSPEC);
-       return spec->type >> 2;
+       return (spec->type >> 2) + ((spec->flags & EFX_FILTER_FLAG_TX) ? 2 : 0);
 }
 
 static struct efx_filter_table *
@@ -109,7 +118,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,9 +152,58 @@ 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);
 }
 
+static void efx_filter_push_tx_limits(struct efx_nic *efx)
+{
+       struct efx_filter_state *state = efx->filter_state;
+       struct efx_filter_table *table;
+       efx_oword_t tx_cfg;
+
+       efx_reado(efx, &tx_cfg, FR_AZ_TX_CFG);
+
+       table = &state->table[EFX_FILTER_TABLE_TX_MAC];
+       if (table->size) {
+               EFX_SET_OWORD_FIELD(
+                       tx_cfg, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
+                       table->search_depth[EFX_FILTER_MAC_FULL] +
+                       FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(
+                       tx_cfg, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
+                       table->search_depth[EFX_FILTER_MAC_WILD] +
+                       FILTER_CTL_SRCH_FUDGE_WILD);
+       }
+
+       efx_writeo(efx, &tx_cfg, FR_AZ_TX_CFG);
+}
+
 static inline void __efx_filter_set_ipv4(struct efx_filter_spec *spec,
                                         __be32 host1, __be16 port1,
                                         __be32 host2, __be16 port2)
@@ -300,7 +358,8 @@ int efx_filter_get_ipv4_full(const struct efx_filter_spec *spec,
 int efx_filter_set_eth_local(struct efx_filter_spec *spec,
                             u16 vid, const u8 *addr)
 {
-       EFX_BUG_ON_PARANOID(!(spec->flags & EFX_FILTER_FLAG_RX));
+       EFX_BUG_ON_PARANOID(!(spec->flags &
+                             (EFX_FILTER_FLAG_RX | EFX_FILTER_FLAG_TX)));
 
        /* This cannot currently be combined with other filtering */
        if (spec->type != EFX_FILTER_UNSPEC)
@@ -319,6 +378,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 +471,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(
@@ -385,6 +497,18 @@ static u32 efx_filter_build(efx_oword_t *filter, struct efx_filter_spec *spec)
                break;
        }
 
+       case EFX_FILTER_TABLE_TX_MAC: {
+               bool is_wild = spec->type == EFX_FILTER_MAC_WILD;
+               EFX_POPULATE_OWORD_5(*filter,
+                                    FRF_CZ_TMFT_TXQ_ID, spec->dmaq_id,
+                                    FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
+                                    FRF_CZ_TMFT_SRC_MAC_HI, spec->data[2],
+                                    FRF_CZ_TMFT_SRC_MAC_LO, spec->data[1],
+                                    FRF_CZ_TMFT_VLAN_ID, spec->data[0]);
+               data3 = is_wild | spec->dmaq_id << 1;
+               break;
+       }
+
        default:
                BUG();
        }
@@ -399,6 +523,10 @@ static bool efx_filter_equal(const struct efx_filter_spec *left,
            memcmp(left->data, right->data, sizeof(left->data)))
                return false;
 
+       if (left->flags & EFX_FILTER_FLAG_TX &&
+           left->dmaq_id != right->dmaq_id)
+               return false;
+
        return true;
 }
 
@@ -448,23 +576,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 +619,30 @@ 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 if (match_pri <=
+                EFX_FILTER_MATCH_PRI_NORMAL_BASE + EFX_FILTER_TABLE_RX_DEF)
+               return EFX_FILTER_FLAG_RX;
+       else
+               return EFX_FILTER_FLAG_TX;
 }
 
 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 +700,20 @@ 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;
+                       if (spec->flags & EFX_FILTER_FLAG_TX)
+                               efx_filter_push_tx_limits(efx);
+                       else
+                               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 +731,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 +781,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 +833,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 +888,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 +916,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 +951,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 +965,8 @@ void efx_restore_filters(struct efx_nic *efx)
                }
        }
 
-       efx_filter_push_rx_limits(efx);
+       efx_filter_push_rx_config(efx);
+       efx_filter_push_tx_limits(efx);
 
        spin_unlock_bh(&state->lock);
 }
@@ -833,6 +1005,16 @@ 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;
+
+               table = &state->table[EFX_FILTER_TABLE_TX_MAC];
+               table->id = EFX_FILTER_TABLE_TX_MAC;
+               table->offset = FR_CZ_TX_MAC_FILTER_TBL0;
+               table->size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS;
+               table->step = FR_CZ_TX_MAC_FILTER_TBL0_STEP;
        }
 
        for (table_id = 0; table_id < EFX_FILTER_TABLE_COUNT; table_id++) {
@@ -849,6 +1031,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: