16.4.1.3 Allocating System-Space Memory
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.
For NT drivers, paged pool can be allocated and accessed only under the following condition:
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.
Allocating Driver Buffer Space
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.
In particular, driver writers should consider the following facts in order to allocate I/O buffer memory economically:
·Each call to MmAllocateNonCachedMemory ties up at least a full page of nonpaged system-space memory, whatever the size of the requested allocation. For requests less than a page, any remainder bytes on the page are wasted: inaccessible by the driver that called MmAllocateNonCachedMemory and unusable by other kernel-mode code.
·Each call to MmAllocateContiguousMemory allocates up to a page if the specified number of bytes is less than or equal to a page. For requests greater than a page, any remainder bytes on the last-allocated page are wasted: inaccessible to the driver that called MmAllocateContiguousMemory and unusable by other kernel-mode code.
·Each call to HalAllocateCommonBuffer uses at least one adapter object map register, which maps at least one byte and at most one page. For more information about map registers and using common buffers, see the section on adapter objects in Chapter 3.
Allocating Memory with ExAllocatePool or ExAllocatePoolWithTag
NT drivers also can call ExAllocatePool or ExAllocatePoolWithTag, specifying one of the following system-defined values for the PoolType parameter:
·NonPagedPoolCacheAligned for a permanent I/O buffer the driver uses, such as a SCSI class driver’s buffer for request-sense data
Note that such a driver could call MmAllocateNonCachedMemory or MmAllocateContiguousMemory instead.
·NonPagedPoolCacheAlignedMustS for a temporary, but critically important, I/O buffer, such as a buffer containing initialization data for a physical device that must be present for the system to boot
·NonPagedPool for any objects or resources not stored in a device extension or controller extension that the driver might access while running at IRQL > APC_LEVEL
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.
·NonPagedPoolMustSucceed for a temporary, but critically important, storage area, that the driver will release as soon as possible, such as memory that a driver needs to correct an error condition because it would corrupt the system otherwise
·PagedPoolCacheAligned for a file system driver’s I/O buffer that it locks down and passes in an IRP requesting a DMA transfer by an underlying mass-storage device driver
·PagedPool for a temporary buffer, used by the DriverEntry or Reinitialize routine to contain objects, data, or resources necessary for initialization, if the buffer will be released before the caller returns, or for a storage area that will be accessed only by one or more driver-created threads
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.