diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-06-07 17:05:02 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-09 21:06:14 -0400 |
commit | 96b52e61be1ad4d4f8de39b9deaf253da804ea3b (patch) | |
tree | 7751b360813e7c821b3eb3439651edcbc77f5eae /net/ipv6/mcast.c | |
parent | 97859160c5158a97a4b976a2d7bc74cf2223afe0 (diff) |
ipv6: mcast: RCU conversions
- ipv6_sock_mc_join() : doesnt touch dev refcount
- ipv6_sock_mc_drop() : doesnt touch dev/idev refcounts
- ip6_mc_find_dev() becomes ip6_mc_find_dev_rcu() (called from rcu),
and doesnt touch dev/idev refcounts
- ipv6_sock_mc_close() : doesnt touch dev/idev refcounts
- ip6_mc_source() uses ip6_mc_find_dev_rcu()
- ip6_mc_msfilter() uses ip6_mc_find_dev_rcu()
- ip6_mc_msfget() uses ip6_mc_find_dev_rcu()
- ipv6_dev_mc_dec(), ipv6_chk_mcast_addr(),
igmp6_event_query(), igmp6_event_report(),
mld_sendpack(), igmp6_send() dont touch idev refcount
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/mcast.c')
-rw-r--r-- | net/ipv6/mcast.c | 183 |
1 files changed, 87 insertions, 96 deletions
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 8752e8084806..3e36d1538b6e 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c | |||
@@ -152,18 +152,19 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
152 | mc_lst->next = NULL; | 152 | mc_lst->next = NULL; |
153 | ipv6_addr_copy(&mc_lst->addr, addr); | 153 | ipv6_addr_copy(&mc_lst->addr, addr); |
154 | 154 | ||
155 | rcu_read_lock(); | ||
155 | if (ifindex == 0) { | 156 | if (ifindex == 0) { |
156 | struct rt6_info *rt; | 157 | struct rt6_info *rt; |
157 | rt = rt6_lookup(net, addr, NULL, 0, 0); | 158 | rt = rt6_lookup(net, addr, NULL, 0, 0); |
158 | if (rt) { | 159 | if (rt) { |
159 | dev = rt->rt6i_dev; | 160 | dev = rt->rt6i_dev; |
160 | dev_hold(dev); | ||
161 | dst_release(&rt->u.dst); | 161 | dst_release(&rt->u.dst); |
162 | } | 162 | } |
163 | } else | 163 | } else |
164 | dev = dev_get_by_index(net, ifindex); | 164 | dev = dev_get_by_index_rcu(net, ifindex); |
165 | 165 | ||
166 | if (dev == NULL) { | 166 | if (dev == NULL) { |
167 | rcu_read_unlock(); | ||
167 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); | 168 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); |
168 | return -ENODEV; | 169 | return -ENODEV; |
169 | } | 170 | } |
@@ -180,8 +181,8 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
180 | err = ipv6_dev_mc_inc(dev, addr); | 181 | err = ipv6_dev_mc_inc(dev, addr); |
181 | 182 | ||
182 | if (err) { | 183 | if (err) { |
184 | rcu_read_unlock(); | ||
183 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); | 185 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); |
184 | dev_put(dev); | ||
185 | return err; | 186 | return err; |
186 | } | 187 | } |
187 | 188 | ||
@@ -190,7 +191,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
190 | np->ipv6_mc_list = mc_lst; | 191 | np->ipv6_mc_list = mc_lst; |
191 | write_unlock_bh(&ipv6_sk_mc_lock); | 192 | write_unlock_bh(&ipv6_sk_mc_lock); |
192 | 193 | ||
193 | dev_put(dev); | 194 | rcu_read_unlock(); |
194 | 195 | ||
195 | return 0; | 196 | return 0; |
196 | } | 197 | } |
@@ -213,18 +214,17 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
213 | *lnk = mc_lst->next; | 214 | *lnk = mc_lst->next; |
214 | write_unlock_bh(&ipv6_sk_mc_lock); | 215 | write_unlock_bh(&ipv6_sk_mc_lock); |
215 | 216 | ||
216 | dev = dev_get_by_index(net, mc_lst->ifindex); | 217 | rcu_read_lock(); |
218 | dev = dev_get_by_index_rcu(net, mc_lst->ifindex); | ||
217 | if (dev != NULL) { | 219 | if (dev != NULL) { |
218 | struct inet6_dev *idev = in6_dev_get(dev); | 220 | struct inet6_dev *idev = __in6_dev_get(dev); |
219 | 221 | ||
220 | (void) ip6_mc_leave_src(sk, mc_lst, idev); | 222 | (void) ip6_mc_leave_src(sk, mc_lst, idev); |
221 | if (idev) { | 223 | if (idev) |
222 | __ipv6_dev_mc_dec(idev, &mc_lst->addr); | 224 | __ipv6_dev_mc_dec(idev, &mc_lst->addr); |
223 | in6_dev_put(idev); | ||
224 | } | ||
225 | dev_put(dev); | ||
226 | } else | 225 | } else |
227 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); | 226 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); |
227 | rcu_read_unlock(); | ||
228 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); | 228 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); |
229 | return 0; | 229 | return 0; |
230 | } | 230 | } |
@@ -234,43 +234,36 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
234 | return -EADDRNOTAVAIL; | 234 | return -EADDRNOTAVAIL; |
235 | } | 235 | } |
236 | 236 | ||
237 | static struct inet6_dev *ip6_mc_find_dev(struct net *net, | 237 | /* called with rcu_read_lock() */ |
238 | struct in6_addr *group, | 238 | static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, |
239 | int ifindex) | 239 | struct in6_addr *group, |
240 | int ifindex) | ||
240 | { | 241 | { |
241 | struct net_device *dev = NULL; | 242 | struct net_device *dev = NULL; |
242 | struct inet6_dev *idev = NULL; | 243 | struct inet6_dev *idev = NULL; |
243 | 244 | ||
244 | if (ifindex == 0) { | 245 | if (ifindex == 0) { |
245 | struct rt6_info *rt; | 246 | struct rt6_info *rt = rt6_lookup(net, group, NULL, 0, 0); |
246 | 247 | ||
247 | rt = rt6_lookup(net, group, NULL, 0, 0); | ||
248 | if (rt) { | 248 | if (rt) { |
249 | dev = rt->rt6i_dev; | 249 | dev = rt->rt6i_dev; |
250 | dev_hold(dev); | 250 | dev_hold(dev); |
251 | dst_release(&rt->u.dst); | 251 | dst_release(&rt->u.dst); |
252 | } | 252 | } |
253 | } else | 253 | } else |
254 | dev = dev_get_by_index(net, ifindex); | 254 | dev = dev_get_by_index_rcu(net, ifindex); |
255 | 255 | ||
256 | if (!dev) | 256 | if (!dev) |
257 | goto nodev; | 257 | return NULL; |
258 | idev = in6_dev_get(dev); | 258 | idev = __in6_dev_get(dev); |
259 | if (!idev) | 259 | if (!idev) |
260 | goto release; | 260 | return NULL;; |
261 | read_lock_bh(&idev->lock); | 261 | read_lock_bh(&idev->lock); |
262 | if (idev->dead) | 262 | if (idev->dead) { |
263 | goto unlock_release; | 263 | read_unlock_bh(&idev->lock); |
264 | 264 | return NULL; | |
265 | } | ||
265 | return idev; | 266 | return idev; |
266 | |||
267 | unlock_release: | ||
268 | read_unlock_bh(&idev->lock); | ||
269 | in6_dev_put(idev); | ||
270 | release: | ||
271 | dev_put(dev); | ||
272 | nodev: | ||
273 | return NULL; | ||
274 | } | 267 | } |
275 | 268 | ||
276 | void ipv6_sock_mc_close(struct sock *sk) | 269 | void ipv6_sock_mc_close(struct sock *sk) |
@@ -286,19 +279,17 @@ void ipv6_sock_mc_close(struct sock *sk) | |||
286 | np->ipv6_mc_list = mc_lst->next; | 279 | np->ipv6_mc_list = mc_lst->next; |
287 | write_unlock_bh(&ipv6_sk_mc_lock); | 280 | write_unlock_bh(&ipv6_sk_mc_lock); |
288 | 281 | ||
289 | dev = dev_get_by_index(net, mc_lst->ifindex); | 282 | rcu_read_lock(); |
283 | dev = dev_get_by_index_rcu(net, mc_lst->ifindex); | ||
290 | if (dev) { | 284 | if (dev) { |
291 | struct inet6_dev *idev = in6_dev_get(dev); | 285 | struct inet6_dev *idev = __in6_dev_get(dev); |
292 | 286 | ||
293 | (void) ip6_mc_leave_src(sk, mc_lst, idev); | 287 | (void) ip6_mc_leave_src(sk, mc_lst, idev); |
294 | if (idev) { | 288 | if (idev) |
295 | __ipv6_dev_mc_dec(idev, &mc_lst->addr); | 289 | __ipv6_dev_mc_dec(idev, &mc_lst->addr); |
296 | in6_dev_put(idev); | ||
297 | } | ||
298 | dev_put(dev); | ||
299 | } else | 290 | } else |
300 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); | 291 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); |
301 | 292 | rcu_read_unlock(); | |
302 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); | 293 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); |
303 | 294 | ||
304 | write_lock_bh(&ipv6_sk_mc_lock); | 295 | write_lock_bh(&ipv6_sk_mc_lock); |
@@ -327,14 +318,17 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
327 | if (!ipv6_addr_is_multicast(group)) | 318 | if (!ipv6_addr_is_multicast(group)) |
328 | return -EINVAL; | 319 | return -EINVAL; |
329 | 320 | ||
330 | idev = ip6_mc_find_dev(net, group, pgsr->gsr_interface); | 321 | rcu_read_lock(); |
331 | if (!idev) | 322 | idev = ip6_mc_find_dev_rcu(net, group, pgsr->gsr_interface); |
323 | if (!idev) { | ||
324 | rcu_read_unlock(); | ||
332 | return -ENODEV; | 325 | return -ENODEV; |
326 | } | ||
333 | dev = idev->dev; | 327 | dev = idev->dev; |
334 | 328 | ||
335 | err = -EADDRNOTAVAIL; | 329 | err = -EADDRNOTAVAIL; |
336 | 330 | ||
337 | read_lock_bh(&ipv6_sk_mc_lock); | 331 | read_lock(&ipv6_sk_mc_lock); |
338 | for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { | 332 | for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) { |
339 | if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface) | 333 | if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface) |
340 | continue; | 334 | continue; |
@@ -358,7 +352,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
358 | pmc->sfmode = omode; | 352 | pmc->sfmode = omode; |
359 | } | 353 | } |
360 | 354 | ||
361 | write_lock_bh(&pmc->sflock); | 355 | write_lock(&pmc->sflock); |
362 | pmclocked = 1; | 356 | pmclocked = 1; |
363 | 357 | ||
364 | psl = pmc->sflist; | 358 | psl = pmc->sflist; |
@@ -433,11 +427,10 @@ int ip6_mc_source(int add, int omode, struct sock *sk, | |||
433 | ip6_mc_add_src(idev, group, omode, 1, source, 1); | 427 | ip6_mc_add_src(idev, group, omode, 1, source, 1); |
434 | done: | 428 | done: |
435 | if (pmclocked) | 429 | if (pmclocked) |
436 | write_unlock_bh(&pmc->sflock); | 430 | write_unlock(&pmc->sflock); |
437 | read_unlock_bh(&ipv6_sk_mc_lock); | 431 | read_unlock(&ipv6_sk_mc_lock); |
438 | read_unlock_bh(&idev->lock); | 432 | read_unlock_bh(&idev->lock); |
439 | in6_dev_put(idev); | 433 | rcu_read_unlock(); |
440 | dev_put(dev); | ||
441 | if (leavegroup) | 434 | if (leavegroup) |
442 | return ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group); | 435 | return ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group); |
443 | return err; | 436 | return err; |
@@ -463,14 +456,17 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) | |||
463 | gsf->gf_fmode != MCAST_EXCLUDE) | 456 | gsf->gf_fmode != MCAST_EXCLUDE) |
464 | return -EINVAL; | 457 | return -EINVAL; |
465 | 458 | ||
466 | idev = ip6_mc_find_dev(net, group, gsf->gf_interface); | 459 | rcu_read_lock(); |
460 | idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface); | ||
467 | 461 | ||
468 | if (!idev) | 462 | if (!idev) { |
463 | rcu_read_unlock(); | ||
469 | return -ENODEV; | 464 | return -ENODEV; |
465 | } | ||
470 | dev = idev->dev; | 466 | dev = idev->dev; |
471 | 467 | ||
472 | err = 0; | 468 | err = 0; |
473 | read_lock_bh(&ipv6_sk_mc_lock); | 469 | read_lock(&ipv6_sk_mc_lock); |
474 | 470 | ||
475 | if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) { | 471 | if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) { |
476 | leavegroup = 1; | 472 | leavegroup = 1; |
@@ -512,7 +508,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) | |||
512 | (void) ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0); | 508 | (void) ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0); |
513 | } | 509 | } |
514 | 510 | ||
515 | write_lock_bh(&pmc->sflock); | 511 | write_lock(&pmc->sflock); |
516 | psl = pmc->sflist; | 512 | psl = pmc->sflist; |
517 | if (psl) { | 513 | if (psl) { |
518 | (void) ip6_mc_del_src(idev, group, pmc->sfmode, | 514 | (void) ip6_mc_del_src(idev, group, pmc->sfmode, |
@@ -522,13 +518,12 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) | |||
522 | (void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); | 518 | (void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0); |
523 | pmc->sflist = newpsl; | 519 | pmc->sflist = newpsl; |
524 | pmc->sfmode = gsf->gf_fmode; | 520 | pmc->sfmode = gsf->gf_fmode; |
525 | write_unlock_bh(&pmc->sflock); | 521 | write_unlock(&pmc->sflock); |
526 | err = 0; | 522 | err = 0; |
527 | done: | 523 | done: |
528 | read_unlock_bh(&ipv6_sk_mc_lock); | 524 | read_unlock(&ipv6_sk_mc_lock); |
529 | read_unlock_bh(&idev->lock); | 525 | read_unlock_bh(&idev->lock); |
530 | in6_dev_put(idev); | 526 | rcu_read_unlock(); |
531 | dev_put(dev); | ||
532 | if (leavegroup) | 527 | if (leavegroup) |
533 | err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group); | 528 | err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group); |
534 | return err; | 529 | return err; |
@@ -551,11 +546,13 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, | |||
551 | if (!ipv6_addr_is_multicast(group)) | 546 | if (!ipv6_addr_is_multicast(group)) |
552 | return -EINVAL; | 547 | return -EINVAL; |
553 | 548 | ||
554 | idev = ip6_mc_find_dev(net, group, gsf->gf_interface); | 549 | rcu_read_lock(); |
550 | idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface); | ||
555 | 551 | ||
556 | if (!idev) | 552 | if (!idev) { |
553 | rcu_read_unlock(); | ||
557 | return -ENODEV; | 554 | return -ENODEV; |
558 | 555 | } | |
559 | dev = idev->dev; | 556 | dev = idev->dev; |
560 | 557 | ||
561 | err = -EADDRNOTAVAIL; | 558 | err = -EADDRNOTAVAIL; |
@@ -577,8 +574,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, | |||
577 | psl = pmc->sflist; | 574 | psl = pmc->sflist; |
578 | count = psl ? psl->sl_count : 0; | 575 | count = psl ? psl->sl_count : 0; |
579 | read_unlock_bh(&idev->lock); | 576 | read_unlock_bh(&idev->lock); |
580 | in6_dev_put(idev); | 577 | rcu_read_unlock(); |
581 | dev_put(dev); | ||
582 | 578 | ||
583 | copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; | 579 | copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; |
584 | gsf->gf_numsrc = count; | 580 | gsf->gf_numsrc = count; |
@@ -604,8 +600,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, | |||
604 | return 0; | 600 | return 0; |
605 | done: | 601 | done: |
606 | read_unlock_bh(&idev->lock); | 602 | read_unlock_bh(&idev->lock); |
607 | in6_dev_put(idev); | 603 | rcu_read_unlock(); |
608 | dev_put(dev); | ||
609 | return err; | 604 | return err; |
610 | } | 605 | } |
611 | 606 | ||
@@ -822,6 +817,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) | |||
822 | struct ifmcaddr6 *mc; | 817 | struct ifmcaddr6 *mc; |
823 | struct inet6_dev *idev; | 818 | struct inet6_dev *idev; |
824 | 819 | ||
820 | /* we need to take a reference on idev */ | ||
825 | idev = in6_dev_get(dev); | 821 | idev = in6_dev_get(dev); |
826 | 822 | ||
827 | if (idev == NULL) | 823 | if (idev == NULL) |
@@ -860,7 +856,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) | |||
860 | setup_timer(&mc->mca_timer, igmp6_timer_handler, (unsigned long)mc); | 856 | setup_timer(&mc->mca_timer, igmp6_timer_handler, (unsigned long)mc); |
861 | 857 | ||
862 | ipv6_addr_copy(&mc->mca_addr, addr); | 858 | ipv6_addr_copy(&mc->mca_addr, addr); |
863 | mc->idev = idev; | 859 | mc->idev = idev; /* (reference taken) */ |
864 | mc->mca_users = 1; | 860 | mc->mca_users = 1; |
865 | /* mca_stamp should be updated upon changes */ | 861 | /* mca_stamp should be updated upon changes */ |
866 | mc->mca_cstamp = mc->mca_tstamp = jiffies; | 862 | mc->mca_cstamp = mc->mca_tstamp = jiffies; |
@@ -915,16 +911,18 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr) | |||
915 | 911 | ||
916 | int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) | 912 | int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) |
917 | { | 913 | { |
918 | struct inet6_dev *idev = in6_dev_get(dev); | 914 | struct inet6_dev *idev; |
919 | int err; | 915 | int err; |
920 | 916 | ||
921 | if (!idev) | 917 | rcu_read_lock(); |
922 | return -ENODEV; | ||
923 | |||
924 | err = __ipv6_dev_mc_dec(idev, addr); | ||
925 | 918 | ||
926 | in6_dev_put(idev); | 919 | idev = __in6_dev_get(dev); |
920 | if (!idev) | ||
921 | err = -ENODEV; | ||
922 | else | ||
923 | err = __ipv6_dev_mc_dec(idev, addr); | ||
927 | 924 | ||
925 | rcu_read_unlock(); | ||
928 | return err; | 926 | return err; |
929 | } | 927 | } |
930 | 928 | ||
@@ -965,7 +963,8 @@ int ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, | |||
965 | struct ifmcaddr6 *mc; | 963 | struct ifmcaddr6 *mc; |
966 | int rv = 0; | 964 | int rv = 0; |
967 | 965 | ||
968 | idev = in6_dev_get(dev); | 966 | rcu_read_lock(); |
967 | idev = __in6_dev_get(dev); | ||
969 | if (idev) { | 968 | if (idev) { |
970 | read_lock_bh(&idev->lock); | 969 | read_lock_bh(&idev->lock); |
971 | for (mc = idev->mc_list; mc; mc=mc->next) { | 970 | for (mc = idev->mc_list; mc; mc=mc->next) { |
@@ -992,8 +991,8 @@ int ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, | |||
992 | rv = 1; /* don't filter unspecified source */ | 991 | rv = 1; /* don't filter unspecified source */ |
993 | } | 992 | } |
994 | read_unlock_bh(&idev->lock); | 993 | read_unlock_bh(&idev->lock); |
995 | in6_dev_put(idev); | ||
996 | } | 994 | } |
995 | rcu_read_unlock(); | ||
997 | return rv; | 996 | return rv; |
998 | } | 997 | } |
999 | 998 | ||
@@ -1104,6 +1103,7 @@ static int mld_marksources(struct ifmcaddr6 *pmc, int nsrcs, | |||
1104 | return 1; | 1103 | return 1; |
1105 | } | 1104 | } |
1106 | 1105 | ||
1106 | /* called with rcu_read_lock() */ | ||
1107 | int igmp6_event_query(struct sk_buff *skb) | 1107 | int igmp6_event_query(struct sk_buff *skb) |
1108 | { | 1108 | { |
1109 | struct mld2_query *mlh2 = NULL; | 1109 | struct mld2_query *mlh2 = NULL; |
@@ -1127,7 +1127,7 @@ int igmp6_event_query(struct sk_buff *skb) | |||
1127 | if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) | 1127 | if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) |
1128 | return -EINVAL; | 1128 | return -EINVAL; |
1129 | 1129 | ||
1130 | idev = in6_dev_get(skb->dev); | 1130 | idev = __in6_dev_get(skb->dev); |
1131 | 1131 | ||
1132 | if (idev == NULL) | 1132 | if (idev == NULL) |
1133 | return 0; | 1133 | return 0; |
@@ -1137,10 +1137,8 @@ int igmp6_event_query(struct sk_buff *skb) | |||
1137 | group_type = ipv6_addr_type(group); | 1137 | group_type = ipv6_addr_type(group); |
1138 | 1138 | ||
1139 | if (group_type != IPV6_ADDR_ANY && | 1139 | if (group_type != IPV6_ADDR_ANY && |
1140 | !(group_type&IPV6_ADDR_MULTICAST)) { | 1140 | !(group_type&IPV6_ADDR_MULTICAST)) |
1141 | in6_dev_put(idev); | ||
1142 | return -EINVAL; | 1141 | return -EINVAL; |
1143 | } | ||
1144 | 1142 | ||
1145 | if (len == 24) { | 1143 | if (len == 24) { |
1146 | int switchback; | 1144 | int switchback; |
@@ -1161,10 +1159,9 @@ int igmp6_event_query(struct sk_buff *skb) | |||
1161 | } else if (len >= 28) { | 1159 | } else if (len >= 28) { |
1162 | int srcs_offset = sizeof(struct mld2_query) - | 1160 | int srcs_offset = sizeof(struct mld2_query) - |
1163 | sizeof(struct icmp6hdr); | 1161 | sizeof(struct icmp6hdr); |
1164 | if (!pskb_may_pull(skb, srcs_offset)) { | 1162 | if (!pskb_may_pull(skb, srcs_offset)) |
1165 | in6_dev_put(idev); | ||
1166 | return -EINVAL; | 1163 | return -EINVAL; |
1167 | } | 1164 | |
1168 | mlh2 = (struct mld2_query *)skb_transport_header(skb); | 1165 | mlh2 = (struct mld2_query *)skb_transport_header(skb); |
1169 | max_delay = (MLDV2_MRC(ntohs(mlh2->mld2q_mrc))*HZ)/1000; | 1166 | max_delay = (MLDV2_MRC(ntohs(mlh2->mld2q_mrc))*HZ)/1000; |
1170 | if (!max_delay) | 1167 | if (!max_delay) |
@@ -1173,28 +1170,23 @@ int igmp6_event_query(struct sk_buff *skb) | |||
1173 | if (mlh2->mld2q_qrv) | 1170 | if (mlh2->mld2q_qrv) |
1174 | idev->mc_qrv = mlh2->mld2q_qrv; | 1171 | idev->mc_qrv = mlh2->mld2q_qrv; |
1175 | if (group_type == IPV6_ADDR_ANY) { /* general query */ | 1172 | if (group_type == IPV6_ADDR_ANY) { /* general query */ |
1176 | if (mlh2->mld2q_nsrcs) { | 1173 | if (mlh2->mld2q_nsrcs) |
1177 | in6_dev_put(idev); | ||
1178 | return -EINVAL; /* no sources allowed */ | 1174 | return -EINVAL; /* no sources allowed */ |
1179 | } | 1175 | |
1180 | mld_gq_start_timer(idev); | 1176 | mld_gq_start_timer(idev); |
1181 | in6_dev_put(idev); | ||
1182 | return 0; | 1177 | return 0; |
1183 | } | 1178 | } |
1184 | /* mark sources to include, if group & source-specific */ | 1179 | /* mark sources to include, if group & source-specific */ |
1185 | if (mlh2->mld2q_nsrcs != 0) { | 1180 | if (mlh2->mld2q_nsrcs != 0) { |
1186 | if (!pskb_may_pull(skb, srcs_offset + | 1181 | if (!pskb_may_pull(skb, srcs_offset + |
1187 | ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr))) { | 1182 | ntohs(mlh2->mld2q_nsrcs) * sizeof(struct in6_addr))) |
1188 | in6_dev_put(idev); | ||
1189 | return -EINVAL; | 1183 | return -EINVAL; |
1190 | } | 1184 | |
1191 | mlh2 = (struct mld2_query *)skb_transport_header(skb); | 1185 | mlh2 = (struct mld2_query *)skb_transport_header(skb); |
1192 | mark = 1; | 1186 | mark = 1; |
1193 | } | 1187 | } |
1194 | } else { | 1188 | } else |
1195 | in6_dev_put(idev); | ||
1196 | return -EINVAL; | 1189 | return -EINVAL; |
1197 | } | ||
1198 | 1190 | ||
1199 | read_lock_bh(&idev->lock); | 1191 | read_lock_bh(&idev->lock); |
1200 | if (group_type == IPV6_ADDR_ANY) { | 1192 | if (group_type == IPV6_ADDR_ANY) { |
@@ -1227,12 +1219,11 @@ int igmp6_event_query(struct sk_buff *skb) | |||
1227 | } | 1219 | } |
1228 | } | 1220 | } |
1229 | read_unlock_bh(&idev->lock); | 1221 | read_unlock_bh(&idev->lock); |
1230 | in6_dev_put(idev); | ||
1231 | 1222 | ||
1232 | return 0; | 1223 | return 0; |
1233 | } | 1224 | } |
1234 | 1225 | ||
1235 | 1226 | /* called with rcu_read_lock() */ | |
1236 | int igmp6_event_report(struct sk_buff *skb) | 1227 | int igmp6_event_report(struct sk_buff *skb) |
1237 | { | 1228 | { |
1238 | struct ifmcaddr6 *ma; | 1229 | struct ifmcaddr6 *ma; |
@@ -1260,7 +1251,7 @@ int igmp6_event_report(struct sk_buff *skb) | |||
1260 | !(addr_type&IPV6_ADDR_LINKLOCAL)) | 1251 | !(addr_type&IPV6_ADDR_LINKLOCAL)) |
1261 | return -EINVAL; | 1252 | return -EINVAL; |
1262 | 1253 | ||
1263 | idev = in6_dev_get(skb->dev); | 1254 | idev = __in6_dev_get(skb->dev); |
1264 | if (idev == NULL) | 1255 | if (idev == NULL) |
1265 | return -ENODEV; | 1256 | return -ENODEV; |
1266 | 1257 | ||
@@ -1280,7 +1271,6 @@ int igmp6_event_report(struct sk_buff *skb) | |||
1280 | } | 1271 | } |
1281 | } | 1272 | } |
1282 | read_unlock_bh(&idev->lock); | 1273 | read_unlock_bh(&idev->lock); |
1283 | in6_dev_put(idev); | ||
1284 | return 0; | 1274 | return 0; |
1285 | } | 1275 | } |
1286 | 1276 | ||
@@ -1396,12 +1386,14 @@ static void mld_sendpack(struct sk_buff *skb) | |||
1396 | struct mld2_report *pmr = | 1386 | struct mld2_report *pmr = |
1397 | (struct mld2_report *)skb_transport_header(skb); | 1387 | (struct mld2_report *)skb_transport_header(skb); |
1398 | int payload_len, mldlen; | 1388 | int payload_len, mldlen; |
1399 | struct inet6_dev *idev = in6_dev_get(skb->dev); | 1389 | struct inet6_dev *idev; |
1400 | struct net *net = dev_net(skb->dev); | 1390 | struct net *net = dev_net(skb->dev); |
1401 | int err; | 1391 | int err; |
1402 | struct flowi fl; | 1392 | struct flowi fl; |
1403 | struct dst_entry *dst; | 1393 | struct dst_entry *dst; |
1404 | 1394 | ||
1395 | rcu_read_lock(); | ||
1396 | idev = __in6_dev_get(skb->dev); | ||
1405 | IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); | 1397 | IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); |
1406 | 1398 | ||
1407 | payload_len = (skb->tail - skb->network_header) - sizeof(*pip6); | 1399 | payload_len = (skb->tail - skb->network_header) - sizeof(*pip6); |
@@ -1441,8 +1433,7 @@ out: | |||
1441 | } else | 1433 | } else |
1442 | IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS); | 1434 | IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS); |
1443 | 1435 | ||
1444 | if (likely(idev != NULL)) | 1436 | rcu_read_unlock(); |
1445 | in6_dev_put(idev); | ||
1446 | return; | 1437 | return; |
1447 | 1438 | ||
1448 | err_out: | 1439 | err_out: |
@@ -1779,7 +1770,8 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) | |||
1779 | IPPROTO_ICMPV6, | 1770 | IPPROTO_ICMPV6, |
1780 | csum_partial(hdr, len, 0)); | 1771 | csum_partial(hdr, len, 0)); |
1781 | 1772 | ||
1782 | idev = in6_dev_get(skb->dev); | 1773 | rcu_read_lock(); |
1774 | idev = __in6_dev_get(skb->dev); | ||
1783 | 1775 | ||
1784 | dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr); | 1776 | dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr); |
1785 | if (!dst) { | 1777 | if (!dst) { |
@@ -1806,8 +1798,7 @@ out: | |||
1806 | } else | 1798 | } else |
1807 | IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); | 1799 | IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); |
1808 | 1800 | ||
1809 | if (likely(idev != NULL)) | 1801 | rcu_read_unlock(); |
1810 | in6_dev_put(idev); | ||
1811 | return; | 1802 | return; |
1812 | 1803 | ||
1813 | err_out: | 1804 | err_out: |