It makes sense to push solock() down to sosetopt() too. For a half cases (*pr_ctloutput)() is just null op, so there is no reason to call it. Also, a lot of things could be done without solock() held.
Index: sys/kern/uipc_socket.c =================================================================== RCS file: /cvs/src/sys/kern/uipc_socket.c,v retrieving revision 1.299 diff -u -p -r1.299 uipc_socket.c --- sys/kern/uipc_socket.c 27 Jan 2023 21:01:59 -0000 1.299 +++ sys/kern/uipc_socket.c 30 Jan 2023 14:02:29 -0000 @@ -1777,63 +1777,25 @@ sosetopt(struct socket *so, int level, i { int error = 0; - soassertlocked(so); - if (level != SOL_SOCKET) { if (so->so_proto->pr_ctloutput) { + solock(so); error = (*so->so_proto->pr_ctloutput)(PRCO_SETOPT, so, level, optname, m); + sounlock(so); return (error); - } - error = ENOPROTOOPT; + } else + return (ENOPROTOOPT); } else { switch (optname) { - case SO_BINDANY: - if ((error = suser(curproc)) != 0) /* XXX */ - return (error); - break; - } - - switch (optname) { - - case SO_LINGER: - if (m == NULL || m->m_len != sizeof (struct linger) || - mtod(m, struct linger *)->l_linger < 0 || - mtod(m, struct linger *)->l_linger > SHRT_MAX) - return (EINVAL); - so->so_linger = mtod(m, struct linger *)->l_linger; - /* FALLTHROUGH */ - - case SO_BINDANY: - case SO_DEBUG: - case SO_KEEPALIVE: - case SO_USELOOPBACK: - case SO_BROADCAST: - case SO_REUSEADDR: - case SO_REUSEPORT: - case SO_OOBINLINE: - case SO_TIMESTAMP: - case SO_ZEROIZE: - if (m == NULL || m->m_len < sizeof (int)) - return (EINVAL); - if (*mtod(m, int *)) - so->so_options |= optname; - else - so->so_options &= ~optname; - break; - - case SO_DONTROUTE: - if (m == NULL || m->m_len < sizeof (int)) - return (EINVAL); - if (*mtod(m, int *)) - error = EOPNOTSUPP; - break; - case SO_SNDBUF: case SO_RCVBUF: case SO_SNDLOWAT: case SO_RCVLOWAT: { + struct sockbuf *sb = ( + optname == (SO_SNDBUF | SO_SNDLOWAT) ? + &so->so_snd : &so->so_rcv); u_long cnt; if (m == NULL || m->m_len < sizeof (int)) @@ -1841,43 +1803,42 @@ sosetopt(struct socket *so, int level, i cnt = *mtod(m, int *); if ((long)cnt <= 0) cnt = 1; - switch (optname) { - case SO_SNDBUF: - if (so->so_snd.sb_state & SS_CANTSENDMORE) - return (EINVAL); - if (sbcheckreserve(cnt, so->so_snd.sb_wat) || - sbreserve(so, &so->so_snd, cnt)) - return (ENOBUFS); - so->so_snd.sb_wat = cnt; - break; + solock(so); + switch (optname) { + case SO_SNDBUF: case SO_RCVBUF: - if (so->so_rcv.sb_state & SS_CANTRCVMORE) - return (EINVAL); - if (sbcheckreserve(cnt, so->so_rcv.sb_wat) || - sbreserve(so, &so->so_rcv, cnt)) - return (ENOBUFS); - so->so_rcv.sb_wat = cnt; + if (sb->sb_state & + (SS_CANTSENDMORE | SS_CANTRCVMORE)) { + error = EINVAL; + break; + } + if (sbcheckreserve(cnt, sb->sb_wat) || + sbreserve(so, sb, cnt)) { + error = ENOBUFS; + break; + } + sb->sb_wat = cnt; break; - case SO_SNDLOWAT: - so->so_snd.sb_lowat = - (cnt > so->so_snd.sb_hiwat) ? - so->so_snd.sb_hiwat : cnt; - break; case SO_RCVLOWAT: - so->so_rcv.sb_lowat = - (cnt > so->so_rcv.sb_hiwat) ? - so->so_rcv.sb_hiwat : cnt; + case SO_SNDLOWAT: + sb->sb_lowat = (cnt > sb->sb_hiwat) ? + sb->sb_hiwat : cnt; break; } - break; + + sounlock(so); + + return (error); } case SO_SNDTIMEO: case SO_RCVTIMEO: { + struct sockbuf *sb = (optname == SO_SNDTIMEO ? + &so->so_snd : &so->so_rcv); struct timeval tv; uint64_t nsecs; @@ -1891,16 +1852,12 @@ sosetopt(struct socket *so, int level, i return (EDOM); if (nsecs == 0) nsecs = INFSLP; - switch (optname) { - case SO_SNDTIMEO: - so->so_snd.sb_timeo_nsecs = nsecs; - break; - case SO_RCVTIMEO: - so->so_rcv.sb_timeo_nsecs = nsecs; - break; - } - break; + solock(so); + sb->sb_timeo_nsecs = nsecs; + sounlock(so); + + return (0); } case SO_RTABLE: @@ -1911,18 +1868,30 @@ sosetopt(struct socket *so, int level, i so->so_proto->pr_domain; level = dom->dom_protosw->pr_protocol; + + solock(so); error = (*so->so_proto->pr_ctloutput) (PRCO_SETOPT, so, level, optname, m); + sounlock(so); + return (error); } - error = ENOPROTOOPT; - break; + return (ENOPROTOOPT); + + case SO_DONTROUTE: + if (m == NULL || m->m_len < sizeof (int)) + return (EINVAL); + if (*mtod(m, int *)) + return (EOPNOTSUPP); + return (0); #ifdef SOCKET_SPLICE case SO_SPLICE: + solock(so); if (m == NULL) { error = sosplice(so, -1, 0, NULL); } else if (m->m_len < sizeof(int)) { + sounlock(so); return (EINVAL); } else if (m->m_len < sizeof(struct splice)) { error = sosplice(so, *mtod(m, int *), 0, NULL); @@ -1932,9 +1901,52 @@ sosetopt(struct socket *so, int level, i mtod(m, struct splice *)->sp_max, &mtod(m, struct splice *)->sp_idle); } - break; + sounlock(so); + + return (error); #endif /* SOCKET_SPLICE */ + } + + switch (optname) { + case SO_BINDANY: + if ((error = suser(curproc)) != 0) /* XXX */ + return (error); + break; + } + + solock(so); + + switch (optname) { + case SO_LINGER: + if (m == NULL || m->m_len != sizeof (struct linger) || + mtod(m, struct linger *)->l_linger < 0 || + mtod(m, struct linger *)->l_linger > SHRT_MAX) { + error = EINVAL; + break; + } + so->so_linger = mtod(m, struct linger *)->l_linger; + /* FALLTHROUGH */ + case SO_BINDANY: + case SO_DEBUG: + case SO_KEEPALIVE: + case SO_USELOOPBACK: + case SO_BROADCAST: + case SO_REUSEADDR: + case SO_REUSEPORT: + case SO_OOBINLINE: + case SO_TIMESTAMP: + case SO_ZEROIZE: + if (m == NULL || m->m_len < sizeof (int)) { + error = EINVAL; + break; + } + if (*mtod(m, int *)) + so->so_options |= optname; + else + so->so_options &= ~optname; + break; + default: error = ENOPROTOOPT; break; @@ -1943,6 +1955,8 @@ sosetopt(struct socket *so, int level, i (*so->so_proto->pr_ctloutput)(PRCO_SETOPT, so, level, optname, m); } + + sounlock(so); } return (error); Index: sys/kern/uipc_syscalls.c =================================================================== RCS file: /cvs/src/sys/kern/uipc_syscalls.c,v retrieving revision 1.211 diff -u -p -r1.211 uipc_syscalls.c --- sys/kern/uipc_syscalls.c 27 Jan 2023 21:01:59 -0000 1.211 +++ sys/kern/uipc_syscalls.c 30 Jan 2023 14:02:29 -0000 @@ -1232,9 +1232,7 @@ sys_setsockopt(struct proc *p, void *v, m->m_len = SCARG(uap, valsize); } so = fp->f_data; - solock(so); error = sosetopt(so, SCARG(uap, level), SCARG(uap, name), m); - sounlock(so); bad: m_freem(m); FRELE(fp, p);