Click to Rate and Give Feedback
MSDN
MSDN Library
Windows Driver Kit
Design Guide
 Using Automatic Synchronization

  Switch on low bandwidth view
Windows Driver Kit: Kernel-Mode Driver Framework
Using Automatic Synchronization

Almost all of the code in a framework-based driver resides in event callback functions. The framework automatically synchronizes most of a driver's callback functions, as follows:

  • The framework always synchronizes general device object, functional device object (FDO), and physical device object (PDO) event callback functions with each other so that only one of the callback functions can be called at a time for each device. These callback functions support Plug and Play (PnP) and power management events and are called at IRQL = PASSIVE_LEVEL.
  • The framework can synchronize the execution of the callback functions that handle a driver's I/O requests, so that these callback functions run one at a time. Specifically, the framework can synchronize the callback functions for queue, interrupt, deferred procedure call (DPC), timer, work-item, and file objects, along with the request object's EvtRequestCancel callback function. The framework calls most of these callback functions at IRQL = DISPATCH_LEVEL, but you can force the queue and file object callback functions to run at IRQL = PASSIVE_LEVEL. (Work-item callback functions always run at PASSIVE_LEVEL.)

The framework implements this automatic synchronization by using a set of internal synchronization locks. The framework ensures that two or more threads cannot call the same callback function at the same time, because each thread must wait until it can acquire a synchronization lock before calling a callback function.

Your driver should store object-specific data in object context space. If your driver uses only framework-defined interfaces, only callback functions that receive a handle to the object can access this data. If the framework is synchronizing calls to the driver's callback functions, only one callback function will be called at a time and the object's context space will be accessible to only one callback function at a time.

Code that services interrupts and accesses interrupt data must run at the device's IRQL (DIRQL) and requires additional synchronization. For more information, see Synchronizing Interrupt Code.

You can choose how the framework synchronizes execution of your driver's I/O request–related callback functions. The synchronization options that are available to your driver are:

  • Device-level synchronization

    The framework synchronizes all of the request handlers for all of the device's queues, along with the device's file object callback functions and each request object's EvtRequestCancel callback function, so that they run one at a time. Optionally, the framework can also synchronize these callback functions with any interrupt, DPC, work-item, and timer object callback functions that your driver provides for the device (excluding the interrupt object's EvtInterruptIsr callback function, which runs at DIRQL).

    The framework achieves this synchronization by acquiring the device's synchronization lock before calling a callback function.

  • Queue-level synchronization

    The framework synchronizes all of the request handlers for a single queue, along with the parent device's file object callback functions and each request object's EvtRequestCancel callback function, so that they run one at a time. Optionally, the framework can also synchronize these callback functions with any interrupt, DPC, work-item, and timer object callback functions that your driver provides for the parent device (excluding the interrupt object's EvtInterruptIsr callback function).

    The framework achieves this synchronization by acquiring the queue's synchronization lock before calling a callback function.

  • No synchronization

    The framework does not synchronize the execution of callback functions that handle a driver's I/O requests and does not acquire a synchronization lock before calling the callback functions. If synchronization is required, the driver must provide it.

Choosing a Synchronization Scope

To specify whether you want the framework to provide device-level synchronization, queue-level synchronization, or no synchronization for your driver, you can specify a synchronization scope for your driver object, device objects, or queue objects. The SynchronizationScope member of an object's WDF_OBJECT_ATTRIBUTES structure identifies the object's synchronization scope. The synchronization scope values that your driver can specify are:

WdfSynchronizationScopeDevice
The framework synchronizes by obtaining a device object's synchronization lock.
WdfSynchronizationScopeQueue
The framework synchronizes by obtaining a queue object's synchronization lock.
WdfSynchronizationScopeNone
The framework does not synchronize and does not obtain a synchronization lock.
WdfSynchronizationScopeInheritFromParent
The framework obtains the object's SynchronizationScope value from the object's parent object.

For more information about the synchronization scope values, see WDF_SYNCHRONIZATION_SCOPE.

The default synchronization scope for driver objects is WdfSynchronizationScopeNone. The default synchronization scope for device and queue objects is WdfSynchronizationScopeInheritFromParent.

If you want the framework to provide device-level synchronization for all devices, you can use the following steps:

  1. Set SynchronizationScope to WdfSynchronizationScopeDevice in the WDF_OBJECT_ATTRIBUTES structure of the driver's driver object.
  2. Use the default WdfSynchronizationScopeInheritFromParent value for each device object.

Alternatively, to provide device-level synchronization for individual devices, you can use the following steps:

  1. Use the default WdfSynchronizationScopeNone value for the driver object.
  2. Set SynchronizationScope to WdfSynchronizationScopeDevice in the WDF_OBJECT_ATTRIBUTES structure of individual device objects.

If you want the framework to provide queue-level synchronization for a device, you must use the following steps:

  1. Set SynchronizationScope to WdfSynchronizationScopeQueue in the WDF_OBJECT_ATTRIBUTES structure of the device object.
  2. Use the default WdfSynchronizationScopeInheritFromParent value for each device's queue objects.

If you do not want the framework to synchronize the callback functions that handle your driver's I/O requests, use the default SynchronizationScope value for your driver's driver, device, and queue objects. In this case, the framework does not automatically synchronize the driver's I/O request–related callback functions, and the callback functions can be called at IRQL <= DISPATCH_LEVEL.

Note that setting a SynchronizationScope value synchronizes only the queue and file object callback functions. If you want the framework to also synchronize the driver's interrupt, DPC, work-item, and timer object callback functions, the driver must set the AutomaticSerialization member of these objects' configuration structures to TRUE.

However, you can set AutomaticSerialization to TRUE only if all of the callback functions that you want to synchronize run at the same IRQL. Choosing an execution level, which is described next, might result in incompatible IRQL levels. In such a situation, the driver must use framework locks instead of setting AutomaticSerialization. For more information about the configuration structures for interrupt, DPC, work-item, and timer objects, and for more information about restrictions that apply to setting AutomaticSerialization in these structures, see WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIG, and WDF_TIMER_CONFIG.

Choosing an Execution Level

When a driver creates some types of framework objects, it can specify an execution level for the object. The execution level specifies the IRQL at which the framework will call the object's event callback functions that handle a driver's I/O requests.

If a driver supplies an execution level, the supplied level affects the callback functions for queue and file objects. Ordinarily, if the driver is using automatic synchronization, the framework calls these callback functions at IRQL = DISPATCH_LEVEL. By specifying an execution level, the driver can force the framework to call these callback functions at IRQL = PASSIVE_LEVEL. The framework uses the following rules when setting the IRQL at which queue and file object callback functions are called:

  • If a driver uses automatic synchronization, its queue and file object callback functions are called at IRQL = DISPATCH_LEVEL unless the driver asks the framework to call its callback functions at IRQL = PASSIVE_LEVEL.
  • If a driver is not using automatic synchronization and does not specify an execution level, the driver's queue and file object callback functions can be called at IRQL <= DISPATCH_LEVEL.

Note that if your driver provides file object callback functions, you will most likely want the framework to call these callback functions at IRQL = PASSIVE_LEVEL because some file data, such as the file name, is pageable.

To supply an execution level, your driver must specify a value for the ExecutionLevel member of an object's WDF_OBJECT_ATTRIBUTES structure. The execution level values that your driver can specify are:

WdfExecutionLevelPassive
The framework calls the object's callback functions at IRQL = PASSIVE_LEVEL.
WdfExecutionLevelDispatch
The framework can call the object's callback functions at IRQL <= DISPATCH_LEVEL. (If the driver is using automatic synchronization, the framework always calls the callback functions at IRQL = DISPATCH_LEVEL.)
WdfExecutionLevelInheritFromParent
The framework obtains the object's ExecutionLevel value from the object's parent.

The default execution level for driver objects is WdfExecutionLevelDispatch. The default execution level for all other objects is WdfExecutionLevelInheritFromParent.

For more information about the execution level values, see WDF_EXECUTION_LEVEL.

The following table shows the IRQL level at which the framework can call a driver's callback functions for queue objects and file objects.

Synchronization ScopeExecution LevelIRQL of Queue and File Callback Functions

You can set the execution level to WdfExecutionLevelPassive or WdfExecutionLevelDispatch for driver, device, file, timer, and general objects. For other objects, only WdfExecutionLevelInheritFromParent is allowed.

You should specify WdfExecutionLevelPassive if:

  • Your driver's callback functions must call framework methods or Windows Driver Model (WDM) routines that can be called only at IRQL = PASSIVE_LEVEL.
  • Your driver's callback functions must access pageable code or data. (For example, file object callback functions typically access pageable data.)

Instead of setting WdfExecutionLevelPassive, your driver can set WdfExecutionLevelDispatch and provide a callback function that creates work items if it must handle some operations at IRQL = PASSIVE_LEVEL.

If your driver is at the top of the driver stack, the system typically calls the driver at IRQL = PASSIVE_LEVEL, so specifying WdfExecutionLevelPassive is relatively efficient. However, if your driver is not at the top of the stack, the system will likely not call your driver at IRQL = PASSIVE_LEVEL and therefore the framework must queue your driver's calls to work items, which are later called at IRQL = PASSIVE_LEVEL. This process is inefficient, compared to allowing your driver's callback functions to be called at IRQL <= DISPATCH_LEVEL. Before deciding whether your driver should set an object's execution level to WdfExecutionLevelPassive, you should determine the IRQL at which other drivers in the driver stack are called.

For DPC objects, and for timer objects that do not represent passive-level timers, note that you cannot set the AutomaticSerialization member of the configuration structure to TRUE if you have set the parent device's execution level to WdfExecutionLevelPassive. This is because the framework will acquire the device object's callback synchronization locks at IRQL = PASSIVE_LEVEL and therefore the locks cannot be used to synchronize the DPC or timer object callback functions, which must execute at IRQL = DISPATCH_LEVEL. In such a case, your driver should use framework spin locks in any device, DPC, or timer object callback functions that must be synchronized with each other.

Also note that for timer objects that do represent passive-level timers, you can set the AutomaticSerialization member of the configuration structure to TRUE only if the parent device's execution level to WdfExecutionLevelPassive.


Send feedback on this topic
Built on May 20, 2009
Tags What's this?: Add a tag
Community Content   What is Community Content?
Add new content RSS  Annotations
Processing
© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Page view tracker