static void efx_ef10_filter_mc_addr_list(struct efx_nic *efx, bool *promisc)
{
struct efx_ef10_filter_table *table = efx->filter_state;
+ struct efx_ef10_nic_data *nic_data = efx->nic_data;
struct net_device *net_dev = efx->net_dev;
struct netdev_hw_addr *mc;
- unsigned int i;
+ unsigned int i, addr_count;
- if (netdev_mc_count(net_dev) + 2 /* room for broadcast and promisc */
- >= EFX_EF10_FILTER_DEV_MC_MAX) {
- table->dev_mc_count = 1;
- eth_broadcast_addr(table->dev_mc_list[0].addr);
+ if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI))
*promisc = true;
+
+ if (nic_data->workaround_26807) {
+ if (*promisc) {
+ table->dev_mc_count = 0;
+ return;
+ }
+ addr_count = netdev_mc_count(net_dev);
} else {
- table->dev_mc_count = 1 + netdev_mc_count(net_dev);
- eth_broadcast_addr(table->dev_mc_list[0].addr);
- i = 1;
- netdev_for_each_mc_addr(mc, net_dev) {
- ether_addr_copy(table->dev_mc_list[i].addr, mc->addr);
- i++;
+ /* Allow room for broadcast and promiscuous */
+ addr_count = netdev_mc_count(net_dev) + 2;
+ }
+
+ if (addr_count >= EFX_EF10_FILTER_DEV_MC_MAX) {
+ if (nic_data->workaround_26807) {
+ table->dev_mc_count = 0;
+ } else {
+ table->dev_mc_count = 1;
+ eth_broadcast_addr(table->dev_mc_list[0].addr);
}
+ *promisc = true;
+ return;
+ }
- if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI))
- *promisc = true;
+ table->dev_mc_count = 1 + netdev_mc_count(net_dev);
+ eth_broadcast_addr(table->dev_mc_list[0].addr);
+ i = 1;
+ netdev_for_each_mc_addr(mc, net_dev) {
+ ether_addr_copy(table->dev_mc_list[i].addr, mc->addr);
+ i++;
}
}
* filter for multicast
*/
while (i--) {
- if (multicast && i == 1)
+ struct efx_ef10_nic_data *nic_data =
+ efx->nic_data;
+
+ if (multicast && i == 1 &&
+ !nic_data->workaround_26807)
break;
efx_ef10_filter_remove_safe(
static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
+ struct efx_ef10_nic_data *nic_data = efx->nic_data;
struct net_device *net_dev = efx->net_dev;
bool uc_promisc = false, mc_promisc = false;
/* Insert/renew filters */
efx_ef10_filter_insert_addr_list(efx, false, &uc_promisc);
+
+ /* 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)
+ efx_ef10_filter_remove_old(efx);
efx_ef10_filter_insert_addr_list(efx, true, &mc_promisc);
efx_ef10_filter_remove_old(efx);
+ efx->mc_promisc = mc_promisc;
}
static int efx_ef10_set_mac_address(struct efx_nic *efx)