Index | Thread | Search

From:
Alexander Bluhm <bluhm@openbsd.org>
Subject:
Re: tcp(4): use per-sockbuf mutex to protect `so_snd' socket buffer
To:
Vitaliy Makkoveev <mvs@openbsd.org>
Cc:
tech@openbsd.org
Date:
Tue, 24 Dec 2024 17:49:32 +0100

Download raw body.

Thread
On Sat, Dec 21, 2024 at 08:16:05AM +0300, Vitaliy Makkoveev wrote:
> This diff only unlocks sosend() path, all the rest is still locked
> exclusively.
> 
> Even for tcp(4) case, sosend() only checks `so_snd' free space and
> sleeps if necessary, actual buffer handling happening within exclusively
> locked PCB layer. In the PCB layer we don't need to protect read-only
> access to `so_snd' because corresponding read-write access is serialized
> by socket lock.
> 
> sosend() needs to be serialized with somove(), so sotask() takes
> sblock() on `so_snd' of spliced socket. This discards previous
> protection of tcp(4) spliced sockets where solock() was used to prevent
> concurrent unsplicing. This scheme was used to avoid sleep in sofree().
> 
> However, sleep is sofree() is possible. We have two cases:
> 
> 1. Socket was not yet accept(2)ed. Such sockets can't be accessed from
> the userland and can't be spliced. sofree() could be called only from
> PCB layer and don't need to sleep.
> 
> 2. Socket was accepted. While called form PCB layer, sofree() ignores it
> because SS_NOFDREF bit is not set. Socket remains spliced, but without
> PCB. Sockets without PCB can't be accessed from PCB layer, only from
> userland. So, soclose()/sofree() thread needs to wait concurrent
> soclose()/sofree() thread for spliced socket as is is already done for
> udp(4) case. In such case it is safe to release solock() and sleep
> within sofree().
> 
> Note, I intentionally left all "if (dosolock)" dances as is.

While running your diff though my performnce tests, I got this panic
twice.  I have never seen it before, so I guess it is related.

panic: softclock: invalid to_clock: 19668320
Stopped at      db_enter+0x14:  popq    %rbp
    TID    PID    UID     PRFLAGS     PFLAGS  CPU  COMMAND
 120146  91986     89   0x1100012          0    1  relayd
  65124  13759     89   0x1100012          0    3  relayd
  33160  19438      0     0x14000      0x200    2  softnet0
db_enter() at db_enter+0x14
panic(ffffffff82883be3) at panic+0xdd
softclock(0) at softclock+0x1d5
softintr_dispatch(0) at softintr_dispatch+0xe6
Xsoftclock() at Xsoftclock+0x27
acpicpu_idle() at acpicpu_idle+0x2b9
sched_idle(ffffffff83593ff0) at sched_idle+0x298
end trace frame: 0x0, count: 8
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{0}> x/s version
version:        OpenBSD 7.6-current (GENERIC.MP) #cvs : D2024.12.23.00.00.00: Tue Dec 24 14:53:39 CET 2024
    root@ot15.obsd-lab.genua.de:/usr/src/sys/arch/amd64/compile/GENERIC.MP

ddb{0}> show panic
*cpu0: softclock: invalid to_clock: 19668320

ddb{0}> trace
db_enter() at db_enter+0x14
panic(ffffffff82883be3) at panic+0xdd
softclock(0) at softclock+0x1d5
softintr_dispatch(0) at softintr_dispatch+0xe6
Xsoftclock() at Xsoftclock+0x27
acpicpu_idle() at acpicpu_idle+0x2b9
sched_idle(ffffffff83593ff0) at sched_idle+0x298
end trace frame: 0x0, count: -7

ddb{0}> show register
rdi                              0xd
rsi                             0x10
rbp               0xffff80002f68d130
rbx               0xffff80000002d080
rdx               0x8000000000000000
rcx                            0x282
rax                             0x2d
r8                 0x101010101010101
r9                                 0
r10               0xadea166f5087a043
r11               0x2bfd2c462c554af2
r12               0xffffffff83594ba0    cpu_info_full_primary+0x2ba0
r13                              0x1
r14                                0
r15               0xffffffff82883be3    apollo_pio_rec+0x7109
rip               0xffffffff816bfef4    db_enter+0x14
cs                               0x8
rflags                         0x202
rsp               0xffff80002f68d130
ss                              0x10
db_enter+0x14:  popq    %rbp

ddb{0}> ps
   PID     TID   PPID    UID  S       FLAGS  WAIT          COMMAND
 73584  108417      0      0  2     0x14200                sosplice
  7477  251218  60716      0  3    0x100082  kqread        tcpbench
 60716  124268  54242      0  3    0x10008a  sigsusp       ksh
 54242  337229  12679      0  3        0x98  kqread        sshd-session
 12679     720  33354      0  3        0x92  kqread        sshd-session
 66636   19868  86133      0  3    0x100082  kqread        tcpbench
 86133  472778  49433      0  3    0x10008a  sigsusp       ksh
 49433   13943  98462      0  3        0x98  kqread        sshd-session
 98462  302770  33354      0  3        0x92  kqread        sshd-session
 47843   33219      1      0  3    0x100083  ttyin         getty
 54288  103991      1      0  3    0x100098  kqread        cron
 81679  486005      1      0  3    0x100080  kqread        iperf3
 11419  283134      1      0  3    0x100080  kqread        iperf3
 98512  409047      1     99  3   0x1100090  kqread        sndiod
 69524  267896      1    110  3    0x100090  kqread        sndiod
  8245  321396  63281     95  3   0x1100092  kqread        smtpd
 83176  145209  63281    103  3   0x1100092  kqread        smtpd
 90889  188254  63281     95  3   0x1100092  kqread        smtpd
 34640  473980  63281     95  3    0x100092  kqread        smtpd
 92195   94566  63281     95  3   0x1100092  kqread        smtpd
  9893  144694  63281     95  3   0x1100092  kqread        smtpd
 63281  283730      1      0  3    0x100080  kqread        smtpd
 50606  379758  94394     89  3   0x1100092  kqread        relayd
 98300  523357  94394     89  3   0x1100092  kqread        relayd
 74167  429861  94394     89  3   0x1100092  kqread        relayd
 91986  120146  94394     89  7   0x1100012                relayd
 13759   65124  94394     89  7   0x1100012                relayd
 15622  250572  94394     89  3   0x1100092  kqread        relayd
 65837  358426  94394     89  3   0x1100092  kqread        relayd
 45864   99476  94394     89  2   0x1100092                relayd
 94394   25533      1      0  3        0x80  kqread        relayd
 52723  463271  21242     91  3        0x92  kqread        snmpd_metrics
 28635  373814  21242     91  3   0x1100092  kqread        snmpd
 21242  215707      1      0  3    0x100080  kqread        snmpd
 33354  216155      1      0  3        0x88  kqread        sshd
  5174  452768      0      0  3     0x14280  nfsidl        nfsio
 61188  392826      0      0  3     0x14280  nfsidl        nfsio
 19241   42949      0      0  3     0x14280  nfsidl        nfsio
 29000  218874      0      0  3     0x14280  nfsidl        nfsio
 84747  311957      1      0  3    0x100080  kqread        ntpd
 46183  214005  80830     83  3    0x100092  kqread        ntpd
 80830   54208      1     83  3   0x1100092  kqread        ntpd
 98690  143438  50519     74  3   0x1100092  bpf           pflogd
 50519  519451      1      0  3        0x80  sbwait        pflogd
 97331  148497  57845     73  3   0x1100090  kqread        syslogd
 57845  372377      1      0  3    0x100082  sbwait        syslogd
 33406  307447      1      0  3    0x100080  kqread        resolvd
 28118  284489  39612     77  3    0x100092  kqread        dhcpleased
  2799  119056  39612     77  3    0x100092  kqread        dhcpleased
 39612   91167      1      0  3        0x80  kqread        dhcpleased
 25679   93938   3362    115  3    0x100092  kqread        slaacd
 76655  165419   3362    115  3    0x100092  kqread        slaacd
  3362  405831      1      0  3    0x100080  kqread        slaacd
 19284  370800      0      0  3     0x14200  bored         smr
 55324    8699      0      0  3     0x14200  pgzero        zerothread
 34421   61421      0      0  3     0x14200  aiodoned      aiodoned
 53137  331468      0      0  3     0x14200  syncer        update
 46196  161555      0      0  3     0x14200  cleaner       cleaner
 55676  492435      0      0  3     0x14200  reaper        reaper
 28210   68611      0      0  3     0x14200  pgdaemon      pagedaemon
  7723  258211      0      0  3     0x14200  usbtsk        usbtask
 63406  162199      0      0  3     0x14200  usbatsk       usbatsk
 67564  342517      0      0  3  0x40014200  acpi0         acpi0
 69404  487689      0      0  3  0x40014200                idle3
 98904  159131      0      0  3  0x40014200                idle2
 38382  270651      0      0  3  0x40014200                idle1
 46636  117171      0      0  3     0x14200  bored         sensors
 96859   56036      0      0  2     0x14200                softnet3
 35522  499886      0      0  3     0x14200  bored         softnet2
 59486  420481      0      0  2     0x14200                softnet1
 19438   33160      0      0  7     0x14200                softnet0
 55477  305831      0      0  3     0x14200  bored         systqmp
  7303  480675      0      0  3     0x14200  bored         systq
 59682   29868      0      0  3     0x14200  tmoslp        softclockmp
 27811  510536      0      0  3  0x40014200  tmoslp        softclock
*22881  483268      0      0  7  0x40014200                idle0
     1   58740      0      0  3        0x82  wait          init
     0       0     -1      0  3     0x10200  scheduler     swapper

ddb{1}> trace
x86_ipi_db(ffff80002f4dbff0) at x86_ipi_db+0x16
x86_ipi_handler() at x86_ipi_handler+0x80
Xresume_lapic_ipi() at Xresume_lapic_ipi+0x27
__mp_release_all(ffffffff837f8000) at __mp_release_all+0x38
msleep(fffffd818af558a8,fffffd818af558a8,318,ffffffff82928096,aff) at msleep+0x128
kqueue_sleep(fffffd818af558a8,ffff80002f814c68) at kqueue_sleep+0xc7
kqueue_scan(ffff80002f814b68,8,ffff80002f814a60,ffff80002f814c68,ffff80002f739c00,ffff80002f814cbc) at kqueue_scan+0x111
sys_kevent(ffff80002f739c00,ffff80002f814dd0,ffff80002f814d40) at sys_kevent+0x3b5
syscall(ffff80002f814dd0) at syscall+0x620
Xsyscall() at Xsyscall+0x128
end of kernel
end trace frame: 0x7e03cb938dd0, count: -10

ddb{2}> trace
x86_ipi_db(ffff80002f4e4ff0) at x86_ipi_db+0x16
x86_ipi_handler() at x86_ipi_handler+0x80
Xresume_lapic_ipi() at Xresume_lapic_ipi+0x27
random() at random+0x4b
clockrequest_advance_random(ffff80002f4e5da0,789680,3fffff) at clockrequest_advance_random+0x55
statclock(ffff80002f4e5da0,ffff80002f6ab1a0,0) at statclock+0x59
clockintr_dispatch(ffff80002f6ab1a0) at clockintr_dispatch+0x249
lapic_clockintr(0,0) at lapic_clockintr+0x43
Xresume_lapic_ltimer() at Xresume_lapic_ltimer+0x2a
msleep_nsec(ffff80000002f000,ffff80000002f018,10,ffff80002f6ab250,246) at msleep_nsec+0xa
taskq_next_work(ffff80000002f000,ffff80002f6ab2b0) at taskq_next_work+0x71
taskq_thread(ffff80000002f000) at taskq_thread+0x10b
end trace frame: 0x0, count: -12

ddb{3}> trace
x86_ipi_db(ffff80002f4edff0) at x86_ipi_db+0x16
x86_ipi_handler() at x86_ipi_handler+0x80
Xresume_lapic_ipi() at Xresume_lapic_ipi+0x27
schedclock(ffff80002f761458) at schedclock+0xce
statclock(ffff80002f4eeda0,ffff80002f80e510,0) at statclock+0x1b1
clockintr_dispatch(ffff80002f80e510) at clockintr_dispatch+0x249
lapic_clockintr(0,0) at lapic_clockintr+0x43
Xresume_lapic_ltimer() at Xresume_lapic_ltimer+0x2a
msleep_nsec(fffffd8190739da8,fffffd8190739da8,318,ffffffff82928096,68ca4f280) at msleep_nsec+0x8e
kqueue_sleep(fffffd8190739da8,ffff80002f80e968) at kqueue_sleep+0xc7
kqueue_scan(ffff80002f80e868,8,ffff80002f80e760,ffff80002f80e968,ffff80002f761458,ffff80002f80e9bc) at kqueue_scan+0x111
sys_kevent(ffff80002f761458,ffff80002f80ead0,ffff80002f80ea40) at sys_kevent+0x3b5
syscall(ffff80002f80ead0) at syscall+0x620
Xsyscall() at Xsyscall+0x128
end of kernel
end trace frame: 0x7518ca381aa0, count: -14

> Index: sys/kern/uipc_socket.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/uipc_socket.c,v
> diff -u -p -r1.347 uipc_socket.c
> --- sys/kern/uipc_socket.c	19 Dec 2024 22:11:35 -0000	1.347
> +++ sys/kern/uipc_socket.c	21 Dec 2024 04:12:02 -0000
> @@ -153,27 +153,8 @@ soalloc(const struct protosw *prp, int w
>  	TAILQ_INIT(&so->so_q0);
>  	TAILQ_INIT(&so->so_q);
>  
> -	switch (dp->dom_family) {
> -	case AF_INET:
> -	case AF_INET6:
> -		switch (prp->pr_type) {
> -		case SOCK_RAW:
> -		case SOCK_DGRAM:
> -			so->so_snd.sb_flags |= SB_MTXLOCK;
> -			/* FALLTHROUGH */
> -		case SOCK_STREAM:
> -			so->so_rcv.sb_flags |= SB_MTXLOCK;
> -			break;
> -		}
> -		break;
> -	case AF_KEY:
> -	case AF_ROUTE:
> -	case AF_UNIX:
> -	case AF_FRAME:
> -		so->so_snd.sb_flags |= SB_MTXLOCK;
> -		so->so_rcv.sb_flags |= SB_MTXLOCK;
> -		break;
> -	}
> +	so->so_snd.sb_flags |= SB_MTXLOCK;
> +	so->so_rcv.sb_flags |= SB_MTXLOCK;
>  
>  	return (so);
>  }
> @@ -327,17 +308,13 @@ sofree(struct socket *so, int keep_lock)
>  			sounlock(head);
>  	}
>  
> -	switch (so->so_proto->pr_domain->dom_family) {
> -	case AF_INET:
> -	case AF_INET6:
> -		if (so->so_proto->pr_type == SOCK_STREAM)
> -			break;
> -		/* FALLTHROUGH */
> -	default:
> +	if (!keep_lock) {
> +		/*
> +		 * sofree() was called from soclose(). Sleep is safe
> +		 * even for tcp(4) sockets.
> +		 */
>  		sounlock(so);
>  		refcnt_finalize(&so->so_refcnt, "sofinal");
> -		solock(so);
> -		break;
>  	}
>  
>  	sigio_free(&so->so_sigio);
> @@ -358,9 +335,6 @@ sofree(struct socket *so, int keep_lock)
>  		(*so->so_proto->pr_domain->dom_dispose)(so->so_rcv.sb_mb);
>  	m_purge(so->so_rcv.sb_mb);
>  
> -	if (!keep_lock)
> -		sounlock(so);
> -
>  #ifdef SOCKET_SPLICE
>  	if (so->so_sp) {
>  		/* Reuse splice idle, sounsplice() has been called before. */
> @@ -458,31 +432,6 @@ discard:
>  	if (so->so_sp) {
>  		struct socket *soback;
>  
> -		if (so->so_proto->pr_flags & PR_WANTRCVD) {
> -			/*
> -			 * Copy - Paste, but can't relock and sleep in
> -			 * sofree() in tcp(4) case. That's why tcp(4)
> -			 * still rely on solock() for splicing and
> -			 * unsplicing.
> -			 */
> -
> -			if (issplicedback(so)) {
> -				int freeing = SOSP_FREEING_WRITE;
> -
> -				if (so->so_sp->ssp_soback == so)
> -					freeing |= SOSP_FREEING_READ;
> -				sounsplice(so->so_sp->ssp_soback, so, freeing);
> -			}
> -			if (isspliced(so)) {
> -				int freeing = SOSP_FREEING_READ;
> -
> -				if (so == so->so_sp->ssp_socket)
> -					freeing |= SOSP_FREEING_WRITE;
> -				sounsplice(so, so->so_sp->ssp_socket, freeing);
> -			}
> -			goto free;
> -		}
> -
>  		sounlock(so);
>  		mtx_enter(&so->so_snd.sb_mtx);
>  		/*
> @@ -530,7 +479,6 @@ notsplicedback:
>  
>  		solock(so);
>  	}
> -free:
>  #endif /* SOCKET_SPLICE */
>  	/* sofree() calls sounlock(). */
>  	sofree(so, 0);
> @@ -1587,17 +1535,22 @@ sotask(void *arg)
>  	 */
>  
>  	sblock(&so->so_rcv, SBL_WAIT | SBL_NOINTR);
> -	if (sockstream)
> -		solock(so);
> -
>  	if (so->so_rcv.sb_flags & SB_SPLICE) {
> -		if (sockstream)
> +		struct socket *sosp = so->so_sp->ssp_socket;
> +
> +		if (sockstream) {
> +			sblock(&sosp->so_snd, SBL_WAIT | SBL_NOINTR);
> +			solock(so);
>  			doyield = 1;
> +		}
> +
>  		somove(so, M_DONTWAIT);
> -	}
>  
> -	if (sockstream)
> -		sounlock(so);
> +		if (sockstream) {
> +			sounlock(so);
> +			sbunlock(&sosp->so_snd);
> +		}
> +	}
>  	sbunlock(&so->so_rcv);
>  
>  	if (doyield) {
> Index: sys/netinet/tcp_input.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/tcp_input.c,v
> diff -u -p -r1.410 tcp_input.c
> --- sys/netinet/tcp_input.c	20 Dec 2024 19:20:34 -0000	1.410
> +++ sys/netinet/tcp_input.c	21 Dec 2024 04:12:02 -0000
> @@ -957,7 +957,10 @@ findpcb:
>  				    acked);
>  				tp->t_rcvacktime = now;
>  				ND6_HINT(tp);
> +
> +				mtx_enter(&so->so_snd.sb_mtx);
>  				sbdrop(so, &so->so_snd, acked);
> +				mtx_leave(&so->so_snd.sb_mtx);
>  
>  				/*
>  				 * If we had a pending ICMP message that
> @@ -1738,10 +1741,14 @@ trimthenstep6:
>  				tp->snd_wnd -= so->so_snd.sb_cc;
>  			else
>  				tp->snd_wnd = 0;
> +			mtx_enter(&so->so_snd.sb_mtx);
>  			sbdrop(so, &so->so_snd, (int)so->so_snd.sb_cc);
> +			mtx_leave(&so->so_snd.sb_mtx);
>  			ourfinisacked = 1;
>  		} else {
> +			mtx_enter(&so->so_snd.sb_mtx);
>  			sbdrop(so, &so->so_snd, acked);
> +			mtx_leave(&so->so_snd.sb_mtx);
>  			if (tp->snd_wnd > acked)
>  				tp->snd_wnd -= acked;
>  			else
> @@ -2999,6 +3006,7 @@ tcp_mss_update(struct tcpcb *tp)
>  	if (rt == NULL)
>  		return;
>  
> +	mtx_enter(&so->so_snd.sb_mtx);
>  	bufsize = so->so_snd.sb_hiwat;
>  	if (bufsize < mss) {
>  		mss = bufsize;
> @@ -3010,6 +3018,7 @@ tcp_mss_update(struct tcpcb *tp)
>  			bufsize = sb_max;
>  		(void)sbreserve(so, &so->so_snd, bufsize);
>  	}
> +	mtx_leave(&so->so_snd.sb_mtx);
>  
>  	mtx_enter(&so->so_rcv.sb_mtx);
>  	bufsize = so->so_rcv.sb_hiwat;
> Index: sys/netinet/tcp_output.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/tcp_output.c,v
> diff -u -p -r1.146 tcp_output.c
> --- sys/netinet/tcp_output.c	19 Dec 2024 22:11:35 -0000	1.146
> +++ sys/netinet/tcp_output.c	21 Dec 2024 04:12:02 -0000
> @@ -202,7 +202,7 @@ tcp_output(struct tcpcb *tp)
>  	u_int32_t optbuf[howmany(MAX_TCPOPTLEN, sizeof(u_int32_t))];
>  	u_char *opt = (u_char *)optbuf;
>  	unsigned int optlen, hdrlen, packetlen;
> -	int idle, sendalot = 0;
> +	int doing_sosend, idle, sendalot = 0;
>  	int i, sack_rxmit = 0;
>  	struct sackhole *p;
>  	uint64_t now;
> @@ -227,6 +227,10 @@ tcp_output(struct tcpcb *tp)
>  
>  	now = tcp_now();
>  
> +	mtx_enter(&so->so_snd.sb_mtx);
> +	doing_sosend=soissending(so);
> +	mtx_leave(&so->so_snd.sb_mtx);
> +
>  	/*
>  	 * Determine length of data that should be transmitted,
>  	 * and flags that will be used.
> @@ -243,7 +247,7 @@ tcp_output(struct tcpcb *tp)
>  		tp->snd_cwnd = 2 * tp->t_maxseg;
>  
>  	/* remember 'idle' for next invocation of tcp_output */
> -	if (idle && soissending(so)) {
> +	if (idle && doing_sosend) {
>  		tp->t_flags |= TF_LASTIDLE;
>  		idle = 0;
>  	} else
> @@ -392,7 +396,7 @@ again:
>  		if (len >= txmaxseg)
>  			goto send;
>  		if ((idle || (tp->t_flags & TF_NODELAY)) &&
> -		    len + off >= so->so_snd.sb_cc && !soissending(so) &&
> +		    len + off >= so->so_snd.sb_cc && !doing_sosend &&
>  		    (tp->t_flags & TF_NOPUSH) == 0)
>  			goto send;
>  		if (tp->t_force)
> @@ -725,7 +729,7 @@ send:
>  		 * give data to the user when a buffer fills or
>  		 * a PUSH comes in.)
>  		 */
> -		if (off + len == so->so_snd.sb_cc && !soissending(so))
> +		if (off + len == so->so_snd.sb_cc && !doing_sosend)
>  			flags |= TH_PUSH;
>  		tp->t_sndtime = now;
>  	} else {
> Index: sys/netinet/tcp_usrreq.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/tcp_usrreq.c,v
> diff -u -p -r1.233 tcp_usrreq.c
> --- sys/netinet/tcp_usrreq.c	19 Dec 2024 22:11:35 -0000	1.233
> +++ sys/netinet/tcp_usrreq.c	21 Dec 2024 04:12:02 -0000
> @@ -302,10 +302,12 @@ tcp_fill_info(struct tcpcb *tp, struct s
>  	ti->tcpi_so_rcv_sb_lowat = so->so_rcv.sb_lowat;
>  	ti->tcpi_so_rcv_sb_wat = so->so_rcv.sb_wat;
>  	mtx_leave(&so->so_rcv.sb_mtx);
> +	mtx_enter(&so->so_snd.sb_mtx);
>  	ti->tcpi_so_snd_sb_cc = so->so_snd.sb_cc;
>  	ti->tcpi_so_snd_sb_hiwat = so->so_snd.sb_hiwat;
>  	ti->tcpi_so_snd_sb_lowat = so->so_snd.sb_lowat;
>  	ti->tcpi_so_snd_sb_wat = so->so_snd.sb_wat;
> +	mtx_leave(&so->so_snd.sb_mtx);
>  
>  	return 0;
>  }
> @@ -842,7 +844,9 @@ tcp_send(struct socket *so, struct mbuf 
>  	if (so->so_options & SO_DEBUG)
>  		ostate = tp->t_state;
>  
> +	mtx_enter(&so->so_snd.sb_mtx);
>  	sbappendstream(so, &so->so_snd, m);
> +	mtx_leave(&so->so_snd.sb_mtx);
>  	m = NULL;
>  
>  	error = tcp_output(tp);
> @@ -895,7 +899,9 @@ tcp_sense(struct socket *so, struct stat
>  	if ((error = tcp_sogetpcb(so, &inp, &tp)))
>  		return (error);
>  
> +	mtx_enter(&so->so_snd.sb_mtx);
>  	ub->st_blksize = so->so_snd.sb_hiwat;
> +	mtx_leave(&so->so_snd.sb_mtx);
>  
>  	if (so->so_options & SO_DEBUG)
>  		tcp_trace(TA_USER, tp->t_state, tp, tp, NULL, PRU_SENSE, 0);
> @@ -970,7 +976,9 @@ tcp_sendoob(struct socket *so, struct mb
>  	 * of data past the urgent section.
>  	 * Otherwise, snd_up should be one lower.
>  	 */
> +	mtx_enter(&so->so_snd.sb_mtx);
>  	sbappendstream(so, &so->so_snd, m);
> +	mtx_leave(&so->so_snd.sb_mtx);
>  	m = NULL;
>  	tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
>  	tp->t_force = 1;
> @@ -1519,7 +1527,11 @@ void
>  tcp_update_sndspace(struct tcpcb *tp)
>  {
>  	struct socket *so = tp->t_inpcb->inp_socket;
> -	u_long nmax = so->so_snd.sb_hiwat;
> +	u_long nmax;
> +
> +	mtx_enter(&so->so_snd.sb_mtx);
> +
> +	nmax = so->so_snd.sb_hiwat;
>  
>  	if (sbchecklowmem()) {
>  		/* low on memory try to get rid of some */
> @@ -1535,7 +1547,7 @@ tcp_update_sndspace(struct tcpcb *tp)
>  	}
>  
>  	/* a writable socket must be preserved because of poll(2) semantics */
> -	if (sbspace(so, &so->so_snd) >= so->so_snd.sb_lowat) {
> +	if (sbspace_locked(so, &so->so_snd) >= so->so_snd.sb_lowat) {
>  		if (nmax < so->so_snd.sb_cc + so->so_snd.sb_lowat)
>  			nmax = so->so_snd.sb_cc + so->so_snd.sb_lowat;
>  		/* keep in sync with sbreserve() calculation */
> @@ -1548,6 +1560,8 @@ tcp_update_sndspace(struct tcpcb *tp)
>  
>  	if (nmax != so->so_snd.sb_hiwat)
>  		sbreserve(so, &so->so_snd, nmax);
> +
> +	mtx_leave(&so->so_snd.sb_mtx);
>  }
>  
>  /*
> @@ -1581,9 +1595,11 @@ tcp_update_rcvspace(struct tcpcb *tp)
>  	}
>  
>  	/* a readable socket must be preserved because of poll(2) semantics */
> +	mtx_enter(&so->so_snd.sb_mtx);
>  	if (so->so_rcv.sb_cc >= so->so_rcv.sb_lowat &&
>  	    nmax < so->so_snd.sb_lowat)
>  		nmax = so->so_snd.sb_lowat;
> +	mtx_leave(&so->so_snd.sb_mtx);
>  
>  	if (nmax != so->so_rcv.sb_hiwat) {
>  		/* round to MSS boundary */