A driver can allocate a buffer and a memory object in the following ways:
Allocating the buffer and the memory object at the same time, in a single call to the framework.
Creating a new memory object and associating it with an existing driver-allocated buffer.
A driver can create a new WDF memory object when it creates an I/O request, or the driver can use an existing memory object that it either created earlier or retrieved from an incoming I/O request.
When a driver creates a new memory object, the framework by default sets the driver object as the parent of the memory object. This default is intended for general memory allocations but is not ideal for memory that is used in I/O requests. If your driver creates a new memory object to use in an I/O request, you should set the parent to an object whose lifetime more closely matches that of the memory object. Specifically:
To … | Set this object as parent … |
---|---|
Delete the memory object when the I/O request is completed | I/O request object |
Retain the memory object for later use in additional I/O requests | Device object |
To set the parent for a memory object:
A UMDF driver supplies a pointer to the IWDFObject interface for the parent object when it creates the memory object.
A KMDF driver supplies a value for the ParentObject field of the object attributes structure that it supplies when it creates the memory object.
If your driver uses a memory object that it retrieved from an incoming WDF I/O request object, that I/O request object is the parent of the memory object. Thus, the memory object can persist no longer than the incoming WDF I/O request object. When all drivers have completed the incoming I/O request, the framework deletes both the request object and the memory object.
Tip | If you do not use the appropriate parent for a driver-created memory object and do not explicitly delete the memory object when it is no longer required, it can persist until the driver object is disposed. If the driver uses several memory objects, this can lead to heavy memory usage and possibly slow performance or cause later memory allocations to fail. Furthermore, leak detection tools might not detect this leak because the memory object is eventually freed. |
UMDF and KMDF drivers use different types of buffers because they have access to different memory pools and address spaces:
UMDF drivers can use only the address space of the host process, in which all memory is pageable. UMDF drivers do not require nonpaged memory because all of their code runs at PASSIVE_LEVEL, so paging is always enabled. The Windows I/O manager locks any buffers that a UMDF driver passes to the kernel-mode portion of the device stack.
KMDF drivers can allocate buffers from the system's paged or nonpaged pool. In a synchronous request, the lifetimes of the memory object and the buffer are the same. Therefore, the driver can supply a simple pointer to memory (PVOID) or a pointer to an MDL (PMDL) for the buffer, or the driver can create the buffer at the same time it creates the memory object. In an asynchronous request, the driver must use WDF memory objects so that the framework can ensure that the buffers persist until the I/O request has completed back to the driver that created the request.
A WDF driver can create a memory object and allocate a buffer with a single call to the framework by using one of the following methods:
UMDF IWDFDriver::CreateWdfMemory Creates a memory object and allocates a buffer of a specified size for a UMDF driver.
KMDF WdfMemoryCreate Creates a memory object and allocates a buffer of a specified size for a KMDF driver.
When the framework creates the memory object and allocates the buffer at the same time, the lifetimes of the memory object and the buffer are the same. The framework ensures that the buffer persists until the I/O request has completed back to the driver.
This technique is the easiest to use and requires a minimum of object lifetime management. Most drivers should create memory objects and buffers in this way whenever practical.
By using an existing buffer, a driver can sometimes avoid double-buffering-that is, copying data from an internal driver buffer to the WDF memory object and vice versa. For example, a driver might already have a region in its device context area that contains the data to send in a device I/O control request. Instead of allocating a new buffer along with the new memory object and copying the data to the new buffer, the driver can associate the existing buffer with the new memory object.
The following methods create a new memory object that uses an existing buffer:
UMDF IWDFDriver::CreatePreallocatedWdfMemory Creates a new framework memory object and associates it with an existing buffer for a UMDF driver.
A UMDF driver can later assign a different buffer to the memory object by calling IWDFMemory::SetBuffer on the memory object.
KMDF WdfMemoryCreatePreallocated Creates a new memory object and associates it with an existing buffer for a KMDF driver.
A KMDF driver can later assign a different buffer to the memory object by calling WdfMemoryAssignBuffer.
KMDF WdfMemoryCreateFromLookaside Creates a new memory object and assigns it a buffer from a lookaside list.
Chapter 12, "WDF Support Objects," provides more information on lookaside lists.
When a driver uses an existing buffer in a WDF memory object, the driver must ensure that the buffer persists until the request has been completed back to the driver. When the memory object is deleted, the framework does not free the buffer. Likewise, the framework does not free the previously assigned buffer when the driver assigns a new buffer to the memory object.
Important | The following are the rules for reusing I/O and request objects:
|
After the driver creates the memory object, the driver must call the framework to associate the memory object with the I/O request object and format the request for the I/O target. In response, the framework prepares the underlying IRP and takes out a reference on the memory object on behalf of the I/O target. This reference persists until one of the following occurs:
The I/O request has been completed back to the driver.
The driver reformats the WDF I/O request object.
The WDF I/O request object has been deleted.
A KMDF driver calls WdfRequestReuse in preparation for sending the WDF I/O request object to another I/O target.
Tip | Chapter 24, "Static Driver Verifier," describes how to annotate your driver's callback functions so that SDV can analyze compliance with KMDF rules that specify that after a request is completed, its buffer, MDL, or memory object cannot be accessed. |
The example in Listing 9-5 shows how a UMDF driver can create a new memory object that uses an existing buffer. The code in the example is from the Device.cpp file in the Fx2_Driver sample.
Listing 9-5: Calling CreatePreallocatedWdfMemory in a UMDF driver
HRESULT CMyDevice::SendControlTransferSynchronously( __in PWINUSB_SETUP_PACKET SetupPacket, __inout PBYTE Buffer, __in ULONG BufferLength, __out PULONG LengthTransferred ) { HRESULT hr = S_OK; IWDFIoRequest *pWdfRequest = NULL; IWDFDriver * FxDriver = NULL; IWDFMemory * FxMemory = NULL; IWDFRequestCompletionParams * FxComplParams = NULL; IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL; *LengthTransferred = 0; hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface NULL, //pParentObject &pWdfRequest); if (SUCCEEDED(hr)) { m_FxDevice->GetDriver(&FxDriver); hr = FxDriver->CreatePreallocatedWdfMemory( Buffer, BufferLength, NULL, //pCallbackInterface pWdfRequest, //pParentObject &FxMemory); . . . // Error handling and additional code omitted } return; }
The sample code in Listing 9-5 creates a framework I/O request object and a framework memory object that the driver sends in the request. The driver calls CreatePreallocatedWdfMemory on the driver object to create the memory object and associate it with the buffer that SendControlTransferSynchronously received as a parameter from its caller.
CreatePreallocatedWdfMemory takes four input parameters: a pointer to the buffer, the length of the buffer, a pointer to an IUnknown interface that the framework can query for object cleanup callbacks, and a pointer to the interface for the parent object.
The sample does not implement a callback object for the memory object, so it passes NULL for the IUnknown interface. By specifying pWdfRequest as the fourth parameter, the driver sets the I/O request object as the parent of the newly created memory object. However, neither the memory object nor the I/O request object is the parent of the buffer. The source of the buffer depends upon the caller of SendControlTransferSynchronously. Consequently, the SendControlTransferSynchronously function cannot make assumptions about the ownership or lifetime of the buffer.
Upon return, CreatePreallocatedWdfMemory supplies a pointer to the IWDFMemory interface for the framework memory object.
Listing 9-6 shows how a driver can create a new memory object and a new buffer in a KMDF driver. The sample code is based on the Sys\Bulkrwr.c file in the Usbsamp sample.
Listing 9-6: Creating a memory object in a KMDF driver
WDF_OBJECT_ATTRIBUTES objectAttribs; WDFREQUEST Request; WDFMEMORY urbMemory; PURB urb = NULL; WDF_OBJECT_ATTRIBUTES_INIT(&objectAttribs); objectAttribs.ParentObject = Request; status = WdfMemoryCreate( &objectAttribs, NonPagedPool, POOL_TAG, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), &urbMemory, (PVOID*) &urb);
The Usbsamp driver creates a new memory object to use in an I/O request that it sends to a USB I/O target. It initializes a WDF_OBJECT_ATTRIBUTES structure for the memory object and then sets the ParentObject field of the attributes structure to the I/O request object that will use the memory object. It then calls WdfMemoryCreate to create the memory object. This method has four input parameters: a pointer to the object attributes structure, an enumeration constant that indicates the type of memory to be allocated, a pool tag that identifies the driver and buffer type, and the size of the buffer to allocate. The method returns a handle to the new memory object and a pointer to the newly allocated buffer that is associated with the memory object.