]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/TcpDxe/TcpTimer.c
BaseTools: Library hashing fix and optimization for --hash feature
[mirror_edk2.git] / NetworkPkg / TcpDxe / TcpTimer.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 TCP timer related functions.\r
3\r
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
5\r
ecf98fbc 6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
a3bcde70
HT
7\r
8**/\r
9\r
10#include "TcpMain.h"\r
11\r
12UINT32 mTcpTick = 1000;\r
13\r
14/**\r
15 Connect timeout handler.\r
16\r
17 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
18\r
19**/\r
20VOID\r
21TcpConnectTimeout (\r
22 IN OUT TCP_CB *Tcb\r
23 );\r
24\r
25/**\r
26 Timeout handler for TCP retransmission timer.\r
27\r
28 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
29\r
30**/\r
31VOID\r
32TcpRexmitTimeout (\r
33 IN OUT TCP_CB *Tcb\r
34 );\r
35\r
36/**\r
37 Timeout handler for window probe timer.\r
38\r
39 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
40\r
41**/\r
42VOID\r
43TcpProbeTimeout (\r
44 IN OUT TCP_CB *Tcb\r
45 );\r
46\r
47/**\r
48 Timeout handler for keepalive timer.\r
49\r
50 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
51\r
52**/\r
53VOID\r
54TcpKeepaliveTimeout (\r
55 IN OUT TCP_CB *Tcb\r
56 );\r
57\r
58/**\r
59 Timeout handler for FIN_WAIT_2 timer.\r
60\r
61 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
62\r
63**/\r
64VOID\r
65TcpFinwait2Timeout (\r
66 IN OUT TCP_CB *Tcb\r
67 );\r
68\r
69/**\r
70 Timeout handler for 2MSL timer.\r
71\r
72 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
73\r
74**/\r
75VOID\r
76Tcp2MSLTimeout (\r
77 IN OUT TCP_CB *Tcb\r
78 );\r
79\r
80TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {\r
81 TcpConnectTimeout,\r
82 TcpRexmitTimeout,\r
83 TcpProbeTimeout,\r
84 TcpKeepaliveTimeout,\r
85 TcpFinwait2Timeout,\r
86 Tcp2MSLTimeout,\r
87};\r
88\r
89/**\r
90 Close the TCP connection.\r
91\r
92 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
93\r
94**/\r
95VOID\r
96TcpClose (\r
97 IN OUT TCP_CB *Tcb\r
98 )\r
99{\r
100 NetbufFreeList (&Tcb->SndQue);\r
101 NetbufFreeList (&Tcb->RcvQue);\r
102\r
103 TcpSetState (Tcb, TCP_CLOSED);\r
104}\r
105\r
106/**\r
107 Backoff the RTO.\r
108\r
109 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
110\r
111**/\r
112VOID\r
113TcpBackoffRto (\r
114 IN OUT TCP_CB *Tcb\r
115 )\r
116{\r
117 //\r
118 // Fold the RTT estimate if too many times, the estimate\r
119 // may be wrong, fold it. So the next time a valid\r
120 // measurement is sampled, we can start fresh.\r
121 //\r
122 if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {\r
123 Tcb->RttVar += Tcb->SRtt >> 2;\r
124 Tcb->SRtt = 0;\r
125 }\r
126\r
127 Tcb->Rto <<= 1;\r
128\r
129 if (Tcb->Rto < TCP_RTO_MIN) {\r
130\r
131 Tcb->Rto = TCP_RTO_MIN;\r
132 } else if (Tcb->Rto > TCP_RTO_MAX) {\r
133\r
134 Tcb->Rto = TCP_RTO_MAX;\r
135 }\r
136}\r
137\r
138/**\r
139 Connect timeout handler.\r
140\r
141 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
142\r
143**/\r
144VOID\r
145TcpConnectTimeout (\r
146 IN OUT TCP_CB *Tcb\r
147 )\r
148{\r
149 if (!TCP_CONNECTED (Tcb->State)) {\r
150 DEBUG (\r
151 (EFI_D_ERROR,\r
152 "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n",\r
153 Tcb)\r
154 );\r
155\r
156 if (EFI_ABORTED == Tcb->Sk->SockError) {\r
157 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
158 }\r
159\r
160 if (TCP_SYN_RCVD == Tcb->State) {\r
161 DEBUG (\r
162 (EFI_D_WARN,\r
163 "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",\r
164 Tcb)\r
165 );\r
166\r
167 TcpResetConnection (Tcb);\r
168\r
169 }\r
170\r
171 TcpClose (Tcb);\r
172 }\r
173}\r
174\r
175\r
176/**\r
177 Timeout handler for TCP retransmission timer.\r
178\r
179 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
180\r
181**/\r
182VOID\r
183TcpRexmitTimeout (\r
184 IN OUT TCP_CB *Tcb\r
185 )\r
186{\r
187 UINT32 FlightSize;\r
188\r
189 DEBUG (\r
190 (EFI_D_WARN,\r
191 "TcpRexmitTimeout: transmission timeout for TCB %p\n",\r
192 Tcb)\r
193 );\r
194\r
195 //\r
196 // Set the congestion window. FlightSize is the\r
197 // amount of data that has been sent but not\r
198 // yet ACKed.\r
199 //\r
200 FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
201 Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);\r
202\r
203 Tcb->CWnd = Tcb->SndMss;\r
204 Tcb->LossRecover = Tcb->SndNxt;\r
205\r
206 Tcb->LossTimes++;\r
207 if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {\r
208\r
209 DEBUG (\r
210 (EFI_D_ERROR,\r
211 "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",\r
212 Tcb)\r
213 );\r
214\r
215 if (EFI_ABORTED == Tcb->Sk->SockError) {\r
216 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
217 }\r
218\r
219 TcpClose (Tcb);\r
220 return ;\r
221 }\r
222\r
223 TcpBackoffRto (Tcb);\r
224 TcpRetransmit (Tcb, Tcb->SndUna);\r
225 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
226\r
227 Tcb->CongestState = TCP_CONGEST_LOSS;\r
228\r
229 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
230}\r
231\r
232/**\r
233 Timeout handler for window probe timer.\r
234\r
235 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
236\r
237**/\r
238VOID\r
239TcpProbeTimeout (\r
240 IN OUT TCP_CB *Tcb\r
241 )\r
242{\r
243 //\r
244 // This is the timer for sender's SWSA. RFC1122 requires\r
245 // a timer set for sender's SWSA, and suggest combine it\r
246 // with window probe timer. If data is sent, don't set\r
247 // the probe timer, since retransmit timer is on.\r
248 //\r
249 if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {\r
250\r
251 ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);\r
252 Tcb->ProbeTimerOn = FALSE;\r
253 return ;\r
254 }\r
255\r
256 TcpSendZeroProbe (Tcb);\r
257 TcpSetProbeTimer (Tcb);\r
258}\r
259\r
260/**\r
261 Timeout handler for keepalive timer.\r
262\r
263 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
264\r
265**/\r
266VOID\r
267TcpKeepaliveTimeout (\r
268 IN OUT TCP_CB *Tcb\r
269 )\r
270{\r
271 Tcb->KeepAliveProbes++;\r
272\r
273 //\r
274 // Too many Keep-alive probes, drop the connection\r
275 //\r
276 if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {\r
277\r
278 if (EFI_ABORTED == Tcb->Sk->SockError) {\r
279 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
280 }\r
281\r
282 TcpClose (Tcb);\r
283 return ;\r
284 }\r
285\r
286 TcpSendZeroProbe (Tcb);\r
287 TcpSetKeepaliveTimer (Tcb);\r
288}\r
289\r
290/**\r
291 Timeout handler for FIN_WAIT_2 timer.\r
292\r
293 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
294\r
295**/\r
296VOID\r
297TcpFinwait2Timeout (\r
298 IN OUT TCP_CB *Tcb\r
299 )\r
300{\r
301 DEBUG (\r
302 (EFI_D_WARN,\r
303 "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",\r
304 Tcb)\r
305 );\r
306\r
307 TcpClose (Tcb);\r
308}\r
309\r
310/**\r
311 Timeout handler for 2MSL timer.\r
312\r
313 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
314\r
315**/\r
316VOID\r
317Tcp2MSLTimeout (\r
318 IN OUT TCP_CB *Tcb\r
319 )\r
320{\r
321 DEBUG (\r
322 (EFI_D_WARN,\r
323 "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",\r
324 Tcb)\r
325 );\r
326\r
327 TcpClose (Tcb);\r
328}\r
329\r
330/**\r
331 Update the timer status and the next expire time according to the timers\r
332 to expire in a specific future time slot.\r
333\r
334 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
335\r
336**/\r
337VOID\r
338TcpUpdateTimer (\r
339 IN OUT TCP_CB *Tcb\r
340 )\r
341{\r
342 UINT16 Index;\r
343\r
344 //\r
345 // Don't use a too large value to init NextExpire\r
346 // since mTcpTick wraps around as sequence no does.\r
347 //\r
348 Tcb->NextExpire = TCP_EXPIRE_TIME;\r
349 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);\r
350\r
351 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {\r
352\r
353 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&\r
354 TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)\r
355 ) {\r
356\r
357 Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);\r
358 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);\r
359 }\r
360 }\r
361}\r
362\r
363/**\r
364 Enable a TCP timer.\r
365\r
366 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
367 @param[in] Timer The index of the timer to be enabled.\r
368 @param[in] TimeOut The timeout value of this timer.\r
369\r
370**/\r
371VOID\r
372TcpSetTimer (\r
373 IN OUT TCP_CB *Tcb,\r
374 IN UINT16 Timer,\r
375 IN UINT32 TimeOut\r
376 )\r
377{\r
378 TCP_SET_TIMER (Tcb->EnabledTimer, Timer);\r
379 Tcb->Timer[Timer] = mTcpTick + TimeOut;\r
380\r
381 TcpUpdateTimer (Tcb);\r
382}\r
383\r
384/**\r
385 Clear one TCP timer.\r
386\r
387 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
388 @param[in] Timer The index of the timer to be cleared.\r
389\r
390**/\r
391VOID\r
392TcpClearTimer (\r
393 IN OUT TCP_CB *Tcb,\r
394 IN UINT16 Timer\r
395 )\r
396{\r
397 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);\r
398 TcpUpdateTimer (Tcb);\r
399}\r
400\r
401/**\r
402 Clear all TCP timers.\r
403\r
404 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
405\r
406**/\r
407VOID\r
408TcpClearAllTimer (\r
409 IN OUT TCP_CB *Tcb\r
410 )\r
411{\r
412 Tcb->EnabledTimer = 0;\r
413 TcpUpdateTimer (Tcb);\r
414}\r
415\r
416/**\r
417 Enable the window prober timer and set the timeout value.\r
418\r
419 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
420\r
421**/\r
422VOID\r
423TcpSetProbeTimer (\r
424 IN OUT TCP_CB *Tcb\r
425 )\r
426{\r
427 if (!Tcb->ProbeTimerOn) {\r
428 Tcb->ProbeTime = Tcb->Rto;\r
429 Tcb->ProbeTimerOn = TRUE;\r
430\r
431 } else {\r
432 Tcb->ProbeTime <<= 1;\r
433 }\r
434\r
435 if (Tcb->ProbeTime < TCP_RTO_MIN) {\r
436\r
437 Tcb->ProbeTime = TCP_RTO_MIN;\r
438 } else if (Tcb->ProbeTime > TCP_RTO_MAX) {\r
439\r
440 Tcb->ProbeTime = TCP_RTO_MAX;\r
441 }\r
442\r
443 TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);\r
444}\r
445\r
446/**\r
447 Enable the keepalive timer and set the timeout value.\r
448\r
449 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
450\r
451**/\r
452VOID\r
453TcpSetKeepaliveTimer (\r
454 IN OUT TCP_CB *Tcb\r
455 )\r
456{\r
457 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {\r
458 return ;\r
459\r
460 }\r
461\r
462 //\r
463 // Set the timer to KeepAliveIdle if either\r
464 // 1. the keepalive timer is off\r
465 // 2. The keepalive timer is on, but the idle\r
466 // is less than KeepAliveIdle, that means the\r
467 // connection is alive since our last probe.\r
468 //\r
469 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||\r
470 (Tcb->Idle < Tcb->KeepAliveIdle)\r
471 ) {\r
472\r
473 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);\r
474 Tcb->KeepAliveProbes = 0;\r
475\r
476 } else {\r
477\r
478 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);\r
479 }\r
480}\r
481\r
482/**\r
483 Heart beat timer handler.\r
484\r
485 @param[in] Context Context of the timer event, ignored.\r
486\r
487**/\r
488VOID\r
489EFIAPI\r
490TcpTickingDpc (\r
491 IN VOID *Context\r
492 )\r
493{\r
494 LIST_ENTRY *Entry;\r
495 LIST_ENTRY *Next;\r
496 TCP_CB *Tcb;\r
497 INT16 Index;\r
498\r
499 mTcpTick++;\r
500 mTcpGlobalIss += TCP_ISS_INCREMENT_2;\r
501\r
502 //\r
503 // Don't use LIST_FOR_EACH, which isn't delete safe.\r
504 //\r
505 for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {\r
506\r
507 Next = Entry->ForwardLink;\r
508\r
509 Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
510\r
511 if (Tcb->State == TCP_CLOSED) {\r
512 continue;\r
513 }\r
514 //\r
515 // The connection is doing RTT measurement.\r
516 //\r
517 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
518 Tcb->RttMeasure++;\r
519 }\r
520\r
521 Tcb->Idle++;\r
522\r
523 if (Tcb->DelayedAck != 0) {\r
524 TcpSendAck (Tcb);\r
525 }\r
526\r
527 if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) {\r
528 Tcb->Tick--;\r
529 }\r
530\r
531 //\r
532 // No timer is active or no timer expired\r
533 //\r
534 if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {\r
535\r
536 continue;\r
537 }\r
538\r
539 //\r
540 // Call the timeout handler for each expired timer.\r
541 //\r
542 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {\r
543\r
544 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {\r
545 //\r
546 // disable the timer before calling the handler\r
547 // in case the handler enables it again.\r
548 //\r
549 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);\r
550 mTcpTimerHandler[Index](Tcb);\r
551\r
552 //\r
553 // The Tcb may have been deleted by the timer, or\r
554 // no other timer is set.\r
555 //\r
556 if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {\r
557 break;\r
558 }\r
559 }\r
560 }\r
561\r
562 //\r
563 // If the Tcb still exist or some timer is set, update the timer\r
564 //\r
565 if (Index == TCP_TIMER_NUMBER) {\r
566 TcpUpdateTimer (Tcb);\r
567 }\r
568 }\r
569}\r
570\r
571/**\r
572 Heart beat timer handler, queues the DPC at TPL_CALLBACK.\r
573\r
574 @param[in] Event Timer event signaled, ignored.\r
575 @param[in] Context Context of the timer event, ignored.\r
576\r
577**/\r
578VOID\r
579EFIAPI\r
580TcpTicking (\r
581 IN EFI_EVENT Event,\r
582 IN VOID *Context\r
583 )\r
584{\r
585 QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);\r
586}\r
587\r