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