diff --git a/drivers/filesystems/fastfat/fsctl.c b/drivers/filesystems/fastfat/fsctl.c index a8cbca4670f..6764a67d329 100644 --- a/drivers/filesystems/fastfat/fsctl.c +++ b/drivers/filesystems/fastfat/fsctl.c @@ -600,6 +600,11 @@ VfatMount( DeviceExt->HashTableSize = HashTableSize; DeviceExt->VolumeDevice = DeviceObject; + KeInitializeSpinLock(&DeviceExt->OverflowQueueSpinLock); + InitializeListHead(&DeviceExt->OverflowQueue); + DeviceExt->OverflowQueueCount = 0; + DeviceExt->PostedRequestCount = 0; + /* use same vpb as device disk */ DeviceObject->Vpb = Vpb; DeviceToMount->Vpb = Vpb; diff --git a/drivers/filesystems/fastfat/misc.c b/drivers/filesystems/fastfat/misc.c index 443ad290817..76b17f1db36 100644 --- a/drivers/filesystems/fastfat/misc.c +++ b/drivers/filesystems/fastfat/misc.c @@ -336,12 +336,53 @@ static VOID NTAPI VfatDoRequest( - PVOID IrpContext) + PVOID Context) { + PVFAT_IRP_CONTEXT IrpContext = Context; + PDEVICE_EXTENSION DeviceExt; + KIRQL OldIrql; + InterlockedDecrement(&QueueCount); - DPRINT("VfatDoRequest(IrpContext %p), MajorFunction %x, %d\n", - IrpContext, ((PVFAT_IRP_CONTEXT)IrpContext)->MajorFunction, QueueCount); - VfatDispatchRequest((PVFAT_IRP_CONTEXT)IrpContext); + + if (IrpContext->Stack->FileObject != NULL) + { + DeviceExt = IrpContext->Stack->DeviceObject->DeviceExtension; + ObReferenceObject(DeviceExt->VolumeDevice); + } + + do + { + DPRINT("VfatDoRequest(IrpContext %p), MajorFunction %x, %d\n", + IrpContext, IrpContext->MajorFunction, QueueCount); + VfatDispatchRequest(IrpContext); + IrpContext = NULL; + + /* Now process any overflow items */ + if (DeviceExt != NULL) + { + KeAcquireSpinLock(&DeviceExt->OverflowQueueSpinLock, &OldIrql); + if (DeviceExt->OverflowQueueCount != 0) + { + IrpContext = CONTAINING_RECORD(RemoveHeadList(&DeviceExt->OverflowQueue), + VFAT_IRP_CONTEXT, + WorkQueueItem.List); + DeviceExt->OverflowQueueCount--; + DPRINT("Processing overflow item for IRP %p context %p (%lu)\n", + IrpContext->Irp, IrpContext, DeviceExt->OverflowQueueCount); + } + else + { + ASSERT(IsListEmpty(&DeviceExt->OverflowQueue)); + DeviceExt->PostedRequestCount--; + } + KeReleaseSpinLock(&DeviceExt->OverflowQueueSpinLock, OldIrql); + } + } while (IrpContext != NULL); + + if (DeviceExt != NULL) + { + ObDereferenceObject(DeviceExt->VolumeDevice); + } } static @@ -349,6 +390,10 @@ NTSTATUS VfatQueueRequest( PVFAT_IRP_CONTEXT IrpContext) { + PDEVICE_EXTENSION DeviceExt; + KIRQL OldIrql; + BOOLEAN Overflow; + InterlockedIncrement(&QueueCount); DPRINT("VfatQueueRequest(IrpContext %p), %d\n", IrpContext, QueueCount); @@ -357,10 +402,41 @@ VfatQueueRequest( ASSERT(!(IrpContext->Flags & IRPCONTEXT_QUEUE) && (IrpContext->Flags & IRPCONTEXT_COMPLETE)); + Overflow = FALSE; IrpContext->Flags |= IRPCONTEXT_CANWAIT; IoMarkIrpPending(IrpContext->Irp); - ExInitializeWorkItem(&IrpContext->WorkQueueItem, VfatDoRequest, IrpContext); - ExQueueWorkItem(&IrpContext->WorkQueueItem, CriticalWorkQueue); + + /* We should not block more than two worker threads per volume, + * or we might stop Cc from doing the work to unblock us. + * Add additional requests into the overflow queue instead and process + * them all in an existing worker thread (see VfatDoRequest above). + */ + if (IrpContext->Stack->FileObject != NULL) + { + DeviceExt = IrpContext->Stack->DeviceObject->DeviceExtension; + KeAcquireSpinLock(&DeviceExt->OverflowQueueSpinLock, &OldIrql); + if (DeviceExt->PostedRequestCount > 2) + { + DeviceExt->OverflowQueueCount++; + DPRINT("Queue overflow. Adding IRP %p context %p to overflow queue (%lu)\n", + IrpContext->Irp, IrpContext, DeviceExt->OverflowQueueCount); + InsertTailList(&DeviceExt->OverflowQueue, + &IrpContext->WorkQueueItem.List); + Overflow = TRUE; + } + else + { + DeviceExt->PostedRequestCount++; + } + KeReleaseSpinLock(&DeviceExt->OverflowQueueSpinLock, OldIrql); + } + + if (!Overflow) + { + ExInitializeWorkItem(&IrpContext->WorkQueueItem, VfatDoRequest, IrpContext); + ExQueueWorkItem(&IrpContext->WorkQueueItem, CriticalWorkQueue); + } + return STATUS_PENDING; } @@ -552,6 +628,10 @@ VfatCheckForDismount( vfatDestroyFCB(Fcb); } + ASSERT(DeviceExt->OverflowQueueCount == 0); + ASSERT(IsListEmpty(&DeviceExt->OverflowQueue)); + ASSERT(DeviceExt->PostedRequestCount == 0); + /* * Now that the closing of the internal opened meta-files has been * handled, we can now set the VPB's DeviceObject to NULL. diff --git a/drivers/filesystems/fastfat/vfat.h b/drivers/filesystems/fastfat/vfat.h index fcb3a25cca7..943c6b76ab1 100644 --- a/drivers/filesystems/fastfat/vfat.h +++ b/drivers/filesystems/fastfat/vfat.h @@ -330,6 +330,12 @@ typedef struct DEVICE_EXTENSION struct _VFATFCB *RootFcb; PSTATISTICS Statistics; + /* Overflow request queue */ + KSPIN_LOCK OverflowQueueSpinLock; + LIST_ENTRY OverflowQueue; + ULONG OverflowQueueCount; + ULONG PostedRequestCount; + /* Pointers to functions for manipulating FAT. */ PGET_NEXT_CLUSTER GetNextCluster; PFIND_AND_MARK_AVAILABLE_CLUSTER FindAndMarkAvailableCluster;