Index | Thread | Search

From:
Vitaliy Makkoveev <mvs@openbsd.org>
Subject:
Re: Please test: shared solock for all intet sockets within knote(9) routines
To:
Alexander Bluhm <alexander.bluhm@gmx.net>
Cc:
tech@openbsd.org
Date:
Thu, 1 Feb 2024 01:07:14 +0300

Download raw body.

Thread
On Wed, Jan 31, 2024 at 10:25:47PM +0100, Alexander Bluhm wrote:
> On Tue, Jan 30, 2024 at 05:39:43PM +0300, Vitaliy Makkoveev wrote:
> > Please test it, but keep in mind, soassertlocked_readonly() could be
> > required in some more places.
> 
> I panics on i386 during regress.  From the timestamps of the logs
> I think it is one of these.  Not that they run on two machines and
> the remote one crashes.
> 
> -rw-r--r--  1 root     wsrc          4863 Jan 31 13:06 sys/kern/sosplice/error/make.log
> -rw-r--r--  1 root     wsrc          4510 Jan 31 13:07 sys/kern/sosplice/loop/make.log
> -rw-r--r--  1 root     wsrc          8872 Jan 31 13:08 sys/kern/sosplice/perf/make.log
> -rw-r--r--  1 root     wsrc          1490 Jan 31 14:08 sys/kern/sosplice/scapy/make.log
> -rw-r--r--  1 root     wsrc         40256 Jan 31 14:09 sys/kern/sosplice/tcp/make.log
> -rw-r--r--  1 root     wsrc          9162 Jan 31 14:10 sys/kern/sosplice/udp/make.log
> 
> [-- MARK -- Wed Jan 31 13:05:00 2024]
> panic: mutex 0xd7dfd9a0 not held in klist_rcv_soassertlk
> Stopped at      db_enter+0x4:   popl    %ebp
>     TID    PID    UID     PRFLAGS     PFLAGS  CPU  COMMAND
> * 70542  31548      0     0x14000      0x200    1  softnet0
> db_enter() at db_enter+0x4
> panic(d0cbc0e4) at panic+0x7a
> klist_rcv_soassertlk(d7dfd92c) at klist_rcv_soassertlk+0x3b
> knote_locked(d7dfd9f0,0) at knote_locked+0x1a
> sohasoutofband(d7dfd92c) at sohasoutofband+0x25
> tcp_input(f5802ea4,f5802ea0,6,2) at tcp_input+0x290d
> ip_deliver(f5802ea4,f5802ea0,6,2) at ip_deliver+0xf4
> ipintr() at ipintr+0x52
> if_netisr(0) at if_netisr+0xd2
> taskq_thread(d82f5000) at taskq_thread+0x84
> https://www.openbsd.org/ddb.html describes the minimum info required in bug
> reports.  Insufficient info makes it difficult to find and fix bugs.
> ddb{1}> [-- MARK -- Wed Jan 31 13:10:00 2024]

Sorry, I forgot to change knote_locked() to knote() within
sohasoutofband(). This diff fixes it. Also sblock() fix included.

Since the `sb_klist' uses `sb_mtx' mutex(9) for protection, special
`socket_klistops' is not required anymore and was removed.

Index: sys/kern/uipc_socket.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.315
diff -u -p -r1.315 uipc_socket.c
--- sys/kern/uipc_socket.c	26 Jan 2024 18:24:23 -0000	1.315
+++ sys/kern/uipc_socket.c	31 Jan 2024 22:06:44 -0000
@@ -72,26 +72,20 @@ int	filt_soread(struct knote *kn, long h
 void	filt_sowdetach(struct knote *kn);
 int	filt_sowrite(struct knote *kn, long hint);
 int	filt_soexcept(struct knote *kn, long hint);
-int	filt_solisten(struct knote *kn, long hint);
-int	filt_somodify(struct kevent *kev, struct knote *kn);
-int	filt_soprocess(struct knote *kn, struct kevent *kev);
 
-const struct filterops solisten_filtops = {
-	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
-	.f_attach	= NULL,
-	.f_detach	= filt_sordetach,
-	.f_event	= filt_solisten,
-	.f_modify	= filt_somodify,
-	.f_process	= filt_soprocess,
-};
+int	filt_snd_somodify(struct kevent *kev, struct knote *kn);
+int	filt_snd_soprocess(struct knote *kn, struct kevent *kev);
+
+int	filt_rcv_somodify(struct kevent *kev, struct knote *kn);
+int	filt_rcv_soprocess(struct knote *kn, struct kevent *kev);
 
 const struct filterops soread_filtops = {
 	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
 	.f_attach	= NULL,
 	.f_detach	= filt_sordetach,
 	.f_event	= filt_soread,
-	.f_modify	= filt_somodify,
-	.f_process	= filt_soprocess,
+	.f_modify	= filt_rcv_somodify,
+	.f_process	= filt_rcv_soprocess,
 };
 
 const struct filterops sowrite_filtops = {
@@ -99,8 +93,8 @@ const struct filterops sowrite_filtops =
 	.f_attach	= NULL,
 	.f_detach	= filt_sowdetach,
 	.f_event	= filt_sowrite,
-	.f_modify	= filt_somodify,
-	.f_process	= filt_soprocess,
+	.f_modify	= filt_snd_somodify,
+	.f_process	= filt_snd_soprocess,
 };
 
 const struct filterops soexcept_filtops = {
@@ -108,18 +102,8 @@ const struct filterops soexcept_filtops 
 	.f_attach	= NULL,
 	.f_detach	= filt_sordetach,
 	.f_event	= filt_soexcept,
-	.f_modify	= filt_somodify,
-	.f_process	= filt_soprocess,
-};
-
-void	klist_soassertlk(void *);
-int	klist_solock(void *);
-void	klist_sounlock(void *, int);
-
-const struct klistops socket_klistops = {
-	.klo_assertlk	= klist_soassertlk,
-	.klo_lock	= klist_solock,
-	.klo_unlock	= klist_sounlock,
+	.f_modify	= filt_rcv_somodify,
+	.f_process	= filt_rcv_soprocess,
 };
 
 #ifndef SOMINCONN
@@ -158,8 +142,10 @@ soalloc(const struct domain *dp, int wai
 		return (NULL);
 	rw_init_flags(&so->so_lock, dp->dom_name, RWL_DUPOK);
 	refcnt_init(&so->so_refcnt);
-	klist_init(&so->so_rcv.sb_klist, &socket_klistops, so);
-	klist_init(&so->so_snd.sb_klist, &socket_klistops, so);
+	mtx_init(&so->so_rcv.sb_mtx, IPL_MPFLOOR);
+	mtx_init(&so->so_snd.sb_mtx, IPL_MPFLOOR);
+	klist_init_mutex(&so->so_rcv.sb_klist, &so->so_rcv.sb_mtx);
+	klist_init_mutex(&so->so_snd.sb_klist, &so->so_snd.sb_mtx);
 	sigio_init(&so->so_sigio);
 	TAILQ_INIT(&so->so_q0);
 	TAILQ_INIT(&so->so_q);
@@ -1757,7 +1743,7 @@ somove(struct socket *so, int wait)
 void
 sorwakeup(struct socket *so)
 {
-	soassertlocked(so);
+	soassertlocked_readonly(so);
 
 #ifdef SOCKET_SPLICE
 	if (so->so_rcv.sb_flags & SB_SPLICE) {
@@ -1785,7 +1771,7 @@ sorwakeup(struct socket *so)
 void
 sowwakeup(struct socket *so)
 {
-	soassertlocked(so);
+	soassertlocked_readonly(so);
 
 #ifdef SOCKET_SPLICE
 	if (so->so_snd.sb_flags & SB_SPLICE)
@@ -2134,7 +2120,46 @@ void
 sohasoutofband(struct socket *so)
 {
 	pgsigio(&so->so_sigio, SIGURG, 0);
-	knote_locked(&so->so_rcv.sb_klist, 0);
+	knote(&so->so_rcv.sb_klist, 0);
+}
+
+void
+sofilt_lock(struct socket *so, struct sockbuf *sb)
+{
+	switch (so->so_proto->pr_domain->dom_family) {
+	case PF_INET:
+	case PF_INET6:
+		NET_LOCK_SHARED();
+		break;
+	default:
+		rw_enter_write(&so->so_lock);
+		break;
+	}
+
+	mtx_enter(&sb->sb_mtx);
+}
+
+void
+sofilt_unlock(struct socket *so, struct sockbuf *sb)
+{
+	mtx_leave(&sb->sb_mtx);
+
+	switch (so->so_proto->pr_domain->dom_family) {
+	case PF_INET:
+	case PF_INET6:
+		NET_UNLOCK_SHARED();
+		break;
+	default:
+		rw_exit_write(&so->so_lock);
+		break;
+	}
+}
+
+static inline void
+sofilt_assert_locked(struct socket *so, struct sockbuf *sb)
+{
+	MUTEX_ASSERT_LOCKED(&sb->sb_mtx);
+	soassertlocked_readonly(so);
 }
 
 int
@@ -2143,13 +2168,9 @@ soo_kqfilter(struct file *fp, struct kno
 	struct socket *so = kn->kn_fp->f_data;
 	struct sockbuf *sb;
 
-	solock(so);
 	switch (kn->kn_filter) {
 	case EVFILT_READ:
-		if (so->so_options & SO_ACCEPTCONN)
-			kn->kn_fop = &solisten_filtops;
-		else
-			kn->kn_fop = &soread_filtops;
+		kn->kn_fop = &soread_filtops;
 		sb = &so->so_rcv;
 		break;
 	case EVFILT_WRITE:
@@ -2161,12 +2182,12 @@ soo_kqfilter(struct file *fp, struct kno
 		sb = &so->so_rcv;
 		break;
 	default:
-		sounlock(so);
 		return (EINVAL);
 	}
 
+	mtx_enter(&sb->sb_mtx);
 	klist_insert_locked(&sb->sb_klist, kn);
-	sounlock(so);
+	mtx_leave(&sb->sb_mtx);
 
 	return (0);
 }
@@ -2185,7 +2206,23 @@ filt_soread(struct knote *kn, long hint)
 	struct socket *so = kn->kn_fp->f_data;
 	int rv = 0;
 
-	soassertlocked(so);
+	sofilt_assert_locked(so, &so->so_rcv);
+
+	if (so->so_options & SO_ACCEPTCONN) {
+		kn->kn_data = so->so_qlen;
+		rv = (kn->kn_data != 0);
+
+		if (kn->kn_flags & (__EV_POLL | __EV_SELECT)) {
+			if (so->so_state & SS_ISDISCONNECTED) {
+				kn->kn_flags |= __EV_HUP;
+				rv = 1;
+			} else {
+				rv = soreadable(so);
+			}
+		}
+
+		return rv;
+	}
 
 	kn->kn_data = so->so_rcv.sb_cc;
 #ifdef SOCKET_SPLICE
@@ -2226,7 +2263,7 @@ filt_sowrite(struct knote *kn, long hint
 	struct socket *so = kn->kn_fp->f_data;
 	int rv;
 
-	soassertlocked(so);
+	sofilt_assert_locked(so, &so->so_snd);
 
 	kn->kn_data = sbspace(so, &so->so_snd);
 	if (so->so_snd.sb_state & SS_CANTSENDMORE) {
@@ -2257,7 +2294,7 @@ filt_soexcept(struct knote *kn, long hin
 	struct socket *so = kn->kn_fp->f_data;
 	int rv = 0;
 
-	soassertlocked(so);
+	sofilt_assert_locked(so, &so->so_rcv);
 
 #ifdef SOCKET_SPLICE
 	if (isspliced(so)) {
@@ -2283,77 +2320,55 @@ filt_soexcept(struct knote *kn, long hin
 }
 
 int
-filt_solisten(struct knote *kn, long hint)
+filt_snd_somodify(struct kevent *kev, struct knote *kn)
 {
 	struct socket *so = kn->kn_fp->f_data;
-	int active;
-
-	soassertlocked(so);
-
-	kn->kn_data = so->so_qlen;
-	active = (kn->kn_data != 0);
+	int rv;
 
-	if (kn->kn_flags & (__EV_POLL | __EV_SELECT)) {
-		if (so->so_state & SS_ISDISCONNECTED) {
-			kn->kn_flags |= __EV_HUP;
-			active = 1;
-		} else {
-			active = soreadable(so);
-		}
-	}
+	sofilt_lock(so, &so->so_snd);
+	rv = knote_modify(kev, kn);
+	sofilt_unlock(so, &so->so_snd);
 
-	return (active);
+	return (rv);
 }
 
 int
-filt_somodify(struct kevent *kev, struct knote *kn)
+filt_snd_soprocess(struct knote *kn, struct kevent *kev)
 {
 	struct socket *so = kn->kn_fp->f_data;
 	int rv;
 
-	solock(so);
-	rv = knote_modify(kev, kn);
-	sounlock(so);
+	sofilt_lock(so, &so->so_snd);
+	rv = knote_process(kn, kev);
+	sofilt_unlock(so, &so->so_snd);
 
 	return (rv);
 }
 
 int
-filt_soprocess(struct knote *kn, struct kevent *kev)
+filt_rcv_somodify(struct kevent *kev, struct knote *kn)
 {
 	struct socket *so = kn->kn_fp->f_data;
 	int rv;
 
-	solock(so);
-	rv = knote_process(kn, kev);
-	sounlock(so);
+	sofilt_lock(so, &so->so_rcv);
+	rv = knote_modify(kev, kn);
+	sofilt_unlock(so, &so->so_rcv);
 
 	return (rv);
 }
 
-void
-klist_soassertlk(void *arg)
-{
-	struct socket *so = arg;
-
-	soassertlocked(so);
-}
-
 int
-klist_solock(void *arg)
+filt_rcv_soprocess(struct knote *kn, struct kevent *kev)
 {
-	struct socket *so = arg;
-
-	solock(so);
-	return (1);
-}
+	struct socket *so = kn->kn_fp->f_data;
+	int rv;
 
-void
-klist_sounlock(void *arg, int ls)
-{
-	struct socket *so = arg;
+	sofilt_lock(so, &so->so_rcv);
+	rv = knote_process(kn, kev);
+	sofilt_unlock(so, &so->so_rcv);
 
-	sounlock(so);
+	return (rv);
 }
 
 #ifdef DDB
Index: sys/kern/uipc_socket2.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_socket2.c,v
retrieving revision 1.140
diff -u -p -r1.140 uipc_socket2.c
--- sys/kern/uipc_socket2.c	11 Jan 2024 14:15:11 -0000	1.140
+++ sys/kern/uipc_socket2.c	31 Jan 2024 22:06:44 -0000
@@ -439,7 +439,7 @@ sounlock_shared(struct socket *so)
 }
 
 void
-soassertlocked(struct socket *so)
+soassertlocked_readonly(struct socket *so)
 {
 	switch (so->so_proto->pr_domain->dom_family) {
 	case PF_INET:
@@ -452,6 +452,27 @@ soassertlocked(struct socket *so)
 	}
 }
 
+void
+soassertlocked(struct socket *so)
+{
+	switch (so->so_proto->pr_domain->dom_family) {
+	case PF_INET:
+	case PF_INET6:
+		if (rw_status(&netlock) == RW_READ) {
+			NET_ASSERT_LOCKED();
+
+			if (splassert_ctl > 0 && pru_locked(so) == 0 &&
+			    rw_status(&so->so_lock) != RW_WRITE)
+				splassert_fail(0, RW_WRITE, __func__);
+		} else
+			NET_ASSERT_LOCKED_EXCLUSIVE();
+		break;
+	default:
+		rw_assert_wrlock(&so->so_lock);
+		break;
+	}
+}
+
 int
 sosleep_nsec(struct socket *so, void *ident, int prio, const char *wmesg,
     uint64_t nsecs)
@@ -489,46 +510,62 @@ sbwait(struct socket *so, struct sockbuf
 
 	soassertlocked(so);
 
+	mtx_enter(&sb->sb_mtx);
 	sb->sb_flags |= SB_WAIT;
+	mtx_leave(&sb->sb_mtx);
+
 	return sosleep_nsec(so, &sb->sb_cc, prio, "netio", sb->sb_timeo_nsecs);
 }
 
 int
 sblock(struct socket *so, struct sockbuf *sb, int flags)
 {
-	int error, prio = PSOCK;
+	int error = 0, prio = PSOCK;
 
 	soassertlocked(so);
 
+	mtx_enter(&sb->sb_mtx);
 	if ((sb->sb_flags & SB_LOCK) == 0) {
 		sb->sb_flags |= SB_LOCK;
-		return (0);
+		goto out;
+	}
+	if ((flags & SBL_WAIT) == 0) {
+		error = EWOULDBLOCK;
+		goto out;
 	}
-	if ((flags & SBL_WAIT) == 0)
-		return (EWOULDBLOCK);
 	if (!(flags & SBL_NOINTR || sb->sb_flags & SB_NOINTR))
 		prio |= PCATCH;
 
 	while (sb->sb_flags & SB_LOCK) {
 		sb->sb_flags |= SB_WANT;
+		mtx_leave(&sb->sb_mtx);
 		error = sosleep_nsec(so, &sb->sb_flags, prio, "netlck", INFSLP);
 		if (error)
 			return (error);
+		mtx_enter(&sb->sb_mtx);
 	}
 	sb->sb_flags |= SB_LOCK;
-	return (0);
+out:
+	mtx_leave(&sb->sb_mtx);
+
+	return (error);
 }
 
 void
 sbunlock(struct socket *so, struct sockbuf *sb)
 {
-	soassertlocked(so);
+	int dowakeup = 0;
 
+	mtx_enter(&sb->sb_mtx);
 	sb->sb_flags &= ~SB_LOCK;
 	if (sb->sb_flags & SB_WANT) {
 		sb->sb_flags &= ~SB_WANT;
-		wakeup(&sb->sb_flags);
+		dowakeup = 1;
 	}
+	mtx_leave(&sb->sb_mtx);
+
+	if (dowakeup)
+		wakeup(&sb->sb_flags);
 }
 
 /*
@@ -539,15 +576,24 @@ sbunlock(struct socket *so, struct sockb
 void
 sowakeup(struct socket *so, struct sockbuf *sb)
 {
-	soassertlocked(so);
+	int dowakeup = 0, dopgsigio = 0;
 
+	mtx_enter(&sb->sb_mtx);
 	if (sb->sb_flags & SB_WAIT) {
 		sb->sb_flags &= ~SB_WAIT;
-		wakeup(&sb->sb_cc);
+		dowakeup = 1;
 	}
 	if (sb->sb_flags & SB_ASYNC)
-		pgsigio(&so->so_sigio, SIGIO, 0);
+		dopgsigio = 1;
+
 	knote_locked(&sb->sb_klist, 0);
+	mtx_leave(&sb->sb_mtx);
+
+	if (dowakeup)
+		wakeup(&sb->sb_cc);
+
+	if (dopgsigio)
+		pgsigio(&so->so_sigio, SIGIO, 0);
 }
 
 /*
Index: sys/kern/uipc_syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.216
diff -u -p -r1.216 uipc_syscalls.c
--- sys/kern/uipc_syscalls.c	3 Jan 2024 11:07:04 -0000	1.216
+++ sys/kern/uipc_syscalls.c	31 Jan 2024 22:06:44 -0000
@@ -326,7 +326,7 @@ doaccept(struct proc *p, int sock, struc
 	    : (flags & SOCK_NONBLOCK ? FNONBLOCK : 0);
 
 	/* connection has been removed from the listen queue */
-	knote_locked(&head->so_rcv.sb_klist, 0);
+	knote(&head->so_rcv.sb_klist, 0);
 
 	if (persocket)
 		sounlock(head);
Index: sys/netinet/ip_divert.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_divert.c,v
retrieving revision 1.92
diff -u -p -r1.92 ip_divert.c
--- sys/netinet/ip_divert.c	16 Sep 2023 09:33:27 -0000	1.92
+++ sys/netinet/ip_divert.c	31 Jan 2024 22:06:44 -0000
@@ -67,6 +67,7 @@ const struct pr_usrreqs divert_usrreqs =
 	.pru_detach	= divert_detach,
 	.pru_lock	= divert_lock,
 	.pru_unlock	= divert_unlock,
+	.pru_locked	= divert_locked,
 	.pru_bind	= divert_bind,
 	.pru_shutdown	= divert_shutdown,
 	.pru_send	= divert_send,
@@ -311,6 +312,14 @@ divert_unlock(struct socket *so)
 
 	NET_ASSERT_LOCKED();
 	mtx_leave(&inp->inp_mtx);
+}
+
+int
+divert_locked(struct socket *so)
+{
+	struct inpcb *inp = sotoinpcb(so);
+
+	return mtx_owned(&inp->inp_mtx);
 }
 
 int
Index: sys/netinet/ip_divert.h
===================================================================
RCS file: /cvs/src/sys/netinet/ip_divert.h,v
retrieving revision 1.24
diff -u -p -r1.24 ip_divert.h
--- sys/netinet/ip_divert.h	17 Oct 2022 14:49:02 -0000	1.24
+++ sys/netinet/ip_divert.h	31 Jan 2024 22:06:44 -0000
@@ -74,6 +74,7 @@ int	 divert_attach(struct socket *, int,
 int	 divert_detach(struct socket *);
 void	 divert_lock(struct socket *);
 void	 divert_unlock(struct socket *);
+int	 divert_locked(struct socket *);
 int	 divert_bind(struct socket *, struct mbuf *, struct proc *);
 int	 divert_shutdown(struct socket *);
 int	 divert_send(struct socket *, struct mbuf *, struct mbuf *,
Index: sys/netinet/ip_var.h
===================================================================
RCS file: /cvs/src/sys/netinet/ip_var.h,v
retrieving revision 1.110
diff -u -p -r1.110 ip_var.h
--- sys/netinet/ip_var.h	26 Nov 2023 22:08:10 -0000	1.110
+++ sys/netinet/ip_var.h	31 Jan 2024 22:06:44 -0000
@@ -260,6 +260,7 @@ int	 rip_attach(struct socket *, int, in
 int	 rip_detach(struct socket *);
 void	 rip_lock(struct socket *);
 void	 rip_unlock(struct socket *);
+int	 rip_locked(struct socket *);
 int	 rip_bind(struct socket *, struct mbuf *, struct proc *);
 int	 rip_connect(struct socket *, struct mbuf *);
 int	 rip_disconnect(struct socket *);
Index: sys/netinet/raw_ip.c
===================================================================
RCS file: /cvs/src/sys/netinet/raw_ip.c,v
retrieving revision 1.154
diff -u -p -r1.154 raw_ip.c
--- sys/netinet/raw_ip.c	21 Jan 2024 01:17:20 -0000	1.154
+++ sys/netinet/raw_ip.c	31 Jan 2024 22:06:44 -0000
@@ -108,6 +108,7 @@ const struct pr_usrreqs rip_usrreqs = {
 	.pru_detach	= rip_detach,
 	.pru_lock	= rip_lock,
 	.pru_unlock	= rip_unlock,
+	.pru_locked	= rip_locked,
 	.pru_bind	= rip_bind,
 	.pru_connect	= rip_connect,
 	.pru_disconnect	= rip_disconnect,
@@ -522,6 +523,14 @@ rip_unlock(struct socket *so)
 
 	NET_ASSERT_LOCKED();
 	mtx_leave(&inp->inp_mtx);
+}
+
+int
+rip_locked(struct socket *so)
+{
+	struct inpcb *inp = sotoinpcb(so);
+
+	return mtx_owned(&inp->inp_mtx);
 }
 
 int
Index: sys/netinet/udp_usrreq.c
===================================================================
RCS file: /cvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.316
diff -u -p -r1.316 udp_usrreq.c
--- sys/netinet/udp_usrreq.c	28 Jan 2024 20:34:25 -0000	1.316
+++ sys/netinet/udp_usrreq.c	31 Jan 2024 22:06:44 -0000
@@ -127,6 +127,7 @@ const struct pr_usrreqs udp_usrreqs = {
 	.pru_detach	= udp_detach,
 	.pru_lock	= udp_lock,
 	.pru_unlock	= udp_unlock,
+	.pru_locked	= udp_locked,
 	.pru_bind	= udp_bind,
 	.pru_connect	= udp_connect,
 	.pru_disconnect	= udp_disconnect,
@@ -143,6 +144,7 @@ const struct pr_usrreqs udp6_usrreqs = {
 	.pru_detach	= udp_detach,
 	.pru_lock	= udp_lock,
 	.pru_unlock	= udp_unlock,
+	.pru_locked	= udp_locked,
 	.pru_bind	= udp_bind,
 	.pru_connect	= udp_connect,
 	.pru_disconnect	= udp_disconnect,
@@ -1154,6 +1156,14 @@ udp_unlock(struct socket *so)
 
 	NET_ASSERT_LOCKED();
 	mtx_leave(&inp->inp_mtx);
+}
+
+int
+udp_locked(struct socket *so)
+{
+	struct inpcb *inp = sotoinpcb(so);
+
+	return mtx_owned(&inp->inp_mtx);
 }
 
 int
Index: sys/netinet/udp_var.h
===================================================================
RCS file: /cvs/src/sys/netinet/udp_var.h,v
retrieving revision 1.50
diff -u -p -r1.50 udp_var.h
--- sys/netinet/udp_var.h	10 Jan 2024 16:44:30 -0000	1.50
+++ sys/netinet/udp_var.h	31 Jan 2024 22:06:44 -0000
@@ -147,6 +147,7 @@ int	 udp_attach(struct socket *, int, in
 int	 udp_detach(struct socket *);
 void	 udp_lock(struct socket *);
 void	 udp_unlock(struct socket *);
+int	 udp_locked(struct socket *);
 int	 udp_bind(struct socket *, struct mbuf *, struct proc *);
 int	 udp_connect(struct socket *, struct mbuf *);
 int	 udp_disconnect(struct socket *);
Index: sys/netinet6/ip6_mroute.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_mroute.c,v
retrieving revision 1.138
diff -u -p -r1.138 ip6_mroute.c
--- sys/netinet6/ip6_mroute.c	6 Dec 2023 09:27:17 -0000	1.138
+++ sys/netinet6/ip6_mroute.c	31 Jan 2024 22:06:44 -0000
@@ -861,12 +861,12 @@ socket6_send(struct socket *so, struct m
 
 		mtx_enter(&inp->inp_mtx);
 		ret = sbappendaddr(so, &so->so_rcv, sin6tosa(src), mm, NULL);
+		if (ret != 0)
+			sorwakeup(so);
 		mtx_leave(&inp->inp_mtx);
 
-		if (ret != 0) {
-			sorwakeup(so);
+		if (ret != 0)
 			return 0;
-		}
 	}
 	m_freem(mm);
 	return -1;
Index: sys/netinet6/ip6_var.h
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_var.h,v
retrieving revision 1.109
diff -u -p -r1.109 ip6_var.h
--- sys/netinet6/ip6_var.h	3 Dec 2023 20:36:24 -0000	1.109
+++ sys/netinet6/ip6_var.h	31 Jan 2024 22:06:44 -0000
@@ -353,6 +353,7 @@ int	rip6_attach(struct socket *, int, in
 int	rip6_detach(struct socket *);
 void	rip6_lock(struct socket *);
 void	rip6_unlock(struct socket *);
+int	rip6_locked(struct socket *);
 int	rip6_bind(struct socket *, struct mbuf *, struct proc *);
 int	rip6_connect(struct socket *, struct mbuf *);
 int	rip6_disconnect(struct socket *);
Index: sys/netinet6/raw_ip6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/raw_ip6.c,v
retrieving revision 1.179
diff -u -p -r1.179 raw_ip6.c
--- sys/netinet6/raw_ip6.c	21 Jan 2024 01:17:20 -0000	1.179
+++ sys/netinet6/raw_ip6.c	31 Jan 2024 22:06:44 -0000
@@ -110,6 +110,7 @@ const struct pr_usrreqs rip6_usrreqs = {
 	.pru_detach	= rip6_detach,
 	.pru_lock	= rip6_lock,
 	.pru_unlock	= rip6_unlock,
+	.pru_locked	= rip6_locked,
 	.pru_bind	= rip6_bind,
 	.pru_connect	= rip6_connect,
 	.pru_disconnect	= rip6_disconnect,
@@ -651,6 +652,14 @@ rip6_unlock(struct socket *so)
 
 	NET_ASSERT_LOCKED();
 	mtx_leave(&inp->inp_mtx);
+}
+
+int
+rip6_locked(struct socket *so)
+{
+	struct inpcb *inp = sotoinpcb(so);
+
+	return mtx_owned(&inp->inp_mtx);
 }
 
 int
Index: sys/sys/mutex.h
===================================================================
RCS file: /cvs/src/sys/sys/mutex.h,v
retrieving revision 1.19
diff -u -p -r1.19 mutex.h
--- sys/sys/mutex.h	1 Dec 2023 14:37:22 -0000	1.19
+++ sys/sys/mutex.h	31 Jan 2024 22:06:44 -0000
@@ -127,6 +127,9 @@ void	mtx_leave(struct mutex *);
 
 #define mtx_init(m, ipl)	mtx_init_flags(m, ipl, NULL, 0)
 
+#define mtx_owned(mtx) \
+	(((mtx)->mtx_owner == curcpu()) || panicstr || db_active)
+
 #ifdef WITNESS
 
 void	_mtx_init_flags(struct mutex *, int, const char *, int,
Index: sys/sys/protosw.h
===================================================================
RCS file: /cvs/src/sys/sys/protosw.h,v
retrieving revision 1.64
diff -u -p -r1.64 protosw.h
--- sys/sys/protosw.h	11 Jan 2024 14:15:12 -0000	1.64
+++ sys/sys/protosw.h	31 Jan 2024 22:06:44 -0000
@@ -69,6 +69,7 @@ struct pr_usrreqs {
 	int	(*pru_detach)(struct socket *);
 	void	(*pru_lock)(struct socket *);
 	void	(*pru_unlock)(struct socket *);
+	int	(*pru_locked)(struct socket *so);
 	int	(*pru_bind)(struct socket *, struct mbuf *, struct proc *);
 	int	(*pru_listen)(struct socket *);
 	int	(*pru_connect)(struct socket *, struct mbuf *);
@@ -294,6 +295,14 @@ pru_unlock(struct socket *so)
 {
 	if (so->so_proto->pr_usrreqs->pru_unlock)
 		(*so->so_proto->pr_usrreqs->pru_unlock)(so);
+}
+
+static inline int
+pru_locked(struct socket *so)
+{
+	if (so->so_proto->pr_usrreqs->pru_locked)
+		return (*so->so_proto->pr_usrreqs->pru_locked)(so);
+	return (0);
 }
 
 static inline int
Index: sys/sys/socketvar.h
===================================================================
RCS file: /cvs/src/sys/sys/socketvar.h,v
retrieving revision 1.121
diff -u -p -r1.121 socketvar.h
--- sys/sys/socketvar.h	11 Jan 2024 14:15:12 -0000	1.121
+++ sys/sys/socketvar.h	31 Jan 2024 22:06:44 -0000
@@ -40,6 +40,7 @@
 #include <sys/sigio.h>				/* for struct sigio_ref */
 #include <sys/task.h>
 #include <sys/timeout.h>
+#include <sys/mutex.h>
 #include <sys/rwlock.h>
 #include <sys/refcnt.h>
 
@@ -105,6 +106,7 @@ struct socket {
  * Variables for socket buffering.
  */
 	struct	sockbuf {
+		struct mutex sb_mtx;
 /* The following fields are all zeroed on flush. */
 #define	sb_startzero	sb_cc
 		u_long	sb_cc;		/* actual chars in buffer */
@@ -174,6 +176,7 @@ struct socket {
 #include <lib/libkern/libkern.h>
 
 void	soassertlocked(struct socket *);
+void	soassertlocked_readonly(struct socket *);
 
 static inline void
 soref(struct socket *so)
@@ -200,9 +203,16 @@ sorele(struct socket *so)
 static inline int
 sb_notify(struct socket *so, struct sockbuf *sb)
 {
+	int rv;
+
 	soassertlocked(so);
-	return ((sb->sb_flags & (SB_WAIT|SB_ASYNC|SB_SPLICE)) != 0 ||
+
+	mtx_enter(&sb->sb_mtx);
+	rv = ((sb->sb_flags & (SB_WAIT|SB_ASYNC|SB_SPLICE)) != 0 ||
 	    !klist_empty(&sb->sb_klist));
+	mtx_leave(&sb->sb_mtx);
+
+	return rv;
 }
 
 /*
@@ -211,10 +221,12 @@ sb_notify(struct socket *so, struct sock
  * still be negative (cc > hiwat or mbcnt > mbmax).  Should detect
  * overflow and return 0.
  */
+
 static inline long
 sbspace(struct socket *so, struct sockbuf *sb)
 {
-	soassertlocked(so);
+	soassertlocked_readonly(so);
+
 	return lmin(sb->sb_hiwat - sb->sb_cc, sb->sb_mbmax - sb->sb_mbcnt);
 }
 
@@ -230,7 +242,7 @@ sbspace(struct socket *so, struct sockbu
 static inline int
 soreadable(struct socket *so)
 {
-	soassertlocked(so);
+	soassertlocked_readonly(so);
 	if (isspliced(so))
 		return 0;
 	return (so->so_rcv.sb_state & SS_CANTRCVMORE) || so->so_qlen ||
@@ -241,7 +253,7 @@ soreadable(struct socket *so)
 static inline int
 sowriteable(struct socket *so)
 {
-	soassertlocked(so);
+	soassertlocked_readonly(so);
 	return ((sbspace(so, &so->so_snd) >= so->so_snd.sb_lowat &&
 	    ((so->so_state & SS_ISCONNECTED) ||
 	    (so->so_proto->pr_flags & PR_CONNREQUIRED)==0)) ||