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.

564 lines
15 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. ntos\tdi\isn\fwd\packets.c
  5. Abstract:
  6. IPX Forwarder Driver packet allocator
  7. Author:
  8. Vadim Eydelman
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. ULONG RcvPktsPerSegment = DEF_RCV_PKTS_PER_SEGMENT;
  13. ULONG MaxRcvPktsPoolSize =0;
  14. ULONG RcvPktsPoolSize = 0;
  15. KSPIN_LOCK AllocatorLock;
  16. const LONGLONG SegmentTimeout = -10i64*10000000i64;
  17. SEGMENT_LIST ListEther={1500};
  18. SEGMENT_LIST ListTR4={4500};
  19. SEGMENT_LIST ListTR16={17986};
  20. PSEGMENT_LIST SegmentMap[FRAME_SIZE_VARIATIONS][FRAME_SIZE_VARIATIONS] = {
  21. {&ListEther, &ListEther, &ListEther},
  22. {&ListEther, &ListTR4, &ListTR4},
  23. {&ListEther, &ListTR4, &ListTR16}
  24. };
  25. VOID
  26. AllocationWorker (
  27. PVOID Context
  28. );
  29. VOID
  30. SegmentTimeoutDpc (
  31. PKDPC dpc,
  32. PVOID Context,
  33. PVOID SystemArgument1,
  34. PVOID SystemArgument2
  35. );
  36. /*++
  37. *******************************************************************
  38. C r e a t e S e g m e n t
  39. Routine Description:
  40. Allocates and initializes packet segment
  41. Arguments:
  42. list - segment list to which new segment will be added
  43. Return Value:
  44. Pointer to allocated segment, NULL if fails
  45. *******************************************************************
  46. --*/
  47. PPACKET_SEGMENT
  48. CreateSegment (
  49. PSEGMENT_LIST list
  50. ) {
  51. KIRQL oldIRQL;
  52. NDIS_STATUS status;
  53. PPACKET_SEGMENT segment;
  54. ULONG segmentsize = list->SL_BlockCount*list->SL_BlockSize
  55. +FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers);
  56. if (MaxRcvPktsPoolSize!=0) {
  57. // Check if this allocation would exceed the limit
  58. KeAcquireSpinLock (&AllocatorLock, &oldIRQL);
  59. if (RcvPktsPoolSize+segmentsize<MaxRcvPktsPoolSize) {
  60. RcvPktsPoolSize += segmentsize;
  61. KeReleaseSpinLock (&AllocatorLock, oldIRQL);
  62. }
  63. else {
  64. KeReleaseSpinLock (&AllocatorLock, oldIRQL);
  65. return NULL;
  66. }
  67. }
  68. // Allocate chunk of memory to hold segment header and buffers
  69. segment = ExAllocatePoolWithTag (
  70. NonPagedPool,
  71. segmentsize,
  72. FWD_POOL_TAG);
  73. if (segment!=NULL) {
  74. segment->PS_SegmentList = list;
  75. segment->PS_FreeHead = NULL;
  76. segment->PS_BusyCount = 0;
  77. segment->PS_PacketPool = (NDIS_HANDLE)FWD_POOL_TAG;
  78. KeQuerySystemTime ((PLARGE_INTEGER)&segment->PS_FreeStamp);
  79. NdisAllocatePacketPoolEx (
  80. &status,
  81. &segment->PS_PacketPool,
  82. list->SL_BlockCount,
  83. 0,
  84. IPXMacHeaderSize
  85. +FIELD_OFFSET (PACKET_TAG, PT_MacHeader));
  86. if (status==NDIS_STATUS_SUCCESS) {
  87. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  88. ("IpxFwd: CreateSegent pool: %x\n", segment->PS_PacketPool));
  89. NdisAllocateBufferPool (
  90. &status,
  91. &segment->PS_BufferPool,
  92. list->SL_BlockCount*2);
  93. if (status==NDIS_STATUS_SUCCESS) {
  94. PUCHAR bufferptr = segment->PS_Buffers;
  95. PNDIS_PACKET packetDscr;
  96. PNDIS_BUFFER bufferDscr;
  97. PPACKET_TAG packetTag;
  98. ULONG i;
  99. for (i=0; i<list->SL_BlockCount; i++,
  100. bufferptr+=list->SL_BlockSize) {
  101. NdisAllocatePacket (
  102. &status,
  103. &packetDscr,
  104. segment->PS_PacketPool);
  105. ASSERT (status==NDIS_STATUS_SUCCESS);
  106. packetTag = (PPACKET_TAG)packetDscr->ProtocolReserved;
  107. packetTag->PT_Segment = segment;
  108. packetTag->PT_Data = bufferptr;
  109. packetTag->PT_InterfaceReference = NULL;
  110. NdisAllocateBuffer (
  111. &status,
  112. &packetTag->PT_MacHdrBufDscr,
  113. segment->PS_BufferPool,
  114. packetTag->PT_MacHeader,
  115. IPXMacHeaderSize);
  116. ASSERT (status==NDIS_STATUS_SUCCESS);
  117. NdisAllocateBuffer (
  118. &status,
  119. &bufferDscr,
  120. segment->PS_BufferPool,
  121. bufferptr,
  122. list->SL_BlockSize);
  123. ASSERT (status==NDIS_STATUS_SUCCESS);
  124. if (bufferDscr)
  125. {
  126. NdisChainBufferAtFront (packetDscr, bufferDscr);
  127. }
  128. packetTag->PT_Next = segment->PS_FreeHead;
  129. segment->PS_FreeHead = packetTag;
  130. }
  131. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  132. ("IpxFwd: Allocated packet segment %08lx for list %ld.\n",
  133. segment, list->SL_BlockSize));
  134. return segment;
  135. }
  136. else {
  137. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR,
  138. ("IpxFwd: Failed to allocate buffer pool"
  139. " for new segment in list %ld.\n",
  140. list->SL_BlockSize));
  141. }
  142. NdisFreePacketPool (segment->PS_PacketPool);
  143. }
  144. else {
  145. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR,
  146. ("IpxFwd: Failed to allocate packet pool"
  147. " for new segment in list %ld.\n",
  148. list->SL_BlockSize));
  149. }
  150. ExFreePool (segment);
  151. }
  152. else {
  153. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR,
  154. ("IpxFwd: Failed to allocate new segment for list %ld.\n",
  155. list->SL_BlockSize));
  156. }
  157. return NULL;
  158. }
  159. /*++
  160. *******************************************************************
  161. D e l e t e S e g m e n t
  162. Routine Description:
  163. Frees packet segment
  164. Arguments:
  165. segment - segment to free
  166. Return Value:
  167. None
  168. *******************************************************************
  169. --*/
  170. VOID
  171. DeleteSegment (
  172. PPACKET_SEGMENT segment
  173. ) {
  174. PSEGMENT_LIST list = segment->PS_SegmentList;
  175. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  176. ("IpxFwd: DeleteSegment entered. %d %x\n",segment->PS_BusyCount, segment->PS_PacketPool));
  177. ASSERT (segment->PS_BusyCount == 0);
  178. // Free all NDIS packet and buffer descriptors first
  179. while (segment->PS_FreeHead!=NULL) {
  180. PNDIS_BUFFER bufferDscr;
  181. PPACKET_TAG packetTag = segment->PS_FreeHead;
  182. PNDIS_PACKET packetDscr = CONTAINING_RECORD (packetTag,
  183. NDIS_PACKET, ProtocolReserved);
  184. segment->PS_FreeHead = packetTag->PT_Next;
  185. ASSERT (packetTag->PT_MacHdrBufDscr!=NULL);
  186. NdisFreeBuffer (packetTag->PT_MacHdrBufDscr);
  187. NdisUnchainBufferAtFront (packetDscr, &bufferDscr);
  188. ASSERT (bufferDscr!=NULL);
  189. NdisFreeBuffer (bufferDscr);
  190. NdisFreePacket (packetDscr);
  191. }
  192. NdisFreeBufferPool (segment->PS_BufferPool);
  193. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  194. ("IpxFwd: DeleteSegment pool: %x\n", segment->PS_PacketPool));
  195. NdisFreePacketPool (segment->PS_PacketPool);
  196. // [pmay] Remove this -- for debugging only
  197. segment->PS_PacketPool = NULL;
  198. // Decrement memory used if we have a quota
  199. if (MaxRcvPktsPoolSize!=0) {
  200. KIRQL oldIRQL;
  201. ULONG segmentsize = list->SL_BlockCount*list->SL_BlockSize
  202. +FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers);
  203. KeAcquireSpinLock (&AllocatorLock, &oldIRQL);
  204. RcvPktsPoolSize -= segmentsize;
  205. KeReleaseSpinLock (&AllocatorLock, oldIRQL);
  206. }
  207. ExFreePool (segment);
  208. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  209. ("IpxFwd: Deleting segment %08lx in list %ld.\n",
  210. segment, list->SL_BlockSize));
  211. }
  212. /*++
  213. *******************************************************************
  214. R e g i s t e r P a c k e t C o n s u m e r
  215. Routine Description:
  216. Registers a consumer (bound interface) of packets of the
  217. given size
  218. Arguments:
  219. pktsize - maximum size of packets needed
  220. listId - buffer to return packet list id where packets
  221. of required size are located
  222. Return Value:
  223. STATUS_SUCCESS - registration succeded
  224. STATUS_INSUFFICIENT_RESOURCES - not enogh resources to register
  225. *******************************************************************
  226. --*/
  227. NTSTATUS
  228. RegisterPacketConsumer (
  229. IN ULONG pktsize,
  230. OUT INT *listID
  231. ) {
  232. NTSTATUS status=STATUS_SUCCESS;
  233. KIRQL oldIRQL;
  234. PSEGMENT_LIST list;
  235. INT i;
  236. LONG addRefCount = 1;
  237. KeAcquireSpinLock (&AllocatorLock, &oldIRQL);
  238. ASSERT (pktsize<=SegmentMap[FRAME_SIZE_VARIATIONS-1]
  239. [FRAME_SIZE_VARIATIONS-1]->SL_BlockSize);
  240. for (i=0; i<FRAME_SIZE_VARIATIONS; i++) {
  241. list = SegmentMap[i][i];
  242. if (pktsize<=list->SL_BlockSize) {
  243. list->SL_RefCount += 1;
  244. *listID = i;
  245. break;
  246. }
  247. }
  248. KeReleaseSpinLock (&AllocatorLock, oldIRQL);
  249. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  250. ("IpxFwd: Registered packet consumer, pktsz: %ld, list: %ld.\n",
  251. pktsize, list->SL_BlockSize));
  252. return status;
  253. }
  254. /*++
  255. *******************************************************************
  256. D e r e g i s t e r P a c k e t C o n s u m e r
  257. Routine Description:
  258. Deregisters a consumer (bound interface) of packets of the
  259. given size
  260. Arguments:
  261. listId - packet list id used by the consumer
  262. Return Value:
  263. None
  264. *******************************************************************
  265. --*/
  266. VOID
  267. DeregisterPacketConsumer (
  268. IN INT listID
  269. ) {
  270. KIRQL oldIRQL;
  271. PSEGMENT_LIST list;
  272. ASSERT ((listID>=0) && (listID<FRAME_SIZE_VARIATIONS));
  273. KeAcquireSpinLock (&AllocatorLock, &oldIRQL);
  274. list = SegmentMap[listID][listID];
  275. ASSERT (list->SL_RefCount>0);
  276. list->SL_RefCount -= 1;
  277. KeReleaseSpinLock (&AllocatorLock, oldIRQL);
  278. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  279. ("IpxFwd: Deregistered packet consumer, list: %ld.\n",
  280. list->SL_BlockSize));
  281. }
  282. /*++
  283. *******************************************************************
  284. I n i t i a l i z e S e g m e n t L i s t
  285. Routine Description:
  286. Initializes list of packet segments
  287. Arguments:
  288. list - list to initalize
  289. Return Value:
  290. None
  291. *******************************************************************
  292. --*/
  293. VOID
  294. InitializeSegmentList(
  295. PSEGMENT_LIST list
  296. ) {
  297. InitializeListHead (&list->SL_Head);
  298. list->SL_FreeCount = 0;
  299. // Make sure we don't have any leftover larger than
  300. // the buffer size (kernel memory allocator
  301. // allocates full pages)
  302. list->SL_BlockCount = (ULONG)
  303. (ROUND_TO_PAGES (
  304. list->SL_BlockSize*RcvPktsPerSegment
  305. +FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers))
  306. -FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers))
  307. /list->SL_BlockSize;
  308. list->SL_LowCount = list->SL_BlockCount/2;
  309. list->SL_RefCount = 0;
  310. list->SL_AllocatorPending = FALSE;
  311. list->SL_TimerDpcPending = FALSE;
  312. KeInitializeSpinLock (&list->SL_Lock);
  313. KeInitializeTimer (&list->SL_Timer);
  314. KeInitializeDpc (&list->SL_TimerDpc, SegmentTimeoutDpc, list);
  315. ExInitializeWorkItem (&list->SL_Allocator, AllocationWorker, list);
  316. }
  317. /*++
  318. *******************************************************************
  319. D e l e t e S e g m e n t L i s t
  320. Routine Description:
  321. Deletes list of packet segments
  322. Arguments:
  323. list - list to delete
  324. Return Value:
  325. None
  326. *******************************************************************
  327. --*/
  328. VOID
  329. DeleteSegmentList (
  330. PSEGMENT_LIST list
  331. ) {
  332. KeCancelTimer (&list->SL_Timer);
  333. while (!IsListEmpty (&list->SL_Head)) {
  334. PPACKET_SEGMENT segment;
  335. segment = CONTAINING_RECORD (list->SL_Head.Blink,
  336. PACKET_SEGMENT, PS_Link);
  337. RemoveEntryList (&segment->PS_Link);
  338. DeleteSegment (segment);
  339. }
  340. }
  341. /*++
  342. *******************************************************************
  343. S e g m e n t T i m e o u t D p c
  344. Routine Description:
  345. Timer DPC that launches allocator worker to get rid of unused
  346. segments
  347. Arguments:
  348. Context - segment list to check for unused segments
  349. Return Value:
  350. None
  351. *******************************************************************
  352. --*/
  353. VOID
  354. SegmentTimeoutDpc (
  355. PKDPC dpc,
  356. PVOID Context,
  357. PVOID SystemArgument1,
  358. PVOID SystemArgument2
  359. ) {
  360. #define list ((PSEGMENT_LIST)Context)
  361. KIRQL oldIRQL;
  362. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_INFORMATION,
  363. ("IpxFwd: Segment timed out in list: %ld.\n",
  364. list->SL_BlockSize));
  365. KeAcquireSpinLock (&list->SL_Lock, &oldIRQL);
  366. list->SL_TimerDpcPending = FALSE;
  367. if (!list->SL_AllocatorPending
  368. && (list->SL_FreeCount>=list->SL_BlockCount)
  369. && EnterForwarder ()) {
  370. list->SL_AllocatorPending = TRUE;
  371. KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
  372. ExQueueWorkItem (&list->SL_Allocator, DelayedWorkQueue);
  373. }
  374. else {
  375. KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
  376. }
  377. LeaveForwarder ();
  378. #undef list
  379. }
  380. /*++
  381. *******************************************************************
  382. A l l o c a t i o n W o r k e r
  383. Routine Description:
  384. Adds new segment or releases unused segments from the list
  385. depending on the free packet count and time that segments
  386. are not used
  387. Arguments:
  388. Context - packet list to process
  389. Return Value:
  390. None
  391. *******************************************************************
  392. --*/
  393. VOID
  394. AllocationWorker (
  395. PVOID Context
  396. ) {
  397. #define list ((PSEGMENT_LIST)Context)
  398. KIRQL oldIRQL;
  399. PPACKET_SEGMENT segment = NULL;
  400. LONGLONG curTime;
  401. IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_INFORMATION,
  402. ("IpxFwd: Allocating/scavenging segment(s) in list: %ld.\n",
  403. list->SL_BlockSize));
  404. KeQuerySystemTime ((PLARGE_INTEGER)&curTime);
  405. KeAcquireSpinLock (&list->SL_Lock, &oldIRQL);
  406. list->SL_AllocatorPending = FALSE;
  407. if (list->SL_FreeCount<list->SL_BlockCount) {
  408. KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
  409. // First allocate a segment
  410. segment = CreateSegment (list);
  411. if (segment!=NULL) {
  412. KeAcquireSpinLock (&list->SL_Lock, &oldIRQL);
  413. InsertTailList (&list->SL_Head, &segment->PS_Link);
  414. list->SL_FreeCount += list->SL_BlockCount;
  415. if (!list->SL_TimerDpcPending
  416. && EnterForwarder ()) {
  417. list->SL_TimerDpcPending = TRUE;
  418. KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
  419. KeSetTimer (&list->SL_Timer, *((PLARGE_INTEGER)&SegmentTimeout), &list->SL_TimerDpc);
  420. }
  421. else {
  422. KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
  423. }
  424. }
  425. }
  426. else {
  427. // Make sure that there is either more than segment in the list
  428. // or there is one and no registered users
  429. if (!IsListEmpty (&list->SL_Head)) {
  430. // [pmay] Remove this -- for debugging purposes only.
  431. //
  432. //{
  433. // LIST_ENTRY * pEntry = &list->SL_Head;
  434. //
  435. // IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  436. // ("IpxFwd: Scanning %x for possible segment deletion.\n",list));
  437. //
  438. // while (pEntry->Flink != list->SL_Head.Flink) {
  439. // segment = CONTAINING_RECORD (pEntry->Flink, PACKET_SEGMENT, PS_Link);
  440. // IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
  441. // ("IpxFwd: Segment: %x\n",segment));
  442. // pEntry = pEntry->Flink;
  443. // }
  444. //}
  445. segment = CONTAINING_RECORD (list->SL_Head.Blink,
  446. PACKET_SEGMENT, PS_Link);
  447. // Check for all segments with no used blocks
  448. // except for the last one (delete event the last
  449. // one if there are no clients)
  450. while ((segment->PS_BusyCount==0)
  451. && ((list->SL_Head.Flink!=&segment->PS_Link)
  452. || (list->SL_RefCount<=0))) {
  453. LONGLONG timeDiff;
  454. // Check if it has not been used for long enough
  455. timeDiff = SegmentTimeout - (segment->PS_FreeStamp-curTime);
  456. if (timeDiff>=0) {
  457. // Delete the segment
  458. RemoveEntryList (&segment->PS_Link);
  459. list->SL_FreeCount -= list->SL_BlockCount;
  460. KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
  461. DeleteSegment (segment);
  462. KeAcquireSpinLock (&list->SL_Lock, &oldIRQL);
  463. if (!IsListEmpty (&list->SL_Head)) {
  464. segment = CONTAINING_RECORD (list->SL_Head.Blink,
  465. PACKET_SEGMENT, PS_Link);
  466. continue;
  467. }
  468. }
  469. else { // Reschedule the timer otherwise
  470. if (!list->SL_TimerDpcPending
  471. && EnterForwarder ()) {
  472. list->SL_TimerDpcPending = TRUE;
  473. KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
  474. KeSetTimer (&list->SL_Timer,
  475. *((PLARGE_INTEGER)&timeDiff),
  476. &list->SL_TimerDpc);
  477. goto ExitAllocator; // Spinlock is already released
  478. }
  479. }
  480. break;
  481. } // while
  482. } // if (IsListEmpty)
  483. KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
  484. }
  485. ExitAllocator:
  486. LeaveForwarder ();
  487. #undef list
  488. }