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
This commit is contained in:
Gleb Smirnoff 2022-08-10 11:09:34 -07:00
parent c7a62c925c
commit 8f5a0a2e4f
2 changed files with 64 additions and 25 deletions

View File

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

View File

@ -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 *