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);