summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c120
1 files changed, 72 insertions, 48 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 35f06ba6bc4e..6cf908953185 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -1774,19 +1774,22 @@ i40e_update_filter_state(int count,
**/
int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
{
- struct i40e_mac_filter *f, *add_head = NULL;
struct hlist_head tmp_add_list, tmp_del_list;
+ struct i40e_mac_filter *f, *add_head = NULL;
struct i40e_hw *hw = &vsi->back->hw;
+ unsigned int vlan_any_filters = 0;
+ unsigned int non_vlan_filters = 0;
+ unsigned int vlan_filters = 0;
bool promisc_changed = false;
char vsi_name[16] = "PF";
int filter_list_len = 0;
- u32 changed_flags = 0;
i40e_status aq_ret = 0;
+ u32 changed_flags = 0;
struct hlist_node *h;
- int retval = 0;
struct i40e_pf *pf;
int num_add = 0;
int num_del = 0;
+ int retval = 0;
int aq_err = 0;
u16 cmd_flags;
int list_size;
@@ -1825,11 +1828,75 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
hash_del(&f->hlist);
hlist_add_head(&f->hlist, &tmp_del_list);
vsi->active_filters--;
+
+ /* Avoid counting removed filters */
+ continue;
}
if (f->state == I40E_FILTER_NEW) {
hash_del(&f->hlist);
hlist_add_head(&f->hlist, &tmp_add_list);
}
+
+ /* Count the number of each type of filter we have
+ * remaining, ignoring any filters we're about to
+ * delete.
+ */
+ if (f->vlan > 0)
+ vlan_filters++;
+ else if (!f->vlan)
+ non_vlan_filters++;
+ else
+ vlan_any_filters++;
+ }
+
+ /* We should never have VLAN=-1 filters at the same time as we
+ * have either VLAN=0 or VLAN>0 filters, so warn about this
+ * case here to help catch any issues.
+ */
+ WARN_ON(vlan_any_filters && (vlan_filters + non_vlan_filters));
+
+ /* If we only have VLAN=0 filters remaining, and don't have
+ * any other VLAN filters, we need to convert these VLAN=0
+ * filters into VLAN=-1 (I40E_VLAN_ANY) so that we operate
+ * correctly in non-VLAN mode and receive all traffic tagged
+ * or untagged.
+ */
+ if (non_vlan_filters && !vlan_filters) {
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f,
+ hlist) {
+ /* Only replace VLAN=0 filters */
+ if (f->vlan)
+ continue;
+
+ /* Allocate a replacement element */
+ add_head = kzalloc(sizeof(*add_head),
+ GFP_KERNEL);
+ if (!add_head)
+ goto err_no_memory_locked;
+
+ /* Copy the filter, with new state and VLAN */
+ *add_head = *f;
+ add_head->state = I40E_FILTER_NEW;
+ add_head->vlan = I40E_VLAN_ANY;
+
+ /* Move the replacement to the add list */
+ INIT_HLIST_NODE(&add_head->hlist);
+ hlist_add_head(&add_head->hlist,
+ &tmp_add_list);
+
+ /* Move the original to the delete list */
+ f->state = I40E_FILTER_REMOVE;
+ hash_del(&f->hlist);
+ hlist_add_head(&f->hlist, &tmp_del_list);
+ vsi->active_filters--;
+ }
+
+ /* Also update any filters on the tmp_add list */
+ hlist_for_each_entry(f, &tmp_add_list, hlist) {
+ if (!f->vlan)
+ f->vlan = I40E_VLAN_ANY;
+ }
+ add_head = NULL;
}
spin_unlock_bh(&vsi->mac_filter_hash_lock);
}
@@ -2150,6 +2217,7 @@ out:
err_no_memory:
/* Restore elements on the temporary add and delete lists */
spin_lock_bh(&vsi->mac_filter_hash_lock);
+err_no_memory_locked:
i40e_undo_filter_entries(vsi, &tmp_del_list);
i40e_undo_filter_entries(vsi, &tmp_add_list);
spin_unlock_bh(&vsi->mac_filter_hash_lock);
@@ -2403,9 +2471,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
{
struct net_device *netdev = vsi->netdev;
- struct i40e_mac_filter *f, *add_f;
+ struct i40e_mac_filter *f;
struct hlist_node *h;
- int filter_count = 0;
int bkt;
/* Locked once because all functions invoked below iterates list */
@@ -2419,49 +2486,6 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
__i40e_del_filter(vsi, f);
}
- /* go through all the filters for this VSI and if there is only
- * vid == 0 it means there are no other filters, so vid 0 must
- * be replaced with -1. This signifies that we should from now
- * on accept any traffic (with any tag present, or untagged)
- */
- hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) {
- if (vsi->netdev) {
- if (f->vlan &&
- ether_addr_equal(netdev->dev_addr, f->macaddr))
- filter_count++;
- }
-
- if (f->vlan)
- filter_count++;
- }
-
- if (!filter_count && vsi->netdev) {
- i40e_del_filter(vsi, netdev->dev_addr, 0);
- f = i40e_add_filter(vsi, netdev->dev_addr, I40E_VLAN_ANY);
- if (!f) {
- dev_info(&vsi->back->pdev->dev,
- "Could not add filter %d for %pM\n",
- I40E_VLAN_ANY, netdev->dev_addr);
- spin_unlock_bh(&vsi->mac_filter_hash_lock);
- return -ENOMEM;
- }
- }
-
- if (!filter_count) {
- hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
- if (!f->vlan)
- __i40e_del_filter(vsi, f);
- add_f = i40e_add_filter(vsi, f->macaddr, I40E_VLAN_ANY);
- if (!add_f) {
- dev_info(&vsi->back->pdev->dev,
- "Could not add filter %d for %pM\n",
- I40E_VLAN_ANY, f->macaddr);
- spin_unlock_bh(&vsi->mac_filter_hash_lock);
- return -ENOMEM;
- }
- }
- }
-
spin_unlock_bh(&vsi->mac_filter_hash_lock);
/* schedule our worker thread which will take care of