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.c103
1 files changed, 30 insertions, 73 deletions
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 8c009bcaa8e7..d4dc6d690a75 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -112,8 +112,8 @@ struct netns_vrf {
};
struct net_vrf {
- struct rtable __rcu *rth;
- struct rt6_info __rcu *rt6;
+ struct rtable *rth;
+ struct rt6_info *rt6;
#if IS_ENABLED(CONFIG_IPV6)
struct fib6_table *fib6_table;
#endif
@@ -648,26 +648,13 @@ static struct sk_buff *vrf_ip6_out_redirect(struct net_device *vrf_dev,
struct sk_buff *skb)
{
struct net_vrf *vrf = netdev_priv(vrf_dev);
- struct dst_entry *dst = NULL;
struct rt6_info *rt6;
- rcu_read_lock();
-
- rt6 = rcu_dereference(vrf->rt6);
- if (likely(rt6)) {
- dst = &rt6->dst;
- dst_hold(dst);
- }
-
- rcu_read_unlock();
-
- if (unlikely(!dst)) {
- vrf_tx_error(vrf_dev, skb);
- return NULL;
- }
+ rt6 = vrf->rt6;
+ dst_hold(&rt6->dst);
skb_dst_drop(skb);
- skb_dst_set(skb, dst);
+ skb_dst_set(skb, &rt6->dst);
return skb;
}
@@ -750,22 +737,11 @@ static struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev,
/* holding rtnl */
static void vrf_rt6_release(struct net_device *dev, struct net_vrf *vrf)
{
- struct rt6_info *rt6 = rtnl_dereference(vrf->rt6);
- struct net *net = dev_net(dev);
- struct dst_entry *dst;
+ struct rt6_info *rt6 = vrf->rt6;
- RCU_INIT_POINTER(vrf->rt6, NULL);
- synchronize_rcu();
-
- /* move dev in dst's to loopback so this VRF device can be deleted
- * - based on dst_ifdown
- */
if (rt6) {
- dst = &rt6->dst;
- netdev_ref_replace(dst->dev, net->loopback_dev,
- &dst->dev_tracker, GFP_KERNEL);
- dst->dev = net->loopback_dev;
- dst_release(dst);
+ dst_dev_put(&rt6->dst);
+ dst_release(&rt6->dst);
}
}
@@ -792,7 +768,7 @@ static int vrf_rt6_create(struct net_device *dev)
rt6->dst.output = vrf_output6;
- rcu_assign_pointer(vrf->rt6, rt6);
+ vrf->rt6 = rt6;
rc = 0;
out:
@@ -878,26 +854,13 @@ static struct sk_buff *vrf_ip_out_redirect(struct net_device *vrf_dev,
struct sk_buff *skb)
{
struct net_vrf *vrf = netdev_priv(vrf_dev);
- struct dst_entry *dst = NULL;
struct rtable *rth;
- rcu_read_lock();
-
- rth = rcu_dereference(vrf->rth);
- if (likely(rth)) {
- dst = &rth->dst;
- dst_hold(dst);
- }
-
- rcu_read_unlock();
-
- if (unlikely(!dst)) {
- vrf_tx_error(vrf_dev, skb);
- return NULL;
- }
+ rth = vrf->rth;
+ dst_hold(&rth->dst);
skb_dst_drop(skb);
- skb_dst_set(skb, dst);
+ skb_dst_set(skb, &rth->dst);
return skb;
}
@@ -997,23 +960,10 @@ static struct sk_buff *vrf_l3_out(struct net_device *vrf_dev,
/* holding rtnl */
static void vrf_rtable_release(struct net_device *dev, struct net_vrf *vrf)
{
- struct rtable *rth = rtnl_dereference(vrf->rth);
- struct net *net = dev_net(dev);
- struct dst_entry *dst;
+ struct rtable *rth = vrf->rth;
- RCU_INIT_POINTER(vrf->rth, NULL);
- synchronize_rcu();
-
- /* move dev in dst's to loopback so this VRF device can be deleted
- * - based on dst_ifdown
- */
- if (rth) {
- dst = &rth->dst;
- netdev_ref_replace(dst->dev, net->loopback_dev,
- &dst->dev_tracker, GFP_KERNEL);
- dst->dev = net->loopback_dev;
- dst_release(dst);
- }
+ dst_dev_put(&rth->dst);
+ dst_release(&rth->dst);
}
static int vrf_rtable_create(struct net_device *dev)
@@ -1031,7 +981,7 @@ static int vrf_rtable_create(struct net_device *dev)
rth->dst.output = vrf_output;
- rcu_assign_pointer(vrf->rth, rth);
+ vrf->rth = rth;
return 0;
}
@@ -1084,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;
}
@@ -1103,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);
@@ -1115,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)
@@ -1218,8 +1175,6 @@ static int vrf_prepare_mac_header(struct sk_buff *skb,
skb->protocol = eth->h_proto;
skb->pkt_type = PACKET_HOST;
- skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
-
skb_pull_inline(skb, ETH_HLEN);
return 0;
@@ -1669,7 +1624,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);
@@ -1801,7 +1756,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;
@@ -1975,7 +1930,9 @@ static int __init vrf_init_module(void)
{
int rc;
- register_netdevice_notifier(&vrf_notifier_block);
+ rc = register_netdevice_notifier(&vrf_notifier_block);
+ if (rc < 0)
+ return rc;
rc = register_pernet_subsys(&vrf_net_ops);
if (rc < 0)