Skip to content

Commit 19e3a9c

Browse files
Nikolay Aleksandrovdavem330
authored andcommitted
net: bridge: convert multicast to generic rhashtable
The bridge multicast code currently uses a custom resizable hashtable which predates the generic rhashtable interface. It has many shortcomings compared and duplicates functionality that is presently available via the generic rhashtable, so this patch removes the custom rhashtable implementation in favor of the kernel's generic rhashtable. The hash maximum is kept and the rhashtable's size is used to do a loose check if it's reached in which case we revert to the old behaviour and disable further bridge multicast processing. Also now we can support any hash maximum, doesn't need to be a power of 2. v3: add non-rcu br_mdb_get variant and use it where multicast_lock is held to avoid RCU splat, drop hash_max function and just set it directly v2: handle when IGMP snooping is undefined, add br_mdb_init/uninit placeholders Signed-off-by: Nikolay Aleksandrov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ba5dfaf commit 19e3a9c

File tree

6 files changed

+163
-436
lines changed

6 files changed

+163
-436
lines changed

net/bridge/br_device.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,17 @@ static int br_dev_init(struct net_device *dev)
131131
return err;
132132
}
133133

134+
err = br_mdb_hash_init(br);
135+
if (err) {
136+
free_percpu(br->stats);
137+
br_fdb_hash_fini(br);
138+
return err;
139+
}
140+
134141
err = br_vlan_init(br);
135142
if (err) {
136143
free_percpu(br->stats);
144+
br_mdb_hash_fini(br);
137145
br_fdb_hash_fini(br);
138146
return err;
139147
}
@@ -142,6 +150,7 @@ static int br_dev_init(struct net_device *dev)
142150
if (err) {
143151
free_percpu(br->stats);
144152
br_vlan_flush(br);
153+
br_mdb_hash_fini(br);
145154
br_fdb_hash_fini(br);
146155
}
147156
br_set_lockdep_class(dev);
@@ -156,6 +165,7 @@ static void br_dev_uninit(struct net_device *dev)
156165
br_multicast_dev_del(br);
157166
br_multicast_uninit_stats(br);
158167
br_vlan_flush(br);
168+
br_mdb_hash_fini(br);
159169
br_fdb_hash_fini(br);
160170
free_percpu(br->stats);
161171
}

net/bridge/br_mdb.c

Lines changed: 51 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -78,82 +78,72 @@ static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip)
7878
static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
7979
struct net_device *dev)
8080
{
81+
int idx = 0, s_idx = cb->args[1], err = 0;
8182
struct net_bridge *br = netdev_priv(dev);
82-
struct net_bridge_mdb_htable *mdb;
83+
struct net_bridge_mdb_entry *mp;
8384
struct nlattr *nest, *nest2;
84-
int i, err = 0;
85-
int idx = 0, s_idx = cb->args[1];
8685

8786
if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
8887
return 0;
8988

90-
mdb = rcu_dereference(br->mdb);
91-
if (!mdb)
92-
return 0;
93-
9489
nest = nla_nest_start(skb, MDBA_MDB);
9590
if (nest == NULL)
9691
return -EMSGSIZE;
9792

98-
for (i = 0; i < mdb->max; i++) {
99-
struct net_bridge_mdb_entry *mp;
93+
hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
10094
struct net_bridge_port_group *p;
10195
struct net_bridge_port_group __rcu **pp;
10296
struct net_bridge_port *port;
10397

104-
hlist_for_each_entry_rcu(mp, &mdb->mhash[i], hlist[mdb->ver]) {
105-
if (idx < s_idx)
106-
goto skip;
98+
if (idx < s_idx)
99+
goto skip;
107100

108-
nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
109-
if (nest2 == NULL) {
110-
err = -EMSGSIZE;
111-
goto out;
112-
}
101+
nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
102+
if (!nest2) {
103+
err = -EMSGSIZE;
104+
break;
105+
}
113106

114-
for (pp = &mp->ports;
115-
(p = rcu_dereference(*pp)) != NULL;
116-
pp = &p->next) {
117-
struct nlattr *nest_ent;
118-
struct br_mdb_entry e;
119-
120-
port = p->port;
121-
if (!port)
122-
continue;
123-
124-
memset(&e, 0, sizeof(e));
125-
e.ifindex = port->dev->ifindex;
126-
e.vid = p->addr.vid;
127-
__mdb_entry_fill_flags(&e, p->flags);
128-
if (p->addr.proto == htons(ETH_P_IP))
129-
e.addr.u.ip4 = p->addr.u.ip4;
107+
for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL;
108+
pp = &p->next) {
109+
struct nlattr *nest_ent;
110+
struct br_mdb_entry e;
111+
112+
port = p->port;
113+
if (!port)
114+
continue;
115+
116+
memset(&e, 0, sizeof(e));
117+
e.ifindex = port->dev->ifindex;
118+
e.vid = p->addr.vid;
119+
__mdb_entry_fill_flags(&e, p->flags);
120+
if (p->addr.proto == htons(ETH_P_IP))
121+
e.addr.u.ip4 = p->addr.u.ip4;
130122
#if IS_ENABLED(CONFIG_IPV6)
131-
if (p->addr.proto == htons(ETH_P_IPV6))
132-
e.addr.u.ip6 = p->addr.u.ip6;
123+
if (p->addr.proto == htons(ETH_P_IPV6))
124+
e.addr.u.ip6 = p->addr.u.ip6;
133125
#endif
134-
e.addr.proto = p->addr.proto;
135-
nest_ent = nla_nest_start(skb,
136-
MDBA_MDB_ENTRY_INFO);
137-
if (!nest_ent) {
138-
nla_nest_cancel(skb, nest2);
139-
err = -EMSGSIZE;
140-
goto out;
141-
}
142-
if (nla_put_nohdr(skb, sizeof(e), &e) ||
143-
nla_put_u32(skb,
144-
MDBA_MDB_EATTR_TIMER,
145-
br_timer_value(&p->timer))) {
146-
nla_nest_cancel(skb, nest_ent);
147-
nla_nest_cancel(skb, nest2);
148-
err = -EMSGSIZE;
149-
goto out;
150-
}
151-
nla_nest_end(skb, nest_ent);
126+
e.addr.proto = p->addr.proto;
127+
nest_ent = nla_nest_start(skb, MDBA_MDB_ENTRY_INFO);
128+
if (!nest_ent) {
129+
nla_nest_cancel(skb, nest2);
130+
err = -EMSGSIZE;
131+
goto out;
152132
}
153-
nla_nest_end(skb, nest2);
154-
skip:
155-
idx++;
133+
if (nla_put_nohdr(skb, sizeof(e), &e) ||
134+
nla_put_u32(skb,
135+
MDBA_MDB_EATTR_TIMER,
136+
br_timer_value(&p->timer))) {
137+
nla_nest_cancel(skb, nest_ent);
138+
nla_nest_cancel(skb, nest2);
139+
err = -EMSGSIZE;
140+
goto out;
141+
}
142+
nla_nest_end(skb, nest_ent);
156143
}
144+
nla_nest_end(skb, nest2);
145+
skip:
146+
idx++;
157147
}
158148

159149
out:
@@ -203,8 +193,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
203193

204194
rcu_read_lock();
205195

206-
/* In theory this could be wrapped to 0... */
207-
cb->seq = net->dev_base_seq + br_mdb_rehash_seq;
196+
cb->seq = net->dev_base_seq;
208197

209198
for_each_netdev_rcu(net, dev) {
210199
if (dev->priv_flags & IFF_EBRIDGE) {
@@ -297,7 +286,6 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)
297286
struct br_mdb_complete_info *data = priv;
298287
struct net_bridge_port_group __rcu **pp;
299288
struct net_bridge_port_group *p;
300-
struct net_bridge_mdb_htable *mdb;
301289
struct net_bridge_mdb_entry *mp;
302290
struct net_bridge_port *port = data->port;
303291
struct net_bridge *br = port->br;
@@ -306,8 +294,7 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)
306294
goto err;
307295

308296
spin_lock_bh(&br->multicast_lock);
309-
mdb = mlock_dereference(br->mdb, br);
310-
mp = br_mdb_ip_get(mdb, &data->ip);
297+
mp = br_mdb_ip_get(br, &data->ip);
311298
if (!mp)
312299
goto out;
313300
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,
588575
struct net_bridge_mdb_entry *mp;
589576
struct net_bridge_port_group *p;
590577
struct net_bridge_port_group __rcu **pp;
591-
struct net_bridge_mdb_htable *mdb;
592578
unsigned long now = jiffies;
593579
int err;
594580

595-
mdb = mlock_dereference(br->mdb, br);
596-
mp = br_mdb_ip_get(mdb, group);
581+
mp = br_mdb_ip_get(br, group);
597582
if (!mp) {
598-
mp = br_multicast_new_group(br, port, group);
583+
mp = br_multicast_new_group(br, group);
599584
err = PTR_ERR_OR_ZERO(mp);
600585
if (err)
601586
return err;
@@ -696,7 +681,6 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
696681

697682
static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
698683
{
699-
struct net_bridge_mdb_htable *mdb;
700684
struct net_bridge_mdb_entry *mp;
701685
struct net_bridge_port_group *p;
702686
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)
709693
__mdb_entry_to_br_ip(entry, &ip);
710694

711695
spin_lock_bh(&br->multicast_lock);
712-
mdb = mlock_dereference(br->mdb, br);
713-
714-
mp = br_mdb_ip_get(mdb, &ip);
696+
mp = br_mdb_ip_get(br, &ip);
715697
if (!mp)
716698
goto unlock;
717699

0 commit comments

Comments
 (0)