xref: /mOS-networking-stack/core/src/timer.c (revision 76404edc)
1*76404edcSAsim Jamshed #include "timer.h"
2*76404edcSAsim Jamshed #include "tcp_in.h"
3*76404edcSAsim Jamshed #include "tcp_out.h"
4*76404edcSAsim Jamshed #include "stat.h"
5*76404edcSAsim Jamshed #include "debug.h"
6*76404edcSAsim Jamshed #include "memory_mgt.h"
7*76404edcSAsim Jamshed #include "config.h"
8*76404edcSAsim Jamshed 
9*76404edcSAsim Jamshed #define MAX(a, b) ((a)>(b)?(a):(b))
10*76404edcSAsim Jamshed #define MIN(a, b) ((a)<(b)?(a):(b))
11*76404edcSAsim Jamshed 
12*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
13*76404edcSAsim Jamshed struct rto_hashstore*
14*76404edcSAsim Jamshed InitRTOHashstore()
15*76404edcSAsim Jamshed {
16*76404edcSAsim Jamshed 	int i;
17*76404edcSAsim Jamshed 	struct rto_hashstore* hs = calloc(1, sizeof(struct rto_hashstore));
18*76404edcSAsim Jamshed 	if (!hs) {
19*76404edcSAsim Jamshed 		TRACE_ERROR("calloc: InitHashStore");
20*76404edcSAsim Jamshed 		return 0;
21*76404edcSAsim Jamshed 	}
22*76404edcSAsim Jamshed 
23*76404edcSAsim Jamshed 	for (i = 0; i < RTO_HASH; i++)
24*76404edcSAsim Jamshed 		TAILQ_INIT(&hs->rto_list[i]);
25*76404edcSAsim Jamshed 
26*76404edcSAsim Jamshed 	TAILQ_INIT(&hs->rto_list[RTO_HASH]);
27*76404edcSAsim Jamshed 
28*76404edcSAsim Jamshed 	return hs;
29*76404edcSAsim Jamshed }
30*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
31*76404edcSAsim Jamshed inline void
32*76404edcSAsim Jamshed AddtoRTOList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
33*76404edcSAsim Jamshed {
34*76404edcSAsim Jamshed 	if (!mtcp->rto_list_cnt) {
35*76404edcSAsim Jamshed 		mtcp->rto_store->rto_now_idx = 0;
36*76404edcSAsim Jamshed 		mtcp->rto_store->rto_now_ts = cur_stream->sndvar->ts_rto;
37*76404edcSAsim Jamshed 	}
38*76404edcSAsim Jamshed 
39*76404edcSAsim Jamshed 	if (cur_stream->on_rto_idx < 0 ) {
40*76404edcSAsim Jamshed 		if (cur_stream->on_timewait_list) {
41*76404edcSAsim Jamshed 			TRACE_ERROR("Stream %u: cannot be in both "
42*76404edcSAsim Jamshed 					"rto and timewait list.\n", cur_stream->id);
43*76404edcSAsim Jamshed #ifdef DUMP_STREAM
44*76404edcSAsim Jamshed 			DumpStream(mtcp, cur_stream);
45*76404edcSAsim Jamshed #endif
46*76404edcSAsim Jamshed 			return;
47*76404edcSAsim Jamshed 		}
48*76404edcSAsim Jamshed 
49*76404edcSAsim Jamshed 		int diff = (int32_t)(cur_stream->sndvar->ts_rto - mtcp->rto_store->rto_now_ts);
50*76404edcSAsim Jamshed #if 0
51*76404edcSAsim Jamshed 		if (diff < RTO_HASH) {
52*76404edcSAsim Jamshed #else
53*76404edcSAsim Jamshed 			int offset= ((diff + mtcp->rto_store->rto_now_idx) & (RTO_HASH - 1));
54*76404edcSAsim Jamshed 			cur_stream->on_rto_idx = offset;
55*76404edcSAsim Jamshed 			TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[offset]),
56*76404edcSAsim Jamshed 					cur_stream, sndvar->timer_link);
57*76404edcSAsim Jamshed #endif
58*76404edcSAsim Jamshed #if 0
59*76404edcSAsim Jamshed 		} else {
60*76404edcSAsim Jamshed 			cur_stream->on_rto_idx = RTO_HASH;
61*76404edcSAsim Jamshed 			TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[RTO_HASH]),
62*76404edcSAsim Jamshed 					cur_stream, sndvar->timer_link);
63*76404edcSAsim Jamshed 		}
64*76404edcSAsim Jamshed #endif
65*76404edcSAsim Jamshed 		mtcp->rto_list_cnt++;
66*76404edcSAsim Jamshed 	}
67*76404edcSAsim Jamshed }
68*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
69*76404edcSAsim Jamshed inline void
70*76404edcSAsim Jamshed RemoveFromRTOList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
71*76404edcSAsim Jamshed {
72*76404edcSAsim Jamshed 	if (cur_stream->on_rto_idx < 0) {
73*76404edcSAsim Jamshed //		assert(0);
74*76404edcSAsim Jamshed 		return;
75*76404edcSAsim Jamshed 	}
76*76404edcSAsim Jamshed 
77*76404edcSAsim Jamshed 	TAILQ_REMOVE(&mtcp->rto_store->rto_list[cur_stream->on_rto_idx],
78*76404edcSAsim Jamshed 			cur_stream, sndvar->timer_link);
79*76404edcSAsim Jamshed 	cur_stream->on_rto_idx = -1;
80*76404edcSAsim Jamshed 
81*76404edcSAsim Jamshed 	mtcp->rto_list_cnt--;
82*76404edcSAsim Jamshed }
83*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
84*76404edcSAsim Jamshed inline void
85*76404edcSAsim Jamshed AddtoTimewaitList(mtcp_manager_t mtcp, tcp_stream *cur_stream, uint32_t cur_ts)
86*76404edcSAsim Jamshed {
87*76404edcSAsim Jamshed 	cur_stream->rcvvar->ts_tw_expire = cur_ts + g_config.mos->tcp_tw_interval;
88*76404edcSAsim Jamshed 
89*76404edcSAsim Jamshed 	if (cur_stream->on_timewait_list) {
90*76404edcSAsim Jamshed 		// Update list in sorted way by ts_tw_expire
91*76404edcSAsim Jamshed 		TAILQ_REMOVE(&mtcp->timewait_list, cur_stream, sndvar->timer_link);
92*76404edcSAsim Jamshed 		TAILQ_INSERT_TAIL(&mtcp->timewait_list, cur_stream, sndvar->timer_link);
93*76404edcSAsim Jamshed 	} else {
94*76404edcSAsim Jamshed 		if (cur_stream->on_rto_idx >= 0) {
95*76404edcSAsim Jamshed 			TRACE_DBG("Stream %u: cannot be in both "
96*76404edcSAsim Jamshed 					"timewait and rto list.\n", cur_stream->id);
97*76404edcSAsim Jamshed 			//assert(0);
98*76404edcSAsim Jamshed #ifdef DUMP_STREAM
99*76404edcSAsim Jamshed 			DumpStream(mtcp, cur_stream);
100*76404edcSAsim Jamshed #endif
101*76404edcSAsim Jamshed 			RemoveFromRTOList(mtcp, cur_stream);
102*76404edcSAsim Jamshed 		}
103*76404edcSAsim Jamshed 
104*76404edcSAsim Jamshed 		cur_stream->on_timewait_list = TRUE;
105*76404edcSAsim Jamshed 		TAILQ_INSERT_TAIL(&mtcp->timewait_list, cur_stream, sndvar->timer_link);
106*76404edcSAsim Jamshed 		mtcp->timewait_list_cnt++;
107*76404edcSAsim Jamshed 	}
108*76404edcSAsim Jamshed }
109*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
110*76404edcSAsim Jamshed inline void
111*76404edcSAsim Jamshed RemoveFromTimewaitList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
112*76404edcSAsim Jamshed {
113*76404edcSAsim Jamshed 	if (!cur_stream->on_timewait_list) {
114*76404edcSAsim Jamshed 		//assert(0);
115*76404edcSAsim Jamshed 		return;
116*76404edcSAsim Jamshed 	}
117*76404edcSAsim Jamshed 
118*76404edcSAsim Jamshed 	TAILQ_REMOVE(&mtcp->timewait_list, cur_stream, sndvar->timer_link);
119*76404edcSAsim Jamshed 	cur_stream->on_timewait_list = FALSE;
120*76404edcSAsim Jamshed 	mtcp->timewait_list_cnt--;
121*76404edcSAsim Jamshed }
122*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
123*76404edcSAsim Jamshed inline void
124*76404edcSAsim Jamshed AddtoTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
125*76404edcSAsim Jamshed {
126*76404edcSAsim Jamshed 	if (cur_stream->on_timeout_list) {
127*76404edcSAsim Jamshed 		//assert(0);
128*76404edcSAsim Jamshed 		return;
129*76404edcSAsim Jamshed 	}
130*76404edcSAsim Jamshed 
131*76404edcSAsim Jamshed 	cur_stream->on_timeout_list = TRUE;
132*76404edcSAsim Jamshed 	TAILQ_INSERT_TAIL(&mtcp->timeout_list, cur_stream, sndvar->timeout_link);
133*76404edcSAsim Jamshed 	mtcp->timeout_list_cnt++;
134*76404edcSAsim Jamshed }
135*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
136*76404edcSAsim Jamshed inline void
137*76404edcSAsim Jamshed RemoveFromTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
138*76404edcSAsim Jamshed {
139*76404edcSAsim Jamshed 	if (cur_stream->on_timeout_list) {
140*76404edcSAsim Jamshed 		cur_stream->on_timeout_list = FALSE;
141*76404edcSAsim Jamshed 		TAILQ_REMOVE(&mtcp->timeout_list, cur_stream, sndvar->timeout_link);
142*76404edcSAsim Jamshed 		mtcp->timeout_list_cnt--;
143*76404edcSAsim Jamshed 	}
144*76404edcSAsim Jamshed }
145*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
146*76404edcSAsim Jamshed inline void
147*76404edcSAsim Jamshed UpdateTimeoutList(mtcp_manager_t mtcp, tcp_stream *cur_stream)
148*76404edcSAsim Jamshed {
149*76404edcSAsim Jamshed 	if (cur_stream->on_timeout_list) {
150*76404edcSAsim Jamshed 		TAILQ_REMOVE(&mtcp->timeout_list, cur_stream, sndvar->timeout_link);
151*76404edcSAsim Jamshed 		TAILQ_INSERT_TAIL(&mtcp->timeout_list, cur_stream, sndvar->timeout_link);
152*76404edcSAsim Jamshed 	}
153*76404edcSAsim Jamshed }
154*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
155*76404edcSAsim Jamshed inline void
156*76404edcSAsim Jamshed UpdateRetransmissionTimer(mtcp_manager_t mtcp,
157*76404edcSAsim Jamshed 		tcp_stream *cur_stream, uint32_t cur_ts)
158*76404edcSAsim Jamshed {
159*76404edcSAsim Jamshed 	/* Update the retransmission timer */
160*76404edcSAsim Jamshed 	assert(cur_stream->sndvar->rto > 0);
161*76404edcSAsim Jamshed 	cur_stream->sndvar->nrtx = 0;
162*76404edcSAsim Jamshed 
163*76404edcSAsim Jamshed 	/* if in rto list, remove it */
164*76404edcSAsim Jamshed 	if (cur_stream->on_rto_idx >= 0) {
165*76404edcSAsim Jamshed 		RemoveFromRTOList(mtcp, cur_stream);
166*76404edcSAsim Jamshed 	}
167*76404edcSAsim Jamshed 
168*76404edcSAsim Jamshed 	/* Reset retransmission timeout */
169*76404edcSAsim Jamshed 	if (TCP_SEQ_GT(cur_stream->snd_nxt, cur_stream->sndvar->snd_una)) {
170*76404edcSAsim Jamshed 		/* there are packets sent but not acked */
171*76404edcSAsim Jamshed 		/* update rto timestamp */
172*76404edcSAsim Jamshed 		cur_stream->sndvar->ts_rto = cur_ts + cur_stream->sndvar->rto;
173*76404edcSAsim Jamshed 		AddtoRTOList(mtcp, cur_stream);
174*76404edcSAsim Jamshed 
175*76404edcSAsim Jamshed 	} else {
176*76404edcSAsim Jamshed 		/* all packets are acked */
177*76404edcSAsim Jamshed 		TRACE_RTO("All packets are acked. snd_una: %u, snd_nxt: %u\n",
178*76404edcSAsim Jamshed 				cur_stream->sndvar->snd_una, cur_stream->snd_nxt);
179*76404edcSAsim Jamshed 	}
180*76404edcSAsim Jamshed }
181*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
182*76404edcSAsim Jamshed static inline int
183*76404edcSAsim Jamshed HandleRTO(mtcp_manager_t mtcp, uint32_t cur_ts, tcp_stream *cur_stream)
184*76404edcSAsim Jamshed {
185*76404edcSAsim Jamshed 	uint8_t backoff;
186*76404edcSAsim Jamshed 
187*76404edcSAsim Jamshed 	TRACE_RTO("Stream %d Timeout! rto: %u (%ums), snd_una: %u, snd_nxt: %u\n",
188*76404edcSAsim Jamshed 			cur_stream->id, cur_stream->sndvar->rto, TS_TO_MSEC(cur_stream->sndvar->rto),
189*76404edcSAsim Jamshed 			cur_stream->sndvar->snd_una, cur_stream->snd_nxt);
190*76404edcSAsim Jamshed 	assert(cur_stream->sndvar->rto > 0);
191*76404edcSAsim Jamshed 
192*76404edcSAsim Jamshed 	/* count number of retransmissions */
193*76404edcSAsim Jamshed 	if (cur_stream->sndvar->nrtx < TCP_MAX_RTX) {
194*76404edcSAsim Jamshed 		cur_stream->sndvar->nrtx++;
195*76404edcSAsim Jamshed 	} else {
196*76404edcSAsim Jamshed 		/* if it exceeds the threshold, destroy and notify to application */
197*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Exceed MAX_RTX\n", cur_stream->id);
198*76404edcSAsim Jamshed 		if (cur_stream->state < TCP_ST_ESTABLISHED) {
199*76404edcSAsim Jamshed 			cur_stream->state = TCP_ST_CLOSED_RSVD;
200*76404edcSAsim Jamshed 			cur_stream->close_reason = TCP_CONN_FAIL;
201*76404edcSAsim Jamshed 			cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE;
202*76404edcSAsim Jamshed 			DestroyTCPStream(mtcp, cur_stream);
203*76404edcSAsim Jamshed 		} else {
204*76404edcSAsim Jamshed 			cur_stream->state = TCP_ST_CLOSED_RSVD;
205*76404edcSAsim Jamshed 			cur_stream->close_reason = TCP_CONN_LOST;
206*76404edcSAsim Jamshed 			cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE;
207*76404edcSAsim Jamshed 			if (cur_stream->socket) {
208*76404edcSAsim Jamshed 				RaiseErrorEvent(mtcp, cur_stream);
209*76404edcSAsim Jamshed 			} else {
210*76404edcSAsim Jamshed 				DestroyTCPStream(mtcp, cur_stream);
211*76404edcSAsim Jamshed 			}
212*76404edcSAsim Jamshed 		}
213*76404edcSAsim Jamshed 		return 0;
214*76404edcSAsim Jamshed 	}
215*76404edcSAsim Jamshed 	if (cur_stream->sndvar->nrtx > cur_stream->sndvar->max_nrtx) {
216*76404edcSAsim Jamshed 		cur_stream->sndvar->max_nrtx = cur_stream->sndvar->nrtx;
217*76404edcSAsim Jamshed 	}
218*76404edcSAsim Jamshed 
219*76404edcSAsim Jamshed 	/* update rto timestamp */
220*76404edcSAsim Jamshed 	if (cur_stream->state >= TCP_ST_ESTABLISHED) {
221*76404edcSAsim Jamshed 		uint32_t rto_prev;
222*76404edcSAsim Jamshed 		backoff = MIN(cur_stream->sndvar->nrtx, TCP_MAX_BACKOFF);
223*76404edcSAsim Jamshed 
224*76404edcSAsim Jamshed 		rto_prev = cur_stream->sndvar->rto;
225*76404edcSAsim Jamshed 		cur_stream->sndvar->rto =
226*76404edcSAsim Jamshed 				((cur_stream->rcvvar->srtt >> 3) + cur_stream->rcvvar->rttvar) << backoff;
227*76404edcSAsim Jamshed 		if (cur_stream->sndvar->rto <= 0) {
228*76404edcSAsim Jamshed 			TRACE_RTO("Stream %d current rto: %u, prev: %u, state: %s\n",
229*76404edcSAsim Jamshed 					cur_stream->id, cur_stream->sndvar->rto, rto_prev,
230*76404edcSAsim Jamshed 					TCPStateToString(cur_stream));
231*76404edcSAsim Jamshed 			cur_stream->sndvar->rto = rto_prev;
232*76404edcSAsim Jamshed 		}
233*76404edcSAsim Jamshed 	} else if (cur_stream->state >= TCP_ST_SYN_SENT) {
234*76404edcSAsim Jamshed 		/* if there is no rtt measured, update rto based on the previous one */
235*76404edcSAsim Jamshed 		if (cur_stream->sndvar->nrtx < TCP_MAX_BACKOFF) {
236*76404edcSAsim Jamshed 			cur_stream->sndvar->rto <<= 1;
237*76404edcSAsim Jamshed 		}
238*76404edcSAsim Jamshed 	}
239*76404edcSAsim Jamshed 	//cur_stream->sndvar->ts_rto = cur_ts + cur_stream->sndvar->rto;
240*76404edcSAsim Jamshed 
241*76404edcSAsim Jamshed 	/* reduce congestion window and ssthresh */
242*76404edcSAsim Jamshed 	cur_stream->sndvar->ssthresh = MIN(cur_stream->sndvar->cwnd, cur_stream->sndvar->peer_wnd) / 2;
243*76404edcSAsim Jamshed 	if (cur_stream->sndvar->ssthresh < (2 * cur_stream->sndvar->mss)) {
244*76404edcSAsim Jamshed 		cur_stream->sndvar->ssthresh = cur_stream->sndvar->mss * 2;
245*76404edcSAsim Jamshed 	}
246*76404edcSAsim Jamshed 	cur_stream->sndvar->cwnd = cur_stream->sndvar->mss;
247*76404edcSAsim Jamshed 	TRACE_CONG("Stream %d Timeout. cwnd: %u, ssthresh: %u\n",
248*76404edcSAsim Jamshed 			cur_stream->id, cur_stream->sndvar->cwnd, cur_stream->sndvar->ssthresh);
249*76404edcSAsim Jamshed 
250*76404edcSAsim Jamshed #if RTM_STAT
251*76404edcSAsim Jamshed 	/* update retransmission stats */
252*76404edcSAsim Jamshed 	cur_stream->sndvar->rstat.rto_cnt++;
253*76404edcSAsim Jamshed 	cur_stream->sndvar->rstat.rto_bytes += (cur_stream->snd_nxt - cur_stream->sndvar->snd_una);
254*76404edcSAsim Jamshed #endif
255*76404edcSAsim Jamshed 
256*76404edcSAsim Jamshed 	if (cur_stream->on_rto_idx >= 0)
257*76404edcSAsim Jamshed 		RemoveFromRTOList(mtcp, cur_stream);
258*76404edcSAsim Jamshed 
259*76404edcSAsim Jamshed 	/* Retransmission */
260*76404edcSAsim Jamshed 	if (cur_stream->state == TCP_ST_SYN_SENT) {
261*76404edcSAsim Jamshed 		/* SYN lost */
262*76404edcSAsim Jamshed 		if (cur_stream->sndvar->nrtx > TCP_MAX_SYN_RETRY) {
263*76404edcSAsim Jamshed 			cur_stream->state = TCP_ST_CLOSED_RSVD;
264*76404edcSAsim Jamshed 			cur_stream->close_reason = TCP_CONN_FAIL;
265*76404edcSAsim Jamshed 			cur_stream->cb_events |= MOS_ON_TCP_STATE_CHANGE;
266*76404edcSAsim Jamshed 			TRACE_RTO("Stream %d: SYN retries exceed maximum retries.\n",
267*76404edcSAsim Jamshed 					cur_stream->id);
268*76404edcSAsim Jamshed 			if (cur_stream->socket) {
269*76404edcSAsim Jamshed 				RaiseErrorEvent(mtcp, cur_stream);
270*76404edcSAsim Jamshed 			} else {
271*76404edcSAsim Jamshed 				DestroyTCPStream(mtcp, cur_stream);
272*76404edcSAsim Jamshed 			}
273*76404edcSAsim Jamshed 
274*76404edcSAsim Jamshed 			return 0;
275*76404edcSAsim Jamshed 		}
276*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d Retransmit SYN. snd_nxt: %u, snd_una: %u\n",
277*76404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
278*76404edcSAsim Jamshed 
279*76404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_SYN_RCVD) {
280*76404edcSAsim Jamshed 		/* SYN/ACK lost */
281*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit SYN/ACK. snd_nxt: %u, snd_una: %u\n",
282*76404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
283*76404edcSAsim Jamshed 
284*76404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_ESTABLISHED) {
285*76404edcSAsim Jamshed 		/* Data lost */
286*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit data. snd_nxt: %u, snd_una: %u\n",
287*76404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
288*76404edcSAsim Jamshed 
289*76404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_CLOSE_WAIT) {
290*76404edcSAsim Jamshed 		/* Data lost */
291*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit data. snd_nxt: %u, snd_una: %u\n",
292*76404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
293*76404edcSAsim Jamshed 
294*76404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_LAST_ACK) {
295*76404edcSAsim Jamshed 		/* FIN/ACK lost */
296*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit FIN/ACK. "
297*76404edcSAsim Jamshed 				"snd_nxt: %u, snd_una: %u\n",
298*76404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
299*76404edcSAsim Jamshed 
300*76404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_FIN_WAIT_1) {
301*76404edcSAsim Jamshed 		/* FIN lost */
302*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit FIN. snd_nxt: %u, snd_una: %u\n",
303*76404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
304*76404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_CLOSING) {
305*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit ACK. snd_nxt: %u, snd_una: %u\n",
306*76404edcSAsim Jamshed 				cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
307*76404edcSAsim Jamshed 		//TRACE_DBG("Stream %d: Retransmitting at CLOSING\n", cur_stream->id);
308*76404edcSAsim Jamshed 
309*76404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_FIN_WAIT_2) {
310*76404edcSAsim Jamshed 		TRACE_RTO("Stream %d: Retransmit ACK. snd_nxt: %u, snd_una: %u\n",
311*76404edcSAsim Jamshed 			  cur_stream->id, cur_stream->snd_nxt, cur_stream->sndvar->snd_una);
312*76404edcSAsim Jamshed 	} else {
313*76404edcSAsim Jamshed 		TRACE_ERROR("Stream %d: not implemented state! state: %s, rto: %u\n",
314*76404edcSAsim Jamshed 				cur_stream->id,
315*76404edcSAsim Jamshed 				TCPStateToString(cur_stream), cur_stream->sndvar->rto);
316*76404edcSAsim Jamshed 		assert(0);
317*76404edcSAsim Jamshed 		return 0;
318*76404edcSAsim Jamshed 	}
319*76404edcSAsim Jamshed 
320*76404edcSAsim Jamshed 	cur_stream->snd_nxt = cur_stream->sndvar->snd_una;
321*76404edcSAsim Jamshed 	if (cur_stream->state == TCP_ST_ESTABLISHED ||
322*76404edcSAsim Jamshed 			cur_stream->state == TCP_ST_CLOSE_WAIT) {
323*76404edcSAsim Jamshed 		/* retransmit data at ESTABLISHED state */
324*76404edcSAsim Jamshed 		AddtoSendList(mtcp, cur_stream);
325*76404edcSAsim Jamshed 
326*76404edcSAsim Jamshed 	} else if (cur_stream->state == TCP_ST_FIN_WAIT_1 ||
327*76404edcSAsim Jamshed 			cur_stream->state == TCP_ST_CLOSING ||
328*76404edcSAsim Jamshed 			cur_stream->state == TCP_ST_LAST_ACK) {
329*76404edcSAsim Jamshed 
330*76404edcSAsim Jamshed 		if (cur_stream->sndvar->fss == 0) {
331*76404edcSAsim Jamshed 			TRACE_ERROR("Stream %u: fss not set.\n", cur_stream->id);
332*76404edcSAsim Jamshed 		}
333*76404edcSAsim Jamshed 		/* decide to retransmit data or control packet */
334*76404edcSAsim Jamshed 		if (TCP_SEQ_LT(cur_stream->snd_nxt, cur_stream->sndvar->fss)) {
335*76404edcSAsim Jamshed 			/* need to retransmit data */
336*76404edcSAsim Jamshed 			if (cur_stream->sndvar->on_control_list) {
337*76404edcSAsim Jamshed 				RemoveFromControlList(mtcp, cur_stream);
338*76404edcSAsim Jamshed 			}
339*76404edcSAsim Jamshed 			cur_stream->control_list_waiting = TRUE;
340*76404edcSAsim Jamshed 			AddtoSendList(mtcp, cur_stream);
341*76404edcSAsim Jamshed 
342*76404edcSAsim Jamshed 		} else {
343*76404edcSAsim Jamshed 			/* need to retransmit control packet */
344*76404edcSAsim Jamshed 			AddtoControlList(mtcp, cur_stream, cur_ts);
345*76404edcSAsim Jamshed 		}
346*76404edcSAsim Jamshed 
347*76404edcSAsim Jamshed 	} else {
348*76404edcSAsim Jamshed 		AddtoControlList(mtcp, cur_stream, cur_ts);
349*76404edcSAsim Jamshed 	}
350*76404edcSAsim Jamshed 
351*76404edcSAsim Jamshed 	return 1;
352*76404edcSAsim Jamshed }
353*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
354*76404edcSAsim Jamshed static inline void
355*76404edcSAsim Jamshed RearrangeRTOStore(mtcp_manager_t mtcp) {
356*76404edcSAsim Jamshed 	tcp_stream *walk, *next;
357*76404edcSAsim Jamshed 	struct rto_head* rto_list = &mtcp->rto_store->rto_list[RTO_HASH];
358*76404edcSAsim Jamshed 	int cnt = 0;
359*76404edcSAsim Jamshed 
360*76404edcSAsim Jamshed 	for (walk = TAILQ_FIRST(rto_list);
361*76404edcSAsim Jamshed 			walk != NULL; walk = next) {
362*76404edcSAsim Jamshed 		next = TAILQ_NEXT(walk, sndvar->timer_link);
363*76404edcSAsim Jamshed 
364*76404edcSAsim Jamshed 		int diff = (int32_t)(mtcp->rto_store->rto_now_ts - walk->sndvar->ts_rto);
365*76404edcSAsim Jamshed 		if (diff < RTO_HASH) {
366*76404edcSAsim Jamshed 			//int offset = (diff + mtcp->rto_store->rto_now_idx) % RTO_HASH;
367*76404edcSAsim Jamshed 			int offset = ((diff + mtcp->rto_store->rto_now_idx) & (RTO_HASH - 1));
368*76404edcSAsim Jamshed 			if (!TAILQ_EMPTY(&mtcp->rto_store->rto_list[RTO_HASH])) {
369*76404edcSAsim Jamshed 				TAILQ_REMOVE(&mtcp->rto_store->rto_list[RTO_HASH],
370*76404edcSAsim Jamshed 					     walk, sndvar->timer_link);
371*76404edcSAsim Jamshed 				walk->on_rto_idx = offset;
372*76404edcSAsim Jamshed 				TAILQ_INSERT_TAIL(&(mtcp->rto_store->rto_list[offset]),
373*76404edcSAsim Jamshed 						  walk, sndvar->timer_link);
374*76404edcSAsim Jamshed 			}
375*76404edcSAsim Jamshed 		}
376*76404edcSAsim Jamshed 		cnt++;
377*76404edcSAsim Jamshed 	}
378*76404edcSAsim Jamshed }
379*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
380*76404edcSAsim Jamshed void
381*76404edcSAsim Jamshed CheckRtmTimeout(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh)
382*76404edcSAsim Jamshed {
383*76404edcSAsim Jamshed 	tcp_stream *walk, *next;
384*76404edcSAsim Jamshed 	struct rto_head* rto_list;
385*76404edcSAsim Jamshed 	int cnt;
386*76404edcSAsim Jamshed 
387*76404edcSAsim Jamshed 	if (!mtcp->rto_list_cnt) {
388*76404edcSAsim Jamshed 		return;
389*76404edcSAsim Jamshed 	}
390*76404edcSAsim Jamshed 
391*76404edcSAsim Jamshed 	STAT_COUNT(mtcp->runstat.rounds_rtocheck);
392*76404edcSAsim Jamshed 
393*76404edcSAsim Jamshed 	cnt = 0;
394*76404edcSAsim Jamshed 
395*76404edcSAsim Jamshed 	while (1) {
396*76404edcSAsim Jamshed 
397*76404edcSAsim Jamshed 		rto_list = &mtcp->rto_store->rto_list[mtcp->rto_store->rto_now_idx];
398*76404edcSAsim Jamshed 		if ((int32_t)(cur_ts - mtcp->rto_store->rto_now_ts) < 0) {
399*76404edcSAsim Jamshed 			break;
400*76404edcSAsim Jamshed 		}
401*76404edcSAsim Jamshed 
402*76404edcSAsim Jamshed 		for (walk = TAILQ_FIRST(rto_list);
403*76404edcSAsim Jamshed 				walk != NULL; walk = next) {
404*76404edcSAsim Jamshed 			if (++cnt > thresh) {
405*76404edcSAsim Jamshed 				break;
406*76404edcSAsim Jamshed 			}
407*76404edcSAsim Jamshed 			next = TAILQ_NEXT(walk, sndvar->timer_link);
408*76404edcSAsim Jamshed 
409*76404edcSAsim Jamshed 			TRACE_LOOP("Inside rto list. cnt: %u, stream: %d\n",
410*76404edcSAsim Jamshed 					cnt, walk->s_id);
411*76404edcSAsim Jamshed 
412*76404edcSAsim Jamshed 			if (walk->on_rto_idx >= 0) {
413*76404edcSAsim Jamshed 				TAILQ_REMOVE(rto_list, walk, sndvar->timer_link);
414*76404edcSAsim Jamshed 				mtcp->rto_list_cnt--;
415*76404edcSAsim Jamshed 				walk->on_rto_idx = -1;
416*76404edcSAsim Jamshed 				HandleRTO(mtcp, cur_ts, walk);
417*76404edcSAsim Jamshed 			} else {
418*76404edcSAsim Jamshed 				TRACE_ERROR("Stream %d: not on rto list.\n", walk->id);
419*76404edcSAsim Jamshed #ifdef DUMP_STREAM
420*76404edcSAsim Jamshed 				DumpStream(mtcp, walk);
421*76404edcSAsim Jamshed #endif
422*76404edcSAsim Jamshed 			}
423*76404edcSAsim Jamshed 		}
424*76404edcSAsim Jamshed 
425*76404edcSAsim Jamshed 		if (cnt > thresh) {
426*76404edcSAsim Jamshed 			break;
427*76404edcSAsim Jamshed 		} else {
428*76404edcSAsim Jamshed 			mtcp->rto_store->rto_now_idx = ((mtcp->rto_store->rto_now_idx + 1) & (RTO_HASH - 1));
429*76404edcSAsim Jamshed 			mtcp->rto_store->rto_now_ts++;
430*76404edcSAsim Jamshed 			if (!((mtcp->rto_store->rto_now_idx & (1024 - 1)))) {
431*76404edcSAsim Jamshed 				RearrangeRTOStore(mtcp);
432*76404edcSAsim Jamshed 			}
433*76404edcSAsim Jamshed 		}
434*76404edcSAsim Jamshed 
435*76404edcSAsim Jamshed 	}
436*76404edcSAsim Jamshed 
437*76404edcSAsim Jamshed 	TRACE_ROUND("Checking retransmission timeout. cnt: %d\n", cnt);
438*76404edcSAsim Jamshed }
439*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
440*76404edcSAsim Jamshed void
441*76404edcSAsim Jamshed CheckTimewaitExpire(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh)
442*76404edcSAsim Jamshed {
443*76404edcSAsim Jamshed 	tcp_stream *walk, *next;
444*76404edcSAsim Jamshed 	int cnt;
445*76404edcSAsim Jamshed 
446*76404edcSAsim Jamshed 	STAT_COUNT(mtcp->runstat.rounds_twcheck);
447*76404edcSAsim Jamshed 
448*76404edcSAsim Jamshed 	cnt = 0;
449*76404edcSAsim Jamshed 
450*76404edcSAsim Jamshed 	for (walk = TAILQ_FIRST(&mtcp->timewait_list);
451*76404edcSAsim Jamshed 				walk != NULL; walk = next) {
452*76404edcSAsim Jamshed 		if (++cnt > thresh)
453*76404edcSAsim Jamshed 			break;
454*76404edcSAsim Jamshed 		next = TAILQ_NEXT(walk, sndvar->timer_link);
455*76404edcSAsim Jamshed 
456*76404edcSAsim Jamshed 		TRACE_LOOP("Inside timewait list. cnt: %u, stream: %d\n",
457*76404edcSAsim Jamshed 				cnt, walk->s_id);
458*76404edcSAsim Jamshed 
459*76404edcSAsim Jamshed 		if (walk->on_timewait_list) {
460*76404edcSAsim Jamshed 			if ((int32_t)(cur_ts - walk->rcvvar->ts_tw_expire) >= 0) {
461*76404edcSAsim Jamshed 				if (!walk->sndvar->on_control_list) {
462*76404edcSAsim Jamshed 
463*76404edcSAsim Jamshed 					TAILQ_REMOVE(&mtcp->timewait_list, walk, sndvar->timer_link);
464*76404edcSAsim Jamshed 					walk->on_timewait_list = FALSE;
465*76404edcSAsim Jamshed 					mtcp->timewait_list_cnt--;
466*76404edcSAsim Jamshed 
467*76404edcSAsim Jamshed 					walk->state = TCP_ST_CLOSED_RSVD;
468*76404edcSAsim Jamshed 					walk->close_reason = TCP_ACTIVE_CLOSE;
469*76404edcSAsim Jamshed 					walk->cb_events |= MOS_ON_TCP_STATE_CHANGE;
470*76404edcSAsim Jamshed 					TRACE_STATE("Stream %d: TCP_ST_CLOSED_RSVD\n", walk->id);
471*76404edcSAsim Jamshed 					DestroyTCPStream(mtcp, walk);
472*76404edcSAsim Jamshed 				}
473*76404edcSAsim Jamshed 			} else {
474*76404edcSAsim Jamshed 				break;
475*76404edcSAsim Jamshed 			}
476*76404edcSAsim Jamshed 		} else {
477*76404edcSAsim Jamshed 			TRACE_ERROR("Stream %d: not on timewait list.\n", walk->id);
478*76404edcSAsim Jamshed #ifdef DUMP_STREAM
479*76404edcSAsim Jamshed 			DumpStream(mtcp, walk);
480*76404edcSAsim Jamshed #endif
481*76404edcSAsim Jamshed 		}
482*76404edcSAsim Jamshed 	}
483*76404edcSAsim Jamshed 
484*76404edcSAsim Jamshed 	TRACE_ROUND("Checking timewait timeout. cnt: %d\n", cnt);
485*76404edcSAsim Jamshed }
486*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
487*76404edcSAsim Jamshed void
488*76404edcSAsim Jamshed CheckConnectionTimeout(mtcp_manager_t mtcp, uint32_t cur_ts, int thresh)
489*76404edcSAsim Jamshed {
490*76404edcSAsim Jamshed 	tcp_stream *walk, *next;
491*76404edcSAsim Jamshed 	int cnt;
492*76404edcSAsim Jamshed 
493*76404edcSAsim Jamshed 	STAT_COUNT(mtcp->runstat.rounds_tocheck);
494*76404edcSAsim Jamshed 
495*76404edcSAsim Jamshed 	cnt = 0;
496*76404edcSAsim Jamshed 	for (walk = TAILQ_FIRST(&mtcp->timeout_list);
497*76404edcSAsim Jamshed 			walk != NULL; walk = next) {
498*76404edcSAsim Jamshed 		if (++cnt > thresh)
499*76404edcSAsim Jamshed 			break;
500*76404edcSAsim Jamshed 		next = TAILQ_NEXT(walk, sndvar->timeout_link);
501*76404edcSAsim Jamshed 
502*76404edcSAsim Jamshed 		if ((int32_t)(cur_ts - walk->last_active_ts) >=
503*76404edcSAsim Jamshed 				g_config.mos->tcp_timeout) {
504*76404edcSAsim Jamshed 
505*76404edcSAsim Jamshed 			TRACE_DBG("stream->sock->id: %d, stream-state: %s, streampair-state: %s\n",
506*76404edcSAsim Jamshed 				  walk->socket->id,
507*76404edcSAsim Jamshed 				  TCPStateToString(walk),
508*76404edcSAsim Jamshed 				  TCPStateToString(walk->pair_stream));
509*76404edcSAsim Jamshed 
510*76404edcSAsim Jamshed 			walk->on_timeout_list = FALSE;
511*76404edcSAsim Jamshed 			TAILQ_REMOVE(&mtcp->timeout_list, walk, sndvar->timeout_link);
512*76404edcSAsim Jamshed 			mtcp->timeout_list_cnt--;
513*76404edcSAsim Jamshed 			walk->state = TCP_ST_CLOSED_RSVD;
514*76404edcSAsim Jamshed 			walk->close_reason = TCP_TIMEDOUT;
515*76404edcSAsim Jamshed 			walk->cb_events |= MOS_ON_TCP_STATE_CHANGE;
516*76404edcSAsim Jamshed 			if (walk->socket && HAS_STREAM_TYPE(walk, MOS_SOCK_STREAM)) {
517*76404edcSAsim Jamshed 				RaiseErrorEvent(mtcp, walk);
518*76404edcSAsim Jamshed 			} else {
519*76404edcSAsim Jamshed 				DestroyTCPStream(mtcp, walk);
520*76404edcSAsim Jamshed 			}
521*76404edcSAsim Jamshed 		} else {
522*76404edcSAsim Jamshed 			break;
523*76404edcSAsim Jamshed 		}
524*76404edcSAsim Jamshed 
525*76404edcSAsim Jamshed 	}
526*76404edcSAsim Jamshed }
527*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
528*76404edcSAsim Jamshed #define TIMEVAL_ADD(a, b) \
529*76404edcSAsim Jamshed do { (a)->tv_sec += (b)->tv_sec; \
530*76404edcSAsim Jamshed 	if (((a)->tv_usec += (b)->tv_usec) > 1000000) { \
531*76404edcSAsim Jamshed 		(a)->tv_sec++; (a)->tv_usec -= 1000000; } \
532*76404edcSAsim Jamshed } while (0)
533*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
534*76404edcSAsim Jamshed static int
535*76404edcSAsim Jamshed RegTimer(mtcp_manager_t mtcp, struct timer *timer)
536*76404edcSAsim Jamshed {
537*76404edcSAsim Jamshed 	/* NOTE: This code assumes that the new timer expires later than existing
538*76404edcSAsim Jamshed 	 * timers with high probability. */
539*76404edcSAsim Jamshed 	struct timer *walk;
540*76404edcSAsim Jamshed 
541*76404edcSAsim Jamshed 	TAILQ_FOREACH_REVERSE(walk, &mtcp->timer_list, timer_head, timer_link) {
542*76404edcSAsim Jamshed 		if (TIMEVAL_LT(&walk->exp, &timer->exp)) {
543*76404edcSAsim Jamshed 			TAILQ_INSERT_AFTER(&mtcp->timer_list, walk, timer, timer_link);
544*76404edcSAsim Jamshed 			return 0;
545*76404edcSAsim Jamshed 		}
546*76404edcSAsim Jamshed 	}
547*76404edcSAsim Jamshed 
548*76404edcSAsim Jamshed 	assert(!walk);
549*76404edcSAsim Jamshed 
550*76404edcSAsim Jamshed 	TAILQ_INSERT_HEAD(&mtcp->timer_list, timer, timer_link);
551*76404edcSAsim Jamshed 	return 0;
552*76404edcSAsim Jamshed }
553*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
554*76404edcSAsim Jamshed static struct timer *
555*76404edcSAsim Jamshed NewTimer(mtcp_manager_t mtcp, int id, struct timeval *timeout, callback_t cb)
556*76404edcSAsim Jamshed {
557*76404edcSAsim Jamshed #ifdef USE_TIMER_POOL
558*76404edcSAsim Jamshed 	struct timer *t = MPAllocateChunk(mtcp->timer_pool);
559*76404edcSAsim Jamshed #else
560*76404edcSAsim Jamshed 	struct timer *t = calloc(1, sizeof(struct timer));
561*76404edcSAsim Jamshed #endif
562*76404edcSAsim Jamshed 	if (!t)
563*76404edcSAsim Jamshed 		return NULL;
564*76404edcSAsim Jamshed 
565*76404edcSAsim Jamshed 	t->id = id;
566*76404edcSAsim Jamshed 	t->cb = cb;
567*76404edcSAsim Jamshed 	gettimeofday(&t->exp, NULL);
568*76404edcSAsim Jamshed 	TIMEVAL_ADD(&t->exp, timeout);
569*76404edcSAsim Jamshed 
570*76404edcSAsim Jamshed 	return t;
571*76404edcSAsim Jamshed }
572*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
573*76404edcSAsim Jamshed void
574*76404edcSAsim Jamshed DelTimer(mtcp_manager_t mtcp, struct timer *timer)
575*76404edcSAsim Jamshed {
576*76404edcSAsim Jamshed 	TAILQ_REMOVE(&mtcp->timer_list, timer, timer_link);
577*76404edcSAsim Jamshed #ifdef USE_TIMER_POOL
578*76404edcSAsim Jamshed 	MPFreeChunk(mtcp->timer_pool, timer);
579*76404edcSAsim Jamshed #else
580*76404edcSAsim Jamshed 	free(timer);
581*76404edcSAsim Jamshed #endif
582*76404edcSAsim Jamshed }
583*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
584*76404edcSAsim Jamshed int
585*76404edcSAsim Jamshed mtcp_settimer(mctx_t mctx, int id, struct timeval *timeout, callback_t cb)
586*76404edcSAsim Jamshed {
587*76404edcSAsim Jamshed 	mtcp_manager_t mtcp = GetMTCPManager(mctx);
588*76404edcSAsim Jamshed 	if (!mtcp)
589*76404edcSAsim Jamshed 		return -1;
590*76404edcSAsim Jamshed 
591*76404edcSAsim Jamshed 	struct timer *t = NewTimer(mtcp, id, timeout, cb);
592*76404edcSAsim Jamshed 	if (!t)
593*76404edcSAsim Jamshed 		return -1;
594*76404edcSAsim Jamshed 
595*76404edcSAsim Jamshed 	RegTimer(mtcp, t);
596*76404edcSAsim Jamshed 
597*76404edcSAsim Jamshed 	return 0;
598*76404edcSAsim Jamshed }
599*76404edcSAsim Jamshed /*----------------------------------------------------------------------------*/
600