|
@@ -1145,25 +1145,22 @@ void i40e_update_stats(struct i40e_vsi *vsi)
|
|
|
* @vsi: the VSI to be searched
|
|
|
* @macaddr: the MAC address
|
|
|
* @vlan: the vlan
|
|
|
- * @is_vf: make sure its a VF filter, else doesn't matter
|
|
|
- * @is_netdev: make sure its a netdev filter, else doesn't matter
|
|
|
*
|
|
|
* Returns ptr to the filter object or NULL
|
|
|
**/
|
|
|
static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi,
|
|
|
- u8 *macaddr, s16 vlan,
|
|
|
- bool is_vf, bool is_netdev)
|
|
|
+ const u8 *macaddr, s16 vlan)
|
|
|
{
|
|
|
struct i40e_mac_filter *f;
|
|
|
+ u64 key;
|
|
|
|
|
|
if (!vsi || !macaddr)
|
|
|
return NULL;
|
|
|
|
|
|
- list_for_each_entry(f, &vsi->mac_filter_list, list) {
|
|
|
+ key = i40e_addr_to_hkey(macaddr);
|
|
|
+ hash_for_each_possible(vsi->mac_filter_hash, f, hlist, key) {
|
|
|
if ((ether_addr_equal(macaddr, f->macaddr)) &&
|
|
|
- (vlan == f->vlan) &&
|
|
|
- (!is_vf || f->is_vf) &&
|
|
|
- (!is_netdev || f->is_netdev))
|
|
|
+ (vlan == f->vlan))
|
|
|
return f;
|
|
|
}
|
|
|
return NULL;
|
|
@@ -1173,24 +1170,21 @@ static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi,
|
|
|
* i40e_find_mac - Find a mac addr in the macvlan filters list
|
|
|
* @vsi: the VSI to be searched
|
|
|
* @macaddr: the MAC address we are searching for
|
|
|
- * @is_vf: make sure its a VF filter, else doesn't matter
|
|
|
- * @is_netdev: make sure its a netdev filter, else doesn't matter
|
|
|
*
|
|
|
* Returns the first filter with the provided MAC address or NULL if
|
|
|
* MAC address was not found
|
|
|
**/
|
|
|
-struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr,
|
|
|
- bool is_vf, bool is_netdev)
|
|
|
+struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr)
|
|
|
{
|
|
|
struct i40e_mac_filter *f;
|
|
|
+ u64 key;
|
|
|
|
|
|
if (!vsi || !macaddr)
|
|
|
return NULL;
|
|
|
|
|
|
- list_for_each_entry(f, &vsi->mac_filter_list, list) {
|
|
|
- if ((ether_addr_equal(macaddr, f->macaddr)) &&
|
|
|
- (!is_vf || f->is_vf) &&
|
|
|
- (!is_netdev || f->is_netdev))
|
|
|
+ key = i40e_addr_to_hkey(macaddr);
|
|
|
+ hash_for_each_possible(vsi->mac_filter_hash, f, hlist, key) {
|
|
|
+ if ((ether_addr_equal(macaddr, f->macaddr)))
|
|
|
return f;
|
|
|
}
|
|
|
return NULL;
|
|
@@ -1204,86 +1198,31 @@ struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr,
|
|
|
**/
|
|
|
bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi)
|
|
|
{
|
|
|
- struct i40e_mac_filter *f;
|
|
|
+ /* If we have a PVID, always operate in VLAN mode */
|
|
|
+ if (vsi->info.pvid)
|
|
|
+ return true;
|
|
|
|
|
|
- /* Only -1 for all the filters denotes not in vlan mode
|
|
|
- * so we have to go through all the list in order to make sure
|
|
|
+ /* We need to operate in VLAN mode whenever we have any filters with
|
|
|
+ * a VLAN other than I40E_VLAN_ALL. We could check the table each
|
|
|
+ * time, incurring search cost repeatedly. However, we can notice two
|
|
|
+ * things:
|
|
|
+ *
|
|
|
+ * 1) the only place where we can gain a VLAN filter is in
|
|
|
+ * i40e_add_filter.
|
|
|
+ *
|
|
|
+ * 2) the only place where filters are actually removed is in
|
|
|
+ * i40e_vsi_sync_filters_subtask.
|
|
|
+ *
|
|
|
+ * Thus, we can simply use a boolean value, has_vlan_filters which we
|
|
|
+ * will set to true when we add a VLAN filter in i40e_add_filter. Then
|
|
|
+ * we have to perform the full search after deleting filters in
|
|
|
+ * i40e_vsi_sync_filters_subtask, but we already have to search
|
|
|
+ * filters here and can perform the check at the same time. This
|
|
|
+ * results in avoiding embedding a loop for VLAN mode inside another
|
|
|
+ * loop over all the filters, and should maintain correctness as noted
|
|
|
+ * above.
|
|
|
*/
|
|
|
- list_for_each_entry(f, &vsi->mac_filter_list, list) {
|
|
|
- if (f->vlan >= 0 || vsi->info.pvid)
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * i40e_put_mac_in_vlan - Make macvlan filters from macaddrs and vlans
|
|
|
- * @vsi: the VSI to be searched
|
|
|
- * @macaddr: the mac address to be filtered
|
|
|
- * @is_vf: true if it is a VF
|
|
|
- * @is_netdev: true if it is a netdev
|
|
|
- *
|
|
|
- * Goes through all the macvlan filters and adds a
|
|
|
- * macvlan filter for each unique vlan that already exists
|
|
|
- *
|
|
|
- * Returns first filter found on success, else NULL
|
|
|
- **/
|
|
|
-struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr,
|
|
|
- bool is_vf, bool is_netdev)
|
|
|
-{
|
|
|
- struct i40e_mac_filter *f;
|
|
|
-
|
|
|
- list_for_each_entry(f, &vsi->mac_filter_list, list) {
|
|
|
- if (vsi->info.pvid)
|
|
|
- f->vlan = le16_to_cpu(vsi->info.pvid);
|
|
|
- if (!i40e_find_filter(vsi, macaddr, f->vlan,
|
|
|
- is_vf, is_netdev)) {
|
|
|
- if (!i40e_add_filter(vsi, macaddr, f->vlan,
|
|
|
- is_vf, is_netdev))
|
|
|
- return NULL;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return list_first_entry_or_null(&vsi->mac_filter_list,
|
|
|
- struct i40e_mac_filter, list);
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * i40e_del_mac_all_vlan - Remove a MAC filter from all VLANS
|
|
|
- * @vsi: the VSI to be searched
|
|
|
- * @macaddr: the mac address to be removed
|
|
|
- * @is_vf: true if it is a VF
|
|
|
- * @is_netdev: true if it is a netdev
|
|
|
- *
|
|
|
- * Removes a given MAC address from a VSI, regardless of VLAN
|
|
|
- *
|
|
|
- * Returns 0 for success, or error
|
|
|
- **/
|
|
|
-int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr,
|
|
|
- bool is_vf, bool is_netdev)
|
|
|
-{
|
|
|
- struct i40e_mac_filter *f = NULL;
|
|
|
- int changed = 0;
|
|
|
-
|
|
|
- WARN(!spin_is_locked(&vsi->mac_filter_list_lock),
|
|
|
- "Missing mac_filter_list_lock\n");
|
|
|
- list_for_each_entry(f, &vsi->mac_filter_list, list) {
|
|
|
- if ((ether_addr_equal(macaddr, f->macaddr)) &&
|
|
|
- (is_vf == f->is_vf) &&
|
|
|
- (is_netdev == f->is_netdev)) {
|
|
|
- f->counter--;
|
|
|
- changed = 1;
|
|
|
- if (f->counter == 0)
|
|
|
- f->state = I40E_FILTER_REMOVE;
|
|
|
- }
|
|
|
- }
|
|
|
- if (changed) {
|
|
|
- vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
|
|
|
- vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
|
|
|
- return 0;
|
|
|
- }
|
|
|
- return -ENOENT;
|
|
|
+ return vsi->has_vlan_filter;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1291,20 +1230,17 @@ int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr,
|
|
|
* @vsi: the VSI to be searched
|
|
|
* @macaddr: the MAC address
|
|
|
* @vlan: the vlan
|
|
|
- * @is_vf: make sure its a VF filter, else doesn't matter
|
|
|
- * @is_netdev: make sure its a netdev filter, else doesn't matter
|
|
|
*
|
|
|
* Returns ptr to the filter object or NULL when no memory available.
|
|
|
*
|
|
|
- * NOTE: This function is expected to be called with mac_filter_list_lock
|
|
|
+ * NOTE: This function is expected to be called with mac_filter_hash_lock
|
|
|
* being held.
|
|
|
**/
|
|
|
struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
|
|
|
- u8 *macaddr, s16 vlan,
|
|
|
- bool is_vf, bool is_netdev)
|
|
|
+ const u8 *macaddr, s16 vlan)
|
|
|
{
|
|
|
struct i40e_mac_filter *f;
|
|
|
- int changed = false;
|
|
|
+ u64 key;
|
|
|
|
|
|
if (!vsi || !macaddr)
|
|
|
return NULL;
|
|
@@ -1316,11 +1252,17 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
|
|
|
if (is_broadcast_ether_addr(macaddr))
|
|
|
return NULL;
|
|
|
|
|
|
- f = i40e_find_filter(vsi, macaddr, vlan, is_vf, is_netdev);
|
|
|
+ f = i40e_find_filter(vsi, macaddr, vlan);
|
|
|
if (!f) {
|
|
|
f = kzalloc(sizeof(*f), GFP_ATOMIC);
|
|
|
if (!f)
|
|
|
- goto add_filter_out;
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* Update the boolean indicating if we need to function in
|
|
|
+ * VLAN mode.
|
|
|
+ */
|
|
|
+ if (vlan >= 0)
|
|
|
+ vsi->has_vlan_filter = true;
|
|
|
|
|
|
ether_addr_copy(f->macaddr, macaddr);
|
|
|
f->vlan = vlan;
|
|
@@ -1332,100 +1274,148 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
|
|
|
f->state = I40E_FILTER_FAILED;
|
|
|
else
|
|
|
f->state = I40E_FILTER_NEW;
|
|
|
- changed = true;
|
|
|
- INIT_LIST_HEAD(&f->list);
|
|
|
- list_add_tail(&f->list, &vsi->mac_filter_list);
|
|
|
- }
|
|
|
+ INIT_HLIST_NODE(&f->hlist);
|
|
|
|
|
|
- /* increment counter and add a new flag if needed */
|
|
|
- if (is_vf) {
|
|
|
- if (!f->is_vf) {
|
|
|
- f->is_vf = true;
|
|
|
- f->counter++;
|
|
|
- }
|
|
|
- } else if (is_netdev) {
|
|
|
- if (!f->is_netdev) {
|
|
|
- f->is_netdev = true;
|
|
|
- f->counter++;
|
|
|
- }
|
|
|
- } else {
|
|
|
- f->counter++;
|
|
|
- }
|
|
|
+ key = i40e_addr_to_hkey(macaddr);
|
|
|
+ hash_add(vsi->mac_filter_hash, &f->hlist, key);
|
|
|
|
|
|
- if (changed) {
|
|
|
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
|
|
|
vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
|
|
|
}
|
|
|
|
|
|
-add_filter_out:
|
|
|
+ /* If we're asked to add a filter that has been marked for removal, it
|
|
|
+ * is safe to simply restore it to active state. __i40e_del_filter
|
|
|
+ * will have simply deleted any filters which were previously marked
|
|
|
+ * NEW or FAILED, so if it is currently marked REMOVE it must have
|
|
|
+ * previously been ACTIVE. Since we haven't yet run the sync filters
|
|
|
+ * task, just restore this filter to the ACTIVE state so that the
|
|
|
+ * sync task leaves it in place
|
|
|
+ */
|
|
|
+ if (f->state == I40E_FILTER_REMOVE)
|
|
|
+ f->state = I40E_FILTER_ACTIVE;
|
|
|
+
|
|
|
return f;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * i40e_del_filter - Remove a mac/vlan filter from the VSI
|
|
|
+ * __i40e_del_filter - Remove a specific filter from the VSI
|
|
|
+ * @vsi: VSI to remove from
|
|
|
+ * @f: the filter to remove from the list
|
|
|
+ *
|
|
|
+ * This function should be called instead of i40e_del_filter only if you know
|
|
|
+ * the exact filter you will remove already, such as via i40e_find_filter or
|
|
|
+ * i40e_find_mac.
|
|
|
+ *
|
|
|
+ * NOTE: This function is expected to be called with mac_filter_hash_lock
|
|
|
+ * being held.
|
|
|
+ * ANOTHER NOTE: This function MUST be called from within the context of
|
|
|
+ * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe()
|
|
|
+ * instead of list_for_each_entry().
|
|
|
+ **/
|
|
|
+static void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f)
|
|
|
+{
|
|
|
+ if (!f)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if ((f->state == I40E_FILTER_FAILED) ||
|
|
|
+ (f->state == I40E_FILTER_NEW)) {
|
|
|
+ /* this one never got added by the FW. Just remove it,
|
|
|
+ * no need to sync anything.
|
|
|
+ */
|
|
|
+ hash_del(&f->hlist);
|
|
|
+ kfree(f);
|
|
|
+ } else {
|
|
|
+ f->state = I40E_FILTER_REMOVE;
|
|
|
+ vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
|
|
|
+ vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * i40e_del_filter - Remove a MAC/VLAN filter from the VSI
|
|
|
* @vsi: the VSI to be searched
|
|
|
* @macaddr: the MAC address
|
|
|
- * @vlan: the vlan
|
|
|
- * @is_vf: make sure it's a VF filter, else doesn't matter
|
|
|
- * @is_netdev: make sure it's a netdev filter, else doesn't matter
|
|
|
+ * @vlan: the VLAN
|
|
|
*
|
|
|
- * NOTE: This function is expected to be called with mac_filter_list_lock
|
|
|
+ * NOTE: This function is expected to be called with mac_filter_hash_lock
|
|
|
* being held.
|
|
|
* ANOTHER NOTE: This function MUST be called from within the context of
|
|
|
* the "safe" variants of any list iterators, e.g. list_for_each_entry_safe()
|
|
|
* instead of list_for_each_entry().
|
|
|
**/
|
|
|
-void i40e_del_filter(struct i40e_vsi *vsi,
|
|
|
- u8 *macaddr, s16 vlan,
|
|
|
- bool is_vf, bool is_netdev)
|
|
|
+void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan)
|
|
|
{
|
|
|
struct i40e_mac_filter *f;
|
|
|
|
|
|
if (!vsi || !macaddr)
|
|
|
return;
|
|
|
|
|
|
- f = i40e_find_filter(vsi, macaddr, vlan, is_vf, is_netdev);
|
|
|
- if (!f || f->counter == 0)
|
|
|
- return;
|
|
|
+ f = i40e_find_filter(vsi, macaddr, vlan);
|
|
|
+ __i40e_del_filter(vsi, f);
|
|
|
+}
|
|
|
|
|
|
- if (is_vf) {
|
|
|
- if (f->is_vf) {
|
|
|
- f->is_vf = false;
|
|
|
- f->counter--;
|
|
|
- }
|
|
|
- } else if (is_netdev) {
|
|
|
- if (f->is_netdev) {
|
|
|
- f->is_netdev = false;
|
|
|
- f->counter--;
|
|
|
- }
|
|
|
- } else {
|
|
|
- /* make sure we don't remove a filter in use by VF or netdev */
|
|
|
- int min_f = 0;
|
|
|
+/**
|
|
|
+ * i40e_put_mac_in_vlan - Make macvlan filters from macaddrs and vlans
|
|
|
+ * @vsi: the VSI to be searched
|
|
|
+ * @macaddr: the mac address to be filtered
|
|
|
+ *
|
|
|
+ * Goes through all the macvlan filters and adds a macvlan filter for each
|
|
|
+ * unique vlan that already exists. If a PVID has been assigned, instead only
|
|
|
+ * add the macaddr to that VLAN.
|
|
|
+ *
|
|
|
+ * Returns last filter added on success, else NULL
|
|
|
+ **/
|
|
|
+struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi,
|
|
|
+ const u8 *macaddr)
|
|
|
+{
|
|
|
+ struct i40e_mac_filter *f, *add = NULL;
|
|
|
+ struct hlist_node *h;
|
|
|
+ int bkt;
|
|
|
|
|
|
- min_f += (f->is_vf ? 1 : 0);
|
|
|
- min_f += (f->is_netdev ? 1 : 0);
|
|
|
+ if (vsi->info.pvid)
|
|
|
+ return i40e_add_filter(vsi, macaddr,
|
|
|
+ le16_to_cpu(vsi->info.pvid));
|
|
|
|
|
|
- if (f->counter > min_f)
|
|
|
- f->counter--;
|
|
|
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
|
|
|
+ if (f->state == I40E_FILTER_REMOVE)
|
|
|
+ continue;
|
|
|
+ add = i40e_add_filter(vsi, macaddr, f->vlan);
|
|
|
+ if (!add)
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
- /* counter == 0 tells sync_filters_subtask to
|
|
|
- * remove the filter from the firmware's list
|
|
|
- */
|
|
|
- if (f->counter == 0) {
|
|
|
- if ((f->state == I40E_FILTER_FAILED) ||
|
|
|
- (f->state == I40E_FILTER_NEW)) {
|
|
|
- /* this one never got added by the FW. Just remove it,
|
|
|
- * no need to sync anything.
|
|
|
- */
|
|
|
- list_del(&f->list);
|
|
|
- kfree(f);
|
|
|
- } else {
|
|
|
- f->state = I40E_FILTER_REMOVE;
|
|
|
- vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
|
|
|
- vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
|
|
|
+ return add;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * i40e_del_mac_all_vlan - Remove a MAC filter from all VLANS
|
|
|
+ * @vsi: the VSI to be searched
|
|
|
+ * @macaddr: the mac address to be removed
|
|
|
+ *
|
|
|
+ * Removes a given MAC address from a VSI, regardless of VLAN
|
|
|
+ *
|
|
|
+ * Returns 0 for success, or error
|
|
|
+ **/
|
|
|
+int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, const u8 *macaddr)
|
|
|
+{
|
|
|
+ struct i40e_mac_filter *f;
|
|
|
+ struct hlist_node *h;
|
|
|
+ bool found = false;
|
|
|
+ int bkt;
|
|
|
+
|
|
|
+ WARN(!spin_is_locked(&vsi->mac_filter_hash_lock),
|
|
|
+ "Missing mac_filter_hash_lock\n");
|
|
|
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
|
|
|
+ if (ether_addr_equal(macaddr, f->macaddr)) {
|
|
|
+ __i40e_del_filter(vsi, f);
|
|
|
+ found = true;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ if (found)
|
|
|
+ return 0;
|
|
|
+ else
|
|
|
+ return -ENOENT;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1466,10 +1456,10 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
|
|
|
else
|
|
|
netdev_info(netdev, "set new mac address %pM\n", addr->sa_data);
|
|
|
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
- i40e_del_mac_all_vlan(vsi, netdev->dev_addr, false, true);
|
|
|
- i40e_put_mac_in_vlan(vsi, addr->sa_data, false, true);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
+ i40e_del_mac_all_vlan(vsi, netdev->dev_addr);
|
|
|
+ i40e_put_mac_in_vlan(vsi, addr->sa_data);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
ether_addr_copy(netdev->dev_addr, addr->sa_data);
|
|
|
if (vsi->type == I40E_VSI_MAIN) {
|
|
|
i40e_status ret;
|
|
@@ -1632,6 +1622,52 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
|
|
|
ctxt->info.valid_sections |= cpu_to_le16(sections);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * i40e_addr_sync - Callback for dev_(mc|uc)_sync to add address
|
|
|
+ * @netdev: the netdevice
|
|
|
+ * @addr: address to add
|
|
|
+ *
|
|
|
+ * Called by __dev_(mc|uc)_sync when an address needs to be added. We call
|
|
|
+ * __dev_(uc|mc)_sync from .set_rx_mode and guarantee to hold the hash lock.
|
|
|
+ */
|
|
|
+static int i40e_addr_sync(struct net_device *netdev, const u8 *addr)
|
|
|
+{
|
|
|
+ struct i40e_netdev_priv *np = netdev_priv(netdev);
|
|
|
+ struct i40e_vsi *vsi = np->vsi;
|
|
|
+ struct i40e_mac_filter *f;
|
|
|
+
|
|
|
+ if (i40e_is_vsi_in_vlan(vsi))
|
|
|
+ f = i40e_put_mac_in_vlan(vsi, addr);
|
|
|
+ else
|
|
|
+ f = i40e_add_filter(vsi, addr, I40E_VLAN_ANY);
|
|
|
+
|
|
|
+ if (f)
|
|
|
+ return 0;
|
|
|
+ else
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * i40e_addr_unsync - Callback for dev_(mc|uc)_sync to remove address
|
|
|
+ * @netdev: the netdevice
|
|
|
+ * @addr: address to add
|
|
|
+ *
|
|
|
+ * Called by __dev_(mc|uc)_sync when an address needs to be removed. We call
|
|
|
+ * __dev_(uc|mc)_sync from .set_rx_mode and guarantee to hold the hash lock.
|
|
|
+ */
|
|
|
+static int i40e_addr_unsync(struct net_device *netdev, const u8 *addr)
|
|
|
+{
|
|
|
+ struct i40e_netdev_priv *np = netdev_priv(netdev);
|
|
|
+ struct i40e_vsi *vsi = np->vsi;
|
|
|
+
|
|
|
+ if (i40e_is_vsi_in_vlan(vsi))
|
|
|
+ i40e_del_mac_all_vlan(vsi, addr);
|
|
|
+ else
|
|
|
+ i40e_del_filter(vsi, addr, I40E_VLAN_ANY);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* i40e_set_rx_mode - NDO callback to set the netdev filters
|
|
|
* @netdev: network interface device structure
|
|
@@ -1643,62 +1679,14 @@ static void i40e_set_rx_mode(struct net_device *netdev)
|
|
|
#endif
|
|
|
{
|
|
|
struct i40e_netdev_priv *np = netdev_priv(netdev);
|
|
|
- struct i40e_mac_filter *f, *ftmp;
|
|
|
struct i40e_vsi *vsi = np->vsi;
|
|
|
- struct netdev_hw_addr *uca;
|
|
|
- struct netdev_hw_addr *mca;
|
|
|
- struct netdev_hw_addr *ha;
|
|
|
-
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
-
|
|
|
- /* add addr if not already in the filter list */
|
|
|
- netdev_for_each_uc_addr(uca, netdev) {
|
|
|
- if (!i40e_find_mac(vsi, uca->addr, false, true)) {
|
|
|
- if (i40e_is_vsi_in_vlan(vsi))
|
|
|
- i40e_put_mac_in_vlan(vsi, uca->addr,
|
|
|
- false, true);
|
|
|
- else
|
|
|
- i40e_add_filter(vsi, uca->addr, I40E_VLAN_ANY,
|
|
|
- false, true);
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- netdev_for_each_mc_addr(mca, netdev) {
|
|
|
- if (!i40e_find_mac(vsi, mca->addr, false, true)) {
|
|
|
- if (i40e_is_vsi_in_vlan(vsi))
|
|
|
- i40e_put_mac_in_vlan(vsi, mca->addr,
|
|
|
- false, true);
|
|
|
- else
|
|
|
- i40e_add_filter(vsi, mca->addr, I40E_VLAN_ANY,
|
|
|
- false, true);
|
|
|
- }
|
|
|
- }
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
|
|
|
- /* remove filter if not in netdev list */
|
|
|
- list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
|
|
|
+ __dev_uc_sync(netdev, i40e_addr_sync, i40e_addr_unsync);
|
|
|
+ __dev_mc_sync(netdev, i40e_addr_sync, i40e_addr_unsync);
|
|
|
|
|
|
- if (!f->is_netdev)
|
|
|
- continue;
|
|
|
-
|
|
|
- netdev_for_each_mc_addr(mca, netdev)
|
|
|
- if (ether_addr_equal(mca->addr, f->macaddr))
|
|
|
- goto bottom_of_search_loop;
|
|
|
-
|
|
|
- netdev_for_each_uc_addr(uca, netdev)
|
|
|
- if (ether_addr_equal(uca->addr, f->macaddr))
|
|
|
- goto bottom_of_search_loop;
|
|
|
-
|
|
|
- for_each_dev_addr(netdev, ha)
|
|
|
- if (ether_addr_equal(ha->addr, f->macaddr))
|
|
|
- goto bottom_of_search_loop;
|
|
|
-
|
|
|
- /* f->macaddr wasn't found in uc, mc, or ha list so delete it */
|
|
|
- i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY, false, true);
|
|
|
-
|
|
|
-bottom_of_search_loop:
|
|
|
- continue;
|
|
|
- }
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
|
|
|
/* check for other flag changes */
|
|
|
if (vsi->current_netdev_flags != vsi->netdev->flags) {
|
|
@@ -1713,21 +1701,26 @@ bottom_of_search_loop:
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * i40e_undo_del_filter_entries - Undo the changes made to MAC filter entries
|
|
|
- * @vsi: pointer to vsi struct
|
|
|
+ * i40e_undo_filter_entries - Undo the changes made to MAC filter entries
|
|
|
+ * @vsi: Pointer to VSI struct
|
|
|
* @from: Pointer to list which contains MAC filter entries - changes to
|
|
|
* those entries needs to be undone.
|
|
|
*
|
|
|
- * MAC filter entries from list were slated to be removed from device.
|
|
|
+ * MAC filter entries from list were slated to be sent to firmware, either for
|
|
|
+ * addition or deletion.
|
|
|
**/
|
|
|
-static void i40e_undo_del_filter_entries(struct i40e_vsi *vsi,
|
|
|
- struct list_head *from)
|
|
|
+static void i40e_undo_filter_entries(struct i40e_vsi *vsi,
|
|
|
+ struct hlist_head *from)
|
|
|
{
|
|
|
- struct i40e_mac_filter *f, *ftmp;
|
|
|
+ struct i40e_mac_filter *f;
|
|
|
+ struct hlist_node *h;
|
|
|
+
|
|
|
+ hlist_for_each_entry_safe(f, h, from, hlist) {
|
|
|
+ u64 key = i40e_addr_to_hkey(f->macaddr);
|
|
|
|
|
|
- list_for_each_entry_safe(f, ftmp, from, list) {
|
|
|
/* Move the element back into MAC filter list*/
|
|
|
- list_move_tail(&f->list, &vsi->mac_filter_list);
|
|
|
+ hlist_del(&f->hlist);
|
|
|
+ hash_add(vsi->mac_filter_hash, &f->hlist, key);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1756,7 +1749,9 @@ i40e_update_filter_state(int count,
|
|
|
/* Everything's good, mark all filters active. */
|
|
|
for (i = 0; i < count ; i++) {
|
|
|
add_head->state = I40E_FILTER_ACTIVE;
|
|
|
- add_head = list_next_entry(add_head, list);
|
|
|
+ add_head = hlist_entry(add_head->hlist.next,
|
|
|
+ typeof(struct i40e_mac_filter),
|
|
|
+ hlist);
|
|
|
}
|
|
|
} else if (aq_err == I40E_AQ_RC_ENOSPC) {
|
|
|
/* Device ran out of filter space. Check the return value
|
|
@@ -1770,19 +1765,97 @@ i40e_update_filter_state(int count,
|
|
|
add_head->state = I40E_FILTER_ACTIVE;
|
|
|
retval++;
|
|
|
}
|
|
|
- add_head = list_next_entry(add_head, list);
|
|
|
+ add_head = hlist_entry(add_head->hlist.next,
|
|
|
+ typeof(struct i40e_mac_filter),
|
|
|
+ hlist);
|
|
|
}
|
|
|
} else {
|
|
|
/* Some other horrible thing happened, fail all filters */
|
|
|
retval = 0;
|
|
|
for (i = 0; i < count ; i++) {
|
|
|
add_head->state = I40E_FILTER_FAILED;
|
|
|
- add_head = list_next_entry(add_head, list);
|
|
|
+ add_head = hlist_entry(add_head->hlist.next,
|
|
|
+ typeof(struct i40e_mac_filter),
|
|
|
+ hlist);
|
|
|
}
|
|
|
}
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * i40e_aqc_del_filters - Request firmware to delete a set of filters
|
|
|
+ * @vsi: ptr to the VSI
|
|
|
+ * @vsi_name: name to display in messages
|
|
|
+ * @list: the list of filters to send to firmware
|
|
|
+ * @num_del: the number of filters to delete
|
|
|
+ * @retval: Set to -EIO on failure to delete
|
|
|
+ *
|
|
|
+ * Send a request to firmware via AdminQ to delete a set of filters. Uses
|
|
|
+ * *retval instead of a return value so that success does not force ret_val to
|
|
|
+ * be set to 0. This ensures that a sequence of calls to this function
|
|
|
+ * preserve the previous value of *retval on successful delete.
|
|
|
+ */
|
|
|
+static
|
|
|
+void i40e_aqc_del_filters(struct i40e_vsi *vsi, const char *vsi_name,
|
|
|
+ struct i40e_aqc_remove_macvlan_element_data *list,
|
|
|
+ int num_del, int *retval)
|
|
|
+{
|
|
|
+ struct i40e_hw *hw = &vsi->back->hw;
|
|
|
+ i40e_status aq_ret;
|
|
|
+ int aq_err;
|
|
|
+
|
|
|
+ aq_ret = i40e_aq_remove_macvlan(hw, vsi->seid, list, num_del, NULL);
|
|
|
+ aq_err = hw->aq.asq_last_status;
|
|
|
+
|
|
|
+ /* Explicitly ignore and do not report when firmware returns ENOENT */
|
|
|
+ if (aq_ret && !(aq_err == I40E_AQ_RC_ENOENT)) {
|
|
|
+ *retval = -EIO;
|
|
|
+ dev_info(&vsi->back->pdev->dev,
|
|
|
+ "ignoring delete macvlan error on %s, err %s, aq_err %s\n",
|
|
|
+ vsi_name, i40e_stat_str(hw, aq_ret),
|
|
|
+ i40e_aq_str(hw, aq_err));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * i40e_aqc_add_filters - Request firmware to add a set of filters
|
|
|
+ * @vsi: ptr to the VSI
|
|
|
+ * @vsi_name: name to display in messages
|
|
|
+ * @list: the list of filters to send to firmware
|
|
|
+ * @add_head: Position in the add hlist
|
|
|
+ * @num_add: the number of filters to add
|
|
|
+ * @promisc_change: set to true on exit if promiscuous mode was forced on
|
|
|
+ *
|
|
|
+ * Send a request to firmware via AdminQ to add a chunk of filters. Will set
|
|
|
+ * promisc_changed to true if the firmware has run out of space for more
|
|
|
+ * filters.
|
|
|
+ */
|
|
|
+static
|
|
|
+void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name,
|
|
|
+ struct i40e_aqc_add_macvlan_element_data *list,
|
|
|
+ struct i40e_mac_filter *add_head,
|
|
|
+ int num_add, bool *promisc_changed)
|
|
|
+{
|
|
|
+ struct i40e_hw *hw = &vsi->back->hw;
|
|
|
+ i40e_status aq_ret;
|
|
|
+ int aq_err, fcnt;
|
|
|
+
|
|
|
+ aq_ret = i40e_aq_add_macvlan(hw, vsi->seid, list, num_add, NULL);
|
|
|
+ aq_err = hw->aq.asq_last_status;
|
|
|
+ fcnt = i40e_update_filter_state(num_add, list, add_head, aq_ret);
|
|
|
+ vsi->active_filters += fcnt;
|
|
|
+
|
|
|
+ if (fcnt != num_add) {
|
|
|
+ *promisc_changed = true;
|
|
|
+ set_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state);
|
|
|
+ vsi->promisc_threshold = (vsi->active_filters * 3) / 4;
|
|
|
+ dev_warn(&vsi->back->pdev->dev,
|
|
|
+ "Error %s adding RX filters on %s, promiscuous mode forced on\n",
|
|
|
+ i40e_aq_str(hw, aq_err),
|
|
|
+ vsi_name);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* i40e_sync_vsi_filters - Update the VSI filter list to the HW
|
|
|
* @vsi: ptr to the VSI
|
|
@@ -1793,22 +1866,25 @@ i40e_update_filter_state(int count,
|
|
|
**/
|
|
|
int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
|
|
|
{
|
|
|
- struct i40e_mac_filter *f, *ftmp, *add_head = NULL;
|
|
|
- struct list_head tmp_add_list, tmp_del_list;
|
|
|
+ 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;
|
|
|
- int retval = 0;
|
|
|
+ u32 changed_flags = 0;
|
|
|
+ struct hlist_node *h;
|
|
|
struct i40e_pf *pf;
|
|
|
int num_add = 0;
|
|
|
int num_del = 0;
|
|
|
- int aq_err = 0;
|
|
|
+ int retval = 0;
|
|
|
u16 cmd_flags;
|
|
|
int list_size;
|
|
|
- int fcnt;
|
|
|
+ int bkt;
|
|
|
|
|
|
/* empty array typed pointers, kcalloc later */
|
|
|
struct i40e_aqc_add_macvlan_element_data *add_list;
|
|
@@ -1823,8 +1899,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
|
|
|
vsi->current_netdev_flags = vsi->netdev->flags;
|
|
|
}
|
|
|
|
|
|
- INIT_LIST_HEAD(&tmp_add_list);
|
|
|
- INIT_LIST_HEAD(&tmp_del_list);
|
|
|
+ INIT_HLIST_HEAD(&tmp_add_list);
|
|
|
+ INIT_HLIST_HEAD(&tmp_del_list);
|
|
|
|
|
|
if (vsi->type == I40E_VSI_SRIOV)
|
|
|
snprintf(vsi_name, sizeof(vsi_name) - 1, "VF %d", vsi->vf_id);
|
|
@@ -1834,41 +1910,98 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
|
|
|
if (vsi->flags & I40E_VSI_FLAG_FILTER_CHANGED) {
|
|
|
vsi->flags &= ~I40E_VSI_FLAG_FILTER_CHANGED;
|
|
|
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
/* Create a list of filters to delete. */
|
|
|
- list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
|
|
|
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
|
|
|
if (f->state == I40E_FILTER_REMOVE) {
|
|
|
- WARN_ON(f->counter != 0);
|
|
|
/* Move the element into temporary del_list */
|
|
|
- list_move_tail(&f->list, &tmp_del_list);
|
|
|
+ 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) {
|
|
|
- WARN_ON(f->counter == 0);
|
|
|
- /* Move the element into temporary add_list */
|
|
|
- list_move_tail(&f->list, &tmp_add_list);
|
|
|
+ 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_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
}
|
|
|
|
|
|
/* Now process 'del_list' outside the lock */
|
|
|
- if (!list_empty(&tmp_del_list)) {
|
|
|
+ if (!hlist_empty(&tmp_del_list)) {
|
|
|
filter_list_len = hw->aq.asq_buf_size /
|
|
|
sizeof(struct i40e_aqc_remove_macvlan_element_data);
|
|
|
list_size = filter_list_len *
|
|
|
sizeof(struct i40e_aqc_remove_macvlan_element_data);
|
|
|
del_list = kzalloc(list_size, GFP_ATOMIC);
|
|
|
- if (!del_list) {
|
|
|
- /* Undo VSI's MAC filter entry element updates */
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
- i40e_undo_del_filter_entries(vsi, &tmp_del_list);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
- retval = -ENOMEM;
|
|
|
- goto out;
|
|
|
- }
|
|
|
+ if (!del_list)
|
|
|
+ goto err_no_memory;
|
|
|
|
|
|
- list_for_each_entry_safe(f, ftmp, &tmp_del_list, list) {
|
|
|
+ hlist_for_each_entry_safe(f, h, &tmp_del_list, hlist) {
|
|
|
cmd_flags = 0;
|
|
|
|
|
|
/* add to delete list */
|
|
@@ -1887,68 +2020,47 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
|
|
|
|
|
|
/* flush a full buffer */
|
|
|
if (num_del == filter_list_len) {
|
|
|
- aq_ret = i40e_aq_remove_macvlan(hw, vsi->seid,
|
|
|
- del_list,
|
|
|
- num_del, NULL);
|
|
|
- aq_err = hw->aq.asq_last_status;
|
|
|
- num_del = 0;
|
|
|
+ i40e_aqc_del_filters(vsi, vsi_name, del_list,
|
|
|
+ num_del, &retval);
|
|
|
memset(del_list, 0, list_size);
|
|
|
-
|
|
|
- /* Explicitly ignore and do not report when
|
|
|
- * firmware returns ENOENT.
|
|
|
- */
|
|
|
- if (aq_ret && !(aq_err == I40E_AQ_RC_ENOENT)) {
|
|
|
- retval = -EIO;
|
|
|
- dev_info(&pf->pdev->dev,
|
|
|
- "ignoring delete macvlan error on %s, err %s, aq_err %s\n",
|
|
|
- vsi_name,
|
|
|
- i40e_stat_str(hw, aq_ret),
|
|
|
- i40e_aq_str(hw, aq_err));
|
|
|
- }
|
|
|
+ num_del = 0;
|
|
|
}
|
|
|
/* Release memory for MAC filter entries which were
|
|
|
* synced up with HW.
|
|
|
*/
|
|
|
- list_del(&f->list);
|
|
|
+ hlist_del(&f->hlist);
|
|
|
kfree(f);
|
|
|
}
|
|
|
|
|
|
if (num_del) {
|
|
|
- aq_ret = i40e_aq_remove_macvlan(hw, vsi->seid, del_list,
|
|
|
- num_del, NULL);
|
|
|
- aq_err = hw->aq.asq_last_status;
|
|
|
- num_del = 0;
|
|
|
-
|
|
|
- /* Explicitly ignore and do not report when firmware
|
|
|
- * returns ENOENT.
|
|
|
- */
|
|
|
- if (aq_ret && !(aq_err == I40E_AQ_RC_ENOENT)) {
|
|
|
- retval = -EIO;
|
|
|
- dev_info(&pf->pdev->dev,
|
|
|
- "ignoring delete macvlan error on %s, err %s aq_err %s\n",
|
|
|
- vsi_name,
|
|
|
- i40e_stat_str(hw, aq_ret),
|
|
|
- i40e_aq_str(hw, aq_err));
|
|
|
- }
|
|
|
+ i40e_aqc_del_filters(vsi, vsi_name, del_list,
|
|
|
+ num_del, &retval);
|
|
|
}
|
|
|
|
|
|
kfree(del_list);
|
|
|
del_list = NULL;
|
|
|
}
|
|
|
|
|
|
- if (!list_empty(&tmp_add_list)) {
|
|
|
+ /* After finishing notifying firmware of the deleted filters, update
|
|
|
+ * the cached value of vsi->has_vlan_filter. Note that we are safe to
|
|
|
+ * use just !!vlan_filters here because if we only have VLAN=0 (that
|
|
|
+ * is, non_vlan_filters) these will all be converted to VLAN=-1 in the
|
|
|
+ * logic above already so this value would still be correct.
|
|
|
+ */
|
|
|
+ vsi->has_vlan_filter = !!vlan_filters;
|
|
|
+
|
|
|
+ if (!hlist_empty(&tmp_add_list)) {
|
|
|
/* Do all the adds now. */
|
|
|
filter_list_len = hw->aq.asq_buf_size /
|
|
|
sizeof(struct i40e_aqc_add_macvlan_element_data);
|
|
|
list_size = filter_list_len *
|
|
|
sizeof(struct i40e_aqc_add_macvlan_element_data);
|
|
|
add_list = kzalloc(list_size, GFP_ATOMIC);
|
|
|
- if (!add_list) {
|
|
|
- retval = -ENOMEM;
|
|
|
- goto out;
|
|
|
- }
|
|
|
+ if (!add_list)
|
|
|
+ goto err_no_memory;
|
|
|
+
|
|
|
num_add = 0;
|
|
|
- list_for_each_entry(f, &tmp_add_list, list) {
|
|
|
+ hlist_for_each_entry(f, &tmp_add_list, hlist) {
|
|
|
if (test_bit(__I40E_FILTER_OVERFLOW_PROMISC,
|
|
|
&vsi->state)) {
|
|
|
f->state = I40E_FILTER_FAILED;
|
|
@@ -1973,57 +2085,28 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
|
|
|
|
|
|
/* flush a full buffer */
|
|
|
if (num_add == filter_list_len) {
|
|
|
- aq_ret = i40e_aq_add_macvlan(hw, vsi->seid,
|
|
|
- add_list, num_add,
|
|
|
- NULL);
|
|
|
- aq_err = hw->aq.asq_last_status;
|
|
|
- fcnt = i40e_update_filter_state(num_add,
|
|
|
- add_list,
|
|
|
- add_head,
|
|
|
- aq_ret);
|
|
|
- vsi->active_filters += fcnt;
|
|
|
-
|
|
|
- if (fcnt != num_add) {
|
|
|
- promisc_changed = true;
|
|
|
- set_bit(__I40E_FILTER_OVERFLOW_PROMISC,
|
|
|
- &vsi->state);
|
|
|
- vsi->promisc_threshold =
|
|
|
- (vsi->active_filters * 3) / 4;
|
|
|
- dev_warn(&pf->pdev->dev,
|
|
|
- "Error %s adding RX filters on %s, promiscuous mode forced on\n",
|
|
|
- i40e_aq_str(hw, aq_err),
|
|
|
- vsi_name);
|
|
|
- }
|
|
|
+ i40e_aqc_add_filters(vsi, vsi_name, add_list,
|
|
|
+ add_head, num_add,
|
|
|
+ &promisc_changed);
|
|
|
memset(add_list, 0, list_size);
|
|
|
num_add = 0;
|
|
|
}
|
|
|
}
|
|
|
if (num_add) {
|
|
|
- aq_ret = i40e_aq_add_macvlan(hw, vsi->seid,
|
|
|
- add_list, num_add, NULL);
|
|
|
- aq_err = hw->aq.asq_last_status;
|
|
|
- fcnt = i40e_update_filter_state(num_add, add_list,
|
|
|
- add_head, aq_ret);
|
|
|
- vsi->active_filters += fcnt;
|
|
|
- if (fcnt != num_add) {
|
|
|
- promisc_changed = true;
|
|
|
- set_bit(__I40E_FILTER_OVERFLOW_PROMISC,
|
|
|
- &vsi->state);
|
|
|
- vsi->promisc_threshold =
|
|
|
- (vsi->active_filters * 3) / 4;
|
|
|
- dev_warn(&pf->pdev->dev,
|
|
|
- "Error %s adding RX filters on %s, promiscuous mode forced on\n",
|
|
|
- i40e_aq_str(hw, aq_err), vsi_name);
|
|
|
- }
|
|
|
+ i40e_aqc_add_filters(vsi, vsi_name, add_list, add_head,
|
|
|
+ num_add, &promisc_changed);
|
|
|
}
|
|
|
/* Now move all of the filters from the temp add list back to
|
|
|
* the VSI's list.
|
|
|
*/
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
- list_for_each_entry_safe(f, ftmp, &tmp_add_list, list) {
|
|
|
- list_move_tail(&f->list, &vsi->mac_filter_list);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
+ hlist_for_each_entry_safe(f, h, &tmp_add_list, hlist) {
|
|
|
+ u64 key = i40e_addr_to_hkey(f->macaddr);
|
|
|
+
|
|
|
+ hlist_del(&f->hlist);
|
|
|
+ hash_add(vsi->mac_filter_hash, &f->hlist, key);
|
|
|
}
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
kfree(add_list);
|
|
|
add_list = NULL;
|
|
|
}
|
|
@@ -2035,12 +2118,12 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
|
|
|
/* See if we have any failed filters. We can't drop out of
|
|
|
* promiscuous until these have all been deleted.
|
|
|
*/
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
- list_for_each_entry(f, &vsi->mac_filter_list, list) {
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
+ hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) {
|
|
|
if (f->state == I40E_FILTER_FAILED)
|
|
|
failed_count++;
|
|
|
}
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
if (!failed_count) {
|
|
|
dev_info(&pf->pdev->dev,
|
|
|
"filter logjam cleared on %s, leaving overflow promiscuous mode\n",
|
|
@@ -2168,6 +2251,18 @@ out:
|
|
|
|
|
|
clear_bit(__I40E_CONFIG_BUSY, &vsi->state);
|
|
|
return retval;
|
|
|
+
|
|
|
+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);
|
|
|
+
|
|
|
+ vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
|
|
|
+ clear_bit(__I40E_CONFIG_BUSY, &vsi->state);
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2322,34 +2417,33 @@ static void i40e_vlan_rx_register(struct net_device *netdev, u32 features)
|
|
|
**/
|
|
|
int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
|
|
|
{
|
|
|
- struct i40e_mac_filter *f, *ftmp, *add_f;
|
|
|
- bool is_netdev, is_vf;
|
|
|
-
|
|
|
- is_vf = (vsi->type == I40E_VSI_SRIOV);
|
|
|
- is_netdev = !!(vsi->netdev);
|
|
|
+ struct i40e_mac_filter *f, *add_f, *del_f;
|
|
|
+ struct hlist_node *h;
|
|
|
+ int bkt;
|
|
|
|
|
|
/* Locked once because all functions invoked below iterates list*/
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
|
|
|
- if (is_netdev) {
|
|
|
- add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, vid,
|
|
|
- is_vf, is_netdev);
|
|
|
+ if (vsi->netdev) {
|
|
|
+ add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, vid);
|
|
|
if (!add_f) {
|
|
|
dev_info(&vsi->back->pdev->dev,
|
|
|
"Could not add vlan filter %d for %pM\n",
|
|
|
vid, vsi->netdev->dev_addr);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
|
|
|
- add_f = i40e_add_filter(vsi, f->macaddr, vid, is_vf, is_netdev);
|
|
|
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
|
|
|
+ if (f->state == I40E_FILTER_REMOVE)
|
|
|
+ continue;
|
|
|
+ add_f = i40e_add_filter(vsi, f->macaddr, vid);
|
|
|
if (!add_f) {
|
|
|
dev_info(&vsi->back->pdev->dev,
|
|
|
"Could not add vlan filter %d for %pM\n",
|
|
|
vid, f->macaddr);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
}
|
|
@@ -2359,19 +2453,17 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
|
|
|
* with 0, so we now accept untagged and specified tagged traffic
|
|
|
* (and not all tags along with untagged)
|
|
|
*/
|
|
|
- if (vid > 0) {
|
|
|
- if (is_netdev && i40e_find_filter(vsi, vsi->netdev->dev_addr,
|
|
|
- I40E_VLAN_ANY,
|
|
|
- is_vf, is_netdev)) {
|
|
|
- i40e_del_filter(vsi, vsi->netdev->dev_addr,
|
|
|
- I40E_VLAN_ANY, is_vf, is_netdev);
|
|
|
- add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, 0,
|
|
|
- is_vf, is_netdev);
|
|
|
+ if (vid > 0 && vsi->netdev) {
|
|
|
+ del_f = i40e_find_filter(vsi, vsi->netdev->dev_addr,
|
|
|
+ I40E_VLAN_ANY);
|
|
|
+ if (del_f) {
|
|
|
+ __i40e_del_filter(vsi, del_f);
|
|
|
+ add_f = i40e_add_filter(vsi, vsi->netdev->dev_addr, 0);
|
|
|
if (!add_f) {
|
|
|
dev_info(&vsi->back->pdev->dev,
|
|
|
"Could not add filter 0 for %pM\n",
|
|
|
vsi->netdev->dev_addr);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
}
|
|
@@ -2379,25 +2471,26 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
|
|
|
|
|
|
/* Do not assume that I40E_VLAN_ANY should be reset to VLAN 0 */
|
|
|
if (vid > 0 && !vsi->info.pvid) {
|
|
|
- list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
|
|
|
- if (!i40e_find_filter(vsi, f->macaddr, I40E_VLAN_ANY,
|
|
|
- is_vf, is_netdev))
|
|
|
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
|
|
|
+ if (f->state == I40E_FILTER_REMOVE)
|
|
|
continue;
|
|
|
- i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY,
|
|
|
- is_vf, is_netdev);
|
|
|
- add_f = i40e_add_filter(vsi, f->macaddr,
|
|
|
- 0, is_vf, is_netdev);
|
|
|
+ del_f = i40e_find_filter(vsi, f->macaddr,
|
|
|
+ I40E_VLAN_ANY);
|
|
|
+ if (!del_f)
|
|
|
+ continue;
|
|
|
+ __i40e_del_filter(vsi, del_f);
|
|
|
+ add_f = i40e_add_filter(vsi, f->macaddr, 0);
|
|
|
if (!add_f) {
|
|
|
dev_info(&vsi->back->pdev->dev,
|
|
|
"Could not add filter 0 for %pM\n",
|
|
|
f->macaddr);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
|
|
|
/* schedule our worker thread which will take care of
|
|
|
* applying the new filter changes
|
|
@@ -2410,79 +2503,31 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
|
|
|
* i40e_vsi_kill_vlan - Remove vsi membership for given vlan
|
|
|
* @vsi: the vsi being configured
|
|
|
* @vid: vlan id to be removed (0 = untagged only , -1 = any)
|
|
|
- *
|
|
|
- * Return: 0 on success or negative otherwise
|
|
|
**/
|
|
|
-int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
|
|
|
+void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
|
|
|
{
|
|
|
struct net_device *netdev = vsi->netdev;
|
|
|
- struct i40e_mac_filter *f, *ftmp, *add_f;
|
|
|
- bool is_vf, is_netdev;
|
|
|
- int filter_count = 0;
|
|
|
-
|
|
|
- is_vf = (vsi->type == I40E_VSI_SRIOV);
|
|
|
- is_netdev = !!(netdev);
|
|
|
+ struct i40e_mac_filter *f;
|
|
|
+ struct hlist_node *h;
|
|
|
+ int bkt;
|
|
|
|
|
|
/* Locked once because all functions invoked below iterates list */
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
-
|
|
|
- if (is_netdev)
|
|
|
- i40e_del_filter(vsi, netdev->dev_addr, vid, is_vf, is_netdev);
|
|
|
-
|
|
|
- list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list)
|
|
|
- i40e_del_filter(vsi, f->macaddr, vid, is_vf, is_netdev);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
|
|
|
- /* 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)
|
|
|
- */
|
|
|
- list_for_each_entry(f, &vsi->mac_filter_list, list) {
|
|
|
- if (is_netdev) {
|
|
|
- if (f->vlan &&
|
|
|
- ether_addr_equal(netdev->dev_addr, f->macaddr))
|
|
|
- filter_count++;
|
|
|
- }
|
|
|
-
|
|
|
- if (f->vlan)
|
|
|
- filter_count++;
|
|
|
- }
|
|
|
-
|
|
|
- if (!filter_count && is_netdev) {
|
|
|
- i40e_del_filter(vsi, netdev->dev_addr, 0, is_vf, is_netdev);
|
|
|
- f = i40e_add_filter(vsi, netdev->dev_addr, I40E_VLAN_ANY,
|
|
|
- is_vf, is_netdev);
|
|
|
- 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_list_lock);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (vsi->netdev)
|
|
|
+ i40e_del_filter(vsi, netdev->dev_addr, vid);
|
|
|
|
|
|
- if (!filter_count) {
|
|
|
- list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
|
|
|
- i40e_del_filter(vsi, f->macaddr, 0, is_vf, is_netdev);
|
|
|
- add_f = i40e_add_filter(vsi, f->macaddr, I40E_VLAN_ANY,
|
|
|
- is_vf, is_netdev);
|
|
|
- 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_list_lock);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- }
|
|
|
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
|
|
|
+ if (f->vlan == vid)
|
|
|
+ __i40e_del_filter(vsi, f);
|
|
|
}
|
|
|
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
|
|
|
/* schedule our worker thread which will take care of
|
|
|
* applying the new filter changes
|
|
|
*/
|
|
|
i40e_service_event_schedule(vsi->back);
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -3969,29 +4014,35 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * i40e_vsi_control_rings - Start or stop a VSI's rings
|
|
|
+ * i40e_vsi_start_rings - Start a VSI's rings
|
|
|
* @vsi: the VSI being configured
|
|
|
- * @enable: start or stop the rings
|
|
|
**/
|
|
|
-int i40e_vsi_control_rings(struct i40e_vsi *vsi, bool request)
|
|
|
+int i40e_vsi_start_rings(struct i40e_vsi *vsi)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
|
|
|
/* do rx first for enable and last for disable */
|
|
|
- if (request) {
|
|
|
- ret = i40e_vsi_control_rx(vsi, request);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- ret = i40e_vsi_control_tx(vsi, request);
|
|
|
- } else {
|
|
|
- /* Ignore return value, we need to shutdown whatever we can */
|
|
|
- i40e_vsi_control_tx(vsi, request);
|
|
|
- i40e_vsi_control_rx(vsi, request);
|
|
|
- }
|
|
|
+ ret = i40e_vsi_control_rx(vsi, true);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ ret = i40e_vsi_control_tx(vsi, true);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * i40e_vsi_stop_rings - Stop a VSI's rings
|
|
|
+ * @vsi: the VSI being configured
|
|
|
+ **/
|
|
|
+void i40e_vsi_stop_rings(struct i40e_vsi *vsi)
|
|
|
+{
|
|
|
+ /* do rx first for enable and last for disable
|
|
|
+ * Ignore return value, we need to shutdown whatever we can
|
|
|
+ */
|
|
|
+ i40e_vsi_control_tx(vsi, false);
|
|
|
+ i40e_vsi_control_rx(vsi, false);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* i40e_vsi_free_irq - Free the irq association with the OS
|
|
|
* @vsi: the VSI being configured
|
|
@@ -5190,7 +5241,7 @@ static int i40e_up_complete(struct i40e_vsi *vsi)
|
|
|
i40e_configure_msi_and_legacy(vsi);
|
|
|
|
|
|
/* start rings */
|
|
|
- err = i40e_vsi_control_rings(vsi, true);
|
|
|
+ err = i40e_vsi_start_rings(vsi);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
@@ -5287,7 +5338,7 @@ void i40e_down(struct i40e_vsi *vsi)
|
|
|
netif_tx_disable(vsi->netdev);
|
|
|
}
|
|
|
i40e_vsi_disable_irq(vsi);
|
|
|
- i40e_vsi_control_rings(vsi, false);
|
|
|
+ i40e_vsi_stop_rings(vsi);
|
|
|
i40e_napi_disable_all(vsi);
|
|
|
|
|
|
for (i = 0; i < vsi->num_queue_pairs; i++) {
|
|
@@ -6670,7 +6721,6 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi);
|
|
|
static void i40e_fdir_sb_setup(struct i40e_pf *pf)
|
|
|
{
|
|
|
struct i40e_vsi *vsi;
|
|
|
- int i;
|
|
|
|
|
|
/* quick workaround for an NVM issue that leaves a critical register
|
|
|
* uninitialized
|
|
@@ -6681,6 +6731,7 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf)
|
|
|
0xeacb7d61, 0xaa4f05b6, 0x9c5c89ed, 0xfc425ddb,
|
|
|
0xa4654832, 0xfc7461d4, 0x8f827619, 0xf5c63c21,
|
|
|
0x95b3a76d};
|
|
|
+ int i;
|
|
|
|
|
|
for (i = 0; i <= I40E_GLQF_HKEY_MAX_INDEX; i++)
|
|
|
wr32(&pf->hw, I40E_GLQF_HKEY(i), hkey[i]);
|
|
@@ -6690,13 +6741,7 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf)
|
|
|
return;
|
|
|
|
|
|
/* find existing VSI and see if it needs configuring */
|
|
|
- vsi = NULL;
|
|
|
- for (i = 0; i < pf->num_alloc_vsi; i++) {
|
|
|
- if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
|
|
|
- vsi = pf->vsi[i];
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR);
|
|
|
|
|
|
/* create a new VSI if none exists */
|
|
|
if (!vsi) {
|
|
@@ -6718,15 +6763,12 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf)
|
|
|
**/
|
|
|
static void i40e_fdir_teardown(struct i40e_pf *pf)
|
|
|
{
|
|
|
- int i;
|
|
|
+ struct i40e_vsi *vsi;
|
|
|
|
|
|
i40e_fdir_filter_exit(pf);
|
|
|
- for (i = 0; i < pf->num_alloc_vsi; i++) {
|
|
|
- if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
|
|
|
- i40e_vsi_release(pf->vsi[i]);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR);
|
|
|
+ if (vsi)
|
|
|
+ i40e_vsi_release(vsi);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -7354,7 +7396,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
|
|
|
pf->rss_table_size : 64;
|
|
|
vsi->netdev_registered = false;
|
|
|
vsi->work_limit = I40E_DEFAULT_IRQ_WORK;
|
|
|
- INIT_LIST_HEAD(&vsi->mac_filter_list);
|
|
|
+ hash_init(vsi->mac_filter_hash);
|
|
|
vsi->irqs_ready = false;
|
|
|
|
|
|
ret = i40e_set_num_rings_in_vsi(vsi);
|
|
@@ -7369,7 +7411,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
|
|
|
i40e_vsi_setup_irqhandler(vsi, i40e_msix_clean_rings);
|
|
|
|
|
|
/* Initialize VSI lock */
|
|
|
- spin_lock_init(&vsi->mac_filter_list_lock);
|
|
|
+ spin_lock_init(&vsi->mac_filter_hash_lock);
|
|
|
pf->vsi[vsi_idx] = vsi;
|
|
|
ret = vsi_idx;
|
|
|
goto unlock_pf;
|
|
@@ -9154,18 +9196,18 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
|
|
|
if (vsi->type == I40E_VSI_MAIN) {
|
|
|
SET_NETDEV_DEV(netdev, &pf->pdev->dev);
|
|
|
ether_addr_copy(mac_addr, hw->mac.perm_addr);
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
- i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY, false, true);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
+ i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
} else {
|
|
|
/* relate the VSI_VMDQ name to the VSI_MAIN name */
|
|
|
snprintf(netdev->name, IFNAMSIZ, "%sv%%d",
|
|
|
pf->vsi[pf->lan_vsi]->netdev->name);
|
|
|
random_ether_addr(mac_addr);
|
|
|
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
- i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY, false, false);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
+ i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
}
|
|
|
|
|
|
ether_addr_copy(netdev->dev_addr, mac_addr);
|
|
@@ -9254,7 +9296,9 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
|
|
|
struct i40e_pf *pf = vsi->back;
|
|
|
struct i40e_hw *hw = &pf->hw;
|
|
|
struct i40e_vsi_context ctxt;
|
|
|
- struct i40e_mac_filter *f, *ftmp;
|
|
|
+ struct i40e_mac_filter *f;
|
|
|
+ struct hlist_node *h;
|
|
|
+ int bkt;
|
|
|
|
|
|
u8 enabled_tc = 0x1; /* TC0 enabled */
|
|
|
int f_count = 0;
|
|
@@ -9453,13 +9497,13 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
|
|
|
|
|
|
vsi->active_filters = 0;
|
|
|
clear_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state);
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
/* If macvlan filters already exist, force them to get loaded */
|
|
|
- list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) {
|
|
|
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
|
|
|
f->state = I40E_FILTER_NEW;
|
|
|
f_count++;
|
|
|
}
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
|
|
|
if (f_count) {
|
|
|
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
|
|
@@ -9489,11 +9533,12 @@ err:
|
|
|
**/
|
|
|
int i40e_vsi_release(struct i40e_vsi *vsi)
|
|
|
{
|
|
|
- struct i40e_mac_filter *f, *ftmp;
|
|
|
+ struct i40e_mac_filter *f;
|
|
|
+ struct hlist_node *h;
|
|
|
struct i40e_veb *veb = NULL;
|
|
|
struct i40e_pf *pf;
|
|
|
u16 uplink_seid;
|
|
|
- int i, n;
|
|
|
+ int i, n, bkt;
|
|
|
|
|
|
pf = vsi->back;
|
|
|
|
|
@@ -9523,11 +9568,19 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
|
|
|
i40e_vsi_disable_irq(vsi);
|
|
|
}
|
|
|
|
|
|
- spin_lock_bh(&vsi->mac_filter_list_lock);
|
|
|
- list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list)
|
|
|
- i40e_del_filter(vsi, f->macaddr, f->vlan,
|
|
|
- f->is_vf, f->is_netdev);
|
|
|
- spin_unlock_bh(&vsi->mac_filter_list_lock);
|
|
|
+ spin_lock_bh(&vsi->mac_filter_hash_lock);
|
|
|
+
|
|
|
+ /* clear the sync flag on all filters */
|
|
|
+ if (vsi->netdev) {
|
|
|
+ __dev_uc_unsync(vsi->netdev, NULL);
|
|
|
+ __dev_mc_unsync(vsi->netdev, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* make sure any remaining filters are marked for deletion */
|
|
|
+ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist)
|
|
|
+ __i40e_del_filter(vsi, f);
|
|
|
+
|
|
|
+ spin_unlock_bh(&vsi->mac_filter_hash_lock);
|
|
|
|
|
|
i40e_sync_vsi_filters(vsi);
|
|
|
|