diff options
author | Roopa Prabhu <roopa@cumulusnetworks.com> | 2020-05-29 08:12:36 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-05-31 07:47:08 +0300 |
commit | 79472fe873dd307101f9b1dbfe0fedebae42219a (patch) | |
tree | 8bb2e4e1e5e7f223f0cbaf995bcb7a558287aef6 /drivers/net/vxlan.c | |
parent | 72b4868211a85d040c42444620f2197bb0094ac8 (diff) | |
download | linux-79472fe873dd307101f9b1dbfe0fedebae42219a.tar.xz |
vxlan: few locking fixes in nexthop event handler
- remove fdb from nh_list before the rcu grace period
- protect fdb->vdev with rcu
- hold spin lock before destroying fdb
Fixes: c7cdbe2efc40 ("vxlan: support for nexthop notifiers")
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
Reviewed-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/vxlan.c')
-rw-r--r-- | drivers/net/vxlan.c | 32 |
1 files changed, 25 insertions, 7 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index fe606c688855..39bc10a7fd2e 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -81,7 +81,7 @@ struct vxlan_fdb { u16 flags; /* see ndm_flags and below */ struct list_head nh_list; struct nexthop __rcu *nh; - struct vxlan_dev *vdev; + struct vxlan_dev __rcu *vdev; }; #define NTF_VXLAN_ADDED_BY_USER 0x100 @@ -837,7 +837,7 @@ static struct vxlan_fdb *vxlan_fdb_alloc(struct vxlan_dev *vxlan, const u8 *mac, f->updated = f->used = jiffies; f->vni = src_vni; f->nh = NULL; - f->vdev = vxlan; + RCU_INIT_POINTER(f->vdev, vxlan); INIT_LIST_HEAD(&f->nh_list); INIT_LIST_HEAD(&f->remotes); memcpy(f->eth_addr, mac, ETH_ALEN); @@ -963,7 +963,7 @@ static void __vxlan_fdb_free(struct vxlan_fdb *f) nh = rcu_dereference_raw(f->nh); if (nh) { rcu_assign_pointer(f->nh, NULL); - list_del_rcu(&f->nh_list); + rcu_assign_pointer(f->vdev, NULL); nexthop_put(nh); } @@ -1000,7 +1000,7 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f, } hlist_del_rcu(&f->hlist); - f->vdev = NULL; + list_del_rcu(&f->nh_list); call_rcu(&f->rcu, vxlan_fdb_free); } @@ -4615,17 +4615,35 @@ static struct notifier_block vxlan_switchdev_notifier_block __read_mostly = { .notifier_call = vxlan_switchdev_event, }; +static void vxlan_fdb_nh_flush(struct nexthop *nh) +{ + struct vxlan_fdb *fdb; + struct vxlan_dev *vxlan; + u32 hash_index; + + rcu_read_lock(); + list_for_each_entry_rcu(fdb, &nh->fdb_list, nh_list) { + vxlan = rcu_dereference(fdb->vdev); + WARN_ON(!vxlan); + hash_index = fdb_head_index(vxlan, fdb->eth_addr, + vxlan->default_dst.remote_vni); + spin_lock_bh(&vxlan->hash_lock[hash_index]); + if (!hlist_unhashed(&fdb->hlist)) + vxlan_fdb_destroy(vxlan, fdb, false, false); + spin_unlock_bh(&vxlan->hash_lock[hash_index]); + } + rcu_read_unlock(); +} + static int vxlan_nexthop_event(struct notifier_block *nb, unsigned long event, void *ptr) { struct nexthop *nh = ptr; - struct vxlan_fdb *fdb, *tmp; if (!nh || event != NEXTHOP_EVENT_DEL) return NOTIFY_DONE; - list_for_each_entry_safe(fdb, tmp, &nh->fdb_list, nh_list) - vxlan_fdb_destroy(fdb->vdev, fdb, false, false); + vxlan_fdb_nh_flush(nh); return NOTIFY_DONE; } |