Reap FIN_WAIT_2 connections marked SOCANTRCVMORE faster. This mitigate

potential issues where the peer does not close, potentially leaving
thousands of connections in FIN_WAIT_2. This is controlled by a new sysctl
fast_finwait2_recycle, which is disabled by default.

Reviewed by: gnn, silby.
This commit is contained in:
mohans 2007-02-26 22:25:21 +00:00
parent 2bd7382fdc
commit 384aeb29f6
9 changed files with 54 additions and 10 deletions

View File

@ -2301,8 +2301,12 @@ tcp_input(m, off0)
* compressed state.
*/
if (so->so_rcv.sb_state & SBS_CANTRCVMORE) {
int timeout;
soisdisconnected(so);
callout_reset(tp->tt_2msl, tcp_maxidle,
timeout = (tcp_fast_finwait2_recycle) ?
tcp_finwait2_timeout : tcp_maxidle;
callout_reset(tp->tt_2msl, timeout,
tcp_timer_2msl, tp);
}
tp->t_state = TCPS_FIN_WAIT_2;

View File

@ -2301,8 +2301,12 @@ tcp_input(m, off0)
* compressed state.
*/
if (so->so_rcv.sb_state & SBS_CANTRCVMORE) {
int timeout;
soisdisconnected(so);
callout_reset(tp->tt_2msl, tcp_maxidle,
timeout = (tcp_fast_finwait2_recycle) ?
tcp_finwait2_timeout : tcp_maxidle;
callout_reset(tp->tt_2msl, timeout,
tcp_timer_2msl, tp);
}
tp->t_state = TCPS_FIN_WAIT_2;

View File

@ -329,6 +329,7 @@ tcp_init(void)
tcp_rexmit_min = TCPTV_MIN;
tcp_rexmit_slop = TCPTV_CPU_VAR;
tcp_inflight_rttthresh = TCPTV_INFLIGHT_RTTTHRESH;
tcp_finwait2_timeout = TCPTV_FINWAIT2_TIMEOUT;
INP_INFO_LOCK_INIT(&tcbinfo, "tcp");
LIST_INIT(&tcb);

View File

@ -96,6 +96,15 @@ static int always_keepalive = 1;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, always_keepalive, CTLFLAG_RW,
&always_keepalive , 0, "Assume SO_KEEPALIVE on all TCP connections");
int tcp_fast_finwait2_recycle = 0;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, fast_finwait2_recycle, CTLFLAG_RW,
&tcp_fast_finwait2_recycle, 0, "Recycle closed FIN_WAIT_2 connections faster");
int tcp_finwait2_timeout;
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, finwait2_timeout, CTLTYPE_INT|CTLFLAG_RW,
&tcp_finwait2_timeout, 0, sysctl_msec_to_ticks, "I", "");
static int tcp_keepcnt = TCPTV_KEEPCNT;
/* max idle probes */
int tcp_maxpersistidle;
@ -211,13 +220,24 @@ tcp_timer_2msl(xtp)
* still waiting for peer to close and connection has been idle
* too long, or if 2MSL time is up from TIME_WAIT, delete connection
* control block. Otherwise, check again in a bit.
*
* If fastrecycle of FIN_WAIT_2, in FIN_WAIT_2 and receiver has closed,
* there's no point in hanging onto FIN_WAIT_2 socket. Just close it.
* Ignore fact that there were recent incoming segments.
*/
if (tp->t_state != TCPS_TIME_WAIT &&
(ticks - tp->t_rcvtime) <= tcp_maxidle)
callout_reset(tp->tt_2msl, tcp_keepintvl,
tcp_timer_2msl, tp);
else
tp = tcp_close(tp);
if (tcp_fast_finwait2_recycle && tp->t_state == TCPS_FIN_WAIT_2 &&
tp->t_inpcb && tp->t_inpcb->inp_socket &&
(tp->t_inpcb->inp_socket->so_rcv.sb_state & SBS_CANTRCVMORE)) {
tcpstat.tcps_finwait2_drops++;
tp = tcp_close(tp);
} else {
if (tp->t_state != TCPS_TIME_WAIT &&
(ticks - tp->t_rcvtime) <= tcp_maxidle)
callout_reset(tp->tt_2msl, tcp_keepintvl,
tcp_timer_2msl, tp);
else
tp = tcp_close(tp);
}
#ifdef TCPDEBUG
if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))

View File

@ -89,6 +89,8 @@
#define TCPTV_INFLIGHT_RTTTHRESH (10*hz/1000) /* below which inflight
disengages, in msec */
#define TCPTV_FINWAIT2_TIMEOUT (60*hz) /* FIN_WAIT_2 timeout if no receiver */
/*
* Minimum retransmit timer is 3 ticks, for algorithmic stability.
* TCPT_RANGESET() will add another TCPTV_CPU_VAR to deal with
@ -152,6 +154,9 @@ extern int tcp_backoff[];
struct tcptw;
extern int tcp_finwait2_timeout;
extern int tcp_fast_finwait2_recycle;
void tcp_timer_init(void);
void tcp_timer_2msl(void *xtp);
struct tcptw *

View File

@ -329,6 +329,7 @@ tcp_init(void)
tcp_rexmit_min = TCPTV_MIN;
tcp_rexmit_slop = TCPTV_CPU_VAR;
tcp_inflight_rttthresh = TCPTV_INFLIGHT_RTTTHRESH;
tcp_finwait2_timeout = TCPTV_FINWAIT2_TIMEOUT;
INP_INFO_LOCK_INIT(&tcbinfo, "tcp");
LIST_INIT(&tcb);

View File

@ -1581,9 +1581,14 @@ tcp_usrclosed(tp)
if (tp && tp->t_state >= TCPS_FIN_WAIT_2) {
soisdisconnected(tp->t_inpcb->inp_socket);
/* To prevent the connection hanging in FIN_WAIT_2 forever. */
if (tp->t_state == TCPS_FIN_WAIT_2)
callout_reset(tp->tt_2msl, tcp_maxidle,
if (tp->t_state == TCPS_FIN_WAIT_2) {
int timeout;
timeout = (tcp_fast_finwait2_recycle) ?
tcp_finwait2_timeout : tcp_maxidle;
callout_reset(tp->tt_2msl, timeout,
tcp_timer_2msl, tp);
}
}
}

View File

@ -413,6 +413,8 @@ struct tcpstat {
u_long tcps_hc_added; /* entry added to hostcache */
u_long tcps_hc_bucketoverflow; /* hostcache per bucket limit hit */
u_long tcps_finwait2_drops; /* Drop FIN_WAIT_2 connection after time limit */
/* SACK related stats */
u_long tcps_sack_recovery_episode; /* SACK recovery episodes */
u_long tcps_sack_rexmits; /* SACK rexmit segments */
@ -455,6 +457,7 @@ struct xtcpcb {
#define TCPCTL_SACK 14 /* Selective Acknowledgement,rfc 2018 */
#define TCPCTL_DROP 15 /* drop tcp connection */
#define TCPCTL_MAXID 16
#define TCPCTL_FINWAIT2_TIMEOUT 17
#define TCPCTL_NAMES { \
{ 0, 0 }, \

View File

@ -436,6 +436,7 @@ tcp_stats(u_long off __unused, const char *name, int af1 __unused)
p(tcps_timeoutdrop, "\t\t%lu connection%s dropped by rexmit timeout\n");
p(tcps_persisttimeo, "\t%lu persist timeout%s\n");
p(tcps_persistdrop, "\t\t%lu connection%s dropped by persist timeout\n");
p(tcps_finwait2_drops, "\t%lu Connection%s (fin_wait_2) dropped because of timeout\n");
p(tcps_keeptimeo, "\t%lu keepalive timeout%s\n");
p(tcps_keepprobe, "\t\t%lu keepalive probe%s sent\n");
p(tcps_keepdrops, "\t\t%lu connection%s dropped by keepalive\n");