/*++ Copyright (c) 1990 Microsoft Corporation Module Name: StrucSup.c Abstract: This module provides support routines for creation and deletion of Lfs structures. Author: Brian Andrew [BrianAn] 20-June-1991 Revision History: --*/ #include "lfsprocs.h" // // The debug trace level // #define Dbg (DEBUG_TRACE_STRUC_SUP) #undef MODULE_POOL_TAG #define MODULE_POOL_TAG ('SsfL') #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, LfsAllocateLbcb) #pragma alloc_text(PAGE, LfsAllocateLfcb) #pragma alloc_text(PAGE, LfsDeallocateLbcb) #pragma alloc_text(PAGE, LfsDeallocateLfcb) #pragma alloc_text(PAGE, LfsAllocateLeb) #pragma alloc_text(PAGE, LfsDeallocateLeb) #pragma alloc_text(PAGE, LfsReadPage) #endif PLFCB LfsAllocateLfcb ( IN ULONG LogPageSize, IN LONGLONG FileSize ) /*++ Routine Description: This routine allocates and initializes a log file control block. Arguments: LogPageSize - lfs log file page size FileSize - Initial file size Return Value: PLFCB - A pointer to the log file control block just allocated and initialized. --*/ { PLFCB Lfcb = NULL; ULONG Count; PLBCB NextLbcb; PLEB NextLeb; PAGED_CODE(); DebugTrace( +1, Dbg, "LfsAllocateLfcb: Entered\n", 0 ); // // Use a try-finally to facilitate cleanup. // try { // // Allocate and zero the structure for the Lfcb. // ASSERT( LogPageSize <= PAGE_SIZE ); Lfcb = LfsAllocatePool( PagedPool, sizeof( LFCB ) + sizeof( PLBCB ) * (PAGE_SIZE / LogPageSize) ); // // Zero out the structure initially. // RtlZeroMemory( Lfcb, sizeof( LFCB ) + sizeof( PLBCB ) * (PAGE_SIZE / LogPageSize)); // // Initialize the log file control block. // Lfcb->NodeTypeCode = LFS_NTC_LFCB; Lfcb->NodeByteSize = sizeof( LFCB ); // // Initialize the client links. // InitializeListHead( &Lfcb->LchLinks ); // // Initialize the Lbcb links. // InitializeListHead( &Lfcb->LbcbWorkque ); InitializeListHead( &Lfcb->LbcbActive ); // // Initialize and allocate the spare Lbcb queue. // InitializeListHead( &Lfcb->SpareLbcbList ); for (Count = 0; Count < LFCB_RESERVE_LBCB_COUNT; Count++) { NextLbcb = ExAllocatePoolWithTag( PagedPool, sizeof( LBCB ), ' sfL' ); if (NextLbcb != NULL) { InsertHeadList( &Lfcb->SpareLbcbList, (PLIST_ENTRY) NextLbcb ); Lfcb->SpareLbcbCount += 1; } } // // Initialize and allocate the spare Leb queue. // InitializeListHead( &Lfcb->SpareLebList ); for (Count = 0; Count < LFCB_RESERVE_LEB_COUNT; Count++) { NextLeb = ExAllocatePoolWithTag( PagedPool, sizeof( LEB ), ' sfL' ); if (NextLeb != NULL) { InsertHeadList( &Lfcb->SpareLebList, (PLIST_ENTRY) NextLeb ); Lfcb->SpareLebCount += 1; } } // // Allocate the Lfcb synchronization event. // Lfcb->Sync = LfsAllocatePool( NonPagedPool, sizeof( LFCB_SYNC )); ExInitializeResourceLite( &Lfcb->Sync->Resource ); // // Initialize the pseudo Lsn for the restart Lbcb's // Lfcb->NextRestartLsn = LfsLi1; // // Initialize the event to the signalled state. // KeInitializeEvent( &Lfcb->Sync->Event, NotificationEvent, TRUE ); Lfcb->Sync->UserCount = 0; // // Initialize the spare list mutex // ExInitializeFastMutex( &(Lfcb->Sync->SpareListMutex) ); Lfcb->FileSize = FileSize; } finally { DebugUnwind( LfsAllocateFileControlBlock ); if (AbnormalTermination() && (Lfcb != NULL)) { LfsDeallocateLfcb( Lfcb, TRUE ); Lfcb = NULL; } DebugTrace( -1, Dbg, "LfsAllocateLfcb: Exit -> %08lx\n", Lfcb ); } return Lfcb; } VOID LfsDeallocateLfcb ( IN PLFCB Lfcb, IN BOOLEAN CompleteTeardown ) /*++ Routine Description: This routine releases the resources associated with a log file control block. Arguments: Lfcb - Supplies a pointer to the log file control block. CompleteTeardown - Indicates if we are to completely remove this Lfcb. Return Value: None --*/ { PLBCB NextLbcb; PLEB NextLeb; PAGED_CODE(); DebugTrace( +1, Dbg, "LfsDeallocateLfcb: Entered\n", 0 ); DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb ); // // Check that there are no buffer blocks. // ASSERT( IsListEmpty( &Lfcb->LbcbActive )); ASSERT( IsListEmpty( &Lfcb->LbcbWorkque )); // // Check that we have no clients. // ASSERT( IsListEmpty( &Lfcb->LchLinks )); // // If there is a restart area we deallocate it. // if (Lfcb->RestartArea != NULL) { LfsDeallocateRestartArea( Lfcb->RestartArea ); } // // If there are any of the tail Lbcb's, deallocate them now. // if (Lfcb->ActiveTail != NULL) { LfsDeallocateLbcb( Lfcb, Lfcb->ActiveTail ); Lfcb->ActiveTail = NULL; } if (Lfcb->PrevTail != NULL) { LfsDeallocateLbcb( Lfcb, Lfcb->PrevTail ); Lfcb->PrevTail = NULL; } // // Only do the following if we are to remove the Lfcb completely. // if (CompleteTeardown) { // // If there is a resource structure we deallocate it. // if (Lfcb->Sync != NULL) { #ifdef BENL_DBG KdPrint(( "LFS: lfcb teardown: 0x%x 0x%x\n", Lfcb, Lfcb->Sync )); #endif ExDeleteResourceLite( &Lfcb->Sync->Resource ); ExFreePool( Lfcb->Sync ); } } // // Deallocate all of the spare Lbcb's. // while (!IsListEmpty( &Lfcb->SpareLbcbList )) { NextLbcb = (PLBCB) Lfcb->SpareLbcbList.Flink; RemoveHeadList( &Lfcb->SpareLbcbList ); ExFreePool( NextLbcb ); } // // Deallocate all of the spare Leb's. // while (!IsListEmpty( &Lfcb->SpareLebList )) { NextLeb = (PLEB) Lfcb->SpareLebList.Flink; RemoveHeadList( &Lfcb->SpareLebList ); ExFreePool( NextLeb ); } // // Cleanup the log head mdls and buffer // if (Lfcb->LogHeadBuffer) { LfsFreePool( Lfcb->LogHeadBuffer ); } if (Lfcb->LogHeadPartialMdl) { IoFreeMdl( Lfcb->LogHeadPartialMdl ); } if (Lfcb->LogHeadMdl) { IoFreeMdl( Lfcb->LogHeadMdl ); } if (Lfcb->ErrorLogPacket) { IoFreeErrorLogEntry( Lfcb->ErrorLogPacket ); Lfcb->ErrorLogPacket = NULL; } // // Discard the Lfcb structure. // ExFreePool( Lfcb ); DebugTrace( -1, Dbg, "LfsDeallocateLfcb: Exit\n", 0 ); return; } VOID LfsAllocateLbcb ( IN PLFCB Lfcb, OUT PLBCB *Lbcb ) /*++ Routine Description: This routine will allocate the next Lbcb. If the pool allocation fails we will look at the private queue of Lbcb's. Arguments: Lfcb - Supplies a pointer to the log file control block. Lbcb - Address to store the allocated Lbcb. Return Value: None --*/ { PLBCB NewLbcb = NULL; PAGED_CODE(); // // If there are enough entries on the look-aside list then get one from // there. // if (Lfcb->SpareLbcbCount > LFCB_RESERVE_LBCB_COUNT) { NewLbcb = (PLBCB) Lfcb->SpareLbcbList.Flink; Lfcb->SpareLbcbCount -= 1; RemoveHeadList( &Lfcb->SpareLbcbList ); // // Otherwise try to allocate from pool. // } else { NewLbcb = ExAllocatePoolWithTag( PagedPool, sizeof( LBCB ), ' sfL' ); } // // If we didn't get one then look at the look-aside list. // if (NewLbcb == NULL) { if (Lfcb->SpareLbcbCount != 0) { NewLbcb = (PLBCB) Lfcb->SpareLbcbList.Flink; Lfcb->SpareLbcbCount -= 1; RemoveHeadList( &Lfcb->SpareLbcbList ); } else { ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); } } // // Initialize the structure. // RtlZeroMemory( NewLbcb, sizeof( LBCB )); NewLbcb->NodeTypeCode = LFS_NTC_LBCB; NewLbcb->NodeByteSize = sizeof( LBCB ); // // Return it to the user. // *Lbcb = NewLbcb; return; } VOID LfsDeallocateLbcb ( IN PLFCB Lfcb, IN PLBCB Lbcb ) /*++ Routine Description: This routine will deallocate the Lbcb. If we need one for the look-aside list we will put it there. Arguments: Lfcb - Supplies a pointer to the log file control block. Lbcb - This is the Lbcb to deallocate. Return Value: None --*/ { PAGED_CODE(); // // Deallocate any restart area attached to this Lbcb. // if (FlagOn( Lbcb->LbcbFlags, LBCB_RESTART_LBCB ) && (Lbcb->PageHeader != NULL)) { LfsDeallocateRestartArea( Lbcb->PageHeader ); } // // Put this in the Lbcb queue if it is short. // if (Lfcb->SpareLbcbCount < LFCB_MAX_LBCB_COUNT) { InsertHeadList( &Lfcb->SpareLbcbList, (PLIST_ENTRY) Lbcb ); Lfcb->SpareLbcbCount += 1; // // Otherwise just free the pool block. // } else { ExFreePool( Lbcb ); } return; } VOID LfsAllocateLeb ( IN PLFCB Lfcb, OUT PLEB *NewLeb ) /*++ Routine Description: This routine will allocate an Leb. If the pool fails we will fall back on our spare list. A failure then will result in an exception Arguments: Lfcb - Supplies a pointer to the log file control block. Leb - This will contain the new Leb Return Value: None --*/ { ExAcquireFastMutex( &(Lfcb->Sync->SpareListMutex) ); try { *NewLeb = NULL; if (Lfcb->SpareLebCount < LFCB_RESERVE_LEB_COUNT) { (*NewLeb) = ExAllocatePoolWithTag( PagedPool, sizeof( LEB ), ' sfL' ); } if ((*NewLeb) == NULL) { if (Lfcb->SpareLebCount > 0) { *NewLeb = (PLEB) Lfcb->SpareLebList.Flink; Lfcb->SpareLebCount -= 1; RemoveHeadList( &Lfcb->SpareLebList ); } else { ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); } } RtlZeroMemory( (*NewLeb), sizeof( LEB ) ); (*NewLeb)->NodeTypeCode = LFS_NTC_LEB; (*NewLeb)->NodeByteSize = sizeof( LEB ); } finally { ExReleaseFastMutex( &(Lfcb->Sync->SpareListMutex) ); } } VOID LfsDeallocateLeb ( IN PLFCB Lfcb, IN PLEB Leb ) /*++ Routine Description: This routine will deallocate an Leb. We'll cache the old Leb if there aren't too many already on the spare list Arguments: Lfcb - Supplies a pointer to the log file control block. Leb - This will contain the Leb to release Return Value: None --*/ { if (Leb->RecordHeaderBcb != NULL) { CcUnpinData( Leb->RecordHeaderBcb ); } if ((Leb->CurrentLogRecord != NULL) && Leb->AuxilaryBuffer) { LfsFreeSpanningBuffer( Leb->CurrentLogRecord ); } ExAcquireFastMutex( &(Lfcb->Sync->SpareListMutex) ); try { if (Lfcb->SpareLebCount < LFCB_MAX_LEB_COUNT) { InsertHeadList( &Lfcb->SpareLebList, (PLIST_ENTRY) Leb ); Lfcb->SpareLebCount += 1; } else { ExFreePool( Leb ); } } finally { ExReleaseFastMutex( &(Lfcb->Sync->SpareListMutex) ); } } VOID LfsReadPage ( IN PLFCB Lfcb, IN PLARGE_INTEGER Offset, OUT PMDL *Mdl, OUT PVOID *Buffer ) /*++ Routine Description: Directly pages in a page off the disk - the cache manager interfaces (LfsPinOrMapPage) may come from the cache. This wil raise if memory can't be allocated and used for verification purposes Arguments: Lfcb - Supplies a pointer to the log file control block. Offset - offset of page to pagein from the logfile Mdl - On success the mdl that describes the mdl - it must be deallocated via IoFreeMdl Buffer - On output an allocated buffer that holds the data from the page - it must be freed using ExFreePool Return Value: None --*/ { IO_STATUS_BLOCK Iosb; KEVENT Event; NTSTATUS Status; PAGED_CODE(); KeInitializeEvent( &Event, NotificationEvent, FALSE ); // // Allocate buffer / mdl and page in the restart page from disk // *Buffer = LfsAllocatePool( NonPagedPool, (ULONG)Lfcb->LogPageSize ); *Mdl = IoAllocateMdl( *Buffer, (ULONG)Lfcb->LogPageSize, FALSE, FALSE, NULL ); if (*Mdl == NULL) { ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); } MmBuildMdlForNonPagedPool( *Mdl ); // // We own the LFCB sync exclusively and there is only a main resource for the logfile // so we don't need to preacquire any resources before doing the page read // Status = IoPageRead( Lfcb->FileObject, *Mdl, Offset, &Event, &Iosb ); if (Status == STATUS_PENDING) { KeWaitForSingleObject( &Event, WrPageIn, KernelMode, FALSE, (PLARGE_INTEGER)NULL); } }