Training
Learning path
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
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. Only one of the callback functions can be called at a time for each device. The exception is EvtDeviceSurpriseRemoval, EvtDeviceQueryRemove, and EvtDeviceQueryStop. These callback functions support Plug and Play (PnP) and power management events and are called at IRQL = PASSIVE_LEVEL.
Optionally, 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 can't call the same callback function at the same time. Each thread must wait until it can acquire a synchronization lock before calling a callback function. (Optionally, drivers can also acquire these synchronization locks when necessary. For more information, see Using Framework Locks.)
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 is called at a time. The object's context space is accessible to only one callback function at a time.
Unless your driver implements passive-level interrupt handling, 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.
If your driver enables automatic synchronization of the callback functions that handle I/O requests, the framework synchronizes these callback functions so that they run one at a time. The following table lists the callback functions that the framework synchronizes.
Object type | Synchronized Callback Functions |
---|---|
Queue object |
|
File object |
|
Request object |
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). To enable this additional synchronization, the driver must set the AutomaticSerialization member of these objects' configuration structures to TRUE.
In summary, the framework's automatic synchronization capability provides the following features:
The framework always synchronizes each device's PnP and power management callback functions.
Optionally, the framework can synchronize an I/O queue's request handlers, and a few additional callback functions (see the previous table).
A driver can ask the framework to synchronize callback functions for interrupt, DPC, work-item, and timer objects.
Drivers must synchronize code that services interrupts and accesses interrupt data by using the techniques that are described in Synchronizing Interrupt Code.
The framework doesn't synchronize a driver's other callback functions, such as the driver's CompletionRoutine callback function, or the callback functions that the I/O target object defines. Instead, the framework provides additional locks that drivers can use to synchronize these callback functions.
You can choose to have the framework synchronize all of the callback functions that are associated with all of a device's I/O queues. Alternatively, you can choose to have the framework separately synchronize the callback functions for each of a device's I/O queues. The synchronization options that are available to your driver are as follows:
Device-level synchronization
The framework synchronizes the callback functions for all of the device's I/O queues, so they run one at a time. The framework achieves this synchronization by acquiring the device's synchronization lock before calling a callback function.
Queue-level synchronization
The framework synchronizes the callback functions for each individual I/O queue, so they run one at a time. The framework achieves this synchronization by acquiring the queue's synchronization lock before calling a callback function.
No synchronization
The framework doesn't synchronize the execution of the callback functions in the preceding table and doesn't acquire a synchronization lock before calling the callback functions. If synchronization is required, the driver must provide it.
To specify whether you want the framework to provide device-level synchronization, queue-level synchronization, or no synchronization for your driver, 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 doesn't synchronize and doesn't obtain a synchronization lock.
WdfSynchronizationScopeInheritFromParent
The framework obtains the object's SynchronizationScope value from the object's parent object.
In general, we don't recommend using device-level synchronization.
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.
To use the framework to provide device-level synchronization for all devices, set SynchronizationScope to WdfSynchronizationScopeDevice in the WDF_OBJECT_ATTRIBUTES structure of the driver's driver object. Use the default WdfSynchronizationScopeInheritFromParent value for each device object.
To provide device-level synchronization for individual devices, use the default WdfSynchronizationScopeNone value for the driver object. 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 can use the following techniques:
For framework versions 1.9 and later, enable queue-level synchronization for individual queues by setting WdfSynchronizationScopeQueue in the WDF_OBJECT_ATTRIBUTES structure of the queue object. This technique is preferred.
Alternatively, you can use the following steps in all framework versions:
If you don't 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 doesn't automatically synchronize the driver's I/O request-related callback functions. The framework can call the callback functions at IRQL <= DISPATCH_LEVEL.
Setting a SynchronizationScope value synchronizes only the callback functions that the previous table contains. If you want the framework to also synchronize the driver's interrupt, DPC, work-item, and timer object callback functions, 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.
If you set AutomaticSerialization to TRUE, select queue-level synchronization.
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 calls 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 isn't using automatic synchronization and doesn't specify an execution level, the driver's queue and file object callback functions can be called at IRQL <= DISPATCH_LEVEL.
If your driver provides file object callback functions, you 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 Scope | Execution Level | IRQL of Queue and File Callback Functions |
---|---|---|
WdfSynchronizationScopeDevice |
WdfExecutionLevelPassive |
PASSIVE_LEVEL |
WdfSynchronizationScopeDevice |
WdfExecutionLevelDispatch |
DISPATCH_LEVEL |
WdfSynchronizationScopeQueue |
WdfExecutionLevelPassive |
PASSIVE_LEVEL |
WdfSynchronizationScopeQueue |
WdfExecutionLevelDispatch |
DISPATCH_LEVEL |
WdfSynchronizationScopeNone |
WdfExecutionLevelPassive |
PASSIVE_LEVEL |
WdfSynchronizationScopeNone |
WdfExecutionLevelDispatch |
<= DISPATCH_LEVEL |
You can set the execution level to WdfExecutionLevelPassive or WdfExecutionLevelDispatch for driver, device, file, queue, 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 you can call 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.
Before you decide whether your driver should set an object's execution level to WdfExecutionLevelPassive, determine the IRQL at which your driver and other drivers in the driver stack are called. Consider the following situations:
If your driver is at the top of the kernel-mode driver stack, the system typically calls the driver at IRQL = PASSIVE_LEVEL. The client of such a driver might be a UMDF-based driver or a user-mode application. Specifying WdfExecutionLevelPassive doesn't adversely affect the driver's performance, because the framework doesn't have to queue your driver's calls to work items that are called at IRQL = PASSIVE_LEVEL.
If your driver isn't at the top of the stack, the system likely doesn't call your driver at IRQL = PASSIVE_LEVEL. Therefore, the framework must queue your driver's calls to work items, which are later called at IRQL = PASSIVE_LEVEL. This process can cause poor driver performance, compared to allowing your driver's callback functions to be called at IRQL <= DISPATCH_LEVEL.
For DPC objects, and for timer objects that don't represent passive-level timers, you can't set the AutomaticSerialization member of the configuration structure to TRUE if you set the parent device's execution level to WdfExecutionLevelPassive. The framework acquires the device object's callback synchronization locks at IRQL = PASSIVE_LEVEL. Therefore, it can't use the locks to synchronize the DPC or timer object callback functions, which must execute at IRQL = DISPATCH_LEVEL. In this 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 is set to WdfExecutionLevelPassive.
Training
Learning path
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization