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: c7cdbe2efc
("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>
This commit is contained in:
parent
72b4868211
commit
79472fe873
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue