]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/sfc/ef10.c
sfc: Forget filter ID when the filter is marked old
[karo-tx-linux.git] / drivers / net / ethernet / sfc / ef10.c
index 1681084cc96f8270d36e55d9ede099a58632566a..120a4b73726a049a89adef040260c69ebcd69f42 100644 (file)
@@ -83,6 +83,8 @@ struct efx_ef10_filter_table {
        u16 ucdef_id;
        u16 bcast_id;
        u16 mcdef_id;
+/* Whether in multicast promiscuous mode when last changed */
+       bool mc_promisc_last;
 };
 
 /* An arbitrary search limit for the software hash table */
@@ -619,6 +621,17 @@ fail:
        return rc;
 }
 
+static void efx_ef10_forget_old_piobufs(struct efx_nic *efx)
+{
+       struct efx_channel *channel;
+       struct efx_tx_queue *tx_queue;
+
+       /* All our existing PIO buffers went away */
+       efx_for_each_channel(channel, efx)
+               efx_for_each_channel_tx_queue(tx_queue, channel)
+                       tx_queue->piobuf = NULL;
+}
+
 #else /* !EFX_USE_PIO */
 
 static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
@@ -635,6 +648,10 @@ static void efx_ef10_free_piobufs(struct efx_nic *efx)
 {
 }
 
+static void efx_ef10_forget_old_piobufs(struct efx_nic *efx)
+{
+}
+
 #endif /* EFX_USE_PIO */
 
 static void efx_ef10_remove(struct efx_nic *efx)
@@ -1018,6 +1035,7 @@ static void efx_ef10_reset_mc_allocations(struct efx_nic *efx)
        nic_data->must_realloc_vis = true;
        nic_data->must_restore_filters = true;
        nic_data->must_restore_piobufs = true;
+       efx_ef10_forget_old_piobufs(efx);
        nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
 
        /* Driver-created vswitches and vports must be re-created */
@@ -3714,9 +3732,16 @@ static int efx_ef10_filter_table_probe(struct efx_nic *efx)
        MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PARSER_DISP_INFO_OUT_LENMAX);
        unsigned int pd_match_pri, pd_match_count;
        struct efx_ef10_filter_table *table;
+       unsigned int i;
        size_t outlen;
        int rc;
 
+       if (!efx_rwsem_assert_write_locked(&efx->filter_sem))
+               return -EINVAL;
+
+       if (efx->filter_state) /* already probed */
+               return 0;
+
        table = kzalloc(sizeof(*table), GFP_KERNEL);
        if (!table)
                return -ENOMEM;
@@ -3759,9 +3784,14 @@ static int efx_ef10_filter_table_probe(struct efx_nic *efx)
                goto fail;
        }
 
+       for (i = 0; i < ARRAY_SIZE(table->dev_uc_list); i++)
+               table->dev_uc_list[i].id = EFX_EF10_FILTER_ID_INVALID;
+       for (i = 0; i < ARRAY_SIZE(table->dev_mc_list); i++)
+               table->dev_mc_list[i].id = EFX_EF10_FILTER_ID_INVALID;
        table->ucdef_id = EFX_EF10_FILTER_ID_INVALID;
        table->bcast_id = EFX_EF10_FILTER_ID_INVALID;
        table->mcdef_id = EFX_EF10_FILTER_ID_INVALID;
+       table->mc_promisc_last = false;
 
        efx->filter_state = table;
        init_waitqueue_head(&table->waitq);
@@ -3827,7 +3857,6 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx)
                nic_data->must_restore_filters = false;
 }
 
-/* Caller must hold efx->filter_sem for write */
 static void efx_ef10_filter_table_remove(struct efx_nic *efx)
 {
        struct efx_ef10_filter_table *table = efx->filter_state;
@@ -3837,6 +3866,15 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
        int rc;
 
        efx->filter_state = NULL;
+       /* If we were called without locking, then it's not safe to free
+        * the table as others might be using it.  So we just WARN, leak
+        * the memory, and potentially get an inconsistent filter table
+        * state.
+        * This should never actually happen.
+        */
+       if (!efx_rwsem_assert_write_locked(&efx->filter_sem))
+               return;
+
        if (!table)
                return;
 
@@ -3864,19 +3902,26 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
        kfree(table);
 }
 
-#define EFX_EF10_FILTER_DO_MARK_OLD(id) \
-       if (id != EFX_EF10_FILTER_ID_INVALID) { \
-               filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \
-               if (!table->entry[filter_idx].spec) \
-                       netif_dbg(efx, drv, efx->net_dev, \
-                                 "%s: marked null spec old %04x:%04x\n", \
-                                 __func__, id, filter_idx); \
-               table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;\
+static void efx_ef10_filter_mark_one_old(struct efx_nic *efx, uint16_t *id)
+{
+       struct efx_ef10_filter_table *table = efx->filter_state;
+       unsigned int filter_idx;
+
+       if (*id != EFX_EF10_FILTER_ID_INVALID) {
+               filter_idx = efx_ef10_filter_get_unsafe_id(efx, *id);
+               if (!table->entry[filter_idx].spec)
+                       netif_dbg(efx, drv, efx->net_dev,
+                                 "marked null spec old %04x:%04x\n", *id,
+                                 filter_idx);
+               table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;
+               *id = EFX_EF10_FILTER_ID_INVALID;
        }
+}
+
 static void efx_ef10_filter_mark_old(struct efx_nic *efx)
 {
        struct efx_ef10_filter_table *table = efx->filter_state;
-       unsigned int filter_idx, i;
+       unsigned int i;
 
        if (!table)
                return;
@@ -3884,15 +3929,14 @@ static void efx_ef10_filter_mark_old(struct efx_nic *efx)
        /* Mark old filters that may need to be removed */
        spin_lock_bh(&efx->filter_lock);
        for (i = 0; i < table->dev_uc_count; i++)
-               EFX_EF10_FILTER_DO_MARK_OLD(table->dev_uc_list[i].id);
+               efx_ef10_filter_mark_one_old(efx, &table->dev_uc_list[i].id);
        for (i = 0; i < table->dev_mc_count; i++)
-               EFX_EF10_FILTER_DO_MARK_OLD(table->dev_mc_list[i].id);
-       EFX_EF10_FILTER_DO_MARK_OLD(table->ucdef_id);
-       EFX_EF10_FILTER_DO_MARK_OLD(table->bcast_id);
-       EFX_EF10_FILTER_DO_MARK_OLD(table->mcdef_id);
+               efx_ef10_filter_mark_one_old(efx, &table->dev_mc_list[i].id);
+       efx_ef10_filter_mark_one_old(efx, &table->ucdef_id);
+       efx_ef10_filter_mark_one_old(efx, &table->bcast_id);
+       efx_ef10_filter_mark_one_old(efx, &table->mcdef_id);
        spin_unlock_bh(&efx->filter_lock);
 }
-#undef EFX_EF10_FILTER_DO_MARK_OLD
 
 static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx, bool *promisc)
 {
@@ -3902,7 +3946,6 @@ static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx, bool *promisc)
        int addr_count;
        unsigned int i;
 
-       table->ucdef_id = EFX_EF10_FILTER_ID_INVALID;
        addr_count = netdev_uc_count(net_dev);
        if (net_dev->flags & IFF_PROMISC)
                *promisc = true;
@@ -3915,7 +3958,6 @@ static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx, bool *promisc)
                        break;
                }
                ether_addr_copy(table->dev_uc_list[i].addr, uc->addr);
-               table->dev_uc_list[i].id = EFX_EF10_FILTER_ID_INVALID;
                i++;
        }
 }
@@ -3927,8 +3969,6 @@ static void efx_ef10_filter_mc_addr_list(struct efx_nic *efx, bool *promisc)
        struct netdev_hw_addr *mc;
        unsigned int i, addr_count;
 
-       table->mcdef_id = EFX_EF10_FILTER_ID_INVALID;
-       table->bcast_id = EFX_EF10_FILTER_ID_INVALID;
        if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI))
                *promisc = true;
 
@@ -3940,7 +3980,6 @@ static void efx_ef10_filter_mc_addr_list(struct efx_nic *efx, bool *promisc)
                        break;
                }
                ether_addr_copy(table->dev_mc_list[i].addr, mc->addr);
-               table->dev_mc_list[i].id = EFX_EF10_FILTER_ID_INVALID;
                i++;
        }
 
@@ -4018,6 +4057,8 @@ static int efx_ef10_filter_insert_addr_list(struct efx_nic *efx,
                        }
                        return rc;
                } else {
+                       EFX_WARN_ON_PARANOID(table->bcast_id !=
+                                            EFX_EF10_FILTER_ID_INVALID);
                        table->bcast_id = efx_ef10_filter_get_unsafe_id(efx, rc);
                }
        }
@@ -4051,6 +4092,8 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
                             "%scast mismatch filter insert failed rc=%d\n",
                             multicast ? "Multi" : "Uni", rc);
        } else if (multicast) {
+               EFX_WARN_ON_PARANOID(table->mcdef_id !=
+                                    EFX_EF10_FILTER_ID_INVALID);
                table->mcdef_id = efx_ef10_filter_get_unsafe_id(efx, rc);
                if (!nic_data->workaround_26807) {
                        /* Also need an Ethernet broadcast filter */
@@ -4073,11 +4116,14 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
                                        return rc;
                                }
                        } else {
+                               EFX_WARN_ON_PARANOID(table->bcast_id !=
+                                                    EFX_EF10_FILTER_ID_INVALID);
                                table->bcast_id = efx_ef10_filter_get_unsafe_id(efx, rc);
                        }
                }
                rc = 0;
        } else {
+               EFX_WARN_ON_PARANOID(table->ucdef_id != EFX_EF10_FILTER_ID_INVALID);
                table->ucdef_id = rc;
                rc = 0;
        }
@@ -4227,7 +4273,7 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
        /* If changing promiscuous state with cascaded multicast filters, remove
         * old filters first, so that packets are dropped rather than duplicated
         */
-       if (nic_data->workaround_26807 && efx->mc_promisc != mc_promisc)
+       if (nic_data->workaround_26807 && table->mc_promisc_last != mc_promisc)
                efx_ef10_filter_remove_old(efx);
        if (mc_promisc) {
                if (nic_data->workaround_26807) {
@@ -4262,7 +4308,7 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
        }
 
        efx_ef10_filter_remove_old(efx);
-       efx->mc_promisc = mc_promisc;
+       table->mc_promisc_last = mc_promisc;
 }
 
 static int efx_ef10_set_mac_address(struct efx_nic *efx)
@@ -4687,6 +4733,12 @@ static int efx_ef10_ptp_set_ts_config(struct efx_nic *efx,
        }
 }
 
+#define EF10_OFFLOAD_FEATURES          \
+       (NETIF_F_IP_CSUM |              \
+        NETIF_F_IPV6_CSUM |            \
+        NETIF_F_RXHASH |               \
+        NETIF_F_NTUPLE)
+
 const struct efx_nic_type efx_hunt_a0_vf_nic_type = {
        .is_vf = true,
        .mem_bar = EFX_MEM_VF_BAR,
@@ -4782,8 +4834,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = {
        .always_rx_scatter = true,
        .max_interrupt_mode = EFX_INT_MODE_MSIX,
        .timer_period_max = 1 << ERF_DD_EVQ_IND_TIMER_VAL_WIDTH,
-       .offload_features = (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
-                            NETIF_F_RXHASH | NETIF_F_NTUPLE),
+       .offload_features = EF10_OFFLOAD_FEATURES,
        .mcdi_max_ver = 2,
        .max_rx_ip_filters = HUNT_FILTER_TBL_ROWS,
        .hwtstamp_filters = 1 << HWTSTAMP_FILTER_NONE |
@@ -4903,8 +4954,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
        .always_rx_scatter = true,
        .max_interrupt_mode = EFX_INT_MODE_MSIX,
        .timer_period_max = 1 << ERF_DD_EVQ_IND_TIMER_VAL_WIDTH,
-       .offload_features = (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
-                            NETIF_F_RXHASH | NETIF_F_NTUPLE),
+       .offload_features = EF10_OFFLOAD_FEATURES,
        .mcdi_max_ver = 2,
        .max_rx_ip_filters = HUNT_FILTER_TBL_ROWS,
        .hwtstamp_filters = 1 << HWTSTAMP_FILTER_NONE |