Skip to content

Commit

Permalink
sfc: support cascaded multicast filters
Browse files Browse the repository at this point in the history
If the workaround to support cascaded multicast filters ("workaround_26807") is
enabled, the broadcast filter and individual multicast filters are not inserted
when in promiscuous or allmulti mode.

There is a race while inserting and removing filters when entering and leaving
promiscuous mode.  When changing promiscuous state with cascaded multicast
filters, the old multicast filters are removed before inserting the new filters
to avoid duplicating packets; this can lead to dropped packets until all
filters have been inserted.

The efx_nic:mc_promisc flag is added to record the presence of a multicast
promiscuous filter; this gives a simple way to tell if the promiscuous state is
changing.

Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Daniel Pieczko authored and David S. Miller committed Jul 22, 2015
1 parent 822b96f commit ab8b1f7
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 14 deletions.
56 changes: 42 additions & 14 deletions drivers/net/ethernet/sfc/ef10.c
Original file line number Diff line number Diff line change
Expand Up @@ -3792,26 +3792,42 @@ static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx, bool *promisc)
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++;
}
}

Expand Down Expand Up @@ -3846,7 +3862,11 @@ static void efx_ef10_filter_insert_addr_list(struct efx_nic *efx,
* 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(
Expand Down Expand Up @@ -3974,6 +3994,7 @@ static int efx_ef10_vport_set_mac_address(struct efx_nic *efx)
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;

Expand All @@ -3995,9 +4016,16 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)

/* 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)
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/sfc/net_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,7 @@ struct vfdi_status;
* @stats_lock: Statistics update lock. Must be held when calling
* efx_nic_type::{update,start,stop}_stats.
* @n_rx_noskb_drops: Count of RX packets dropped due to failure to allocate an skb
* @mc_promisc: Whether in multicast promiscuous mode when last changed
*
* This is stored in the private area of the &struct net_device.
*/
Expand Down Expand Up @@ -1072,6 +1073,7 @@ struct efx_nic {
int last_irq_cpu;
spinlock_t stats_lock;
atomic_t n_rx_noskb_drops;
bool mc_promisc;
};

static inline int efx_dev_registered(struct efx_nic *efx)
Expand Down

0 comments on commit ab8b1f7

Please sign in to comment.