Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

983 lines
33 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: EndPt.cpp
  6. * Content: This file contains EndPoint management routines.
  7. * An End Point is a DirectNet instance that we know about and may communicate
  8. * with. An End Point Descriptor (EPD) tracks each known End Point and was mapped
  9. * onto an hEndPoint by a hash table. Now, the SP maintains the mapping and hands
  10. * us our EPD address as a context with each indication ReceiveEvent.
  11. *
  12. * In addition to EndPoint creation and destruction, this file contains routines
  13. * which handle link tuning. This is described in detailed comments below.
  14. *
  15. * History:
  16. * Date By Reason
  17. * ==== == ======
  18. * 11/06/98 ejs Created
  19. * 07/01/2000 masonb Assumed Ownership
  20. *
  21. ****************************************************************************/
  22. #include "dnproti.h"
  23. VOID RunAdaptiveAlg(PEPD, DWORD);
  24. VOID ThrottleBack(PEPD, DWORD);
  25. #define USE_BURST_GAP TRUE
  26. /*
  27. ** Crack EndPoint Descriptor
  28. **
  29. */
  30. #undef DPF_MODNAME
  31. #define DPF_MODNAME "DNPCrackEndPointDescriptor"
  32. HRESULT DNPCrackEndPointDescriptor(HANDLE hEndPoint, PSPGETADDRESSINFODATA pSPData)
  33. {
  34. PEPD pEPD = (PEPD) hEndPoint;
  35. HRESULT hr = DPNERR_INVALIDENDPOINT;
  36. DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hEndPoint[%x], pSPData[%p]", hEndPoint, pSPData);
  37. ASSERT_EPD(pEPD);
  38. if(LOCK_EPD(pEPD, "LOCK (Crack EPD)"))
  39. {
  40. Lock(&pEPD->EPLock);
  41. if(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTED)
  42. {
  43. PSPD pSPD = pEPD->pSPD;
  44. pSPData->hEndpoint = pEPD->hEndPt;
  45. RELEASE_EPD(pEPD, "UNLOCK (Crack EPD)"); // releases EPLock
  46. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling SP->GetAddressInfo, pSPD[%p]", pEPD, pSPD);
  47. hr = IDP8ServiceProvider_GetAddressInfo(pSPD->IISPIntf, pSPData);
  48. }
  49. else
  50. {
  51. RELEASE_EPD(pEPD, "UNLOCK (Crack EPD)"); // releases EPLock
  52. }
  53. }
  54. else
  55. {
  56. DPFX(DPFPREP,1, "(%p) DNPCrackEndPointDescriptor called on unreferenced EPD, returning DPNERR_INVALIDENDPOINT", pEPD);
  57. }
  58. return hr;
  59. }
  60. /*
  61. ** INTERNAL - EndPoint management functions
  62. */
  63. /*
  64. ** New End Point
  65. **
  66. ** Everytime a packet is indicated with an address that we dont recognize we will allocate
  67. ** an EPD for it and add it to our tables. It is a higher layer's responsibility to tell
  68. ** us when it no longer wants to talk to the EP so that we can clear it out of our
  69. ** (and the SP's) table.
  70. **
  71. */
  72. #undef DPF_MODNAME
  73. #define DPF_MODNAME "NewEndPoint"
  74. PEPD NewEndPoint(PSPD pSPD, HANDLE hEP)
  75. {
  76. PEPD pEPD;
  77. if((pEPD = static_cast<PEPD> (EPDPool->Get(EPDPool))) == NULL)
  78. {
  79. DPFX(DPFPREP,0, "Failed to allocate new EPD");
  80. return NULL;
  81. }
  82. ASSERT(hEP != INVALID_HANDLE_VALUE);
  83. pEPD->hEndPt = hEP; // Record ID in structure
  84. pEPD->pSPD = pSPD;
  85. pEPD->bNextMsgID = 0;
  86. pEPD->uiRTT = 0;
  87. pEPD->uiBytesAcked = 0;
  88. pEPD->uiQueuedMessageCount = 0;
  89. #ifdef DEBUG
  90. pEPD->bLastDataSeq = 0xFF;
  91. #endif
  92. // We track a byte-window and a frame-window separately. Start with 2 discreet frames, but only 1 FULL frame.
  93. pEPD->uiWindowF = 2; // start with 1 full-frame window (this could still be many frames)
  94. pEPD->uiWindowBIndex = 1;
  95. pEPD->uiWindowB = pSPD->uiFrameLength; // ditto for bytes
  96. pEPD->uiUnackedFrames = 0; // outstanding frame count
  97. pEPD->uiUnackedBytes = 0; // outstanding byte count
  98. pEPD->uiBurstGap = 100; // starting point 100ms/burst
  99. pEPD->dwSessID = 0;
  100. // ReceiveComplete flag prevents received data from being indicated to core until after new connection is indicated
  101. // Initialize state
  102. pEPD->ulEPFlags = EPFLAGS_END_POINT_IN_USE | EPFLAGS_STATE_DORMANT | EPFLAGS_IN_RECEIVE_COMPLETE; // Initialize state
  103. ASSERT(pEPD->lRefCnt == 0); // WE NOW HAVE A -1 BASED REFCNT INSTEAD OF ZERO BASED (FOR EPDs)
  104. pEPD->SendTimer = 0; // Timer for next send-burst opportunity
  105. pEPD->RetryTimer = 0; // window to receive Ack
  106. pEPD->ConnectTimer = 0;
  107. pEPD->DelayedAckTimer = 0; // wait for piggyback opportunity before sending Ack
  108. pEPD->DelayedMaskTimer = 0; // wait for piggyback opportunity before sending Mask frame
  109. pEPD->BGTimer = 0; // Periodic background timer
  110. pEPD->uiCompleteMsgCount = 0;
  111. LOCK_EPD(pEPD, "SP reference"); // We will not remove this reference until the SP tells us to go away.
  112. Lock(&pSPD->SPLock);
  113. pEPD->blActiveLinkage.InsertAfter( &pSPD->blEPDActiveList); // Place this guy in active list
  114. Unlock(&pSPD->SPLock);
  115. return pEPD;
  116. }
  117. /*
  118. ** Initial Link Parameters
  119. **
  120. ** we have kept a checkpoint structure matching everying frame we sent in the Connect
  121. ** handshake so that we can match a response to a specific frame or retry. This allows us
  122. ** to measure a single sample Round Trip Time (RTT), which we will use below to generate
  123. ** initial values for our link-state variables.
  124. **
  125. */
  126. #undef DPF_MODNAME
  127. #define DPF_MODNAME "InitLinkParameters"
  128. VOID InitLinkParameters(PEPD pEPD, UINT uiRTT, UINT normal_load, UINT bias, DWORD tNow)
  129. {
  130. DWORD dwTimerInterval;
  131. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  132. if(uiRTT == 0)
  133. {
  134. uiRTT = 1;
  135. }
  136. pEPD->uiRTT = uiRTT; // we know the base RTT
  137. pEPD->fpRTT = TO_FP(uiRTT); // 16.16 fixed point version
  138. pEPD->uiDropCount = 0;
  139. pEPD->uiThrottleEvents = 0; // Count times we throttle-back for all reasons
  140. #ifdef DEBUG
  141. pEPD->uiTotalThrottleEvents = 0;
  142. #endif
  143. pEPD->uiBurstGap = 0; // For now assume we dont need a burst gap
  144. pEPD->uiMsgSentHigh = 0;
  145. pEPD->uiMsgSentNorm = 0;
  146. pEPD->uiMsgSentLow = 0;
  147. pEPD->uiMsgTOHigh = 0;
  148. pEPD->uiMsgTONorm = 0;
  149. pEPD->uiMsgTOLow = 0;
  150. pEPD->uiMessagesReceived = 0;
  151. pEPD->uiGuaranteedFramesSent = 0;
  152. pEPD->uiGuaranteedBytesSent = 0;
  153. pEPD->uiDatagramFramesSent = 0;
  154. pEPD->uiDatagramBytesSent = 0;
  155. pEPD->uiGuaranteedFramesReceived = 0;
  156. pEPD->uiGuaranteedBytesReceived = 0;
  157. pEPD->uiDatagramFramesReceived = 0;
  158. pEPD->uiDatagramBytesReceived = 0;
  159. pEPD->uiGuaranteedFramesDropped = 0;
  160. pEPD->uiGuaranteedBytesDropped = 0;
  161. pEPD->uiDatagramFramesDropped = 0;
  162. pEPD->uiDatagramBytesDropped = 0;
  163. pEPD->uiGoodBurstGap = 0; // No Known Good Gap!
  164. pEPD->uiGoodRTT = 60000; // We need this to initially be artificially high
  165. pEPD->uiGoodWindowF = 1;
  166. pEPD->uiGoodWindowBI = 1;
  167. pEPD->iBurstCredit = 0;
  168. pEPD->tLastDelta = tNow;
  169. pEPD->uiWindowFilled = 0;
  170. pEPD->tLastThruPutSample = tNow;
  171. pEPD->uiLastBytesAcked = 0;
  172. pEPD->uiPeriodAcksBytes = 0;
  173. pEPD->uiPeriodXmitTime = 0;
  174. pEPD->uiPeriodRateB = 0;
  175. pEPD->uiPeakRateB = 0;
  176. pEPD->uiLastRateB = 0;
  177. pEPD->ulReceiveMask = 0;
  178. pEPD->ulReceiveMask2 = 0;
  179. pEPD->tReceiveMaskDelta = 0;
  180. pEPD->ulSendMask = 0;
  181. pEPD->ulSendMask2 = 0;
  182. pEPD->Context = NULL;
  183. DPFX(DPFPREP,7, "CONNECTION ESTABLISHED pEPD = 0x%p RTT = %dms, BurstGap=%dms", pEPD, pEPD->uiRTT, pEPD->uiBurstGap);
  184. // We set the IdleThreshhold very low to generate a little bit of traffic for initial link tuning in case the
  185. // application doesnt do any right away
  186. pEPD->ulEPFlags |= EPFLAGS_USE_POLL_DELAY; // always assume balanced traffic at start-up
  187. pEPD->uiAdaptAlgCount = 4; // start running adpt alg fairly often
  188. // Calc a retry timeout value based upon the measured RTT (2.5 * RTT) + MAX_DELAY
  189. pEPD->uiRetryTimeout = ((pEPD->uiRTT + (pEPD->uiRTT >> 2)) * 2) + DELAYED_ACK_TIMEOUT;
  190. // don't want to get more aggressive because we drop a frame.
  191. if(pEPD->uiRetryTimeout < pEPD->uiBurstGap)
  192. {
  193. pEPD->uiRetryTimeout = pEPD->uiBurstGap;
  194. }
  195. pEPD->uiUserFrameLength = pEPD->pSPD->uiUserFrameLength;
  196. if(pEPD->BGTimer == 0)
  197. {
  198. if (pEPD->pSPD->pPData->tIdleThreshhold > ENDPOINT_BACKGROUND_INTERVAL)
  199. {
  200. dwTimerInterval = ENDPOINT_BACKGROUND_INTERVAL;
  201. }
  202. else
  203. {
  204. dwTimerInterval = pEPD->pSPD->pPData->tIdleThreshhold;
  205. }
  206. DPFX(DPFPREP,7, "(%p) Setting Endpoint Background Timer for %u ms", pEPD, dwTimerInterval);
  207. SetMyTimer(dwTimerInterval, 1000, EndPointBackgroundProcess, (PVOID) pEPD, &pEPD->BGTimer, &pEPD->BGTimerUnique);
  208. LOCK_EPD(pEPD, "LOCK (BG Timer)"); // create reference for this timer
  209. }
  210. }
  211. /****************
  212. *
  213. * Link Tuning
  214. *
  215. * Here are current ideas about link tuning. Idea is to track Round Trip Time of key-frames and throttle
  216. * based upon changes in this measured RTT when possible. This would benefit us in determining link saturation
  217. * before packet loss occurs, instead of waiting for the inevitable packet loss before throttling back.
  218. *
  219. * On high-speed media, the average RTT is small compared to the standard deviations making it hard to
  220. * predict anything useful from them. In these cases, we must look at packet drops. Except for one exception:
  221. * We will look for large spikes in RTT and we will respond to these with an immediate, temporary throttle back.
  222. * This will allow a bottle-neck to clear hopefully without packet-loss. So far, I have not been able to verfify
  223. * any benefit from this behavior on reliable links. It is more likely to be beneficial with datagram traffic
  224. * where send windows do not limit write-ahead.
  225. *
  226. * I would like to take a measurement of the through-put acheived compared to the transmission rate, but I
  227. * havent yet come up with a good way to measure this. What I do calculate is packet acknowledgement rate, which
  228. * can be calculated without any additional input from the remote side. We will store AckRates acheived at the
  229. * previous transmission rate, so we can look for improvements in Acks as we increase Transmissions. When we
  230. * no longer detect AckRate improvements then we assume we have plateaued and we stop trying to increase the rate.
  231. *
  232. * TRANSMISSION RATE
  233. *
  234. * Transmission rate is controlled by two distinct parameters: Insertion Rate and Window Size. Where a
  235. * conventional protocol would dump a window full of packets onto the wire in one burst, we would like to
  236. * spread the packet insertions out over the full RTT so that the window never completely fills and hence
  237. * blocks the link from transmitting. This has a wide array of potential benefits: Causes less congestions
  238. * throughout the network path; Allows more balanced access to the wire to all Endpoints (especially on
  239. * slower media); Allows MUCH more accurate measurements to be made of trasmission times when packets
  240. * spend less time enqueued locally; Allows retry timers to be set much lower giving us quicker error
  241. * recovery (because there is less queue time fudged into the timer); Allows recovery to be made more
  242. * quickly when we don't have a lot of data enqueued in SP (both our own data and other Endpoint's data).
  243. * ...And I am sure there are more.
  244. *
  245. * So, we would like to trickle out packets just fast enough to fill the window as the next ACK is received.
  246. * We will grow the window fairly liberally and let the burst rate increase more cautiously.
  247. *
  248. * On high-speed media the insertion time becomes fairly small (near zero) and we are less likely to queue
  249. * up large quantities of data. Therefore we may allow insertion rate to go max and use the window alone to
  250. * control flow. I will experiment with this more.
  251. *
  252. ******************/
  253. #define RTT_SLOW_WEIGHT 8 // fpRTT gain = 1/8
  254. #define THROTTLE_EVENT_THRESHOLD 20
  255. /*
  256. ** Update Endpoint
  257. **
  258. ** We will let the sliding window control the flow
  259. ** and increase the window as long as through-put continues to increase and frames continue to get delivered without
  260. ** excessive droppage.
  261. **
  262. ** We still calculate RTT for the purpose of determining RetryTimer values. For cases with large RTTs we may still
  263. ** implement an inter-packet gap, but we will try to make it an aggressive gap (conservatively small) because we would
  264. ** rather feed the pipe too quickly than artificially add latency by letting the pipe go idle with data ready to be sent.
  265. **
  266. ** ** CALLED WITH EPD STATELOCK HELD **
  267. */
  268. #undef DPF_MODNAME
  269. #define DPF_MODNAME "UpdateEndPoint"
  270. VOID UpdateEndPoint(PEPD pEPD, UINT uiRTT, UINT payload, UINT bias, DWORD tNow)
  271. {
  272. UINT fpRTT;
  273. INT fpDiff;
  274. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  275. // Don't allow zero RTTs
  276. if(uiRTT == 0)
  277. {
  278. uiRTT = 1;
  279. }
  280. // Filter out HUGE samples, they often popup during debug sessions
  281. else if(uiRTT > (pEPD->uiRTT * 128))
  282. {
  283. DPFX(DPFPREP,7, "Tossing huge sample (%dms)", uiRTT);
  284. return;
  285. }
  286. // Perform next iteration of math on new RTT sample in 16.16 fixed point
  287. fpRTT = TO_FP(uiRTT); // Fixed point sample
  288. fpDiff = fpRTT - pEPD->fpRTT; // Current Delta (signed)
  289. pEPD->fpRTT = pEPD->fpRTT + (fpDiff / RTT_SLOW_WEIGHT); // .0625 weighted avg
  290. pEPD->uiRTT = FP_INT(pEPD->fpRTT); // Store integer portion
  291. // Calc a retry timeout value based upon the measured RTT (2.5 * RTT) + MAX_DELAY
  292. pEPD->uiRetryTimeout = ((pEPD->uiRTT + (pEPD->uiRTT >> 2)) * 2) + DELAYED_ACK_TIMEOUT;
  293. // don't want to get more aggressive because we drop a frame.
  294. if(pEPD->uiRetryTimeout < pEPD->uiBurstGap)
  295. {
  296. pEPD->uiRetryTimeout = pEPD->uiBurstGap;
  297. }
  298. DPFX(DPFPREP,7, "(%p) RTT SAMPLE: Size = %d; RTT = %d, Avg = %d <<<<", pEPD, payload, uiRTT, FP_INT(pEPD->fpRTT));
  299. // If throttle is engaged we will see if we can release it yet
  300. if(pEPD->ulEPFlags & EPFLAGS_THROTTLED_BACK)
  301. {
  302. if((tNow - pEPD->tThrottleTime) > (pEPD->uiRTT * 8))
  303. {
  304. pEPD->ulEPFlags &= ~(EPFLAGS_THROTTLED_BACK);
  305. pEPD->uiDropCount = 0;
  306. pEPD->uiBurstGap = pEPD->uiRestoreBurstGap;
  307. pEPD->uiWindowF = pEPD->uiRestoreWindowF;
  308. pEPD->uiWindowBIndex = pEPD->uiRestoreWindowBI;
  309. pEPD->uiWindowB = pEPD->uiWindowBIndex * pEPD->pSPD->uiFrameLength;
  310. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "** (%p) RECOVER FROM THROTTLE EVENT: Window(F:%d,B:%d); Gap=%d", pEPD, pEPD->uiWindowF, pEPD->uiWindowBIndex, pEPD->uiBurstGap);
  311. pEPD->tLastDelta = tNow; // Enforce waiting period after back-off before tuning up again
  312. }
  313. }
  314. // Throttle Event tracks how often a packet-drop has caused us to throttle back transmission rate. We will let this value
  315. // decay over time. If throttle events happen faster then the decay occurs then this value will grow un-bounded. This
  316. // growth is what causes a decrease in the actual send window/transmit rate that will persist beyond the throttle event.
  317. else if(pEPD->uiThrottleEvents)
  318. {
  319. pEPD->uiThrottleEvents--; // Let this decay...
  320. }
  321. if(--pEPD->uiAdaptAlgCount == 0)
  322. {
  323. RunAdaptiveAlg(pEPD, tNow);
  324. }
  325. }
  326. /*
  327. ** Grow Send Window
  328. **
  329. ** The two parallel send windows, frame-based and byte-based, can grow and shrink independently. In this
  330. ** routine we will grow one or both windows. We will grow each window providing that it has been filled in the
  331. ** last period, during which we have determined that thru-put has increased.
  332. */
  333. #undef DPF_MODNAME
  334. #define DPF_MODNAME "GrowSendWindow"
  335. BOOL
  336. GrowSendWindow(PEPD pEPD, DWORD tNow)
  337. {
  338. UINT delta = 0;
  339. pEPD->tLastDelta = tNow;
  340. // first store current good values for a restore
  341. pEPD->uiGoodWindowF = pEPD->uiWindowF;
  342. pEPD->uiGoodWindowBI = pEPD->uiWindowBIndex;
  343. pEPD->uiGoodRTT = pEPD->uiRTT;
  344. #ifdef USE_BURST_GAP
  345. pEPD->uiGoodBurstGap=pEPD->uiBurstGap;
  346. if(pEPD->uiBurstGap)
  347. {
  348. // cut the burst gap by 25% if less than 3 ms go to 0.
  349. if(pEPD->uiBurstGap > 3)
  350. {
  351. pEPD->uiBurstGap -= pEPD->uiBurstGap >> 2;
  352. }
  353. else
  354. {
  355. pEPD->uiBurstGap = 0;
  356. }
  357. pEPD->uiLastRateB = pEPD->uiPeriodRateB;
  358. pEPD->uiPeriodAcksBytes = 0;
  359. pEPD->uiPeriodXmitTime = 0;
  360. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p), burst gap set to %d ms", pEPD, pEPD->uiBurstGap);
  361. }
  362. else
  363. {
  364. #endif
  365. if((pEPD->ulEPFlags & EPFLAGS_FILLED_WINDOW_FRAME) && (pEPD->uiWindowF < MAX_RECEIVE_RANGE))
  366. {
  367. pEPD->uiWindowF++;
  368. delta = 1;
  369. }
  370. if((pEPD->ulEPFlags & EPFLAGS_FILLED_WINDOW_BYTE) && (pEPD->uiWindowBIndex < MAX_RECEIVE_RANGE))
  371. {
  372. pEPD->uiWindowBIndex++;
  373. pEPD->uiWindowB += pEPD->pSPD->uiFrameLength;
  374. delta = 1;
  375. }
  376. pEPD->ulEPFlags &= ~(EPFLAGS_FILLED_WINDOW_FRAME | EPFLAGS_FILLED_WINDOW_BYTE);
  377. pEPD->uiWindowFilled = 0;
  378. if(delta)
  379. {
  380. pEPD->uiLastRateB = pEPD->uiPeriodRateB;
  381. pEPD->uiPeriodAcksBytes = 0;
  382. pEPD->uiPeriodXmitTime = 0;
  383. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) ** GROW SEND WINDOW to %d frames and %d (%d) bytes", pEPD, pEPD->uiWindowF, pEPD->uiWindowB, pEPD->uiWindowBIndex);
  384. }
  385. else
  386. {
  387. // We get here if we have already max'd out the window
  388. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) GROW SEND WINDOW -- Nothing to grow. Transition to Stable!", pEPD);
  389. pEPD->ulEPFlags |= EPFLAGS_LINK_STABLE;
  390. return FALSE;
  391. }
  392. #ifdef USE_BURST_GAP
  393. }
  394. #endif
  395. return TRUE;
  396. }
  397. #undef DPF_MODNAME
  398. #define DPF_MODNAME "RunAdaptiveAlg"
  399. VOID
  400. RunAdaptiveAlg(PEPD pEPD, DWORD tNow)
  401. {
  402. LONG tDelta; // Time the link was transmitting since last run of AdaptAlg
  403. UINT uiBytesAcked;
  404. UINT uiNewSum;
  405. // Calculate the time during which this link was actually transmitting to make sure we have enough
  406. // data to run the Adaptive Alg. This is easy unless we are currently idle...
  407. tDelta = tNow - pEPD->tLastThruPutSample;
  408. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) Adaptive Alg tDelta = %d", pEPD, tDelta);
  409. // THIS PROBABLY IS UNNECESSARY NOW...
  410. if(tDelta <= 0)
  411. {
  412. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "DELAYING Adaptive Alg");
  413. pEPD->uiAdaptAlgCount = 4;
  414. return;
  415. }
  416. // Calculate current throughput acheived
  417. //
  418. // We will determine the amount of time the link was not idle and then number of bytes (& frames) which
  419. // were acknowleged by our partner.
  420. //
  421. // tDelta = Time since last calculation minus the time the link was idle.
  422. uiBytesAcked = pEPD->uiBytesAcked - pEPD->uiLastBytesAcked;
  423. uiNewSum = pEPD->uiPeriodAcksBytes + (uiBytesAcked * 256);
  424. if(uiNewSum < pEPD->uiPeriodAcksBytes)
  425. {
  426. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "THRUPUT is about to wrap. Correcting...");
  427. pEPD->uiPeriodAcksBytes /= 2;
  428. pEPD->uiPeriodXmitTime /= 2;
  429. pEPD->uiPeriodAcksBytes += (uiBytesAcked * 256);
  430. }
  431. else
  432. {
  433. pEPD->uiPeriodAcksBytes = uiNewSum;
  434. }
  435. pEPD->uiPeriodXmitTime += tDelta; // Track complete values for this period
  436. pEPD->tLastThruPutSample = tNow;
  437. pEPD->uiLastBytesAcked = pEPD->uiBytesAcked;
  438. pEPD->uiPeriodRateB = pEPD->uiPeriodAcksBytes / pEPD->uiPeriodXmitTime;
  439. if(pEPD->uiPeriodRateB > pEPD->uiPeakRateB)
  440. {
  441. pEPD->uiPeakRateB = pEPD->uiPeriodRateB; // Track the largest value we ever measure
  442. }
  443. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) PERIOD COUNT BYTES = %u, XmitTime = %u, Thruput=(%u bytes/s), RTT=%u, Window=(%u,%u)", pEPD, pEPD->uiPeriodAcksBytes, pEPD->uiPeriodXmitTime, pEPD->uiPeriodRateB * 4, pEPD->uiRTT, pEPD->uiWindowF, pEPD->uiWindowB);
  444. if (pEPD->ulEPFlags & EPFLAGS_LINK_FROZEN)
  445. {
  446. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) Test App requests that dynamic algorithm not be run, skipping", pEPD);
  447. pEPD->uiAdaptAlgCount = 32; // Make sure the throughput numbers get updated from time to time
  448. return;
  449. }
  450. if(pEPD->ulEPFlags & EPFLAGS_LINK_STABLE)
  451. {
  452. /* We are in a STABLE state, meaning we think we are transmitting at an optimal
  453. ** rate for the current network conditions. Conditions may change. If things slow down
  454. ** or grow congested a Backoff will trigger normally. Since conditions might also change
  455. ** for the better, we will still want to periodically probe higher rates, but much less
  456. ** often than when we are in DYNAMIC mode, which means we are searching for an optimal rate.
  457. */
  458. pEPD->uiAdaptAlgCount = 32; // tNow + (pEPD->uiRTT * 32) + 32;
  459. if((tNow - pEPD->tLastDelta) > INITIAL_STATIC_PERIOD)
  460. {
  461. if(pEPD->ulEPFlags & (EPFLAGS_FILLED_WINDOW_FRAME | EPFLAGS_FILLED_WINDOW_BYTE))
  462. {
  463. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) RETURNING LINK TO DYNAMIC MODE", pEPD);
  464. pEPD->ulEPFlags &= ~(EPFLAGS_LINK_STABLE);
  465. pEPD->uiPeriodAcksBytes = 0;
  466. pEPD->uiPeriodXmitTime = 0;
  467. pEPD->uiWindowFilled = 0;
  468. pEPD->ulEPFlags &= ~(EPFLAGS_FILLED_WINDOW_FRAME | EPFLAGS_FILLED_WINDOW_BYTE);
  469. pEPD->uiAdaptAlgCount = 12;
  470. }
  471. else
  472. {
  473. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) NO WINDOWS FILLED, Not returning to Dynamic Mode", pEPD);
  474. pEPD->tLastDelta = tNow;
  475. }
  476. }
  477. else
  478. {
  479. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) STILL IN STATIC PERIOD, time=%u, period=%u", pEPD, tNow - pEPD->tLastDelta, INITIAL_STATIC_PERIOD);
  480. }
  481. }
  482. // DYNAMIC STATE LINK
  483. else
  484. {
  485. pEPD->uiAdaptAlgCount = 8;
  486. // Possibly increase transmission rates. We will not do this if we have had a ThrottleEvent
  487. // in recent memory, or if we have not been actually transmitting for enough of the interval
  488. // to have collected worthwhile data
  489. //
  490. // Also, we dont want to even consider growing the send window unless we are consistantly
  491. // filling it. Since one job of the window is to prevent us from flooding the net during a backup,
  492. // we dont want to grow the window following each backup. The best way to distinguish between a
  493. // backup and too small of a window is that the small window should fill up regularly while the
  494. // backups should only occur intermittantly. The hard part is coming up with the actual test.
  495. // Truth is, we can be fairly lax about allowing growth because it will also have to meet the increased
  496. // bandwidth test before the larger window is accepted. So a crude rule would be to fix a number like 3.
  497. // Yes, crude but probably effective. Perhaps a more reasonable figure would be a ratio of the total
  498. // number of packets sent divided by the window size. I.e., if your window size is 10 frames then one
  499. // packet in ten should fill the window. Of course, this would have to be calculated in bytes...
  500. if((pEPD->uiWindowFilled > 12)&&(pEPD->uiThrottleEvents == 0))
  501. {
  502. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) DYNAMIC ALG: Window Fills: %d; B-Ack = (%x vs %x)", pEPD, pEPD->uiWindowFilled, pEPD->uiPeriodRateB, pEPD->uiLastRateB);
  503. pEPD->uiWindowFilled = 0;
  504. if (!(pEPD->ulEPFlags & EPFLAGS_TESTING_GROWTH))
  505. {
  506. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) GROWING WINDOW", pEPD);
  507. // In the case that GrowSendWindow doesn't grow anything because we are already max'd out
  508. // it will return FALSE, and it should have transitioned us to STABLE.
  509. if (GrowSendWindow(pEPD, tNow))
  510. {
  511. pEPD->ulEPFlags |= EPFLAGS_TESTING_GROWTH;
  512. }
  513. else
  514. {
  515. ASSERT(pEPD->ulEPFlags & EPFLAGS_LINK_STABLE);
  516. }
  517. return;
  518. }
  519. // GETTING HERE means that we have used our current transmit parameters long enough
  520. // to have an idea of their performance. We will now compare this to the performance
  521. // of the previous transmit parameters and we will either Revert to the previous set if
  522. // the perf is not improved, or else we will advance to faster parameters if we did see
  523. // a jump.
  524. // In order to keep higher transmit parameters we need to see an increase in throughput
  525. // with no corresponding rise in RTT. We will want to see this twice just to be sure
  526. // since the cost of incorrect growth is so high on a modem.
  527. if( (pEPD->uiPeriodRateB > pEPD->uiLastRateB) &&
  528. (pEPD->uiRTT <= (pEPD->uiGoodRTT + 10))
  529. )
  530. {
  531. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) Throughput increased after window growth, keeping new parameters", pEPD);
  532. pEPD->ulEPFlags &= ~(EPFLAGS_TESTING_GROWTH);
  533. pEPD->uiPeriodAcksBytes = 0;
  534. pEPD->uiPeriodXmitTime = 0;
  535. }
  536. else
  537. {
  538. // We did not see a thru-put improvement so we will back off the previous value
  539. // and transition the link to STABLE state.
  540. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) INSUFFICENT INCREASE IN THRUPUT, BACK OFF AND TRANSITION TO STABLE", pEPD);
  541. // Because we have over-transmitted for at least one period, we may have put excess data
  542. // on the link in a buffer. This will have the effect of gradually growing our RTT if we
  543. // don't bleed that data off which we will do here by backing off two steps where we
  544. // previously grew one step.
  545. #ifdef USE_BURST_GAP
  546. if (pEPD->uiBurstGap != pEPD->uiGoodBurstGap)
  547. {
  548. // increase the burst gap by 25%
  549. pEPD->uiBurstGap = pEPD->uiGoodBurstGap + (pEPD->uiGoodBurstGap >> 2);
  550. }
  551. #endif
  552. if (pEPD->uiWindowF != pEPD->uiGoodWindowF)
  553. {
  554. if (pEPD->uiGoodWindowF > 2)
  555. {
  556. pEPD->uiWindowF = MAX(pEPD->uiGoodWindowF - 1, 1);
  557. }
  558. else
  559. {
  560. pEPD->uiWindowF = pEPD->uiGoodWindowF;
  561. }
  562. }
  563. if (pEPD->uiWindowBIndex != pEPD->uiGoodWindowBI)
  564. {
  565. pEPD->uiWindowBIndex = MAX(pEPD->uiGoodWindowBI - 1, 1);
  566. pEPD->uiWindowB = pEPD->uiWindowBIndex * pEPD->pSPD->uiFrameLength;
  567. }
  568. pEPD->ulEPFlags |= EPFLAGS_LINK_STABLE; // TRANSITION TO STABLE STATE
  569. pEPD->ulEPFlags &= ~(EPFLAGS_TESTING_GROWTH);
  570. pEPD->uiPeriodAcksBytes = 0;
  571. pEPD->uiPeriodXmitTime = 0;
  572. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) ** TUNING LINK: BurstGap=%d; FWindow=%d, BWindow=%d (%d)",pEPD, pEPD->uiBurstGap, pEPD->uiWindowF, pEPD->uiWindowB, pEPD->uiWindowBIndex);
  573. }
  574. }
  575. else
  576. {
  577. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) DYN ALG -- Not trying to increase: WindowFills = %d, ThrottleCount = %d", pEPD, pEPD->uiWindowFilled, pEPD->uiThrottleEvents);
  578. }
  579. } // END IF DYNAMIC STATE LINK
  580. }
  581. /*
  582. ** End Point Dropped Frame
  583. **
  584. ** We have two levels of Backoff. We have an immediate BackOff implemented
  585. ** upon first detection of a drop-event in order to relieve the congestion which
  586. ** caused the drop. An immediate backoff will resume transmitting at the original
  587. ** rate without going through slow-start again after the congestion event has passed.
  588. ** If we have multiple immediate-backoffs in a certain interval we will have a
  589. ** hard backoff which will not restore.
  590. **
  591. ** CALLED WITH EPD->SPLock held (and sometimes with StateLock held too)
  592. */
  593. #undef DPF_MODNAME
  594. #define DPF_MODNAME "EndPointDroppedFrame"
  595. VOID
  596. EndPointDroppedFrame(PEPD pEPD, DWORD tNow)
  597. {
  598. // If this is first drop in short interval
  599. if(!pEPD->uiDropCount++)
  600. {
  601. DPFX(DPFPREP,7, "DROP EVENT INITIATED: Throttling Back");
  602. ThrottleBack(pEPD, tNow);
  603. }
  604. // Multiple drops reported in short order
  605. else
  606. {
  607. // There are two possibilities and trick is to distinguish them. Either we are still xmitting too fast
  608. // OR we are seeing additional drops caused by the previous xmit rate. In first case we must back off
  609. // further. In second case we should do nothing. As a heuristic, we can say we will ignore the second
  610. // and third drop, but backoff further by the fourth. Kinda kludgy but will probably work pretty well.
  611. // Robust solution would be to try to calculate back-log ala aaron, but I am not convinced that we could
  612. // do that efficiently. Alternatively, we could just fake the calc for the back-off and back off some
  613. // fudge based upon RTT and outstanding frames.
  614. // Throttle back every fourth drop!
  615. if((pEPD->uiDropCount & 3) == 0)
  616. {
  617. DPFX(DPFPREP,7, "(%p) THROTTLING BACK FURTHER", pEPD);
  618. ThrottleBack(pEPD, tNow);
  619. }
  620. }
  621. }
  622. /*
  623. ** Throttle Back
  624. **
  625. ** We suspect network congestion due to dropped frames ((or a spike in latency)). We want
  626. ** to quickly scale back our transmit rate to releive the congestion and avoid further packet drops.
  627. ** This is a temporary backoff and we will resume our current transmit rate when the congestions
  628. ** clears.
  629. **
  630. ** If we find that we are throttling back frequently then we may conclude that our current xmit
  631. ** rate is higher then optimal and we will BackOff to a lower rate, and transition to a STABLE link
  632. ** state (if not already there) to indicate that we have plateaued.
  633. **
  634. ** A note on convergence. The ThrottleEvents variable is incremented 10 points each time a throttle
  635. ** event is triggered. This variable also decays slowly when the link is running without events. So if
  636. ** the variable grows faster then it decays we will eventually trigger a switch to STABLE state
  637. */
  638. #undef DPF_MODNAME
  639. #define DPF_MODNAME "ThrottleBack"
  640. VOID
  641. ThrottleBack(PEPD pEPD, DWORD tNow)
  642. {
  643. if (pEPD->ulEPFlags & EPFLAGS_LINK_FROZEN)
  644. {
  645. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) Test App requests that throttle code not be run, skipping", pEPD);
  646. return;
  647. }
  648. pEPD->ulEPFlags |= EPFLAGS_THROTTLED_BACK; // Set link to THROTTLED state
  649. pEPD->uiThrottleEvents += 10; // Count times we throttle-back for all reasons
  650. pEPD->tThrottleTime = tNow; // Remember time that throttle was engaged
  651. #ifdef DEBUG
  652. pEPD->uiTotalThrottleEvents++; // Count times we throttle-back for all reasons
  653. #endif
  654. pEPD->uiRestoreBurstGap = pEPD->uiBurstGap;
  655. pEPD->uiRestoreWindowF = pEPD->uiWindowF;
  656. pEPD->uiRestoreWindowBI = pEPD->uiWindowBIndex;
  657. #ifdef USE_BURST_GAP
  658. if(pEPD->uiWindowF == 1)
  659. {
  660. if(pEPD->uiBurstGap == 0)
  661. {
  662. pEPD->uiBurstGap = MAX(1,pEPD->uiRTT/2);
  663. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p), first burst gap, set to %d ms", pEPD, pEPD->uiBurstGap);
  664. }
  665. else
  666. {
  667. pEPD->uiBurstGap = pEPD->uiBurstGap*2;
  668. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p), burst gap doubled to %d ms", pEPD, pEPD->uiBurstGap);
  669. }
  670. pEPD->uiBurstGap = MIN(pEPD->uiBurstGap, MAX_RETRY_INTERVAL/2);
  671. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p), burst gap is now %d ms", pEPD, pEPD->uiBurstGap);
  672. }
  673. #endif
  674. pEPD->uiWindowF = MAX(pEPD->uiWindowF / 2, 1); // be sure window remains > 0.
  675. pEPD->uiWindowBIndex = MAX(pEPD->uiWindowBIndex / 2, 1);
  676. pEPD->uiWindowB = pEPD->uiWindowBIndex * pEPD->pSPD->uiFrameLength;
  677. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) THROTTLE ENGAGED (%d): Backoff to Window=%d; Gap=%d", pEPD, pEPD->uiThrottleEvents, pEPD->uiWindowF, pEPD->uiBurstGap);
  678. if(pEPD->uiThrottleEvents > THROTTLE_EVENT_THRESHOLD)
  679. {
  680. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) ** DETECT TRANSMIT CEILING ** Reducing 'good' speed and marking link STABLE", pEPD);
  681. // We have already reduced our current transmit rates. Here we will reduce the "good" rates that
  682. // we will restore to when we clear the throttled state.
  683. pEPD->uiThrottleEvents = 0;
  684. pEPD->uiRestoreWindowF = MAX((pEPD->uiRestoreWindowF - 1), 1);
  685. pEPD->uiRestoreWindowBI = MAX((pEPD->uiRestoreWindowBI - 1), 1);
  686. #ifdef USE_BURST_GAP
  687. if (pEPD->uiRestoreBurstGap)
  688. {
  689. UINT t;
  690. t=pEPD->uiRestoreBurstGap;
  691. pEPD->uiRestoreBurstGap = (t+1) + (t >> 2); // 1.25*pEPD->uiRestoreBurstGap
  692. }
  693. #endif
  694. DPFX(DPFPREP,DPF_ADAPTIVE_LVL, "(%p) New Restore Values: Window=%d; Gap=%d", pEPD, pEPD->uiRestoreWindowF, pEPD->uiRestoreBurstGap);
  695. pEPD->ulEPFlags |= EPFLAGS_LINK_STABLE;
  696. pEPD->ulEPFlags &= ~(EPFLAGS_TESTING_GROWTH);
  697. }
  698. }
  699. /*
  700. ** EPD Pool Support Routines
  701. **
  702. ** These are the functions called by Fixed Pool Manager as it handles EPDs.
  703. */
  704. // Allocate is called when a new EPD is first created
  705. #define pELEMENT ((PEPD) pElement)
  706. #undef DPF_MODNAME
  707. #define DPF_MODNAME "EPD_Allocate"
  708. BOOL EPD_Allocate(PVOID pElement)
  709. {
  710. DPFX(DPFPREP,7, "(%p) Allocating new EPD", pELEMENT);
  711. pELEMENT->blHighPriSendQ.Initialize(); // Can you beleive there are SIX send queues per Endpoint?
  712. pELEMENT->blNormPriSendQ.Initialize(); // Six send queues.
  713. pELEMENT->blLowPriSendQ.Initialize(); // Well, it beats sorting the sends into the queues upon submission.
  714. pELEMENT->blCompleteSendList.Initialize();
  715. pELEMENT->blSendWindow.Initialize();
  716. pELEMENT->blRetryQueue.Initialize();
  717. pELEMENT->blCompleteList.Initialize();
  718. pELEMENT->blOddFrameList.Initialize();
  719. pELEMENT->blChkPtQueue.Initialize();
  720. pELEMENT->blSPLinkage.Initialize();
  721. if (DNInitializeCriticalSection(&pELEMENT->EPLock) == FALSE)
  722. {
  723. DPFX(DPFPREP, 0, "Failed to initialize endpoint CS");
  724. return FALSE;
  725. }
  726. DebugSetCriticalSectionRecursionCount(&pELEMENT->EPLock, 0);
  727. pELEMENT->Sign = EPD_SIGN;
  728. pELEMENT->pCurrentSend = NULL;
  729. pELEMENT->pCurrentFrame = NULL;
  730. pELEMENT->pCommand = NULL;
  731. pELEMENT->RetryTimer = 0;
  732. pELEMENT->ConnectTimer = 0;
  733. pELEMENT->DelayedAckTimer = 0;
  734. pELEMENT->ulEPFlags = 0; // EPFLAGS_STATE_CLEAR - make this line show up in state searches
  735. return TRUE;
  736. }
  737. // Get is called each time an EPD is used
  738. #undef DPF_MODNAME
  739. #define DPF_MODNAME "EPD_Get"
  740. VOID EPD_Get(PVOID pElement)
  741. {
  742. DPFX(DPFPREP,DPF_EP_REFCNT_FINAL_LVL, "CREATING EPD %p", pELEMENT);
  743. // NOTE: First sizeof(PVOID) bytes will have been overwritten by the pool code,
  744. // we must set them to acceptable values.
  745. pELEMENT->hEndPt = INVALID_HANDLE_VALUE;
  746. pELEMENT->lRefCnt = 0; // We are -1 based, so place the first reference on the endpoint
  747. pELEMENT->pNewMessage = NULL;
  748. pELEMENT->pNewTail = NULL;
  749. ASSERT_EPD(pELEMENT);
  750. }
  751. #undef DPF_MODNAME
  752. #define DPF_MODNAME "EPD_Release"
  753. VOID EPD_Release(PVOID pElement)
  754. {
  755. PCHKPT pCP;
  756. ASSERT_EPD(pELEMENT);
  757. DPFX(DPFPREP,DPF_EP_REFCNT_FINAL_LVL, "RELEASING EPD %p", pELEMENT);
  758. ASSERT((pELEMENT->ulEPFlags & EPFLAGS_LINKED_TO_LISTEN)==0);
  759. // Clear any checkpoints still waiting on EP
  760. while(!pELEMENT->blChkPtQueue.IsEmpty())
  761. {
  762. pCP = CONTAINING_RECORD(pELEMENT->blChkPtQueue.GetNext(), CHKPT, blLinkage);
  763. pCP->blLinkage.RemoveFromList();
  764. ChkPtPool->Release(ChkPtPool, pCP);
  765. }
  766. // These lists should be empty before End Point is released...
  767. ASSERT(pELEMENT->blOddFrameList.IsEmpty());
  768. ASSERT(pELEMENT->blCompleteList.IsEmpty());
  769. ASSERT(pELEMENT->blHighPriSendQ.IsEmpty());
  770. ASSERT(pELEMENT->blNormPriSendQ.IsEmpty());
  771. ASSERT(pELEMENT->blLowPriSendQ.IsEmpty());
  772. ASSERT(pELEMENT->blCompleteSendList.IsEmpty());
  773. ASSERT(pELEMENT->blSendWindow.IsEmpty());
  774. ASSERT(pELEMENT->blRetryQueue.IsEmpty());
  775. ASSERT(pELEMENT->blActiveLinkage.IsEmpty());
  776. ASSERT(pELEMENT->blSPLinkage.IsEmpty());
  777. ASSERT(pELEMENT->blChkPtQueue.IsEmpty());
  778. ASSERT(pELEMENT->pCurrentSend == NULL);
  779. ASSERT(pELEMENT->pCurrentFrame == NULL);
  780. pELEMENT->ulEPFlags = 0; // EPFLAGS_STATE_CLEAR - make this line show up in state searches
  781. pELEMENT->pCommand = NULL;
  782. pELEMENT->Context = NULL;
  783. }
  784. #undef DPF_MODNAME
  785. #define DPF_MODNAME "EPD_Free"
  786. VOID EPD_Free(PVOID pElement)
  787. {
  788. DNDeleteCriticalSection(&pELEMENT->EPLock);
  789. }
  790. #undef ELEMENT