Moved bus device clean-up internal and automatic
This commit is contained in:
+15
-65
@@ -19,8 +19,9 @@ Typical client driver usage flow:
|
||||
1. **DriverEntry** – fill in `DMF_BusFilter_CONFIG` and call `DMF_BusFilter_Initialize` after `WdfDriverCreate` returns.
|
||||
2. **EvtDriverDeviceAdd** – register `DMF_BusFilter_DeviceAdd` as the `EvtDriverDeviceAdd` callback (or call it directly from a
|
||||
wrapper).
|
||||
3. **EvtDeviceReleaseHardware or EvtCleanupCallback** – call `DMF_BusFilter_DeviceCleanup` to drain and destroy any remaining
|
||||
proxy child devices and prevent resource leaks.
|
||||
|
||||
Proxy child teardown is automatic. The library registers an internal WDF context cleanup callback on the bus FDO so that any
|
||||
remaining proxy children are drained and destroyed when the FDO is deleted. No client-side cleanup call is required.
|
||||
|
||||
Available only in kernel-mode builds (`DMF_KERNEL_MODE`).
|
||||
|
||||
@@ -244,8 +245,8 @@ EVT_DMF_BusFilter_DeviceRemove(
|
||||
````
|
||||
|
||||
Called when a proxy child device is about to be destroyed. This fires both during the normal `IRP_MN_REMOVE_DEVICE` path and
|
||||
during forced teardown in `DMF_BusFilter_DeviceCleanup`. At this point in-flight I/O to the child has already been drained via
|
||||
the per-child `IO_REMOVE_LOCK`, so the child is quiescent.
|
||||
during the automatic teardown that runs when the bus FDO is deleted. At this point in-flight I/O to the child has already been
|
||||
drained via the per-child `IO_REMOVE_LOCK`, so the child is quiescent.
|
||||
|
||||
The callback must not attempt to forward any IRPs to the child or access the child's WDM stack; both are being torn down. After
|
||||
this callback returns the `ChildDevice` handle is deleted by the library.
|
||||
@@ -467,43 +468,6 @@ DeviceInit | The `WDFDEVICE_INIT` prepared by the framework.
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
##### DMF_BusFilter_DeviceCleanup
|
||||
|
||||
````
|
||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||
VOID
|
||||
DMF_BusFilter_DeviceCleanup(
|
||||
_In_ WDFDEVICE Device
|
||||
);
|
||||
````
|
||||
|
||||
Tears down all proxy child filter devices still associated with the bus device. The client **must** call this from its
|
||||
`EvtDeviceReleaseHardware` or `EvtCleanupCallback` to prevent WDM device object leaks. Without this call, any child devices
|
||||
remaining in the internal list at unload time will be leaked and will fault when the I/O manager later dereferences them.
|
||||
|
||||
Under normal PnP operation the child list is already empty when this function runs (`IRP_MN_REMOVE_DEVICE` has been dispatched
|
||||
to every child). This function is a safety net for abnormal paths such as surprise removal of the parent before all children
|
||||
received `REMOVE`.
|
||||
|
||||
The function first waits for any outstanding bus-relations work items to drain (preventing a race with a pending passive-level
|
||||
`Relations_AddDevice` call), then for each remaining child: acquires the per-child `IO_REMOVE_LOCK` to drain in-flight I/O,
|
||||
calls `EvtDeviceRemove` (if set), and detaches and deletes the proxy filter device in the mandatory order.
|
||||
|
||||
If a child's `IO_REMOVE_LOCK` cannot be acquired (`STATUS_DELETE_PENDING`), it means a concurrent `IRP_MN_REMOVE_DEVICE` is
|
||||
already processing that child; this function skips it and lets the `REMOVE` path handle teardown.
|
||||
|
||||
##### Returns
|
||||
|
||||
None.
|
||||
|
||||
##### Parameters
|
||||
|
||||
Parameter | Description
|
||||
----|----
|
||||
Device | The bus filter `WDFDEVICE` whose proxy children should be cleaned up.
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
##### DMF_BusFilter_WdmDeviceObjectGet
|
||||
|
||||
````
|
||||
@@ -587,8 +551,10 @@ ChildDevice | Opaque handle for a proxy child device.
|
||||
|
||||
* `DMF_BusFilter_Initialize` must be called before `DMF_BusFilter_DeviceAdd`. If `DeviceAdd` is called without a prior
|
||||
successful `Initialize`, it returns `STATUS_INVALID_DEVICE_STATE`.
|
||||
* `DMF_BusFilter_DeviceCleanup` must be called from `EvtDeviceReleaseHardware` or `EvtCleanupCallback`. Omitting this call
|
||||
leaks WDM device objects that remain in the child list at driver unload.
|
||||
* Proxy child teardown is automatic. The library registers an internal WDF context `EvtCleanupCallback` on the bus FDO's
|
||||
`PARENT_BUS_DEVICE_CONTEXT`. When the FDO is deleted the callback drains and destroys any remaining proxy children. On the
|
||||
normal PnP path the child list is already empty (every `IRP_MN_REMOVE_DEVICE` self-drained it), so the callback is a safe
|
||||
no-op; it serves as the safety net for surprise-removal paths where children may not have received `REMOVE` before the parent.
|
||||
* The mandatory WDM filter teardown order for each proxy child is: (1) `IoReleaseRemoveLockAndWait` to drain in-flight I/O,
|
||||
(2) `IoDetachDevice` to detach the filter from the PDO stack, (3) `IoDeleteDevice` to lazy-free the device object. Nothing
|
||||
may access the device extension after `IoDeleteDevice` returns.
|
||||
@@ -650,8 +616,9 @@ manager dispatching `IRP_MN_START_DEVICE` to the child FDO. Attaching outside th
|
||||
construction.
|
||||
|
||||
The parent bus device tracks outstanding passive-level workers with an atomic counter (`OutstandingWorkItems`) and a kernel
|
||||
event (`WorkItemsIdle`, signaled when the counter reaches zero). `DMF_BusFilter_DeviceCleanup` waits on `WorkItemsIdle` before
|
||||
draining the child list, preventing a race between a concurrent `Relations_AddDevice` worker and list teardown.
|
||||
event (`WorkItemsIdle`, signaled when the counter reaches zero). The internal teardown routine (invoked by the FDO context
|
||||
cleanup callback) waits on `WorkItemsIdle` before draining the child list, preventing a race between a concurrent
|
||||
`Relations_AddDevice` worker and list teardown.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
@@ -686,11 +653,12 @@ the filter. Release rules:
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
The following sketch shows the minimum wiring for a client driver.
|
||||
The following sketch shows the minimum wiring for a client driver. Proxy child teardown is handled automatically by the
|
||||
library when the bus FDO is deleted, so no `EvtDeviceReleaseHardware` or explicit cleanup call is needed.
|
||||
|
||||
````c
|
||||
//
|
||||
// Optional: per-child callback.
|
||||
// Optional: per-child callbacks.
|
||||
//
|
||||
static
|
||||
NTSTATUS
|
||||
@@ -719,24 +687,6 @@ MyDriver_EvtDeviceRemove(
|
||||
UNREFERENCED_PARAMETER(ChildDevice);
|
||||
}
|
||||
|
||||
//
|
||||
// Called when the bus filter FDO's hardware is being released.
|
||||
//
|
||||
static
|
||||
NTSTATUS
|
||||
MyDriver_EvtDeviceReleaseHardware(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFCMRESLIST ResourcesTranslated
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ResourcesTranslated);
|
||||
|
||||
// Mandatory: drain and destroy all remaining proxy children.
|
||||
DMF_BusFilter_DeviceCleanup(Device);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// DriverEntry
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user