git: 8f5a0a2e4f31 - main - sockets: provide solisten_clone(), solisten_enqueue()

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Wed, 10 Aug 2022 18:10:27 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=8f5a0a2e4f315a03caaa2d51efdcf837e303e510

commit 8f5a0a2e4f315a03caaa2d51efdcf837e303e510
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2022-08-10 18:09:34 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2022-08-10 18:09:34 +0000

    sockets: provide solisten_clone(), solisten_enqueue()
    
    as alternative KPI to sonewconn().  The latter has three stages:
    - check the listening socket queue limits
    - allocate a new socket
    - call into protocol attach method
    - link the new socket into the listen queue of the listening socket
    
    The attach method, originally designed for a creation of socket by the
    socket(2) syscall has slightly different semantics than attach of a socket
    cloned by listener.  Make it possible for protocols to call into the
    first stage, then perform a different attach, and then call into the
    final stage.  The first stage, that checks limits and clones a socket
    is called solisten_clone(), and the function that enqueues the socket
    is solisten_enqueue().
    
    Reviewed by:            tuexen
    Differential revision:  https://reviews.freebsd.org/D36063
---
 sys/kern/uipc_socket.c | 86 +++++++++++++++++++++++++++++++++++---------------
 sys/sys/socketvar.h    |  3 ++
 2 files changed, 64 insertions(+), 25 deletions(-)

diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 8ecf83d30e28..73725efceb2d 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -587,16 +587,17 @@ SYSCTL_TIMEVAL_SEC(_kern_ipc, OID_AUTO, sooverinterval, CTLFLAG_RW,
     "Delay in seconds between warnings for listen socket overflows");
 
 /*
- * When an attempt at a new connection is noted on a socket which accepts
- * connections, sonewconn is called.  If the connection is possible (subject
- * to space constraints, etc.) then we allocate a new structure, properly
- * linked into the data structure of the original socket, and return this.
- * Connstatus may be 0, or SS_ISCONFIRMING, or SS_ISCONNECTED.
+ * When an attempt at a new connection is noted on a socket which supports
+ * accept(2), the protocol has two options:
+ * 1) Call legacy sonewconn() function, which would call protocol attach
+ *    method, same as used for socket(2).
+ * 2) Call solisten_clone(), do attach that is specific to a cloned connection,
+ *    and then call solisten_enqueue().
  *
  * Note: the ref count on the socket is 0 on return.
  */
 struct socket *
-sonewconn(struct socket *head, int connstatus)
+solisten_clone(struct socket *head)
 {
 	struct sbuf descrsb;
 	struct socket *so;
@@ -701,20 +702,27 @@ sonewconn(struct socket *head, int connstatus)
 			}
 			KASSERT(sbuf_len(&descrsb) > 0,
 			    ("%s: sbuf creation failed", __func__));
+			/*
+			 * Preserve the historic listen queue overflow log
+			 * message, that starts with "sonewconn:".  It has
+			 * been known to sysadmins for years and also test
+			 * sys/kern/sonewconn_overflow checks for it.
+			 */
 			if (head->so_cred == 0) {
-				log(LOG_DEBUG,
-			    	"%s: pcb %p (%s): Listen queue overflow: "
-			    	"%i already in queue awaiting acceptance "
-			    	"(%d occurrences)\n",
-			    	__func__, head->so_pcb, sbuf_data(&descrsb),
+				log(LOG_DEBUG, "sonewconn: pcb %p (%s): "
+				    "Listen queue overflow: %i already in "
+				    "queue awaiting acceptance (%d "
+				    "occurrences)\n", head->so_pcb,
+				    sbuf_data(&descrsb),
 			    	qlen, overcount);
 			} else {
-				log(LOG_DEBUG, "%s: pcb %p (%s): Listen queue overflow: "
+				log(LOG_DEBUG, "sonewconn: pcb %p (%s): "
+				    "Listen queue overflow: "
 				    "%i already in queue awaiting acceptance "
 				    "(%d occurrences), euid %d, rgid %d, jail %s\n",
-				    __func__, head->so_pcb, sbuf_data(&descrsb),
-				    qlen, overcount,
-				    head->so_cred->cr_uid, head->so_cred->cr_rgid,
+				    head->so_pcb, sbuf_data(&descrsb), qlen,
+				    overcount, head->so_cred->cr_uid,
+				    head->so_cred->cr_rgid,
 				    head->so_cred->cr_prison ?
 					head->so_cred->cr_prison->pr_name :
 					"not_jailed");
@@ -758,25 +766,52 @@ sonewconn(struct socket *head, int connstatus)
 		    __func__, head->so_pcb);
 		return (NULL);
 	}
+	so->so_rcv.sb_lowat = head->sol_sbrcv_lowat;
+	so->so_snd.sb_lowat = head->sol_sbsnd_lowat;
+	so->so_rcv.sb_timeo = head->sol_sbrcv_timeo;
+	so->so_snd.sb_timeo = head->sol_sbsnd_timeo;
+	so->so_rcv.sb_flags = head->sol_sbrcv_flags & SB_AUTOSIZE;
+	so->so_snd.sb_flags = head->sol_sbsnd_flags & SB_AUTOSIZE;
 	if ((so->so_proto->pr_flags & PR_SOCKBUF) == 0) {
 		so->so_snd.sb_mtx = &so->so_snd_mtx;
 		so->so_rcv.sb_mtx = &so->so_rcv_mtx;
 	}
-	/* Socket starts with a reference from the listen queue. */
-	refcount_init(&so->so_count, 1);
+
+	return (so);
+}
+
+/* Connstatus may be 0, or SS_ISCONFIRMING, or SS_ISCONNECTED. */
+struct socket *
+sonewconn(struct socket *head, int connstatus)
+{
+	struct socket *so;
+
+	if ((so = solisten_clone(head)) == NULL)
+		return (NULL);
+
 	if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0, NULL)) {
-		MPASS(refcount_release(&so->so_count));
 		sodealloc(so);
 		log(LOG_DEBUG, "%s: pcb %p: pru_attach() failed\n",
 		    __func__, head->so_pcb);
 		return (NULL);
 	}
-	so->so_rcv.sb_lowat = head->sol_sbrcv_lowat;
-	so->so_snd.sb_lowat = head->sol_sbsnd_lowat;
-	so->so_rcv.sb_timeo = head->sol_sbrcv_timeo;
-	so->so_snd.sb_timeo = head->sol_sbsnd_timeo;
-	so->so_rcv.sb_flags |= head->sol_sbrcv_flags & SB_AUTOSIZE;
-	so->so_snd.sb_flags |= head->sol_sbsnd_flags & SB_AUTOSIZE;
+
+	solisten_enqueue(so, connstatus);
+
+	return (so);
+}
+
+/*
+ * Enqueue socket cloned by solisten_clone() to the listen queue of the
+ * listener it has been cloned from.
+ */
+void
+solisten_enqueue(struct socket *so, int connstatus)
+{
+	struct socket *head = so->so_listen;
+
+	MPASS(refcount_load(&so->so_count) == 0);
+	refcount_init(&so->so_count, 1);
 
 	SOLISTEN_LOCK(head);
 	if (head->sol_accept_filter != NULL)
@@ -815,13 +850,14 @@ sonewconn(struct socket *head, int connstatus)
 		head->sol_incqlen++;
 		SOLISTEN_UNLOCK(head);
 	}
-	return (so);
 }
 
 #if defined(SCTP) || defined(SCTP_SUPPORT)
 /*
  * Socket part of sctp_peeloff().  Detach a new socket from an
  * association.  The new socket is returned with a reference.
+ *
+ * XXXGL: reduce copy-paste with solisten_clone().
  */
 struct socket *
 sopeeloff(struct socket *head)
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
index c496239f92bc..ed797c6d6239 100644
--- a/sys/sys/socketvar.h
+++ b/sys/sys/socketvar.h
@@ -474,7 +474,10 @@ int	solisten(struct socket *so, int backlog, struct thread *td);
 void	solisten_proto(struct socket *so, int backlog);
 void	solisten_proto_abort(struct socket *so);
 int	solisten_proto_check(struct socket *so);
+void	solisten_enqueue(struct socket *, int);
 int	solisten_dequeue(struct socket *, struct socket **, int);
+struct socket *
+	solisten_clone(struct socket *);
 struct socket *
 	sonewconn(struct socket *head, int connstatus);
 struct socket *