When WDF Drivers Can Use Power-Managed I/O Queues

With Windows Driver Foundation (WDF), drivers must have their read, write, and device I/O control requests handled by a WDF queue object. The queue object receives requests from the system and dispatches them to the driver's callback function. Other types of requests, such as Plug and Play and power management requests, can be handled directly by callback functions.

Queue objects can be either power managed or not power managed. When a device leaves its working state (D0):

  • A queue object that is power-managed continues to queue requests, but waits until the device reenters D0 before dispatching the requests to the driver.

  • A queue object that is not power-managed continues to dispatch requests just as it does when the device is in D0.

A key issue for power-managed queues is handling requests for idled devices—where the system is still in the working state (S0) but the device has been powered down to one of its low-power states (Dx). The way in which power-managed queues handle incoming requests for idled devices depends on whether the driver is the stack's power policy owner (PPO).

  • PPO

    The PPO manages power policy for a device stack. For typical device stacks, the function driver serves as the PPO. However, for stacks that are based on a raw physical device object (PDO), the bus driver serves as the PPO. Power-managed queues for PPOs support power-up logic for idle devices. If the device is idle when an I/O request arrives, the queue's power-up logic initiates the process of returning the device to D0 so that the queue can dispatch the request.

    By default, queue objects for PPOs are power managed.

  • Non-PPO

    Filter drivers rarely serve as the PPO, nor do bus drivers unless the stack is based on a raw PDO. Power-managed queues for non-PPOs do not support power-up logic. If the device is idle when an I/O request arrives, the queue object queues the request but does not initiate the wake process.

    By default, queue objects for non-PPOs are not power managed. To use power-managed queues, non-PPOs must explicitly enable power management when creating the queue object.

You configure a queue as power managed when you create the queue, typically in the EvtDriverDeviceAdd callback for a kernel-mode driver framework (KMDF) driver or in the IDriverEntry::OnDeviceAdd callback for a user-mode driver framework (UMDF) driver.

The following example shows how to create a manual power-managed queue for a KMDF filter driver.

NTSTATUS MyDriver_EvtDriverDeviceAdd(
    __in WDFDRIVER        Driver,
    __in PWDFDEVICE_INIT  DeviceInit
    )
{
    WDFDEVICE                           device;
    WDF_IO_QUEUE_CONFIG                 ioQueueConfig;
    PDEVICE_CONTEXT                     pDevContext;
    WDFQUEUE                            queue;
    ...
    WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,
                             WdfIoQueueDispatchManual);
    ioQueueConfig.PowerManaged = WdfTrue;
    status = WdfIoQueueCreate(device,
                              &ioQueueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &pDevContext->InterruptMsgQueue
                              );
    ...
}

UMDF drivers configure a queue as power managed by setting bPowerManaged to TRUE when they call IWDFDevice::CreateIoQueue to create the queue object.

Power-managed queues can greatly simplify driver implementation when they are used in the proper configuration, but they are not suitable for all drivers. Each device stack has one driver that is designated as the PPO. This driver manages the device's power policy and controls the device's power state. Any driver in the stack can be the PPO, but the function driver usually handles this task. If a driver that is above the PPO in the stack uses a power-managed queue, that driver can create a deadlock and stall the stack.

The problem arises when a device is idle and powered down. In that case, the entire device stack is powered off, but the system is still in S0 and can send I/O requests to the device. When the system sends an I/O request, the device stack should wake the device so that it can process the request. This desired behavior does not occur if a driver above the PPO implements a power-managed queue. As an example, consider a simple stack that consists of an upper filter (Driver A) followed by a function driver (Driver B). Driver B is the stack's PPO.

Consider two power-management scenarios for this stack:

  • Scenario 1: Driver A uses a queue that is not power managed to handle all I/O requests.

  • Scenario 2: Driver A uses a queue that is power managed to handle all read/write requests and a default queue that is not power managed to handle all other I/O requests.

For both scenarios, Driver B uses power-managed queues to handle all requests.

Figure 1 shows how I/O requests are handled in scenario 1.

Figure 1: Scenario 1

All read, write, and device I/O control requests are handled in the same way, as follows:

  1. The device is initially powered down and in a Dx state.

  2. The system sends an I/O request to the device.

  3. Driver A's I/O queue object receives the request.

  4. The queue object dispatches the request to Driver A.

  5. Driver A processes the request and then sends the request to its I/O target—Driver B.

  6. Driver B's queue object receives the request and initiates the wake process (details not shown here).

  7. After the device is awake, Driver B's queue object dispatches the request to Driver B, which sends the request to the now-awakened device.

Scenario 1 demonstrates the correct way for drivers to use power-managed queues. The request is handled promptly, and the user should experience no perceptible delay except perhaps for the amount of time that is required for the device to wake.

Figure 2 shows how a read request, followed by a device I/O control request, would be handled in scenario 2.

Figure 2: Scenario 2

In this case, the read request is handled differently from the device I/O control request, as follows:

  1. The device is initially powered down and in a Dx state.

  2. The system sends a read request to the device.

  3. Driver A's power-managed read/write queue object receives the request. Because Driver A is not the PPO, the queue object cannot initiate the wake process. Instead, the queue object pends the request until it is notified that the device has reentered D0, which could take a substantial amount of time.

  4. After some time has passed, the system sends a device I/O control request that Driver A's default queue receives.

  5. The default queue is not power managed, so it dispatches the request to Driver A, which processes the request and sends it to Driver B.

  6. Driver B's power-managed queue object initiates the wake process and then sends the request to the awakened device.

  7. Driver A's read/write queue object receives a power-up event and dispatches the read request to Driver A, which processes and sends it to Driver B, and so on.

The reason that Driver A should not use a power-managed queue can be seen in step 3. The stack deadlocks at that point and remains in that state until a request arrives—a device I/O control request in this example—that is not handled by a power-managed queue. Depending on how the drivers are implemented and the types and frequencies of incoming requests, Driver A's power-managed read/write queue could stall the stack for a considerable amount of time, perhaps indefinitely.

Power-Managed Queues and UMDF Drivers

UMDF drivers can explicitly designate themselves as the PPO by calling IWDFDeviceInitialize::SetPowerPolicyOwnership.

The PPO for a device stack that contains UMDF drivers could be one of the lower kernel-mode drivers. If so, all UMDF drivers in the stack are above the PPO and could stall the stack if they use a power-managed queue.

UMDF USB drivers are a good example of a driver that can stall the stack by using power-managed queues. By default, the underlying kernel-mode WinUSB.sys driver is the PPO for the USB device stack. Therefore, UMDF USB drivers by default are above the PPO in the stack.

A UMDF USB driver can either rely on WinUSB for selective suspend support or can claim power policy ownership and use the UMDF support for selective suspend that is available beginning with WDF 1.9. Both approaches require only small amounts of driver code. If the UMDF USB driver relies on WinUSB for selective suspend support, it should not use power-managed queues. However, if the UMDF USB driver is the PPO and implements its own selective suspend support, it can use queues that are either power managed or not power managed.

The following guidelines apply to the use of power-managed queues in UMDF USB drivers:

  • UMDF drivers that claim power policy ownership and rely on the UMDF selective suspend support can use power-managed queues.

  • UMDF drivers that rely on WinUSB for selective suspend support should not use power-managed queues. With selective suspend, an incoming I/O request should wake the device. However, the request must be dispatched to the UMDF driver before it can be forwarded to WinUSB.sys, which can then initiate the wake process. As discussed in the preceding section, a power-managed queue does not dispatch incoming requests until the device reenters D0. If the UMDF driver uses a power-managed queue, the queue does not dispatch any requests until the device reenters D0, so the stack is stalled.

  • UMDF drivers that respond only to system wake events and do not support selective suspend can use power-managed queues whether or not they are the PPO. If the driver does not support selective suspend, the device reenters D0 only in response to system power transitions. These events are handled by Plug and Play and power callbacks—not by I/O queues—so they can awaken the device at the appropriate time regardless of whether the UMDF driver’s I/O queues are power managed.

What should you do?

  • WDF drivers that are above the PPO and must wake idle devices should use only I/O queues that are not power managed.

  • UMDF USB drivers that rely on the underlying WinUSB driver for selective suspend support should use only I/O queues that are not power managed.

For more information:

 

 

Send comments about this topic to Microsoft