summaryrefslogtreecommitdiff
path: root/drivers/net/vxlan.c
diff options
context:
space:
mode:
authorRoopa Prabhu <roopa@cumulusnetworks.com>2020-05-28 22:12:36 -0700
committerDavid S. Miller <davem@davemloft.net>2020-05-30 21:47:08 -0700
commit79472fe873dd307101f9b1dbfe0fedebae42219a (patch)
tree8bb2e4e1e5e7f223f0cbaf995bcb7a558287aef6 /drivers/net/vxlan.c
parent72b4868211a85d040c42444620f2197bb0094ac8 (diff)
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.c32
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;
}