Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mld: do not remove mld souce list info when set link down

This is an IPv6 version of commit 24803f38a5c0 ("igmp: do not remove igmp
souce list..."). In mld_del_delrec(), we will restore back all source filter
info instead of flush them.

Move mld_clear_delrec() from ipv6_mc_down() to ipv6_mc_destroy_dev() since
we should not remove source list info when set link down. Remove
igmp6_group_dropped() in ipv6_mc_destroy_dev() since we have called it in
ipv6_mc_down().

Also clear all source info after igmp6_group_dropped() instead of in it
because ipv6_mc_down() will call igmp6_group_dropped().

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Hangbin Liu and committed by
David S. Miller
1666d49e 34393529

+30 -21
+30 -21
net/ipv6/mcast.c
··· 81 81 static void mld_ifc_timer_expire(unsigned long data); 82 82 static void mld_ifc_event(struct inet6_dev *idev); 83 83 static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc); 84 - static void mld_del_delrec(struct inet6_dev *idev, const struct in6_addr *addr); 84 + static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc); 85 85 static void mld_clear_delrec(struct inet6_dev *idev); 86 86 static bool mld_in_v1_mode(const struct inet6_dev *idev); 87 87 static int sf_setstate(struct ifmcaddr6 *pmc); ··· 692 692 dev_mc_del(dev, buf); 693 693 } 694 694 695 - if (mc->mca_flags & MAF_NOREPORT) 696 - goto done; 697 695 spin_unlock_bh(&mc->mca_lock); 696 + if (mc->mca_flags & MAF_NOREPORT) 697 + return; 698 698 699 699 if (!mc->idev->dead) 700 700 igmp6_leave_group(mc); ··· 702 702 spin_lock_bh(&mc->mca_lock); 703 703 if (del_timer(&mc->mca_timer)) 704 704 atomic_dec(&mc->mca_refcnt); 705 - done: 706 - ip6_mc_clear_src(mc); 707 705 spin_unlock_bh(&mc->mca_lock); 708 706 } 709 707 ··· 746 748 spin_unlock_bh(&idev->mc_lock); 747 749 } 748 750 749 - static void mld_del_delrec(struct inet6_dev *idev, const struct in6_addr *pmca) 751 + static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im) 750 752 { 751 753 struct ifmcaddr6 *pmc, *pmc_prev; 752 - struct ip6_sf_list *psf, *psf_next; 754 + struct ip6_sf_list *psf; 755 + struct in6_addr *pmca = &im->mca_addr; 753 756 754 757 spin_lock_bh(&idev->mc_lock); 755 758 pmc_prev = NULL; ··· 767 768 } 768 769 spin_unlock_bh(&idev->mc_lock); 769 770 771 + spin_lock_bh(&im->mca_lock); 770 772 if (pmc) { 771 - for (psf = pmc->mca_tomb; psf; psf = psf_next) { 772 - psf_next = psf->sf_next; 773 - kfree(psf); 773 + im->idev = pmc->idev; 774 + im->mca_crcount = idev->mc_qrv; 775 + im->mca_sfmode = pmc->mca_sfmode; 776 + if (pmc->mca_sfmode == MCAST_INCLUDE) { 777 + im->mca_tomb = pmc->mca_tomb; 778 + im->mca_sources = pmc->mca_sources; 779 + for (psf = im->mca_sources; psf; psf = psf->sf_next) 780 + psf->sf_crcount = im->mca_crcount; 774 781 } 775 782 in6_dev_put(pmc->idev); 776 - kfree(pmc); 777 783 } 784 + spin_unlock_bh(&im->mca_lock); 778 785 } 779 786 780 787 static void mld_clear_delrec(struct inet6_dev *idev) ··· 909 904 mca_get(mc); 910 905 write_unlock_bh(&idev->lock); 911 906 912 - mld_del_delrec(idev, &mc->mca_addr); 907 + mld_del_delrec(idev, mc); 913 908 igmp6_group_added(mc); 914 909 ma_put(mc); 915 910 return 0; ··· 932 927 write_unlock_bh(&idev->lock); 933 928 934 929 igmp6_group_dropped(ma); 930 + ip6_mc_clear_src(ma); 935 931 936 932 ma_put(ma); 937 933 return 0; ··· 2507 2501 /* Withdraw multicast list */ 2508 2502 2509 2503 read_lock_bh(&idev->lock); 2510 - mld_ifc_stop_timer(idev); 2511 - mld_gq_stop_timer(idev); 2512 - mld_dad_stop_timer(idev); 2513 2504 2514 2505 for (i = idev->mc_list; i; i = i->next) 2515 2506 igmp6_group_dropped(i); 2516 - read_unlock_bh(&idev->lock); 2517 2507 2518 - mld_clear_delrec(idev); 2508 + /* Should stop timer after group drop. or we will 2509 + * start timer again in mld_ifc_event() 2510 + */ 2511 + mld_ifc_stop_timer(idev); 2512 + mld_gq_stop_timer(idev); 2513 + mld_dad_stop_timer(idev); 2514 + read_unlock_bh(&idev->lock); 2519 2515 } 2520 2516 2521 2517 static void ipv6_mc_reset(struct inet6_dev *idev) ··· 2539 2531 2540 2532 read_lock_bh(&idev->lock); 2541 2533 ipv6_mc_reset(idev); 2542 - for (i = idev->mc_list; i; i = i->next) 2534 + for (i = idev->mc_list; i; i = i->next) { 2535 + mld_del_delrec(idev, i); 2543 2536 igmp6_group_added(i); 2537 + } 2544 2538 read_unlock_bh(&idev->lock); 2545 2539 } 2546 2540 ··· 2575 2565 2576 2566 /* Deactivate timers */ 2577 2567 ipv6_mc_down(idev); 2568 + mld_clear_delrec(idev); 2578 2569 2579 2570 /* Delete all-nodes address. */ 2580 2571 /* We cannot call ipv6_dev_mc_dec() directly, our caller in ··· 2590 2579 write_lock_bh(&idev->lock); 2591 2580 while ((i = idev->mc_list) != NULL) { 2592 2581 idev->mc_list = i->next; 2582 + 2593 2583 write_unlock_bh(&idev->lock); 2594 - 2595 - igmp6_group_dropped(i); 2596 2584 ma_put(i); 2597 - 2598 2585 write_lock_bh(&idev->lock); 2599 2586 } 2600 2587 write_unlock_bh(&idev->lock);