diff options
author | Jon Cooper <jcooper@solarflare.com> | 2015-07-21 17:10:15 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-07-22 08:21:32 +0300 |
commit | b6f568e27b6b214beaa0d57ddb4fcc446fcd5555 (patch) | |
tree | 147cb51a71f5973a1d4aeb3844f1b13da91850ca /drivers/net/ethernet/sfc | |
parent | 5a55a72abe48e5d6c0ec86e7b06cd73ab7d517c8 (diff) |
sfc: Insert multicast filters as well as mismatch filters in promiscuous mode
If a function is in promiscuous mode and another function has a broadcast or
multicast filter inserted, the function in promiscuous mode won't see that
broadcast or multicast traffic.
Most notably this breaks broadcast, which means ARP doesn't work. Less
show-stoppingly, a function listening on a multicast address that's also in
promiscuous mode will not see that multicast traffic if another function is
also listening on that multicast address.
Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/sfc')
-rw-r--r-- | drivers/net/ethernet/sfc/ef10.c | 104 |
1 files changed, 53 insertions, 51 deletions
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 18d6388fb5da..784b46fb039a 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -3758,7 +3758,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) struct netdev_hw_addr *uc; struct netdev_hw_addr *mc; unsigned int filter_idx; - int i, n, rc; + int i, rc; + bool uc_promisc = false, mc_promisc = false; if (!efx_dev_registered(efx)) return; @@ -3768,13 +3769,11 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) /* Mark old filters that may need to be removed */ spin_lock_bh(&efx->filter_lock); - n = table->dev_uc_count < 0 ? 1 : table->dev_uc_count; - for (i = 0; i < n; i++) { + for (i = 0; i < table->dev_uc_count; i++) { filter_idx = table->dev_uc_list[i].id % HUNT_FILTER_TBL_ROWS; table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; } - n = table->dev_mc_count < 0 ? 1 : table->dev_mc_count; - for (i = 0; i < n; i++) { + for (i = 0; i < table->dev_mc_count; i++) { filter_idx = table->dev_mc_list[i].id % HUNT_FILTER_TBL_ROWS; table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; } @@ -3786,7 +3785,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) netif_addr_lock_bh(net_dev); if (net_dev->flags & IFF_PROMISC || netdev_uc_count(net_dev) >= EFX_EF10_FILTER_DEV_UC_MAX) { - table->dev_uc_count = -1; + table->dev_uc_count = 0; + uc_promisc = true; } else { table->dev_uc_count = 1 + netdev_uc_count(net_dev); ether_addr_copy(table->dev_uc_list[0].addr, net_dev->dev_addr); @@ -3796,9 +3796,11 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) i++; } } - if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI) || - netdev_mc_count(net_dev) >= EFX_EF10_FILTER_DEV_MC_MAX) { - table->dev_mc_count = -1; + 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); + mc_promisc = true; } else { table->dev_mc_count = 1 + netdev_mc_count(net_dev); eth_broadcast_addr(table->dev_mc_list[0].addr); @@ -3807,31 +3809,32 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) ether_addr_copy(table->dev_mc_list[i].addr, mc->addr); i++; } + if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) + mc_promisc = true; } netif_addr_unlock_bh(net_dev); /* Insert/renew unicast filters */ - if (table->dev_uc_count >= 0) { - for (i = 0; i < table->dev_uc_count; i++) { - efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, - EFX_FILTER_FLAG_RX_RSS, - 0); - efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, - table->dev_uc_list[i].addr); - rc = efx_ef10_filter_insert(efx, &spec, true); - if (rc < 0) { - /* Fall back to unicast-promisc */ - while (i--) - efx_ef10_filter_remove_safe( - efx, EFX_FILTER_PRI_AUTO, - table->dev_uc_list[i].id); - table->dev_uc_count = -1; - break; - } - table->dev_uc_list[i].id = rc; + for (i = 0; i < table->dev_uc_count; i++) { + efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, + EFX_FILTER_FLAG_RX_RSS, + 0); + efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, + table->dev_uc_list[i].addr); + rc = efx_ef10_filter_insert(efx, &spec, true); + if (rc < 0) { + /* Fall back to unicast-promisc */ + while (i--) + efx_ef10_filter_remove_safe( + efx, EFX_FILTER_PRI_AUTO, + table->dev_uc_list[i].id); + table->dev_uc_count = 0; + uc_promisc = true; + break; } + table->dev_uc_list[i].id = rc; } - if (table->dev_uc_count < 0) { + if (uc_promisc) { efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, EFX_FILTER_FLAG_RX_RSS, 0); @@ -3839,34 +3842,34 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) { WARN_ON(1); - table->dev_uc_count = 0; } else { - table->dev_uc_list[0].id = rc; + table->dev_uc_list[table->dev_uc_count++].id = rc; } } /* Insert/renew multicast filters */ - if (table->dev_mc_count >= 0) { - for (i = 0; i < table->dev_mc_count; i++) { - efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, - EFX_FILTER_FLAG_RX_RSS, - 0); - efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, - table->dev_mc_list[i].addr); - rc = efx_ef10_filter_insert(efx, &spec, true); - if (rc < 0) { - /* Fall back to multicast-promisc */ - while (i--) - efx_ef10_filter_remove_safe( - efx, EFX_FILTER_PRI_AUTO, - table->dev_mc_list[i].id); - table->dev_mc_count = -1; - break; - } - table->dev_mc_list[i].id = rc; + for (i = 0; i < table->dev_mc_count; i++) { + efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, + EFX_FILTER_FLAG_RX_RSS, + 0); + efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, + table->dev_mc_list[i].addr); + rc = efx_ef10_filter_insert(efx, &spec, true); + if (rc < 0) { + /* Fall back to multicast-promisc. + * Leave the broadcast filter. + */ + while (i > 1) + efx_ef10_filter_remove_safe( + efx, EFX_FILTER_PRI_AUTO, + table->dev_mc_list[--i].id); + table->dev_mc_count = i; + mc_promisc = true; + break; } + table->dev_mc_list[i].id = rc; } - if (table->dev_mc_count < 0) { + if (mc_promisc) { efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, EFX_FILTER_FLAG_RX_RSS, 0); @@ -3874,9 +3877,8 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) { WARN_ON(1); - table->dev_mc_count = 0; } else { - table->dev_mc_list[0].id = rc; + table->dev_mc_list[table->dev_mc_count++].id = rc; } } |