What does MmProbeAndLockPages actually lock?

Drivers sometimes need to lock pages so they stay in memory during certain operations, such as copying data from a device to the data buffer in a DPC routine or performing DMA to the buffer. The MmProbeAndLockPages routine makes a specified memory range resident (if it isn't already), confirms that the pages permit the specified operation at the specified access mode, and locks the pages in memory so that they cannot be paged out.

MmProbeAndLockPages does this by incrementing the reference count on the page frame number (PFN) entries in the page frame number database, which describes the state of each page in physical memory. As long as the PFN reference count is non-zero, the physical page will not be reused.

However, it's important not to confuse the physical pages with their virtual addresses. MmProbeAndLockPages locks physical pages in memory, but their virtual addresses can become invalid if the relevant working set is trimmed (or if the process frees or unmaps the address range). Even though the backing physical pages are guaranteed to remain resident, the virtual address mappings are not. One symptom of this in your driver is that MmIsAddressValid will indicate a page fault for the virtual address, suggesting that the memory has been paged out. If the driver attempts to touch the virtual address (assuming the address range hasn't been deleted) it encounters the same issues as when it attempts to touch pageable memory: If the driver is running below DISPATCH_LEVEL, the system can service a page fault; if the driver is running at or above DISPATCH_LEVEL, the system will bugcheck. (For an in-depth discussion of how Windows manages virtual and physical memory, see Chapter Seven of Inside Windows 2000, Third Edition.)

If a virtual address is needed at or above DISPATCH_LEVEL, or in another process context, the driver must map the MDL using MmGetSystemAddressForMdlSafe to obtain a non-trimmable system virtual address for the MDL. This virtual address is never inserted in the working set and so it cannot be removed—it is guaranteed to work regardless of the process context of the thread or the IRQL at which the address is referenced.

What should you do?

  • Call MmProbeAndLockPages to lock pages described by an MDL in memory. Always perform this operation in a try/except block, because MmProbeAndLockPages will raise an exception if it fails.
  • To map the locked physical pages to system space, call MmGetSystemAddressForMdlSafe with the pointer to the MDL. (Use MmGetSystemAddressForMdl for drivers that must run on Windows 98.) To avoid wasting system resources, do this only if you really need to access the pages using a virtual address.
  • Use the system address returned by MmGetSystemAddressForMdlSafe to access the locked pages by virtual address.
  • When you no longer need the pages described by the MDL, call MmUnlockPages to unlock them and then call IoFreeMdl to release them.

The following code fragment from sioctl.c file in the IOCTL driver sample shows how to do this:

mdl = IoAllocateMdl(inBuf, inBufLength,  FALSE, TRUE, NULL);
        if(!mdl)
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        try 
        {            
            //
            // Probe and lock the pages of this buffer in physical memory.
            // You can specify IoReadAccess, IoWriteAccess or IoModifyAccess
            // Always perform this operation in a try except block. 
            //  MmProbeAndLockPages will raise an exception if it fails.
            //
            MmProbeAndLockPages(mdl, UserMode, IoReadAccess);
        }
        except(EXCEPTION_EXECUTE_HANDLER) 
        {
            ntStatus = GetExceptionCode();
            SIOCTL_KDPRINT((
                "Exception while locking inBuf 0X%08X in METHOD_NEITHER\n",
                    ntStatus));
            IoFreeMdl(mdl);
            break;
        }
        //         
        // Map the physical pages described by the MDL into system space. 
        // Note: double mapping the buffer this way causes lot of 
        // system overhead for large size buffers. 
        // 
        buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority );
        if(!buffer) {
                ntStatus = STATUS_INSUFFICIENT_RESOURCES;
                MmUnlockPages(mdl);
                IoFreeMdl(mdl);
                break;           
        }
        // 
        // Now you can safely read the data from the buffer.
        //
        SIOCTL_KDPRINT(("\tData from User (SystemAddress) : "));
        PrintChars(buffer, inBufLength);
        //
        // Once the read is over unmap and unlock the pages.
        //
        MmUnlockPages(mdl);
        IoFreeMdl(mdl);

MmProbeAndLockPages

MmGetSystemAddressForMdlSafe

IOCTL driver sample

 

 

Send comments about this topic to Microsoft