Re: [PATCH net] ipv6: mcast: Delay put pmc->idev in mld_del_delrec(): manual merge

From: Matthieu Baerts
Date: Thu Jul 17 2025 - 10:42:04 EST


Hi Yue, Paolo, Jakub,

On 14/07/2025 16:19, Yue Haibing wrote:
> pmc->idev is still used in ip6_mc_clear_src(), so as mld_clear_delrec()
> does, the reference should be put after ip6_mc_clear_src() return.

FYI, I got a small conflict when merging 'net' in 'net-next' in the
MPTCP tree due to this patch applied in 'net':

ae3264a25a46 ("ipv6: mcast: Delay put pmc->idev in mld_del_delrec()")

and this one from 'net-next':

a8594c956cc9 ("ipv6: mcast: Avoid a duplicate pointer check in
mld_del_delrec()")

----- Generic Message -----
The best is to avoid conflicts between 'net' and 'net-next' trees but if
they cannot be avoided when preparing patches, a note about how to fix
them is much appreciated.

The conflict has been resolved on our side[1] and the resolution we
suggest is attached to this email. Please report any issues linked to
this conflict resolution as it might be used by others. If you worked on
the mentioned patches, don't hesitate to ACK this conflict resolution.
---------------------------

Regarding this conflict, the patch from net has been applied at a
slightly different place after the code refactoring from net-next.

Rerere cache is available in [2].

[1] https://github.com/multipath-tcp/mptcp_net-next/commit/ec9d9e40de20
[2] https://github.com/multipath-tcp/mptcp-upstream-rr-cache/commit/fe71

Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
diff --cc net/ipv6/mcast.c
index 0c63c33ab080,616bf4c0c8fd..36ca27496b3c
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@@ -786,34 -783,37 +786,34 @@@ static void mld_del_delrec(struct inet6
break;
pmc_prev = pmc;
}
- if (pmc) {
- if (pmc_prev)
- rcu_assign_pointer(pmc_prev->next, pmc->next);
- else
- rcu_assign_pointer(idev->mc_tomb, pmc->next);
- }
+ if (!pmc)
+ return;
+ if (pmc_prev)
+ rcu_assign_pointer(pmc_prev->next, pmc->next);
+ else
+ rcu_assign_pointer(idev->mc_tomb, pmc->next);

- if (pmc) {
- im->idev = pmc->idev;
- if (im->mca_sfmode == MCAST_INCLUDE) {
- tomb = rcu_replace_pointer(im->mca_tomb,
- mc_dereference(pmc->mca_tomb, pmc->idev),
- lockdep_is_held(&im->idev->mc_lock));
- rcu_assign_pointer(pmc->mca_tomb, tomb);
+ im->idev = pmc->idev;
+ if (im->mca_sfmode == MCAST_INCLUDE) {
+ tomb = rcu_replace_pointer(im->mca_tomb,
+ mc_dereference(pmc->mca_tomb, pmc->idev),
+ lockdep_is_held(&im->idev->mc_lock));
+ rcu_assign_pointer(pmc->mca_tomb, tomb);

- sources = rcu_replace_pointer(im->mca_sources,
- mc_dereference(pmc->mca_sources, pmc->idev),
- lockdep_is_held(&im->idev->mc_lock));
- rcu_assign_pointer(pmc->mca_sources, sources);
- for_each_psf_mclock(im, psf)
- psf->sf_crcount = idev->mc_qrv;
- } else {
- im->mca_crcount = idev->mc_qrv;
- }
- ip6_mc_clear_src(pmc);
- in6_dev_put(pmc->idev);
- kfree_rcu(pmc, rcu);
+ sources = rcu_replace_pointer(im->mca_sources,
+ mc_dereference(pmc->mca_sources, pmc->idev),
+ lockdep_is_held(&im->idev->mc_lock));
+ rcu_assign_pointer(pmc->mca_sources, sources);
+ for_each_psf_mclock(im, psf)
+ psf->sf_crcount = idev->mc_qrv;
+ } else {
+ im->mca_crcount = idev->mc_qrv;
}
- in6_dev_put(pmc->idev);
+ ip6_mc_clear_src(pmc);
++ in6_dev_put(pmc->idev);
+ kfree_rcu(pmc, rcu);
}

-/* called with mc_lock */
static void mld_clear_delrec(struct inet6_dev *idev)
{
struct ifmcaddr6 *pmc, *nextpmc;