Index: sys/netinet/tcp_timer.h =================================================================== --- sys/netinet/tcp_timer.h +++ sys/netinet/tcp_timer.h @@ -86,6 +86,7 @@ #define TCPTV_KEEP_IDLE (120*60*hz) /* dflt time before probing */ #define TCPTV_KEEPINTVL ( 75*hz) /* default probe interval */ #define TCPTV_KEEPCNT 8 /* max probes before drop */ +#define TCPTV_KEEPSLOP 2 /* idle time max % variance */ #define TCPTV_FINWAIT2_TIMEOUT (60*hz) /* FIN_WAIT_2 timeout if no receiver */ Index: sys/netinet/tcp_timer.c =================================================================== --- sys/netinet/tcp_timer.c +++ sys/netinet/tcp_timer.c @@ -135,6 +135,10 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, keepcnt, CTLFLAG_RW, &tcp_keepcnt, 0, "Number of keepalive probes to send"); +int tcp_keepslop = TCPTV_KEEPSLOP; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, keepslop, CTLFLAG_RW, &tcp_keepslop, 0, + "Percentage variance allowed in the idle time (0-100)"); + /* max idle probes */ int tcp_maxpersistidle; @@ -421,6 +425,7 @@ struct tcpcb *tp = xtp; struct tcptemp *t_template; struct inpcb *inp; + int earlytime, idletime; CURVNET_SET(tp->t_vnet); #ifdef TCPDEBUG int ostate; @@ -446,25 +451,6 @@ ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); /* - * Because we don't regularly reset the keepalive callout in - * the ESTABLISHED state, it may be that we don't actually need - * to send a keepalive yet. If that occurs, schedule another - * call for the next time the keepalive timer might expire. - */ - if (TCPS_HAVEESTABLISHED(tp->t_state)) { - u_int idletime; - - idletime = ticks - tp->t_rcvtime; - if (idletime < TP_KEEPIDLE(tp)) { - callout_reset(&tp->t_timers->tt_keep, - TP_KEEPIDLE(tp) - idletime, tcp_timer_keep, tp); - INP_WUNLOCK(inp); - CURVNET_RESTORE(); - return; - } - } - - /* * Keep-alive timer went off; send something * or drop connection if idle for too long. */ @@ -474,9 +460,32 @@ if ((tcp_always_keepalive || inp->inp_socket->so_options & SO_KEEPALIVE) && tp->t_state <= TCPS_CLOSING) { - if (ticks - tp->t_rcvtime >= TP_KEEPIDLE(tp) + TP_MAXIDLE(tp)) + idletime = ticks - tp->t_rcvtime; + + if (idletime >= TP_KEEPIDLE(tp) + TP_MAXIDLE(tp)) goto dropit; /* + * Because we don't regularly reset the keepalive callout in + * the ESTABLISHED state, it may be that we don't actually need + * to send a keepalive yet. If that occurs, schedule another + * call for the next time the keepalive timer might expire. + * However, to avoid unnecessarily rescheduling the keepalive + * timer to fire again in the very near future, let's proceed + * to send a keepalive early if we are "close" (as defined + * by tcp_keepslop) of the allowed time. + */ + idletime = ticks - tp->t_rcvtime; + if (idletime < TP_KEEPIDLE(tp) - + tcp_keepslop * (TP_KEEPIDLE(tp) / 100)) { + callout_reset(&tp->t_timers->tt_keep, + TP_KEEPIDLE(tp) - idletime, tcp_timer_keep, tp); + goto done; + } else if (idletime < TP_KEEPIDLE(tp)) + earlytime = TP_KEEPIDLE(tp) - idletime; + else + earlytime = 0; + + /* * Send a packet designed to force a response * if the peer is up and reachable: * either an ACK if the connection is still alive, @@ -496,8 +505,8 @@ tp->rcv_nxt, tp->snd_una - 1, 0); free(t_template, M_TEMP); } - callout_reset(&tp->t_timers->tt_keep, TP_KEEPINTVL(tp), - tcp_timer_keep, tp); + callout_reset(&tp->t_timers->tt_keep, + TP_KEEPINTVL(tp) + earlytime, tcp_timer_keep, tp); } else callout_reset(&tp->t_timers->tt_keep, TP_KEEPIDLE(tp), tcp_timer_keep, tp); @@ -508,6 +517,7 @@ PRU_SLOWTIMO); #endif TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); +done: INP_WUNLOCK(inp); CURVNET_RESTORE(); return;