ENTERPRISE

Windows 7 : Programming Multiple I/O Queues and Programming I/O - Creating and Configuring the Queues (part 1)

7/17/2014 4:39:19 AM

1. Introduction to Programming I/O Queues

Drivers have several options when creating and configuring I/O queues. The most important are which I/O requests to queue, what type of dispatching to use, and whether to let KMDF manage the queue. A device object can have any number of I/O queues, and each can be configured differently.

One queue for each device object can be configured as a default queue, into which KMDF places requests for which the driver has not specifically configured any other queue. If the device object has a default queue and one or more other queues, KMDF queues specific requests to the correspondingly configured queues and queues all other requests to the default queue. If the device object does not have a default queue, KMDF queues only the specific request types to the configured queues and, for a function or bus driver, fails all other requests. (For a filter driver, it passes all other requests to the next lower driver.)

A queue’s dispatch type determines when KMDF presents I/O requests from the queue to the driver. KMDF supports three dispatch types:

  • Sequential

  • Parallel

  • Manual

KMDF presents requests from a sequential queue to the driver one at a time. When the first request arrives, KMDF calls the event callback that was specified for the request type and queue. After the driver completes the request, KMDF calls the event callback for another request if the queue is not empty. A sequential queue is thus used for synchronous I/O. By default, queues are configured as sequential (WdfIoQueueDispatchSequential).

For a parallel queue, KMDF presents requests to the driver as soon as they arrive. KMDF does not wait for any “inflight” requests to complete before sending another request. An inflight request is an I/O request that is currently active—that is, it is not in a queue and has not been completed. A parallel queue can thus be used for asynchronous I/O.

For a manual queue, KMDF does not present requests to the driver. Instead, the driver must call WdfIoQueueRetrieveRequest when it is ready to handle a request. A driver can thus use manual queuing for either synchronous or asynchronous I/O.

A driver can temporarily stop the delivery of requests from a sequential or parallel queue by calling WdfIoQueueStop or WdfIoQueueStopSynchronously, depending on the type of queue and the reasons for pausing delivery. To restart the queue, the driver calls WdfIoQueueStart.

By default, KMDF handles I/O cancellation for queued I/O requests. Consequently, if the user cancels an I/O request after KMDF has queued it but before KMDF has delivered it to the driver, KMDF removes it from the queue and completes it with STATUS_CANCELED. The driver can request notification by registering an EvtIoCanceledOnQueue callback for the queue; otherwise, KMDF cancels the request without notifying the driver.

After a request has been dispatched to the driver, it cannot be canceled unless the driver specifically marks it as cancelable and registers an EvtRequestCancel callback. If the driver forwards the request to another queue, it immediately becomes cancelable again.

Also by default, KMDF handles power management for I/O queues, and each I/O queue inherits the power state of its associated device. During Plug and Play or power state transitions and any time the device is not in the working state, KMDF queues incoming I/O requests but does not dispatch them to the driver. Therefore, if the driver creates its queues before the device enters DO, the queues are in the WDF_IO_QUEUE_STOPPED state, and KMDF queues any I/O requests targeted at the device. When the device enters the working state, KMDF resumes presenting requests. A driver can change this default when it configures each queue by setting the PowerManaged field of the configuration structure to FALSE.

A driver can also specify whether a queue accepts I/O requests that have a zero-length buffer. By default, KMDF does not dispatch such requests to the driver; instead, it completes them with STATUS_SUCCESS.

2. Creating and Configuring the Queues

To create and configure a queue, a driver takes the following steps:

1.
Defines a WDF_IO_QUEUE_CONFIG structure to hold configuration settings for the queue.

2.
Initializes the configuration structure by calling the WDF_IO_QUEUE_CONFIG_INIT or WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE function (for a default queue). These functions take a pointer to the configuration structure and an enumerator that defines the dispatching type for the queue.

3.
Sets the event callbacks for this queue in the WDF_IO_QUEUE_CONFIG structure, if the queue uses sequential or parallel dispatching. A driver can set callbacks for one or more of the following I/O events: EvtIoRead, EvtIoWrite, EvtIoDeviceIoControl, EvtIoInternalDeviceIoControl, EvtIoDefault, EvtIoStop, EvtIoResume, and EvtIoCanceledOnQueue.

4.
Sets Boolean values for the PowerManaged and AllowZero-LengthRequests fields in the queue configuration structure if the default values are not suitable.

5.
Creates the queue by calling WdfIoQueueCreate, passing a handle to the WDFDEVICE object, a pointer to the filled-in WDF_IO_QUEUE_CONFIG structure, a pointer to a WDF_OBJECT_ATTRIBUTES structure, and a pointer to receive a handle to the created queue instance.

6.
Specifies which I/O requests KMDF should direct to the queue by calling WdfDeviceConfigureRequestDispatching.

The PCIDRV sample creates two parallel queues and three manual queues, which are used as follows:

  • For write requests, the driver creates a parallel queue. If a write request cannot be satisfied immediately, the driver puts the request into an internal manual queue. (The queue is considered internal because only the driver, and not KMDF, adds requests to it.)

  • For read requests, the driver creates a manual queue.

  • For IOCTL requests, the driver creates a parallel queue. If the IOCTL cannot be satisfied immediately, the driver puts the request into an internal manual queue.

In this driver, each I/O queue is configured for a particular type of I/O request. Therefore, the driver does not create a default queue.

The code to create all the queues is excerpted from the NICAllocateSoftwareResources function (in the file cidrv\sys\hw\Nic_init.c), which is called from the driver’s EvtDriverDeviceAdd callback.

2.1. Code to Create Queues for Write Requests

The following excerpt shows how the PCIDRV sample creates a parallel queue for incoming write requests. While requests are in this queue, KMDF handles cancellation without notifying the driver.

NTSTATUS                 status;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
WDF_OBJECT_ATTRIBUTES attributes;

......

WDF_IO_QUEUE_CONFIG_INIT (
&ioQueueConfig,
WdfIoQueueDispatchParallel
);

ioQueueConfig.EvtIoWrite = PciDrvEvtIoWrite;

status = WdfIoQueueCreate (
FdoData->WdfDevice,
&ioQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&FdoData->WriteQueue // queue handle
);

if (!NT_SUCCESS (status))
{
return status;
}

status = WdfDeviceconfigureRequestDispatching (
FdoData->WdfDevice,
FdoData->WriteQueue,
WdfRequestTypeWrite );

if (!NT_SUCCESS (status))
{
ASSERT (NT_SUCCESS (status));
return status;
}


The driver calls WDF_IO_QUEUE_CONFIG_INIT to initialize the queue as a parallel queue. The queue holds only write requests, so the driver sets only an EvtIoWrite callback in the ioQueueConfig structure. It creates the queue, and then calls WdfDeviceConfigureRequestDispatching to configure the queue for requests of type WdfRequestTypeWrite only. All other I/O requests are directed to a queue that is configured for them or, if the driver has not configured a queue for them, are handled by the framework without being sent to the driver.

The PCIDRV sample also creates a manual queue into which to place pending write requests. The driver’s EvtIoWrite callback places an I/O request in this internal queue when it cannot handle the request immediately.

The driver creates this queue as follows:

WDF_IO_QUEUE_CONFIG_INIT (
&ioQueueConfig,
WdfIoQueueDispatchManual
);

status = WdfIoQueueCreate (
FdoData->WdfDevice,
&ioQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&FdoData->PndingWriteQueue
);
if (!NT_SUCCESS (status))
{
return status;
}

To create the manual internal queue for its pending write requests, the driver configures a WDF_IO_QUEUE_CONFIG structure, this time setting the queue type to WdfIoDispatchManual, which indicates that the driver requests items from the queue when it is ready. It then creates the queue by calling WdfIoQueueCreate. The driver does not configure the queue for any particular type of I/O request because the driver itself determines which request to queue. KMDF does not put any request in this queue because the driver did not call WdfDeviceconfigureRequestDispatching. However, KMDF handles power management and request cancellation.

Other  
  •  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
  •  Microsoft Exchange Server 2010 : Working with Active Mailbox Databases (part 2) - Setting Mailbox Database Limits and Deletion retention
  •  Microsoft Exchange Server 2010 : Working with Active Mailbox Databases (part 1) - Creating Mailbox Databases
  •  Microsoft Exchange Server 2010 : Managing Public Folder Settings (part 3) - Manipulating, Renaming, and Recovering Public Folders
  •  
    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