Skip to content

Commit f30490e

Browse files
aloktionanguy11
authored andcommitted
i40e: fix race condition by adding filter's intermediate sync state
Fix a race condition in the i40e driver that leads to MAC/VLAN filters becoming corrupted and leaking. Address the issue that occurs under heavy load when multiple threads are concurrently modifying MAC/VLAN filters by setting mac and port VLAN. 1. Thread T0 allocates a filter in i40e_add_filter() within i40e_ndo_set_vf_port_vlan(). 2. Thread T1 concurrently frees the filter in __i40e_del_filter() within i40e_ndo_set_vf_mac(). 3. Subsequently, i40e_service_task() calls i40e_sync_vsi_filters(), which refers to the already freed filter memory, causing corruption. Reproduction steps: 1. Spawn multiple VFs. 2. Apply a concurrent heavy load by running parallel operations to change MAC addresses on the VFs and change port VLANs on the host. 3. Observe errors in dmesg: "Error I40E_AQ_RC_ENOSPC adding RX filters on VF XX, please set promiscuous on manually for VF XX". Exact code for stable reproduction Intel can't open-source now. The fix involves implementing a new intermediate filter state, I40E_FILTER_NEW_SYNC, for the time when a filter is on a tmp_add_list. These filters cannot be deleted from the hash list directly but must be removed using the full process. Fixes: 278e7d0 ("i40e: store MAC/VLAN filters in a hash with the MAC Address as key") Signed-off-by: Aleksandr Loktionov <[email protected]> Tested-by: Pucha Himasekhar Reddy <[email protected]> (A Contingent worker at Intel) Reviewed-by: Michal Schmidt <[email protected]> Tested-by: Michal Schmidt <[email protected]> Signed-off-by: Tony Nguyen <[email protected]>
1 parent 9b58031 commit f30490e

File tree

3 files changed

+12
-2
lines changed

3 files changed

+12
-2
lines changed

drivers/net/ethernet/intel/i40e/i40e.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ enum i40e_filter_state {
755755
I40E_FILTER_ACTIVE, /* Added to switch by FW */
756756
I40E_FILTER_FAILED, /* Rejected by FW */
757757
I40E_FILTER_REMOVE, /* To be removed */
758+
I40E_FILTER_NEW_SYNC, /* New, not sent yet, is in i40e_sync_vsi_filters() */
758759
/* There is no 'removed' state; the filter struct is freed */
759760
};
760761
struct i40e_mac_filter {

drivers/net/ethernet/intel/i40e/i40e_debugfs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ static char *i40e_filter_state_string[] = {
8989
"ACTIVE",
9090
"FAILED",
9191
"REMOVE",
92+
"NEW_SYNC",
9293
};
9394

9495
/**

drivers/net/ethernet/intel/i40e/i40e_main.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,7 @@ int i40e_count_filters(struct i40e_vsi *vsi)
12551255

12561256
hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
12571257
if (f->state == I40E_FILTER_NEW ||
1258+
f->state == I40E_FILTER_NEW_SYNC ||
12581259
f->state == I40E_FILTER_ACTIVE)
12591260
++cnt;
12601261
}
@@ -1441,6 +1442,8 @@ static int i40e_correct_mac_vlan_filters(struct i40e_vsi *vsi,
14411442

14421443
new->f = add_head;
14431444
new->state = add_head->state;
1445+
if (add_head->state == I40E_FILTER_NEW)
1446+
add_head->state = I40E_FILTER_NEW_SYNC;
14441447

14451448
/* Add the new filter to the tmp list */
14461449
hlist_add_head(&new->hlist, tmp_add_list);
@@ -1550,6 +1553,8 @@ static int i40e_correct_vf_mac_vlan_filters(struct i40e_vsi *vsi,
15501553
return -ENOMEM;
15511554
new_mac->f = add_head;
15521555
new_mac->state = add_head->state;
1556+
if (add_head->state == I40E_FILTER_NEW)
1557+
add_head->state = I40E_FILTER_NEW_SYNC;
15531558

15541559
/* Add the new filter to the tmp list */
15551560
hlist_add_head(&new_mac->hlist, tmp_add_list);
@@ -2437,7 +2442,8 @@ static int
24372442
i40e_aqc_broadcast_filter(struct i40e_vsi *vsi, const char *vsi_name,
24382443
struct i40e_mac_filter *f)
24392444
{
2440-
bool enable = f->state == I40E_FILTER_NEW;
2445+
bool enable = f->state == I40E_FILTER_NEW ||
2446+
f->state == I40E_FILTER_NEW_SYNC;
24412447
struct i40e_hw *hw = &vsi->back->hw;
24422448
int aq_ret;
24432449

@@ -2611,6 +2617,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
26112617

26122618
/* Add it to the hash list */
26132619
hlist_add_head(&new->hlist, &tmp_add_list);
2620+
f->state = I40E_FILTER_NEW_SYNC;
26142621
}
26152622

26162623
/* Count the number of active (current and new) VLAN
@@ -2762,7 +2769,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
27622769
spin_lock_bh(&vsi->mac_filter_hash_lock);
27632770
hlist_for_each_entry_safe(new, h, &tmp_add_list, hlist) {
27642771
/* Only update the state if we're still NEW */
2765-
if (new->f->state == I40E_FILTER_NEW)
2772+
if (new->f->state == I40E_FILTER_NEW ||
2773+
new->f->state == I40E_FILTER_NEW_SYNC)
27662774
new->f->state = new->state;
27672775
hlist_del(&new->hlist);
27682776
netdev_hw_addr_refcnt(new->f, vsi->netdev, -1);

0 commit comments

Comments
 (0)