From e68f31b46ad43d2cc73f436e3350638aee478159 Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Thu, 12 Aug 2010 21:59:22 +0000 Subject: [PATCH] [FASTFAT_NEW] - Implement asynchronous and delayed close operations. - Fix a logical bug in FatiClose code, which led to always closing the file object (and freeing respective FCB/VCB/CCB), and then checking flags and queuing a delayed close. Instead, it should only try to close if it's not marked as a delayed close. - Support stuff added (queues, mutex for closing lists, etc). - Misc cleanup, debug silencing. svn path=/trunk/; revision=48525 --- .../drivers/filesystems/fastfat_new/close.c | 317 +++++++++++++++++- reactos/drivers/filesystems/fastfat_new/dir.c | 7 - .../drivers/filesystems/fastfat_new/fastfat.c | 7 + .../drivers/filesystems/fastfat_new/fastfat.h | 3 + reactos/drivers/filesystems/fastfat_new/fat.c | 7 + .../filesystems/fastfat_new/fatstruc.h | 69 ++-- .../drivers/filesystems/fastfat_new/fsctl.c | 2 +- 7 files changed, 366 insertions(+), 46 deletions(-) diff --git a/reactos/drivers/filesystems/fastfat_new/close.c b/reactos/drivers/filesystems/fastfat_new/close.c index 0661aeeb912..69ccf3c8c43 100644 --- a/reactos/drivers/filesystems/fastfat_new/close.c +++ b/reactos/drivers/filesystems/fastfat_new/close.c @@ -11,6 +11,16 @@ #define NDEBUG #include "fastfat.h" +VOID NTAPI +FatQueueClose(IN PCLOSE_CONTEXT CloseContext, + IN BOOLEAN DelayClose); + +PCLOSE_CONTEXT NTAPI +FatRemoveClose(PVCB Vcb OPTIONAL, + PVCB LastVcbHint OPTIONAL); + +const ULONG FatMaxDelayedCloseCount = 16; + /* FUNCTIONS ****************************************************************/ NTSTATUS @@ -246,8 +256,8 @@ FatiClose(IN PFAT_IRP_CONTEXT IrpContext, PVCB Vcb; PFCB Fcb; PCCB Ccb; - BOOLEAN TopLevel, Wait, VcbDeleted = FALSE; - NTSTATUS Status; + BOOLEAN TopLevel, Wait, VcbDeleted = FALSE, DelayedClose = FALSE; + NTSTATUS Status = STATUS_SUCCESS; PCLOSE_CONTEXT CloseContext = NULL; TopLevel = FatIsTopLevelIrp(Irp); @@ -265,16 +275,57 @@ FatiClose(IN PFAT_IRP_CONTEXT IrpContext, /* It's possible to wait only if we are top level or not a system process */ Wait = TopLevel && (PsGetCurrentProcess() != FatGlobalData.SystemProcess); - /* Call the common handler */ - Status = FatiCommonClose(Vcb, Fcb, Ccb, TypeOfOpen, Wait, &VcbDeleted); - - if (((TypeOfOpen == UserFileOpen || - TypeOfOpen == UserDirectoryOpen) && + /* Determine if it's a delayed close, by flags first */ + if ((TypeOfOpen == UserFileOpen || TypeOfOpen == UserDirectoryOpen) && (Fcb->State & FCB_STATE_DELAY_CLOSE) && - !FatGlobalData.ShutdownStarted) || - Status == STATUS_PENDING) + !FatGlobalData.ShutdownStarted) { - DPRINT1("TODO: Queue a pending close request\n"); + DelayedClose = TRUE; + } + + /* If close is not delayed, try to perform the close operation */ + if (!DelayedClose) + Status = FatiCommonClose(Vcb, Fcb, Ccb, TypeOfOpen, Wait, &VcbDeleted); + + /* We have to delay close if either it's defined by a flag or it was not possible + to perform it synchronously */ + if (DelayedClose || Status == STATUS_PENDING) + { + DPRINT1("Queuing a pending close, Vcb %p, Fcb %p, Ccb %p\n", Vcb, Fcb, Ccb); + + /* Check if a close context should be allocated */ + if (TypeOfOpen == VirtualVolumeFile) + { + ASSERT(Vcb->CloseContext != NULL); + CloseContext = Vcb->CloseContext; + Vcb->CloseContext = NULL; + CloseContext->Free = TRUE; + } + else if (TypeOfOpen == DirectoryFile || + TypeOfOpen == EaFile) + { + UNIMPLEMENTED; + //CloseContext = FatAllocateCloseContext(Vcb); + //ASSERT(CloseContext != NULL); + CloseContext->Free = TRUE; + } + else + { + //TODO: FatDeallocateCcbStrings( Ccb ); + + /* Set CloseContext to a buffer inside Ccb */ + CloseContext = &Ccb->CloseContext; + CloseContext->Free = FALSE; + SetFlag(Ccb->Flags, CCB_CLOSE_CONTEXT); + } + + /* Save all info in the close context */ + CloseContext->Vcb = Vcb; + CloseContext->Fcb = Fcb; + CloseContext->TypeOfOpen = TypeOfOpen; + + /* Queue the close */ + FatQueueClose(CloseContext, (BOOLEAN)(Fcb && FlagOn(Fcb->State, FCB_STATE_DELAY_CLOSE))); } else { @@ -351,4 +402,250 @@ FatClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) return Status; } +VOID +NTAPI +FatPendingClose(IN PVCB Vcb OPTIONAL) +{ + PCLOSE_CONTEXT CloseContext; + PVCB CurrentVcb = NULL; + PVCB LastVcb = NULL; + BOOLEAN FreeContext; + ULONG Loops = 0; + + /* Do the top-level IRP trick */ + if (!Vcb) IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP); + + while ((CloseContext = FatRemoveClose(Vcb, LastVcb))) + { + if (!Vcb) + { + if (!FatGlobalData.ShutdownStarted) + { + if (CloseContext->Vcb != CurrentVcb) + { + Loops = 0; + + /* Release previous VCB */ + if (CurrentVcb) + ExReleaseResourceLite(&CurrentVcb->Resource); + + /* Lock the new VCB */ + CurrentVcb = CloseContext->Vcb; + (VOID)ExAcquireResourceExclusiveLite(&CurrentVcb->Resource, TRUE); + } + else + { + /* Try to lock */ + if (++Loops >= 20) + { + if (ExGetSharedWaiterCount(&CurrentVcb->Resource) + + ExGetExclusiveWaiterCount(&CurrentVcb->Resource)) + { + ExReleaseResourceLite(&CurrentVcb->Resource); + (VOID)ExAcquireResourceExclusiveLite(&CurrentVcb->Resource, TRUE); + } + + Loops = 0; + } + } + + /* Check open count */ + if (CurrentVcb->OpenFileCount <= 1) + { + ExReleaseResourceLite(&CurrentVcb->Resource); + CurrentVcb = NULL; + } + } + else if (CurrentVcb) + { + ExReleaseResourceLite(&CurrentVcb->Resource); + CurrentVcb = NULL; + } + } + + LastVcb = CurrentVcb; + + /* Remember if we should free the context */ + FreeContext = CloseContext->Free; + + FatiCommonClose(CloseContext->Vcb, + CloseContext->Fcb, + (FreeContext ? NULL : CONTAINING_RECORD(CloseContext, CCB, CloseContext)), + CloseContext->TypeOfOpen, + TRUE, + NULL); + + /* Free context if necessary */ + if (FreeContext) ExFreePool(CloseContext); + } + + /* Release VCB if necessary */ + if (CurrentVcb) ExReleaseResourceLite(&CurrentVcb->Resource); + + /* Reset top level IRP */ + if (!Vcb) IoSetTopLevelIrp( NULL ); +} + +VOID +NTAPI +FatCloseWorker(IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context) +{ + FsRtlEnterFileSystem(); + + FatPendingClose((PVCB)Context); + + FsRtlExitFileSystem(); +} + +VOID +NTAPI +FatQueueClose(IN PCLOSE_CONTEXT CloseContext, + IN BOOLEAN DelayClose) +{ + BOOLEAN RunWorker = FALSE; + + /* Acquire the close lists mutex */ + ExAcquireFastMutexUnsafe(&FatCloseQueueMutex); + + /* Add it to the desired list */ + if (DelayClose) + { + InsertTailList(&FatGlobalData.DelayedCloseList, + &CloseContext->GlobalLinks); + InsertTailList(&CloseContext->Vcb->DelayedCloseList, + &CloseContext->VcbLinks); + + FatGlobalData.DelayedCloseCount++; + + if (FatGlobalData.DelayedCloseCount > FatMaxDelayedCloseCount && + !FatGlobalData.AsyncCloseActive) + { + FatGlobalData.AsyncCloseActive = TRUE; + RunWorker = TRUE; + } + } + else + { + InsertTailList(&FatGlobalData.AsyncCloseList, + &CloseContext->GlobalLinks); + InsertTailList(&CloseContext->Vcb->AsyncCloseList, + &CloseContext->VcbLinks); + + FatGlobalData.AsyncCloseCount++; + + if (!FatGlobalData.AsyncCloseActive) + { + FatGlobalData.AsyncCloseActive = TRUE; + RunWorker = TRUE; + } + } + + /* Release the close lists mutex */ + ExReleaseFastMutexUnsafe(&FatCloseQueueMutex); + + if (RunWorker) + IoQueueWorkItem(FatGlobalData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL); +} + +PCLOSE_CONTEXT +NTAPI +FatRemoveClose(PVCB Vcb OPTIONAL, + PVCB LastVcbHint OPTIONAL) +{ + PLIST_ENTRY Entry; + PCLOSE_CONTEXT CloseContext; + BOOLEAN IsWorker = FALSE; + + /* Acquire the close lists mutex */ + ExAcquireFastMutexUnsafe(&FatCloseQueueMutex); + + if (!Vcb) IsWorker = TRUE; + + if (Vcb == NULL && LastVcbHint != NULL) + { + // TODO: A very special case of overflowing the queue + UNIMPLEMENTED; + } + + /* Usual processing from a worker thread */ + if (!Vcb) + { +TryToCloseAgain: + + /* Is there anything in the async close list */ + if (!IsListEmpty(&FatGlobalData.AsyncCloseList)) + { + Entry = RemoveHeadList(&FatGlobalData.AsyncCloseList); + FatGlobalData.AsyncCloseCount--; + + CloseContext = CONTAINING_RECORD(Entry, + CLOSE_CONTEXT, + GlobalLinks); + + RemoveEntryList(&CloseContext->VcbLinks); + } else if (!IsListEmpty(&FatGlobalData.DelayedCloseList) && + (FatGlobalData.DelayedCloseCount > FatMaxDelayedCloseCount/2 || + FatGlobalData.ShutdownStarted)) + { + /* In case of a shutdown or when delayed queue is filled at half - perform closing */ + Entry = RemoveHeadList(&FatGlobalData.DelayedCloseList); + FatGlobalData.DelayedCloseCount--; + + CloseContext = CONTAINING_RECORD(Entry, + CLOSE_CONTEXT, + GlobalLinks); + RemoveEntryList(&CloseContext->VcbLinks); + } + else + { + /* Nothing to close */ + CloseContext = NULL; + if (IsWorker) FatGlobalData.AsyncCloseActive = FALSE; + } + } + else + { + if (!IsListEmpty(&Vcb->AsyncCloseList)) + { + /* Is there anything in the async close list */ + Entry = RemoveHeadList(&Vcb->AsyncCloseList); + FatGlobalData.AsyncCloseCount--; + + CloseContext = CONTAINING_RECORD(Entry, + CLOSE_CONTEXT, + VcbLinks); + + RemoveEntryList(&CloseContext->GlobalLinks); + } + else if (!IsListEmpty(&Vcb->DelayedCloseList)) + { + /* Process delayed close list */ + Entry = RemoveHeadList(&Vcb->DelayedCloseList); + FatGlobalData.DelayedCloseCount--; + + CloseContext = CONTAINING_RECORD(Entry, + CLOSE_CONTEXT, + VcbLinks); + + RemoveEntryList(&CloseContext->GlobalLinks); + } + else if (LastVcbHint) + { + /* Try again */ + goto TryToCloseAgain; + } + else + { + /* Nothing to close */ + CloseContext = NULL; + } + } + + /* Release the close lists mutex */ + ExReleaseFastMutexUnsafe(&FatCloseQueueMutex); + + return CloseContext; +} + /* EOF */ diff --git a/reactos/drivers/filesystems/fastfat_new/dir.c b/reactos/drivers/filesystems/fastfat_new/dir.c index eb7df066c32..991971545bf 100644 --- a/reactos/drivers/filesystems/fastfat_new/dir.c +++ b/reactos/drivers/filesystems/fastfat_new/dir.c @@ -111,13 +111,6 @@ FatCreateRootDcb(IN PFAT_IRP_CONTEXT IrpContext, #endif UNIMPLEMENTED; } - - /* Initialize free dirent bitmap */ - RtlInitializeBitMap(&Dcb->Dcb.FreeBitmap, NULL, 0); - - /* Fill the dirent bitmap */ - DPRINT1("Filling the free dirent bitmap is missing\n"); - //FatCheckFreeDirentBitmap( IrpContext, Dcb ); } PFCB diff --git a/reactos/drivers/filesystems/fastfat_new/fastfat.c b/reactos/drivers/filesystems/fastfat_new/fastfat.c index 6b8e87ff12e..c469437b152 100644 --- a/reactos/drivers/filesystems/fastfat_new/fastfat.c +++ b/reactos/drivers/filesystems/fastfat_new/fastfat.c @@ -14,6 +14,7 @@ /* GLOBALS ******************************************************************/ FAT_GLOBAL_DATA FatGlobalData; +FAST_MUTEX FatCloseQueueMutex; /* FUNCTIONS ****************************************************************/ @@ -109,6 +110,12 @@ DriverEntry(PDRIVER_OBJECT DriverObject, /* Initialize synchronization resource for the global data */ ExInitializeResourceLite(&FatGlobalData.Resource); + /* Initialize queued close stuff */ + InitializeListHead(&FatGlobalData.AsyncCloseList); + InitializeListHead(&FatGlobalData.DelayedCloseList); + FatGlobalData.FatCloseItem = IoAllocateWorkItem(DeviceObject); + ExInitializeFastMutex(&FatCloseQueueMutex); + /* Initialize global VCB list */ InitializeListHead(&FatGlobalData.VcbListHead); diff --git a/reactos/drivers/filesystems/fastfat_new/fastfat.h b/reactos/drivers/filesystems/fastfat_new/fastfat.h index 7c93e2443c0..fbbf272c413 100644 --- a/reactos/drivers/filesystems/fastfat_new/fastfat.h +++ b/reactos/drivers/filesystems/fastfat_new/fastfat.h @@ -16,6 +16,7 @@ #define TAG_FCB 'BCFV' #define TAG_IRP 'PRIV' #define TAG_VFAT 'TAFV' +#define TAG_FSD_CLOSE_CONTEXT 'CLCV' /* Global resource acquire/release */ @@ -183,6 +184,8 @@ FatNoopRelease(IN PVOID Context); /* --------------------------------------------------------- fastfat.c */ +extern FAST_MUTEX FatCloseQueueMutex; + PFAT_IRP_CONTEXT NTAPI FatBuildIrpContext(PIRP Irp, BOOLEAN CanWait); diff --git a/reactos/drivers/filesystems/fastfat_new/fat.c b/reactos/drivers/filesystems/fastfat_new/fat.c index 147df8d1325..a6ac9fa9995 100644 --- a/reactos/drivers/filesystems/fastfat_new/fat.c +++ b/reactos/drivers/filesystems/fastfat_new/fat.c @@ -163,6 +163,10 @@ FatInitializeVcb(IN PFAT_IRP_CONTEXT IrpContext, /* Initialize VCB's resource */ ExInitializeResourceLite(&Vcb->Resource); + /* Initialize close queue lists */ + InitializeListHead(&Vcb->AsyncCloseList); + InitializeListHead(&Vcb->DelayedCloseList); + /* Initialize CC */ CcInitializeCacheMap(Vcb->StreamFileObject, (PCC_FILE_SIZES)&Vcb->Header.AllocationSize, @@ -243,6 +247,9 @@ FatUninitializeVcb(IN PVCB Vcb) Vcb->StreamFileObject = NULL; } + /* Free ContextClose if it's not freed up already */ + if (Vcb->CloseContext) ExFreePool(Vcb->CloseContext); + /* Free notifications stuff */ FsRtlNotifyUninitializeSync(&Vcb->NotifySync); diff --git a/reactos/drivers/filesystems/fastfat_new/fatstruc.h b/reactos/drivers/filesystems/fastfat_new/fatstruc.h index f484a211553..2ca2f54b8da 100644 --- a/reactos/drivers/filesystems/fastfat_new/fatstruc.h +++ b/reactos/drivers/filesystems/fastfat_new/fatstruc.h @@ -39,6 +39,16 @@ typedef struct _FAT_GLOBAL_DATA /* Jan 1, 1980 System Time */ LARGE_INTEGER DefaultFileTime; + /* Queued close */ + ULONG AsyncCloseCount; + ULONG DelayedCloseCount; + LIST_ENTRY AsyncCloseList; + LIST_ENTRY DelayedCloseList; + PIO_WORKITEM FatCloseItem; + + /* Various flags */ + BOOLEAN AsyncCloseActive; + /* FullFAT integration */ FF_IOMAN *Ioman; FF_ERROR FF_Error; @@ -171,6 +181,8 @@ typedef struct _VCB VCB_CONDITION Condition; ERESOURCE Resource; struct _CLOSE_CONTEXT *CloseContext; + LIST_ENTRY AsyncCloseList; + LIST_ENTRY DelayedCloseList; /* Direct volume access */ SHARE_ACCESS ShareAccess; @@ -239,6 +251,18 @@ typedef struct _VOLUME_DEVICE_OBJECT VCB Vcb; /* Must be the last entry! */ }; } VOLUME_DEVICE_OBJECT, *PVOLUME_DEVICE_OBJECT; + +typedef enum _TYPE_OF_OPEN +{ + UnopenedFileObject, + UserFileOpen, + UserDirectoryOpen, + UserVolumeOpen, + VirtualVolumeFile, + DirectoryFile, + EaFile +} TYPE_OF_OPEN; + // // Short name always exists in FAT // @@ -342,8 +366,6 @@ typedef struct _FCB PFILE_OBJECT DirectoryFile; /* Directory data stream (just handy to have it). */ //PFILE_OBJECT StreamFileObject; - /* Bitmap to search for free dirents. */ - RTL_BITMAP FreeBitmap; /* Names */ PRTL_SPLAY_LINKS SplayLinksAnsi; PRTL_SPLAY_LINKS SplayLinksUnicode; @@ -388,28 +410,6 @@ typedef struct _FAT_FIND_DIRENT_CONTEXT BOOLEAN Valid8dot3Name; } FAT_FIND_DIRENT_CONTEXT, *PFAT_FIND_DIRENT_CONTEXT; -typedef struct _CCB -{ - CSHORT NodeTypeCode; - CSHORT NodeByteSize; - - LARGE_INTEGER CurrentByteOffset; - ULONG Entry; - UNICODE_STRING SearchPattern; - UCHAR Flags; -} CCB, *PCCB; - -typedef enum _TYPE_OF_OPEN -{ - UnopenedFileObject, - UserFileOpen, - UserDirectoryOpen, - UserVolumeOpen, - VirtualVolumeFile, - DirectoryFile, - EaFile -} TYPE_OF_OPEN; - typedef struct _CLOSE_CONTEXT { LIST_ENTRY GlobalLinks; @@ -421,6 +421,18 @@ typedef struct _CLOSE_CONTEXT BOOLEAN Free; } CLOSE_CONTEXT, *PCLOSE_CONTEXT; +typedef struct _CCB +{ + CSHORT NodeTypeCode; + CSHORT NodeByteSize; + + LARGE_INTEGER CurrentByteOffset; + ULONG Entry; + UNICODE_STRING SearchPattern; + UCHAR Flags; + CLOSE_CONTEXT CloseContext; +} CCB, *PCCB; + typedef enum _FILE_TIME_INDEX { FileCreationTime = 0, @@ -432,9 +444,10 @@ typedef enum _FILE_TIME_INDEX #define CCB_SEARCH_RETURN_SINGLE_ENTRY 0x01 #define CCB_SEARCH_PATTERN_LEGAL_8DOT3 0x02 #define CCB_SEARCH_PATTERN_HAS_WILD_CARD 0x04 -#define CCB_DASD_IO 0x10 -#define CCB_READ_ONLY 0x20 -#define CCB_DELETE_ON_CLOSE 0x40 -#define CCB_COMPLETE_DISMOUNT 0x80 +#define CCB_DASD_IO 0x08 +#define CCB_READ_ONLY 0x10 +#define CCB_DELETE_ON_CLOSE 0x20 +#define CCB_COMPLETE_DISMOUNT 0x40 +#define CCB_CLOSE_CONTEXT 0x80 extern FAT_GLOBAL_DATA FatGlobalData; diff --git a/reactos/drivers/filesystems/fastfat_new/fsctl.c b/reactos/drivers/filesystems/fastfat_new/fsctl.c index 75374ab3c3f..540b417b8d8 100644 --- a/reactos/drivers/filesystems/fastfat_new/fsctl.c +++ b/reactos/drivers/filesystems/fastfat_new/fsctl.c @@ -171,7 +171,7 @@ FatUserFsCtrl(PFAT_IRP_CONTEXT IrpContext, PIRP Irp) break; default: - DPRINT1("FatUserFsCtrl(), unhandled fs control code 0x%x\n", Code); + DPRINT("FatUserFsCtrl(), unhandled fs control code 0x%x\n", Code); Status = STATUS_INVALID_DEVICE_REQUEST; FatCompleteRequest(IrpContext, Irp, Status); }