Download raw body.
tcp(4): use per-sockbuf mutex to protect `so_snd' socket buffer
tcp(4): use per-sockbuf mutex to protect `so_snd' socket buffer
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 */
tcp(4): use per-sockbuf mutex to protect `so_snd' socket buffer
tcp(4): use per-sockbuf mutex to protect `so_snd' socket buffer