Make sonewconn() overflow messages have per-socket rate-limits and values.
sonewconn() emits debug-level messages when a listen socket's queue overflows. Currently, sonewconn() tracks overflows on a global basis. It will only log one message every 60 seconds, regardless of how many sockets experience overflows. And, when it next logs at the end of the 60 seconds, it records a single message referencing a single PCB with the total number of overflows across all sockets. This commit changes to per-socket overflow tracking. The code will now log one message every 60 seconds per socket. And, the code will provide per-socket queue length and overflow counts. It also provides a way to change the period between log messages using a sysctl. Reviewed by: jhb (previous version), bcr (manpages) MFC after: 2 weeks Sponsored by: Netflix, Inc. Differential Revision: https://reviews.freebsd.org/D24316
This commit is contained in:
parent
f6ab9795d4
commit
fb401f1bba
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=359923
@ -28,7 +28,7 @@
|
|||||||
.\" From: @(#)listen.2 8.2 (Berkeley) 12/11/93
|
.\" From: @(#)listen.2 8.2 (Berkeley) 12/11/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd August 18, 2016
|
.Dd April 14, 2020
|
||||||
.Dt LISTEN 2
|
.Dt LISTEN 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -110,6 +110,13 @@ or less than zero is specified,
|
|||||||
.Fa backlog
|
.Fa backlog
|
||||||
is silently forced to
|
is silently forced to
|
||||||
.Va kern.ipc.soacceptqueue .
|
.Va kern.ipc.soacceptqueue .
|
||||||
|
.Pp
|
||||||
|
If the listen queue overflows, the kernel will emit a LOG_DEBUG syslog message.
|
||||||
|
The
|
||||||
|
.Xr sysctl 3
|
||||||
|
MIB variable
|
||||||
|
.Va kern.ipc.sooverinterval
|
||||||
|
specifies a per-socket limit on how often the kernel will emit these messages.
|
||||||
.Sh INTERACTION WITH ACCEPT FILTERS
|
.Sh INTERACTION WITH ACCEPT FILTERS
|
||||||
When accept filtering is used on a socket, a second queue will
|
When accept filtering is used on a socket, a second queue will
|
||||||
be used to hold sockets that have connected, but have not yet
|
be used to hold sockets that have connected, but have not yet
|
||||||
|
@ -575,6 +575,11 @@ SYSCTL_INT(_regression, OID_AUTO, sonewconn_earlytest, CTLFLAG_RW,
|
|||||||
®ression_sonewconn_earlytest, 0, "Perform early sonewconn limit test");
|
®ression_sonewconn_earlytest, 0, "Perform early sonewconn limit test");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static struct timeval overinterval = { 60, 0 };
|
||||||
|
SYSCTL_TIMEVAL_SEC(_kern_ipc, OID_AUTO, sooverinterval, CTLFLAG_RW,
|
||||||
|
&overinterval,
|
||||||
|
"Delay in seconds between warnings for listen socket overflows");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When an attempt at a new connection is noted on a socket which accepts
|
* When an attempt at a new connection is noted on a socket which accepts
|
||||||
* connections, sonewconn is called. If the connection is possible (subject
|
* connections, sonewconn is called. If the connection is possible (subject
|
||||||
@ -587,14 +592,10 @@ SYSCTL_INT(_regression, OID_AUTO, sonewconn_earlytest, CTLFLAG_RW,
|
|||||||
struct socket *
|
struct socket *
|
||||||
sonewconn(struct socket *head, int connstatus)
|
sonewconn(struct socket *head, int connstatus)
|
||||||
{
|
{
|
||||||
static struct timeval lastover;
|
|
||||||
static struct timeval overinterval = { 60, 0 };
|
|
||||||
static int overcount;
|
|
||||||
|
|
||||||
struct sbuf descrsb;
|
struct sbuf descrsb;
|
||||||
struct socket *so;
|
struct socket *so;
|
||||||
u_int over;
|
int len, overcount;
|
||||||
int len;
|
u_int qlen;
|
||||||
const char localprefix[] = "local:";
|
const char localprefix[] = "local:";
|
||||||
char descrbuf[SUNPATHLEN + sizeof(localprefix)];
|
char descrbuf[SUNPATHLEN + sizeof(localprefix)];
|
||||||
#if defined(INET6)
|
#if defined(INET6)
|
||||||
@ -602,18 +603,31 @@ sonewconn(struct socket *head, int connstatus)
|
|||||||
#elif defined(INET)
|
#elif defined(INET)
|
||||||
char addrbuf[INET_ADDRSTRLEN];
|
char addrbuf[INET_ADDRSTRLEN];
|
||||||
#endif
|
#endif
|
||||||
|
bool dolog, over;
|
||||||
|
|
||||||
SOLISTEN_LOCK(head);
|
SOLISTEN_LOCK(head);
|
||||||
over = (head->sol_qlen > 3 * head->sol_qlimit / 2);
|
over = (head->sol_qlen > 3 * head->sol_qlimit / 2);
|
||||||
SOLISTEN_UNLOCK(head);
|
|
||||||
#ifdef REGRESSION
|
#ifdef REGRESSION
|
||||||
if (regression_sonewconn_earlytest && over) {
|
if (regression_sonewconn_earlytest && over) {
|
||||||
#else
|
#else
|
||||||
if (over) {
|
if (over) {
|
||||||
#endif
|
#endif
|
||||||
overcount++;
|
head->sol_overcount++;
|
||||||
|
dolog = !!ratecheck(&head->sol_lastover, &overinterval);
|
||||||
|
|
||||||
if (ratecheck(&lastover, &overinterval)) {
|
/*
|
||||||
|
* If we're going to log, copy the overflow count and queue
|
||||||
|
* length from the listen socket before dropping the lock.
|
||||||
|
* Also, reset the overflow count.
|
||||||
|
*/
|
||||||
|
if (dolog) {
|
||||||
|
overcount = head->sol_overcount;
|
||||||
|
head->sol_overcount = 0;
|
||||||
|
qlen = head->sol_qlen;
|
||||||
|
}
|
||||||
|
SOLISTEN_UNLOCK(head);
|
||||||
|
|
||||||
|
if (dolog) {
|
||||||
/*
|
/*
|
||||||
* Try to print something descriptive about the
|
* Try to print something descriptive about the
|
||||||
* socket for the error message.
|
* socket for the error message.
|
||||||
@ -686,7 +700,7 @@ sonewconn(struct socket *head, int connstatus)
|
|||||||
"%i already in queue awaiting acceptance "
|
"%i already in queue awaiting acceptance "
|
||||||
"(%d occurrences)\n",
|
"(%d occurrences)\n",
|
||||||
__func__, head->so_pcb, sbuf_data(&descrsb),
|
__func__, head->so_pcb, sbuf_data(&descrsb),
|
||||||
head->sol_qlen, overcount);
|
qlen, overcount);
|
||||||
sbuf_delete(&descrsb);
|
sbuf_delete(&descrsb);
|
||||||
|
|
||||||
overcount = 0;
|
overcount = 0;
|
||||||
@ -694,6 +708,7 @@ sonewconn(struct socket *head, int connstatus)
|
|||||||
|
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
SOLISTEN_UNLOCK(head);
|
||||||
VNET_ASSERT(head->so_vnet != NULL, ("%s: so %p vnet is NULL",
|
VNET_ASSERT(head->so_vnet != NULL, ("%s: so %p vnet is NULL",
|
||||||
__func__, head));
|
__func__, head));
|
||||||
so = soalloc(head->so_vnet);
|
so = soalloc(head->so_vnet);
|
||||||
|
@ -172,6 +172,10 @@ struct socket {
|
|||||||
short sol_sbsnd_flags;
|
short sol_sbsnd_flags;
|
||||||
sbintime_t sol_sbrcv_timeo;
|
sbintime_t sol_sbrcv_timeo;
|
||||||
sbintime_t sol_sbsnd_timeo;
|
sbintime_t sol_sbsnd_timeo;
|
||||||
|
|
||||||
|
/* Information tracking listen queue overflows. */
|
||||||
|
struct timeval sol_lastover; /* (e) */
|
||||||
|
int sol_overcount; /* (e) */
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user