xref: /mOS-networking-stack/core/src/timer.c (revision 05e3289c)
176404edcSAsim Jamshed #include "timer.h"
276404edcSAsim Jamshed #include "tcp_in.h"
376404edcSAsim Jamshed #include "tcp_out.h"
476404edcSAsim Jamshed #include "stat.h"
576404edcSAsim Jamshed #include "debug.h"
676404edcSAsim Jamshed #include "memory_mgt.h"
776404edcSAsim Jamshed #include "config.h"
876404edcSAsim Jamshed 
976404edcSAsim Jamshed #define MAX(a, b) ((a)>(b)?(a):(b))
1076404edcSAsim Jamshed #define MIN(a, b) ((a)<(b)?(a):(b))
1176404edcSAsim Jamshed 
1276404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
1376404edcSAsim Jamshed struct rto_hashstore*
InitRTOHashstore()1476404edcSAsim Jamshed InitRTOHashstore()
1576404edcSAsim Jamshed {
1676404edcSAsim Jamshed 	int i;
1776404edcSAsim Jamshed 	struct rto_hashstore* hs = calloc(1, sizeof(struct rto_hashstore));
1876404edcSAsim Jamshed 	if (!hs) {
1976404edcSAsim Jamshed 		TRACE_ERROR("calloc: InitHashStore");
2076404edcSAsim Jamshed 		return 0;
2176404edcSAsim Jamshed 	}
2276404edcSAsim Jamshed 
2376404edcSAsim Jamshed 	for (i = 0; i < RTO_HASH; i++)
2476404edcSAsim Jamshed 		TAILQ_INIT(&hs->rto_list[i]);
2576404edcSAsim Jamshed 
2676404edcSAsim Jamshed 	TAILQ_INIT(&hs->rto_list[RTO_HASH]);
2776404edcSAsim Jamshed 
2876404edcSAsim Jamshed 	return hs;
2976404edcSAsim Jamshed }
3076404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
3176404edcSAsim Jamshed inline void
AddtoRTOList(mtcp_manager_t mtcp,tcp_stream * cur_stream)3276404edcSAsim Jamshed AddtoRTOList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
3376404edcSAsim Jamshed {
3476404edcSAsim Jamshed 	if (!mtcp->rto_list_cnt) {
3576404edcSAsim Jamshed 		mtcp->rto_store->rto_now_idx = 0;
3676404edcSAsim Jamshed 		mtcp->rto_store->rto_now_ts = cur_stream->sndvar->ts_rto;
3776404edcSAsim Jamshed 	}
3876404edcSAsim Jamshed 
3976404edcSAsim Jamshed 	if (cur_stream->on_rto_idx < 0 ) {
4076404edcSAsim Jamshed 		if (cur_stream->on_timewait_list) {
4176404edcSAsim Jamshed 			TRACE_ERROR("Stream %u: cannot be in both "
4276404edcSAsim Jamshed 					"rto and timewait list.\n", cur_stream->id);
4376404edcSAsim Jamshed #ifdef DUMP_STREAM
4476404edcSAsim Jamshed 			DumpStream(mtcp, cur_stream);
4576404edcSAsim Jamshed #endif
4676404edcSAsim Jamshed 			return;
4776404edcSAsim Jamshed 		}
4876404edcSAsim Jamshed 
4976404edcSAsim Jamshed 		int diff = (int32_t)(cur_stream->sndvar->ts_rto - mtcp->rto_store->rto_now_ts);
5076404edcSAsim Jamshed #if 0
5176404edcSAsim Jamshed 		if (diff < RTO_HASH) {
5276404edcSAsim Jamshed #else
5376404edcSAsim Jamshed 			int offset= ((diff + mtcp->rto_store->rto_now_idx) & (RTO_HASH - 1));
5476404edcSAsim Jamshed 			cur_stream->on_rto_idx = offset;
5576404edcSAsim Jamshed 			TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[offset]),
5676404edcSAsim Jamshed 					cur_stream, sndvar->timer_link);
5776404edcSAsim Jamshed #endif
5876404edcSAsim Jamshed #if 0
5976404edcSAsim Jamshed 		} else {
6076404edcSAsim Jamshed 			cur_stream->on_rto_idx = RTO_HASH;
6176404edcSAsim Jamshed 			TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[RTO_HASH]),
6276404edcSAsim Jamshed 					cur_stream, sndvar->timer_link);
6376404edcSAsim Jamshed 		}
6476404edcSAsim Jamshed #endif
6576404edcSAsim Jamshed 		mtcp->rto_list_cnt++;
6676404edcSAsim Jamshed 	}
6776404edcSAsim Jamshed }
6876404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
6976404edcSAsim Jamshed inline void
RemoveFromRTOList(mtcp_manager_t mtcp,tcp_stream * cur_stream)7076404edcSAsim Jamshed RemoveFromRTOList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
7176404edcSAsim Jamshed {
7276404edcSAsim Jamshed 	if (cur_stream->on_rto_idx < 0) {
7376404edcSAsim Jamshed //		assert(0);
7476404edcSAsim Jamshed 		return;
7576404edcSAsim Jamshed 	}
7676404edcSAsim Jamshed 
7776404edcSAsim Jamshed 	TAILQ_REMOVE(&mtcp->rto_store->rto_list[cur_stream->on_rto_idx],
7876404edcSAsim Jamshed 			cur_stream, sndvar->timer_link);
7976404edcSAsim Jamshed 	cur_stream->on_rto_idx = -1;
8076404edcSAsim Jamshed 
8176404edcSAsim Jamshed 	mtcp->rto_list_cnt--;
8276404edcSAsim Jamshed }
8376404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
8476404edcSAsim Jamshed inline void
AddtoTimewaitList(mtcp_manager_t mtcp,tcp_stream * cur_stream,uint32_t cur_ts)8576404edcSAsim Jamshed AddtoTimewaitList(mtcp_manager_t mtcp, tcp_stream *cur_stream, uint32_t cur_ts)
8676404edcSAsim Jamshed {
8776404edcSAsim Jamshed 	cur_stream->rcvvar->ts_tw_expire = cur_ts + g_config.mos->tcp_tw_interval;
8876404edcSAsim Jamshed 
8976404edcSAsim Jamshed 	if (cur_stream->on_timewait_list) {
9076404edcSAsim Jamshed 		// Update list in sorted way by ts_tw_expire
9176404edcSAsim Jamshed 		TAILQ_REMOVE(&mtcp->timewait_list, cur_stream, sndvar->timer_link);
9276404edcSAsim Jamshed 		TAILQ_INSERT_TAIL(&mtcp->timewait_list, cur_stream, sndvar->timer_link);
9376404edcSAsim Jamshed 	} else {
9476404edcSAsim Jamshed 		if (cur_stream->on_rto_idx >= 0) {
9576404edcSAsim Jamshed 			TRACE_DBG("Stream %u: cannot be in both "
9676404edcSAsim Jamshed 					"timewait and rto list.\n", cur_stream->id);
9776404edcSAsim Jamshed 			//assert(0);
9876404edcSAsim Jamshed #ifdef DUMP_STREAM
9976404edcSAsim Jamshed 			DumpStream(mtcp, cur_stream);
10076404edcSAsim Jamshed #endif
10176404edcSAsim Jamshed 			RemoveFromRTOList(mtcp, cur_stream);
10276404edcSAsim Jamshed 		}
10376404edcSAsim Jamshed 
10476404edcSAsim Jamshed 		cur_stream->on_timewait_list = TRUE;
10576404edcSAsim Jamshed 		TAILQ_INSERT_TAIL(&mtcp->timewait_list, cur_stream, sndvar->timer_link);
10676404edcSAsim Jamshed 		mtcp->timewait_list_cnt++;
10776404edcSAsim Jamshed 	}
10876404edcSAsim Jamshed }
10976404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
11076404edcSAsim Jamshed inline void
RemoveFromTimewaitList(mtcp_manager_t mtcp,tcp_stream * cur_stream)11176404edcSAsim Jamshed RemoveFromTimewaitList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
11276404edcSAsim Jamshed {
11376404edcSAsim Jamshed 	if (!cur_stream->on_timewait_list) {
11476404edcSAsim Jamshed 		//assert(0);
11576404edcSAsim Jamshed 		return;
11676404edcSAsim Jamshed 	}
11776404edcSAsim Jamshed 
11876404edcSAsim Jamshed 	TAILQ_REMOVE(&mtcp->timewait_list, cur_stream, sndvar->timer_link);
11976404edcSAsim Jamshed 	cur_stream->on_timewait_list = FALSE;
12076404edcSAsim Jamshed 	mtcp->timewait_list_cnt--;
12176404edcSAsim Jamshed }
12276404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
12376404edcSAsim Jamshed inline void
AddtoTimeoutList(mtcp_manager_t mtcp,tcp_stream * cur_stream)12476404edcSAsim Jamshed AddtoTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
12576404edcSAsim Jamshed {
12676404edcSAsim Jamshed 	if (cur_stream->on_timeout_list) {
12776404edcSAsim Jamshed 		//assert(0);
12876404edcSAsim Jamshed 		return;
12976404edcSAsim Jamshed 	}
13076404edcSAsim Jamshed 
13176404edcSAsim Jamshed 	cur_stream->on_timeout_list = TRUE;
13276404edcSAsim Jamshed 	TAILQ_INSERT_TAIL(&mtcp->timeout_list, cur_stream, sndvar->timeout_link);
13376404edcSAsim Jamshed 	mtcp->timeout_list_cnt++;
13476404edcSAsim Jamshed }
13576404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
13676404edcSAsim Jamshed inline void
RemoveFromTimeoutList(mtcp_manager_t mtcp,tcp_stream * cur_stream)13776404edcSAsim Jamshed RemoveFromTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
13876404edcSAsim Jamshed {
13976404edcSAsim Jamshed 	if (cur_stream->on_timeout_list) {
14076404edcSAsim Jamshed 		cur_stream->on_timeout_list = FALSE;
14176404edcSAsim Jamshed 		TAILQ_REMOVE(&mtcp->timeout_list, cur_stream, sndvar->timeout_link);
14276404edcSAsim Jamshed 		mtcp->timeout_list_cnt--;
14376404edcSAsim Jamshed 	}
14476404edcSAsim Jamshed }
14576404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
14676404edcSAsim Jamshed inline void
UpdateTimeoutList(mtcp_manager_t mtcp,tcp_stream * cur_stream)14776404edcSAsim Jamshed UpdateTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
14876404edcSAsim Jamshed {
14976404edcSAsim Jamshed 	if (cur_stream->on_timeout_list) {
15076404edcSAsim Jamshed 		TAILQ_REMOVE(&mtcp->timeout_list, cur_stream, sndvar->timeout_link);
15176404edcSAsim Jamshed 		TAILQ_INSERT_TAIL(&mtcp->timeout_list, cur_stream, sndvar->timeout_link);
15276404edcSAsim Jamshed 	}
15376404edcSAsim Jamshed }
15476404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
15576404edcSAsim Jamshed inline void
UpdateRetransmissionTimer(mtcp_manager_t mtcp,tcp_stream * cur_stream,uint32_t cur_ts)15676404edcSAsim Jamshed UpdateRetransmissionTimer(mtcp_manager_t mtcp,
15776404edcSAsim Jamshed 		tcp_stream *cur_stream, uint32_t cur_ts)
15876404edcSAsim Jamshed {
15976404edcSAsim Jamshed 	/* Update the retransmission timer */
16076404edcSAsim Jamshed 	assert(cur_stream->sndvar->rto > 0);
16176404edcSAsim Jamshed 	cur_stream->sndvar->nrtx = 0;
16276404edcSAsim Jamshed 
16376404edcSAsim Jamshed 	/* if in rto list, remove it */
16476404edcSAsim Jamshed 	if (cur_stream->on_rto_idx >= 0) {
16576404edcSAsim Jamshed 		RemoveFromRTOList(mtcp, cur_stream);
16676404edcSAsim Jamshed 	}
16776404edcSAsim Jamshed 
16876404edcSAsim Jamshed 	/* Reset retransmission timeout */
16976404edcSAsim Jamshed 	if (TCP_SEQ_GT(cur_stream->snd_nxt, cur_stream->sndvar->snd_una)) {
17076404edcSAsim Jamshed 		/* there are packets sent but not acked */
17176404edcSAsim Jamshed 		/* update rto timestamp */
17276404edcSAsim Jamshed 		cur_stream->sndvar->ts_rto = cur_ts + cur_stream->sndvar->rto;
17376404edcSAsim Jamshed 		AddtoRTOList(mtcp, cur_stream);
17476404edcSAsim Jamshed 
17576404edcSAsim Jamshed 	} else {
17676404edcSAsim Jamshed 		/* all packets are acked */
17776404edcSAsim Jamshed 		TRACE_RTO("All packets are acked. snd_una: %u, snd_nxt: %u\n",
17876404edcSAsim Jamshed 				cur_stream->sndvar->snd_una, cur_stream->snd_nxt);
17976404edcSAsim Jamshed 	}
18076404edcSAsim Jamshed }
18176404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
18276404edcSAsim Jamshed static inline int
HandleRTO(mtcp_manager_t mtcp,uint32_t cur_ts,tcp_stream * cur_stream)18376404edcSAsim Jamshed HandleRTO(mtcp_manager_t mtcp, uint32_t cur_ts, tcp_stream *cur_stream)
18476404edcSAsim Jamshed {
18576404edcSAsim Jamshed 	uint8_t backoff;
18676404edcSAsim Jamshed 
18776404edcSAsim Jamshed 	TRACE_RTO("Stream %d Timeout! rto: %u (%ums), snd_una: %u, snd_nxt: %u\n",
18876404edcSAsim Jamshed 			cur_stream->id, cur_stream->sndvar->rto, TS_TO_MSEC(cur_stream->sndvar->rto),
18976404edcSAsim Jamshed 			cur_stream->sndvar->snd_una, cur_stream->snd_nxt);
19076404edcSAsim Jamshed 	assert(cur_stream->sndvar->rto > 0);
19176404edcSAsim Jamshed 
19276404edcSAsim Jamshed 	/* count number of retransmissions */
19376404edcSAsim Jamshed 	if (cur_stream->sndvar->nrtx < TCP_MAX_RTX) {
19476404edcSAsim Jamshed 		cur_stream->sndvar->nrtx++;
19576404edcSAsim Jamshed 	} else {
19676404edcSAsim Jamshed 		/* if it exceeds the threshold, destroy and notify to application */
19776404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Exceed MAX_RTX\n", cur_stream->id);
19876404edcSAsim Jamshed 		if (cur_stream->state < TCP_ST_ESTABLISHED) {
19976404edcSAsim Jamshed 			cur_stream->state = TCP_ST_CLOSED_RSVD;
20076404edcSAsim Jamshed 			cur_stream->close_reason = TCP_CONN_FAIL;
20176404edcSAsim Jamshed 			cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE;
20276404edcSAsim Jamshed 			DestroyTCPStream(mtcp, cur_stream);
20376404edcSAsim Jamshed 		} else {
20476404edcSAsim Jamshed 			cur_stream->state = TCP_ST_CLOSED_RSVD;
20576404edcSAsim Jamshed 			cur_stream->close_reason = TCP_CONN_LOST;
20676404edcSAsim Jamshed 			cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE;
20776404edcSAsim Jamshed 			if (cur_stream->socket) {
20876404edcSAsim Jamshed 				RaiseErrorEvent(mtcp, cur_stream);
20976404edcSAsim Jamshed 			} else {
21076404edcSAsim Jamshed 				DestroyTCPStream(mtcp, cur_stream);
21176404edcSAsim Jamshed 			}
21276404edcSAsim Jamshed 		}
21376404edcSAsim Jamshed 		return 0;
21476404edcSAsim Jamshed 	}
21576404edcSAsim Jamshed 	if (cur_stream->sndvar->nrtx > cur_stream->sndvar->max_nrtx) {
21676404edcSAsim Jamshed 		cur_stream->sndvar->max_nrtx = cur_stream->sndvar->nrtx;
21776404edcSAsim Jamshed 	}
21876404edcSAsim Jamshed 
21976404edcSAsim Jamshed 	/* update rto timestamp */
22076404edcSAsim Jamshed 	if (cur_stream->state >= TCP_ST_ESTABLISHED) {
22176404edcSAsim Jamshed 		uint32_t rto_prev;
22276404edcSAsim Jamshed 		backoff = MIN(cur_stream->sndvar->nrtx, TCP_MAX_BACKOFF);
22376404edcSAsim Jamshed 
22476404edcSAsim Jamshed 		rto_prev = cur_stream->sndvar->rto;
22576404edcSAsim Jamshed 		cur_stream->sndvar->rto =
22676404edcSAsim Jamshed 				((cur_stream->rcvvar->srtt >> 3) + cur_stream->rcvvar->rttvar) << backoff;
22776404edcSAsim Jamshed 		if (cur_stream->sndvar->rto <= 0) {
22876404edcSAsim Jamshed 			TRACE_RTO("Stream %d current rto: %u, prev: %u, state: %s\n",
22976404edcSAsim Jamshed 					cur_stream->id, cur_stream->sndvar->rto, rto_prev,
23076404edcSAsim Jamshed 					TCPStateToString(cur_stream));
23176404edcSAsim Jamshed 			cur_stream->sndvar->rto = rto_prev;
23276404edcSAsim Jamshed 		}
23376404edcSAsim Jamshed 	} else if (cur_stream->state >= TCP_ST_SYN_SENT) {
23476404edcSAsim Jamshed 		/* if there is no rtt measured, update rto based on the previous one */
23576404edcSAsim Jamshed 		if (cur_stream->sndvar->nrtx < TCP_MAX_BACKOFF) {
23676404edcSAsim Jamshed 			cur_stream->sndvar->rto <<= 1;
23776404edcSAsim Jamshed 		}
23876404edcSAsim Jamshed 	}
23976404edcSAsim Jamshed 	//cur_stream->sndvar->ts_rto = cur_ts + cur_stream->sndvar->rto;
24076404edcSAsim Jamshed 
24176404edcSAsim Jamshed 	/* reduce congestion window and ssthresh */
24276404edcSAsim Jamshed 	cur_stream->sndvar->ssthresh = MIN(cur_stream->sndvar->cwnd, cur_stream->sndvar->peer_wnd) / 2;
24376404edcSAsim Jamshed 	if (cur_stream->sndvar->ssthresh < (2 * cur_stream->sndvar->mss)) {
24476404edcSAsim Jamshed 		cur_stream->sndvar->ssthresh = cur_stream->sndvar->mss * 2;
24576404edcSAsim Jamshed 	}
24676404edcSAsim Jamshed 	cur_stream->sndvar->cwnd = cur_stream->sndvar->mss;
24776404edcSAsim Jamshed 	TRACE_CONG("Stream %d Timeout. cwnd: %u, ssthresh: %u\n",
24876404edcSAsim Jamshed 			cur_stream->id, cur_stream->sndvar->cwnd, cur_stream->sndvar->ssthresh);
24976404edcSAsim Jamshed 
25076404edcSAsim Jamshed #if RTM_STAT
25176404edcSAsim Jamshed 	/* update retransmission stats */
25276404edcSAsim Jamshed 	cur_stream->sndvar->rstat.rto_cnt++;
25376404edcSAsim Jamshed 	cur_stream->sndvar->rstat.rto_bytes += (cur_stream->snd_nxt - cur_stream->sndvar->snd_una);
25476404edcSAsim Jamshed #endif
25576404edcSAsim Jamshed 
25676404edcSAsim Jamshed 	if (cur_stream->on_rto_idx >= 0)
25776404edcSAsim Jamshed 		RemoveFromRTOList(mtcp, cur_stream);
25876404edcSAsim Jamshed 
25976404edcSAsim Jamshed 	/* Retransmission */
26076404edcSAsim Jamshed 	if (cur_stream->state == TCP_ST_SYN_SENT) {
26176404edcSAsim Jamshed 		/* SYN lost */
26276404edcSAsim Jamshed 		if (cur_stream->sndvar->nrtx > TCP_MAX_SYN_RETRY) {
26376404edcSAsim Jamshed 			cur_stream->state = TCP_ST_CLOSED_RSVD;
26476404edcSAsim Jamshed 			cur_stream->close_reason = TCP_CONN_FAIL;
26576404edcSAsim Jamshed 			cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE;
26676404edcSAsim Jamshed 			TRACE_RTO("Stream %d: SYN retries exceed maximum retries.\n",
26776404edcSAsim Jamshed 					cur_stream->id);
26876404edcSAsim Jamshed 			if (cur_stream->socket) {
26976404edcSAsim Jamshed 				RaiseErrorEvent(mtcp, cur_stream);
27076404edcSAsim Jamshed 			} else {
27176404edcSAsim Jamshed 				DestroyTCPStream(mtcp, cur_stream);
27276404edcSAsim Jamshed 			}
27376404edcSAsim Jamshed 
27476404edcSAsim Jamshed 			return 0;
27576404edcSAsim Jamshed 		}
27676404edcSAsim Jamshed 		TRACE_RTO("Stream %d Retransmit SYN. snd_nxt: %u, snd_una: %u\n",
27776404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
27876404edcSAsim Jamshed 
27976404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_SYN_RCVD) {
28076404edcSAsim Jamshed 		/* SYN/ACK lost */
28176404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit SYN/ACK. snd_nxt: %u, snd_una: %u\n",
28276404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
28376404edcSAsim Jamshed 
28476404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_ESTABLISHED) {
28576404edcSAsim Jamshed 		/* Data lost */
28676404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit data. snd_nxt: %u, snd_una: %u\n",
28776404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
28876404edcSAsim Jamshed 
28976404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_CLOSE_WAIT) {
29076404edcSAsim Jamshed 		/* Data lost */
29176404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit data. snd_nxt: %u, snd_una: %u\n",
29276404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
29376404edcSAsim Jamshed 
29476404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_LAST_ACK) {
29576404edcSAsim Jamshed 		/* FIN/ACK lost */
29676404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit FIN/ACK. "
29776404edcSAsim Jamshed 				"snd_nxt: %u, snd_una: %u\n",
29876404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
29976404edcSAsim Jamshed 
30076404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_FIN_WAIT_1) {
30176404edcSAsim Jamshed 		/* FIN lost */
30276404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit FIN. snd_nxt: %u, snd_una: %u\n",
30376404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
30476404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_CLOSING) {
30576404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit ACK. snd_nxt: %u, snd_una: %u\n",
30676404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
30776404edcSAsim Jamshed 		//TRACE_DBG("Stream %d: Retransmitting at CLOSING\n", cur_stream->id);
30876404edcSAsim Jamshed 
30976404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_FIN_WAIT_2) {
31076404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit ACK. snd_nxt: %u, snd_una: %u\n",
31176404edcSAsim Jamshed 			  cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
31276404edcSAsim Jamshed 	} else {
31376404edcSAsim Jamshed 		TRACE_ERROR("Stream %d: not implemented state! state: %s, rto: %u\n",
31476404edcSAsim Jamshed 				cur_stream->id,
31576404edcSAsim Jamshed 				TCPStateToString(cur_stream), cur_stream->sndvar->rto);
31676404edcSAsim Jamshed 		assert(0);
31776404edcSAsim Jamshed 		return 0;
31876404edcSAsim Jamshed 	}
31976404edcSAsim Jamshed 
32076404edcSAsim Jamshed 	cur_stream->snd_nxt = cur_stream->sndvar->snd_una;
32176404edcSAsim Jamshed 	if (cur_stream->state == TCP_ST_ESTABLISHED ||
32276404edcSAsim Jamshed 			cur_stream->state == TCP_ST_CLOSE_WAIT) {
32376404edcSAsim Jamshed 		/* retransmit data at ESTABLISHED state */
32476404edcSAsim Jamshed 		AddtoSendList(mtcp, cur_stream);
32576404edcSAsim Jamshed 
32676404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_FIN_WAIT_1 ||
32776404edcSAsim Jamshed 			cur_stream->state == TCP_ST_CLOSING ||
32876404edcSAsim Jamshed 			cur_stream->state == TCP_ST_LAST_ACK) {
32976404edcSAsim Jamshed 
33076404edcSAsim Jamshed 		if (cur_stream->sndvar->fss == 0) {
33176404edcSAsim Jamshed 			TRACE_ERROR("Stream %u: fss not set.\n", cur_stream->id);
33276404edcSAsim Jamshed 		}
33376404edcSAsim Jamshed 		/* decide to retransmit data or control packet */
33476404edcSAsim Jamshed 		if (TCP_SEQ_LT(cur_stream->snd_nxt, cur_stream->sndvar->fss)) {
33576404edcSAsim Jamshed 			/* need to retransmit data */
33676404edcSAsim Jamshed 			if (cur_stream->sndvar->on_control_list) {
33776404edcSAsim Jamshed 				RemoveFromControlList(mtcp, cur_stream);
33876404edcSAsim Jamshed 			}
33976404edcSAsim Jamshed 			cur_stream->control_list_waiting = TRUE;
34076404edcSAsim Jamshed 			AddtoSendList(mtcp, cur_stream);
34176404edcSAsim Jamshed 
34276404edcSAsim Jamshed 		} else {
34376404edcSAsim Jamshed 			/* need to retransmit control packet */
34476404edcSAsim Jamshed 			AddtoControlList(mtcp, cur_stream, cur_ts);
34576404edcSAsim Jamshed 		}
34676404edcSAsim Jamshed 
34776404edcSAsim Jamshed 	} else {
34876404edcSAsim Jamshed 		AddtoControlList(mtcp, cur_stream, cur_ts);
34976404edcSAsim Jamshed 	}
35076404edcSAsim Jamshed 
35176404edcSAsim Jamshed 	return 1;
35276404edcSAsim Jamshed }
35376404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
35476404edcSAsim Jamshed static inline void
RearrangeRTOStore(mtcp_manager_t mtcp)35576404edcSAsim Jamshed RearrangeRTOStore(mtcp_manager_t mtcp) {
35676404edcSAsim Jamshed 	tcp_stream *walk, *next;
35776404edcSAsim Jamshed 	struct rto_head* rto_list = &mtcp->rto_store->rto_list[RTO_HASH];
35876404edcSAsim Jamshed 	int cnt = 0;
35976404edcSAsim Jamshed 
36076404edcSAsim Jamshed 	for (walk = TAILQ_FIRST(rto_list);
36176404edcSAsim Jamshed 			walk != NULL; walk = next) {
36276404edcSAsim Jamshed 		next = TAILQ_NEXT(walk, sndvar->timer_link);
36376404edcSAsim Jamshed 
36476404edcSAsim Jamshed 		int diff = (int32_t)(mtcp->rto_store->rto_now_ts - walk->sndvar->ts_rto);
36576404edcSAsim Jamshed 		if (diff < RTO_HASH) {
36676404edcSAsim Jamshed 			//int offset = (diff + mtcp->rto_store->rto_now_idx) % RTO_HASH;
36776404edcSAsim Jamshed 			int offset = ((diff + mtcp->rto_store->rto_now_idx) & (RTO_HASH - 1));
36876404edcSAsim Jamshed 			if (!TAILQ_EMPTY(&mtcp->rto_store->rto_list[RTO_HASH])) {
36976404edcSAsim Jamshed 				TAILQ_REMOVE(&mtcp->rto_store->rto_list[RTO_HASH],
37076404edcSAsim Jamshed 					     walk, sndvar->timer_link);
37176404edcSAsim Jamshed 				walk->on_rto_idx = offset;
37276404edcSAsim Jamshed 				TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[offset]),
37376404edcSAsim Jamshed 						  walk, sndvar->timer_link);
37476404edcSAsim Jamshed 			}
37576404edcSAsim Jamshed 		}
37676404edcSAsim Jamshed 		cnt++;
37776404edcSAsim Jamshed 	}
37876404edcSAsim Jamshed }
37976404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
38076404edcSAsim Jamshed void
CheckRtmTimeout(mtcp_manager_t mtcp,uint32_t cur_ts,int thresh)38176404edcSAsim Jamshed CheckRtmTimeout(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh)
38276404edcSAsim Jamshed {
38376404edcSAsim Jamshed 	tcp_stream *walk, *next;
38476404edcSAsim Jamshed 	struct rto_head* rto_list;
38576404edcSAsim Jamshed 	int cnt;
38676404edcSAsim Jamshed 
38776404edcSAsim Jamshed 	if (!mtcp->rto_list_cnt) {
38876404edcSAsim Jamshed 		return;
38976404edcSAsim Jamshed 	}
39076404edcSAsim Jamshed 
39176404edcSAsim Jamshed 	STAT_COUNT(mtcp->runstat.rounds_rtocheck);
39276404edcSAsim Jamshed 
39376404edcSAsim Jamshed 	cnt = 0;
39476404edcSAsim Jamshed 
39576404edcSAsim Jamshed 	while (1) {
39676404edcSAsim Jamshed 
39776404edcSAsim Jamshed 		rto_list = &mtcp->rto_store->rto_list[mtcp->rto_store->rto_now_idx];
39876404edcSAsim Jamshed 		if ((int32_t)(cur_ts - mtcp->rto_store->rto_now_ts) < 0) {
39976404edcSAsim Jamshed 			break;
40076404edcSAsim Jamshed 		}
40176404edcSAsim Jamshed 
40276404edcSAsim Jamshed 		for (walk = TAILQ_FIRST(rto_list);
40376404edcSAsim Jamshed 				walk != NULL; walk = next) {
40476404edcSAsim Jamshed 			if (++cnt > thresh) {
40576404edcSAsim Jamshed 				break;
40676404edcSAsim Jamshed 			}
40776404edcSAsim Jamshed 			next = TAILQ_NEXT(walk, sndvar->timer_link);
40876404edcSAsim Jamshed 
40976404edcSAsim Jamshed 			TRACE_LOOP("Inside rto list. cnt: %u, stream: %d\n",
41076404edcSAsim Jamshed 					cnt, walk->s_id);
41176404edcSAsim Jamshed 
41276404edcSAsim Jamshed 			if (walk->on_rto_idx >= 0) {
41376404edcSAsim Jamshed 				TAILQ_REMOVE(rto_list, walk, sndvar->timer_link);
41476404edcSAsim Jamshed 				mtcp->rto_list_cnt--;
41576404edcSAsim Jamshed 				walk->on_rto_idx = -1;
41676404edcSAsim Jamshed 				HandleRTO(mtcp, cur_ts, walk);
41776404edcSAsim Jamshed 			} else {
41876404edcSAsim Jamshed 				TRACE_ERROR("Stream %d: not on rto list.\n", walk->id);
41976404edcSAsim Jamshed #ifdef DUMP_STREAM
42076404edcSAsim Jamshed 				DumpStream(mtcp, walk);
42176404edcSAsim Jamshed #endif
42276404edcSAsim Jamshed 			}
42376404edcSAsim Jamshed 		}
42476404edcSAsim Jamshed 
42576404edcSAsim Jamshed 		if (cnt > thresh) {
42676404edcSAsim Jamshed 			break;
42776404edcSAsim Jamshed 		} else {
42876404edcSAsim Jamshed 			mtcp->rto_store->rto_now_idx = ((mtcp->rto_store->rto_now_idx + 1) & (RTO_HASH - 1));
42976404edcSAsim Jamshed 			mtcp->rto_store->rto_now_ts++;
43076404edcSAsim Jamshed 			if (!((mtcp->rto_store->rto_now_idx & (1024 - 1)))) {
43176404edcSAsim Jamshed 				RearrangeRTOStore(mtcp);
43276404edcSAsim Jamshed 			}
43376404edcSAsim Jamshed 		}
43476404edcSAsim Jamshed 
43576404edcSAsim Jamshed 	}
43676404edcSAsim Jamshed 
43776404edcSAsim Jamshed 	TRACE_ROUND("Checking retransmission timeout. cnt: %d\n", cnt);
43876404edcSAsim Jamshed }
43976404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
44076404edcSAsim Jamshed void
CheckTimewaitExpire(mtcp_manager_t mtcp,uint32_t cur_ts,int thresh)44176404edcSAsim Jamshed CheckTimewaitExpire(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh)
44276404edcSAsim Jamshed {
44376404edcSAsim Jamshed 	tcp_stream *walk, *next;
44476404edcSAsim Jamshed 	int cnt;
44576404edcSAsim Jamshed 
44676404edcSAsim Jamshed 	STAT_COUNT(mtcp->runstat.rounds_twcheck);
44776404edcSAsim Jamshed 
44876404edcSAsim Jamshed 	cnt = 0;
44976404edcSAsim Jamshed 
45076404edcSAsim Jamshed 	for (walk = TAILQ_FIRST(&mtcp->timewait_list);
45176404edcSAsim Jamshed 				walk != NULL; walk = next) {
45276404edcSAsim Jamshed 		if (++cnt > thresh)
45376404edcSAsim Jamshed 			break;
45476404edcSAsim Jamshed 		next = TAILQ_NEXT(walk, sndvar->timer_link);
45576404edcSAsim Jamshed 
45676404edcSAsim Jamshed 		TRACE_LOOP("Inside timewait list. cnt: %u, stream: %d\n",
45776404edcSAsim Jamshed 				cnt, walk->s_id);
45876404edcSAsim Jamshed 
45976404edcSAsim Jamshed 		if (walk->on_timewait_list) {
4608c9e1184SAsim Jamshed 			/* wait until the other end is finished in monitoring socket */
4618c9e1184SAsim Jamshed 			if ((walk->pair_stream != NULL)
4628c9e1184SAsim Jamshed 			     && (walk->pair_stream->state != TCP_ST_CLOSED_RSVD)
4638c9e1184SAsim Jamshed 			     && (walk->pair_stream->state != TCP_ST_TIME_WAIT))
4648c9e1184SAsim Jamshed 				continue;
4658c9e1184SAsim Jamshed 
46676404edcSAsim Jamshed 			if ((int32_t)(cur_ts - walk->rcvvar->ts_tw_expire) >= 0) {
46776404edcSAsim Jamshed 				if (!walk->sndvar->on_control_list) {
46876404edcSAsim Jamshed 
46976404edcSAsim Jamshed 					TAILQ_REMOVE(&mtcp->timewait_list, walk, sndvar->timer_link);
47076404edcSAsim Jamshed 					walk->on_timewait_list = FALSE;
47176404edcSAsim Jamshed 					mtcp->timewait_list_cnt--;
47276404edcSAsim Jamshed 
47376404edcSAsim Jamshed 					walk->state = TCP_ST_CLOSED_RSVD;
47476404edcSAsim Jamshed 					walk->close_reason = TCP_ACTIVE_CLOSE;
47576404edcSAsim Jamshed 					walk->cb_events |= MOS_ON_TCP_STATE_CHANGE;
47676404edcSAsim Jamshed 					TRACE_STATE("Stream %d: TCP_ST_CLOSED_RSVD\n", walk->id);
47776404edcSAsim Jamshed 					DestroyTCPStream(mtcp, walk);
47876404edcSAsim Jamshed 				}
47976404edcSAsim Jamshed 			} else {
48076404edcSAsim Jamshed 				break;
48176404edcSAsim Jamshed 			}
48276404edcSAsim Jamshed 		} else {
48376404edcSAsim Jamshed 			TRACE_ERROR("Stream %d: not on timewait list.\n", walk->id);
48476404edcSAsim Jamshed #ifdef DUMP_STREAM
48576404edcSAsim Jamshed 			DumpStream(mtcp, walk);
48676404edcSAsim Jamshed #endif
48776404edcSAsim Jamshed 		}
48876404edcSAsim Jamshed 	}
48976404edcSAsim Jamshed 
49076404edcSAsim Jamshed 	TRACE_ROUND("Checking timewait timeout. cnt: %d\n", cnt);
49176404edcSAsim Jamshed }
49276404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
49376404edcSAsim Jamshed void
CheckConnectionTimeout(mtcp_manager_t mtcp,uint32_t cur_ts,int thresh)49476404edcSAsim Jamshed CheckConnectionTimeout(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh)
49576404edcSAsim Jamshed {
49676404edcSAsim Jamshed 	tcp_stream *walk, *next;
49776404edcSAsim Jamshed 	int cnt;
49876404edcSAsim Jamshed 
49976404edcSAsim Jamshed 	STAT_COUNT(mtcp->runstat.rounds_tocheck);
50076404edcSAsim Jamshed 
50176404edcSAsim Jamshed 	cnt = 0;
50276404edcSAsim Jamshed 	for (walk = TAILQ_FIRST(&mtcp->timeout_list);
50376404edcSAsim Jamshed 			walk != NULL; walk = next) {
50476404edcSAsim Jamshed 		if (++cnt > thresh)
50576404edcSAsim Jamshed 			break;
50676404edcSAsim Jamshed 		next = TAILQ_NEXT(walk, sndvar->timeout_link);
50776404edcSAsim Jamshed 
50876404edcSAsim Jamshed 		if ((int32_t)(cur_ts - walk->last_active_ts) >=
50976404edcSAsim Jamshed 				g_config.mos->tcp_timeout) {
51076404edcSAsim Jamshed 
511cafe7743SAsim Jamshed 			TRACE_DBG("stream-state: %s, streampair-state: %s\n",
51276404edcSAsim Jamshed 				  TCPStateToString(walk),
51376404edcSAsim Jamshed 				  TCPStateToString(walk->pair_stream));
51476404edcSAsim Jamshed 
51576404edcSAsim Jamshed 			walk->on_timeout_list = FALSE;
51676404edcSAsim Jamshed 			TAILQ_REMOVE(&mtcp->timeout_list, walk, sndvar->timeout_link);
51776404edcSAsim Jamshed 			mtcp->timeout_list_cnt--;
51876404edcSAsim Jamshed 			walk->state = TCP_ST_CLOSED_RSVD;
51976404edcSAsim Jamshed 			walk->close_reason = TCP_TIMEDOUT;
52076404edcSAsim Jamshed 			walk->cb_events |= MOS_ON_TCP_STATE_CHANGE;
52176404edcSAsim Jamshed 			if (walk->socket && HAS_STREAM_TYPE(walk, MOS_SOCK_STREAM)) {
52276404edcSAsim Jamshed 				RaiseErrorEvent(mtcp, walk);
52376404edcSAsim Jamshed 			} else {
52476404edcSAsim Jamshed 				DestroyTCPStream(mtcp, walk);
52576404edcSAsim Jamshed 			}
52676404edcSAsim Jamshed 		} else {
52776404edcSAsim Jamshed 			break;
52876404edcSAsim Jamshed 		}
52976404edcSAsim Jamshed 
53076404edcSAsim Jamshed 	}
53176404edcSAsim Jamshed }
53276404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
53376404edcSAsim Jamshed static int
RegTimer(mtcp_manager_t mtcp,struct timer * timer)53476404edcSAsim Jamshed RegTimer(mtcp_manager_t mtcp, struct timer *timer)
53576404edcSAsim Jamshed {
53676404edcSAsim Jamshed 	/* NOTE: This code assumes that the new timer expires later than existing
53776404edcSAsim Jamshed 	 * timers with high probability. */
53876404edcSAsim Jamshed 	struct timer *walk;
53976404edcSAsim Jamshed 
54076404edcSAsim Jamshed 	TAILQ_FOREACH_REVERSE(walk, &mtcp->timer_list, timer_head, timer_link) {
54176404edcSAsim Jamshed 		if (TIMEVAL_LT(&walk->exp, &timer->exp)) {
54276404edcSAsim Jamshed 			TAILQ_INSERT_AFTER(&mtcp->timer_list, walk, timer, timer_link);
54376404edcSAsim Jamshed 			return 0;
54476404edcSAsim Jamshed 		}
54576404edcSAsim Jamshed 	}
54676404edcSAsim Jamshed 
54776404edcSAsim Jamshed 	assert(!walk);
54876404edcSAsim Jamshed 
54976404edcSAsim Jamshed 	TAILQ_INSERT_HEAD(&mtcp->timer_list, timer, timer_link);
55076404edcSAsim Jamshed 	return 0;
55176404edcSAsim Jamshed }
55276404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
55376404edcSAsim Jamshed static struct timer *
NewTimer(mtcp_manager_t mtcp,int id,struct timeval * timeout,callback_t cb)55476404edcSAsim Jamshed NewTimer(mtcp_manager_t mtcp, int id, struct timeval *timeout, callback_t cb)
55576404edcSAsim Jamshed {
55676404edcSAsim Jamshed #ifdef USE_TIMER_POOL
55776404edcSAsim Jamshed 	struct timer *t = MPAllocateChunk(mtcp->timer_pool);
55876404edcSAsim Jamshed #else
55976404edcSAsim Jamshed 	struct timer *t = calloc(1, sizeof(struct timer));
56076404edcSAsim Jamshed #endif
56176404edcSAsim Jamshed 	if (!t)
56276404edcSAsim Jamshed 		return NULL;
56376404edcSAsim Jamshed 
56476404edcSAsim Jamshed 	t->id = id;
56576404edcSAsim Jamshed 	t->cb = cb;
56676404edcSAsim Jamshed 	gettimeofday(&t->exp, NULL);
56776404edcSAsim Jamshed 	TIMEVAL_ADD(&t->exp, timeout);
56876404edcSAsim Jamshed 
56976404edcSAsim Jamshed 	return t;
57076404edcSAsim Jamshed }
57176404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
57276404edcSAsim Jamshed void
DelTimer(mtcp_manager_t mtcp,struct timer * timer)57376404edcSAsim Jamshed DelTimer(mtcp_manager_t mtcp, struct timer *timer)
57476404edcSAsim Jamshed {
57576404edcSAsim Jamshed 	TAILQ_REMOVE(&mtcp->timer_list, timer, timer_link);
57676404edcSAsim Jamshed #ifdef USE_TIMER_POOL
57776404edcSAsim Jamshed 	MPFreeChunk(mtcp->timer_pool, timer);
57876404edcSAsim Jamshed #else
57976404edcSAsim Jamshed 	free(timer);
58076404edcSAsim Jamshed #endif
58176404edcSAsim Jamshed }
58276404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
58376404edcSAsim Jamshed int
mtcp_settimer(mctx_t mctx,int id,struct timeval * timeout,callback_t cb)58476404edcSAsim Jamshed mtcp_settimer(mctx_t mctx, int id, struct timeval *timeout, callback_t cb)
58576404edcSAsim Jamshed {
58676404edcSAsim Jamshed 	mtcp_manager_t mtcp = GetMTCPManager(mctx);
587*05e3289cSYoungGyoun 	if (!mtcp || !timeout || !cb)
58876404edcSAsim Jamshed 		return -1;
58976404edcSAsim Jamshed 
59076404edcSAsim Jamshed 	struct timer *t = NewTimer(mtcp, id, timeout, cb);
59176404edcSAsim Jamshed 	if (!t)
59276404edcSAsim Jamshed 		return -1;
59376404edcSAsim Jamshed 
59476404edcSAsim Jamshed 	RegTimer(mtcp, t);
59576404edcSAsim Jamshed 
59676404edcSAsim Jamshed 	return 0;
59776404edcSAsim Jamshed }
59876404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
599