[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20211025222415.983883-14-vladimir.oltean@nxp.com>
Date: Tue, 26 Oct 2021 01:24:13 +0300
From: Vladimir Oltean <vladimir.oltean@....com>
To: netdev@...r.kernel.org, Roopa Prabhu <roopa@...dia.com>,
Nikolay Aleksandrov <nikolay@...dia.com>,
Ido Schimmel <idosch@...dia.com>
Cc: Jakub Kicinski <kuba@...nel.org>,
"David S. Miller" <davem@...emloft.net>,
Andrew Lunn <andrew@...n.ch>,
Florian Fainelli <f.fainelli@...il.com>,
Vivien Didelot <vivien.didelot@...il.com>,
Vladimir Oltean <olteanv@...il.com>,
Jiri Pirko <jiri@...dia.com>
Subject: [RFC PATCH net-next 13/15] net: bridge: wait for errors from switchdev when deleting FDB entries
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@....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
Powered by blists - more mailing lists