mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
net: bridge: use rhashtable for fdbs
Before this patch the bridge used a fixed 256 element hash table which was fine for small use cases (in my tests it starts to degrade above 1000 entries), but it wasn't enough for medium or large scale deployments. Modern setups have thousands of participants in a single bridge, even only enabling vlans and adding a few thousand vlan entries will cause a few thousand fdbs to be automatically inserted per participating port. So we need to scale the fdb table considerably to cope with modern workloads, and this patch converts it to use a rhashtable for its operations thus improving the bridge scalability. Tests show the following results (10 runs each), at up to 1000 entries rhashtable is ~3% slower, at 2000 rhashtable is 30% faster, at 3000 it is 2 times faster and at 30000 it is 50 times faster. Obviously this happens because of the properties of the two constructs and is expected, rhashtable keeps pretty much a constant time even with 10000000 entries (tested), while the fixed hash table struggles considerably even above 10000. As a side effect this also reduces the net_bridge struct size from 3248 bytes to 1344 bytes. Also note that the key struct is 8 bytes. Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
e8952babf8
commit
eb7935830d
@@ -82,8 +82,8 @@ TRACE_EVENT(fdb_delete,
|
||||
TP_fast_assign(
|
||||
__assign_str(br_dev, br->dev->name);
|
||||
__assign_str(dev, f->dst ? f->dst->dev->name : "null");
|
||||
memcpy(__entry->addr, f->addr.addr, ETH_ALEN);
|
||||
__entry->vid = f->vlan_id;
|
||||
memcpy(__entry->addr, f->key.addr.addr, ETH_ALEN);
|
||||
__entry->vid = f->key.vlan_id;
|
||||
),
|
||||
|
||||
TP_printk("br_dev %s dev %s addr %02x:%02x:%02x:%02x:%02x:%02x vid %u",
|
||||
|
||||
@@ -125,9 +125,16 @@ static int br_dev_init(struct net_device *dev)
|
||||
if (!br->stats)
|
||||
return -ENOMEM;
|
||||
|
||||
err = br_fdb_hash_init(br);
|
||||
if (err) {
|
||||
free_percpu(br->stats);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = br_vlan_init(br);
|
||||
if (err) {
|
||||
free_percpu(br->stats);
|
||||
br_fdb_hash_fini(br);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -135,6 +142,7 @@ static int br_dev_init(struct net_device *dev)
|
||||
if (err) {
|
||||
free_percpu(br->stats);
|
||||
br_vlan_flush(br);
|
||||
br_fdb_hash_fini(br);
|
||||
}
|
||||
br_set_lockdep_class(dev);
|
||||
|
||||
@@ -148,6 +156,7 @@ static void br_dev_uninit(struct net_device *dev)
|
||||
br_multicast_dev_del(br);
|
||||
br_multicast_uninit_stats(br);
|
||||
br_vlan_flush(br);
|
||||
br_fdb_hash_fini(br);
|
||||
free_percpu(br->stats);
|
||||
}
|
||||
|
||||
@@ -416,6 +425,7 @@ void br_dev_setup(struct net_device *dev)
|
||||
br->dev = dev;
|
||||
spin_lock_init(&br->lock);
|
||||
INIT_LIST_HEAD(&br->port_list);
|
||||
INIT_HLIST_HEAD(&br->fdb_list);
|
||||
spin_lock_init(&br->hash_lock);
|
||||
|
||||
br->bridge_id.prio[0] = 0x80;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -168,12 +168,17 @@ struct net_bridge_vlan_group {
|
||||
u16 pvid;
|
||||
};
|
||||
|
||||
struct net_bridge_fdb_key {
|
||||
mac_addr addr;
|
||||
u16 vlan_id;
|
||||
};
|
||||
|
||||
struct net_bridge_fdb_entry {
|
||||
struct hlist_node hlist;
|
||||
struct rhash_head rhnode;
|
||||
struct net_bridge_port *dst;
|
||||
|
||||
mac_addr addr;
|
||||
__u16 vlan_id;
|
||||
struct net_bridge_fdb_key key;
|
||||
struct hlist_node fdb_node;
|
||||
unsigned char is_local:1,
|
||||
is_static:1,
|
||||
added_by_user:1,
|
||||
@@ -315,7 +320,7 @@ struct net_bridge {
|
||||
struct net_bridge_vlan_group __rcu *vlgrp;
|
||||
#endif
|
||||
|
||||
struct hlist_head hash[BR_HASH_SIZE];
|
||||
struct rhashtable fdb_hash_tbl;
|
||||
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
|
||||
union {
|
||||
struct rtable fake_rtable;
|
||||
@@ -405,6 +410,7 @@ struct net_bridge {
|
||||
int offload_fwd_mark;
|
||||
#endif
|
||||
bool neigh_suppress_enabled;
|
||||
struct hlist_head fdb_list;
|
||||
};
|
||||
|
||||
struct br_input_skb_cb {
|
||||
@@ -515,6 +521,8 @@ static inline void br_netpoll_disable(struct net_bridge_port *p)
|
||||
/* br_fdb.c */
|
||||
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_find_delete_local(struct net_bridge *br,
|
||||
const struct net_bridge_port *p,
|
||||
|
||||
@@ -121,13 +121,13 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
|
||||
|
||||
switch (type) {
|
||||
case RTM_DELNEIGH:
|
||||
br_switchdev_fdb_call_notifiers(false, fdb->addr.addr,
|
||||
fdb->vlan_id,
|
||||
br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
|
||||
fdb->key.vlan_id,
|
||||
fdb->dst->dev);
|
||||
break;
|
||||
case RTM_NEWNEIGH:
|
||||
br_switchdev_fdb_call_notifiers(true, fdb->addr.addr,
|
||||
fdb->vlan_id,
|
||||
br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
|
||||
fdb->key.vlan_id,
|
||||
fdb->dst->dev);
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user