]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/TcpDxe/TcpTimer.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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
d1050b9d 12UINT32 mTcpTick = 1000;\r
a3bcde70
HT
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
d1050b9d 22 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
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
d1050b9d 33 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
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
d1050b9d 44 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
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
d1050b9d 55 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
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
d1050b9d 66 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
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
d1050b9d 77 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
78 );\r
79\r
d1050b9d 80TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {\r
a3bcde70
HT
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
d1050b9d 97 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
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
d1050b9d 114 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
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
d1050b9d 124 Tcb->SRtt = 0;\r
a3bcde70
HT
125 }\r
126\r
127 Tcb->Rto <<= 1;\r
128\r
129 if (Tcb->Rto < TCP_RTO_MIN) {\r
a3bcde70
HT
130 Tcb->Rto = TCP_RTO_MIN;\r
131 } else if (Tcb->Rto > TCP_RTO_MAX) {\r
a3bcde70
HT
132 Tcb->Rto = TCP_RTO_MAX;\r
133 }\r
134}\r
135\r
136/**\r
137 Connect timeout handler.\r
138\r
139 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
140\r
141**/\r
142VOID\r
143TcpConnectTimeout (\r
d1050b9d 144 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
145 )\r
146{\r
147 if (!TCP_CONNECTED (Tcb->State)) {\r
148 DEBUG (\r
c49ca4a2 149 (DEBUG_ERROR,\r
d1050b9d
MK
150 "TcpConnectTimeout: connection closed because connection timer timeout for TCB %p\n",\r
151 Tcb)\r
a3bcde70
HT
152 );\r
153\r
154 if (EFI_ABORTED == Tcb->Sk->SockError) {\r
155 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
156 }\r
157\r
158 if (TCP_SYN_RCVD == Tcb->State) {\r
159 DEBUG (\r
c49ca4a2 160 (DEBUG_WARN,\r
d1050b9d
MK
161 "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",\r
162 Tcb)\r
a3bcde70
HT
163 );\r
164\r
165 TcpResetConnection (Tcb);\r
a3bcde70
HT
166 }\r
167\r
168 TcpClose (Tcb);\r
169 }\r
170}\r
171\r
a3bcde70
HT
172/**\r
173 Timeout handler for TCP retransmission timer.\r
174\r
175 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
176\r
177**/\r
178VOID\r
179TcpRexmitTimeout (\r
d1050b9d 180 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
181 )\r
182{\r
183 UINT32 FlightSize;\r
184\r
185 DEBUG (\r
c49ca4a2 186 (DEBUG_WARN,\r
d1050b9d
MK
187 "TcpRexmitTimeout: transmission timeout for TCB %p\n",\r
188 Tcb)\r
a3bcde70
HT
189 );\r
190\r
191 //\r
192 // Set the congestion window. FlightSize is the\r
193 // amount of data that has been sent but not\r
194 // yet ACKed.\r
195 //\r
d1050b9d
MK
196 FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);\r
197 Tcb->Ssthresh = MAX ((UINT32)(2 * Tcb->SndMss), FlightSize / 2);\r
a3bcde70 198\r
d1050b9d
MK
199 Tcb->CWnd = Tcb->SndMss;\r
200 Tcb->LossRecover = Tcb->SndNxt;\r
a3bcde70
HT
201\r
202 Tcb->LossTimes++;\r
203 if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {\r
a3bcde70 204 DEBUG (\r
c49ca4a2 205 (DEBUG_ERROR,\r
d1050b9d
MK
206 "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",\r
207 Tcb)\r
a3bcde70
HT
208 );\r
209\r
210 if (EFI_ABORTED == Tcb->Sk->SockError) {\r
211 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
212 }\r
213\r
214 TcpClose (Tcb);\r
d1050b9d 215 return;\r
a3bcde70
HT
216 }\r
217\r
218 TcpBackoffRto (Tcb);\r
219 TcpRetransmit (Tcb, Tcb->SndUna);\r
220 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
221\r
222 Tcb->CongestState = TCP_CONGEST_LOSS;\r
223\r
224 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
225}\r
226\r
227/**\r
228 Timeout handler for window probe timer.\r
229\r
230 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
231\r
232**/\r
233VOID\r
234TcpProbeTimeout (\r
d1050b9d 235 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
236 )\r
237{\r
238 //\r
239 // This is the timer for sender's SWSA. RFC1122 requires\r
240 // a timer set for sender's SWSA, and suggest combine it\r
241 // with window probe timer. If data is sent, don't set\r
242 // the probe timer, since retransmit timer is on.\r
243 //\r
244 if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {\r
a3bcde70
HT
245 ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);\r
246 Tcb->ProbeTimerOn = FALSE;\r
d1050b9d 247 return;\r
a3bcde70
HT
248 }\r
249\r
250 TcpSendZeroProbe (Tcb);\r
251 TcpSetProbeTimer (Tcb);\r
252}\r
253\r
254/**\r
255 Timeout handler for keepalive timer.\r
256\r
257 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
258\r
259**/\r
260VOID\r
261TcpKeepaliveTimeout (\r
d1050b9d 262 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
263 )\r
264{\r
265 Tcb->KeepAliveProbes++;\r
266\r
267 //\r
268 // Too many Keep-alive probes, drop the connection\r
269 //\r
270 if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {\r
a3bcde70
HT
271 if (EFI_ABORTED == Tcb->Sk->SockError) {\r
272 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);\r
273 }\r
274\r
275 TcpClose (Tcb);\r
d1050b9d 276 return;\r
a3bcde70
HT
277 }\r
278\r
279 TcpSendZeroProbe (Tcb);\r
280 TcpSetKeepaliveTimer (Tcb);\r
281}\r
282\r
283/**\r
284 Timeout handler for FIN_WAIT_2 timer.\r
285\r
286 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
287\r
288**/\r
289VOID\r
290TcpFinwait2Timeout (\r
d1050b9d 291 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
292 )\r
293{\r
294 DEBUG (\r
c49ca4a2 295 (DEBUG_WARN,\r
d1050b9d
MK
296 "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",\r
297 Tcb)\r
a3bcde70
HT
298 );\r
299\r
300 TcpClose (Tcb);\r
301}\r
302\r
303/**\r
304 Timeout handler for 2MSL timer.\r
305\r
306 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
307\r
308**/\r
309VOID\r
310Tcp2MSLTimeout (\r
d1050b9d 311 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
312 )\r
313{\r
314 DEBUG (\r
c49ca4a2 315 (DEBUG_WARN,\r
d1050b9d
MK
316 "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",\r
317 Tcb)\r
a3bcde70
HT
318 );\r
319\r
320 TcpClose (Tcb);\r
321}\r
322\r
323/**\r
324 Update the timer status and the next expire time according to the timers\r
325 to expire in a specific future time slot.\r
326\r
327 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
328\r
329**/\r
330VOID\r
331TcpUpdateTimer (\r
d1050b9d 332 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
333 )\r
334{\r
335 UINT16 Index;\r
336\r
337 //\r
338 // Don't use a too large value to init NextExpire\r
339 // since mTcpTick wraps around as sequence no does.\r
340 //\r
341 Tcb->NextExpire = TCP_EXPIRE_TIME;\r
342 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);\r
343\r
344 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {\r
a3bcde70
HT
345 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&\r
346 TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)\r
d1050b9d
MK
347 )\r
348 {\r
a3bcde70
HT
349 Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);\r
350 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);\r
351 }\r
352 }\r
353}\r
354\r
355/**\r
356 Enable a TCP timer.\r
357\r
358 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
359 @param[in] Timer The index of the timer to be enabled.\r
360 @param[in] TimeOut The timeout value of this timer.\r
361\r
362**/\r
363VOID\r
364TcpSetTimer (\r
d1050b9d
MK
365 IN OUT TCP_CB *Tcb,\r
366 IN UINT16 Timer,\r
367 IN UINT32 TimeOut\r
a3bcde70
HT
368 )\r
369{\r
370 TCP_SET_TIMER (Tcb->EnabledTimer, Timer);\r
371 Tcb->Timer[Timer] = mTcpTick + TimeOut;\r
372\r
373 TcpUpdateTimer (Tcb);\r
374}\r
375\r
376/**\r
377 Clear one TCP timer.\r
378\r
379 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
380 @param[in] Timer The index of the timer to be cleared.\r
381\r
382**/\r
383VOID\r
384TcpClearTimer (\r
d1050b9d
MK
385 IN OUT TCP_CB *Tcb,\r
386 IN UINT16 Timer\r
a3bcde70
HT
387 )\r
388{\r
389 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);\r
390 TcpUpdateTimer (Tcb);\r
391}\r
392\r
393/**\r
394 Clear all TCP timers.\r
395\r
396 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
397\r
398**/\r
399VOID\r
400TcpClearAllTimer (\r
d1050b9d 401 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
402 )\r
403{\r
404 Tcb->EnabledTimer = 0;\r
405 TcpUpdateTimer (Tcb);\r
406}\r
407\r
408/**\r
409 Enable the window prober timer and set the timeout value.\r
410\r
411 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
412\r
413**/\r
414VOID\r
415TcpSetProbeTimer (\r
d1050b9d 416 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
417 )\r
418{\r
419 if (!Tcb->ProbeTimerOn) {\r
420 Tcb->ProbeTime = Tcb->Rto;\r
421 Tcb->ProbeTimerOn = TRUE;\r
a3bcde70
HT
422 } else {\r
423 Tcb->ProbeTime <<= 1;\r
424 }\r
425\r
426 if (Tcb->ProbeTime < TCP_RTO_MIN) {\r
a3bcde70
HT
427 Tcb->ProbeTime = TCP_RTO_MIN;\r
428 } else if (Tcb->ProbeTime > TCP_RTO_MAX) {\r
a3bcde70
HT
429 Tcb->ProbeTime = TCP_RTO_MAX;\r
430 }\r
431\r
432 TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);\r
433}\r
434\r
435/**\r
436 Enable the keepalive timer and set the timeout value.\r
437\r
438 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.\r
439\r
440**/\r
441VOID\r
442TcpSetKeepaliveTimer (\r
d1050b9d 443 IN OUT TCP_CB *Tcb\r
a3bcde70
HT
444 )\r
445{\r
446 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {\r
d1050b9d 447 return;\r
a3bcde70
HT
448 }\r
449\r
450 //\r
451 // Set the timer to KeepAliveIdle if either\r
452 // 1. the keepalive timer is off\r
453 // 2. The keepalive timer is on, but the idle\r
454 // is less than KeepAliveIdle, that means the\r
455 // connection is alive since our last probe.\r
456 //\r
457 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||\r
458 (Tcb->Idle < Tcb->KeepAliveIdle)\r
d1050b9d
MK
459 )\r
460 {\r
a3bcde70
HT
461 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);\r
462 Tcb->KeepAliveProbes = 0;\r
a3bcde70 463 } else {\r
a3bcde70
HT
464 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);\r
465 }\r
466}\r
467\r
468/**\r
469 Heart beat timer handler.\r
470\r
471 @param[in] Context Context of the timer event, ignored.\r
472\r
473**/\r
474VOID\r
475EFIAPI\r
476TcpTickingDpc (\r
d1050b9d 477 IN VOID *Context\r
a3bcde70
HT
478 )\r
479{\r
d1050b9d
MK
480 LIST_ENTRY *Entry;\r
481 LIST_ENTRY *Next;\r
482 TCP_CB *Tcb;\r
483 INT16 Index;\r
a3bcde70
HT
484\r
485 mTcpTick++;\r
486 mTcpGlobalIss += TCP_ISS_INCREMENT_2;\r
487\r
488 //\r
489 // Don't use LIST_FOR_EACH, which isn't delete safe.\r
490 //\r
491 for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {\r
d1050b9d 492 Next = Entry->ForwardLink;\r
a3bcde70 493\r
d1050b9d 494 Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);\r
a3bcde70
HT
495\r
496 if (Tcb->State == TCP_CLOSED) {\r
497 continue;\r
498 }\r
d1050b9d 499\r
a3bcde70
HT
500 //\r
501 // The connection is doing RTT measurement.\r
502 //\r
503 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
504 Tcb->RttMeasure++;\r
505 }\r
506\r
507 Tcb->Idle++;\r
508\r
509 if (Tcb->DelayedAck != 0) {\r
510 TcpSendAck (Tcb);\r
511 }\r
512\r
d1050b9d 513 if ((Tcb->IpInfo->IpVersion == IP_VERSION_6) && (Tcb->Tick > 0)) {\r
a3bcde70
HT
514 Tcb->Tick--;\r
515 }\r
516\r
517 //\r
518 // No timer is active or no timer expired\r
519 //\r
520 if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {\r
a3bcde70
HT
521 continue;\r
522 }\r
523\r
524 //\r
525 // Call the timeout handler for each expired timer.\r
526 //\r
527 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {\r
a3bcde70
HT
528 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {\r
529 //\r
530 // disable the timer before calling the handler\r
531 // in case the handler enables it again.\r
532 //\r
533 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);\r
534 mTcpTimerHandler[Index](Tcb);\r
535\r
536 //\r
537 // The Tcb may have been deleted by the timer, or\r
538 // no other timer is set.\r
539 //\r
540 if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {\r
541 break;\r
542 }\r
543 }\r
544 }\r
545\r
546 //\r
547 // If the Tcb still exist or some timer is set, update the timer\r
548 //\r
549 if (Index == TCP_TIMER_NUMBER) {\r
550 TcpUpdateTimer (Tcb);\r
551 }\r
552 }\r
553}\r
554\r
555/**\r
556 Heart beat timer handler, queues the DPC at TPL_CALLBACK.\r
557\r
558 @param[in] Event Timer event signaled, ignored.\r
559 @param[in] Context Context of the timer event, ignored.\r
560\r
561**/\r
562VOID\r
563EFIAPI\r
564TcpTicking (\r
d1050b9d
MK
565 IN EFI_EVENT Event,\r
566 IN VOID *Context\r
a3bcde70
HT
567 )\r
568{\r
569 QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);\r
570}\r