diff options
Diffstat (limited to 'private/ntos/lfs/lbcbsup.c')
-rw-r--r-- | private/ntos/lfs/lbcbsup.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/private/ntos/lfs/lbcbsup.c b/private/ntos/lfs/lbcbsup.c new file mode 100644 index 000000000..71e601730 --- /dev/null +++ b/private/ntos/lfs/lbcbsup.c @@ -0,0 +1,464 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + LbcbSup.c + +Abstract: + + This module provides support for manipulating log buffer control blocks. + +Author: + + Brian Andrew [BrianAn] 20-June-1991 + +Revision History: + +--*/ + +#include "lfsprocs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_LBCB_SUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, LfsFlushLbcb) +#pragma alloc_text(PAGE, LfsFlushToLsnPriv) +#pragma alloc_text(PAGE, LfsGetLbcb) +#endif + + +VOID +LfsFlushLbcb ( + IN PLFCB Lfcb, + IN PLBCB Lbcb + ) + +/*++ + +Routine Description: + + This routine is called to make sure the data within an Lbcb makes it out + to disk. The Lbcb must either already be in the workque or it must be + a restart Lbcb. + +Arguments: + + Lfcb - This is the file control block for the log file. + + Lbcb - This is the Lbcb to flush. + +Return Value: + + None. + +--*/ + +{ + LSN LastLsn; + PLSN FlushedLsn; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, "LfsFlushLbcb: Entered\n", 0 ); + DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb ); + DebugTrace( 0, Dbg, "Lbcb -> %08lx\n", Lbcb ); + + LastLsn = Lbcb->LastEndLsn; + + // + // If this is a restart area we use the restart counter in the + // Lfcb. Otherwise we can use the LastFlushedLsn value in the + // Lfcb. This way we can determine that the Lbcb that interests + // us has made it out to disk. + // + + if (LfsLbcbIsRestart( Lbcb )) { + + FlushedLsn = &Lfcb->LastFlushedRestartLsn; + + } else { + + FlushedLsn = &Lfcb->LastFlushedLsn; + } + + // + // We loop here until the desired Lsn has made it to disk. + // If we are able to do the I/O, we will perform it. + // + + do { + + // + // + // If we can do the Io, call down to flush the Lfcb. + // + + if (Lfcb->LfsIoState == LfsNoIoInProgress) { + + LfsFlushLfcb( Lfcb, Lbcb ); + + break; + } + + // + // Otherwise we release the Lfcb and immediately wait on the event. + // + + LfsReleaseLfcb( Lfcb ); + + KeWaitForSingleObject( &Lfcb->Sync->Event, + Executive, + KernelMode, + FALSE, + NULL ); + + LfsAcquireLfcb( Lfcb ); + + } while ( LastLsn.QuadPart > FlushedLsn->QuadPart ); + + DebugTrace( -1, Dbg, "LfsFlushLbcb: Exit\n", 0 ); + return; +} + + +VOID +LfsFlushToLsnPriv ( + IN PLFCB Lfcb, + IN LSN Lsn + ) + +/*++ + +Routine Description: + + This routine is the worker routine which performs the work of flushing + a particular Lsn to disk. This routine is always called with the + Lfcb acquired. This routines makes no guarantee about whether the Lfcb + is acquired on exit. + +Arguments: + + Lfcb - This is the file control block for the log file. + + Lsn - This is the Lsn to flush to disk. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace( +1, Dbg, "LfsFlushToLsnPriv: Entered\n", 0 ); + DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb ); + DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart ); + DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn.HighPart ); + + // + // We check if the Lsn is in the valid range. Raising an + // exception if not. + // + + if ( Lsn.QuadPart > Lfcb->RestartArea->CurrentLsn.QuadPart ) { + + DebugTrace( 0, Dbg, "Lsn is not in the file\n", 0 ); + ExRaiseStatus( STATUS_INVALID_PARAMETER ); + } + + // + // If the Lsn has already been flushed we are done. + // Otherwise we need to look through the workqueues and the + // active queue. + // + + if ( Lsn.QuadPart > Lfcb->LastFlushedLsn.QuadPart ) { + + PLIST_ENTRY ThisEntry; + PLBCB ThisLbcb; + + // + // Check the workqueue first. We are looking for the last + // buffer block of a log page block which contains this + // Lsn. + // + + ThisEntry = Lfcb->LbcbWorkque.Flink; + + // + // We keep looping. + // + + while (TRUE) { + + ThisLbcb = CONTAINING_RECORD( ThisEntry, + LBCB, + WorkqueLinks ); + + // + // We pass over any restart areas. We also skip any + // Lbcb's which do not contain the end of a log record. + // + + if (!LfsLbcbIsRestart( ThisLbcb ) + && FlagOn( ThisLbcb->Flags, LOG_PAGE_LOG_RECORD_END )) { + + // + // If the last complete Lsn in this Lbcb is greater or equal + // to the desired Lsn, we exit the loop. + // + + if ( ThisLbcb->LastEndLsn.QuadPart >= Lsn.QuadPart ) { + + break; + } + } + + // + // Otherwise move to the next Lbcb. + // + + ThisEntry = ThisEntry->Flink; + + ASSERT( ThisEntry != &Lfcb->LbcbWorkque ); + } + + // + // If we are not supporting a packed log file and this Lbcb is from + // the active queue, we need to check that losing the tail of the + // will not swallow up any of our reserved space. + // + + if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG ) + && FlagOn( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE )) { + + LONGLONG CurrentAvail; + LONGLONG UnusedBytes; + + // + // Find the unused bytes. + // + + UnusedBytes = 0; + + LfsCurrentAvailSpace( Lfcb, + &CurrentAvail, + (PULONG)&UnusedBytes ); + + CurrentAvail = CurrentAvail - Lfcb->TotalUndoCommitment; + + if ( UnusedBytes > CurrentAvail ) { + + DebugTrace( -1, Dbg, "Have to preserve these bytes for possible aborts\n", 0 ); + + ExRaiseStatus( STATUS_LOG_FILE_FULL ); + } + + // + // We want to make sure we don't write any more data into this + // page. Remove this from the active queue. + // + + RemoveEntryList( &ThisLbcb->ActiveLinks ); + ClearFlag( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE ); + } + + // + // We now have the Lbcb we want to flush to disk. + // + + LfsFlushLbcb( Lfcb, ThisLbcb ); + } + + DebugTrace( -1, Dbg, "LfsFlushToLsnPriv: Exit\n", 0 ); + + return; +} + + +PLBCB +LfsGetLbcb ( + IN PLFCB Lfcb + ) + +/*++ + +Routine Description: + + This routine is called to add a Lbcb to the active queue. + +Arguments: + + Lfcb - This is the file control block for the log file. + +Return Value: + + PLBCB - Pointer to the Lbcb allocated. + +--*/ + +{ + PLBCB Lbcb = NULL; + PVOID PageHeader; + PBCB PageHeaderBcb = NULL; + + BOOLEAN WrappedOrUsaError; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, "LfsGetLbcb: Entered\n", 0 ); + DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Pin the desired record page. + // + + LfsPreparePinWriteData( Lfcb, + Lfcb->NextLogPage, + (ULONG)Lfcb->LogPageSize, + &PageHeader, + &PageHeaderBcb ); + + // + // Put our signature into the page so we won't fail if we + // see a previous 'BAAD' signature. + // + + *((PULONG) PageHeader) = LFS_SIGNATURE_RECORD_PAGE_ULONG; + + // + // Now allocate an Lbcb. + // + + LfsAllocateLbcb( Lfcb, &Lbcb ); + + // + // If we are at the beginning of the file we test that the + // sequence number won't wrap to 0. + // + + if (!FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_REUSE_TAIL ) + && ( Lfcb->NextLogPage == Lfcb->FirstLogPage )) { + + Lfcb->SeqNumber = Lfcb->SeqNumber + 1; + + // + // If the sequence number is going from 0 to 1, then + // this is the first time the log file has wrapped. We want + // to remember this because it means that we can now do + // large spiral writes. + // + + if (Int64ShllMod32( Lfcb->SeqNumber, Lfcb->FileDataBits ) == 0) { + + DebugTrace( 0, Dbg, "Log sequence number about to wrap: Lfcb -> %08lx\n", Lfcb ); + KeBugCheck( FILE_SYSTEM ); + } + + // + // If this number is greater or equal to the wrap sequence number in + // the Lfcb, set the wrap flag in the Lbcb. + // + + if (!FlagOn( Lfcb->Flags, LFCB_LOG_WRAPPED ) + && ( Lfcb->SeqNumber >= Lfcb->SeqNumberForWrap )) { + + SetFlag( Lbcb->LbcbFlags, LBCB_LOG_WRAPPED ); + SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED ); + } + } + + // + // Now initialize the rest of the Lbcb fields. + // + + Lbcb->FileOffset = Lfcb->NextLogPage; + Lbcb->SeqNumber = Lfcb->SeqNumber; + Lbcb->BufferOffset = Lfcb->LogPageDataOffset; + + // + // Store the next page in the Lfcb. + // + + LfsNextLogPageOffset( Lfcb, + Lfcb->NextLogPage, + &Lfcb->NextLogPage, + &WrappedOrUsaError ); + + Lbcb->Length = Lfcb->LogPageSize; + Lbcb->PageHeader = PageHeader; + Lbcb->LogPageBcb = PageHeaderBcb; + + Lbcb->ResourceThread = ExGetCurrentResourceThread(); + + // + // If we are reusing a previous page then set a flag in + // the Lbcb to indicate that we should flush a copy + // first. + // + + if (FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL )) { + + SetFlag( Lbcb->LbcbFlags, LBCB_FLUSH_COPY ); + ClearFlag( Lfcb->Flags, LFCB_REUSE_TAIL ); + + (ULONG)Lbcb->BufferOffset = Lfcb->ReusePageOffset; + + Lbcb->Flags = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Flags; + Lbcb->LastLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Copy.LastLsn; + Lbcb->LastEndLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Header.Packed.LastEndLsn; + } + + // + // Put the Lbcb on the active queue + // + + InsertTailList( &Lfcb->LbcbActive, &Lbcb->ActiveLinks ); + + SetFlag( Lbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE ); + + } finally { + + DebugUnwind( LfsGetLbcb ); + + // + // If an error occurred, we need to clean up any blocks which + // have not been added to the active queue. + // + + if (AbnormalTermination()) { + + if (Lbcb != NULL) { + + LfsDeallocateLbcb( Lfcb, Lbcb ); + Lbcb = NULL; + } + + // + // Unpin the system page if pinned. + // + + if (PageHeaderBcb != NULL) { + + CcUnpinData( PageHeaderBcb ); + } + } + + DebugTrace( -1, Dbg, "LfsGetLbcb: Exit\n", 0 ); + } + + return Lbcb; +} |