diff options
author | Patrick McHardy <kaber@trash.net> | 2011-01-14 08:12:37 -0500 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-01-14 08:12:37 -0500 |
commit | 0134e89c7bcc9fde1da962c82a120691e185619f (patch) | |
tree | 3e03335cf001019a2687d161e956de4f73379984 /net/ipv4/igmp.c | |
parent | c7066f70d9610df0b9406cc635fc09e86136e714 (diff) | |
parent | 6faee60a4e82075853a437831768cc9e2e563e4e (diff) |
Merge branch 'master' of git://1984.lsi.us.es/net-next-2.6
Conflicts:
net/ipv4/route.c
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net/ipv4/igmp.c')
-rw-r--r-- | net/ipv4/igmp.c | 284 |
1 files changed, 140 insertions, 144 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 08d0d81ffc15..e0e77e297de3 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -149,21 +149,37 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc); | |||
149 | static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, | 149 | static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, |
150 | int sfcount, __be32 *psfsrc, int delta); | 150 | int sfcount, __be32 *psfsrc, int delta); |
151 | 151 | ||
152 | |||
153 | static void ip_mc_list_reclaim(struct rcu_head *head) | ||
154 | { | ||
155 | kfree(container_of(head, struct ip_mc_list, rcu)); | ||
156 | } | ||
157 | |||
152 | static void ip_ma_put(struct ip_mc_list *im) | 158 | static void ip_ma_put(struct ip_mc_list *im) |
153 | { | 159 | { |
154 | if (atomic_dec_and_test(&im->refcnt)) { | 160 | if (atomic_dec_and_test(&im->refcnt)) { |
155 | in_dev_put(im->interface); | 161 | in_dev_put(im->interface); |
156 | kfree(im); | 162 | call_rcu(&im->rcu, ip_mc_list_reclaim); |
157 | } | 163 | } |
158 | } | 164 | } |
159 | 165 | ||
166 | #define for_each_pmc_rcu(in_dev, pmc) \ | ||
167 | for (pmc = rcu_dereference(in_dev->mc_list); \ | ||
168 | pmc != NULL; \ | ||
169 | pmc = rcu_dereference(pmc->next_rcu)) | ||
170 | |||
171 | #define for_each_pmc_rtnl(in_dev, pmc) \ | ||
172 | for (pmc = rtnl_dereference(in_dev->mc_list); \ | ||
173 | pmc != NULL; \ | ||
174 | pmc = rtnl_dereference(pmc->next_rcu)) | ||
175 | |||
160 | #ifdef CONFIG_IP_MULTICAST | 176 | #ifdef CONFIG_IP_MULTICAST |
161 | 177 | ||
162 | /* | 178 | /* |
163 | * Timer management | 179 | * Timer management |
164 | */ | 180 | */ |
165 | 181 | ||
166 | static __inline__ void igmp_stop_timer(struct ip_mc_list *im) | 182 | static void igmp_stop_timer(struct ip_mc_list *im) |
167 | { | 183 | { |
168 | spin_lock_bh(&im->lock); | 184 | spin_lock_bh(&im->lock); |
169 | if (del_timer(&im->timer)) | 185 | if (del_timer(&im->timer)) |
@@ -284,6 +300,8 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) | |||
284 | return scount; | 300 | return scount; |
285 | } | 301 | } |
286 | 302 | ||
303 | #define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb)) | ||
304 | |||
287 | static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) | 305 | static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) |
288 | { | 306 | { |
289 | struct sk_buff *skb; | 307 | struct sk_buff *skb; |
@@ -292,14 +310,20 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) | |||
292 | struct igmpv3_report *pig; | 310 | struct igmpv3_report *pig; |
293 | struct net *net = dev_net(dev); | 311 | struct net *net = dev_net(dev); |
294 | 312 | ||
295 | skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), GFP_ATOMIC); | 313 | while (1) { |
296 | if (skb == NULL) | 314 | skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), |
297 | return NULL; | 315 | GFP_ATOMIC | __GFP_NOWARN); |
316 | if (skb) | ||
317 | break; | ||
318 | size >>= 1; | ||
319 | if (size < 256) | ||
320 | return NULL; | ||
321 | } | ||
322 | igmp_skb_size(skb) = size; | ||
298 | 323 | ||
299 | { | 324 | { |
300 | struct flowi fl = { .oif = dev->ifindex, | 325 | struct flowi fl = { .oif = dev->ifindex, |
301 | .nl_u = { .ip4_u = { | 326 | .fl4_dst = IGMPV3_ALL_MCR, |
302 | .daddr = IGMPV3_ALL_MCR } }, | ||
303 | .proto = IPPROTO_IGMP }; | 327 | .proto = IPPROTO_IGMP }; |
304 | if (ip_route_output_key(net, &rt, &fl)) { | 328 | if (ip_route_output_key(net, &rt, &fl)) { |
305 | kfree_skb(skb); | 329 | kfree_skb(skb); |
@@ -384,7 +408,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, | |||
384 | return skb; | 408 | return skb; |
385 | } | 409 | } |
386 | 410 | ||
387 | #define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \ | 411 | #define AVAILABLE(skb) ((skb) ? ((skb)->dev ? igmp_skb_size(skb) - (skb)->len : \ |
388 | skb_tailroom(skb)) : 0) | 412 | skb_tailroom(skb)) : 0) |
389 | 413 | ||
390 | static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, | 414 | static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, |
@@ -502,8 +526,8 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) | |||
502 | int type; | 526 | int type; |
503 | 527 | ||
504 | if (!pmc) { | 528 | if (!pmc) { |
505 | read_lock(&in_dev->mc_list_lock); | 529 | rcu_read_lock(); |
506 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 530 | for_each_pmc_rcu(in_dev, pmc) { |
507 | if (pmc->multiaddr == IGMP_ALL_HOSTS) | 531 | if (pmc->multiaddr == IGMP_ALL_HOSTS) |
508 | continue; | 532 | continue; |
509 | spin_lock_bh(&pmc->lock); | 533 | spin_lock_bh(&pmc->lock); |
@@ -514,7 +538,7 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) | |||
514 | skb = add_grec(skb, pmc, type, 0, 0); | 538 | skb = add_grec(skb, pmc, type, 0, 0); |
515 | spin_unlock_bh(&pmc->lock); | 539 | spin_unlock_bh(&pmc->lock); |
516 | } | 540 | } |
517 | read_unlock(&in_dev->mc_list_lock); | 541 | rcu_read_unlock(); |
518 | } else { | 542 | } else { |
519 | spin_lock_bh(&pmc->lock); | 543 | spin_lock_bh(&pmc->lock); |
520 | if (pmc->sfcount[MCAST_EXCLUDE]) | 544 | if (pmc->sfcount[MCAST_EXCLUDE]) |
@@ -556,7 +580,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) | |||
556 | struct sk_buff *skb = NULL; | 580 | struct sk_buff *skb = NULL; |
557 | int type, dtype; | 581 | int type, dtype; |
558 | 582 | ||
559 | read_lock(&in_dev->mc_list_lock); | 583 | rcu_read_lock(); |
560 | spin_lock_bh(&in_dev->mc_tomb_lock); | 584 | spin_lock_bh(&in_dev->mc_tomb_lock); |
561 | 585 | ||
562 | /* deleted MCA's */ | 586 | /* deleted MCA's */ |
@@ -593,7 +617,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) | |||
593 | spin_unlock_bh(&in_dev->mc_tomb_lock); | 617 | spin_unlock_bh(&in_dev->mc_tomb_lock); |
594 | 618 | ||
595 | /* change recs */ | 619 | /* change recs */ |
596 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 620 | for_each_pmc_rcu(in_dev, pmc) { |
597 | spin_lock_bh(&pmc->lock); | 621 | spin_lock_bh(&pmc->lock); |
598 | if (pmc->sfcount[MCAST_EXCLUDE]) { | 622 | if (pmc->sfcount[MCAST_EXCLUDE]) { |
599 | type = IGMPV3_BLOCK_OLD_SOURCES; | 623 | type = IGMPV3_BLOCK_OLD_SOURCES; |
@@ -616,7 +640,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) | |||
616 | } | 640 | } |
617 | spin_unlock_bh(&pmc->lock); | 641 | spin_unlock_bh(&pmc->lock); |
618 | } | 642 | } |
619 | read_unlock(&in_dev->mc_list_lock); | 643 | rcu_read_unlock(); |
620 | 644 | ||
621 | if (!skb) | 645 | if (!skb) |
622 | return; | 646 | return; |
@@ -644,7 +668,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, | |||
644 | 668 | ||
645 | { | 669 | { |
646 | struct flowi fl = { .oif = dev->ifindex, | 670 | struct flowi fl = { .oif = dev->ifindex, |
647 | .nl_u = { .ip4_u = { .daddr = dst } }, | 671 | .fl4_dst = dst, |
648 | .proto = IPPROTO_IGMP }; | 672 | .proto = IPPROTO_IGMP }; |
649 | if (ip_route_output_key(net, &rt, &fl)) | 673 | if (ip_route_output_key(net, &rt, &fl)) |
650 | return -1; | 674 | return -1; |
@@ -813,14 +837,14 @@ static void igmp_heard_report(struct in_device *in_dev, __be32 group) | |||
813 | if (group == IGMP_ALL_HOSTS) | 837 | if (group == IGMP_ALL_HOSTS) |
814 | return; | 838 | return; |
815 | 839 | ||
816 | read_lock(&in_dev->mc_list_lock); | 840 | rcu_read_lock(); |
817 | for (im=in_dev->mc_list; im!=NULL; im=im->next) { | 841 | for_each_pmc_rcu(in_dev, im) { |
818 | if (im->multiaddr == group) { | 842 | if (im->multiaddr == group) { |
819 | igmp_stop_timer(im); | 843 | igmp_stop_timer(im); |
820 | break; | 844 | break; |
821 | } | 845 | } |
822 | } | 846 | } |
823 | read_unlock(&in_dev->mc_list_lock); | 847 | rcu_read_unlock(); |
824 | } | 848 | } |
825 | 849 | ||
826 | static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | 850 | static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, |
@@ -906,8 +930,8 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | |||
906 | * - Use the igmp->igmp_code field as the maximum | 930 | * - Use the igmp->igmp_code field as the maximum |
907 | * delay possible | 931 | * delay possible |
908 | */ | 932 | */ |
909 | read_lock(&in_dev->mc_list_lock); | 933 | rcu_read_lock(); |
910 | for (im=in_dev->mc_list; im!=NULL; im=im->next) { | 934 | for_each_pmc_rcu(in_dev, im) { |
911 | int changed; | 935 | int changed; |
912 | 936 | ||
913 | if (group && group != im->multiaddr) | 937 | if (group && group != im->multiaddr) |
@@ -925,7 +949,7 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | |||
925 | if (changed) | 949 | if (changed) |
926 | igmp_mod_timer(im, max_delay); | 950 | igmp_mod_timer(im, max_delay); |
927 | } | 951 | } |
928 | read_unlock(&in_dev->mc_list_lock); | 952 | rcu_read_unlock(); |
929 | } | 953 | } |
930 | 954 | ||
931 | /* called in rcu_read_lock() section */ | 955 | /* called in rcu_read_lock() section */ |
@@ -1110,8 +1134,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) | |||
1110 | kfree(pmc); | 1134 | kfree(pmc); |
1111 | } | 1135 | } |
1112 | /* clear dead sources, too */ | 1136 | /* clear dead sources, too */ |
1113 | read_lock(&in_dev->mc_list_lock); | 1137 | rcu_read_lock(); |
1114 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 1138 | for_each_pmc_rcu(in_dev, pmc) { |
1115 | struct ip_sf_list *psf, *psf_next; | 1139 | struct ip_sf_list *psf, *psf_next; |
1116 | 1140 | ||
1117 | spin_lock_bh(&pmc->lock); | 1141 | spin_lock_bh(&pmc->lock); |
@@ -1123,7 +1147,7 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) | |||
1123 | kfree(psf); | 1147 | kfree(psf); |
1124 | } | 1148 | } |
1125 | } | 1149 | } |
1126 | read_unlock(&in_dev->mc_list_lock); | 1150 | rcu_read_unlock(); |
1127 | } | 1151 | } |
1128 | #endif | 1152 | #endif |
1129 | 1153 | ||
@@ -1209,7 +1233,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) | |||
1209 | 1233 | ||
1210 | ASSERT_RTNL(); | 1234 | ASSERT_RTNL(); |
1211 | 1235 | ||
1212 | for (im=in_dev->mc_list; im; im=im->next) { | 1236 | for_each_pmc_rtnl(in_dev, im) { |
1213 | if (im->multiaddr == addr) { | 1237 | if (im->multiaddr == addr) { |
1214 | im->users++; | 1238 | im->users++; |
1215 | ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); | 1239 | ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); |
@@ -1217,7 +1241,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) | |||
1217 | } | 1241 | } |
1218 | } | 1242 | } |
1219 | 1243 | ||
1220 | im = kmalloc(sizeof(*im), GFP_KERNEL); | 1244 | im = kzalloc(sizeof(*im), GFP_KERNEL); |
1221 | if (!im) | 1245 | if (!im) |
1222 | goto out; | 1246 | goto out; |
1223 | 1247 | ||
@@ -1227,26 +1251,18 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) | |||
1227 | im->multiaddr = addr; | 1251 | im->multiaddr = addr; |
1228 | /* initial mode is (EX, empty) */ | 1252 | /* initial mode is (EX, empty) */ |
1229 | im->sfmode = MCAST_EXCLUDE; | 1253 | im->sfmode = MCAST_EXCLUDE; |
1230 | im->sfcount[MCAST_INCLUDE] = 0; | ||
1231 | im->sfcount[MCAST_EXCLUDE] = 1; | 1254 | im->sfcount[MCAST_EXCLUDE] = 1; |
1232 | im->sources = NULL; | ||
1233 | im->tomb = NULL; | ||
1234 | im->crcount = 0; | ||
1235 | atomic_set(&im->refcnt, 1); | 1255 | atomic_set(&im->refcnt, 1); |
1236 | spin_lock_init(&im->lock); | 1256 | spin_lock_init(&im->lock); |
1237 | #ifdef CONFIG_IP_MULTICAST | 1257 | #ifdef CONFIG_IP_MULTICAST |
1238 | im->tm_running = 0; | ||
1239 | setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im); | 1258 | setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im); |
1240 | im->unsolicit_count = IGMP_Unsolicited_Report_Count; | 1259 | im->unsolicit_count = IGMP_Unsolicited_Report_Count; |
1241 | im->reporter = 0; | ||
1242 | im->gsquery = 0; | ||
1243 | #endif | 1260 | #endif |
1244 | im->loaded = 0; | 1261 | |
1245 | write_lock_bh(&in_dev->mc_list_lock); | 1262 | im->next_rcu = in_dev->mc_list; |
1246 | im->next = in_dev->mc_list; | ||
1247 | in_dev->mc_list = im; | ||
1248 | in_dev->mc_count++; | 1263 | in_dev->mc_count++; |
1249 | write_unlock_bh(&in_dev->mc_list_lock); | 1264 | rcu_assign_pointer(in_dev->mc_list, im); |
1265 | |||
1250 | #ifdef CONFIG_IP_MULTICAST | 1266 | #ifdef CONFIG_IP_MULTICAST |
1251 | igmpv3_del_delrec(in_dev, im->multiaddr); | 1267 | igmpv3_del_delrec(in_dev, im->multiaddr); |
1252 | #endif | 1268 | #endif |
@@ -1260,26 +1276,32 @@ EXPORT_SYMBOL(ip_mc_inc_group); | |||
1260 | 1276 | ||
1261 | /* | 1277 | /* |
1262 | * Resend IGMP JOIN report; used for bonding. | 1278 | * Resend IGMP JOIN report; used for bonding. |
1279 | * Called with rcu_read_lock() | ||
1263 | */ | 1280 | */ |
1264 | void ip_mc_rejoin_group(struct ip_mc_list *im) | 1281 | void ip_mc_rejoin_groups(struct in_device *in_dev) |
1265 | { | 1282 | { |
1266 | #ifdef CONFIG_IP_MULTICAST | 1283 | #ifdef CONFIG_IP_MULTICAST |
1267 | struct in_device *in_dev = im->interface; | 1284 | struct ip_mc_list *im; |
1285 | int type; | ||
1268 | 1286 | ||
1269 | if (im->multiaddr == IGMP_ALL_HOSTS) | 1287 | for_each_pmc_rcu(in_dev, im) { |
1270 | return; | 1288 | if (im->multiaddr == IGMP_ALL_HOSTS) |
1289 | continue; | ||
1271 | 1290 | ||
1272 | /* a failover is happening and switches | 1291 | /* a failover is happening and switches |
1273 | * must be notified immediately */ | 1292 | * must be notified immediately |
1274 | if (IGMP_V1_SEEN(in_dev)) | 1293 | */ |
1275 | igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT); | 1294 | if (IGMP_V1_SEEN(in_dev)) |
1276 | else if (IGMP_V2_SEEN(in_dev)) | 1295 | type = IGMP_HOST_MEMBERSHIP_REPORT; |
1277 | igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT); | 1296 | else if (IGMP_V2_SEEN(in_dev)) |
1278 | else | 1297 | type = IGMPV2_HOST_MEMBERSHIP_REPORT; |
1279 | igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT); | 1298 | else |
1299 | type = IGMPV3_HOST_MEMBERSHIP_REPORT; | ||
1300 | igmp_send_report(in_dev, im, type); | ||
1301 | } | ||
1280 | #endif | 1302 | #endif |
1281 | } | 1303 | } |
1282 | EXPORT_SYMBOL(ip_mc_rejoin_group); | 1304 | EXPORT_SYMBOL(ip_mc_rejoin_groups); |
1283 | 1305 | ||
1284 | /* | 1306 | /* |
1285 | * A socket has left a multicast group on device dev | 1307 | * A socket has left a multicast group on device dev |
@@ -1287,17 +1309,18 @@ EXPORT_SYMBOL(ip_mc_rejoin_group); | |||
1287 | 1309 | ||
1288 | void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) | 1310 | void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) |
1289 | { | 1311 | { |
1290 | struct ip_mc_list *i, **ip; | 1312 | struct ip_mc_list *i; |
1313 | struct ip_mc_list __rcu **ip; | ||
1291 | 1314 | ||
1292 | ASSERT_RTNL(); | 1315 | ASSERT_RTNL(); |
1293 | 1316 | ||
1294 | for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) { | 1317 | for (ip = &in_dev->mc_list; |
1318 | (i = rtnl_dereference(*ip)) != NULL; | ||
1319 | ip = &i->next_rcu) { | ||
1295 | if (i->multiaddr == addr) { | 1320 | if (i->multiaddr == addr) { |
1296 | if (--i->users == 0) { | 1321 | if (--i->users == 0) { |
1297 | write_lock_bh(&in_dev->mc_list_lock); | 1322 | *ip = i->next_rcu; |
1298 | *ip = i->next; | ||
1299 | in_dev->mc_count--; | 1323 | in_dev->mc_count--; |
1300 | write_unlock_bh(&in_dev->mc_list_lock); | ||
1301 | igmp_group_dropped(i); | 1324 | igmp_group_dropped(i); |
1302 | 1325 | ||
1303 | if (!in_dev->dead) | 1326 | if (!in_dev->dead) |
@@ -1316,34 +1339,34 @@ EXPORT_SYMBOL(ip_mc_dec_group); | |||
1316 | 1339 | ||
1317 | void ip_mc_unmap(struct in_device *in_dev) | 1340 | void ip_mc_unmap(struct in_device *in_dev) |
1318 | { | 1341 | { |
1319 | struct ip_mc_list *i; | 1342 | struct ip_mc_list *pmc; |
1320 | 1343 | ||
1321 | ASSERT_RTNL(); | 1344 | ASSERT_RTNL(); |
1322 | 1345 | ||
1323 | for (i = in_dev->mc_list; i; i = i->next) | 1346 | for_each_pmc_rtnl(in_dev, pmc) |
1324 | igmp_group_dropped(i); | 1347 | igmp_group_dropped(pmc); |
1325 | } | 1348 | } |
1326 | 1349 | ||
1327 | void ip_mc_remap(struct in_device *in_dev) | 1350 | void ip_mc_remap(struct in_device *in_dev) |
1328 | { | 1351 | { |
1329 | struct ip_mc_list *i; | 1352 | struct ip_mc_list *pmc; |
1330 | 1353 | ||
1331 | ASSERT_RTNL(); | 1354 | ASSERT_RTNL(); |
1332 | 1355 | ||
1333 | for (i = in_dev->mc_list; i; i = i->next) | 1356 | for_each_pmc_rtnl(in_dev, pmc) |
1334 | igmp_group_added(i); | 1357 | igmp_group_added(pmc); |
1335 | } | 1358 | } |
1336 | 1359 | ||
1337 | /* Device going down */ | 1360 | /* Device going down */ |
1338 | 1361 | ||
1339 | void ip_mc_down(struct in_device *in_dev) | 1362 | void ip_mc_down(struct in_device *in_dev) |
1340 | { | 1363 | { |
1341 | struct ip_mc_list *i; | 1364 | struct ip_mc_list *pmc; |
1342 | 1365 | ||
1343 | ASSERT_RTNL(); | 1366 | ASSERT_RTNL(); |
1344 | 1367 | ||
1345 | for (i=in_dev->mc_list; i; i=i->next) | 1368 | for_each_pmc_rtnl(in_dev, pmc) |
1346 | igmp_group_dropped(i); | 1369 | igmp_group_dropped(pmc); |
1347 | 1370 | ||
1348 | #ifdef CONFIG_IP_MULTICAST | 1371 | #ifdef CONFIG_IP_MULTICAST |
1349 | in_dev->mr_ifc_count = 0; | 1372 | in_dev->mr_ifc_count = 0; |
@@ -1374,7 +1397,6 @@ void ip_mc_init_dev(struct in_device *in_dev) | |||
1374 | in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; | 1397 | in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; |
1375 | #endif | 1398 | #endif |
1376 | 1399 | ||
1377 | rwlock_init(&in_dev->mc_list_lock); | ||
1378 | spin_lock_init(&in_dev->mc_tomb_lock); | 1400 | spin_lock_init(&in_dev->mc_tomb_lock); |
1379 | } | 1401 | } |
1380 | 1402 | ||
@@ -1382,14 +1404,14 @@ void ip_mc_init_dev(struct in_device *in_dev) | |||
1382 | 1404 | ||
1383 | void ip_mc_up(struct in_device *in_dev) | 1405 | void ip_mc_up(struct in_device *in_dev) |
1384 | { | 1406 | { |
1385 | struct ip_mc_list *i; | 1407 | struct ip_mc_list *pmc; |
1386 | 1408 | ||
1387 | ASSERT_RTNL(); | 1409 | ASSERT_RTNL(); |
1388 | 1410 | ||
1389 | ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); | 1411 | ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); |
1390 | 1412 | ||
1391 | for (i=in_dev->mc_list; i; i=i->next) | 1413 | for_each_pmc_rtnl(in_dev, pmc) |
1392 | igmp_group_added(i); | 1414 | igmp_group_added(pmc); |
1393 | } | 1415 | } |
1394 | 1416 | ||
1395 | /* | 1417 | /* |
@@ -1405,24 +1427,19 @@ void ip_mc_destroy_dev(struct in_device *in_dev) | |||
1405 | /* Deactivate timers */ | 1427 | /* Deactivate timers */ |
1406 | ip_mc_down(in_dev); | 1428 | ip_mc_down(in_dev); |
1407 | 1429 | ||
1408 | write_lock_bh(&in_dev->mc_list_lock); | 1430 | while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { |
1409 | while ((i = in_dev->mc_list) != NULL) { | 1431 | in_dev->mc_list = i->next_rcu; |
1410 | in_dev->mc_list = i->next; | ||
1411 | in_dev->mc_count--; | 1432 | in_dev->mc_count--; |
1412 | write_unlock_bh(&in_dev->mc_list_lock); | 1433 | |
1413 | igmp_group_dropped(i); | 1434 | igmp_group_dropped(i); |
1414 | ip_ma_put(i); | 1435 | ip_ma_put(i); |
1415 | |||
1416 | write_lock_bh(&in_dev->mc_list_lock); | ||
1417 | } | 1436 | } |
1418 | write_unlock_bh(&in_dev->mc_list_lock); | ||
1419 | } | 1437 | } |
1420 | 1438 | ||
1421 | /* RTNL is locked */ | 1439 | /* RTNL is locked */ |
1422 | static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) | 1440 | static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) |
1423 | { | 1441 | { |
1424 | struct flowi fl = { .nl_u = { .ip4_u = | 1442 | struct flowi fl = { .fl4_dst = imr->imr_multiaddr.s_addr }; |
1425 | { .daddr = imr->imr_multiaddr.s_addr } } }; | ||
1426 | struct rtable *rt; | 1443 | struct rtable *rt; |
1427 | struct net_device *dev = NULL; | 1444 | struct net_device *dev = NULL; |
1428 | struct in_device *idev = NULL; | 1445 | struct in_device *idev = NULL; |
@@ -1513,18 +1530,18 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, | |||
1513 | 1530 | ||
1514 | if (!in_dev) | 1531 | if (!in_dev) |
1515 | return -ENODEV; | 1532 | return -ENODEV; |
1516 | read_lock(&in_dev->mc_list_lock); | 1533 | rcu_read_lock(); |
1517 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 1534 | for_each_pmc_rcu(in_dev, pmc) { |
1518 | if (*pmca == pmc->multiaddr) | 1535 | if (*pmca == pmc->multiaddr) |
1519 | break; | 1536 | break; |
1520 | } | 1537 | } |
1521 | if (!pmc) { | 1538 | if (!pmc) { |
1522 | /* MCA not found?? bug */ | 1539 | /* MCA not found?? bug */ |
1523 | read_unlock(&in_dev->mc_list_lock); | 1540 | rcu_read_unlock(); |
1524 | return -ESRCH; | 1541 | return -ESRCH; |
1525 | } | 1542 | } |
1526 | spin_lock_bh(&pmc->lock); | 1543 | spin_lock_bh(&pmc->lock); |
1527 | read_unlock(&in_dev->mc_list_lock); | 1544 | rcu_read_unlock(); |
1528 | #ifdef CONFIG_IP_MULTICAST | 1545 | #ifdef CONFIG_IP_MULTICAST |
1529 | sf_markstate(pmc); | 1546 | sf_markstate(pmc); |
1530 | #endif | 1547 | #endif |
@@ -1685,18 +1702,18 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, | |||
1685 | 1702 | ||
1686 | if (!in_dev) | 1703 | if (!in_dev) |
1687 | return -ENODEV; | 1704 | return -ENODEV; |
1688 | read_lock(&in_dev->mc_list_lock); | 1705 | rcu_read_lock(); |
1689 | for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { | 1706 | for_each_pmc_rcu(in_dev, pmc) { |
1690 | if (*pmca == pmc->multiaddr) | 1707 | if (*pmca == pmc->multiaddr) |
1691 | break; | 1708 | break; |
1692 | } | 1709 | } |
1693 | if (!pmc) { | 1710 | if (!pmc) { |
1694 | /* MCA not found?? bug */ | 1711 | /* MCA not found?? bug */ |
1695 | read_unlock(&in_dev->mc_list_lock); | 1712 | rcu_read_unlock(); |
1696 | return -ESRCH; | 1713 | return -ESRCH; |
1697 | } | 1714 | } |
1698 | spin_lock_bh(&pmc->lock); | 1715 | spin_lock_bh(&pmc->lock); |
1699 | read_unlock(&in_dev->mc_list_lock); | 1716 | rcu_read_unlock(); |
1700 | 1717 | ||
1701 | #ifdef CONFIG_IP_MULTICAST | 1718 | #ifdef CONFIG_IP_MULTICAST |
1702 | sf_markstate(pmc); | 1719 | sf_markstate(pmc); |
@@ -1793,7 +1810,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) | |||
1793 | 1810 | ||
1794 | err = -EADDRINUSE; | 1811 | err = -EADDRINUSE; |
1795 | ifindex = imr->imr_ifindex; | 1812 | ifindex = imr->imr_ifindex; |
1796 | for (i = inet->mc_list; i; i = i->next) { | 1813 | for_each_pmc_rtnl(inet, i) { |
1797 | if (i->multi.imr_multiaddr.s_addr == addr && | 1814 | if (i->multi.imr_multiaddr.s_addr == addr && |
1798 | i->multi.imr_ifindex == ifindex) | 1815 | i->multi.imr_ifindex == ifindex) |
1799 | goto done; | 1816 | goto done; |
@@ -1807,7 +1824,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) | |||
1807 | goto done; | 1824 | goto done; |
1808 | 1825 | ||
1809 | memcpy(&iml->multi, imr, sizeof(*imr)); | 1826 | memcpy(&iml->multi, imr, sizeof(*imr)); |
1810 | iml->next = inet->mc_list; | 1827 | iml->next_rcu = inet->mc_list; |
1811 | iml->sflist = NULL; | 1828 | iml->sflist = NULL; |
1812 | iml->sfmode = MCAST_EXCLUDE; | 1829 | iml->sfmode = MCAST_EXCLUDE; |
1813 | rcu_assign_pointer(inet->mc_list, iml); | 1830 | rcu_assign_pointer(inet->mc_list, iml); |
@@ -1821,17 +1838,14 @@ EXPORT_SYMBOL(ip_mc_join_group); | |||
1821 | 1838 | ||
1822 | static void ip_sf_socklist_reclaim(struct rcu_head *rp) | 1839 | static void ip_sf_socklist_reclaim(struct rcu_head *rp) |
1823 | { | 1840 | { |
1824 | struct ip_sf_socklist *psf; | 1841 | kfree(container_of(rp, struct ip_sf_socklist, rcu)); |
1825 | |||
1826 | psf = container_of(rp, struct ip_sf_socklist, rcu); | ||
1827 | /* sk_omem_alloc should have been decreased by the caller*/ | 1842 | /* sk_omem_alloc should have been decreased by the caller*/ |
1828 | kfree(psf); | ||
1829 | } | 1843 | } |
1830 | 1844 | ||
1831 | static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, | 1845 | static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, |
1832 | struct in_device *in_dev) | 1846 | struct in_device *in_dev) |
1833 | { | 1847 | { |
1834 | struct ip_sf_socklist *psf = iml->sflist; | 1848 | struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist); |
1835 | int err; | 1849 | int err; |
1836 | 1850 | ||
1837 | if (psf == NULL) { | 1851 | if (psf == NULL) { |
@@ -1851,11 +1865,8 @@ static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, | |||
1851 | 1865 | ||
1852 | static void ip_mc_socklist_reclaim(struct rcu_head *rp) | 1866 | static void ip_mc_socklist_reclaim(struct rcu_head *rp) |
1853 | { | 1867 | { |
1854 | struct ip_mc_socklist *iml; | 1868 | kfree(container_of(rp, struct ip_mc_socklist, rcu)); |
1855 | |||
1856 | iml = container_of(rp, struct ip_mc_socklist, rcu); | ||
1857 | /* sk_omem_alloc should have been decreased by the caller*/ | 1869 | /* sk_omem_alloc should have been decreased by the caller*/ |
1858 | kfree(iml); | ||
1859 | } | 1870 | } |
1860 | 1871 | ||
1861 | 1872 | ||
@@ -1866,7 +1877,8 @@ static void ip_mc_socklist_reclaim(struct rcu_head *rp) | |||
1866 | int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | 1877 | int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) |
1867 | { | 1878 | { |
1868 | struct inet_sock *inet = inet_sk(sk); | 1879 | struct inet_sock *inet = inet_sk(sk); |
1869 | struct ip_mc_socklist *iml, **imlp; | 1880 | struct ip_mc_socklist *iml; |
1881 | struct ip_mc_socklist __rcu **imlp; | ||
1870 | struct in_device *in_dev; | 1882 | struct in_device *in_dev; |
1871 | struct net *net = sock_net(sk); | 1883 | struct net *net = sock_net(sk); |
1872 | __be32 group = imr->imr_multiaddr.s_addr; | 1884 | __be32 group = imr->imr_multiaddr.s_addr; |
@@ -1876,7 +1888,9 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | |||
1876 | rtnl_lock(); | 1888 | rtnl_lock(); |
1877 | in_dev = ip_mc_find_dev(net, imr); | 1889 | in_dev = ip_mc_find_dev(net, imr); |
1878 | ifindex = imr->imr_ifindex; | 1890 | ifindex = imr->imr_ifindex; |
1879 | for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) { | 1891 | for (imlp = &inet->mc_list; |
1892 | (iml = rtnl_dereference(*imlp)) != NULL; | ||
1893 | imlp = &iml->next_rcu) { | ||
1880 | if (iml->multi.imr_multiaddr.s_addr != group) | 1894 | if (iml->multi.imr_multiaddr.s_addr != group) |
1881 | continue; | 1895 | continue; |
1882 | if (ifindex) { | 1896 | if (ifindex) { |
@@ -1888,7 +1902,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | |||
1888 | 1902 | ||
1889 | (void) ip_mc_leave_src(sk, iml, in_dev); | 1903 | (void) ip_mc_leave_src(sk, iml, in_dev); |
1890 | 1904 | ||
1891 | rcu_assign_pointer(*imlp, iml->next); | 1905 | *imlp = iml->next_rcu; |
1892 | 1906 | ||
1893 | if (in_dev) | 1907 | if (in_dev) |
1894 | ip_mc_dec_group(in_dev, group); | 1908 | ip_mc_dec_group(in_dev, group); |
@@ -1934,7 +1948,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1934 | } | 1948 | } |
1935 | err = -EADDRNOTAVAIL; | 1949 | err = -EADDRNOTAVAIL; |
1936 | 1950 | ||
1937 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 1951 | for_each_pmc_rtnl(inet, pmc) { |
1938 | if ((pmc->multi.imr_multiaddr.s_addr == | 1952 | if ((pmc->multi.imr_multiaddr.s_addr == |
1939 | imr.imr_multiaddr.s_addr) && | 1953 | imr.imr_multiaddr.s_addr) && |
1940 | (pmc->multi.imr_ifindex == imr.imr_ifindex)) | 1954 | (pmc->multi.imr_ifindex == imr.imr_ifindex)) |
@@ -1958,7 +1972,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1958 | pmc->sfmode = omode; | 1972 | pmc->sfmode = omode; |
1959 | } | 1973 | } |
1960 | 1974 | ||
1961 | psl = pmc->sflist; | 1975 | psl = rtnl_dereference(pmc->sflist); |
1962 | if (!add) { | 1976 | if (!add) { |
1963 | if (!psl) | 1977 | if (!psl) |
1964 | goto done; /* err = -EADDRNOTAVAIL */ | 1978 | goto done; /* err = -EADDRNOTAVAIL */ |
@@ -2077,7 +2091,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | |||
2077 | goto done; | 2091 | goto done; |
2078 | } | 2092 | } |
2079 | 2093 | ||
2080 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 2094 | for_each_pmc_rtnl(inet, pmc) { |
2081 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && | 2095 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && |
2082 | pmc->multi.imr_ifindex == imr.imr_ifindex) | 2096 | pmc->multi.imr_ifindex == imr.imr_ifindex) |
2083 | break; | 2097 | break; |
@@ -2107,7 +2121,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | |||
2107 | (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, | 2121 | (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, |
2108 | msf->imsf_fmode, 0, NULL, 0); | 2122 | msf->imsf_fmode, 0, NULL, 0); |
2109 | } | 2123 | } |
2110 | psl = pmc->sflist; | 2124 | psl = rtnl_dereference(pmc->sflist); |
2111 | if (psl) { | 2125 | if (psl) { |
2112 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, | 2126 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, |
2113 | psl->sl_count, psl->sl_addr, 0); | 2127 | psl->sl_count, psl->sl_addr, 0); |
@@ -2155,7 +2169,7 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, | |||
2155 | } | 2169 | } |
2156 | err = -EADDRNOTAVAIL; | 2170 | err = -EADDRNOTAVAIL; |
2157 | 2171 | ||
2158 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 2172 | for_each_pmc_rtnl(inet, pmc) { |
2159 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && | 2173 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && |
2160 | pmc->multi.imr_ifindex == imr.imr_ifindex) | 2174 | pmc->multi.imr_ifindex == imr.imr_ifindex) |
2161 | break; | 2175 | break; |
@@ -2163,7 +2177,7 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, | |||
2163 | if (!pmc) /* must have a prior join */ | 2177 | if (!pmc) /* must have a prior join */ |
2164 | goto done; | 2178 | goto done; |
2165 | msf->imsf_fmode = pmc->sfmode; | 2179 | msf->imsf_fmode = pmc->sfmode; |
2166 | psl = pmc->sflist; | 2180 | psl = rtnl_dereference(pmc->sflist); |
2167 | rtnl_unlock(); | 2181 | rtnl_unlock(); |
2168 | if (!psl) { | 2182 | if (!psl) { |
2169 | len = 0; | 2183 | len = 0; |
@@ -2208,7 +2222,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, | |||
2208 | 2222 | ||
2209 | err = -EADDRNOTAVAIL; | 2223 | err = -EADDRNOTAVAIL; |
2210 | 2224 | ||
2211 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 2225 | for_each_pmc_rtnl(inet, pmc) { |
2212 | if (pmc->multi.imr_multiaddr.s_addr == addr && | 2226 | if (pmc->multi.imr_multiaddr.s_addr == addr && |
2213 | pmc->multi.imr_ifindex == gsf->gf_interface) | 2227 | pmc->multi.imr_ifindex == gsf->gf_interface) |
2214 | break; | 2228 | break; |
@@ -2216,7 +2230,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, | |||
2216 | if (!pmc) /* must have a prior join */ | 2230 | if (!pmc) /* must have a prior join */ |
2217 | goto done; | 2231 | goto done; |
2218 | gsf->gf_fmode = pmc->sfmode; | 2232 | gsf->gf_fmode = pmc->sfmode; |
2219 | psl = pmc->sflist; | 2233 | psl = rtnl_dereference(pmc->sflist); |
2220 | rtnl_unlock(); | 2234 | rtnl_unlock(); |
2221 | count = psl ? psl->sl_count : 0; | 2235 | count = psl ? psl->sl_count : 0; |
2222 | copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; | 2236 | copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; |
@@ -2257,7 +2271,7 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) | |||
2257 | goto out; | 2271 | goto out; |
2258 | 2272 | ||
2259 | rcu_read_lock(); | 2273 | rcu_read_lock(); |
2260 | for (pmc=rcu_dereference(inet->mc_list); pmc; pmc=rcu_dereference(pmc->next)) { | 2274 | for_each_pmc_rcu(inet, pmc) { |
2261 | if (pmc->multi.imr_multiaddr.s_addr == loc_addr && | 2275 | if (pmc->multi.imr_multiaddr.s_addr == loc_addr && |
2262 | pmc->multi.imr_ifindex == dif) | 2276 | pmc->multi.imr_ifindex == dif) |
2263 | break; | 2277 | break; |
@@ -2265,7 +2279,7 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) | |||
2265 | ret = inet->mc_all; | 2279 | ret = inet->mc_all; |
2266 | if (!pmc) | 2280 | if (!pmc) |
2267 | goto unlock; | 2281 | goto unlock; |
2268 | psl = pmc->sflist; | 2282 | psl = rcu_dereference(pmc->sflist); |
2269 | ret = (pmc->sfmode == MCAST_EXCLUDE); | 2283 | ret = (pmc->sfmode == MCAST_EXCLUDE); |
2270 | if (!psl) | 2284 | if (!psl) |
2271 | goto unlock; | 2285 | goto unlock; |
@@ -2300,16 +2314,14 @@ void ip_mc_drop_socket(struct sock *sk) | |||
2300 | return; | 2314 | return; |
2301 | 2315 | ||
2302 | rtnl_lock(); | 2316 | rtnl_lock(); |
2303 | while ((iml = inet->mc_list) != NULL) { | 2317 | while ((iml = rtnl_dereference(inet->mc_list)) != NULL) { |
2304 | struct in_device *in_dev; | 2318 | struct in_device *in_dev; |
2305 | rcu_assign_pointer(inet->mc_list, iml->next); | ||
2306 | 2319 | ||
2320 | inet->mc_list = iml->next_rcu; | ||
2307 | in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); | 2321 | in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); |
2308 | (void) ip_mc_leave_src(sk, iml, in_dev); | 2322 | (void) ip_mc_leave_src(sk, iml, in_dev); |
2309 | if (in_dev != NULL) { | 2323 | if (in_dev != NULL) |
2310 | ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); | 2324 | ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); |
2311 | in_dev_put(in_dev); | ||
2312 | } | ||
2313 | /* decrease mem now to avoid the memleak warning */ | 2325 | /* decrease mem now to avoid the memleak warning */ |
2314 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); | 2326 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); |
2315 | call_rcu(&iml->rcu, ip_mc_socklist_reclaim); | 2327 | call_rcu(&iml->rcu, ip_mc_socklist_reclaim); |
@@ -2323,8 +2335,8 @@ int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 p | |||
2323 | struct ip_sf_list *psf; | 2335 | struct ip_sf_list *psf; |
2324 | int rv = 0; | 2336 | int rv = 0; |
2325 | 2337 | ||
2326 | read_lock(&in_dev->mc_list_lock); | 2338 | rcu_read_lock(); |
2327 | for (im=in_dev->mc_list; im; im=im->next) { | 2339 | for_each_pmc_rcu(in_dev, im) { |
2328 | if (im->multiaddr == mc_addr) | 2340 | if (im->multiaddr == mc_addr) |
2329 | break; | 2341 | break; |
2330 | } | 2342 | } |
@@ -2345,7 +2357,7 @@ int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 p | |||
2345 | } else | 2357 | } else |
2346 | rv = 1; /* unspecified source; tentatively allow */ | 2358 | rv = 1; /* unspecified source; tentatively allow */ |
2347 | } | 2359 | } |
2348 | read_unlock(&in_dev->mc_list_lock); | 2360 | rcu_read_unlock(); |
2349 | return rv; | 2361 | return rv; |
2350 | } | 2362 | } |
2351 | 2363 | ||
@@ -2371,13 +2383,11 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) | |||
2371 | in_dev = __in_dev_get_rcu(state->dev); | 2383 | in_dev = __in_dev_get_rcu(state->dev); |
2372 | if (!in_dev) | 2384 | if (!in_dev) |
2373 | continue; | 2385 | continue; |
2374 | read_lock(&in_dev->mc_list_lock); | 2386 | im = rcu_dereference(in_dev->mc_list); |
2375 | im = in_dev->mc_list; | ||
2376 | if (im) { | 2387 | if (im) { |
2377 | state->in_dev = in_dev; | 2388 | state->in_dev = in_dev; |
2378 | break; | 2389 | break; |
2379 | } | 2390 | } |
2380 | read_unlock(&in_dev->mc_list_lock); | ||
2381 | } | 2391 | } |
2382 | return im; | 2392 | return im; |
2383 | } | 2393 | } |
@@ -2385,11 +2395,9 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) | |||
2385 | static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) | 2395 | static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) |
2386 | { | 2396 | { |
2387 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); | 2397 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); |
2388 | im = im->next; | ||
2389 | while (!im) { | ||
2390 | if (likely(state->in_dev != NULL)) | ||
2391 | read_unlock(&state->in_dev->mc_list_lock); | ||
2392 | 2398 | ||
2399 | im = rcu_dereference(im->next_rcu); | ||
2400 | while (!im) { | ||
2393 | state->dev = next_net_device_rcu(state->dev); | 2401 | state->dev = next_net_device_rcu(state->dev); |
2394 | if (!state->dev) { | 2402 | if (!state->dev) { |
2395 | state->in_dev = NULL; | 2403 | state->in_dev = NULL; |
@@ -2398,8 +2406,7 @@ static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_li | |||
2398 | state->in_dev = __in_dev_get_rcu(state->dev); | 2406 | state->in_dev = __in_dev_get_rcu(state->dev); |
2399 | if (!state->in_dev) | 2407 | if (!state->in_dev) |
2400 | continue; | 2408 | continue; |
2401 | read_lock(&state->in_dev->mc_list_lock); | 2409 | im = rcu_dereference(state->in_dev->mc_list); |
2402 | im = state->in_dev->mc_list; | ||
2403 | } | 2410 | } |
2404 | return im; | 2411 | return im; |
2405 | } | 2412 | } |
@@ -2435,10 +2442,8 @@ static void igmp_mc_seq_stop(struct seq_file *seq, void *v) | |||
2435 | __releases(rcu) | 2442 | __releases(rcu) |
2436 | { | 2443 | { |
2437 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); | 2444 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); |
2438 | if (likely(state->in_dev != NULL)) { | 2445 | |
2439 | read_unlock(&state->in_dev->mc_list_lock); | 2446 | state->in_dev = NULL; |
2440 | state->in_dev = NULL; | ||
2441 | } | ||
2442 | state->dev = NULL; | 2447 | state->dev = NULL; |
2443 | rcu_read_unlock(); | 2448 | rcu_read_unlock(); |
2444 | } | 2449 | } |
@@ -2460,7 +2465,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) | |||
2460 | querier = "NONE"; | 2465 | querier = "NONE"; |
2461 | #endif | 2466 | #endif |
2462 | 2467 | ||
2463 | if (state->in_dev->mc_list == im) { | 2468 | if (rcu_dereference(state->in_dev->mc_list) == im) { |
2464 | seq_printf(seq, "%d\t%-10s: %5d %7s\n", | 2469 | seq_printf(seq, "%d\t%-10s: %5d %7s\n", |
2465 | state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); | 2470 | state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); |
2466 | } | 2471 | } |
@@ -2519,8 +2524,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) | |||
2519 | idev = __in_dev_get_rcu(state->dev); | 2524 | idev = __in_dev_get_rcu(state->dev); |
2520 | if (unlikely(idev == NULL)) | 2525 | if (unlikely(idev == NULL)) |
2521 | continue; | 2526 | continue; |
2522 | read_lock(&idev->mc_list_lock); | 2527 | im = rcu_dereference(idev->mc_list); |
2523 | im = idev->mc_list; | ||
2524 | if (likely(im != NULL)) { | 2528 | if (likely(im != NULL)) { |
2525 | spin_lock_bh(&im->lock); | 2529 | spin_lock_bh(&im->lock); |
2526 | psf = im->sources; | 2530 | psf = im->sources; |
@@ -2531,7 +2535,6 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) | |||
2531 | } | 2535 | } |
2532 | spin_unlock_bh(&im->lock); | 2536 | spin_unlock_bh(&im->lock); |
2533 | } | 2537 | } |
2534 | read_unlock(&idev->mc_list_lock); | ||
2535 | } | 2538 | } |
2536 | return psf; | 2539 | return psf; |
2537 | } | 2540 | } |
@@ -2545,9 +2548,6 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l | |||
2545 | spin_unlock_bh(&state->im->lock); | 2548 | spin_unlock_bh(&state->im->lock); |
2546 | state->im = state->im->next; | 2549 | state->im = state->im->next; |
2547 | while (!state->im) { | 2550 | while (!state->im) { |
2548 | if (likely(state->idev != NULL)) | ||
2549 | read_unlock(&state->idev->mc_list_lock); | ||
2550 | |||
2551 | state->dev = next_net_device_rcu(state->dev); | 2551 | state->dev = next_net_device_rcu(state->dev); |
2552 | if (!state->dev) { | 2552 | if (!state->dev) { |
2553 | state->idev = NULL; | 2553 | state->idev = NULL; |
@@ -2556,8 +2556,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l | |||
2556 | state->idev = __in_dev_get_rcu(state->dev); | 2556 | state->idev = __in_dev_get_rcu(state->dev); |
2557 | if (!state->idev) | 2557 | if (!state->idev) |
2558 | continue; | 2558 | continue; |
2559 | read_lock(&state->idev->mc_list_lock); | 2559 | state->im = rcu_dereference(state->idev->mc_list); |
2560 | state->im = state->idev->mc_list; | ||
2561 | } | 2560 | } |
2562 | if (!state->im) | 2561 | if (!state->im) |
2563 | break; | 2562 | break; |
@@ -2603,10 +2602,7 @@ static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) | |||
2603 | spin_unlock_bh(&state->im->lock); | 2602 | spin_unlock_bh(&state->im->lock); |
2604 | state->im = NULL; | 2603 | state->im = NULL; |
2605 | } | 2604 | } |
2606 | if (likely(state->idev != NULL)) { | 2605 | state->idev = NULL; |
2607 | read_unlock(&state->idev->mc_list_lock); | ||
2608 | state->idev = NULL; | ||
2609 | } | ||
2610 | state->dev = NULL; | 2606 | state->dev = NULL; |
2611 | rcu_read_unlock(); | 2607 | rcu_read_unlock(); |
2612 | } | 2608 | } |