A thread in NTDEV yesterday and today brought to light an interesting misunderstanding:
http://www.osronline.com/showthread.cfm?link=158854
While it may not be obvious, MmGetSystemAddressForMdlSafe potentially has a side effect that must be undone. Unfortunately, the documentation makes no mention of this and so I can see why there is room for some confusion.
Turns out that this driver developer interface (DDI) is actually a macro, so we can see its inner workings:
#define MmGetSystemAddressForMdlSafe(MDL, PRIORITY)
(((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |
MDL_SOURCE_IS_NONPAGED_POOL)) ?
((MDL)->MappedSystemVa) :
(MmMapLockedPagesSpecifyCache((MDL),
KernelMode,
MmCached,
NULL,
FALSE,
(PRIORITY))))
The first thing we learn here is that this routine doesn’t do much if either the MDL_MAPPED_TO_SYSTEM_VA or MDL_SOURCE_IS_NONPAGED_POOL bits are set.
MDL_MAPPED_TO_SYSTEM_VA we’ll talk about in a moment, but MDL_SOURCE_IS_NONPAGED_POOL means that this MDL was built with MmBuildMdlForNonPagedPool. In that case, the MDL already has a non-paged system address for this MDL, so MmGetSystemAddressForMdlSafe can just be effectively a NOP and return the non-paged pool address passed to MmBuildMdlForNonPagedPool.
If neither of these bits are set, then the macro makes a call to MmMapLockedPagesSpecifyCache to build a kernel mode virtual address to the MDL. This routine will map the MDL with a contiguous kernel virtual address range via system page table entries (PTEs). The base of the address range will be put in to the MappedSystemVa field of the MDL and the MDL_MAPPED_TO_SYSTEM_VA bit will be set in the MDL. Thus, the next time a call to MmGetSystemAddressForMdlSafe is made on the MDL the operation will be a NOP.
If you read the docs for MmMapLockedPagesSpecifyCache, you’ll see that this mapping needs to be torn down with a call to MmUnmapLockedPages. Thus, any MDL with the MDL_MAPPED_TO_SYSTEM_VA bit set must be unmapped before the MDL is freed.
However, what’s interesting is that you won’t actually find many drivers with calls to MmUnmapLockedPages with pointers returned from MmGetSystemAddressForMdlSafe. Why is that? Well, it’s actually a two part answer:
1) MmUnlockPages will automatically unmap the MDL for you if it sees that the MDL_MAPPED_TO_SYSTEM_VA bit is set:
2) The I/O manager calls MmUnlockPages on the MDL in Irp->MdlAddress as part of I/O completion
So, as driver devs we generally don’t have to worry about this as it’s done for us automatically behind the scenes.
