Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 28 Jul 2023 09:44:33 GMT
From:      Kristof Provost <kp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 680ad06f90bf - main - mroute: avoid calling if_allmulti with the lock held
Message-ID:  <202307280944.36S9iXnJ029493@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=680ad06f90bf3c9728e1352475915547665b0b96

commit 680ad06f90bf3c9728e1352475915547665b0b96
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2023-07-27 06:03:25 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-07-28 09:32:39 +0000

    mroute: avoid calling if_allmulti with the lock held
    
    Avoid locking issues when if_allmulti() calls the driver's if_ioctl,
    because that may acquire sleepable locks (while we hold a non-sleepable
    rwlock).
    
    Fortunately there's no pressing need to hold the mroute lock while we
    do this, so we can postpone the call slightly, until after we've
    released the lock.
    
    This avoids the following WITNESS warning (with iflib drivers):
    
            lock order reversal: (sleepable after non-sleepable)
             1st 0xffffffff82f64960 IPv4 multicast forwarding (IPv4 multicast forwarding, rw) @ /usr/src/sys/netinet/ip_mroute.c:1050
             2nd 0xfffff8000480f180 iflib ctx lock (iflib ctx lock, sx) @ /usr/src/sys/net/iflib.c:4525
            lock order IPv4 multicast forwarding -> iflib ctx lock attempted at:
            #0 0xffffffff80bbd6ce at witness_checkorder+0xbbe
            #1 0xffffffff80b56d10 at _sx_xlock+0x60
            #2 0xffffffff80c9ce5c at iflib_if_ioctl+0x2dc
            #3 0xffffffff80c7c395 at if_setflag+0xe5
            #4 0xffffffff82f60a0e at del_vif_locked+0x9e
            #5 0xffffffff82f5f0d5 at X_ip_mrouter_set+0x265
            #6 0xffffffff80bfd402 at sosetopt+0xc2
            #7 0xffffffff80c02105 at kern_setsockopt+0xa5
            #8 0xffffffff80c02054 at sys_setsockopt+0x24
            #9 0xffffffff81046be8 at amd64_syscall+0x138
            #10 0xffffffff8101930b at fast_syscall_common+0xf8
    
    See also:       https://redmine.pfsense.org/issues/12079
    Reviewed by:    mjg
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D41209
---
 sys/netinet/ip_mroute.c | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
index ac5e1c5f09c6..124a8cd394cb 100644
--- a/sys/netinet/ip_mroute.c
+++ b/sys/netinet/ip_mroute.c
@@ -317,7 +317,7 @@ static void	bw_upcalls_send(void);
 static int	del_bw_upcall(struct bw_upcall *);
 static int	del_mfc(struct mfcctl2 *);
 static int	del_vif(vifi_t);
-static int	del_vif_locked(vifi_t, struct ifnet **);
+static int	del_vif_locked(vifi_t, struct ifnet **, struct ifnet **);
 static void	expire_bw_upcalls_send(void *);
 static void	expire_mfc(struct mfc *);
 static void	expire_upcalls(void *);
@@ -618,7 +618,7 @@ if_detached_event(void *arg __unused, struct ifnet *ifp)
 {
 	vifi_t vifi;
 	u_long i, vifi_cnt = 0;
-	struct ifnet *free_ptr;
+	struct ifnet *free_ptr, *multi_leave;
 
 	MRW_WLOCK();
 
@@ -635,6 +635,7 @@ if_detached_event(void *arg __unused, struct ifnet *ifp)
 	 * 3. Expire any matching multicast forwarding cache entries.
 	 * 4. Free vif state. This should disable ALLMULTI on the interface.
 	 */
+restart:
 	for (vifi = 0; vifi < V_numvifs; vifi++) {
 		if (V_viftable[vifi].v_ifp != ifp)
 			continue;
@@ -647,9 +648,15 @@ if_detached_event(void *arg __unused, struct ifnet *ifp)
 				}
 			}
 		}
-		del_vif_locked(vifi, &free_ptr);
+		del_vif_locked(vifi, &multi_leave, &free_ptr);
 		if (free_ptr != NULL)
 			vifi_cnt++;
+		if (multi_leave) {
+			MRW_WUNLOCK();
+			if_allmulti(multi_leave, 0);
+			MRW_WLOCK();
+			goto restart;
+		}
 	}
 
 	MRW_WUNLOCK();
@@ -998,11 +1005,12 @@ add_vif(struct vifctl *vifcp)
  * Delete a vif from the vif table
  */
 static int
-del_vif_locked(vifi_t vifi, struct ifnet **ifp_free)
+del_vif_locked(vifi_t vifi, struct ifnet **ifp_multi_leave, struct ifnet **ifp_free)
 {
 	struct vif *vifp;
 
 	*ifp_free = NULL;
+	*ifp_multi_leave = NULL;
 
 	MRW_WLOCK_ASSERT();
 
@@ -1015,7 +1023,7 @@ del_vif_locked(vifi_t vifi, struct ifnet **ifp_free)
 	}
 
 	if (!(vifp->v_flags & (VIFF_TUNNEL | VIFF_REGISTER)))
-		if_allmulti(vifp->v_ifp, 0);
+		*ifp_multi_leave = vifp->v_ifp;
 
 	if (vifp->v_flags & VIFF_REGISTER) {
 		V_reg_vif_num = VIFI_INVALID;
@@ -1045,14 +1053,17 @@ static int
 del_vif(vifi_t vifi)
 {
 	int cc;
-	struct ifnet *free_ptr;
+	struct ifnet *free_ptr, *multi_leave;
 
 	MRW_WLOCK();
-	cc = del_vif_locked(vifi, &free_ptr);
+	cc = del_vif_locked(vifi, &multi_leave, &free_ptr);
 	MRW_WUNLOCK();
 
-	if (free_ptr)
+	if (multi_leave)
+		if_allmulti(multi_leave, 0);
+	if (free_ptr) {
 		if_free(free_ptr);
+	}
 
 	return cc;
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202307280944.36S9iXnJ029493>