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.

533 lines
17 KiB

  1. /****************************************************************************/
  2. // buffer.c
  3. //
  4. // TermDD default OutBuf management.
  5. //
  6. // Copyright (C) 1998-2000 Microsoft Corporation
  7. /****************************************************************************/
  8. #include <precomp.h>
  9. #pragma hdrstop
  10. /*
  11. * Define default stack size for IRP allocation.
  12. * (This size will be checked by the TdRawWrite routine.)
  13. */
  14. #define OUTBUF_STACK_SIZE 4
  15. #define OUTBUF_TIMEOUT 60000 // 1 minute
  16. #if DBG
  17. extern PICA_DISPATCH IcaStackDispatchTable[];
  18. #endif
  19. /****************************************************************************/
  20. // IcaBufferGetUsableSpace
  21. //
  22. // Used by the protocol stack drivers to determine the number of usable bytes
  23. // in a TermDD-created OutBuf, given the total size of the OutBuf. This allows
  24. // a stack driver to target a particular OutBuf size and pack data up to the
  25. // edges of the internal OutBuf headers. The returned size can be used as an
  26. // allocation size request that will return an OutBuf of the right size.
  27. /****************************************************************************/
  28. unsigned IcaBufferGetUsableSpace(unsigned OutBufTotalSize)
  29. {
  30. unsigned IrpSize;
  31. unsigned MdlSize;
  32. unsigned MaxOutBufOverhead;
  33. // Use the same overhead calculations used in IcaBufferAllocInternal()
  34. // below, plus a 4-byte offset to cover the extra 1-byte difference
  35. // required in the requesting size to reach a target buffer size.
  36. IrpSize = IoSizeOfIrp(OUTBUF_STACK_SIZE) + 8;
  37. if (OutBufTotalSize <= MaxOutBufAlloc)
  38. MdlSize = MaxOutBufMdlOverhead;
  39. else
  40. MdlSize = (unsigned)MmSizeOfMdl((PVOID)(PAGE_SIZE - 1),
  41. OutBufTotalSize);
  42. MaxOutBufOverhead = ((sizeof(OUTBUF) + 7) & ~7) + IrpSize + MdlSize;
  43. return OutBufTotalSize - MaxOutBufOverhead - 4;
  44. }
  45. /*******************************************************************************
  46. * IcaBufferAlloc
  47. *
  48. * pContext (input)
  49. * pointer to SDCONTEXT of caller
  50. * fWait (input)
  51. * wait for buffer
  52. * fControl (input)
  53. * control buffer flag
  54. * ByteCount (input)
  55. * size of buffer to allocate (zero - use default size)
  56. * pOutBufOrig (input)
  57. * pointer to original OUTBUF (or null)
  58. * pOutBuf (output)
  59. * address to return pointer to OUTBUF structure
  60. ******************************************************************************/
  61. NTSTATUS IcaBufferAlloc(
  62. IN PSDCONTEXT pContext,
  63. IN BOOLEAN fWait,
  64. IN BOOLEAN fControl,
  65. IN ULONG ByteCount,
  66. IN POUTBUF pOutBufOrig,
  67. OUT POUTBUF *ppOutBuf)
  68. {
  69. PSDLINK pSdLink;
  70. PICA_STACK pStack;
  71. NTSTATUS Status;
  72. /*
  73. * Use SD passed context to get the SDLINK pointer.
  74. */
  75. pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext);
  76. pStack = pSdLink->pStack;
  77. ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack);
  78. ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable);
  79. ASSERT(ExIsResourceAcquiredExclusiveLite( &pStack->Resource));
  80. /*
  81. * Walk up the SDLINK list looking for a driver which has specified
  82. * a BufferAlloc callup routine. If we find one, then call the
  83. * driver BufferAlloc routine to let it handle the call.
  84. */
  85. while ((pSdLink = IcaGetPreviousSdLink(pSdLink)) != NULL) {
  86. ASSERT( pSdLink->pStack == pStack );
  87. if (pSdLink->SdContext.pCallup->pSdBufferAlloc) {
  88. IcaReferenceSdLink(pSdLink);
  89. Status = (pSdLink->SdContext.pCallup->pSdBufferAlloc)(
  90. pSdLink->SdContext.pContext,
  91. fWait,
  92. fControl,
  93. ByteCount,
  94. pOutBufOrig,
  95. ppOutBuf);
  96. IcaDereferenceSdLink(pSdLink);
  97. return Status;
  98. }
  99. }
  100. /*
  101. * We didn't find a callup routine to handle the request,
  102. * so we'll process it here.
  103. */
  104. Status = IcaBufferAllocInternal(pContext, fWait, fControl, ByteCount,
  105. pOutBufOrig, ppOutBuf);
  106. TRACESTACK((pStack, TC_ICADD, TT_API3,
  107. "TermDD: IcaBufferAlloc: 0x%08x, Status=0x%x\n", *ppOutBuf,
  108. Status));
  109. return Status;
  110. }
  111. NTSTATUS IcaBufferAllocInternal(
  112. IN PSDCONTEXT pContext,
  113. IN BOOLEAN fWait,
  114. IN BOOLEAN fControl,
  115. IN ULONG ByteCount,
  116. IN POUTBUF pOutBufOrig,
  117. OUT POUTBUF *ppOutBuf)
  118. {
  119. PSDLINK pSdLink;
  120. PICA_STACK pStack;
  121. int PoolIndex;
  122. ULONG irpSize;
  123. ULONG mdlSize;
  124. ULONG AllocationSize;
  125. KIRQL oldIrql;
  126. PLIST_ENTRY Head;
  127. POUTBUF pOutBuf;
  128. NTSTATUS Status;
  129. unsigned MaxOutBufOverhead;
  130. /*
  131. * Use SD passed context to get the SDLINK pointer.
  132. */
  133. pSdLink = CONTAINING_RECORD( pContext, SDLINK, SdContext );
  134. pStack = pSdLink->pStack;
  135. ASSERT( pSdLink->pStack->Header.Type == IcaType_Stack );
  136. ASSERT( pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable );
  137. /*
  138. * If original buffer is specified use it's flags
  139. */
  140. if (pOutBufOrig) {
  141. fWait = (BOOLEAN) pOutBufOrig->fWait;
  142. fControl = (BOOLEAN) pOutBufOrig->fControl;
  143. }
  144. /*
  145. * Check if we already have our maximum number of buffers allocated
  146. */
  147. while (!fControl && (pStack->OutBufAllocCount >= pStack->OutBufCount)) {
  148. /*
  149. * increment performance counter
  150. */
  151. pStack->ProtocolStatus.Output.WaitForOutBuf++;
  152. /*
  153. * Return if it's not ok to wait
  154. */
  155. if (!fWait)
  156. return(STATUS_IO_TIMEOUT);
  157. /*
  158. * We hit the high watermark
  159. */
  160. pStack->fWaitForOutBuf = TRUE;
  161. /*
  162. * Only wait for non-control requests
  163. */
  164. KeClearEvent(&pStack->OutBufEvent);
  165. Status = IcaWaitForSingleObject(pContext, &pStack->OutBufEvent,
  166. OUTBUF_TIMEOUT);
  167. if (NT_SUCCESS(Status)) {
  168. if (Status != STATUS_WAIT_0)
  169. return STATUS_IO_TIMEOUT;
  170. }
  171. else {
  172. return Status;
  173. }
  174. }
  175. /*
  176. * If the caller did not specify a byte count
  177. * then use the standard outbuf size for this stack.
  178. */
  179. if (ByteCount == 0)
  180. ByteCount = pStack->OutBufLength;
  181. // Note MaxOutBufOverhead is the max for the default max allocation.
  182. // It will be recalculated if the requested alloc size is greater
  183. // than can be handled by the default.
  184. irpSize = IoSizeOfIrp(OUTBUF_STACK_SIZE) + 8;
  185. mdlSize = MaxOutBufMdlOverhead;
  186. MaxOutBufOverhead = ((sizeof(OUTBUF) + 7) & ~7) + irpSize + mdlSize;
  187. /*
  188. * Determine which buffer pool to use, if any,
  189. * and the OutBuf length to allocate, if necessary.
  190. * Note that the max requested ByteCount that will hit the buffer pool
  191. * is (MaxOutBufAlloc - 1 - MaxOutBufOverhead).
  192. */
  193. if ((ByteCount + MaxOutBufOverhead) < MaxOutBufAlloc) {
  194. ASSERT(((ByteCount + MaxOutBufOverhead) / MinOutBufAlloc) <
  195. (1 << NumAllocSigBits));
  196. PoolIndex = OutBufPoolMapping[(ByteCount + MaxOutBufOverhead) /
  197. MinOutBufAlloc];
  198. IcaAcquireSpinLock(&IcaSpinLock, &oldIrql);
  199. if (!IsListEmpty(&IcaFreeOutBufHead[PoolIndex])) {
  200. Head = RemoveHeadList(&IcaFreeOutBufHead[PoolIndex]);
  201. IcaReleaseSpinLock(&IcaSpinLock, oldIrql);
  202. pOutBuf = CONTAINING_RECORD(Head, OUTBUF, Links);
  203. ASSERT(pOutBuf->PoolIndex == PoolIndex);
  204. }
  205. else {
  206. IcaReleaseSpinLock(&IcaSpinLock, oldIrql);
  207. AllocationSize = OutBufPoolAllocSizes[PoolIndex];
  208. pOutBuf = ICA_ALLOCATE_POOL(NonPagedPool, AllocationSize);
  209. if (pOutBuf == NULL)
  210. return STATUS_NO_MEMORY;
  211. // Prevent leaking control OutBufs on OutBuf free.
  212. if (fControl)
  213. PoolIndex = FreeThisOutBuf;
  214. }
  215. }
  216. else {
  217. PoolIndex = FreeThisOutBuf;
  218. /*
  219. * Determine the sizes of the various components of an OUTBUF.
  220. * Note that these are all worst-case calculations --
  221. * actual size of the MDL may be smaller.
  222. */
  223. mdlSize = (ULONG)MmSizeOfMdl((PVOID)(PAGE_SIZE - 1), ByteCount);
  224. /*
  225. * Add up the component sizes of an OUTBUF to determine
  226. * the total size that is needed to allocate.
  227. */
  228. AllocationSize = ((sizeof(OUTBUF) + 7) & ~7) + irpSize + mdlSize +
  229. ((ByteCount + 3) & ~3);
  230. pOutBuf = ICA_ALLOCATE_POOL(NonPagedPool, AllocationSize);
  231. if (pOutBuf == NULL)
  232. return STATUS_NO_MEMORY;
  233. }
  234. /*
  235. * Initialize the IRP pointer and the IRP itself.
  236. */
  237. pOutBuf->pIrp = (PIRP)((BYTE *)pOutBuf + ((sizeof(OUTBUF) + 7) & ~7));
  238. IoInitializeIrp(pOutBuf->pIrp, (USHORT)irpSize, OUTBUF_STACK_SIZE);
  239. /*
  240. * Set up the MDL pointer but don't build it yet.
  241. * It will be built by the TD write code if needed.
  242. */
  243. pOutBuf->pMdl = (PMDL)((PCHAR)pOutBuf->pIrp + irpSize);
  244. /*
  245. * Set up the address buffer pointer.
  246. */
  247. pOutBuf->pBuffer = (PUCHAR)pOutBuf->pMdl + mdlSize +
  248. pStack->SdOutBufHeader;
  249. /*
  250. * Initialize the rest of output buffer
  251. */
  252. InitializeListHead(&pOutBuf->Links);
  253. pOutBuf->OutBufLength = ByteCount;
  254. pOutBuf->PoolIndex = PoolIndex;
  255. pOutBuf->MaxByteCount = ByteCount - (pStack->SdOutBufHeader +
  256. pStack->SdOutBufTrailer);
  257. pOutBuf->ByteCount = 0;
  258. pOutBuf->fIrpCompleted = FALSE;
  259. /*
  260. * Copy inherited fields
  261. */
  262. if (pOutBufOrig == NULL) {
  263. pOutBuf->fWait = fWait; // wait for outbuf allocation
  264. pOutBuf->fControl = fControl; // control buffer (ack/nak)
  265. pOutBuf->fRetransmit = FALSE; // not a retransmit
  266. pOutBuf->fCompress = TRUE; // compress data
  267. pOutBuf->StartTime = 0; // time stamp
  268. pOutBuf->Sequence = 0; // zero sequence number
  269. pOutBuf->Fragment = 0; // zero fragment number
  270. }
  271. else {
  272. pOutBuf->fWait = pOutBufOrig->fWait;
  273. pOutBuf->fControl = pOutBufOrig->fControl;
  274. pOutBuf->fRetransmit = pOutBufOrig->fRetransmit;
  275. pOutBuf->fCompress = pOutBufOrig->fCompress;
  276. pOutBuf->StartTime = pOutBufOrig->StartTime;
  277. pOutBuf->Sequence = pOutBufOrig->Sequence;
  278. pOutBuf->Fragment = pOutBufOrig->Fragment++;
  279. }
  280. /*
  281. * Increment allocated buffer count
  282. */
  283. pStack->OutBufAllocCount++;
  284. /*
  285. * Return buffer to caller
  286. */
  287. *ppOutBuf = pOutBuf;
  288. /*
  289. * Return buffer to caller
  290. */
  291. return STATUS_SUCCESS;
  292. }
  293. /*******************************************************************************
  294. * IcaBufferFree
  295. *
  296. * pContext (input)
  297. * pointer to SDCONTEXT of caller
  298. * pOutBuf (input)
  299. * pointer to OUTBUF structure
  300. ******************************************************************************/
  301. void IcaBufferFree(IN PSDCONTEXT pContext, IN POUTBUF pOutBuf)
  302. {
  303. PSDLINK pSdLink;
  304. PICA_STACK pStack;
  305. NTSTATUS Status;
  306. /*
  307. * Use SD passed context to get the SDLINK pointer.
  308. */
  309. pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext);
  310. pStack = pSdLink->pStack;
  311. ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack);
  312. ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable);
  313. ASSERT(ExIsResourceAcquiredExclusiveLite( &pStack->Resource));
  314. /*
  315. * Walk up the SDLINK list looking for a driver which has specified
  316. * a BufferFree callup routine. If we find one, then call the
  317. * driver BufferFree routine to let it handle the call.
  318. */
  319. while ((pSdLink = IcaGetPreviousSdLink(pSdLink)) != NULL) {
  320. ASSERT(pSdLink->pStack == pStack);
  321. if (pSdLink->SdContext.pCallup->pSdBufferFree) {
  322. IcaReferenceSdLink(pSdLink);
  323. (pSdLink->SdContext.pCallup->pSdBufferFree)(
  324. pSdLink->SdContext.pContext,
  325. pOutBuf);
  326. IcaDereferenceSdLink(pSdLink);
  327. return;
  328. }
  329. }
  330. IcaBufferFreeInternal(pContext, pOutBuf);
  331. TRACESTACK((pStack, TC_ICADD, TT_API3,
  332. "TermDD: IcaBufferFree: 0x%08x\n", pOutBuf));
  333. }
  334. /*******************************************************************************
  335. * IcaBufferError
  336. *
  337. * pContext (input)
  338. * pointer to SDCONTEXT of caller
  339. * pOutBuf (input)
  340. * pointer to OUTBUF structure
  341. ******************************************************************************/
  342. void IcaBufferError(IN PSDCONTEXT pContext, IN POUTBUF pOutBuf)
  343. {
  344. PSDLINK pSdLink;
  345. PICA_STACK pStack;
  346. NTSTATUS Status;
  347. /*
  348. * Use SD passed context to get the SDLINK pointer.
  349. */
  350. pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext);
  351. pStack = pSdLink->pStack;
  352. ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack);
  353. ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable);
  354. ASSERT(ExIsResourceAcquiredExclusiveLite( &pStack->Resource));
  355. /*
  356. * Walk up the SDLINK list looking for a driver which has specified
  357. * a BufferError callup routine. If we find one, then call the
  358. * driver BufferError routine to let it handle the call.
  359. */
  360. while ((pSdLink = IcaGetPreviousSdLink(pSdLink)) != NULL) {
  361. ASSERT(pSdLink->pStack == pStack);
  362. if (pSdLink->SdContext.pCallup->pSdBufferError) {
  363. IcaReferenceSdLink(pSdLink);
  364. (pSdLink->SdContext.pCallup->pSdBufferError)(
  365. pSdLink->SdContext.pContext,
  366. pOutBuf);
  367. IcaDereferenceSdLink(pSdLink);
  368. return;
  369. }
  370. }
  371. IcaBufferFreeInternal(pContext, pOutBuf);
  372. TRACESTACK((pStack, TC_ICADD, TT_API3,
  373. "TermDD: IcaBufferError: 0x%08x\n", pOutBuf));
  374. }
  375. void IcaBufferFreeInternal(IN PSDCONTEXT pContext, IN POUTBUF pOutBuf)
  376. {
  377. PSDLINK pSdLink;
  378. PICA_STACK pStack;
  379. KIRQL oldIrql;
  380. /*
  381. * Use SD passed context to get the SDLINK pointer.
  382. */
  383. pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext);
  384. pStack = pSdLink->pStack;
  385. ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack);
  386. ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable);
  387. /*
  388. * If the buffer came from a free pool, return it to the pool,
  389. * otherwise free it. Note that pOutBuf->OutBufLength is actually the
  390. * pool index to use.
  391. */
  392. if (pOutBuf->PoolIndex != FreeThisOutBuf) {
  393. ASSERT(pOutBuf->PoolIndex >= 0 &&
  394. pOutBuf->PoolIndex < NumOutBufPools);
  395. IcaAcquireSpinLock(&IcaSpinLock, &oldIrql);
  396. InsertHeadList(&IcaFreeOutBufHead[pOutBuf->PoolIndex],
  397. &pOutBuf->Links);
  398. IcaReleaseSpinLock(&IcaSpinLock, oldIrql);
  399. }
  400. else {
  401. ICA_FREE_POOL(pOutBuf);
  402. }
  403. /*
  404. * Decrement allocated buffer count
  405. */
  406. pStack->OutBufAllocCount--;
  407. ASSERT((LONG)pStack->OutBufAllocCount >= 0);
  408. /*
  409. * If we hit the high watermark then we should wait until the low
  410. * watermark is hit before signaling the allocator to continue.
  411. * This should prevent excessive task switching.
  412. */
  413. if (pStack->fWaitForOutBuf) {
  414. if (pStack->OutBufAllocCount <= pStack->OutBufLowWaterMark) {
  415. pStack->fWaitForOutBuf = FALSE;
  416. /*
  417. * Signal outbuf event (buffer is now available)
  418. */
  419. (void) KeSetEvent(&pStack->OutBufEvent, EVENT_INCREMENT, FALSE);
  420. }
  421. }
  422. }
  423. /*******************************************************************************
  424. * IcaGetLowWaterMark
  425. *
  426. * Description : Gets the low water mark that the stack specified
  427. *
  428. * pContext (input)
  429. * pointer to SDCONTEXT of caller
  430. ******************************************************************************/
  431. ULONG IcaGetLowWaterMark(IN PSDCONTEXT pContext)
  432. {
  433. ULONG ulRet = 0;
  434. PICA_STACK pStack;
  435. PSDLINK pSdLink = CONTAINING_RECORD(pContext, SDLINK, SdContext);
  436. ASSERT(pSdLink);
  437. ASSERT(pSdLink->pStack->Header.Type == IcaType_Stack);
  438. ASSERT(pSdLink->pStack->Header.pDispatchTable == IcaStackDispatchTable);
  439. ASSERT(ExIsResourceAcquiredExclusive( &pSdLink->pStack->Resource));
  440. if (NULL != pSdLink) {
  441. pStack = pSdLink->pStack;
  442. ulRet = pStack->OutBufLowWaterMark;
  443. }
  444. return ulRet;
  445. }
  446. /*******************************************************************************
  447. * IcaGetSizeForNoLowWaterMark
  448. *
  449. * Description : Finds if the stack specified a no low water mark
  450. * If so, returns the size needed to bypass ring
  451. * returns zero if the stack does not specify a PD_NO_LOWWATERMARK
  452. * pContext (input)
  453. * pointer to SDCONTEXT of caller
  454. ******************************************************************************/
  455. ULONG IcaGetSizeForNoLowWaterMark(IN PSDCONTEXT pContext)
  456. {
  457. ULONG retVal = 0;
  458. ULONG ulLowWm = IcaGetLowWaterMark(pContext);
  459. if ( MAX_LOW_WATERMARK == ulLowWm ) {
  460. retVal = MaxOutBufAlloc;
  461. }
  462. return retVal;
  463. }