[FASTFAT] Implement the overflow queue. CORE-17344 CORE-17328

This avoids blocking all Ex worker threads in fastfat, thereby making Cc
unable to issue the lazy writes that would unblock those workers.
This is more or less directly taken from fastfat_new.
This commit is contained in:
Thomas Faber 2020-10-18 15:23:52 +02:00
parent cdb48b8219
commit 303f17f884
No known key found for this signature in database
GPG key ID: 076E7C3D44720826
3 changed files with 97 additions and 6 deletions

View file

@ -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;

View file

@ -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.

View file

@ -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;