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