Hello driver experts,
I am currently troubleshooting a locking issue with a rather complex driver, which functions as an UpperFilter for the DiskDrive class. I have narrowed the problem down to the IRP_MJ_READ operation. The deadlock occurs when processing the IRP asynchronously. Here is the sequence of actions leading to the deadlock:
- Pending the original IRP.
- Adding it to the worker thread queue.
- Making a copy of the original IRP.
- Sending the new IRP to the lower device.
- Waiting for the result.
- Completing the original request.
The read operation must come from the pagefile, specifically after the pagefile has been increased in size.
Here is the stack when its deadlocked.
nt!KiSwapThread+0x500
nt!KiCommitThreadWait+0x14f
nt!KeWaitForSingleObject+0x233
nt!ExfAcquirePushLockExclusiveEx+0x1a0
nt!ExAcquirePushLockExclusiveEx+0x1a2
nt!RtlpHpSegPageRangeShrink+0x423
nt!ExFreeHeapPool+0x6b2
nt!ExFreePool+0x9
Wof!FileProvReadCompressedOnNewStackExtendedCompletion+0x376
Wof!FileProvReadCompressedCompletionWorker+0x75
Wof!FileProvReadCompressedCompletion+0x3e
FLTMGR!FltpPassThroughCompletionWorker+0x48a
FLTMGR!FltpPassThroughCompletion+0xc
nt!IopfCompleteRequest+0x1a5
nt!IofCompleteRequest+0x17MyDisk!ReadDispatchSync+0x241
MyDisk!ThreadCallbackRead+0xb4
nt!PspSystemThreadStartup+0x55
nt!KiStartSystemThread+0x28
And here is function which "clones" the original IRP, sends it down , waits for response and completes the original IRP.
NTSTATUS
CompletionRoutine(
In PDEVICE_OBJECT DeviceObject,
In PIRP Irp,
In PVOID Context)
{
UNREFERENCED_PARAMETER(DeviceObject);if (Irp->PendingReturned) { PKEVENT event = (PKEVENT)Context; KeSetEvent(event, IO_NO_INCREMENT, FALSE); } return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
ReadDispatchSync(
In PDEVICE_OBJECT DeviceObject,
Inout PIRP Irp)
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
DEVICE_EXTENSION* deviceExtension = (DEVICE_EXTENSION*)DeviceObject->DeviceExtension;
NTSTATUS status;
KEVENT event;
PIRP newIrp;
PMDL mdl;// Initialize an event to wait for the completion of the new IRP KeInitializeEvent(&event, SynchronizationEvent, FALSE); newIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (newIrp == NULL) { Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INSUFFICIENT_RESOURCES; } PIO_STACK_LOCATION newStack = IoGetNextIrpStackLocation(newIrp); newStack->Parameters.Read.ByteOffset.QuadPart = irpStack->Parameters.Read.ByteOffset.QuadPart; newStack->Parameters.Read.Length = irpStack->Parameters.Read.Length; newStack->MajorFunction = irpStack->MajorFunction; newStack->MinorFunction = irpStack->MinorFunction; // Allocate an MDL for the new IRP // get buffer from the parent IRP mdl = IoAllocateMdl( MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority), irpStack->Parameters.Read.Length, FALSE, FALSE, newIrp); if (mdl == NULL) { IoFreeIrp(newIrp); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool(mdl); newIrp->MdlAddress = mdl; IoSetCompletionRoutine(newIrp, CompletionRoutine, &event, TRUE, TRUE, TRUE); newIrp->Flags = Irp->Flags; newIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread; // Send the new IRP down the stack status = IoCallDriver(deviceExtension->LowerDeviceObject, newIrp); if (status == STATUS_PENDING) { // Wait for the new IRP to complete KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = newIrp->IoStatus.Status; } // Complete the original IRP // Copy status and information Irp->IoStatus.Status = status; Irp->IoStatus.Information = newIrp->IoStatus.Information; // complete original IRP, // !!!!!!!! Locks up here IoCompleteRequest(Irp, IO_NO_INCREMENT); IoFreeMdl(mdl); IoFreeIrp(newIrp); return status;
}
Everything else it pretty much boilerplate code
Increasing the number of worker threads only delays the deadlock. Using work items with the NormalWorkQueue seems to solve the problem, but I suspect this is not the correct solution and may cause issues later. I believe I need to lock the pages with MmProbeAndLockPages, but my initial attempt at this has failed.
I would appreciate any insights or suggestions
Thank you!