Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenexpool.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Kernel 00003 * LICENSE: BSD - See COPYING.ARM in the top level directory 00004 * FILE: ntoskrnl/mm/ARM3/expool.c 00005 * PURPOSE: ARM Memory Manager Executive Pool Manager 00006 * PROGRAMMERS: ReactOS Portable Systems Group 00007 */ 00008 00009 /* INCLUDES *******************************************************************/ 00010 00011 #include <ntoskrnl.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 #define MODULE_INVOLVED_IN_ARM3 00016 #include "../ARM3/miarm.h" 00017 00018 #undef ExAllocatePoolWithQuota 00019 #undef ExAllocatePoolWithQuotaTag 00020 00021 /* GLOBALS ********************************************************************/ 00022 00023 #define POOL_BIG_TABLE_ENTRY_FREE 0x1 00024 00025 typedef struct _POOL_DPC_CONTEXT 00026 { 00027 PPOOL_TRACKER_TABLE PoolTrackTable; 00028 SIZE_T PoolTrackTableSize; 00029 PPOOL_TRACKER_TABLE PoolTrackTableExpansion; 00030 SIZE_T PoolTrackTableSizeExpansion; 00031 } POOL_DPC_CONTEXT, *PPOOL_DPC_CONTEXT; 00032 00033 ULONG ExpNumberOfPagedPools; 00034 POOL_DESCRIPTOR NonPagedPoolDescriptor; 00035 PPOOL_DESCRIPTOR ExpPagedPoolDescriptor[16 + 1]; 00036 PPOOL_DESCRIPTOR PoolVector[2]; 00037 PKGUARDED_MUTEX ExpPagedPoolMutex; 00038 SIZE_T PoolTrackTableSize, PoolTrackTableMask; 00039 SIZE_T PoolBigPageTableSize, PoolBigPageTableHash; 00040 PPOOL_TRACKER_TABLE PoolTrackTable; 00041 PPOOL_TRACKER_BIG_PAGES PoolBigPageTable; 00042 KSPIN_LOCK ExpTaggedPoolLock; 00043 ULONG PoolHitTag; 00044 BOOLEAN ExStopBadTags; 00045 KSPIN_LOCK ExpLargePoolTableLock; 00046 LONG ExpPoolBigEntriesInUse; 00047 ULONG ExpPoolFlags; 00048 ULONG ExPoolFailures; 00049 00050 /* Pool block/header/list access macros */ 00051 #define POOL_ENTRY(x) (PPOOL_HEADER)((ULONG_PTR)(x) - sizeof(POOL_HEADER)) 00052 #define POOL_FREE_BLOCK(x) (PLIST_ENTRY)((ULONG_PTR)(x) + sizeof(POOL_HEADER)) 00053 #define POOL_BLOCK(x, i) (PPOOL_HEADER)((ULONG_PTR)(x) + ((i) * POOL_BLOCK_SIZE)) 00054 #define POOL_NEXT_BLOCK(x) POOL_BLOCK((x), (x)->BlockSize) 00055 #define POOL_PREV_BLOCK(x) POOL_BLOCK((x), -((x)->PreviousSize)) 00056 00057 /* 00058 * Pool list access debug macros, similar to Arthur's pfnlist.c work. 00059 * Microsoft actually implements similar checks in the Windows Server 2003 SP1 00060 * pool code, but only for checked builds. 00061 * 00062 * As of Vista, however, an MSDN Blog entry by a Security Team Manager indicates 00063 * that these checks are done even on retail builds, due to the increasing 00064 * number of kernel-mode attacks which depend on dangling list pointers and other 00065 * kinds of list-based attacks. 00066 * 00067 * For now, I will leave these checks on all the time, but later they are likely 00068 * to be DBG-only, at least until there are enough kernel-mode security attacks 00069 * against ReactOS to warrant the performance hit. 00070 * 00071 * For now, these are not made inline, so we can get good stack traces. 00072 */ 00073 PLIST_ENTRY 00074 NTAPI 00075 ExpDecodePoolLink(IN PLIST_ENTRY Link) 00076 { 00077 return (PLIST_ENTRY)((ULONG_PTR)Link & ~1); 00078 } 00079 00080 PLIST_ENTRY 00081 NTAPI 00082 ExpEncodePoolLink(IN PLIST_ENTRY Link) 00083 { 00084 return (PLIST_ENTRY)((ULONG_PTR)Link | 1); 00085 } 00086 00087 VOID 00088 NTAPI 00089 ExpCheckPoolLinks(IN PLIST_ENTRY ListHead) 00090 { 00091 if ((ExpDecodePoolLink(ExpDecodePoolLink(ListHead->Flink)->Blink) != ListHead) || 00092 (ExpDecodePoolLink(ExpDecodePoolLink(ListHead->Blink)->Flink) != ListHead)) 00093 { 00094 KeBugCheckEx(BAD_POOL_HEADER, 00095 3, 00096 (ULONG_PTR)ListHead, 00097 (ULONG_PTR)ExpDecodePoolLink(ExpDecodePoolLink(ListHead->Flink)->Blink), 00098 (ULONG_PTR)ExpDecodePoolLink(ExpDecodePoolLink(ListHead->Blink)->Flink)); 00099 } 00100 } 00101 00102 VOID 00103 NTAPI 00104 ExpInitializePoolListHead(IN PLIST_ENTRY ListHead) 00105 { 00106 ListHead->Flink = ListHead->Blink = ExpEncodePoolLink(ListHead); 00107 } 00108 00109 BOOLEAN 00110 NTAPI 00111 ExpIsPoolListEmpty(IN PLIST_ENTRY ListHead) 00112 { 00113 return (ExpDecodePoolLink(ListHead->Flink) == ListHead); 00114 } 00115 00116 VOID 00117 NTAPI 00118 ExpRemovePoolEntryList(IN PLIST_ENTRY Entry) 00119 { 00120 PLIST_ENTRY Blink, Flink; 00121 Flink = ExpDecodePoolLink(Entry->Flink); 00122 Blink = ExpDecodePoolLink(Entry->Blink); 00123 Flink->Blink = ExpEncodePoolLink(Blink); 00124 Blink->Flink = ExpEncodePoolLink(Flink); 00125 } 00126 00127 PLIST_ENTRY 00128 NTAPI 00129 ExpRemovePoolHeadList(IN PLIST_ENTRY ListHead) 00130 { 00131 PLIST_ENTRY Entry, Flink; 00132 Entry = ExpDecodePoolLink(ListHead->Flink); 00133 Flink = ExpDecodePoolLink(Entry->Flink); 00134 ListHead->Flink = ExpEncodePoolLink(Flink); 00135 Flink->Blink = ExpEncodePoolLink(ListHead); 00136 return Entry; 00137 } 00138 00139 PLIST_ENTRY 00140 NTAPI 00141 ExpRemovePoolTailList(IN PLIST_ENTRY ListHead) 00142 { 00143 PLIST_ENTRY Entry, Blink; 00144 Entry = ExpDecodePoolLink(ListHead->Blink); 00145 Blink = ExpDecodePoolLink(Entry->Blink); 00146 ListHead->Blink = ExpEncodePoolLink(Blink); 00147 Blink->Flink = ExpEncodePoolLink(ListHead); 00148 return Entry; 00149 } 00150 00151 VOID 00152 NTAPI 00153 ExpInsertPoolTailList(IN PLIST_ENTRY ListHead, 00154 IN PLIST_ENTRY Entry) 00155 { 00156 PLIST_ENTRY Blink; 00157 ExpCheckPoolLinks(ListHead); 00158 Blink = ExpDecodePoolLink(ListHead->Blink); 00159 Entry->Flink = ExpEncodePoolLink(ListHead); 00160 Entry->Blink = ExpEncodePoolLink(Blink); 00161 Blink->Flink = ExpEncodePoolLink(Entry); 00162 ListHead->Blink = ExpEncodePoolLink(Entry); 00163 ExpCheckPoolLinks(ListHead); 00164 } 00165 00166 VOID 00167 NTAPI 00168 ExpInsertPoolHeadList(IN PLIST_ENTRY ListHead, 00169 IN PLIST_ENTRY Entry) 00170 { 00171 PLIST_ENTRY Flink; 00172 ExpCheckPoolLinks(ListHead); 00173 Flink = ExpDecodePoolLink(ListHead->Flink); 00174 Entry->Flink = ExpEncodePoolLink(Flink); 00175 Entry->Blink = ExpEncodePoolLink(ListHead); 00176 Flink->Blink = ExpEncodePoolLink(Entry); 00177 ListHead->Flink = ExpEncodePoolLink(Entry); 00178 ExpCheckPoolLinks(ListHead); 00179 } 00180 00181 VOID 00182 NTAPI 00183 ExpCheckPoolHeader(IN PPOOL_HEADER Entry) 00184 { 00185 PPOOL_HEADER PreviousEntry, NextEntry; 00186 00187 /* Is there a block before this one? */ 00188 if (Entry->PreviousSize) 00189 { 00190 /* Get it */ 00191 PreviousEntry = POOL_PREV_BLOCK(Entry); 00192 00193 /* The two blocks must be on the same page! */ 00194 if (PAGE_ALIGN(Entry) != PAGE_ALIGN(PreviousEntry)) 00195 { 00196 /* Something is awry */ 00197 KeBugCheckEx(BAD_POOL_HEADER, 00198 6, 00199 (ULONG_PTR)PreviousEntry, 00200 __LINE__, 00201 (ULONG_PTR)Entry); 00202 } 00203 00204 /* This block should also indicate that it's as large as we think it is */ 00205 if (PreviousEntry->BlockSize != Entry->PreviousSize) 00206 { 00207 /* Otherwise, someone corrupted one of the sizes */ 00208 DPRINT1("PreviousEntry BlockSize %lu, tag %.4s. Entry PreviousSize %lu, tag %.4s\n", 00209 PreviousEntry->BlockSize, (char *)&PreviousEntry->PoolTag, 00210 Entry->PreviousSize, (char *)&Entry->PoolTag); 00211 KeBugCheckEx(BAD_POOL_HEADER, 00212 5, 00213 (ULONG_PTR)PreviousEntry, 00214 __LINE__, 00215 (ULONG_PTR)Entry); 00216 } 00217 } 00218 else if (PAGE_ALIGN(Entry) != Entry) 00219 { 00220 /* If there's no block before us, we are the first block, so we should be on a page boundary */ 00221 KeBugCheckEx(BAD_POOL_HEADER, 00222 7, 00223 0, 00224 __LINE__, 00225 (ULONG_PTR)Entry); 00226 } 00227 00228 /* This block must have a size */ 00229 if (!Entry->BlockSize) 00230 { 00231 /* Someone must've corrupted this field */ 00232 if (Entry->PreviousSize) 00233 { 00234 PreviousEntry = POOL_PREV_BLOCK(Entry); 00235 DPRINT1("PreviousEntry tag %.4s. Entry tag %.4s\n", 00236 (char *)&PreviousEntry->PoolTag, 00237 (char *)&Entry->PoolTag); 00238 } 00239 else 00240 { 00241 DPRINT1("Entry tag %.4s\n", 00242 (char *)&Entry->PoolTag); 00243 } 00244 KeBugCheckEx(BAD_POOL_HEADER, 00245 8, 00246 0, 00247 __LINE__, 00248 (ULONG_PTR)Entry); 00249 } 00250 00251 /* Okay, now get the next block */ 00252 NextEntry = POOL_NEXT_BLOCK(Entry); 00253 00254 /* If this is the last block, then we'll be page-aligned, otherwise, check this block */ 00255 if (PAGE_ALIGN(NextEntry) != NextEntry) 00256 { 00257 /* The two blocks must be on the same page! */ 00258 if (PAGE_ALIGN(Entry) != PAGE_ALIGN(NextEntry)) 00259 { 00260 /* Something is messed up */ 00261 KeBugCheckEx(BAD_POOL_HEADER, 00262 9, 00263 (ULONG_PTR)NextEntry, 00264 __LINE__, 00265 (ULONG_PTR)Entry); 00266 } 00267 00268 /* And this block should think we are as large as we truly are */ 00269 if (NextEntry->PreviousSize != Entry->BlockSize) 00270 { 00271 /* Otherwise, someone corrupted the field */ 00272 DPRINT1("Entry BlockSize %lu, tag %.4s. NextEntry PreviousSize %lu, tag %.4s\n", 00273 Entry->BlockSize, (char *)&Entry->PoolTag, 00274 NextEntry->PreviousSize, (char *)&NextEntry->PoolTag); 00275 KeBugCheckEx(BAD_POOL_HEADER, 00276 5, 00277 (ULONG_PTR)NextEntry, 00278 __LINE__, 00279 (ULONG_PTR)Entry); 00280 } 00281 } 00282 } 00283 00284 VOID 00285 NTAPI 00286 ExpCheckPoolBlocks(IN PVOID Block) 00287 { 00288 BOOLEAN FoundBlock = FALSE; 00289 SIZE_T Size = 0; 00290 PPOOL_HEADER Entry; 00291 00292 /* Get the first entry for this page, make sure it really is the first */ 00293 Entry = PAGE_ALIGN(Block); 00294 ASSERT(Entry->PreviousSize == 0); 00295 00296 /* Now scan each entry */ 00297 while (TRUE) 00298 { 00299 /* When we actually found our block, remember this */ 00300 if (Entry == Block) FoundBlock = TRUE; 00301 00302 /* Now validate this block header */ 00303 ExpCheckPoolHeader(Entry); 00304 00305 /* And go to the next one, keeping track of our size */ 00306 Size += Entry->BlockSize; 00307 Entry = POOL_NEXT_BLOCK(Entry); 00308 00309 /* If we hit the last block, stop */ 00310 if (Size >= (PAGE_SIZE / POOL_BLOCK_SIZE)) break; 00311 00312 /* If we hit the end of the page, stop */ 00313 if (PAGE_ALIGN(Entry) == Entry) break; 00314 } 00315 00316 /* We must've found our block, and we must have hit the end of the page */ 00317 if ((PAGE_ALIGN(Entry) != Entry) || !(FoundBlock)) 00318 { 00319 /* Otherwise, the blocks are messed up */ 00320 KeBugCheckEx(BAD_POOL_HEADER, 10, (ULONG_PTR)Block, __LINE__, (ULONG_PTR)Entry); 00321 } 00322 } 00323 00324 FORCEINLINE 00325 VOID 00326 ExpCheckPoolIrqlLevel(IN POOL_TYPE PoolType, 00327 IN SIZE_T NumberOfBytes, 00328 IN PVOID Entry) 00329 { 00330 // 00331 // Validate IRQL: It must be APC_LEVEL or lower for Paged Pool, and it must 00332 // be DISPATCH_LEVEL or lower for Non Paged Pool 00333 // 00334 if (((PoolType & BASE_POOL_TYPE_MASK) == PagedPool) ? 00335 (KeGetCurrentIrql() > APC_LEVEL) : 00336 (KeGetCurrentIrql() > DISPATCH_LEVEL)) 00337 { 00338 // 00339 // Take the system down 00340 // 00341 KeBugCheckEx(BAD_POOL_CALLER, 00342 !Entry ? POOL_ALLOC_IRQL_INVALID : POOL_FREE_IRQL_INVALID, 00343 KeGetCurrentIrql(), 00344 PoolType, 00345 !Entry ? NumberOfBytes : (ULONG_PTR)Entry); 00346 } 00347 } 00348 00349 FORCEINLINE 00350 ULONG 00351 ExpComputeHashForTag(IN ULONG Tag, 00352 IN SIZE_T BucketMask) 00353 { 00354 // 00355 // Compute the hash by multiplying with a large prime number and then XORing 00356 // with the HIDWORD of the result. 00357 // 00358 // Finally, AND with the bucket mask to generate a valid index/bucket into 00359 // the table 00360 // 00361 ULONGLONG Result = 40543 * Tag; 00362 return (ULONG)BucketMask & ((ULONG)Result ^ (Result >> 32)); 00363 } 00364 00365 FORCEINLINE 00366 ULONG 00367 ExpComputePartialHashForAddress(IN PVOID BaseAddress) 00368 { 00369 ULONG Result; 00370 // 00371 // Compute the hash by converting the address into a page number, and then 00372 // XORing each nibble with the next one. 00373 // 00374 // We do *NOT* AND with the bucket mask at this point because big table expansion 00375 // might happen. Therefore, the final step of the hash must be performed 00376 // while holding the expansion pushlock, and this is why we call this a 00377 // "partial" hash only. 00378 // 00379 Result = (ULONG)((ULONG_PTR)BaseAddress >> PAGE_SHIFT); 00380 return (Result >> 24) ^ (Result >> 16) ^ (Result >> 8) ^ Result; 00381 } 00382 00383 /* PRIVATE FUNCTIONS **********************************************************/ 00384 00385 VOID 00386 NTAPI 00387 INIT_FUNCTION 00388 ExpSeedHotTags(VOID) 00389 { 00390 ULONG i, Key, Hash, Index; 00391 PPOOL_TRACKER_TABLE TrackTable = PoolTrackTable; 00392 ULONG TagList[] = 00393 { 00394 ' oI', 00395 ' laH', 00396 'PldM', 00397 'LooP', 00398 'tSbO', 00399 ' prI', 00400 'bdDN', 00401 'LprI', 00402 'pOoI', 00403 ' ldM', 00404 'eliF', 00405 'aVMC', 00406 'dSeS', 00407 'CFtN', 00408 'looP', 00409 'rPCT', 00410 'bNMC', 00411 'dTeS', 00412 'sFtN', 00413 'TPCT', 00414 'CPCT', 00415 ' yeK', 00416 'qSbO', 00417 'mNoI', 00418 'aEoI', 00419 'cPCT', 00420 'aFtN', 00421 '0ftN', 00422 'tceS', 00423 'SprI', 00424 'ekoT', 00425 ' eS', 00426 'lCbO', 00427 'cScC', 00428 'lFtN', 00429 'cAeS', 00430 'mfSF', 00431 'kWcC', 00432 'miSF', 00433 'CdfA', 00434 'EdfA', 00435 'orSF', 00436 'nftN', 00437 'PRIU', 00438 'rFpN', 00439 'RFpN', 00440 'aPeS', 00441 'sUeS', 00442 'FpcA', 00443 'MpcA', 00444 'cSeS', 00445 'mNbO', 00446 'sFpN', 00447 'uLeS', 00448 'DPcS', 00449 'nevE', 00450 'vrqR', 00451 'ldaV', 00452 ' pP', 00453 'SdaV', 00454 ' daV', 00455 'LdaV', 00456 'FdaV', 00457 ' GIB', 00458 }; 00459 00460 // 00461 // Loop all 64 hot tags 00462 // 00463 ASSERT((sizeof(TagList) / sizeof(ULONG)) == 64); 00464 for (i = 0; i < sizeof(TagList) / sizeof(ULONG); i++) 00465 { 00466 // 00467 // Get the current tag, and compute its hash in the tracker table 00468 // 00469 Key = TagList[i]; 00470 Hash = ExpComputeHashForTag(Key, PoolTrackTableMask); 00471 00472 // 00473 // Loop all the hashes in this index/bucket 00474 // 00475 Index = Hash; 00476 while (TRUE) 00477 { 00478 // 00479 // Find an empty entry, and make sure this isn't the last hash that 00480 // can fit. 00481 // 00482 // On checked builds, also make sure this is the first time we are 00483 // seeding this tag. 00484 // 00485 ASSERT(TrackTable[Hash].Key != Key); 00486 if (!(TrackTable[Hash].Key) && (Hash != PoolTrackTableSize - 1)) 00487 { 00488 // 00489 // It has been seeded, move on to the next tag 00490 // 00491 TrackTable[Hash].Key = Key; 00492 break; 00493 } 00494 00495 // 00496 // This entry was already taken, compute the next possible hash while 00497 // making sure we're not back at our initial index. 00498 // 00499 ASSERT(TrackTable[Hash].Key != Key); 00500 Hash = (Hash + 1) & PoolTrackTableMask; 00501 if (Hash == Index) break; 00502 } 00503 } 00504 } 00505 00506 VOID 00507 NTAPI 00508 ExpRemovePoolTracker(IN ULONG Key, 00509 IN SIZE_T NumberOfBytes, 00510 IN POOL_TYPE PoolType) 00511 { 00512 ULONG Hash, Index; 00513 PPOOL_TRACKER_TABLE Table, TableEntry; 00514 SIZE_T TableMask, TableSize; 00515 00516 // 00517 // Remove the PROTECTED_POOL flag which is not part of the tag 00518 // 00519 Key &= ~PROTECTED_POOL; 00520 00521 // 00522 // With WinDBG you can set a tag you want to break on when an allocation is 00523 // attempted 00524 // 00525 if (Key == PoolHitTag) DbgBreakPoint(); 00526 00527 // 00528 // Why the double indirection? Because normally this function is also used 00529 // when doing session pool allocations, which has another set of tables, 00530 // sizes, and masks that live in session pool. Now we don't support session 00531 // pool so we only ever use the regular tables, but I'm keeping the code this 00532 // way so that the day we DO support session pool, it won't require that 00533 // many changes 00534 // 00535 Table = PoolTrackTable; 00536 TableMask = PoolTrackTableMask; 00537 TableSize = PoolTrackTableSize; 00538 00539 // 00540 // Compute the hash for this key, and loop all the possible buckets 00541 // 00542 Hash = ExpComputeHashForTag(Key, TableMask); 00543 Index = Hash; 00544 while (TRUE) 00545 { 00546 // 00547 // Have we found the entry for this tag? */ 00548 // 00549 TableEntry = &Table[Hash]; 00550 if (TableEntry->Key == Key) 00551 { 00552 // 00553 // Decrement the counters depending on if this was paged or nonpaged 00554 // pool 00555 // 00556 if ((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool) 00557 { 00558 InterlockedIncrement(&TableEntry->NonPagedFrees); 00559 InterlockedExchangeAddSizeT(&TableEntry->NonPagedBytes, 00560 -(SSIZE_T)NumberOfBytes); 00561 return; 00562 } 00563 InterlockedIncrement(&TableEntry->PagedFrees); 00564 InterlockedExchangeAddSizeT(&TableEntry->PagedBytes, 00565 -(SSIZE_T)NumberOfBytes); 00566 return; 00567 } 00568 00569 // 00570 // We should have only ended up with an empty entry if we've reached 00571 // the last bucket 00572 // 00573 if (!TableEntry->Key) ASSERT(Hash == TableMask); 00574 00575 // 00576 // This path is hit when we don't have an entry, and the current bucket 00577 // is full, so we simply try the next one 00578 // 00579 Hash = (Hash + 1) & TableMask; 00580 if (Hash == Index) break; 00581 } 00582 00583 // 00584 // And finally this path is hit when all the buckets are full, and we need 00585 // some expansion. This path is not yet supported in ReactOS and so we'll 00586 // ignore the tag 00587 // 00588 DPRINT1("Out of pool tag space, ignoring...\n"); 00589 } 00590 00591 VOID 00592 NTAPI 00593 ExpInsertPoolTracker(IN ULONG Key, 00594 IN SIZE_T NumberOfBytes, 00595 IN POOL_TYPE PoolType) 00596 { 00597 ULONG Hash, Index; 00598 KIRQL OldIrql; 00599 PPOOL_TRACKER_TABLE Table, TableEntry; 00600 SIZE_T TableMask, TableSize; 00601 00602 // 00603 // Remove the PROTECTED_POOL flag which is not part of the tag 00604 // 00605 Key &= ~PROTECTED_POOL; 00606 00607 // 00608 // With WinDBG you can set a tag you want to break on when an allocation is 00609 // attempted 00610 // 00611 if (Key == PoolHitTag) DbgBreakPoint(); 00612 00613 // 00614 // There is also an internal flag you can set to break on malformed tags 00615 // 00616 if (ExStopBadTags) ASSERT(Key & 0xFFFFFF00); 00617 00618 // 00619 // ASSERT on ReactOS features not yet supported 00620 // 00621 ASSERT(!(PoolType & SESSION_POOL_MASK)); 00622 ASSERT(KeGetCurrentProcessorNumber() == 0); 00623 00624 // 00625 // Why the double indirection? Because normally this function is also used 00626 // when doing session pool allocations, which has another set of tables, 00627 // sizes, and masks that live in session pool. Now we don't support session 00628 // pool so we only ever use the regular tables, but I'm keeping the code this 00629 // way so that the day we DO support session pool, it won't require that 00630 // many changes 00631 // 00632 Table = PoolTrackTable; 00633 TableMask = PoolTrackTableMask; 00634 TableSize = PoolTrackTableSize; 00635 00636 // 00637 // Compute the hash for this key, and loop all the possible buckets 00638 // 00639 Hash = ExpComputeHashForTag(Key, TableMask); 00640 Index = Hash; 00641 while (TRUE) 00642 { 00643 // 00644 // Do we already have an entry for this tag? */ 00645 // 00646 TableEntry = &Table[Hash]; 00647 if (TableEntry->Key == Key) 00648 { 00649 // 00650 // Increment the counters depending on if this was paged or nonpaged 00651 // pool 00652 // 00653 if ((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool) 00654 { 00655 InterlockedIncrement(&TableEntry->NonPagedAllocs); 00656 InterlockedExchangeAddSizeT(&TableEntry->NonPagedBytes, NumberOfBytes); 00657 return; 00658 } 00659 InterlockedIncrement(&TableEntry->PagedAllocs); 00660 InterlockedExchangeAddSizeT(&TableEntry->PagedBytes, NumberOfBytes); 00661 return; 00662 } 00663 00664 // 00665 // We don't have an entry yet, but we've found a free bucket for it 00666 // 00667 if (!(TableEntry->Key) && (Hash != PoolTrackTableSize - 1)) 00668 { 00669 // 00670 // We need to hold the lock while creating a new entry, since other 00671 // processors might be in this code path as well 00672 // 00673 ExAcquireSpinLock(&ExpTaggedPoolLock, &OldIrql); 00674 if (!PoolTrackTable[Hash].Key) 00675 { 00676 // 00677 // We've won the race, so now create this entry in the bucket 00678 // 00679 ASSERT(Table[Hash].Key == 0); 00680 PoolTrackTable[Hash].Key = Key; 00681 TableEntry->Key = Key; 00682 } 00683 ExReleaseSpinLock(&ExpTaggedPoolLock, OldIrql); 00684 00685 // 00686 // Now we force the loop to run again, and we should now end up in 00687 // the code path above which does the interlocked increments... 00688 // 00689 continue; 00690 } 00691 00692 // 00693 // This path is hit when we don't have an entry, and the current bucket 00694 // is full, so we simply try the next one 00695 // 00696 Hash = (Hash + 1) & TableMask; 00697 if (Hash == Index) break; 00698 } 00699 00700 // 00701 // And finally this path is hit when all the buckets are full, and we need 00702 // some expansion. This path is not yet supported in ReactOS and so we'll 00703 // ignore the tag 00704 // 00705 DPRINT1("Out of pool tag space, ignoring...\n"); 00706 } 00707 00708 VOID 00709 NTAPI 00710 INIT_FUNCTION 00711 ExInitializePoolDescriptor(IN PPOOL_DESCRIPTOR PoolDescriptor, 00712 IN POOL_TYPE PoolType, 00713 IN ULONG PoolIndex, 00714 IN ULONG Threshold, 00715 IN PVOID PoolLock) 00716 { 00717 PLIST_ENTRY NextEntry, LastEntry; 00718 00719 // 00720 // Setup the descriptor based on the caller's request 00721 // 00722 PoolDescriptor->PoolType = PoolType; 00723 PoolDescriptor->PoolIndex = PoolIndex; 00724 PoolDescriptor->Threshold = Threshold; 00725 PoolDescriptor->LockAddress = PoolLock; 00726 00727 // 00728 // Initialize accounting data 00729 // 00730 PoolDescriptor->RunningAllocs = 0; 00731 PoolDescriptor->RunningDeAllocs = 0; 00732 PoolDescriptor->TotalPages = 0; 00733 PoolDescriptor->TotalBytes = 0; 00734 PoolDescriptor->TotalBigPages = 0; 00735 00736 // 00737 // Nothing pending for now 00738 // 00739 PoolDescriptor->PendingFrees = NULL; 00740 PoolDescriptor->PendingFreeDepth = 0; 00741 00742 // 00743 // Loop all the descriptor's allocation lists and initialize them 00744 // 00745 NextEntry = PoolDescriptor->ListHeads; 00746 LastEntry = NextEntry + POOL_LISTS_PER_PAGE; 00747 while (NextEntry < LastEntry) 00748 { 00749 ExpInitializePoolListHead(NextEntry); 00750 NextEntry++; 00751 } 00752 00753 // 00754 // Note that ReactOS does not support Session Pool Yet 00755 // 00756 ASSERT(PoolType != PagedPoolSession); 00757 } 00758 00759 VOID 00760 NTAPI 00761 INIT_FUNCTION 00762 InitializePool(IN POOL_TYPE PoolType, 00763 IN ULONG Threshold) 00764 { 00765 PPOOL_DESCRIPTOR Descriptor; 00766 SIZE_T TableSize; 00767 ULONG i; 00768 00769 // 00770 // Check what kind of pool this is 00771 // 00772 if (PoolType == NonPagedPool) 00773 { 00774 // 00775 // Compute the track table size and convert it from a power of two to an 00776 // actual byte size 00777 // 00778 // NOTE: On checked builds, we'll assert if the registry table size was 00779 // invalid, while on retail builds we'll just break out of the loop at 00780 // that point. 00781 // 00782 TableSize = min(PoolTrackTableSize, MmSizeOfNonPagedPoolInBytes >> 8); 00783 for (i = 0; i < 32; i++) 00784 { 00785 if (TableSize & 1) 00786 { 00787 ASSERT((TableSize & ~1) == 0); 00788 if (!(TableSize & ~1)) break; 00789 } 00790 TableSize >>= 1; 00791 } 00792 00793 // 00794 // If we hit bit 32, than no size was defined in the registry, so 00795 // we'll use the default size of 2048 entries. 00796 // 00797 // Otherwise, use the size from the registry, as long as it's not 00798 // smaller than 64 entries. 00799 // 00800 if (i == 32) 00801 { 00802 PoolTrackTableSize = 2048; 00803 } 00804 else 00805 { 00806 PoolTrackTableSize = max(1 << i, 64); 00807 } 00808 00809 // 00810 // Loop trying with the biggest specified size first, and cut it down 00811 // by a power of two each iteration in case not enough memory exist 00812 // 00813 while (TRUE) 00814 { 00815 // 00816 // Do not allow overflow 00817 // 00818 if ((PoolTrackTableSize + 1) > (MAXULONG_PTR / sizeof(POOL_TRACKER_TABLE))) 00819 { 00820 PoolTrackTableSize >>= 1; 00821 continue; 00822 } 00823 00824 // 00825 // Allocate the tracker table and exit the loop if this worked 00826 // 00827 PoolTrackTable = MiAllocatePoolPages(NonPagedPool, 00828 (PoolTrackTableSize + 1) * 00829 sizeof(POOL_TRACKER_TABLE)); 00830 if (PoolTrackTable) break; 00831 00832 // 00833 // Otherwise, as long as we're not down to the last bit, keep 00834 // iterating 00835 // 00836 if (PoolTrackTableSize == 1) 00837 { 00838 KeBugCheckEx(MUST_SUCCEED_POOL_EMPTY, 00839 TableSize, 00840 0xFFFFFFFF, 00841 0xFFFFFFFF, 00842 0xFFFFFFFF); 00843 } 00844 PoolTrackTableSize >>= 1; 00845 } 00846 00847 // 00848 // Finally, add one entry, compute the hash, and zero the table 00849 // 00850 PoolTrackTableSize++; 00851 PoolTrackTableMask = PoolTrackTableSize - 2; 00852 00853 RtlZeroMemory(PoolTrackTable, 00854 PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE)); 00855 00856 // 00857 // We now do the exact same thing with the tracker table for big pages 00858 // 00859 TableSize = min(PoolBigPageTableSize, MmSizeOfNonPagedPoolInBytes >> 8); 00860 for (i = 0; i < 32; i++) 00861 { 00862 if (TableSize & 1) 00863 { 00864 ASSERT((TableSize & ~1) == 0); 00865 if (!(TableSize & ~1)) break; 00866 } 00867 TableSize >>= 1; 00868 } 00869 00870 // 00871 // For big pages, the default tracker table is 4096 entries, while the 00872 // minimum is still 64 00873 // 00874 if (i == 32) 00875 { 00876 PoolBigPageTableSize = 4096; 00877 } 00878 else 00879 { 00880 PoolBigPageTableSize = max(1 << i, 64); 00881 } 00882 00883 // 00884 // Again, run the exact same loop we ran earlier, but this time for the 00885 // big pool tracker instead 00886 // 00887 while (TRUE) 00888 { 00889 if ((PoolBigPageTableSize + 1) > (MAXULONG_PTR / sizeof(POOL_TRACKER_BIG_PAGES))) 00890 { 00891 PoolBigPageTableSize >>= 1; 00892 continue; 00893 } 00894 00895 PoolBigPageTable = MiAllocatePoolPages(NonPagedPool, 00896 PoolBigPageTableSize * 00897 sizeof(POOL_TRACKER_BIG_PAGES)); 00898 if (PoolBigPageTable) break; 00899 00900 if (PoolBigPageTableSize == 1) 00901 { 00902 KeBugCheckEx(MUST_SUCCEED_POOL_EMPTY, 00903 TableSize, 00904 0xFFFFFFFF, 00905 0xFFFFFFFF, 00906 0xFFFFFFFF); 00907 } 00908 00909 PoolBigPageTableSize >>= 1; 00910 } 00911 00912 // 00913 // An extra entry is not needed for for the big pool tracker, so just 00914 // compute the hash and zero it 00915 // 00916 PoolBigPageTableHash = PoolBigPageTableSize - 1; 00917 RtlZeroMemory(PoolBigPageTable, 00918 PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES)); 00919 for (i = 0; i < PoolBigPageTableSize; i++) PoolBigPageTable[i].Va = (PVOID)1; 00920 00921 // 00922 // During development, print this out so we can see what's happening 00923 // 00924 DPRINT1("EXPOOL: Pool Tracker Table at: 0x%p with 0x%lx bytes\n", 00925 PoolTrackTable, PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE)); 00926 DPRINT1("EXPOOL: Big Pool Tracker Table at: 0x%p with 0x%lx bytes\n", 00927 PoolBigPageTable, PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES)); 00928 00929 // 00930 // Insert the generic tracker for all of big pool 00931 // 00932 ExpInsertPoolTracker('looP', 00933 ROUND_TO_PAGES(PoolBigPageTableSize * 00934 sizeof(POOL_TRACKER_BIG_PAGES)), 00935 NonPagedPool); 00936 00937 // 00938 // No support for NUMA systems at this time 00939 // 00940 ASSERT(KeNumberNodes == 1); 00941 00942 // 00943 // Initialize the tag spinlock 00944 // 00945 KeInitializeSpinLock(&ExpTaggedPoolLock); 00946 00947 // 00948 // Initialize the nonpaged pool descriptor 00949 // 00950 PoolVector[NonPagedPool] = &NonPagedPoolDescriptor; 00951 ExInitializePoolDescriptor(PoolVector[NonPagedPool], 00952 NonPagedPool, 00953 0, 00954 Threshold, 00955 NULL); 00956 } 00957 else 00958 { 00959 // 00960 // No support for NUMA systems at this time 00961 // 00962 ASSERT(KeNumberNodes == 1); 00963 00964 // 00965 // Allocate the pool descriptor 00966 // 00967 Descriptor = ExAllocatePoolWithTag(NonPagedPool, 00968 sizeof(KGUARDED_MUTEX) + 00969 sizeof(POOL_DESCRIPTOR), 00970 'looP'); 00971 if (!Descriptor) 00972 { 00973 // 00974 // This is really bad... 00975 // 00976 KeBugCheckEx(MUST_SUCCEED_POOL_EMPTY, 00977 0, 00978 -1, 00979 -1, 00980 -1); 00981 } 00982 00983 // 00984 // Setup the vector and guarded mutex for paged pool 00985 // 00986 PoolVector[PagedPool] = Descriptor; 00987 ExpPagedPoolMutex = (PKGUARDED_MUTEX)(Descriptor + 1); 00988 ExpPagedPoolDescriptor[0] = Descriptor; 00989 KeInitializeGuardedMutex(ExpPagedPoolMutex); 00990 ExInitializePoolDescriptor(Descriptor, 00991 PagedPool, 00992 0, 00993 Threshold, 00994 ExpPagedPoolMutex); 00995 00996 // 00997 // Insert the generic tracker for all of nonpaged pool 00998 // 00999 ExpInsertPoolTracker('looP', 01000 ROUND_TO_PAGES(PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE)), 01001 NonPagedPool); 01002 } 01003 } 01004 01005 FORCEINLINE 01006 KIRQL 01007 ExLockPool(IN PPOOL_DESCRIPTOR Descriptor) 01008 { 01009 // 01010 // Check if this is nonpaged pool 01011 // 01012 if ((Descriptor->PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool) 01013 { 01014 // 01015 // Use the queued spin lock 01016 // 01017 return KeAcquireQueuedSpinLock(LockQueueNonPagedPoolLock); 01018 } 01019 else 01020 { 01021 // 01022 // Use the guarded mutex 01023 // 01024 KeAcquireGuardedMutex(Descriptor->LockAddress); 01025 return APC_LEVEL; 01026 } 01027 } 01028 01029 FORCEINLINE 01030 VOID 01031 ExUnlockPool(IN PPOOL_DESCRIPTOR Descriptor, 01032 IN KIRQL OldIrql) 01033 { 01034 // 01035 // Check if this is nonpaged pool 01036 // 01037 if ((Descriptor->PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool) 01038 { 01039 // 01040 // Use the queued spin lock 01041 // 01042 KeReleaseQueuedSpinLock(LockQueueNonPagedPoolLock, OldIrql); 01043 } 01044 else 01045 { 01046 // 01047 // Use the guarded mutex 01048 // 01049 KeReleaseGuardedMutex(Descriptor->LockAddress); 01050 } 01051 } 01052 01053 VOID 01054 NTAPI 01055 ExpGetPoolTagInfoTarget(IN PKDPC Dpc, 01056 IN PVOID DeferredContext, 01057 IN PVOID SystemArgument1, 01058 IN PVOID SystemArgument2) 01059 { 01060 PPOOL_DPC_CONTEXT Context = DeferredContext; 01061 UNREFERENCED_PARAMETER(Dpc); 01062 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 01063 01064 // 01065 // Make sure we win the race, and if we did, copy the data atomically 01066 // 01067 if (KeSignalCallDpcSynchronize(SystemArgument2)) 01068 { 01069 RtlCopyMemory(Context->PoolTrackTable, 01070 PoolTrackTable, 01071 Context->PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE)); 01072 01073 // 01074 // This is here because ReactOS does not yet support expansion 01075 // 01076 ASSERT(Context->PoolTrackTableSizeExpansion == 0); 01077 } 01078 01079 // 01080 // Regardless of whether we won or not, we must now synchronize and then 01081 // decrement the barrier since this is one more processor that has completed 01082 // the callback. 01083 // 01084 KeSignalCallDpcSynchronize(SystemArgument2); 01085 KeSignalCallDpcDone(SystemArgument1); 01086 } 01087 01088 NTSTATUS 01089 NTAPI 01090 ExGetPoolTagInfo(IN PSYSTEM_POOLTAG_INFORMATION SystemInformation, 01091 IN ULONG SystemInformationLength, 01092 IN OUT PULONG ReturnLength OPTIONAL) 01093 { 01094 ULONG TableSize, CurrentLength; 01095 ULONG EntryCount; 01096 NTSTATUS Status = STATUS_SUCCESS; 01097 PSYSTEM_POOLTAG TagEntry; 01098 PPOOL_TRACKER_TABLE Buffer, TrackerEntry; 01099 POOL_DPC_CONTEXT Context; 01100 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); 01101 01102 // 01103 // Keep track of how much data the caller's buffer must hold 01104 // 01105 CurrentLength = FIELD_OFFSET(SYSTEM_POOLTAG_INFORMATION, TagInfo); 01106 01107 // 01108 // Initialize the caller's buffer 01109 // 01110 TagEntry = &SystemInformation->TagInfo[0]; 01111 SystemInformation->Count = 0; 01112 01113 // 01114 // Capture the number of entries, and the total size needed to make a copy 01115 // of the table 01116 // 01117 EntryCount = (ULONG)PoolTrackTableSize; 01118 TableSize = EntryCount * sizeof(POOL_TRACKER_TABLE); 01119 01120 // 01121 // Allocate the "Generic DPC" temporary buffer 01122 // 01123 Buffer = ExAllocatePoolWithTag(NonPagedPool, TableSize, 'ofnI'); 01124 if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES; 01125 01126 // 01127 // Do a "Generic DPC" to atomically retrieve the tag and allocation data 01128 // 01129 Context.PoolTrackTable = Buffer; 01130 Context.PoolTrackTableSize = PoolTrackTableSize; 01131 Context.PoolTrackTableExpansion = NULL; 01132 Context.PoolTrackTableSizeExpansion = 0; 01133 KeGenericCallDpc(ExpGetPoolTagInfoTarget, &Context); 01134 01135 // 01136 // Now parse the results 01137 // 01138 for (TrackerEntry = Buffer; TrackerEntry < (Buffer + EntryCount); TrackerEntry++) 01139 { 01140 // 01141 // If the entry is empty, skip it 01142 // 01143 if (!TrackerEntry->Key) continue; 01144 01145 // 01146 // Otherwise, add one more entry to the caller's buffer, and ensure that 01147 // enough space has been allocated in it 01148 // 01149 SystemInformation->Count++; 01150 CurrentLength += sizeof(*TagEntry); 01151 if (SystemInformationLength < CurrentLength) 01152 { 01153 // 01154 // The caller's buffer is too small, so set a failure code. The 01155 // caller will know the count, as well as how much space is needed. 01156 // 01157 // We do NOT break out of the loop, because we want to keep incrementing 01158 // the Count as well as CurrentLength so that the caller can know the 01159 // final numbers 01160 // 01161 Status = STATUS_INFO_LENGTH_MISMATCH; 01162 } 01163 else 01164 { 01165 // 01166 // Small sanity check that our accounting is working correctly 01167 // 01168 ASSERT(TrackerEntry->PagedAllocs >= TrackerEntry->PagedFrees); 01169 ASSERT(TrackerEntry->NonPagedAllocs >= TrackerEntry->NonPagedFrees); 01170 01171 // 01172 // Return the data into the caller's buffer 01173 // 01174 TagEntry->TagUlong = TrackerEntry->Key; 01175 TagEntry->PagedAllocs = TrackerEntry->PagedAllocs; 01176 TagEntry->PagedFrees = TrackerEntry->PagedFrees; 01177 TagEntry->PagedUsed = TrackerEntry->PagedBytes; 01178 TagEntry->NonPagedAllocs = TrackerEntry->NonPagedAllocs; 01179 TagEntry->NonPagedFrees = TrackerEntry->NonPagedFrees; 01180 TagEntry->NonPagedUsed = TrackerEntry->NonPagedBytes; 01181 TagEntry++; 01182 } 01183 } 01184 01185 // 01186 // Free the "Generic DPC" temporary buffer, return the buffer length and status 01187 // 01188 ExFreePool(Buffer); 01189 if (ReturnLength) *ReturnLength = CurrentLength; 01190 return Status; 01191 } 01192 01193 BOOLEAN 01194 NTAPI 01195 ExpAddTagForBigPages(IN PVOID Va, 01196 IN ULONG Key, 01197 IN ULONG NumberOfPages, 01198 IN POOL_TYPE PoolType) 01199 { 01200 ULONG Hash, i = 0; 01201 PVOID OldVa; 01202 KIRQL OldIrql; 01203 SIZE_T TableSize; 01204 PPOOL_TRACKER_BIG_PAGES Entry, EntryEnd, EntryStart; 01205 ASSERT(((ULONG_PTR)Va & POOL_BIG_TABLE_ENTRY_FREE) == 0); 01206 ASSERT(!(PoolType & SESSION_POOL_MASK)); 01207 01208 // 01209 // As the table is expandable, these values must only be read after acquiring 01210 // the lock to avoid a teared access during an expansion 01211 // 01212 Hash = ExpComputePartialHashForAddress(Va); 01213 KeAcquireSpinLock(&ExpLargePoolTableLock, &OldIrql); 01214 Hash &= PoolBigPageTableHash; 01215 TableSize = PoolBigPageTableSize; 01216 01217 // 01218 // We loop from the current hash bucket to the end of the table, and then 01219 // rollover to hash bucket 0 and keep going from there. If we return back 01220 // to the beginning, then we attempt expansion at the bottom of the loop 01221 // 01222 EntryStart = Entry = &PoolBigPageTable[Hash]; 01223 EntryEnd = &PoolBigPageTable[TableSize]; 01224 do 01225 { 01226 // 01227 // Make sure that this is a free entry and attempt to atomically make the 01228 // entry busy now 01229 // 01230 OldVa = Entry->Va; 01231 if (((ULONG_PTR)OldVa & POOL_BIG_TABLE_ENTRY_FREE) && 01232 (InterlockedCompareExchangePointer(&Entry->Va, Va, OldVa) == OldVa)) 01233 { 01234 // 01235 // We now own this entry, write down the size and the pool tag 01236 // 01237 Entry->Key = Key; 01238 Entry->NumberOfPages = NumberOfPages; 01239 01240 // 01241 // Add one more entry to the count, and see if we're getting within 01242 // 25% of the table size, at which point we'll do an expansion now 01243 // to avoid blocking too hard later on. 01244 // 01245 // Note that we only do this if it's also been the 16th time that we 01246 // keep losing the race or that we are not finding a free entry anymore, 01247 // which implies a massive number of concurrent big pool allocations. 01248 // 01249 InterlockedIncrement(&ExpPoolBigEntriesInUse); 01250 if ((i >= 16) && (ExpPoolBigEntriesInUse > (TableSize / 4))) 01251 { 01252 DPRINT1("Should attempt expansion since we now have %d entries\n", 01253 ExpPoolBigEntriesInUse); 01254 } 01255 01256 // 01257 // We have our entry, return 01258 // 01259 KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql); 01260 return TRUE; 01261 } 01262 01263 // 01264 // We don't have our entry yet, so keep trying, making the entry list 01265 // circular if we reach the last entry. We'll eventually break out of 01266 // the loop once we've rolled over and returned back to our original 01267 // hash bucket 01268 // 01269 i++; 01270 if (++Entry >= EntryEnd) Entry = &PoolBigPageTable[0]; 01271 } while (Entry != EntryStart); 01272 01273 // 01274 // This means there's no free hash buckets whatsoever, so we would now have 01275 // to attempt expanding the table 01276 // 01277 DPRINT1("Big pool expansion needed, not implemented!\n"); 01278 KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql); 01279 return FALSE; 01280 } 01281 01282 ULONG 01283 NTAPI 01284 ExpFindAndRemoveTagBigPages(IN PVOID Va, 01285 OUT PULONG_PTR BigPages, 01286 IN POOL_TYPE PoolType) 01287 { 01288 BOOLEAN FirstTry = TRUE; 01289 SIZE_T TableSize; 01290 KIRQL OldIrql; 01291 ULONG PoolTag, Hash; 01292 PPOOL_TRACKER_BIG_PAGES Entry; 01293 ASSERT(((ULONG_PTR)Va & POOL_BIG_TABLE_ENTRY_FREE) == 0); 01294 ASSERT(!(PoolType & SESSION_POOL_MASK)); 01295 01296 // 01297 // As the table is expandable, these values must only be read after acquiring 01298 // the lock to avoid a teared access during an expansion 01299 // 01300 Hash = ExpComputePartialHashForAddress(Va); 01301 KeAcquireSpinLock(&ExpLargePoolTableLock, &OldIrql); 01302 Hash &= PoolBigPageTableHash; 01303 TableSize = PoolBigPageTableSize; 01304 01305 // 01306 // Loop while trying to find this big page allocation 01307 // 01308 while (PoolBigPageTable[Hash].Va != Va) 01309 { 01310 // 01311 // Increment the size until we go past the end of the table 01312 // 01313 if (++Hash >= TableSize) 01314 { 01315 // 01316 // Is this the second time we've tried? 01317 // 01318 if (!FirstTry) 01319 { 01320 // 01321 // This means it was never inserted into the pool table and it 01322 // received the special "BIG" tag -- return that and return 0 01323 // so that the code can ask Mm for the page count instead 01324 // 01325 KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql); 01326 *BigPages = 0; 01327 return ' GIB'; 01328 } 01329 01330 // 01331 // The first time this happens, reset the hash index and try again 01332 // 01333 Hash = 0; 01334 FirstTry = FALSE; 01335 } 01336 } 01337 01338 // 01339 // Now capture all the information we need from the entry, since after we 01340 // release the lock, the data can change 01341 // 01342 Entry = &PoolBigPageTable[Hash]; 01343 *BigPages = Entry->NumberOfPages; 01344 PoolTag = Entry->Key; 01345 01346 // 01347 // Set the free bit, and decrement the number of allocations. Finally, release 01348 // the lock and return the tag that was located 01349 // 01350 InterlockedIncrement((PLONG)&Entry->Va); 01351 InterlockedDecrement(&ExpPoolBigEntriesInUse); 01352 KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql); 01353 return PoolTag; 01354 } 01355 01356 VOID 01357 NTAPI 01358 ExQueryPoolUsage(OUT PULONG PagedPoolPages, 01359 OUT PULONG NonPagedPoolPages, 01360 OUT PULONG PagedPoolAllocs, 01361 OUT PULONG PagedPoolFrees, 01362 OUT PULONG PagedPoolLookasideHits, 01363 OUT PULONG NonPagedPoolAllocs, 01364 OUT PULONG NonPagedPoolFrees, 01365 OUT PULONG NonPagedPoolLookasideHits) 01366 { 01367 ULONG i; 01368 PPOOL_DESCRIPTOR PoolDesc; 01369 01370 // 01371 // Assume all failures 01372 // 01373 *PagedPoolPages = 0; 01374 *PagedPoolAllocs = 0; 01375 *PagedPoolFrees = 0; 01376 01377 // 01378 // Tally up the totals for all the apged pool 01379 // 01380 for (i = 0; i < ExpNumberOfPagedPools + 1; i++) 01381 { 01382 PoolDesc = ExpPagedPoolDescriptor[i]; 01383 *PagedPoolPages += PoolDesc->TotalPages + PoolDesc->TotalBigPages; 01384 *PagedPoolAllocs += PoolDesc->RunningAllocs; 01385 *PagedPoolFrees += PoolDesc->RunningDeAllocs; 01386 } 01387 01388 // 01389 // The first non-paged pool has a hardcoded well-known descriptor name 01390 // 01391 PoolDesc = &NonPagedPoolDescriptor; 01392 *NonPagedPoolPages = PoolDesc->TotalPages + PoolDesc->TotalBigPages; 01393 *NonPagedPoolAllocs = PoolDesc->RunningAllocs; 01394 *NonPagedPoolFrees = PoolDesc->RunningDeAllocs; 01395 01396 // 01397 // If the system has more than one non-paged pool, copy the other descriptor 01398 // totals as well 01399 // 01400 #if 0 01401 if (ExpNumberOfNonPagedPools > 1) 01402 { 01403 for (i = 0; i < ExpNumberOfNonPagedPools; i++) 01404 { 01405 PoolDesc = ExpNonPagedPoolDescriptor[i]; 01406 *NonPagedPoolPages += PoolDesc->TotalPages + PoolDesc->TotalBigPages; 01407 *NonPagedPoolAllocs += PoolDesc->RunningAllocs; 01408 *NonPagedPoolFrees += PoolDesc->RunningDeAllocs; 01409 } 01410 } 01411 #endif 01412 01413 // 01414 // FIXME: Not yet supported 01415 // 01416 *NonPagedPoolLookasideHits += 0; 01417 *PagedPoolLookasideHits += 0; 01418 } 01419 01420 /* PUBLIC FUNCTIONS ***********************************************************/ 01421 01422 /* 01423 * @implemented 01424 */ 01425 PVOID 01426 NTAPI 01427 ExAllocatePoolWithTag(IN POOL_TYPE PoolType, 01428 IN SIZE_T NumberOfBytes, 01429 IN ULONG Tag) 01430 { 01431 PPOOL_DESCRIPTOR PoolDesc; 01432 PLIST_ENTRY ListHead; 01433 PPOOL_HEADER Entry, NextEntry, FragmentEntry; 01434 KIRQL OldIrql; 01435 USHORT BlockSize, i; 01436 ULONG OriginalType; 01437 PKPRCB Prcb = KeGetCurrentPrcb(); 01438 PGENERAL_LOOKASIDE LookasideList; 01439 01440 // 01441 // Some sanity checks 01442 // 01443 ASSERT(Tag != 0); 01444 ASSERT(Tag != ' GIB'); 01445 ASSERT(NumberOfBytes != 0); 01446 ExpCheckPoolIrqlLevel(PoolType, NumberOfBytes, NULL); 01447 01448 // 01449 // Not supported in ReactOS 01450 // 01451 ASSERT(!(PoolType & SESSION_POOL_MASK)); 01452 01453 // 01454 // Check if verifier or special pool is enabled 01455 // 01456 if (ExpPoolFlags & (POOL_FLAG_VERIFIER | POOL_FLAG_SPECIAL_POOL)) 01457 { 01458 // 01459 // For verifier, we should call the verification routine 01460 // 01461 if (ExpPoolFlags & POOL_FLAG_VERIFIER) 01462 { 01463 DPRINT1("Driver Verifier is not yet supported\n"); 01464 } 01465 01466 // 01467 // For special pool, we check if this is a suitable allocation and do 01468 // the special allocation if needed 01469 // 01470 if (ExpPoolFlags & POOL_FLAG_SPECIAL_POOL) 01471 { 01472 // 01473 // Check if this is a special pool allocation 01474 // 01475 if (MmUseSpecialPool(NumberOfBytes, Tag)) 01476 { 01477 // 01478 // Try to allocate using special pool 01479 // 01480 Entry = MmAllocateSpecialPool(NumberOfBytes, Tag, PoolType, 2); 01481 if (Entry) return Entry; 01482 } 01483 } 01484 } 01485 01486 // 01487 // Get the pool type and its corresponding vector for this request 01488 // 01489 OriginalType = PoolType; 01490 PoolType = PoolType & BASE_POOL_TYPE_MASK; 01491 PoolDesc = PoolVector[PoolType]; 01492 ASSERT(PoolDesc != NULL); 01493 01494 // 01495 // Check if this is a big page allocation 01496 // 01497 if (NumberOfBytes > POOL_MAX_ALLOC) 01498 { 01499 // 01500 // Allocate pages for it 01501 // 01502 Entry = MiAllocatePoolPages(OriginalType, NumberOfBytes); 01503 if (!Entry) 01504 { 01505 // 01506 // Must succeed pool is deprecated, but still supported. These allocation 01507 // failures must cause an immediate bugcheck 01508 // 01509 if (OriginalType & MUST_SUCCEED_POOL_MASK) 01510 { 01511 KeBugCheckEx(MUST_SUCCEED_POOL_EMPTY, 01512 NumberOfBytes, 01513 NonPagedPoolDescriptor.TotalPages, 01514 NonPagedPoolDescriptor.TotalBigPages, 01515 0); 01516 } 01517 01518 // 01519 // Internal debugging 01520 // 01521 ExPoolFailures++; 01522 01523 // 01524 // This flag requests printing failures, and can also further specify 01525 // breaking on failures 01526 // 01527 if (ExpPoolFlags & POOL_FLAG_DBGPRINT_ON_FAILURE) 01528 { 01529 DPRINT1("EX: ExAllocatePool (%p, 0x%x) returning NULL\n", 01530 NumberOfBytes, 01531 OriginalType); 01532 if (ExpPoolFlags & POOL_FLAG_CRASH_ON_FAILURE) DbgBreakPoint(); 01533 } 01534 01535 // 01536 // Finally, this flag requests an exception, which we are more than 01537 // happy to raise! 01538 // 01539 if (OriginalType & POOL_RAISE_IF_ALLOCATION_FAILURE) 01540 { 01541 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); 01542 } 01543 } 01544 01545 // 01546 // Increment required counters 01547 // 01548 InterlockedExchangeAdd((PLONG)&PoolDesc->TotalBigPages, 01549 (LONG)BYTES_TO_PAGES(NumberOfBytes)); 01550 InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, NumberOfBytes); 01551 InterlockedIncrement((PLONG)&PoolDesc->RunningAllocs); 01552 01553 // 01554 // Add a tag for the big page allocation and switch to the generic "BIG" 01555 // tag if we failed to do so, then insert a tracker for this alloation. 01556 // 01557 if (!ExpAddTagForBigPages(Entry, 01558 Tag, 01559 (ULONG)BYTES_TO_PAGES(NumberOfBytes), 01560 OriginalType)) 01561 { 01562 Tag = ' GIB'; 01563 } 01564 ExpInsertPoolTracker(Tag, ROUND_TO_PAGES(NumberOfBytes), OriginalType); 01565 return Entry; 01566 } 01567 01568 // 01569 // Should never request 0 bytes from the pool, but since so many drivers do 01570 // it, we'll just assume they want 1 byte, based on NT's similar behavior 01571 // 01572 if (!NumberOfBytes) NumberOfBytes = 1; 01573 01574 // 01575 // A pool allocation is defined by its data, a linked list to connect it to 01576 // the free list (if necessary), and a pool header to store accounting info. 01577 // Calculate this size, then convert it into a block size (units of pool 01578 // headers) 01579 // 01580 // Note that i cannot overflow (past POOL_LISTS_PER_PAGE) because any such 01581 // request would've been treated as a POOL_MAX_ALLOC earlier and resulted in 01582 // the direct allocation of pages. 01583 // 01584 i = (USHORT)((NumberOfBytes + sizeof(POOL_HEADER) + (POOL_BLOCK_SIZE - 1)) 01585 / POOL_BLOCK_SIZE); 01586 01587 // 01588 // Handle lookaside list optimization for both paged and nonpaged pool 01589 // 01590 if (i <= MAXIMUM_PROCESSORS) 01591 { 01592 // 01593 // Try popping it from the per-CPU lookaside list 01594 // 01595 LookasideList = (PoolType == PagedPool) ? 01596 Prcb->PPPagedLookasideList[i - 1].P : 01597 Prcb->PPNPagedLookasideList[i - 1].P; 01598 LookasideList->TotalAllocates++; 01599 Entry = (PPOOL_HEADER)InterlockedPopEntrySList(&LookasideList->ListHead); 01600 if (!Entry) 01601 { 01602 // 01603 // We failed, try popping it from the global list 01604 // 01605 LookasideList = (PoolType == PagedPool) ? 01606 Prcb->PPPagedLookasideList[i - 1].L : 01607 Prcb->PPNPagedLookasideList[i - 1].L; 01608 LookasideList->TotalAllocates++; 01609 Entry = (PPOOL_HEADER)InterlockedPopEntrySList(&LookasideList->ListHead); 01610 } 01611 01612 // 01613 // If we were able to pop it, update the accounting and return the block 01614 // 01615 if (Entry) 01616 { 01617 LookasideList->AllocateHits++; 01618 01619 // 01620 // Get the real entry, write down its pool type, and track it 01621 // 01622 Entry--; 01623 Entry->PoolType = PoolType + 1; 01624 ExpInsertPoolTracker(Tag, 01625 Entry->BlockSize * POOL_BLOCK_SIZE, 01626 OriginalType); 01627 01628 // 01629 // Return the pool allocation 01630 // 01631 Entry->PoolTag = Tag; 01632 (POOL_FREE_BLOCK(Entry))->Flink = NULL; 01633 (POOL_FREE_BLOCK(Entry))->Blink = NULL; 01634 return POOL_FREE_BLOCK(Entry); 01635 } 01636 } 01637 01638 // 01639 // Loop in the free lists looking for a block if this size. Start with the 01640 // list optimized for this kind of size lookup 01641 // 01642 ListHead = &PoolDesc->ListHeads[i]; 01643 do 01644 { 01645 // 01646 // Are there any free entries available on this list? 01647 // 01648 if (!ExpIsPoolListEmpty(ListHead)) 01649 { 01650 // 01651 // Acquire the pool lock now 01652 // 01653 OldIrql = ExLockPool(PoolDesc); 01654 01655 // 01656 // And make sure the list still has entries 01657 // 01658 if (ExpIsPoolListEmpty(ListHead)) 01659 { 01660 // 01661 // Someone raced us (and won) before we had a chance to acquire 01662 // the lock. 01663 // 01664 // Try again! 01665 // 01666 ExUnlockPool(PoolDesc, OldIrql); 01667 ListHead++; 01668 continue; 01669 } 01670 01671 // 01672 // Remove a free entry from the list 01673 // Note that due to the way we insert free blocks into multiple lists 01674 // there is a guarantee that any block on this list will either be 01675 // of the correct size, or perhaps larger. 01676 // 01677 ExpCheckPoolLinks(ListHead); 01678 Entry = POOL_ENTRY(ExpRemovePoolHeadList(ListHead)); 01679 ExpCheckPoolLinks(ListHead); 01680 ExpCheckPoolBlocks(Entry); 01681 ASSERT(Entry->BlockSize >= i); 01682 ASSERT(Entry->PoolType == 0); 01683 01684 // 01685 // Check if this block is larger that what we need. The block could 01686 // not possibly be smaller, due to the reason explained above (and 01687 // we would've asserted on a checked build if this was the case). 01688 // 01689 if (Entry->BlockSize != i) 01690 { 01691 // 01692 // Is there an entry before this one? 01693 // 01694 if (Entry->PreviousSize == 0) 01695 { 01696 // 01697 // There isn't anyone before us, so take the next block and 01698 // turn it into a fragment that contains the leftover data 01699 // that we don't need to satisfy the caller's request 01700 // 01701 FragmentEntry = POOL_BLOCK(Entry, i); 01702 FragmentEntry->BlockSize = Entry->BlockSize - i; 01703 01704 // 01705 // And make it point back to us 01706 // 01707 FragmentEntry->PreviousSize = i; 01708 01709 // 01710 // Now get the block that follows the new fragment and check 01711 // if it's still on the same page as us (and not at the end) 01712 // 01713 NextEntry = POOL_NEXT_BLOCK(FragmentEntry); 01714 if (PAGE_ALIGN(NextEntry) != NextEntry) 01715 { 01716 // 01717 // Adjust this next block to point to our newly created 01718 // fragment block 01719 // 01720 NextEntry->PreviousSize = FragmentEntry->BlockSize; 01721 } 01722 } 01723 else 01724 { 01725 // 01726 // There is a free entry before us, which we know is smaller 01727 // so we'll make this entry the fragment instead 01728 // 01729 FragmentEntry = Entry; 01730 01731 // 01732 // And then we'll remove from it the actual size required. 01733 // Now the entry is a leftover free fragment 01734 // 01735 Entry->BlockSize -= i; 01736 01737 // 01738 // Now let's go to the next entry after the fragment (which 01739 // used to point to our original free entry) and make it 01740 // reference the new fragment entry instead. 01741 // 01742 // This is the entry that will actually end up holding the 01743 // allocation! 01744 // 01745 Entry = POOL_NEXT_BLOCK(Entry); 01746 Entry->PreviousSize = FragmentEntry->BlockSize; 01747 01748 // 01749 // And now let's go to the entry after that one and check if 01750 // it's still on the same page, and not at the end 01751 // 01752 NextEntry = POOL_BLOCK(Entry, i); 01753 if (PAGE_ALIGN(NextEntry) != NextEntry) 01754 { 01755 // 01756 // Make it reference the allocation entry 01757 // 01758 NextEntry->PreviousSize = i; 01759 } 01760 } 01761 01762 // 01763 // Now our (allocation) entry is the right size 01764 // 01765 Entry->BlockSize = i; 01766 01767 // 01768 // And the next entry is now the free fragment which contains 01769 // the remaining difference between how big the original entry 01770 // was, and the actual size the caller needs/requested. 01771 // 01772 FragmentEntry->PoolType = 0; 01773 BlockSize = FragmentEntry->BlockSize; 01774 01775 // 01776 // Now check if enough free bytes remained for us to have a 01777 // "full" entry, which contains enough bytes for a linked list 01778 // and thus can be used for allocations (up to 8 bytes...) 01779 // 01780 ExpCheckPoolLinks(&PoolDesc->ListHeads[BlockSize - 1]); 01781 if (BlockSize != 1) 01782 { 01783 // 01784 // Insert the free entry into the free list for this size 01785 // 01786 ExpInsertPoolTailList(&PoolDesc->ListHeads[BlockSize - 1], 01787 POOL_FREE_BLOCK(FragmentEntry)); 01788 ExpCheckPoolLinks(POOL_FREE_BLOCK(FragmentEntry)); 01789 } 01790 } 01791 01792 // 01793 // We have found an entry for this allocation, so set the pool type 01794 // and release the lock since we're done 01795 // 01796 Entry->PoolType = PoolType + 1; 01797 ExpCheckPoolBlocks(Entry); 01798 ExUnlockPool(PoolDesc, OldIrql); 01799 01800 // 01801 // Increment required counters 01802 // 01803 InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, Entry->BlockSize * POOL_BLOCK_SIZE); 01804 InterlockedIncrement((PLONG)&PoolDesc->RunningAllocs); 01805 01806 // 01807 // Track this allocation 01808 // 01809 ExpInsertPoolTracker(Tag, 01810 Entry->BlockSize * POOL_BLOCK_SIZE, 01811 OriginalType); 01812 01813 // 01814 // Return the pool allocation 01815 // 01816 Entry->PoolTag = Tag; 01817 (POOL_FREE_BLOCK(Entry))->Flink = NULL; 01818 (POOL_FREE_BLOCK(Entry))->Blink = NULL; 01819 return POOL_FREE_BLOCK(Entry); 01820 } 01821 } while (++ListHead != &PoolDesc->ListHeads[POOL_LISTS_PER_PAGE]); 01822 01823 // 01824 // There were no free entries left, so we have to allocate a new fresh page 01825 // 01826 Entry = MiAllocatePoolPages(PoolType, PAGE_SIZE); 01827 if (!Entry) 01828 { 01829 // 01830 // Must succeed pool is deprecated, but still supported. These allocation 01831 // failures must cause an immediate bugcheck 01832 // 01833 if (OriginalType & MUST_SUCCEED_POOL_MASK) 01834 { 01835 KeBugCheckEx(MUST_SUCCEED_POOL_EMPTY, 01836 PAGE_SIZE, 01837 NonPagedPoolDescriptor.TotalPages, 01838 NonPagedPoolDescriptor.TotalBigPages, 01839 0); 01840 } 01841 01842 // 01843 // Internal debugging 01844 // 01845 ExPoolFailures++; 01846 01847 // 01848 // This flag requests printing failures, and can also further specify 01849 // breaking on failures 01850 // 01851 if (ExpPoolFlags & POOL_FLAG_DBGPRINT_ON_FAILURE) 01852 { 01853 DPRINT1("EX: ExAllocatePool (%p, 0x%x) returning NULL\n", 01854 NumberOfBytes, 01855 OriginalType); 01856 if (ExpPoolFlags & POOL_FLAG_CRASH_ON_FAILURE) DbgBreakPoint(); 01857 } 01858 01859 // 01860 // Finally, this flag requests an exception, which we are more than 01861 // happy to raise! 01862 // 01863 if (OriginalType & POOL_RAISE_IF_ALLOCATION_FAILURE) 01864 { 01865 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); 01866 } 01867 01868 // 01869 // Return NULL to the caller in all other cases 01870 // 01871 return NULL; 01872 } 01873 01874 // 01875 // Setup the entry data 01876 // 01877 Entry->Ulong1 = 0; 01878 Entry->BlockSize = i; 01879 Entry->PoolType = PoolType + 1; 01880 01881 // 01882 // This page will have two entries -- one for the allocation (which we just 01883 // created above), and one for the remaining free bytes, which we're about 01884 // to create now. The free bytes are the whole page minus what was allocated 01885 // and then converted into units of block headers. 01886 // 01887 BlockSize = (PAGE_SIZE / POOL_BLOCK_SIZE) - i; 01888 FragmentEntry = POOL_BLOCK(Entry, i); 01889 FragmentEntry->Ulong1 = 0; 01890 FragmentEntry->BlockSize = BlockSize; 01891 FragmentEntry->PreviousSize = i; 01892 01893 // 01894 // Increment required counters 01895 // 01896 InterlockedIncrement((PLONG)&PoolDesc->TotalPages); 01897 InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, Entry->BlockSize * POOL_BLOCK_SIZE); 01898 01899 // 01900 // Now check if enough free bytes remained for us to have a "full" entry, 01901 // which contains enough bytes for a linked list and thus can be used for 01902 // allocations (up to 8 bytes...) 01903 // 01904 if (FragmentEntry->BlockSize != 1) 01905 { 01906 // 01907 // Excellent -- acquire the pool lock 01908 // 01909 OldIrql = ExLockPool(PoolDesc); 01910 01911 // 01912 // And insert the free entry into the free list for this block size 01913 // 01914 ExpCheckPoolLinks(&PoolDesc->ListHeads[BlockSize - 1]); 01915 ExpInsertPoolTailList(&PoolDesc->ListHeads[BlockSize - 1], 01916 POOL_FREE_BLOCK(FragmentEntry)); 01917 ExpCheckPoolLinks(POOL_FREE_BLOCK(FragmentEntry)); 01918 01919 // 01920 // Release the pool lock 01921 // 01922 ExpCheckPoolBlocks(Entry); 01923 ExUnlockPool(PoolDesc, OldIrql); 01924 } 01925 else 01926 { 01927 // 01928 // Simply do a sanity check 01929 // 01930 ExpCheckPoolBlocks(Entry); 01931 } 01932 01933 // 01934 // Increment performance counters and track this allocation 01935 // 01936 InterlockedIncrement((PLONG)&PoolDesc->RunningAllocs); 01937 ExpInsertPoolTracker(Tag, 01938 Entry->BlockSize * POOL_BLOCK_SIZE, 01939 PoolType); 01940 01941 // 01942 // And return the pool allocation 01943 // 01944 ExpCheckPoolBlocks(Entry); 01945 Entry->PoolTag = Tag; 01946 return POOL_FREE_BLOCK(Entry); 01947 } 01948 01949 /* 01950 * @implemented 01951 */ 01952 PVOID 01953 NTAPI 01954 ExAllocatePool(POOL_TYPE PoolType, 01955 SIZE_T NumberOfBytes) 01956 { 01957 // 01958 // Use a default tag of "None" 01959 // 01960 return ExAllocatePoolWithTag(PoolType, NumberOfBytes, TAG_NONE); 01961 } 01962 01963 /* 01964 * @implemented 01965 */ 01966 VOID 01967 NTAPI 01968 ExFreePoolWithTag(IN PVOID P, 01969 IN ULONG TagToFree) 01970 { 01971 PPOOL_HEADER Entry, NextEntry; 01972 USHORT BlockSize; 01973 KIRQL OldIrql; 01974 POOL_TYPE PoolType; 01975 PPOOL_DESCRIPTOR PoolDesc; 01976 ULONG Tag; 01977 BOOLEAN Combined = FALSE; 01978 PFN_NUMBER PageCount, RealPageCount; 01979 PKPRCB Prcb = KeGetCurrentPrcb(); 01980 PGENERAL_LOOKASIDE LookasideList; 01981 01982 // 01983 // Check if any of the debug flags are enabled 01984 // 01985 if (ExpPoolFlags & (POOL_FLAG_CHECK_TIMERS | 01986 POOL_FLAG_CHECK_WORKERS | 01987 POOL_FLAG_CHECK_RESOURCES | 01988 POOL_FLAG_VERIFIER | 01989 POOL_FLAG_CHECK_DEADLOCK | 01990 POOL_FLAG_SPECIAL_POOL)) 01991 { 01992 // 01993 // Check if special pool is enabled 01994 // 01995 if (ExpPoolFlags & POOL_FLAG_SPECIAL_POOL) 01996 { 01997 // 01998 // Check if it was allocated from a special pool 01999 // 02000 if (MmIsSpecialPoolAddress(P)) 02001 { 02002 // 02003 // Was deadlock verification also enabled? We can do some extra 02004 // checks at this point 02005 // 02006 if (ExpPoolFlags & POOL_FLAG_CHECK_DEADLOCK) 02007 { 02008 DPRINT1("Verifier not yet supported\n"); 02009 } 02010 02011 // 02012 // It is, so handle it via special pool free routine 02013 // 02014 MmFreeSpecialPool(P); 02015 return; 02016 } 02017 } 02018 02019 // 02020 // For non-big page allocations, we'll do a bunch of checks in here 02021 // 02022 if (PAGE_ALIGN(P) != P) 02023 { 02024 // 02025 // Get the entry for this pool allocation 02026 // The pointer math here may look wrong or confusing, but it is quite right 02027 // 02028 Entry = P; 02029 Entry--; 02030 02031 // 02032 // Get the pool type 02033 // 02034 PoolType = (Entry->PoolType - 1) & BASE_POOL_TYPE_MASK; 02035 02036 // 02037 // FIXME: Many other debugging checks go here 02038 // 02039 ExpCheckPoolIrqlLevel(PoolType, 0, P); 02040 } 02041 } 02042 02043 // 02044 // Check if this is a big page allocation 02045 // 02046 if (PAGE_ALIGN(P) == P) 02047 { 02048 // 02049 // We need to find the tag for it, so first we need to find out what 02050 // kind of allocation this was (paged or nonpaged), then we can go 02051 // ahead and try finding the tag for it. Remember to get rid of the 02052 // PROTECTED_POOL tag if it's found. 02053 // 02054 // Note that if at insertion time, we failed to add the tag for a big 02055 // pool allocation, we used a special tag called 'BIG' to identify the 02056 // allocation, and we may get this tag back. In this scenario, we must 02057 // manually get the size of the allocation by actually counting through 02058 // the PFN database. 02059 // 02060 PoolType = MmDeterminePoolType(P); 02061 ExpCheckPoolIrqlLevel(PoolType, 0, P); 02062 Tag = ExpFindAndRemoveTagBigPages(P, &PageCount, PoolType); 02063 if (!Tag) 02064 { 02065 DPRINT1("We do not know the size of this allocation. This is not yet supported\n"); 02066 ASSERT(Tag == ' GIB'); 02067 PageCount = 1; // We are going to lie! This might screw up accounting? 02068 } 02069 else if (Tag & PROTECTED_POOL) 02070 { 02071 Tag &= ~PROTECTED_POOL; 02072 } 02073 02074 // 02075 // We have our tag and our page count, so we can go ahead and remove this 02076 // tracker now 02077 // 02078 ExpRemovePoolTracker(Tag, PageCount << PAGE_SHIFT, PoolType); 02079 02080 // 02081 // Check if any of the debug flags are enabled 02082 // 02083 if (ExpPoolFlags & (POOL_FLAG_CHECK_TIMERS | 02084 POOL_FLAG_CHECK_WORKERS | 02085 POOL_FLAG_CHECK_RESOURCES | 02086 POOL_FLAG_CHECK_DEADLOCK)) 02087 { 02088 // 02089 // Was deadlock verification also enabled? We can do some extra 02090 // checks at this point 02091 // 02092 if (ExpPoolFlags & POOL_FLAG_CHECK_DEADLOCK) 02093 { 02094 DPRINT1("Verifier not yet supported\n"); 02095 } 02096 02097 // 02098 // FIXME: Many debugging checks go here 02099 // 02100 } 02101 02102 // 02103 // Update counters 02104 // 02105 PoolDesc = PoolVector[PoolType]; 02106 InterlockedIncrement((PLONG)&PoolDesc->RunningDeAllocs); 02107 InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, 02108 -(LONG_PTR)(PageCount << PAGE_SHIFT)); 02109 02110 // 02111 // Do the real free now and update the last counter with the big page count 02112 // 02113 RealPageCount = MiFreePoolPages(P); 02114 ASSERT(RealPageCount == PageCount); 02115 InterlockedExchangeAdd((PLONG)&PoolDesc->TotalBigPages, 02116 -(LONG)RealPageCount); 02117 return; 02118 } 02119 02120 // 02121 // Get the entry for this pool allocation 02122 // The pointer math here may look wrong or confusing, but it is quite right 02123 // 02124 Entry = P; 02125 Entry--; 02126 02127 // 02128 // Get the size of the entry, and it's pool type, then load the descriptor 02129 // for this pool type 02130 // 02131 BlockSize = Entry->BlockSize; 02132 PoolType = (Entry->PoolType - 1) & BASE_POOL_TYPE_MASK; 02133 PoolDesc = PoolVector[PoolType]; 02134 02135 // 02136 // Make sure that the IRQL makes sense 02137 // 02138 ExpCheckPoolIrqlLevel(PoolType, 0, P); 02139 02140 // 02141 // Get the pool tag and get rid of the PROTECTED_POOL flag 02142 // 02143 Tag = Entry->PoolTag; 02144 if (Tag & PROTECTED_POOL) Tag &= ~PROTECTED_POOL; 02145 02146 // 02147 // Stop tracking this allocation 02148 // 02149 ExpRemovePoolTracker(Tag, 02150 BlockSize * POOL_BLOCK_SIZE, 02151 Entry->PoolType - 1); 02152 02153 // 02154 // Is this allocation small enough to have come from a lookaside list? 02155 // 02156 if (BlockSize <= MAXIMUM_PROCESSORS) 02157 { 02158 // 02159 // Try pushing it into the per-CPU lookaside list 02160 // 02161 LookasideList = (PoolType == PagedPool) ? 02162 Prcb->PPPagedLookasideList[BlockSize - 1].P : 02163 Prcb->PPNPagedLookasideList[BlockSize - 1].P; 02164 LookasideList->TotalFrees++; 02165 if (ExQueryDepthSList(&LookasideList->ListHead) < LookasideList->Depth) 02166 { 02167 LookasideList->FreeHits++; 02168 InterlockedPushEntrySList(&LookasideList->ListHead, P); 02169 return; 02170 } 02171 02172 // 02173 // We failed, try to push it into the global lookaside list 02174 // 02175 LookasideList = (PoolType == PagedPool) ? 02176 Prcb->PPPagedLookasideList[BlockSize - 1].L : 02177 Prcb->PPNPagedLookasideList[BlockSize - 1].L; 02178 LookasideList->TotalFrees++; 02179 if (ExQueryDepthSList(&LookasideList->ListHead) < LookasideList->Depth) 02180 { 02181 LookasideList->FreeHits++; 02182 InterlockedPushEntrySList(&LookasideList->ListHead, P); 02183 return; 02184 } 02185 } 02186 02187 // 02188 // Get the pointer to the next entry 02189 // 02190 NextEntry = POOL_BLOCK(Entry, BlockSize); 02191 02192 // 02193 // Update performance counters 02194 // 02195 InterlockedIncrement((PLONG)&PoolDesc->RunningDeAllocs); 02196 InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, -BlockSize * POOL_BLOCK_SIZE); 02197 02198 // 02199 // Acquire the pool lock 02200 // 02201 OldIrql = ExLockPool(PoolDesc); 02202 02203 // 02204 // Check block tag 02205 // 02206 if (TagToFree && TagToFree != Entry->PoolTag) 02207 { 02208 DPRINT1("Freeing pool - invalid tag specified: %.4s != %.4s\n", (char*)&TagToFree, (char*)&Entry->PoolTag); 02209 KeBugCheckEx(BAD_POOL_CALLER, 0x0A, (ULONG_PTR)P, Entry->PoolTag, TagToFree); 02210 } 02211 02212 // 02213 // Check if the next allocation is at the end of the page 02214 // 02215 ExpCheckPoolBlocks(Entry); 02216 if (PAGE_ALIGN(NextEntry) != NextEntry) 02217 { 02218 // 02219 // We may be able to combine the block if it's free 02220 // 02221 if (NextEntry->PoolType == 0) 02222 { 02223 // 02224 // The next block is free, so we'll do a combine 02225 // 02226 Combined = TRUE; 02227 02228 // 02229 // Make sure there's actual data in the block -- anything smaller 02230 // than this means we only have the header, so there's no linked list 02231 // for us to remove 02232 // 02233 if ((NextEntry->BlockSize != 1)) 02234 { 02235 // 02236 // The block is at least big enough to have a linked list, so go 02237 // ahead and remove it 02238 // 02239 ExpCheckPoolLinks(POOL_FREE_BLOCK(NextEntry)); 02240 ExpRemovePoolEntryList(POOL_FREE_BLOCK(NextEntry)); 02241 ExpCheckPoolLinks(ExpDecodePoolLink((POOL_FREE_BLOCK(NextEntry))->Flink)); 02242 ExpCheckPoolLinks(ExpDecodePoolLink((POOL_FREE_BLOCK(NextEntry))->Blink)); 02243 } 02244 02245 // 02246 // Our entry is now combined with the next entry 02247 // 02248 Entry->BlockSize = Entry->BlockSize + NextEntry->BlockSize; 02249 } 02250 } 02251 02252 // 02253 // Now check if there was a previous entry on the same page as us 02254 // 02255 if (Entry->PreviousSize) 02256 { 02257 // 02258 // Great, grab that entry and check if it's free 02259 // 02260 NextEntry = POOL_PREV_BLOCK(Entry); 02261 if (NextEntry->PoolType == 0) 02262 { 02263 // 02264 // It is, so we can do a combine 02265 // 02266 Combined = TRUE; 02267 02268 // 02269 // Make sure there's actual data in the block -- anything smaller 02270 // than this means we only have the header so there's no linked list 02271 // for us to remove 02272 // 02273 if ((NextEntry->BlockSize != 1)) 02274 { 02275 // 02276 // The block is at least big enough to have a linked list, so go 02277 // ahead and remove it 02278 // 02279 ExpCheckPoolLinks(POOL_FREE_BLOCK(NextEntry)); 02280 ExpRemovePoolEntryList(POOL_FREE_BLOCK(NextEntry)); 02281 ExpCheckPoolLinks(ExpDecodePoolLink((POOL_FREE_BLOCK(NextEntry))->Flink)); 02282 ExpCheckPoolLinks(ExpDecodePoolLink((POOL_FREE_BLOCK(NextEntry))->Blink)); 02283 } 02284 02285 // 02286 // Combine our original block (which might've already been combined 02287 // with the next block), into the previous block 02288 // 02289 NextEntry->BlockSize = NextEntry->BlockSize + Entry->BlockSize; 02290 02291 // 02292 // And now we'll work with the previous block instead 02293 // 02294 Entry = NextEntry; 02295 } 02296 } 02297 02298 // 02299 // By now, it may have been possible for our combined blocks to actually 02300 // have made up a full page (if there were only 2-3 allocations on the 02301 // page, they could've all been combined). 02302 // 02303 if ((PAGE_ALIGN(Entry) == Entry) && 02304 (PAGE_ALIGN(POOL_NEXT_BLOCK(Entry)) == POOL_NEXT_BLOCK(Entry))) 02305 { 02306 // 02307 // In this case, release the pool lock, update the performance counter, 02308 // and free the page 02309 // 02310 ExUnlockPool(PoolDesc, OldIrql); 02311 InterlockedExchangeAdd((PLONG)&PoolDesc->TotalPages, -1); 02312 MiFreePoolPages(Entry); 02313 return; 02314 } 02315 02316 // 02317 // Otherwise, we now have a free block (or a combination of 2 or 3) 02318 // 02319 Entry->PoolType = 0; 02320 BlockSize = Entry->BlockSize; 02321 ASSERT(BlockSize != 1); 02322 02323 // 02324 // Check if we actually did combine it with anyone 02325 // 02326 if (Combined) 02327 { 02328 // 02329 // Get the first combined block (either our original to begin with, or 02330 // the one after the original, depending if we combined with the previous) 02331 // 02332 NextEntry = POOL_NEXT_BLOCK(Entry); 02333 02334 // 02335 // As long as the next block isn't on a page boundary, have it point 02336 // back to us 02337 // 02338 if (PAGE_ALIGN(NextEntry) != NextEntry) NextEntry->PreviousSize = BlockSize; 02339 } 02340 02341 // 02342 // Insert this new free block, and release the pool lock 02343 // 02344 ExpInsertPoolHeadList(&PoolDesc->ListHeads[BlockSize - 1], POOL_FREE_BLOCK(Entry)); 02345 ExpCheckPoolLinks(POOL_FREE_BLOCK(Entry)); 02346 ExUnlockPool(PoolDesc, OldIrql); 02347 } 02348 02349 /* 02350 * @implemented 02351 */ 02352 VOID 02353 NTAPI 02354 ExFreePool(PVOID P) 02355 { 02356 // 02357 // Just free without checking for the tag 02358 // 02359 ExFreePoolWithTag(P, 0); 02360 } 02361 02362 /* 02363 * @unimplemented 02364 */ 02365 SIZE_T 02366 NTAPI 02367 ExQueryPoolBlockSize(IN PVOID PoolBlock, 02368 OUT PBOOLEAN QuotaCharged) 02369 { 02370 // 02371 // Not implemented 02372 // 02373 UNIMPLEMENTED; 02374 return FALSE; 02375 } 02376 02377 /* 02378 * @implemented 02379 */ 02380 02381 PVOID 02382 NTAPI 02383 ExAllocatePoolWithQuota(IN POOL_TYPE PoolType, 02384 IN SIZE_T NumberOfBytes) 02385 { 02386 // 02387 // Allocate the pool 02388 // 02389 return ExAllocatePoolWithQuotaTag(PoolType, NumberOfBytes, 'enoN'); 02390 } 02391 02392 /* 02393 * @implemented 02394 */ 02395 PVOID 02396 NTAPI 02397 ExAllocatePoolWithTagPriority(IN POOL_TYPE PoolType, 02398 IN SIZE_T NumberOfBytes, 02399 IN ULONG Tag, 02400 IN EX_POOL_PRIORITY Priority) 02401 { 02402 // 02403 // Allocate the pool 02404 // 02405 UNIMPLEMENTED; 02406 return ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); 02407 } 02408 02409 /* 02410 * @implemented 02411 */ 02412 PVOID 02413 NTAPI 02414 ExAllocatePoolWithQuotaTag(IN POOL_TYPE PoolType, 02415 IN SIZE_T NumberOfBytes, 02416 IN ULONG Tag) 02417 { 02418 // 02419 // Allocate the pool 02420 // 02421 UNIMPLEMENTED; 02422 return ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); 02423 } 02424 02425 /* EOF */ Generated on Sun May 27 2012 04:37:33 for ReactOS by
1.7.6.1
|