Commit 932c4417 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-bridge-convert-multicast-to-generic-rhashtable'

Nikolay Aleksandrov says:

====================
net: bridge: convert multicast to generic rhashtable

The current bridge multicast code uses a custom rhashtable
implementation which predates the generic rhashtable API. Patch 01
converts it to use the generic kernel rhashtable which simplifies the
code a lot and removes duplicated functionality. The convert also makes
hash_elasticity obsolete as the generic rhashtable already has such
checks and has a fixed elasticity of RHT_ELASTICITY (16 currently) so we
emit a warning whenever elasticity is set and return RHT_ELASTICITY when
read (patch 03). Patch 02 converts the multicast code to use non-bh RCU
flavor as it was mixing bh and non-bh. Since now we have the generic
rhashtable which autoshrinks we can be more liberal with the default
hash maximum so patch 04 increases it to 4096 and moves it to a define in
br_private.h.

v3: add non-rcu br_mdb_get variant and use it where we have
    multicast_lock, drop special hash_max handling and just set it where
    needed and use non-bh RCU consistently (patch 02, new)
v2: send the latest version of the set which handles when IGMP snooping
    is not defined, changes are in patch 01
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ba5dfaff d08c6bc0
...@@ -131,9 +131,17 @@ static int br_dev_init(struct net_device *dev) ...@@ -131,9 +131,17 @@ static int br_dev_init(struct net_device *dev)
return err; return err;
} }
err = br_mdb_hash_init(br);
if (err) {
free_percpu(br->stats);
br_fdb_hash_fini(br);
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_mdb_hash_fini(br);
br_fdb_hash_fini(br); br_fdb_hash_fini(br);
return err; return err;
} }
...@@ -142,6 +150,7 @@ static int br_dev_init(struct net_device *dev) ...@@ -142,6 +150,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_mdb_hash_fini(br);
br_fdb_hash_fini(br); br_fdb_hash_fini(br);
} }
br_set_lockdep_class(dev); br_set_lockdep_class(dev);
...@@ -156,6 +165,7 @@ static void br_dev_uninit(struct net_device *dev) ...@@ -156,6 +165,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_mdb_hash_fini(br);
br_fdb_hash_fini(br); br_fdb_hash_fini(br);
free_percpu(br->stats); free_percpu(br->stats);
} }
......
...@@ -78,82 +78,72 @@ static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip) ...@@ -78,82 +78,72 @@ static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip)
static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb, static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev) struct net_device *dev)
{ {
int idx = 0, s_idx = cb->args[1], err = 0;
struct net_bridge *br = netdev_priv(dev); struct net_bridge *br = netdev_priv(dev);
struct net_bridge_mdb_htable *mdb; struct net_bridge_mdb_entry *mp;
struct nlattr *nest, *nest2; struct nlattr *nest, *nest2;
int i, err = 0;
int idx = 0, s_idx = cb->args[1];
if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
return 0; return 0;
mdb = rcu_dereference(br->mdb);
if (!mdb)
return 0;
nest = nla_nest_start(skb, MDBA_MDB); nest = nla_nest_start(skb, MDBA_MDB);
if (nest == NULL) if (nest == NULL)
return -EMSGSIZE; return -EMSGSIZE;
for (i = 0; i < mdb->max; i++) { hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p; struct net_bridge_port_group *p;
struct net_bridge_port_group __rcu **pp; struct net_bridge_port_group __rcu **pp;
struct net_bridge_port *port; struct net_bridge_port *port;
hlist_for_each_entry_rcu(mp, &mdb->mhash[i], hlist[mdb->ver]) { if (idx < s_idx)
if (idx < s_idx) goto skip;
goto skip;
nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY); nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
if (nest2 == NULL) { if (!nest2) {
err = -EMSGSIZE; err = -EMSGSIZE;
goto out; break;
} }
for (pp = &mp->ports; for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL;
(p = rcu_dereference(*pp)) != NULL; pp = &p->next) {
pp = &p->next) { struct nlattr *nest_ent;
struct nlattr *nest_ent; struct br_mdb_entry e;
struct br_mdb_entry e;
port = p->port;
port = p->port; if (!port)
if (!port) continue;
continue;
memset(&e, 0, sizeof(e));
memset(&e, 0, sizeof(e)); e.ifindex = port->dev->ifindex;
e.ifindex = port->dev->ifindex; e.vid = p->addr.vid;
e.vid = p->addr.vid; __mdb_entry_fill_flags(&e, p->flags);
__mdb_entry_fill_flags(&e, p->flags); if (p->addr.proto == htons(ETH_P_IP))
if (p->addr.proto == htons(ETH_P_IP)) e.addr.u.ip4 = p->addr.u.ip4;
e.addr.u.ip4 = p->addr.u.ip4;
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
if (p->addr.proto == htons(ETH_P_IPV6)) if (p->addr.proto == htons(ETH_P_IPV6))
e.addr.u.ip6 = p->addr.u.ip6; e.addr.u.ip6 = p->addr.u.ip6;
#endif #endif
e.addr.proto = p->addr.proto; e.addr.proto = p->addr.proto;
nest_ent = nla_nest_start(skb, nest_ent = nla_nest_start(skb, MDBA_MDB_ENTRY_INFO);
MDBA_MDB_ENTRY_INFO); if (!nest_ent) {
if (!nest_ent) { nla_nest_cancel(skb, nest2);
nla_nest_cancel(skb, nest2); err = -EMSGSIZE;
err = -EMSGSIZE; goto out;
goto out;
}
if (nla_put_nohdr(skb, sizeof(e), &e) ||
nla_put_u32(skb,
MDBA_MDB_EATTR_TIMER,
br_timer_value(&p->timer))) {
nla_nest_cancel(skb, nest_ent);
nla_nest_cancel(skb, nest2);
err = -EMSGSIZE;
goto out;
}
nla_nest_end(skb, nest_ent);
} }
nla_nest_end(skb, nest2); if (nla_put_nohdr(skb, sizeof(e), &e) ||
skip: nla_put_u32(skb,
idx++; MDBA_MDB_EATTR_TIMER,
br_timer_value(&p->timer))) {
nla_nest_cancel(skb, nest_ent);
nla_nest_cancel(skb, nest2);
err = -EMSGSIZE;
goto out;
}
nla_nest_end(skb, nest_ent);
} }
nla_nest_end(skb, nest2);
skip:
idx++;
} }
out: out:
...@@ -203,8 +193,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -203,8 +193,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
rcu_read_lock(); rcu_read_lock();
/* In theory this could be wrapped to 0... */ cb->seq = net->dev_base_seq;
cb->seq = net->dev_base_seq + br_mdb_rehash_seq;
for_each_netdev_rcu(net, dev) { for_each_netdev_rcu(net, dev) {
if (dev->priv_flags & IFF_EBRIDGE) { if (dev->priv_flags & IFF_EBRIDGE) {
...@@ -297,7 +286,6 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv) ...@@ -297,7 +286,6 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)
struct br_mdb_complete_info *data = priv; struct br_mdb_complete_info *data = priv;
struct net_bridge_port_group __rcu **pp; struct net_bridge_port_group __rcu **pp;
struct net_bridge_port_group *p; struct net_bridge_port_group *p;
struct net_bridge_mdb_htable *mdb;
struct net_bridge_mdb_entry *mp; struct net_bridge_mdb_entry *mp;
struct net_bridge_port *port = data->port; struct net_bridge_port *port = data->port;
struct net_bridge *br = port->br; struct net_bridge *br = port->br;
...@@ -306,8 +294,7 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv) ...@@ -306,8 +294,7 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)
goto err; goto err;
spin_lock_bh(&br->multicast_lock); spin_lock_bh(&br->multicast_lock);
mdb = mlock_dereference(br->mdb, br); mp = br_mdb_ip_get(br, &data->ip);
mp = br_mdb_ip_get(mdb, &data->ip);
if (!mp) if (!mp)
goto out; goto out;
for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL; for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
...@@ -588,14 +575,12 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port, ...@@ -588,14 +575,12 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
struct net_bridge_mdb_entry *mp; struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p; struct net_bridge_port_group *p;
struct net_bridge_port_group __rcu **pp; struct net_bridge_port_group __rcu **pp;
struct net_bridge_mdb_htable *mdb;
unsigned long now = jiffies; unsigned long now = jiffies;
int err; int err;
mdb = mlock_dereference(br->mdb, br); mp = br_mdb_ip_get(br, group);
mp = br_mdb_ip_get(mdb, group);
if (!mp) { if (!mp) {
mp = br_multicast_new_group(br, port, group); mp = br_multicast_new_group(br, group);
err = PTR_ERR_OR_ZERO(mp); err = PTR_ERR_OR_ZERO(mp);
if (err) if (err)
return err; return err;
...@@ -696,7 +681,6 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -696,7 +681,6 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry) static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
{ {
struct net_bridge_mdb_htable *mdb;
struct net_bridge_mdb_entry *mp; struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p; struct net_bridge_port_group *p;
struct net_bridge_port_group __rcu **pp; struct net_bridge_port_group __rcu **pp;
...@@ -709,9 +693,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry) ...@@ -709,9 +693,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
__mdb_entry_to_br_ip(entry, &ip); __mdb_entry_to_br_ip(entry, &ip);
spin_lock_bh(&br->multicast_lock); spin_lock_bh(&br->multicast_lock);
mdb = mlock_dereference(br->mdb, br); mp = br_mdb_ip_get(br, &ip);
mp = br_mdb_ip_get(mdb, &ip);
if (!mp) if (!mp)
goto unlock; goto unlock;
...@@ -728,7 +710,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry) ...@@ -728,7 +710,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
rcu_assign_pointer(*pp, p->next); rcu_assign_pointer(*pp, p->next);
hlist_del_init(&p->mglist); hlist_del_init(&p->mglist);
del_timer(&p->timer); del_timer(&p->timer);
call_rcu_bh(&p->rcu, br_multicast_free_pg); kfree_rcu(p, rcu);
err = 0; err = 0;
if (!mp->ports && !mp->host_joined && if (!mp->ports && !mp->host_joined &&
......
This diff is collapsed.
...@@ -1189,19 +1189,12 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], ...@@ -1189,19 +1189,12 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
return err; return err;
} }
if (data[IFLA_BR_MCAST_HASH_ELASTICITY]) { if (data[IFLA_BR_MCAST_HASH_ELASTICITY])
u32 val = nla_get_u32(data[IFLA_BR_MCAST_HASH_ELASTICITY]); br_warn(br, "the hash_elasticity option has been deprecated and is always %u\n",
RHT_ELASTICITY);
br->hash_elasticity = val; if (data[IFLA_BR_MCAST_HASH_MAX])
} br->hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
if (data[IFLA_BR_MCAST_HASH_MAX]) {
u32 hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
err = br_multicast_set_hash_max(br, hash_max);
if (err)
return err;
}
if (data[IFLA_BR_MCAST_LAST_MEMBER_CNT]) { if (data[IFLA_BR_MCAST_LAST_MEMBER_CNT]) {
u32 val = nla_get_u32(data[IFLA_BR_MCAST_LAST_MEMBER_CNT]); u32 val = nla_get_u32(data[IFLA_BR_MCAST_LAST_MEMBER_CNT]);
...@@ -1457,8 +1450,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) ...@@ -1457,8 +1450,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
br_opt_get(br, BROPT_MULTICAST_QUERIER)) || br_opt_get(br, BROPT_MULTICAST_QUERIER)) ||
nla_put_u8(skb, IFLA_BR_MCAST_STATS_ENABLED, nla_put_u8(skb, IFLA_BR_MCAST_STATS_ENABLED,
br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) || br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) ||
nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, RHT_ELASTICITY) ||
br->hash_elasticity) ||
nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) || nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) ||
nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT, nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT,
br->multicast_last_member_count) || br->multicast_last_member_count) ||
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#define BR_PORT_BITS 10 #define BR_PORT_BITS 10
#define BR_MAX_PORTS (1<<BR_PORT_BITS) #define BR_MAX_PORTS (1<<BR_PORT_BITS)
#define BR_MULTICAST_DEFAULT_HASH_MAX 4096
#define BR_VERSION "2.3" #define BR_VERSION "2.3"
/* Control of forwarding link local multicast */ /* Control of forwarding link local multicast */
...@@ -213,23 +215,14 @@ struct net_bridge_port_group { ...@@ -213,23 +215,14 @@ struct net_bridge_port_group {
}; };
struct net_bridge_mdb_entry { struct net_bridge_mdb_entry {
struct hlist_node hlist[2]; struct rhash_head rhnode;
struct net_bridge *br; struct net_bridge *br;
struct net_bridge_port_group __rcu *ports; struct net_bridge_port_group __rcu *ports;
struct rcu_head rcu; struct rcu_head rcu;
struct timer_list timer; struct timer_list timer;
struct br_ip addr; struct br_ip addr;
bool host_joined; bool host_joined;
}; struct hlist_node mdb_node;
struct net_bridge_mdb_htable {
struct hlist_head *mhash;
struct rcu_head rcu;
struct net_bridge_mdb_htable *old;
u32 size;
u32 max;
u32 secret;
u32 ver;
}; };
struct net_bridge_port { struct net_bridge_port {
...@@ -381,7 +374,6 @@ struct net_bridge { ...@@ -381,7 +374,6 @@ struct net_bridge {
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
u32 hash_elasticity;
u32 hash_max; u32 hash_max;
u32 multicast_last_member_count; u32 multicast_last_member_count;
...@@ -400,7 +392,9 @@ struct net_bridge { ...@@ -400,7 +392,9 @@ struct net_bridge {
unsigned long multicast_query_response_interval; unsigned long multicast_query_response_interval;
unsigned long multicast_startup_query_interval; unsigned long multicast_startup_query_interval;
struct net_bridge_mdb_htable __rcu *mdb; struct rhashtable mdb_hash_tbl;
struct hlist_head mdb_list;
struct hlist_head router_list; struct hlist_head router_list;
struct timer_list multicast_router_timer; struct timer_list multicast_router_timer;
...@@ -659,7 +653,6 @@ int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, ...@@ -659,7 +653,6 @@ int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd,
/* br_multicast.c */ /* br_multicast.c */
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
extern unsigned int br_mdb_rehash_seq;
int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
struct sk_buff *skb, u16 vid); struct sk_buff *skb, u16 vid);
struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
...@@ -684,17 +677,15 @@ int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val); ...@@ -684,17 +677,15 @@ int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val);
int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val); int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val);
#endif #endif
struct net_bridge_mdb_entry * struct net_bridge_mdb_entry *
br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst); br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
struct net_bridge_mdb_entry * struct net_bridge_mdb_entry *
br_multicast_new_group(struct net_bridge *br, struct net_bridge_port *port, br_multicast_new_group(struct net_bridge *br, struct br_ip *group);
struct br_ip *group);
void br_multicast_free_pg(struct rcu_head *head);
struct net_bridge_port_group * struct net_bridge_port_group *
br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group, br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
struct net_bridge_port_group __rcu *next, struct net_bridge_port_group __rcu *next,
unsigned char flags, const unsigned char *src); unsigned char flags, const unsigned char *src);
void br_mdb_init(void); int br_mdb_hash_init(struct net_bridge *br);
void br_mdb_uninit(void); void br_mdb_hash_fini(struct net_bridge *br);
void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
struct br_ip *group, int type, u8 flags); struct br_ip *group, int type, u8 flags);
void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port, void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
...@@ -706,6 +697,8 @@ void br_multicast_uninit_stats(struct net_bridge *br); ...@@ -706,6 +697,8 @@ void br_multicast_uninit_stats(struct net_bridge *br);
void br_multicast_get_stats(const struct net_bridge *br, void br_multicast_get_stats(const struct net_bridge *br,
const struct net_bridge_port *p, const struct net_bridge_port *p,
struct br_mcast_stats *dest); struct br_mcast_stats *dest);
void br_mdb_init(void);
void br_mdb_uninit(void);
#define mlock_dereference(X, br) \ #define mlock_dereference(X, br) \
rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
...@@ -831,6 +824,15 @@ static inline void br_mdb_uninit(void) ...@@ -831,6 +824,15 @@ static inline void br_mdb_uninit(void)
{ {
} }
static inline int br_mdb_hash_init(struct net_bridge *br)
{
return 0;
}
static inline void br_mdb_hash_fini(struct net_bridge *br)
{
}
static inline void br_multicast_count(struct net_bridge *br, static inline void br_multicast_count(struct net_bridge *br,
const struct net_bridge_port *p, const struct net_bridge_port *p,
const struct sk_buff *skb, const struct sk_buff *skb,
......
...@@ -424,13 +424,13 @@ static DEVICE_ATTR_RW(multicast_querier); ...@@ -424,13 +424,13 @@ static DEVICE_ATTR_RW(multicast_querier);
static ssize_t hash_elasticity_show(struct device *d, static ssize_t hash_elasticity_show(struct device *d,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct net_bridge *br = to_bridge(d); return sprintf(buf, "%u\n", RHT_ELASTICITY);
return sprintf(buf, "%u\n", br->hash_elasticity);
} }
static int set_elasticity(struct net_bridge *br, unsigned long val) static int set_elasticity(struct net_bridge *br, unsigned long val)
{ {
br->hash_elasticity = val; br_warn(br, "the hash_elasticity option has been deprecated and is always %u\n",
RHT_ELASTICITY);
return 0; return 0;
} }
...@@ -449,10 +449,16 @@ static ssize_t hash_max_show(struct device *d, struct device_attribute *attr, ...@@ -449,10 +449,16 @@ static ssize_t hash_max_show(struct device *d, struct device_attribute *attr,
return sprintf(buf, "%u\n", br->hash_max); return sprintf(buf, "%u\n", br->hash_max);
} }
static int set_hash_max(struct net_bridge *br, unsigned long val)
{
br->hash_max = val;
return 0;
}
static ssize_t hash_max_store(struct device *d, struct device_attribute *attr, static ssize_t hash_max_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t len) const char *buf, size_t len)
{ {
return store_bridge_parm(d, buf, len, br_multicast_set_hash_max); return store_bridge_parm(d, buf, len, set_hash_max);
} }
static DEVICE_ATTR_RW(hash_max); static DEVICE_ATTR_RW(hash_max);
......
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