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