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

Reply via email to