Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netinet/tcp_timer.c
Show First 20 Lines • Show All 288 Lines • ▼ Show 20 Lines | if (callout_pending(&tp->t_timers->tt_delack) || | ||||
return; | return; | ||||
} | } | ||||
callout_deactivate(&tp->t_timers->tt_delack); | callout_deactivate(&tp->t_timers->tt_delack); | ||||
if ((inp->inp_flags & INP_DROPPED) != 0) { | if ((inp->inp_flags & INP_DROPPED) != 0) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | |||||
("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | |||||
KASSERT((tp->t_timers->tt_flags & TT_DELACK) != 0, | |||||
("%s: tp %p delack callout should be running", __func__, tp)); | |||||
tp->t_flags |= TF_ACKNOW; | tp->t_flags |= TF_ACKNOW; | ||||
TCPSTAT_INC(tcps_delack); | TCPSTAT_INC(tcps_delack); | ||||
(void) tp->t_fb->tfb_tcp_output(tp); | (void) tp->t_fb->tfb_tcp_output(tp); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
int | |||||
tcp_inpinfo_lock_add(struct inpcb *inp) | |||||
{ | |||||
in_pcbref(inp); | |||||
INP_WUNLOCK(inp); | |||||
INP_INFO_RLOCK(&V_tcbinfo); | |||||
INP_WLOCK(inp); | |||||
if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { | |||||
return(1); | |||||
} | |||||
return(0); | |||||
} | |||||
void | void | ||||
tcp_inpinfo_lock_del(struct inpcb *inp, struct tcpcb *tp) | |||||
{ | |||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
if (inp && (tp == NULL)) { | |||||
/* | |||||
* If tcp_close/drop() gets called and tp | |||||
* returns NULL, then the function dropped | |||||
* the inp lock, we hold a reference keeping | |||||
* this around, so we must re-aquire the | |||||
* INP_WLOCK() in order to proceed with | |||||
* our dropping the inp reference. | |||||
*/ | |||||
INP_WLOCK(inp); | |||||
} | |||||
if (inp && in_pcbrele_wlocked(inp) == 0) | |||||
INP_WUNLOCK(inp); | |||||
} | |||||
void | |||||
tcp_timer_2msl(void *xtp) | tcp_timer_2msl(void *xtp) | ||||
{ | { | ||||
struct tcpcb *tp = xtp; | struct tcpcb *tp = xtp; | ||||
struct inpcb *inp; | struct inpcb *inp; | ||||
CURVNET_SET(tp->t_vnet); | CURVNET_SET(tp->t_vnet); | ||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
int ostate; | int ostate; | ||||
ostate = tp->t_state; | ostate = tp->t_state; | ||||
#endif | #endif | ||||
INP_INFO_RLOCK(&V_tcbinfo); | |||||
inp = tp->t_inpcb; | inp = tp->t_inpcb; | ||||
KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); | KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
tcp_free_sackholes(tp); | tcp_free_sackholes(tp); | ||||
if (callout_pending(&tp->t_timers->tt_2msl) || | if (callout_pending(&tp->t_timers->tt_2msl) || | ||||
!callout_active(&tp->t_timers->tt_2msl)) { | !callout_active(&tp->t_timers->tt_2msl)) { | ||||
INP_WUNLOCK(tp->t_inpcb); | INP_WUNLOCK(tp->t_inpcb); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
callout_deactivate(&tp->t_timers->tt_2msl); | callout_deactivate(&tp->t_timers->tt_2msl); | ||||
if ((inp->inp_flags & INP_DROPPED) != 0) { | if ((inp->inp_flags & INP_DROPPED) != 0) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | ||||
("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | ||||
KASSERT((tp->t_timers->tt_flags & TT_2MSL) != 0, | |||||
("%s: tp %p 2msl callout should be running", __func__, tp)); | |||||
/* | /* | ||||
* 2 MSL timeout in shutdown went off. If we're closed but | * 2 MSL timeout in shutdown went off. If we're closed but | ||||
* still waiting for peer to close and connection has been idle | * still waiting for peer to close and connection has been idle | ||||
* too long delete connection control block. Otherwise, check | * too long delete connection control block. Otherwise, check | ||||
* again in a bit. | * again in a bit. | ||||
* | * | ||||
* If in TIME_WAIT state just ignore as this timeout is handled in | * If in TIME_WAIT state just ignore as this timeout is handled in | ||||
* tcp_tw_2msl_scan(). | * tcp_tw_2msl_scan(). | ||||
* | * | ||||
* If fastrecycle of FIN_WAIT_2, in FIN_WAIT_2 and receiver has closed, | * 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. | * there's no point in hanging onto FIN_WAIT_2 socket. Just close it. | ||||
* Ignore fact that there were recent incoming segments. | * Ignore fact that there were recent incoming segments. | ||||
*/ | */ | ||||
if ((inp->inp_flags & INP_TIMEWAIT) != 0) { | if ((inp->inp_flags & INP_TIMEWAIT) != 0) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
if (tcp_fast_finwait2_recycle && tp->t_state == TCPS_FIN_WAIT_2 && | if (tcp_fast_finwait2_recycle && tp->t_state == TCPS_FIN_WAIT_2 && | ||||
tp->t_inpcb && tp->t_inpcb->inp_socket && | tp->t_inpcb && tp->t_inpcb->inp_socket && | ||||
(tp->t_inpcb->inp_socket->so_rcv.sb_state & SBS_CANTRCVMORE)) { | (tp->t_inpcb->inp_socket->so_rcv.sb_state & SBS_CANTRCVMORE)) { | ||||
TCPSTAT_INC(tcps_finwait2_drops); | TCPSTAT_INC(tcps_finwait2_drops); | ||||
if (tcp_inpinfo_lock_add(inp)) { | |||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | |||||
} | |||||
tp = tcp_close(tp); | tp = tcp_close(tp); | ||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | |||||
} else { | } else { | ||||
if (ticks - tp->t_rcvtime <= TP_MAXIDLE(tp)) { | if (ticks - tp->t_rcvtime <= TP_MAXIDLE(tp)) { | ||||
if (!callout_reset(&tp->t_timers->tt_2msl, | callout_reset(&tp->t_timers->tt_2msl, | ||||
TP_KEEPINTVL(tp), tcp_timer_2msl, tp)) { | TP_KEEPINTVL(tp), tcp_timer_2msl, tp); | ||||
tp->t_timers->tt_flags &= ~TT_2MSL_RST; | } else { | ||||
if (tcp_inpinfo_lock_add(inp)) { | |||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | |||||
} | } | ||||
} else | |||||
tp = tcp_close(tp); | tp = tcp_close(tp); | ||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | |||||
} | } | ||||
} | |||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) | if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) | ||||
tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, | tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, | ||||
PRU_SLOWTIMO); | PRU_SLOWTIMO); | ||||
#endif | #endif | ||||
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | ||||
if (tp != NULL) | if (tp != NULL) | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | out: | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
void | void | ||||
tcp_timer_keep(void *xtp) | tcp_timer_keep(void *xtp) | ||||
{ | { | ||||
struct tcpcb *tp = xtp; | struct tcpcb *tp = xtp; | ||||
struct tcptemp *t_template; | struct tcptemp *t_template; | ||||
struct inpcb *inp; | struct inpcb *inp; | ||||
CURVNET_SET(tp->t_vnet); | CURVNET_SET(tp->t_vnet); | ||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
int ostate; | int ostate; | ||||
ostate = tp->t_state; | ostate = tp->t_state; | ||||
#endif | #endif | ||||
INP_INFO_RLOCK(&V_tcbinfo); | |||||
inp = tp->t_inpcb; | inp = tp->t_inpcb; | ||||
KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); | KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
if (callout_pending(&tp->t_timers->tt_keep) || | if (callout_pending(&tp->t_timers->tt_keep) || | ||||
!callout_active(&tp->t_timers->tt_keep)) { | !callout_active(&tp->t_timers->tt_keep)) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
callout_deactivate(&tp->t_timers->tt_keep); | callout_deactivate(&tp->t_timers->tt_keep); | ||||
if ((inp->inp_flags & INP_DROPPED) != 0) { | if ((inp->inp_flags & INP_DROPPED) != 0) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | ||||
("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | ||||
KASSERT((tp->t_timers->tt_flags & TT_KEEP) != 0, | |||||
("%s: tp %p keep callout should be running", __func__, tp)); | |||||
/* | /* | ||||
* Keep-alive timer went off; send something | * Keep-alive timer went off; send something | ||||
* or drop connection if idle for too long. | * or drop connection if idle for too long. | ||||
*/ | */ | ||||
TCPSTAT_INC(tcps_keeptimeo); | TCPSTAT_INC(tcps_keeptimeo); | ||||
if (tp->t_state < TCPS_ESTABLISHED) | if (tp->t_state < TCPS_ESTABLISHED) | ||||
goto dropit; | goto dropit; | ||||
if ((always_keepalive || inp->inp_socket->so_options & SO_KEEPALIVE) && | if ((always_keepalive || inp->inp_socket->so_options & SO_KEEPALIVE) && | ||||
Show All 15 Lines | if ((always_keepalive || inp->inp_socket->so_options & SO_KEEPALIVE) && | ||||
TCPSTAT_INC(tcps_keepprobe); | TCPSTAT_INC(tcps_keepprobe); | ||||
t_template = tcpip_maketemplate(inp); | t_template = tcpip_maketemplate(inp); | ||||
if (t_template) { | if (t_template) { | ||||
tcp_respond(tp, t_template->tt_ipgen, | tcp_respond(tp, t_template->tt_ipgen, | ||||
&t_template->tt_t, (struct mbuf *)NULL, | &t_template->tt_t, (struct mbuf *)NULL, | ||||
tp->rcv_nxt, tp->snd_una - 1, 0); | tp->rcv_nxt, tp->snd_una - 1, 0); | ||||
free(t_template, M_TEMP); | free(t_template, M_TEMP); | ||||
} | } | ||||
if (!callout_reset(&tp->t_timers->tt_keep, TP_KEEPINTVL(tp), | callout_reset(&tp->t_timers->tt_keep, TP_KEEPINTVL(tp), | ||||
tcp_timer_keep, tp)) { | tcp_timer_keep, tp); | ||||
tp->t_timers->tt_flags &= ~TT_KEEP_RST; | } else | ||||
} | callout_reset(&tp->t_timers->tt_keep, TP_KEEPIDLE(tp), | ||||
} else if (!callout_reset(&tp->t_timers->tt_keep, TP_KEEPIDLE(tp), | tcp_timer_keep, tp); | ||||
tcp_timer_keep, tp)) { | |||||
tp->t_timers->tt_flags &= ~TT_KEEP_RST; | |||||
} | |||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
if (inp->inp_socket->so_options & SO_DEBUG) | if (inp->inp_socket->so_options & SO_DEBUG) | ||||
tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, | tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, | ||||
PRU_SLOWTIMO); | PRU_SLOWTIMO); | ||||
#endif | #endif | ||||
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
dropit: | dropit: | ||||
TCPSTAT_INC(tcps_keepdrops); | TCPSTAT_INC(tcps_keepdrops); | ||||
if (tcp_inpinfo_lock_add(inp)) { | |||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | |||||
} | |||||
tp = tcp_drop(tp, ETIMEDOUT); | tp = tcp_drop(tp, ETIMEDOUT); | ||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) | if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) | ||||
tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, | tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, | ||||
PRU_SLOWTIMO); | PRU_SLOWTIMO); | ||||
#endif | #endif | ||||
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | ||||
if (tp != NULL) | tcp_inpinfo_lock_del(inp, tp); | ||||
INP_WUNLOCK(tp->t_inpcb); | out: | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
void | void | ||||
tcp_timer_persist(void *xtp) | tcp_timer_persist(void *xtp) | ||||
{ | { | ||||
struct tcpcb *tp = xtp; | struct tcpcb *tp = xtp; | ||||
struct inpcb *inp; | struct inpcb *inp; | ||||
CURVNET_SET(tp->t_vnet); | CURVNET_SET(tp->t_vnet); | ||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
int ostate; | int ostate; | ||||
ostate = tp->t_state; | ostate = tp->t_state; | ||||
#endif | #endif | ||||
INP_INFO_RLOCK(&V_tcbinfo); | |||||
inp = tp->t_inpcb; | inp = tp->t_inpcb; | ||||
KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); | KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
if (callout_pending(&tp->t_timers->tt_persist) || | if (callout_pending(&tp->t_timers->tt_persist) || | ||||
!callout_active(&tp->t_timers->tt_persist)) { | !callout_active(&tp->t_timers->tt_persist)) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
callout_deactivate(&tp->t_timers->tt_persist); | callout_deactivate(&tp->t_timers->tt_persist); | ||||
if ((inp->inp_flags & INP_DROPPED) != 0) { | if ((inp->inp_flags & INP_DROPPED) != 0) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | ||||
("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | ||||
KASSERT((tp->t_timers->tt_flags & TT_PERSIST) != 0, | |||||
("%s: tp %p persist callout should be running", __func__, tp)); | |||||
/* | /* | ||||
* Persistence timer into zero window. | * Persistence timer into zero window. | ||||
* Force a byte to be output, if possible. | * Force a byte to be output, if possible. | ||||
*/ | */ | ||||
TCPSTAT_INC(tcps_persisttimeo); | TCPSTAT_INC(tcps_persisttimeo); | ||||
/* | /* | ||||
* Hack: if the peer is dead/unreachable, we do not | * Hack: if the peer is dead/unreachable, we do not | ||||
* time out if the window is closed. After a full | * time out if the window is closed. After a full | ||||
* backoff, drop the connection if the idle time | * backoff, drop the connection if the idle time | ||||
* (no responses to probes) reaches the maximum | * (no responses to probes) reaches the maximum | ||||
* backoff that we would use if retransmitting. | * backoff that we would use if retransmitting. | ||||
*/ | */ | ||||
if (tp->t_rxtshift == TCP_MAXRXTSHIFT && | if (tp->t_rxtshift == TCP_MAXRXTSHIFT && | ||||
(ticks - tp->t_rcvtime >= tcp_maxpersistidle || | (ticks - tp->t_rcvtime >= tcp_maxpersistidle || | ||||
ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff)) { | ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff)) { | ||||
TCPSTAT_INC(tcps_persistdrop); | TCPSTAT_INC(tcps_persistdrop); | ||||
if (tcp_inpinfo_lock_add(inp)) { | |||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | |||||
} | |||||
tp = tcp_drop(tp, ETIMEDOUT); | tp = tcp_drop(tp, ETIMEDOUT); | ||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | goto out; | ||||
} | } | ||||
/* | /* | ||||
* If the user has closed the socket then drop a persisting | * If the user has closed the socket then drop a persisting | ||||
* connection after a much reduced timeout. | * connection after a much reduced timeout. | ||||
*/ | */ | ||||
if (tp->t_state > TCPS_CLOSE_WAIT && | if (tp->t_state > TCPS_CLOSE_WAIT && | ||||
(ticks - tp->t_rcvtime) >= TCPTV_PERSMAX) { | (ticks - tp->t_rcvtime) >= TCPTV_PERSMAX) { | ||||
TCPSTAT_INC(tcps_persistdrop); | TCPSTAT_INC(tcps_persistdrop); | ||||
if (tcp_inpinfo_lock_add(inp)) { | |||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | |||||
} | |||||
tp = tcp_drop(tp, ETIMEDOUT); | tp = tcp_drop(tp, ETIMEDOUT); | ||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | goto out; | ||||
} | } | ||||
tcp_setpersist(tp); | tcp_setpersist(tp); | ||||
tp->t_flags |= TF_FORCEDATA; | tp->t_flags |= TF_FORCEDATA; | ||||
(void) tp->t_fb->tfb_tcp_output(tp); | (void) tp->t_fb->tfb_tcp_output(tp); | ||||
tp->t_flags &= ~TF_FORCEDATA; | tp->t_flags &= ~TF_FORCEDATA; | ||||
out: | |||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
if (tp != NULL && tp->t_inpcb->inp_socket->so_options & SO_DEBUG) | if (tp != NULL && tp->t_inpcb->inp_socket->so_options & SO_DEBUG) | ||||
tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); | tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); | ||||
#endif | #endif | ||||
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | ||||
if (tp != NULL) | |||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | out: | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
void | void | ||||
tcp_timer_rexmt(void * xtp) | tcp_timer_rexmt(void * xtp) | ||||
{ | { | ||||
struct tcpcb *tp = xtp; | struct tcpcb *tp = xtp; | ||||
CURVNET_SET(tp->t_vnet); | CURVNET_SET(tp->t_vnet); | ||||
int rexmt; | int rexmt; | ||||
int headlocked; | |||||
struct inpcb *inp; | struct inpcb *inp; | ||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
int ostate; | int ostate; | ||||
ostate = tp->t_state; | ostate = tp->t_state; | ||||
#endif | #endif | ||||
INP_INFO_RLOCK(&V_tcbinfo); | |||||
inp = tp->t_inpcb; | inp = tp->t_inpcb; | ||||
KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); | KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
if (callout_pending(&tp->t_timers->tt_rexmt) || | if (callout_pending(&tp->t_timers->tt_rexmt) || | ||||
!callout_active(&tp->t_timers->tt_rexmt)) { | !callout_active(&tp->t_timers->tt_rexmt)) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
callout_deactivate(&tp->t_timers->tt_rexmt); | callout_deactivate(&tp->t_timers->tt_rexmt); | ||||
if ((inp->inp_flags & INP_DROPPED) != 0) { | if ((inp->inp_flags & INP_DROPPED) != 0) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
return; | return; | ||||
} | } | ||||
KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, | ||||
("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); | ||||
KASSERT((tp->t_timers->tt_flags & TT_REXMT) != 0, | |||||
("%s: tp %p rexmt callout should be running", __func__, tp)); | |||||
tcp_free_sackholes(tp); | tcp_free_sackholes(tp); | ||||
if (tp->t_fb->tfb_tcp_rexmit_tmr) { | if (tp->t_fb->tfb_tcp_rexmit_tmr) { | ||||
/* The stack has a timer action too. */ | /* The stack has a timer action too. */ | ||||
(*tp->t_fb->tfb_tcp_rexmit_tmr)(tp); | (*tp->t_fb->tfb_tcp_rexmit_tmr)(tp); | ||||
} | } | ||||
/* | /* | ||||
* Retransmission timer went off. Message has not | * Retransmission timer went off. Message has not | ||||
* been acked within retransmit interval. Back off | * been acked within retransmit interval. Back off | ||||
* to a longer retransmit interval and retransmit one segment. | * to a longer retransmit interval and retransmit one segment. | ||||
*/ | */ | ||||
if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { | if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { | ||||
tp->t_rxtshift = TCP_MAXRXTSHIFT; | tp->t_rxtshift = TCP_MAXRXTSHIFT; | ||||
TCPSTAT_INC(tcps_timeoutdrop); | TCPSTAT_INC(tcps_timeoutdrop); | ||||
if (tcp_inpinfo_lock_add(inp)) { | |||||
tcp_inpinfo_lock_del(inp, tp); | |||||
goto out; | |||||
} | |||||
tp = tcp_drop(tp, tp->t_softerror ? | tp = tcp_drop(tp, tp->t_softerror ? | ||||
tp->t_softerror : ETIMEDOUT); | tp->t_softerror : ETIMEDOUT); | ||||
headlocked = 1; | tcp_inpinfo_lock_del(inp, tp); | ||||
goto out; | goto out; | ||||
} | } | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
headlocked = 0; | |||||
if (tp->t_state == TCPS_SYN_SENT) { | if (tp->t_state == TCPS_SYN_SENT) { | ||||
/* | /* | ||||
* If the SYN was retransmitted, indicate CWND to be | * If the SYN was retransmitted, indicate CWND to be | ||||
* limited to 1 segment in cc_conn_init(). | * limited to 1 segment in cc_conn_init(). | ||||
*/ | */ | ||||
tp->snd_cwnd = 1; | tp->snd_cwnd = 1; | ||||
} else if (tp->t_rxtshift == 1) { | } else if (tp->t_rxtshift == 1) { | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | #endif | ||||
* If timing a segment in this window, stop the timer. | * If timing a segment in this window, stop the timer. | ||||
*/ | */ | ||||
tp->t_rtttime = 0; | tp->t_rtttime = 0; | ||||
cc_cong_signal(tp, NULL, CC_RTO); | cc_cong_signal(tp, NULL, CC_RTO); | ||||
(void) tp->t_fb->tfb_tcp_output(tp); | (void) tp->t_fb->tfb_tcp_output(tp); | ||||
out: | |||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) | if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) | ||||
tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, | tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, | ||||
PRU_SLOWTIMO); | PRU_SLOWTIMO); | ||||
#endif | #endif | ||||
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); | ||||
if (tp != NULL) | |||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
if (headlocked) | out: | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
void | void | ||||
tcp_timer_activate(struct tcpcb *tp, uint32_t timer_type, u_int delta) | tcp_timer_activate(struct tcpcb *tp, uint32_t timer_type, u_int delta) | ||||
{ | { | ||||
struct callout *t_callout; | struct callout *t_callout; | ||||
timeout_t *f_callout; | timeout_t *f_callout; | ||||
struct inpcb *inp = tp->t_inpcb; | struct inpcb *inp = tp->t_inpcb; | ||||
int cpu = inp_to_cpuid(inp); | int cpu = inp_to_cpuid(inp); | ||||
uint32_t f_reset; | |||||
#ifdef TCP_OFFLOAD | #ifdef TCP_OFFLOAD | ||||
if (tp->t_flags & TF_TOE) | if (tp->t_flags & TF_TOE) | ||||
return; | return; | ||||
#endif | #endif | ||||
if (tp->t_timers->tt_flags & TT_STOPPED) | if (tp->t_timers->tt_flags & TT_STOPPED) | ||||
return; | return; | ||||
switch (timer_type) { | switch (timer_type) { | ||||
case TT_DELACK: | case TT_DELACK: | ||||
t_callout = &tp->t_timers->tt_delack; | t_callout = &tp->t_timers->tt_delack; | ||||
f_callout = tcp_timer_delack; | f_callout = tcp_timer_delack; | ||||
f_reset = TT_DELACK_RST; | |||||
break; | break; | ||||
case TT_REXMT: | case TT_REXMT: | ||||
t_callout = &tp->t_timers->tt_rexmt; | t_callout = &tp->t_timers->tt_rexmt; | ||||
f_callout = tcp_timer_rexmt; | f_callout = tcp_timer_rexmt; | ||||
f_reset = TT_REXMT_RST; | |||||
break; | break; | ||||
case TT_PERSIST: | case TT_PERSIST: | ||||
t_callout = &tp->t_timers->tt_persist; | t_callout = &tp->t_timers->tt_persist; | ||||
f_callout = tcp_timer_persist; | f_callout = tcp_timer_persist; | ||||
f_reset = TT_PERSIST_RST; | |||||
break; | break; | ||||
case TT_KEEP: | case TT_KEEP: | ||||
t_callout = &tp->t_timers->tt_keep; | t_callout = &tp->t_timers->tt_keep; | ||||
f_callout = tcp_timer_keep; | f_callout = tcp_timer_keep; | ||||
f_reset = TT_KEEP_RST; | |||||
break; | break; | ||||
case TT_2MSL: | case TT_2MSL: | ||||
t_callout = &tp->t_timers->tt_2msl; | t_callout = &tp->t_timers->tt_2msl; | ||||
f_callout = tcp_timer_2msl; | f_callout = tcp_timer_2msl; | ||||
f_reset = TT_2MSL_RST; | |||||
break; | break; | ||||
default: | default: | ||||
if (tp->t_fb->tfb_tcp_timer_activate) { | if (tp->t_fb->tfb_tcp_timer_activate) { | ||||
tp->t_fb->tfb_tcp_timer_activate(tp, timer_type, delta); | tp->t_fb->tfb_tcp_timer_activate(tp, timer_type, delta); | ||||
return; | return; | ||||
} | } | ||||
panic("tp %p bad timer_type %#x", tp, timer_type); | panic("tp %p bad timer_type %#x", tp, timer_type); | ||||
} | } | ||||
if (delta == 0) { | if (delta == 0) { | ||||
if ((tp->t_timers->tt_flags & timer_type) && | callout_stop(t_callout); | ||||
(callout_stop(t_callout) > 0) && | |||||
(tp->t_timers->tt_flags & f_reset)) { | |||||
tp->t_timers->tt_flags &= ~(timer_type | f_reset); | |||||
} | |||||
} else { | } else { | ||||
if ((tp->t_timers->tt_flags & timer_type) == 0) { | |||||
tp->t_timers->tt_flags |= (timer_type | f_reset); | |||||
callout_reset_on(t_callout, delta, f_callout, tp, cpu); | callout_reset_on(t_callout, delta, f_callout, tp, cpu); | ||||
} else { | |||||
/* Reset already running callout on the same CPU. */ | |||||
if (!callout_reset(t_callout, delta, f_callout, tp)) { | |||||
/* | |||||
* Callout not cancelled, consider it as not | |||||
* properly restarted. */ | |||||
tp->t_timers->tt_flags &= ~f_reset; | |||||
} | } | ||||
} | } | ||||
} | |||||
} | |||||
int | int | ||||
tcp_timer_active(struct tcpcb *tp, uint32_t timer_type) | tcp_timer_active(struct tcpcb *tp, uint32_t timer_type) | ||||
{ | { | ||||
struct callout *t_callout; | struct callout *t_callout; | ||||
switch (timer_type) { | switch (timer_type) { | ||||
case TT_DELACK: | case TT_DELACK: | ||||
Show All 19 Lines | switch (timer_type) { | ||||
} | } | ||||
return callout_active(t_callout); | return callout_active(t_callout); | ||||
} | } | ||||
void | void | ||||
tcp_timer_stop(struct tcpcb *tp, uint32_t timer_type) | tcp_timer_stop(struct tcpcb *tp, uint32_t timer_type) | ||||
{ | { | ||||
struct callout *t_callout; | struct callout *t_callout; | ||||
uint32_t f_reset; | |||||
tp->t_timers->tt_flags |= TT_STOPPED; | tp->t_timers->tt_flags |= TT_STOPPED; | ||||
switch (timer_type) { | switch (timer_type) { | ||||
case TT_DELACK: | case TT_DELACK: | ||||
t_callout = &tp->t_timers->tt_delack; | t_callout = &tp->t_timers->tt_delack; | ||||
f_reset = TT_DELACK_RST; | |||||
break; | break; | ||||
case TT_REXMT: | case TT_REXMT: | ||||
t_callout = &tp->t_timers->tt_rexmt; | t_callout = &tp->t_timers->tt_rexmt; | ||||
f_reset = TT_REXMT_RST; | |||||
break; | break; | ||||
case TT_PERSIST: | case TT_PERSIST: | ||||
t_callout = &tp->t_timers->tt_persist; | t_callout = &tp->t_timers->tt_persist; | ||||
f_reset = TT_PERSIST_RST; | |||||
break; | break; | ||||
case TT_KEEP: | case TT_KEEP: | ||||
t_callout = &tp->t_timers->tt_keep; | t_callout = &tp->t_timers->tt_keep; | ||||
f_reset = TT_KEEP_RST; | |||||
break; | break; | ||||
case TT_2MSL: | case TT_2MSL: | ||||
t_callout = &tp->t_timers->tt_2msl; | t_callout = &tp->t_timers->tt_2msl; | ||||
f_reset = TT_2MSL_RST; | |||||
break; | break; | ||||
default: | default: | ||||
if (tp->t_fb->tfb_tcp_timer_stop) { | if (tp->t_fb->tfb_tcp_timer_stop) { | ||||
/* | /* | ||||
* XXXrrs we need to look at this with the | * XXXrrs we need to look at this with the | ||||
* stop case below (flags). | * stop case below (flags). | ||||
*/ | */ | ||||
tp->t_fb->tfb_tcp_timer_stop(tp, timer_type); | tp->t_fb->tfb_tcp_timer_stop(tp, timer_type); | ||||
return; | return; | ||||
} | } | ||||
panic("tp %p bad timer_type %#x", tp, timer_type); | panic("tp %p bad timer_type %#x", tp, timer_type); | ||||
} | } | ||||
if (tp->t_timers->tt_flags & timer_type) { | |||||
if (callout_async_drain(t_callout, tcp_timer_discard) == 0) { | if (callout_async_drain(t_callout, tcp_timer_discard) == 0) { | ||||
/* | /* | ||||
* Can't stop the callout, defer tcpcb actual deletion | * Can't stop the callout, defer tcpcb actual deletion | ||||
* to the last one. We do this using the async drain | * to the last one. We do this using the async drain | ||||
* function and incrementing the count in | * function and incrementing the count in | ||||
*/ | */ | ||||
tp->t_timers->tt_draincnt++; | tp->t_timers->tt_draincnt++; | ||||
} | |||||
} | } | ||||
} | } | ||||
#define ticks_to_msecs(t) (1000*(t) / hz) | #define ticks_to_msecs(t) (1000*(t) / hz) | ||||
void | void | ||||
tcp_timer_to_xtimer(struct tcpcb *tp, struct tcp_timer *timer, | tcp_timer_to_xtimer(struct tcpcb *tp, struct tcp_timer *timer, | ||||
struct xtcp_timer *xtimer) | struct xtcp_timer *xtimer) | ||||
Show All 19 Lines |