Commit eb793583 authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller

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: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e8952bab
...@@ -82,8 +82,8 @@ TRACE_EVENT(fdb_delete, ...@@ -82,8 +82,8 @@ TRACE_EVENT(fdb_delete,
TP_fast_assign( TP_fast_assign(
__assign_str(br_dev, br->dev->name); __assign_str(br_dev, br->dev->name);
__assign_str(dev, f->dst ? f->dst->dev->name : "null"); __assign_str(dev, f->dst ? f->dst->dev->name : "null");
memcpy(__entry->addr, f->addr.addr, ETH_ALEN); memcpy(__entry->addr, f->key.addr.addr, ETH_ALEN);
__entry->vid = f->vlan_id; __entry->vid = f->key.vlan_id;
), ),
TP_printk("br_dev %s dev %s addr %02x:%02x:%02x:%02x:%02x:%02x vid %u", 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) ...@@ -125,9 +125,16 @@ static int br_dev_init(struct net_device *dev)
if (!br->stats) if (!br->stats)
return -ENOMEM; return -ENOMEM;
err = br_fdb_hash_init(br);
if (err) {
free_percpu(br->stats);
return err;
}
err = br_vlan_init(br); err = br_vlan_init(br);
if (err) { if (err) {
free_percpu(br->stats); free_percpu(br->stats);
br_fdb_hash_fini(br);
return err; return err;
} }
...@@ -135,6 +142,7 @@ static int br_dev_init(struct net_device *dev) ...@@ -135,6 +142,7 @@ static int br_dev_init(struct net_device *dev)
if (err) { if (err) {
free_percpu(br->stats); free_percpu(br->stats);
br_vlan_flush(br); br_vlan_flush(br);
br_fdb_hash_fini(br);
} }
br_set_lockdep_class(dev); br_set_lockdep_class(dev);
...@@ -148,6 +156,7 @@ static void br_dev_uninit(struct net_device *dev) ...@@ -148,6 +156,7 @@ static void br_dev_uninit(struct net_device *dev)
br_multicast_dev_del(br); br_multicast_dev_del(br);
br_multicast_uninit_stats(br); br_multicast_uninit_stats(br);
br_vlan_flush(br); br_vlan_flush(br);
br_fdb_hash_fini(br);
free_percpu(br->stats); free_percpu(br->stats);
} }
...@@ -416,6 +425,7 @@ void br_dev_setup(struct net_device *dev) ...@@ -416,6 +425,7 @@ void br_dev_setup(struct net_device *dev)
br->dev = dev; br->dev = dev;
spin_lock_init(&br->lock); spin_lock_init(&br->lock);
INIT_LIST_HEAD(&br->port_list); INIT_LIST_HEAD(&br->port_list);
INIT_HLIST_HEAD(&br->fdb_list);
spin_lock_init(&br->hash_lock); spin_lock_init(&br->hash_lock);
br->bridge_id.prio[0] = 0x80; br->bridge_id.prio[0] = 0x80;
......
This diff is collapsed.
...@@ -168,12 +168,17 @@ struct net_bridge_vlan_group { ...@@ -168,12 +168,17 @@ struct net_bridge_vlan_group {
u16 pvid; u16 pvid;
}; };
struct net_bridge_fdb_key {
mac_addr addr;
u16 vlan_id;
};
struct net_bridge_fdb_entry { struct net_bridge_fdb_entry {
struct hlist_node hlist; struct rhash_head rhnode;
struct net_bridge_port *dst; struct net_bridge_port *dst;
mac_addr addr; struct net_bridge_fdb_key key;
__u16 vlan_id; struct hlist_node fdb_node;
unsigned char is_local:1, unsigned char is_local:1,
is_static:1, is_static:1,
added_by_user:1, added_by_user:1,
...@@ -315,7 +320,7 @@ struct net_bridge { ...@@ -315,7 +320,7 @@ struct net_bridge {
struct net_bridge_vlan_group __rcu *vlgrp; struct net_bridge_vlan_group __rcu *vlgrp;
#endif #endif
struct hlist_head hash[BR_HASH_SIZE]; struct rhashtable fdb_hash_tbl;
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
union { union {
struct rtable fake_rtable; struct rtable fake_rtable;
...@@ -405,6 +410,7 @@ struct net_bridge { ...@@ -405,6 +410,7 @@ struct net_bridge {
int offload_fwd_mark; int offload_fwd_mark;
#endif #endif
bool neigh_suppress_enabled; bool neigh_suppress_enabled;
struct hlist_head fdb_list;
}; };
struct br_input_skb_cb { struct br_input_skb_cb {
...@@ -515,6 +521,8 @@ static inline void br_netpoll_disable(struct net_bridge_port *p) ...@@ -515,6 +521,8 @@ static inline void br_netpoll_disable(struct net_bridge_port *p)
/* br_fdb.c */ /* br_fdb.c */
int br_fdb_init(void); int br_fdb_init(void);
void br_fdb_fini(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);
void br_fdb_find_delete_local(struct net_bridge *br, void br_fdb_find_delete_local(struct net_bridge *br,
const struct net_bridge_port *p, const struct net_bridge_port *p,
......
...@@ -121,13 +121,13 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) ...@@ -121,13 +121,13 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
switch (type) { switch (type) {
case RTM_DELNEIGH: case RTM_DELNEIGH:
br_switchdev_fdb_call_notifiers(false, fdb->addr.addr, br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
fdb->vlan_id, fdb->key.vlan_id,
fdb->dst->dev); fdb->dst->dev);
break; break;
case RTM_NEWNEIGH: case RTM_NEWNEIGH:
br_switchdev_fdb_call_notifiers(true, fdb->addr.addr, br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
fdb->vlan_id, fdb->key.vlan_id,
fdb->dst->dev); fdb->dst->dev);
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment