[RFC PATCH net-next 13/15] net: bridge: wait for errors from switchdev when deleting FDB entries
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Date: 2021-10-25 22:25:13
Subsystem:
ethernet bridge, networking [general], the rest · Maintainers:
Nikolay Aleksandrov, Ido Schimmel, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Similar to the logic added for RTM_NEWNEIGH, we can make the bridge RTM_DELNEIGH handler let switchdev veto an FDB entry deletion. The strategy is: - get hold of the FDB entry under &br->hash_lock - notify switchdev of its deletion via the atomic notifier chain - release the &br->hash_lock, wait for the response - switchdev vetoes => error out - switchdev agrees => attempt to get hold again of the FDB entry under &br->hash_lock, this time delete it without notifying switchdev _or_ rtnetlink (we've notified both already). Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> --- net/bridge/br_fdb.c | 82 ++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 38 deletions(-)
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index ce49e5f914b1..5f9bef6e4472 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c@@ -407,7 +407,7 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) } static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f, - bool swdev_notify) + bool notify_rtnl, bool swdev_notify) { trace_fdb_delete(br, f);
@@ -417,7 +417,8 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f, hlist_del_init_rcu(&f->fdb_node); rhashtable_remove_fast(&br->fdb_hash_tbl, &f->rhnode, br_fdb_rht_params); - br_fdb_notify(br, f, RTM_DELNEIGH, swdev_notify); + if (notify_rtnl) + br_fdb_notify(br, f, RTM_DELNEIGH, swdev_notify); call_rcu(&f->rcu, fdb_rcu_free); }
@@ -453,7 +454,7 @@ static void fdb_delete_local(struct net_bridge *br, return; } - fdb_delete(br, f, true); + fdb_delete(br, f, true, true); } void br_fdb_find_delete_local(struct net_bridge *br,
@@ -514,7 +515,7 @@ static int fdb_add_local(struct net_bridge *br, struct net_bridge_port *source, return 0; br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n", source ? source->dev->name : br->dev->name, addr, vid); - fdb_delete(br, fdb, true); + fdb_delete(br, fdb, true, true); } fdb = fdb_create(br, source, addr, vid,
@@ -639,7 +640,7 @@ void br_fdb_cleanup(struct work_struct *work) } else { spin_lock_bh(&br->hash_lock); if (!hlist_unhashed(&f->fdb_node)) - fdb_delete(br, f, true); + fdb_delete(br, f, true, true); spin_unlock_bh(&br->hash_lock); } }
@@ -659,7 +660,7 @@ void br_fdb_flush(struct net_bridge *br) spin_lock_bh(&br->hash_lock); hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) { if (!test_bit(BR_FDB_STATIC, &f->flags)) - fdb_delete(br, f, true); + fdb_delete(br, f, true, true); } spin_unlock_bh(&br->hash_lock); }
@@ -691,7 +692,7 @@ void br_fdb_delete_by_port(struct net_bridge *br, if (test_bit(BR_FDB_LOCAL, &f->flags)) fdb_delete_local(br, p, f); else - fdb_delete(br, f, true); + fdb_delete(br, f, true, true); } spin_unlock_bh(&br->hash_lock); }
@@ -931,9 +932,10 @@ int br_fdb_get(struct sk_buff *skb, } /* Delete an FDB entry and don't notify switchdev */ -static void fdb_delete_by_addr_and_port(struct net_bridge *br, - const struct net_bridge_port *p, - const u8 *addr, u16 vlan) +static int fdb_delete_by_addr_and_port(struct net_bridge *br, + const struct net_bridge_port *p, + const u8 *addr, u16 vlan, + bool notify_rtnl) { struct net_bridge_fdb_entry *fdb;
@@ -942,12 +944,14 @@ static void fdb_delete_by_addr_and_port(struct net_bridge *br, fdb = br_fdb_find(br, addr, vlan); if (!fdb || READ_ONCE(fdb->dst) != p) { spin_unlock_bh(&br->hash_lock); - return; + return -ENOENT; } - fdb_delete(br, fdb, false); + fdb_delete(br, fdb, notify_rtnl, false); spin_unlock_bh(&br->hash_lock); + + return 0; } /* returns true if the fdb is modified */
@@ -1079,7 +1083,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, err = br_switchdev_fdb_wait(&wait_ctx); if (err) - fdb_delete_by_addr_and_port(br, source, addr, vid); + fdb_delete_by_addr_and_port(br, source, addr, vid, true); return err; }
@@ -1208,36 +1212,38 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return err; } -/* Delete an FDB entry and notify switchdev. - * Caller must hold &br->hash_lock. - */ -static int -fdb_delete_by_addr_and_port_switchdev(struct net_bridge *br, - const struct net_bridge_port *p, - const u8 *addr, u16 vlan) +/* Delete an FDB entry and notify switchdev. */ +static int __br_fdb_delete(struct net_bridge *br, + const struct net_bridge_port *p, + const u8 *addr, u16 vlan, + struct netlink_ext_ack *extack) { + struct br_switchdev_fdb_wait_ctx wait_ctx; struct net_bridge_fdb_entry *fdb; + int err; - fdb = br_fdb_find(br, addr, vlan); - if (!fdb || READ_ONCE(fdb->dst) != p) - return -ENOENT; + br_switchdev_fdb_wait_ctx_init(&wait_ctx); - fdb_delete(br, fdb, true); + spin_lock_bh(&br->hash_lock); - return 0; -} + fdb = br_fdb_find(br, addr, vlan); + if (!fdb || READ_ONCE(fdb->dst) != p) { + spin_unlock_bh(&br->hash_lock); + return -ENOENT; + } -static int __br_fdb_delete(struct net_bridge *br, - const struct net_bridge_port *p, - const unsigned char *addr, u16 vid) -{ - int err; + br_fdb_notify_async(br, fdb, RTM_DELNEIGH, extack, &wait_ctx); - spin_lock_bh(&br->hash_lock); - err = fdb_delete_by_addr_and_port_switchdev(br, p, addr, vid); spin_unlock_bh(&br->hash_lock); - return err; + err = br_switchdev_fdb_wait(&wait_ctx); + if (err) + return err; + + /* We've notified rtnl and switchdev once, don't do it again, + * just delete. + */ + return fdb_delete_by_addr_and_port(br, p, addr, vlan, false); } /* Remove neighbor entry with RTM_DELNEIGH */
@@ -1273,17 +1279,17 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], return -EINVAL; } - err = __br_fdb_delete(br, p, addr, vid); + err = __br_fdb_delete(br, p, addr, vid, extack); } else { err = -ENOENT; - err &= __br_fdb_delete(br, p, addr, 0); + err &= __br_fdb_delete(br, p, addr, 0, extack); if (!vg || !vg->num_vlans) return err; list_for_each_entry(v, &vg->vlan_list, vlist) { if (!br_vlan_should_use(v)) continue; - err &= __br_fdb_delete(br, p, addr, v->vid); + err &= __br_fdb_delete(br, p, addr, v->vid, extack); } }
@@ -1414,7 +1420,7 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, fdb = br_fdb_find(br, addr, vid); if (fdb && test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) - fdb_delete(br, fdb, swdev_notify); + fdb_delete(br, fdb, true, swdev_notify); else err = -ENOENT;
--
2.25.1