Skip to content

Commit 458867b

Browse files
jacob-kellerJeff Kirsher
authored andcommitted
i40e: don't remove netdev->dev_addr when syncing uc list
In some circumstances, such as with bridging, it is possible that the stack will add a devices own MAC address to its unicast address list. If, later, the stack deletes this address, then the i40e driver will receive a request to remove this address. The driver stores its current MAC address as part of the MAC/VLAN hash array, since it is convenient and matches exactly how the hardware expects to be told which traffic to receive. This causes a problem, since for more devices, the MAC address is stored separately, and requests to delete a unicast address should not have the ability to remove the filter for the MAC address. Fix this by forcing a check on every address sync to ensure we do not remove the device address. There is a very narrow possibility of a race between .set_mac and .set_rx_mode, if we don't change netdev->dev_addr before updating our internal MAC list in .set_mac. This might be possible if .set_rx_mode is going to remove MAC "XYZ" from the list, at the same time as .set_mac changes our dev_addr to MAC "XYZ", we might possibly queue a delete, then an add in .set_mac, then queue a delete in .set_rx_mode's dev_uc_sync and then update netdev->dev_addr. We can avoid this by moving the copy into dev_addr prior to the changes to the MAC filter list. A similar race on the other side does not cause problems, as if we're changing our MAC form A to B, and we race with .set_rx_mode, it could queue a delete from A, we'd update our address, and allow the delete. This seems like a race, but in reality we're about to queue a delete of A anyways, so it would not cause any issues. A race in the initialization code is unlikely because the netdevice has not yet been fully initialized and the stack should not be adding or removing addresses yet. Note that we don't (yet) need similar code for the VF driver because it does not make use of __dev_uc_sync and __dev_mc_sync, but instead roles its own method for handling updates to the MAC/VLAN list, which already has code to protect against removal of the hardware address. Signed-off-by: Jacob Keller <[email protected]> Tested-by: Andrew Bowers <[email protected]> Signed-off-by: Jeff Kirsher <[email protected]>
1 parent 248de22 commit 458867b

File tree

1 file changed

+16
-1
lines changed

1 file changed

+16
-1
lines changed

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1573,11 +1573,18 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
15731573
else
15741574
netdev_info(netdev, "set new mac address %pM\n", addr->sa_data);
15751575

1576+
/* Copy the address first, so that we avoid a possible race with
1577+
* .set_rx_mode(). If we copy after changing the address in the filter
1578+
* list, we might open ourselves to a narrow race window where
1579+
* .set_rx_mode could delete our dev_addr filter and prevent traffic
1580+
* from passing.
1581+
*/
1582+
ether_addr_copy(netdev->dev_addr, addr->sa_data);
1583+
15761584
spin_lock_bh(&vsi->mac_filter_hash_lock);
15771585
i40e_del_mac_filter(vsi, netdev->dev_addr);
15781586
i40e_add_mac_filter(vsi, addr->sa_data);
15791587
spin_unlock_bh(&vsi->mac_filter_hash_lock);
1580-
ether_addr_copy(netdev->dev_addr, addr->sa_data);
15811588
if (vsi->type == I40E_VSI_MAIN) {
15821589
i40e_status ret;
15831590

@@ -1923,6 +1930,14 @@ static int i40e_addr_unsync(struct net_device *netdev, const u8 *addr)
19231930
struct i40e_netdev_priv *np = netdev_priv(netdev);
19241931
struct i40e_vsi *vsi = np->vsi;
19251932

1933+
/* Under some circumstances, we might receive a request to delete
1934+
* our own device address from our uc list. Because we store the
1935+
* device address in the VSI's MAC/VLAN filter list, we need to ignore
1936+
* such requests and not delete our device address from this list.
1937+
*/
1938+
if (ether_addr_equal(addr, netdev->dev_addr))
1939+
return 0;
1940+
19261941
i40e_del_mac_filter(vsi, addr);
19271942

19281943
return 0;

0 commit comments

Comments
 (0)