Skip to content

Commit

Permalink
net: bridge: fdb: add support for fine-grained flushing
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Nikolay Aleksandrov authored and David S. Miller committed Apr 13, 2022
1 parent edaef19 commit 1f78ee1
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 12 deletions.
41 changes: 33 additions & 8 deletions net/bridge/br_fdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
Expand Down
9 changes: 7 additions & 2 deletions net/bridge/br_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -1326,8 +1326,13 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
br_recalculate_fwd_mask(br);
}

if (data[IFLA_BR_FDB_FLUSH])
br_fdb_flush(br);
if (data[IFLA_BR_FDB_FLUSH]) {
struct net_bridge_fdb_flush_desc desc = {
.flags_mask = BR_FDB_STATIC
};

br_fdb_flush(br, &desc);
}

#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
if (data[IFLA_BR_MCAST_ROUTER]) {
Expand Down
10 changes: 9 additions & 1 deletion net/bridge/br_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,13 @@ struct net_bridge_fdb_entry {
struct rcu_head rcu;
};

struct net_bridge_fdb_flush_desc {
unsigned long flags;
unsigned long flags_mask;
int port_ifindex;
u16 vlan_id;
};

#define MDB_PG_FLAGS_PERMANENT BIT(0)
#define MDB_PG_FLAGS_OFFLOAD BIT(1)
#define MDB_PG_FLAGS_FAST_LEAVE BIT(2)
Expand Down Expand Up @@ -759,7 +766,8 @@ int br_fdb_init(void);
void br_fdb_fini(void);
int br_fdb_hash_init(struct net_bridge *br);
void br_fdb_hash_fini(struct net_bridge *br);
void br_fdb_flush(struct net_bridge *br);
void br_fdb_flush(struct net_bridge *br,
const struct net_bridge_fdb_flush_desc *desc);
void br_fdb_find_delete_local(struct net_bridge *br,
const struct net_bridge_port *p,
const unsigned char *addr, u16 vid);
Expand Down
6 changes: 5 additions & 1 deletion net/bridge/br_sysfs_br.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,11 @@ static DEVICE_ATTR_RW(group_addr);
static int set_flush(struct net_bridge *br, unsigned long val,
struct netlink_ext_ack *extack)
{
br_fdb_flush(br);
struct net_bridge_fdb_flush_desc desc = {
.flags_mask = BR_FDB_STATIC
};

br_fdb_flush(br, &desc);
return 0;
}

Expand Down

0 comments on commit 1f78ee1

Please sign in to comment.