From 460c4e0c55acc49ee7f6a57054c92c530a07ffaa Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Wed, 16 Dec 2015 16:11:29 -0800 Subject: [PATCH] sys: implement FsvrtDeviceExpirationRoutine and supporting infra --- src/sys/device.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++ src/sys/driver.c | 2 + src/sys/driver.h | 4 ++ src/sys/fsctl.c | 4 +- src/sys/ioq.c | 6 ++- 5 files changed, 135 insertions(+), 3 deletions(-) diff --git a/src/sys/device.c b/src/sys/device.c index 6c93e748..62205799 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -13,17 +13,23 @@ NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize, NTSTATUS FspDeviceCreate(UINT32 Kind, ULONG ExtraSize, DEVICE_TYPE DeviceType, PDEVICE_OBJECT *PDeviceObject); +VOID FspDeviceInitComplete(PDEVICE_OBJECT DeviceObject); VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject); static NTSTATUS FspFsctlDeviceInit(PDEVICE_OBJECT DeviceObject); +static VOID FspFsctlDeviceInitComplete(PDEVICE_OBJECT DeviceObject); static VOID FspFsctlDeviceFini(PDEVICE_OBJECT DeviceObject); static NTSTATUS FspFsvrtDeviceInit(PDEVICE_OBJECT DeviceObject); +static VOID FspFsvrtDeviceInitComplete(PDEVICE_OBJECT DeviceObject); static VOID FspFsvrtDeviceFini(PDEVICE_OBJECT DeviceObject); static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject); +static VOID FspFsvolDeviceInitComplete(PDEVICE_OBJECT DeviceObject); static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject); BOOLEAN FspDeviceRetain(PDEVICE_OBJECT DeviceObject); VOID FspDeviceRelease(PDEVICE_OBJECT DeviceObject); VOID FspFsctlDeviceVolumeCreated(PDEVICE_OBJECT DeviceObject); VOID FspFsctlDeviceVolumeDeleted(PDEVICE_OBJECT DeviceObject); +static IO_TIMER_ROUTINE FspFsvrtDeviceTimerRoutine; +static WORKER_THREAD_ROUTINE FspFsvrtDeviceExpirationRoutine; PVOID FspFsvolDeviceLookupContext(PDEVICE_OBJECT DeviceObject, UINT64 Identifier); PVOID FspFsvolDeviceInsertContext(PDEVICE_OBJECT DeviceObject, UINT64 Identifier, PVOID Context, FSP_DEVICE_GENERIC_TABLE_ELEMENT *ElementStorage, PBOOLEAN PInserted); @@ -41,12 +47,16 @@ VOID FspDeviceDeleteAll(VOID); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, FspDeviceCreateSecure) #pragma alloc_text(PAGE, FspDeviceCreate) +#pragma alloc_text(PAGE, FspDeviceInitComplete) #pragma alloc_text(PAGE, FspDeviceDelete) #pragma alloc_text(PAGE, FspFsctlDeviceInit) +#pragma alloc_text(PAGE, FspFsctlDeviceInitComplete) #pragma alloc_text(PAGE, FspFsctlDeviceFini) #pragma alloc_text(PAGE, FspFsvrtDeviceInit) +#pragma alloc_text(PAGE, FspFsvrtDeviceInitComplete) #pragma alloc_text(PAGE, FspFsvrtDeviceFini) #pragma alloc_text(PAGE, FspFsvolDeviceInit) +#pragma alloc_text(PAGE, FspFsvolDeviceInitComplete) #pragma alloc_text(PAGE, FspFsvolDeviceFini) #pragma alloc_text(PAGE, FspFsctlDeviceVolumeCreated) #pragma alloc_text(PAGE, FspFsctlDeviceVolumeDeleted) @@ -142,6 +152,31 @@ NTSTATUS FspDeviceCreate(UINT32 Kind, ULONG ExtraSize, return FspDeviceCreateSecure(Kind, ExtraSize, 0, DeviceType, 0, 0, PDeviceObject); } +VOID FspDeviceInitComplete(PDEVICE_OBJECT DeviceObject) +{ + PAGED_CODE(); + + FSP_DEVICE_EXTENSION *DeviceExtension = FspDeviceExtension(DeviceObject); + + switch (DeviceExtension->Kind) + { + case FspFsvolDeviceExtensionKind: + FspFsvolDeviceInitComplete(DeviceObject); + break; + case FspFsvrtDeviceExtensionKind: + FspFsvrtDeviceInitComplete(DeviceObject); + break; + case FspFsctlDeviceExtensionKind: + FspFsctlDeviceInitComplete(DeviceObject); + break; + default: + ASSERT(0); + return; + } + + ClearFlag(DeviceObject->Flags, DO_DEVICE_INITIALIZING); +} + VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); @@ -176,6 +211,11 @@ static NTSTATUS FspFsctlDeviceInit(PDEVICE_OBJECT DeviceObject) return STATUS_SUCCESS; } +static VOID FspFsctlDeviceInitComplete(PDEVICE_OBJECT DeviceObject) +{ + PAGED_CODE(); +} + static VOID FspFsctlDeviceFini(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); @@ -185,9 +225,18 @@ static NTSTATUS FspFsvrtDeviceInit(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); + NTSTATUS Result; FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(DeviceObject); + /* initialize our timer routine */ + Result = IoInitializeTimer(DeviceObject, FspFsvrtDeviceTimerRoutine, 0); + if (!NT_SUCCESS(Result)) + return Result; + FspIoqInitialize(&FsvrtDeviceExtension->Ioq); + KeInitializeSpinLock(&FsvrtDeviceExtension->ExpirationLock); + ExInitializeWorkItem(&FsvrtDeviceExtension->ExpirationWorkItem, + FspFsvrtDeviceExpirationRoutine, DeviceObject); FsvrtDeviceExtension->SwapVpb = FspAllocNonPagedExternal(sizeof *FsvrtDeviceExtension->SwapVpb); if (0 == FsvrtDeviceExtension->SwapVpb) @@ -197,12 +246,28 @@ static NTSTATUS FspFsvrtDeviceInit(PDEVICE_OBJECT DeviceObject) return STATUS_SUCCESS; } +static VOID FspFsvrtDeviceInitComplete(PDEVICE_OBJECT DeviceObject) +{ + PAGED_CODE(); + + IoStartTimer(DeviceObject); +} + static VOID FspFsvrtDeviceFini(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(DeviceObject); + /* + * First things first: stop our timer. + * + * Our IoTimer routine will NOT be called again after IoStopTimer() returns. + * However a work item may be in flight. For this reason our IoTimer routine + * does an ObReferenceObject() on our DeviceObject before queueing work items. + */ + IoStopTimer(DeviceObject); + if (0 != FsvrtDeviceExtension->SwapVpb) FspFreeExternal(FsvrtDeviceExtension->SwapVpb); } @@ -219,6 +284,11 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject) return STATUS_SUCCESS; } +static VOID FspFsvolDeviceInitComplete(PDEVICE_OBJECT DeviceObject) +{ + PAGED_CODE(); +} + static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject) { PAGED_CODE(); @@ -314,6 +384,58 @@ VOID FspFsctlDeviceVolumeDeleted(PDEVICE_OBJECT DeviceObject) #endif } +static VOID FspFsvrtDeviceTimerRoutine(PDEVICE_OBJECT DeviceObject, PVOID Context) +{ + // !PAGED_CODE(); + + /* + * This routine runs at DPC level. Reference our DeviceObject and queue a work item + * so that we can do our processing at Passive level. Only do so if the work item + * is not already in flight (otherwise we could requeue the same work item). + */ + + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(DeviceObject); + + KeAcquireSpinLockAtDpcLevel(&FsvrtDeviceExtension->ExpirationLock); + if (!FsvrtDeviceExtension->ExpirationInProgress) + { + FsvrtDeviceExtension->ExpirationInProgress = TRUE; + ObReferenceObject(DeviceObject); + ExQueueWorkItem(&FsvrtDeviceExtension->ExpirationWorkItem, DelayedWorkQueue); + } + KeReleaseSpinLockFromDpcLevel(&FsvrtDeviceExtension->ExpirationLock); +} + +static VOID FspFsvrtDeviceExpirationRoutine(PVOID Context) +{ + // !PAGED_CODE(); + + PDEVICE_OBJECT DeviceObject = Context; + if (FspDeviceRetain(DeviceObject)) + try + { + FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(DeviceObject); + LARGE_INTEGER Timeout; + KIRQL Irql; + + Timeout.QuadPart = FsvrtDeviceExtension->VolumeParams.IrpTimeout * 10000; + /* convert millis to nanos */ + FspIoqRemoveExpired(&FsvrtDeviceExtension->Ioq, &Timeout); + + KeAcquireSpinLock(&FsvrtDeviceExtension->ExpirationLock, &Irql); + FsvrtDeviceExtension->ExpirationInProgress = FALSE; + KeReleaseSpinLock(&FsvrtDeviceExtension->ExpirationLock, Irql); + } + finally + { + FspDeviceRelease(DeviceObject); + } + + ObDereferenceObject(DeviceObject); +} + PVOID FspFsvolDeviceLookupContext(PDEVICE_OBJECT DeviceObject, UINT64 Identifier) { PAGED_CODE(); diff --git a/src/sys/driver.c b/src/sys/driver.c index 10ae34c1..cb7a0e7f 100644 --- a/src/sys/driver.c +++ b/src/sys/driver.c @@ -39,6 +39,8 @@ NTSTATUS DriverEntry( &FspFsctlNetDeviceObject); if (!NT_SUCCESS(Result)) FSP_RETURN(FspDeviceDelete(FspFsctlDiskDeviceObject)); + FspDeviceInitComplete(FspFsctlDiskDeviceObject); + FspDeviceInitComplete(FspFsctlNetDeviceObject); /* setup the driver object */ DriverObject->DriverUnload = FspUnload; diff --git a/src/sys/driver.h b/src/sys/driver.h index a7a193a3..0b9df793 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -318,6 +318,9 @@ typedef struct PDEVICE_OBJECT FsvolDeviceObject; FSP_FSCTL_VOLUME_PARAMS VolumeParams; FSP_IOQ Ioq; + KSPIN_LOCK ExpirationLock; + WORK_QUEUE_ITEM ExpirationWorkItem; + BOOLEAN ExpirationInProgress; PVPB SwapVpb; BOOLEAN Deleted; FSP_FSCTL_DECLSPEC_ALIGN UINT8 SecurityDescriptorBuf[]; @@ -369,6 +372,7 @@ NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize, NTSTATUS FspDeviceCreate(UINT32 Kind, ULONG ExtraSize, DEVICE_TYPE DeviceType, PDEVICE_OBJECT *PDeviceObject); +VOID FspDeviceInitComplete(PDEVICE_OBJECT DeviceObject); VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject); BOOLEAN FspDeviceRetain(PDEVICE_OBJECT DeviceObject); VOID FspDeviceRelease(PDEVICE_OBJECT DeviceObject); diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c index 3e5c2f68..9b97c926 100644 --- a/src/sys/fsctl.c +++ b/src/sys/fsctl.c @@ -178,7 +178,7 @@ static NTSTATUS FspFsctlCreateVolume( FsvrtDeviceExtension->VolumeParams = VolumeParams; RtlCopyMemory(FsvrtDeviceExtension->SecurityDescriptorBuf, SecurityDescriptorBuf, SecurityDescriptorSize); - ClearFlag(FsvrtDeviceObject->Flags, DO_DEVICE_INITIALIZING); + FspDeviceInitComplete(FsvrtDeviceObject); Irp->IoStatus.Information = DeviceName.Length + sizeof(WCHAR); FspFsctlDeviceVolumeCreated(DeviceObject); } @@ -255,7 +255,7 @@ static NTSTATUS FspFsctlMountVolume( FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); FsvolDeviceExtension->FsvrtDeviceObject = FsvrtDeviceObject; FsvrtDeviceExtension->FsvolDeviceObject = FsvolDeviceObject; - ClearFlag(FsvolDeviceObject->Flags, DO_DEVICE_INITIALIZING); + FspDeviceInitComplete(FsvolDeviceObject); Vpb->DeviceObject = FsvolDeviceObject; Vpb->SerialNumber = FsvrtDeviceExtension->VolumeParams.SerialNumber; Irp->IoStatus.Information = 0; diff --git a/src/sys/ioq.c b/src/sys/ioq.c index ee7f12ec..b0a5ad6b 100644 --- a/src/sys/ioq.c +++ b/src/sys/ioq.c @@ -238,7 +238,11 @@ VOID FspIoqRemoveExpired(FSP_IOQ *Ioq, PLARGE_INTEGER Timeout) { FSP_IOQ_PEEK_CONTEXT PeekContext; PeekContext.IrpHint = 0; - PeekContext.ExpirationTime = KeQueryInterruptTime() - Timeout->QuadPart; + PeekContext.ExpirationTime = KeQueryInterruptTime(); + if (PeekContext.ExpirationTime >= (ULONGLONG)Timeout->QuadPart) + PeekContext.ExpirationTime -= Timeout->QuadPart; + else + PeekContext.ExpirationTime = 0; PIRP Irp; while (0 != (Irp = IoCsqRemoveNextIrp(&Ioq->PendingIoCsq, &PeekContext))) FspIoqPendingCompleteCanceledIrp(&Ioq->PendingIoCsq, Irp);