The system-space virtual memory shown in Figure 16.3 consists of a limited amount of paged pool and an even more limited amount of nonpaged pool.
Nonpaged pool is guaranteed to be resident at all times. Consequently, it can be accessed safely while running at any IRQL.
The routine using the corresponding paged-pool virtual addresses must be running at IRQL <= APC_LEVEL. As mentioned in Section 16.1, if a page fault occurs while running at IRQL > APC_LEVEL, it is a fatal error.
Except during their initialization or, possibly, while unloading, NT device and intermediate drivers seldom allocate memory from paged pool because these types of drivers so frequently run at IRQL greater than APC_LEVEL. Any pageable storage that such a driver allocates cannot be accessed safely except by driver-created threads or by the DriverEntry, Reinitialize (if any), and Unload (if any) routines, which can use paged pool allocations to contain data, objects, and resources needed only during initialization or while unloading.
Since several standard NT driver routines run at an IRQL higher than APC_LEVEL, memory allocated from paged pool is inaccessible to most of an NT intermediate or device driver’s routines. For example, higher-level drivers’ IoCompletion routines execute in an arbitrary thread context, usually at DISPATCH_LEVEL IRQL. Such a driver should never allocate pageable storage for data to be accessed from an IoCompletion routine. For more information about the IRQLs at which standard driver routines execute, see Section 16.1.
For specific information about any of the support routines mentioned in this section, see also the Kernel-Mode Driver Reference.
For an I/O buffer space, an NT driver can call MmAllocateNonCachedMemory, MmAllocateContiguousMemory, HalAllocateCommonBuffer if the driver’s device uses busmaster DMA or a system DMA controller’s autoinitialize mode, or ExAllocatePool.
Nonpaged pool tends to become fragmented as the system runs, so an NT driver’s DriverEntry routine should call these routines to set up any long-term I/O buffers the driver needs. Each of these routines, except possibly ExAllocatePool, allocates memory that is aligned on a processor-specific boundary (determined by the processor’s data-cache-line size) to prevent cache and coherency problems.
NT drivers should allocate their internal I/O buffers, if any, as economically as possible because nonpaged pool memory is a limited system resource. In general, a DriverEntry routine should avoid calling these support routines repeatedly to request allocations of less than PAGE_SIZE.
NT drivers also can call ExAllocatePool or ExAllocatePoolWithTag, specifying one of the following system-defined values for the PoolType parameter:
Note that such a driver could call MmAllocateNonCachedMemory or MmAllocateContiguousMemory instead.
For this PoolType value, ExAllocatePool or ExAllocatePoolWithTag allocates exactly the amount of memory requested if the specified NumberOfBytes is less than or equal to PAGE_SIZE. Otherwise, any remainder bytes on the last-allocated page are wasted: inaccessible to the caller and unusable by other kernel-mode code.
For example, on an x86, an allocation request of 5K returns two 4K pages. The last 3K of the second page is unavailable to the caller or another caller. To avoid wasting nonpaged pool, a driver writer should design the driver to use a multipage allocation efficiently, such as making two allocations,, one for PAGE_SIZE and the other for 1K to allocate a total of 5K.
An NT driver’s Unload routine also can allocate memory from paged pool if the buffer will be released before the Unload routine returns control.
Because the must-succeed pool is a very limited system resource, allocations must be released by calling ExFreePool as soon as possible. Most NT drivers should not call ExAllocatePool or ExAllocatePoolWithTag with the PoolType values NonPagedPoolMustSucceed or NonPagedPoolCacheAlignedMustS, unless the system cannot continue to run if the driver’s allocation request does not succeed. For these PoolType specifications, ExAllocatePool brings down the system if it cannot allocate the requested memory.
For all other PoolType specifications, ExAllocatePool or ExAllocatePoolWithTag returns a NULL pointer if it cannot allocate the requested NumberOfBytes. NT drivers should always check the pointer returned by ExAllocatePool or ExAllocatePoolWithTag. If its value is NULL, the DriverEntry routine (or any other driver routine that returns NTSTATUS) should return STATUS_INSUFFICIENT_RESOURCES or handle the error condition if possible. For more information about handling I/O errors, see Section 16.6.
For the CacheAligned PoolType specifications, ExAllocatePool or ExAllocatePoolWithTag allocates memory that is aligned on a processor-specific boundary (determined by the processor’s data-cache-line size) to prevent cache and coherency problems.