33
33
34
34
#include "br_private.h"
35
35
36
+ #define mlock_dereference (X , br ) \
37
+ rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
38
+
36
39
#if defined(CONFIG_IPV6 ) || defined(CONFIG_IPV6_MODULE )
37
40
static inline int ipv6_is_local_multicast (const struct in6_addr * addr )
38
41
{
@@ -135,7 +138,7 @@ static struct net_bridge_mdb_entry *br_mdb_ip6_get(
135
138
struct net_bridge_mdb_entry * br_mdb_get (struct net_bridge * br ,
136
139
struct sk_buff * skb )
137
140
{
138
- struct net_bridge_mdb_htable * mdb = br -> mdb ;
141
+ struct net_bridge_mdb_htable * mdb = rcu_dereference ( br -> mdb ) ;
139
142
struct br_ip ip ;
140
143
141
144
if (br -> multicast_disabled )
@@ -235,7 +238,8 @@ static void br_multicast_group_expired(unsigned long data)
235
238
if (mp -> ports )
236
239
goto out ;
237
240
238
- mdb = br -> mdb ;
241
+ mdb = mlock_dereference (br -> mdb , br );
242
+
239
243
hlist_del_rcu (& mp -> hlist [mdb -> ver ]);
240
244
mdb -> size -- ;
241
245
@@ -249,16 +253,20 @@ static void br_multicast_group_expired(unsigned long data)
249
253
static void br_multicast_del_pg (struct net_bridge * br ,
250
254
struct net_bridge_port_group * pg )
251
255
{
252
- struct net_bridge_mdb_htable * mdb = br -> mdb ;
256
+ struct net_bridge_mdb_htable * mdb ;
253
257
struct net_bridge_mdb_entry * mp ;
254
258
struct net_bridge_port_group * p ;
255
- struct net_bridge_port_group * * pp ;
259
+ struct net_bridge_port_group __rcu * * pp ;
260
+
261
+ mdb = mlock_dereference (br -> mdb , br );
256
262
257
263
mp = br_mdb_ip_get (mdb , & pg -> addr );
258
264
if (WARN_ON (!mp ))
259
265
return ;
260
266
261
- for (pp = & mp -> ports ; (p = * pp ); pp = & p -> next ) {
267
+ for (pp = & mp -> ports ;
268
+ (p = mlock_dereference (* pp , br )) != NULL ;
269
+ pp = & p -> next ) {
262
270
if (p != pg )
263
271
continue ;
264
272
@@ -294,10 +302,10 @@ static void br_multicast_port_group_expired(unsigned long data)
294
302
spin_unlock (& br -> multicast_lock );
295
303
}
296
304
297
- static int br_mdb_rehash (struct net_bridge_mdb_htable * * mdbp , int max ,
305
+ static int br_mdb_rehash (struct net_bridge_mdb_htable __rcu * * mdbp , int max ,
298
306
int elasticity )
299
307
{
300
- struct net_bridge_mdb_htable * old = * mdbp ;
308
+ struct net_bridge_mdb_htable * old = rcu_dereference_protected ( * mdbp , 1 ) ;
301
309
struct net_bridge_mdb_htable * mdb ;
302
310
int err ;
303
311
@@ -569,14 +577,15 @@ static struct net_bridge_mdb_entry *br_multicast_get_group(
569
577
struct net_bridge * br , struct net_bridge_port * port ,
570
578
struct br_ip * group , int hash )
571
579
{
572
- struct net_bridge_mdb_htable * mdb = br -> mdb ;
580
+ struct net_bridge_mdb_htable * mdb ;
573
581
struct net_bridge_mdb_entry * mp ;
574
582
struct hlist_node * p ;
575
583
unsigned count = 0 ;
576
584
unsigned max ;
577
585
int elasticity ;
578
586
int err ;
579
587
588
+ mdb = rcu_dereference_protected (br -> mdb , 1 );
580
589
hlist_for_each_entry (mp , p , & mdb -> mhash [hash ], hlist [mdb -> ver ]) {
581
590
count ++ ;
582
591
if (unlikely (br_ip_equal (group , & mp -> addr )))
@@ -642,10 +651,11 @@ static struct net_bridge_mdb_entry *br_multicast_new_group(
642
651
struct net_bridge * br , struct net_bridge_port * port ,
643
652
struct br_ip * group )
644
653
{
645
- struct net_bridge_mdb_htable * mdb = br -> mdb ;
654
+ struct net_bridge_mdb_htable * mdb ;
646
655
struct net_bridge_mdb_entry * mp ;
647
656
int hash ;
648
657
658
+ mdb = rcu_dereference_protected (br -> mdb , 1 );
649
659
if (!mdb ) {
650
660
if (br_mdb_rehash (& br -> mdb , BR_HASH_SIZE , 0 ))
651
661
return NULL ;
@@ -660,7 +670,7 @@ static struct net_bridge_mdb_entry *br_multicast_new_group(
660
670
661
671
case - EAGAIN :
662
672
rehash :
663
- mdb = br -> mdb ;
673
+ mdb = rcu_dereference_protected ( br -> mdb , 1 ) ;
664
674
hash = br_ip_hash (mdb , group );
665
675
break ;
666
676
@@ -692,7 +702,7 @@ static int br_multicast_add_group(struct net_bridge *br,
692
702
{
693
703
struct net_bridge_mdb_entry * mp ;
694
704
struct net_bridge_port_group * p ;
695
- struct net_bridge_port_group * * pp ;
705
+ struct net_bridge_port_group __rcu * * pp ;
696
706
unsigned long now = jiffies ;
697
707
int err ;
698
708
@@ -712,7 +722,9 @@ static int br_multicast_add_group(struct net_bridge *br,
712
722
goto out ;
713
723
}
714
724
715
- for (pp = & mp -> ports ; (p = * pp ); pp = & p -> next ) {
725
+ for (pp = & mp -> ports ;
726
+ (p = mlock_dereference (* pp , br )) != NULL ;
727
+ pp = & p -> next ) {
716
728
if (p -> port == port )
717
729
goto found ;
718
730
if ((unsigned long )p -> port < (unsigned long )port )
@@ -1106,7 +1118,7 @@ static int br_ip4_multicast_query(struct net_bridge *br,
1106
1118
struct net_bridge_mdb_entry * mp ;
1107
1119
struct igmpv3_query * ih3 ;
1108
1120
struct net_bridge_port_group * p ;
1109
- struct net_bridge_port_group * * pp ;
1121
+ struct net_bridge_port_group __rcu * * pp ;
1110
1122
unsigned long max_delay ;
1111
1123
unsigned long now = jiffies ;
1112
1124
__be32 group ;
@@ -1145,7 +1157,7 @@ static int br_ip4_multicast_query(struct net_bridge *br,
1145
1157
if (!group )
1146
1158
goto out ;
1147
1159
1148
- mp = br_mdb_ip4_get (br -> mdb , group );
1160
+ mp = br_mdb_ip4_get (mlock_dereference ( br -> mdb , br ) , group );
1149
1161
if (!mp )
1150
1162
goto out ;
1151
1163
@@ -1157,7 +1169,9 @@ static int br_ip4_multicast_query(struct net_bridge *br,
1157
1169
try_to_del_timer_sync (& mp -> timer ) >= 0 ))
1158
1170
mod_timer (& mp -> timer , now + max_delay );
1159
1171
1160
- for (pp = & mp -> ports ; (p = * pp ); pp = & p -> next ) {
1172
+ for (pp = & mp -> ports ;
1173
+ (p = mlock_dereference (* pp , br )) != NULL ;
1174
+ pp = & p -> next ) {
1161
1175
if (timer_pending (& p -> timer ) ?
1162
1176
time_after (p -> timer .expires , now + max_delay ) :
1163
1177
try_to_del_timer_sync (& p -> timer ) >= 0 )
@@ -1178,7 +1192,8 @@ static int br_ip6_multicast_query(struct net_bridge *br,
1178
1192
struct mld_msg * mld = (struct mld_msg * ) icmp6_hdr (skb );
1179
1193
struct net_bridge_mdb_entry * mp ;
1180
1194
struct mld2_query * mld2q ;
1181
- struct net_bridge_port_group * p , * * pp ;
1195
+ struct net_bridge_port_group * p ;
1196
+ struct net_bridge_port_group __rcu * * pp ;
1182
1197
unsigned long max_delay ;
1183
1198
unsigned long now = jiffies ;
1184
1199
struct in6_addr * group = NULL ;
@@ -1214,7 +1229,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
1214
1229
if (!group )
1215
1230
goto out ;
1216
1231
1217
- mp = br_mdb_ip6_get (br -> mdb , group );
1232
+ mp = br_mdb_ip6_get (mlock_dereference ( br -> mdb , br ) , group );
1218
1233
if (!mp )
1219
1234
goto out ;
1220
1235
@@ -1225,7 +1240,9 @@ static int br_ip6_multicast_query(struct net_bridge *br,
1225
1240
try_to_del_timer_sync (& mp -> timer ) >= 0 ))
1226
1241
mod_timer (& mp -> timer , now + max_delay );
1227
1242
1228
- for (pp = & mp -> ports ; (p = * pp ); pp = & p -> next ) {
1243
+ for (pp = & mp -> ports ;
1244
+ (p = mlock_dereference (* pp , br )) != NULL ;
1245
+ pp = & p -> next ) {
1229
1246
if (timer_pending (& p -> timer ) ?
1230
1247
time_after (p -> timer .expires , now + max_delay ) :
1231
1248
try_to_del_timer_sync (& p -> timer ) >= 0 )
@@ -1254,7 +1271,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
1254
1271
timer_pending (& br -> multicast_querier_timer ))
1255
1272
goto out ;
1256
1273
1257
- mdb = br -> mdb ;
1274
+ mdb = mlock_dereference ( br -> mdb , br ) ;
1258
1275
mp = br_mdb_ip_get (mdb , group );
1259
1276
if (!mp )
1260
1277
goto out ;
@@ -1277,7 +1294,9 @@ static void br_multicast_leave_group(struct net_bridge *br,
1277
1294
goto out ;
1278
1295
}
1279
1296
1280
- for (p = mp -> ports ; p ; p = p -> next ) {
1297
+ for (p = mlock_dereference (mp -> ports , br );
1298
+ p != NULL ;
1299
+ p = mlock_dereference (p -> next , br )) {
1281
1300
if (p -> port != port )
1282
1301
continue ;
1283
1302
@@ -1625,7 +1644,7 @@ void br_multicast_stop(struct net_bridge *br)
1625
1644
del_timer_sync (& br -> multicast_query_timer );
1626
1645
1627
1646
spin_lock_bh (& br -> multicast_lock );
1628
- mdb = br -> mdb ;
1647
+ mdb = mlock_dereference ( br -> mdb , br ) ;
1629
1648
if (!mdb )
1630
1649
goto out ;
1631
1650
@@ -1729,6 +1748,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val)
1729
1748
{
1730
1749
struct net_bridge_port * port ;
1731
1750
int err = 0 ;
1751
+ struct net_bridge_mdb_htable * mdb ;
1732
1752
1733
1753
spin_lock (& br -> multicast_lock );
1734
1754
if (br -> multicast_disabled == !val )
@@ -1741,15 +1761,16 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val)
1741
1761
if (!netif_running (br -> dev ))
1742
1762
goto unlock ;
1743
1763
1744
- if (br -> mdb ) {
1745
- if (br -> mdb -> old ) {
1764
+ mdb = mlock_dereference (br -> mdb , br );
1765
+ if (mdb ) {
1766
+ if (mdb -> old ) {
1746
1767
err = - EEXIST ;
1747
1768
rollback :
1748
1769
br -> multicast_disabled = !!val ;
1749
1770
goto unlock ;
1750
1771
}
1751
1772
1752
- err = br_mdb_rehash (& br -> mdb , br -> mdb -> max ,
1773
+ err = br_mdb_rehash (& br -> mdb , mdb -> max ,
1753
1774
br -> hash_elasticity );
1754
1775
if (err )
1755
1776
goto rollback ;
@@ -1774,6 +1795,7 @@ int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
1774
1795
{
1775
1796
int err = - ENOENT ;
1776
1797
u32 old ;
1798
+ struct net_bridge_mdb_htable * mdb ;
1777
1799
1778
1800
spin_lock (& br -> multicast_lock );
1779
1801
if (!netif_running (br -> dev ))
@@ -1782,16 +1804,18 @@ int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
1782
1804
err = - EINVAL ;
1783
1805
if (!is_power_of_2 (val ))
1784
1806
goto unlock ;
1785
- if (br -> mdb && val < br -> mdb -> size )
1807
+
1808
+ mdb = mlock_dereference (br -> mdb , br );
1809
+ if (mdb && val < mdb -> size )
1786
1810
goto unlock ;
1787
1811
1788
1812
err = 0 ;
1789
1813
1790
1814
old = br -> hash_max ;
1791
1815
br -> hash_max = val ;
1792
1816
1793
- if (br -> mdb ) {
1794
- if (br -> mdb -> old ) {
1817
+ if (mdb ) {
1818
+ if (mdb -> old ) {
1795
1819
err = - EEXIST ;
1796
1820
rollback :
1797
1821
br -> hash_max = old ;
0 commit comments