ENTERPRISE

Windows 7 : Programming Multiple I/O Queues and Programming I/O - Retrieving Requests from a Manual Queue

7/17/2014 4:41:26 AM

1. Forwarding Requests to a Queue

In some situations, a driver must requeue requests on its own, after it has received them from KMDF. For example, a driver might be able to respond to some device I/O control requests immediately, but might have to handle others at a later time. Because the requests are all of the same type (WdfRequestTypeDeviceControl), KMDF cannot deliver some to one queue and some to another. Instead, the driver must sort the requests as KMDF delivers them and place any that it cannot satisfy immediately into a manual, internal queue to handle later.

To forward a request to a queue, a KMDF driver calls WdfRequestForwardToIoQueue, passing as parameters the handle to the request and the handle to the queue. If the request is added successfully, KMDF returns the status STATUS_SUCCESS. A driver cannot return a request to the queue from which the driver most recently received it.

The PCIDRV sample uses this technique to delay processing IOCTL_NDISPROT_INDICATE_STATUS requests, as the following excerpt from its EvtIoDeviceControl callback (in pcidrv.c) shows:

case IOCTL_NDISPROT_INDICATE_STATUS:
status = WdfRequestForwardToIoQueue (Request,
fdoData->PendingIoctlQueue);
ASSERT (status == STATUS_WDF_FORWARDED);
break;

2. Retrieving Requests from a Manual Queue

When the driver is ready to handle a request from a manual queue, it calls a method on the queue object to retrieve one. A KMDF driver can

  • Retrieve the next request from the queue.

  • Retrieve the oldest request in the queue that pertains to a particular file object.

  • Search the queue until it finds a particular request and then retrieve that request.

To remove the next item from a manual queue, a driver calls WdfIoQueueRetrieveNextRequest with a handle to the queue and a pointer to a location to receive the handle to the request.

To remove the oldest request that specifies a particular file object, a driver calls WdfIoQueueRetrieveRequestByFileObject. The driver passes a handle to the file object along with the handle to the queue and a pointer to a location to receive the handle to the request. This method updates an internal queue pointer, so that the next time the driver calls it, it returns the next-oldest item, and so forth.

To search the queue for a particular request, the driver calls WdfIoQueueFindRequest. This method returns a handle to the request but does not remove the request from the queue. The driver can inspect the request to determine whether it is the one that the driver was seeking. If not, the request stays in the queue and the driver can search again. If so, the driver can dequeue the request by calling WdfIoQueueRetrieveFoundRequest.

After the driver has removed a request from the queue, the driver “owns” that request. The driver must complete the request, forward it to another driver, or forward it to a different queue.

2.1. Code to Find a Request

The following function shows how the PCIDRV sample searches its manual device I/O control queue for a request with a particular function code and then retrieves that request. (The code is from the source file pcidrv\sys\hw\nic_req.c, and it has been slightly abridged.)

NICGETIoctlRequest (
IN WDFQUEUE Queue,
IN ULONG FunctionCode,
OUT WDFREQUEST* Request
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL
WDF_REQUEST_PARAMETERS params;
WDFREQUEST tagRequest;
WDFREQUEST prevTagRequest;

WDF_REQUEST_PARAMETER_INIT (&params);

*Request = NULL;
prevTagRequest = tagRequest = NULL;

do
{
WDF_REQUEST_PARAMETERS_INIT (&params);
status = WdfIoQueueFindRequest (Queue,
prevTagRequest,
NULL,
&params,
&tagRequest);
// WdfIoQueueFindRequest takes an extra reference on
// the returned tagRequest to prevent the memory
// being freed. However, the tagRequest is still
// in the queue and can be canceled or removed by
// another thread and completed.
//

if (prevTagRequest)
{
WdfObjectDereference (prevTagRequest);
}

if (status == STATUS_NO_MORE_ENTRIES)
{
status = STATUS_UNSUCCESSFUL;
break;
}

if (status == STATUS_NOT_FOUND)
{
//
// The prevTagRequest disappeared from the
// queue for some reason – either it was
// canceled or dispatched to the driver. There
// might be other requests that match our
// criteria so restart the search.
//
prevTagRequest = tagRequest = NULL;
continue;
}

if (!NT_SUCCESS (status ))
{
status = STATUS_UNSUCCESSFUL;
break;
}

if (FunctionCode ==
params.Parameters.DeviceIoControl.IoControlCode)
{
status = WdfIoQueueRetrieveFoundRequest (
Queue,
tagRequest, // TagRequest
Request
);
WdfObjectDereference (tagRequest);

if (status == STATUS_NOT_FOUND)
{
//
// The TagRequest disappeared
// for some reason – either it was
// canceled or dispatched to the driver.
// Other requests might match our
// criteria so restart the search.
//
prevTagRequest = tagRequest = NULL;
continue;
}

if (!NT_SUCCESS (status))
{
status = STATUS_UNSUCCESSFUL;
break;
}

//
// Found a request. Drop the extra reference
// before returning.
//
ASSERT (*Request == tagRequest);
status = STATUS_SUCCESS;
break;
}
else
{
//
// This is not the request we need. Drop the
// reference on the tagRequest after looking for
// the next request.
prevTagRequest = tagRequest;
continue;
}
} WHILE (TRUE);
return status;
}


The sample driver starts by calling WDF_REQUEST_PARAMETERS_INIT to initialize a WDF_REQUEST_PARAMETERS structure. Later, when the driver calls WdfIoQueueFindRequest, KMDF returns the parameters for the request in this structure.

Next, the driver initializes the variables that it uses to keep track of the requests it has searched through. The variable prevTagRequest holds a handle to the previous request that the driver inspected, and tagRequest holds a handle to the current request. The driver initializes both values to NULL before it starts searching.

The search is conducted in a loop. Each time NICGetIoctlRequest calls WdfIoQueueFindRequest, it passes prevTagRequest to indicate where KMDF should start searching and passes a pointer to tagRequest to receive the handle to the current request. WdfIoQueueFindRequest also takes a handle to the queue, a handle to the related file object, and a pointer to the initialized WDF_REQUEST_PARAMETERS structure. The PCIDRV sample does not use file objects, so it passes NULL for the file object handle. Note that the driver reinitializes the WDF_REQUEST_PARAMETERS structure before each call, thus ensuring that it does not receive old data.

On the first iteration of the loop, preTagRequest is NULL. Therefore, the search starts at the beginning of the queue. WdfIoQueueFindRequest searches the queue and returns the request’s parameters (in the Params variable) and a handle to the request (in tagRequest). To prevent another component from deleting the request while the driver inspects it, WdfIoQueueFindRequest takes out a reference on request.

NICGetIoctlRequest compares the function code value that was returned in the request’s parameters structure with the function code that the caller passed in. If the codes match, the driver calls WdfIoQueueRetrieveFoundRequest to dequeue the request. WdfIoQueueRetrieveFoundRequest takes three parameters: the handle to the queue, the handle returned by WdfIoQueueFindRequest that indicates which request to dequeue, and pointer to a location that will receive a handle to the dequeued request.

When WdfIoQueueRetrieveFoundRequest returns successfully, the driver “owns” the retrieved request. It deletes extra reference previously taken on the request by calling WdfObjectDeference. It then exits from the loop, and the NICGetIoctlRequest function returns a handle to the retrieved request. The caller can then perform the I/O operations that are required to satisfy the request.

If the function codes do not match, the driver sets prevTagRequest to tagRequest so that the search starts at the current location in the queue. However, the driver does not yet dereference the request object that prevTagRequest represents. It must maintain this reference until WdfIoQueueFindRequest has returned on the next iteration of the loop. The loop then executes again. This time, if WdfIoQueueFindRequest successfully finds a request, the driver deletes the reference that WdfIoQueueFindRequest acquired for the prevTagRequest and then compares the function codes as it did in the previous iteration.

If the request is no longer in the queue, WdfIoQueueFindRequest returns STATUS_NOT_FOUND. For example, the request might not be in the queue if it was canceled or was already retrieved by another thread. WdfIoQueueRetrieveFoundRequest can also return this same status if the handle passed in tagRequest is not valid. If either of these errors occurs, the driver restarts the search at the beginning. If either of these methods fails for any other reason, such as exhausting the queue, the driver exits from the loop.

Other  
  •  Windows 7 : Programming Multiple I/O Queues and Programming I/O - Handling Requests from a Parallel Queue
  •  Windows 7 : Programming Multiple I/O Queues and Programming I/O - Creating and Configuring the Queues (part 2)
  •  Windows 7 : Programming Multiple I/O Queues and Programming I/O - Creating and Configuring the Queues (part 1)
  •  Microsoft Exchange Server 2010 : Managing Mailbox and public Folder Databases (part 2) - Moving Databases
  •  Microsoft Exchange Server 2010 : Managing Mailbox and public Folder Databases (part 1) - Mounting and Dismounting Databases
  •  Microsoft Exchange Server 2010 : Using Public Folder Databases (part 4) - Configuring Public Folder Referrals, Recovering Deleted Items from Public Folder Databases
  •  Microsoft Exchange Server 2010 : Using Public Folder Databases (part 3) - Configuring Public Folder Replication
  •  Microsoft Exchange Server 2010 : Using Public Folder Databases (part 2) - Setting Public Folder Database Limits
  •  Microsoft Exchange Server 2010 : Using Public Folder Databases (part 1) - Creating Public Folder Databases
  •  Microsoft Exchange Server 2010 : Working with Active Mailbox Databases (part 3) - Recovering Deleted Mailboxes , Recovering Deleted Items from Mailbox Databases
  •  
    Top 10
    Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
    Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
    3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
    3 Tips for Maintaining Your Cell Phone Battery (part 1) - Charge Smart
    OPEL MERIVA : Making a grand entrance
    FORD MONDEO 2.0 ECOBOOST : Modern Mondeo
    BMW 650i COUPE : Sexy retooling of BMW's 6-series
    BMW 120d; M135i - Finely tuned
    PHP Tutorials : Storing Images in MySQL with PHP (part 2) - Creating the HTML, Inserting the Image into MySQL
    PHP Tutorials : Storing Images in MySQL with PHP (part 1) - Why store binary files in MySQL using PHP?
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    VIDEO TUTORIAL
    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
    Popular Tags
    Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS