1
0

Moved bus device clean-up internal and automatic

This commit is contained in:
2026-07-04 22:45:43 +02:00
parent 861813776b
commit 53a210e011
4 changed files with 81 additions and 122 deletions
+15 -65
View File
@@ -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
//