be2net: Avoid unnecessary firmware updates of multicast list

Eachtime the ndo_set_rx_mode() routine is called, the driver programs the
multicast list in the adapter without checking if there are any changes to
the list. This leads to a flood of RX_FILTER cmds when a number of vlan
interfaces are configured over the device, as the ndo_ gets
called for each vlan interface. To avoid this, we now use __dev_mc_sync()
and __dev_uc_sync() API, but only to detect if there is a change in the
mc/uc lists. Now that we use this API, the code has to be-designed to
issue these API calls for each invocation of the be_set_rx_mode() call.

Signed-off-by: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
Signed-off-by: Sathya Perla <sathya.perla@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Sriharsha Basavapatna 2016-07-27 05:26:17 -04:00 committed by David S. Miller
parent 0aff1fbfe7
commit 92fbb1df83
2 changed files with 130 additions and 41 deletions

View File

@ -573,6 +573,8 @@ struct be_adapter {
u32 uc_macs; /* Count of secondary UC MAC programmed */
unsigned long vids[BITS_TO_LONGS(VLAN_N_VID)];
u16 vlans_added;
bool update_uc_list;
bool update_mc_list;
u32 beacon_state; /* for set_phys_id */

View File

@ -1420,8 +1420,8 @@ static int be_vid_config(struct be_adapter *adapter)
u16 num = 0, i = 0;
int status = 0;
/* No need to further configure vids if in promiscuous mode */
if (be_in_all_promisc(adapter))
/* No need to change the VLAN state if the I/F is in promiscuous */
if (adapter->netdev->flags & IFF_PROMISC)
return 0;
if (adapter->vlans_added > be_max_vlans(adapter))
@ -1483,12 +1483,6 @@ static int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid)
return be_vid_config(adapter);
}
static void be_clear_all_promisc(struct be_adapter *adapter)
{
be_cmd_rx_filter(adapter, BE_IF_FLAGS_ALL_PROMISCUOUS, OFF);
adapter->if_flags &= ~BE_IF_FLAGS_ALL_PROMISCUOUS;
}
static void be_set_all_promisc(struct be_adapter *adapter)
{
be_cmd_rx_filter(adapter, BE_IF_FLAGS_ALL_PROMISCUOUS, ON);
@ -1507,42 +1501,144 @@ static void be_set_mc_promisc(struct be_adapter *adapter)
adapter->if_flags |= BE_IF_FLAGS_MCAST_PROMISCUOUS;
}
static void be_set_mc_list(struct be_adapter *adapter)
static void be_set_uc_promisc(struct be_adapter *adapter)
{
int status;
status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_MULTICAST, ON);
if (adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS)
return;
status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_PROMISCUOUS, ON);
if (!status)
adapter->if_flags &= ~BE_IF_FLAGS_MCAST_PROMISCUOUS;
else
adapter->if_flags |= BE_IF_FLAGS_PROMISCUOUS;
}
static void be_clear_uc_promisc(struct be_adapter *adapter)
{
int status;
if (!(adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS))
return;
status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_PROMISCUOUS, OFF);
if (!status)
adapter->if_flags &= ~BE_IF_FLAGS_PROMISCUOUS;
}
/* The below 2 functions are the callback args for __dev_mc_sync/dev_uc_sync().
* We use a single callback function for both sync and unsync. We really don't
* add/remove addresses through this callback. But, we use it to detect changes
* to the uc/mc lists. The entire uc/mc list is programmed in be_set_rx_mode().
*/
static int be_uc_list_update(struct net_device *netdev,
const unsigned char *addr)
{
struct be_adapter *adapter = netdev_priv(netdev);
adapter->update_uc_list = true;
return 0;
}
static int be_mc_list_update(struct net_device *netdev,
const unsigned char *addr)
{
struct be_adapter *adapter = netdev_priv(netdev);
adapter->update_mc_list = true;
return 0;
}
static void be_set_mc_list(struct be_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
bool mc_promisc = false;
int status;
__dev_mc_sync(netdev, be_mc_list_update, be_mc_list_update);
if (netdev->flags & IFF_PROMISC) {
adapter->update_mc_list = false;
} else if (netdev->flags & IFF_ALLMULTI ||
netdev_mc_count(netdev) > be_max_mc(adapter)) {
/* Enable multicast promisc if num configured exceeds
* what we support
*/
mc_promisc = true;
adapter->update_mc_list = false;
} else if (adapter->if_flags & BE_IF_FLAGS_MCAST_PROMISCUOUS) {
/* Update mc-list unconditionally if the iface was previously
* in mc-promisc mode and now is out of that mode.
*/
adapter->update_mc_list = true;
}
if (mc_promisc) {
be_set_mc_promisc(adapter);
} else if (adapter->update_mc_list) {
status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_MULTICAST, ON);
if (!status)
adapter->if_flags &= ~BE_IF_FLAGS_MCAST_PROMISCUOUS;
else
be_set_mc_promisc(adapter);
adapter->update_mc_list = false;
}
}
static void be_clear_mc_list(struct be_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
__dev_mc_unsync(netdev, NULL);
be_cmd_rx_filter(adapter, BE_IF_FLAGS_MULTICAST, OFF);
}
static void be_set_uc_list(struct be_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
struct netdev_hw_addr *ha;
bool uc_promisc = false;
int i = 1; /* First slot is claimed by the Primary MAC */
for (; adapter->uc_macs > 0; adapter->uc_macs--, i++)
be_cmd_pmac_del(adapter, adapter->if_handle,
adapter->pmac_id[i], 0);
__dev_uc_sync(netdev, be_uc_list_update, be_uc_list_update);
if (netdev_uc_count(adapter->netdev) > be_max_uc(adapter)) {
be_set_all_promisc(adapter);
return;
if (netdev->flags & IFF_PROMISC) {
adapter->update_uc_list = false;
} else if (netdev_uc_count(netdev) > (be_max_uc(adapter) - 1)) {
uc_promisc = true;
adapter->update_uc_list = false;
} else if (adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS) {
/* Update uc-list unconditionally if the iface was previously
* in uc-promisc mode and now is out of that mode.
*/
adapter->update_uc_list = true;
}
netdev_for_each_uc_addr(ha, adapter->netdev) {
adapter->uc_macs++; /* First slot is for Primary MAC */
be_cmd_pmac_add(adapter, (u8 *)ha->addr, adapter->if_handle,
&adapter->pmac_id[adapter->uc_macs], 0);
if (uc_promisc) {
be_set_uc_promisc(adapter);
} else if (adapter->update_uc_list) {
be_clear_uc_promisc(adapter);
for (; adapter->uc_macs > 0; adapter->uc_macs--, i++)
be_cmd_pmac_del(adapter, adapter->if_handle,
adapter->pmac_id[i], 0);
netdev_for_each_uc_addr(ha, adapter->netdev) {
adapter->uc_macs++; /* First slot is for Primary MAC */
be_cmd_pmac_add(adapter,
(u8 *)ha->addr, adapter->if_handle,
&adapter->pmac_id[adapter->uc_macs], 0);
}
adapter->update_uc_list = false;
}
}
static void be_clear_uc_list(struct be_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
int i;
__dev_uc_unsync(netdev, NULL);
for (i = 1; i < (adapter->uc_macs + 1); i++)
be_cmd_pmac_del(adapter, adapter->if_handle,
adapter->pmac_id[i], 0);
@ -1554,27 +1650,17 @@ static void be_set_rx_mode(struct net_device *netdev)
struct be_adapter *adapter = netdev_priv(netdev);
if (netdev->flags & IFF_PROMISC) {
be_set_all_promisc(adapter);
return;
if (!be_in_all_promisc(adapter))
be_set_all_promisc(adapter);
} else if (be_in_all_promisc(adapter)) {
/* We need to re-program the vlan-list or clear
* vlan-promisc mode (if needed) when the interface
* comes out of promisc mode.
*/
be_vid_config(adapter);
}
/* Interface was previously in promiscuous mode; disable it */
if (be_in_all_promisc(adapter)) {
be_clear_all_promisc(adapter);
if (adapter->vlans_added)
be_vid_config(adapter);
}
/* Enable multicast promisc if num configured exceeds what we support */
if (netdev->flags & IFF_ALLMULTI ||
netdev_mc_count(netdev) > be_max_mc(adapter)) {
be_set_mc_promisc(adapter);
return;
}
if (netdev_uc_count(netdev) != adapter->uc_macs)
be_set_uc_list(adapter);
be_set_uc_list(adapter);
be_set_mc_list(adapter);
}
@ -3426,6 +3512,7 @@ static void be_disable_if_filters(struct be_adapter *adapter)
adapter->pmac_id[0], 0);
be_clear_uc_list(adapter);
be_clear_mc_list(adapter);
/* The IFACE flags are enabled in the open path and cleared
* in the close path. When a VF gets detached from the host and