From 50b00f0fcca638a7c205bdf022ade079135e58c6 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Sat, 18 Aug 2018 17:04:02 +0200 Subject: [PATCH] [FASTFAT] Implement delayed close When we're about to close a file (ie, forget everything about it and release any associated structure), actually delay it. This allows keep data fresh in memory for faster reuse in case it would be required. The effective closing will only happen after some time. For specific operations, this will produce a real speed up in ReactOS. For instance, with that patch, Winamp starts within seconds, instead of dozen of minutes. In most cases, it will bring ReactOS to performances it had before fixing the huge leak in FastFAT (commit 94ead99) without leaking the whole FS. For now, due to regressions, this is only activated for files and not for directories. Once it gets fixed, it will be enabled for both. CORE-14826 CORE-14917 --- drivers/filesystems/fastfat/cleanup.c | 24 ++++ drivers/filesystems/fastfat/close.c | 174 +++++++++++++++++++++----- drivers/filesystems/fastfat/create.c | 38 ++++++ drivers/filesystems/fastfat/iface.c | 15 +++ drivers/filesystems/fastfat/kdbg.c | 3 +- drivers/filesystems/fastfat/vfat.h | 22 +++- 6 files changed, 244 insertions(+), 32 deletions(-) diff --git a/drivers/filesystems/fastfat/cleanup.c b/drivers/filesystems/fastfat/cleanup.c index d19fa94703e..ae354086e7f 100644 --- a/drivers/filesystems/fastfat/cleanup.c +++ b/drivers/filesystems/fastfat/cleanup.c @@ -4,6 +4,7 @@ * FILE: drivers/fs/vfat/cleanup.c * PURPOSE: VFAT Filesystem * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com) + * Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES *****************************************************************/ @@ -127,6 +128,29 @@ VfatCleanupFile( { IoRemoveShareAccess(FileObject, &pFcb->FCBShareAccess); } + /* If that's the last open handle we just closed, try to see whether + * we can delay close operation + */ + else if (!BooleanFlagOn(pFcb->Flags, FCB_DELETE_PENDING) && !BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE) && + !BooleanFlagOn(pFcb->Flags, FCB_IS_FAT) && !BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME)) + { + /* This is only allowed if that's a directory with no open files + * OR if it's a file with no section opened + * FIXME: only allow files for now + */ +#if 0 + if ((vfatFCBIsDirectory(pFcb) && IsListEmpty(&pFcb->ParentListHead)) || + (!vfatFCBIsDirectory(pFcb) && FileObject->SectionObjectPointer->DataSectionObject == NULL && + FileObject->SectionObjectPointer->ImageSectionObject == NULL)) +#else + if (!vfatFCBIsDirectory(pFcb) && FileObject->SectionObjectPointer->DataSectionObject == NULL && + FileObject->SectionObjectPointer->ImageSectionObject == NULL) +#endif + { + DPRINT("Delaying close of: %wZ\n", &pFcb->PathNameU); + SetFlag(pFcb->Flags, FCB_DELAYED_CLOSE); + } + } FileObject->Flags |= FO_CLEANUP_COMPLETE; #ifdef KDBG diff --git a/drivers/filesystems/fastfat/close.c b/drivers/filesystems/fastfat/close.c index bb5cfaae786..0c313e6629b 100644 --- a/drivers/filesystems/fastfat/close.c +++ b/drivers/filesystems/fastfat/close.c @@ -4,6 +4,7 @@ * FILE: drivers/filesystems/fastfat/close.c * PURPOSE: VFAT Filesystem * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com) + * Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES *****************************************************************/ @@ -15,6 +16,143 @@ /* FUNCTIONS ****************************************************************/ +VOID +VfatCommonCloseFile( + PDEVICE_EXTENSION DeviceExt, + PVFATFCB pFcb) +{ + /* Nothing to do for volumes */ + if (BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME)) + { + return; + } + + /* If cache is still initialized, release it + * This only affects directories + */ + if (pFcb->OpenHandleCount == 0 && BooleanFlagOn(pFcb->Flags, FCB_CACHE_INITIALIZED)) + { + PFILE_OBJECT tmpFileObject; + tmpFileObject = pFcb->FileObject; + if (tmpFileObject != NULL) + { + pFcb->FileObject = NULL; + CcUninitializeCacheMap(tmpFileObject, NULL, NULL); + ClearFlag(pFcb->Flags, FCB_CACHE_INITIALIZED); + ObDereferenceObject(tmpFileObject); + } + } + +#ifdef KDBG + pFcb->Flags |= FCB_CLOSED; +#endif + + /* Release the FCB, we likely cause its deletion */ + vfatReleaseFCB(DeviceExt, pFcb); +} + +VOID +NTAPI +VfatCloseWorker( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context) +{ + PLIST_ENTRY Entry; + PVFATFCB pFcb; + PDEVICE_EXTENSION Vcb; + PVFAT_CLOSE_CONTEXT CloseContext; + BOOLEAN ConcurrentDeletion; + + /* Start removing work items */ + ExAcquireFastMutex(&VfatGlobalData->CloseMutex); + while (!IsListEmpty(&VfatGlobalData->CloseListHead)) + { + Entry = RemoveHeadList(&VfatGlobalData->CloseListHead); + CloseContext = CONTAINING_RECORD(Entry, VFAT_CLOSE_CONTEXT, CloseListEntry); + + /* One less */ + --VfatGlobalData->CloseCount; + /* Reset its entry to detect concurrent deletions */ + InitializeListHead(&CloseContext->CloseListEntry); + ExReleaseFastMutex(&VfatGlobalData->CloseMutex); + + /* Get the elements */ + Vcb = CloseContext->Vcb; + pFcb = CloseContext->Fcb; + ExAcquireResourceExclusiveLite(&Vcb->DirResource, TRUE); + /* If it didn't got deleted in between */ + if (BooleanFlagOn(pFcb->Flags, FCB_DELAYED_CLOSE)) + { + /* Close it! */ + DPRINT("Late closing: %wZ\n", &pFcb->PathNameU); + ClearFlag(pFcb->Flags, FCB_DELAYED_CLOSE); + pFcb->CloseContext = NULL; + VfatCommonCloseFile(Vcb, pFcb); + ConcurrentDeletion = FALSE; + } + else + { + /* Otherwise, mark not to delete it */ + ConcurrentDeletion = TRUE; + } + ExReleaseResourceLite(&Vcb->DirResource); + + /* If we were the fastest, delete the context */ + if (!ConcurrentDeletion) + { + ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext); + } + + /* Lock again the list */ + ExAcquireFastMutex(&VfatGlobalData->CloseMutex); + } + + /* We're done, bye! */ + VfatGlobalData->CloseWorkerRunning = FALSE; + ExReleaseFastMutex(&VfatGlobalData->CloseMutex); +} + +NTSTATUS +VfatPostCloseFile( + PDEVICE_EXTENSION DeviceExt, + PFILE_OBJECT FileObject) +{ + PVFAT_CLOSE_CONTEXT CloseContext; + + /* Allocate a work item */ + CloseContext = ExAllocateFromPagedLookasideList(&VfatGlobalData->CloseContextLookasideList); + if (CloseContext == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Set relevant fields */ + CloseContext->Vcb = DeviceExt; + CloseContext->Fcb = FileObject->FsContext; + CloseContext->Fcb->CloseContext = CloseContext; + + /* Acquire the lock to insert in list */ + ExAcquireFastMutex(&VfatGlobalData->CloseMutex); + + /* One more element */ + InsertTailList(&VfatGlobalData->CloseListHead, &CloseContext->CloseListEntry); + ++VfatGlobalData->CloseCount; + + /* If we have more than 16 items in list, and no worker thread + * start a new one + */ + if (VfatGlobalData->CloseCount > 16 && !VfatGlobalData->CloseWorkerRunning) + { + VfatGlobalData->CloseWorkerRunning = TRUE; + IoQueueWorkItem(VfatGlobalData->CloseWorkItem, VfatCloseWorker, CriticalWorkQueue, NULL); + } + + /* We're done */ + ExReleaseFastMutex(&VfatGlobalData->CloseMutex); + + return STATUS_SUCCESS; +} + /* * FUNCTION: Closes a file */ @@ -25,7 +163,6 @@ VfatCloseFile( { PVFATFCB pFcb; PVFATCCB pCcb; - BOOLEAN IsVolume; NTSTATUS Status = STATUS_SUCCESS; DPRINT("VfatCloseFile(DeviceExt %p, FileObject %p)\n", @@ -40,44 +177,23 @@ VfatCloseFile( return STATUS_SUCCESS; } - IsVolume = BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME); - if (IsVolume) + if (pCcb) { - DPRINT("Volume\n"); - FileObject->FsContext2 = NULL; + vfatDestroyCCB(pCcb); } - else - { - if (pFcb->OpenHandleCount == 0 && BooleanFlagOn(pFcb->Flags, FCB_CACHE_INITIALIZED)) - { - PFILE_OBJECT tmpFileObject; - tmpFileObject = pFcb->FileObject; - if (tmpFileObject != NULL) - { - pFcb->FileObject = NULL; - CcUninitializeCacheMap(tmpFileObject, NULL, NULL); - ClearFlag(pFcb->Flags, FCB_CACHE_INITIALIZED); - ObDereferenceObject(tmpFileObject); - } - } -#ifdef KDBG - pFcb->Flags |= FCB_CLOSED; -#endif - vfatReleaseFCB(DeviceExt, pFcb); + /* If we have to close immediately, or if delaying failed, close */ + if (!BooleanFlagOn(pFcb->Flags, FCB_DELAYED_CLOSE) || !NT_SUCCESS(VfatPostCloseFile(DeviceExt, FileObject))) + { + VfatCommonCloseFile(DeviceExt, pFcb); } FileObject->FsContext2 = NULL; FileObject->FsContext = NULL; FileObject->SectionObjectPointer = NULL; - if (pCcb) - { - vfatDestroyCCB(pCcb); - } - #ifdef ENABLE_SWAPOUT - if (IsVolume && DeviceExt->OpenHandleCount == 0) + if (BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME) && DeviceExt->OpenHandleCount == 0) { VfatCheckForDismount(DeviceExt, FALSE); } diff --git a/drivers/filesystems/fastfat/create.c b/drivers/filesystems/fastfat/create.c index e2fbccaf9c8..6dbf032e6e6 100644 --- a/drivers/filesystems/fastfat/create.c +++ b/drivers/filesystems/fastfat/create.c @@ -370,6 +370,44 @@ VfatOpenFile( return STATUS_CANNOT_DELETE; } + /* If that one was marked for closing, remove it */ + if (BooleanFlagOn(Fcb->Flags, FCB_DELAYED_CLOSE)) + { + BOOLEAN ConcurrentDeletion; + PVFAT_CLOSE_CONTEXT CloseContext; + + /* Get the context */ + CloseContext = Fcb->CloseContext; + /* Is someone already taking over? */ + if (CloseContext != NULL) + { + ConcurrentDeletion = FALSE; + /* Lock list */ + ExAcquireFastMutex(&VfatGlobalData->CloseMutex); + /* Check whether it was already removed, if not, do it */ + if (!IsListEmpty(&CloseContext->CloseListEntry)) + { + RemoveEntryList(&CloseContext->CloseListEntry); + --VfatGlobalData->CloseCount; + ConcurrentDeletion = TRUE; + } + ExReleaseFastMutex(&VfatGlobalData->CloseMutex); + + /* It's not delayed anymore! */ + ClearFlag(Fcb->Flags, FCB_DELAYED_CLOSE); + /* Release the extra reference (would have been removed by IRP_MJ_CLOSE) */ + vfatReleaseFCB(DeviceExt, Fcb); + Fcb->CloseContext = NULL; + /* If no concurrent deletion, free work item */ + if (!ConcurrentDeletion) + { + ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext); + } + } + + DPRINT("Reusing delayed close FCB for %wZ\n", &Fcb->PathNameU); + } + DPRINT("Attaching FCB to fileObject\n"); Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, FileObject); if (!NT_SUCCESS(Status)) diff --git a/drivers/filesystems/fastfat/iface.c b/drivers/filesystems/fastfat/iface.c index 41653a25493..e99409c6695 100644 --- a/drivers/filesystems/fastfat/iface.c +++ b/drivers/filesystems/fastfat/iface.c @@ -21,6 +21,7 @@ * FILE: drivers/fs/vfat/iface.c * PURPOSE: VFAT Filesystem * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com) + * Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES *****************************************************************/ @@ -95,6 +96,18 @@ DriverEntry( * has been detected: VfatGlobalData->Flags = VFAT_BREAK_ON_CORRUPTION; */ + /* Delayed close support */ + ExInitializeFastMutex(&VfatGlobalData->CloseMutex); + InitializeListHead(&VfatGlobalData->CloseListHead); + VfatGlobalData->CloseCount = 0; + VfatGlobalData->CloseWorkerRunning = FALSE; + VfatGlobalData->CloseWorkItem = IoAllocateWorkItem(DeviceObject); + if (VfatGlobalData->CloseWorkItem == NULL) + { + IoDeleteDevice(DeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + DeviceObject->Flags |= DO_DIRECT_IO; DriverObject->MajorFunction[IRP_MJ_CLOSE] = VfatBuildRequest; DriverObject->MajorFunction[IRP_MJ_CREATE] = VfatBuildRequest; @@ -132,6 +145,8 @@ DriverEntry( NULL, NULL, 0, sizeof(VFATCCB), TAG_CCB, 0); ExInitializeNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList, NULL, NULL, 0, sizeof(VFAT_IRP_CONTEXT), TAG_IRP, 0); + ExInitializePagedLookasideList(&VfatGlobalData->CloseContextLookasideList, + NULL, NULL, 0, sizeof(VFAT_CLOSE_CONTEXT), TAG_CLOSE, 0); ExInitializeResourceLite(&VfatGlobalData->VolumeListLock); InitializeListHead(&VfatGlobalData->VolumeListHead); diff --git a/drivers/filesystems/fastfat/kdbg.c b/drivers/filesystems/fastfat/kdbg.c index b278329299c..ef2ded50de4 100644 --- a/drivers/filesystems/fastfat/kdbg.c +++ b/drivers/filesystems/fastfat/kdbg.c @@ -109,10 +109,11 @@ vfatKdbgHandler( ListEntry = ListEntry->Flink) { Fcb = CONTAINING_RECORD(ListEntry, VFATFCB, FcbListEntry); - DPRINT1("FCB %p (ref: %d, oc: %d %s %s) for FO %p with path: %.*S\n", + DPRINT1("FCB %p (ref: %d, oc: %d %s %s %s) for FO %p with path: %.*S\n", Fcb, Fcb->RefCount, Fcb->OpenHandleCount, ((Fcb->Flags & FCB_CLEANED_UP) ? "U" : "NU"), ((Fcb->Flags & FCB_CLOSED) ? "C" : "NC"), + ((Fcb->Flags & FCB_DELAYED_CLOSE) ? "D" : "ND"), Fcb->FileObject, Fcb->PathNameU.Length, Fcb->PathNameU.Buffer); } } diff --git a/drivers/filesystems/fastfat/vfat.h b/drivers/filesystems/fastfat/vfat.h index bec3dc83941..f03d5139525 100644 --- a/drivers/filesystems/fastfat/vfat.h +++ b/drivers/filesystems/fastfat/vfat.h @@ -269,6 +269,7 @@ typedef struct struct _VFATFCB; struct _VFAT_DIRENTRY_CONTEXT; struct _VFAT_MOVE_CONTEXT; +struct _VFAT_CLOSE_CONTEXT; typedef struct _HASHENTRY { @@ -408,8 +409,14 @@ typedef struct NPAGED_LOOKASIDE_LIST FcbLookasideList; NPAGED_LOOKASIDE_LIST CcbLookasideList; NPAGED_LOOKASIDE_LIST IrpContextLookasideList; + PAGED_LOOKASIDE_LIST CloseContextLookasideList; FAST_IO_DISPATCH FastIoDispatch; CACHE_MANAGER_CALLBACKS CacheMgrCallbacks; + FAST_MUTEX CloseMutex; + ULONG CloseCount; + LIST_ENTRY CloseListHead; + BOOLEAN CloseWorkerRunning; + PIO_WORKITEM CloseWorkItem; } VFAT_GLOBAL_DATA, *PVFAT_GLOBAL_DATA; extern PVFAT_GLOBAL_DATA VfatGlobalData; @@ -420,9 +427,10 @@ extern PVFAT_GLOBAL_DATA VfatGlobalData; #define FCB_IS_PAGE_FILE 0x0008 #define FCB_IS_VOLUME 0x0010 #define FCB_IS_DIRTY 0x0020 +#define FCB_DELAYED_CLOSE 0x0040 #ifdef KDBG -#define FCB_CLEANED_UP 0x0040 -#define FCB_CLOSED 0x0080 +#define FCB_CLEANED_UP 0x0080 +#define FCB_CLOSED 0x0100 #endif #define NODE_TYPE_FCB ((CSHORT)0x0502) @@ -510,6 +518,8 @@ typedef struct _VFATFCB FAST_MUTEX LastMutex; ULONG LastCluster; ULONG LastOffset; + + struct _VFAT_CLOSE_CONTEXT * CloseContext; } VFATFCB, *PVFATFCB; #define CCB_DELETE_ON_CLOSE 0x0001 @@ -528,6 +538,7 @@ typedef struct _VFATCCB #define TAG_FCB 'BCFV' #define TAG_IRP 'PRIV' #define TAG_VFAT 'TAFV' +#define TAG_CLOSE 'xtaF' #define ENTRIES_PER_SECTOR (BLOCKSIZE / sizeof(FATDirEntry)) @@ -588,6 +599,13 @@ typedef struct _VFAT_MOVE_CONTEXT BOOLEAN InPlace; } VFAT_MOVE_CONTEXT, *PVFAT_MOVE_CONTEXT; +typedef struct _VFAT_CLOSE_CONTEXT +{ + PDEVICE_EXTENSION Vcb; + PVFATFCB Fcb; + LIST_ENTRY CloseListEntry; +} VFAT_CLOSE_CONTEXT, *PVFAT_CLOSE_CONTEXT; + FORCEINLINE NTSTATUS VfatMarkIrpContextForQueue(PVFAT_IRP_CONTEXT IrpContext) -- 2.17.1