ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

expool.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.