summaryrefslogtreecommitdiffstats
path: root/net/bridge/br_fdb.c
diff options
context:
space:
mode:
authorNikolay Aleksandrov <razor@blackwall.org>2022-04-13 12:51:59 +0200
committerDavid S. Miller <davem@davemloft.net>2022-04-13 13:46:26 +0200
commit1f78ee14eeac03952ec765cea7ce23cbf12d6894 (patch)
tree26205da8814352b7c6e00fc0b416e3661084782b /net/bridge/br_fdb.c
parentnet: bridge: fdb: add ndo_fdb_del_bulk (diff)
downloadlinux-1f78ee14eeac03952ec765cea7ce23cbf12d6894.tar.xz
linux-1f78ee14eeac03952ec765cea7ce23cbf12d6894.zip
net: bridge: fdb: add support for fine-grained flushing
Add the ability to specify exactly which fdbs to be flushed. They are described by a new structure - net_bridge_fdb_flush_desc. Currently it can match on port/bridge ifindex, vlan id and fdb flags. It is used to describe the existing dynamic fdb flush operation. Note that this flush operation doesn't treat permanent entries in a special way (fdb_delete vs fdb_delete_local), it will delete them regardless if any port is using them, so currently it can't directly replace deletes which need to handle that case, although we can extend it later for that too. Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_fdb.c')
-rw-r--r--net/bridge/br_fdb.c41
1 files changed, 33 insertions, 8 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 363985f1a540..45d02f2264db 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -558,24 +558,49 @@ void br_fdb_cleanup(struct work_struct *work)
mod_delayed_work(system_long_wq, &br->gc_work, work_delay);
}
-/* Completely flush all dynamic entries in forwarding database.*/
-void br_fdb_flush(struct net_bridge *br)
+static bool __fdb_flush_matches(const struct net_bridge *br,
+ const struct net_bridge_fdb_entry *f,
+ const struct net_bridge_fdb_flush_desc *desc)
+{
+ const struct net_bridge_port *dst = READ_ONCE(f->dst);
+ int port_ifidx = dst ? dst->dev->ifindex : br->dev->ifindex;
+
+ if (desc->vlan_id && desc->vlan_id != f->key.vlan_id)
+ return false;
+ if (desc->port_ifindex && desc->port_ifindex != port_ifidx)
+ return false;
+ if (desc->flags_mask && (f->flags & desc->flags_mask) != desc->flags)
+ return false;
+
+ return true;
+}
+
+/* Flush forwarding database entries matching the description */
+void br_fdb_flush(struct net_bridge *br,
+ const struct net_bridge_fdb_flush_desc *desc)
{
struct net_bridge_fdb_entry *f;
- struct hlist_node *tmp;
- 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))
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
+ if (!__fdb_flush_matches(br, f, desc))
+ continue;
+
+ spin_lock_bh(&br->hash_lock);
+ if (!hlist_unhashed(&f->fdb_node))
fdb_delete(br, f, true);
+ spin_unlock_bh(&br->hash_lock);
}
- spin_unlock_bh(&br->hash_lock);
+ rcu_read_unlock();
}
int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, u16 vid,
struct netlink_ext_ack *extack)
{
+ struct net_bridge_fdb_flush_desc desc = {
+ .flags_mask = BR_FDB_STATIC
+ };
struct net_bridge_port *p = NULL;
struct net_bridge *br;
@@ -590,7 +615,7 @@ int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
br = p->br;
}
- br_fdb_flush(br);
+ br_fdb_flush(br, &desc);
return 0;
}