summaryrefslogtreecommitdiff
path: root/drivers/net/vrf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/vrf.c')
-rw-r--r--drivers/net/vrf.c15
1 files changed, 11 insertions, 4 deletions
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 2cf2dbd1c12f..46209917ae4d 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -1034,6 +1034,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev,
err:
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
+ synchronize_net();
return ret;
}
@@ -1053,10 +1054,16 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev,
}
/* inverse of do_vrf_add_slave */
-static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
+static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev,
+ bool needs_sync)
{
netdev_upper_dev_unlink(port_dev, dev);
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
+ /* Make sure that concurrent RCU readers that identified the device
+ * as a VRF port see a VRF master or no master at all.
+ */
+ if (needs_sync)
+ synchronize_net();
cycle_netdev(port_dev, NULL);
@@ -1065,7 +1072,7 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
{
- return do_vrf_del_slave(dev, port_dev);
+ return do_vrf_del_slave(dev, port_dev, true);
}
static void vrf_dev_uninit(struct net_device *dev)
@@ -1619,7 +1626,7 @@ static void vrf_dellink(struct net_device *dev, struct list_head *head)
struct list_head *iter;
netdev_for_each_lower_dev(dev, port_dev, iter)
- vrf_del_slave(dev, port_dev);
+ do_vrf_del_slave(dev, port_dev, false);
vrf_map_unregister_dev(dev);
@@ -1751,7 +1758,7 @@ static int vrf_device_event(struct notifier_block *unused,
goto out;
vrf_dev = netdev_master_upper_dev_get(dev);
- vrf_del_slave(vrf_dev, dev);
+ do_vrf_del_slave(vrf_dev, dev, false);
}
out:
return NOTIFY_DONE;