521 lines
13 KiB
C
521 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
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.
|
|||
|
//
|
|||
|
|
|||
|
Lfcb->Waiters += 1;
|
|||
|
|
|||
|
LfsReleaseLfcb( Lfcb );
|
|||
|
|
|||
|
KeWaitForSingleObject( &Lfcb->Sync->Event,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL );
|
|||
|
|
|||
|
LfsAcquireLfcb( Lfcb );
|
|||
|
Lfcb->Waiters -= 1;
|
|||
|
|
|||
|
} 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.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN UseLastRecordLbcb = FALSE;
|
|||
|
PLBCB LastRecordLbcb = NULL;
|
|||
|
|
|||
|
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) {
|
|||
|
|
|||
|
UseLastRecordLbcb = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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 )) {
|
|||
|
|
|||
|
LastRecordLbcb = ThisLbcb;
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
|
|||
|
//
|
|||
|
// If we have reached the end of the list then break out. We
|
|||
|
// were given an Lsn which is larger than any flushed Lsn so
|
|||
|
// we will just flush to the end of the log file.
|
|||
|
//
|
|||
|
|
|||
|
if (ThisEntry == &Lfcb->LbcbWorkque) {
|
|||
|
|
|||
|
if (UseLastRecordLbcb) {
|
|||
|
|
|||
|
ThisLbcb = LastRecordLbcb;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (ThisLbcb != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// 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,
|
|||
|
FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL ),
|
|||
|
&PageHeader,
|
|||
|
&PageHeaderBcb );
|
|||
|
|
|||
|
#ifdef LFS_CLUSTER_CHECK
|
|||
|
//
|
|||
|
// Check the page to see if there is already data on this page with the current sequence
|
|||
|
// number. Useful to track cases where ntfs didn't find the correct end of the log or
|
|||
|
// where the cluster service has the volume mounted twice.
|
|||
|
//
|
|||
|
|
|||
|
if (LfsTestCheckLbcb &&
|
|||
|
*((PULONG) PageHeader) == LFS_SIGNATURE_RECORD_PAGE_ULONG) {
|
|||
|
|
|||
|
LSN LastLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Copy.LastLsn;
|
|||
|
|
|||
|
//
|
|||
|
// This is not an exhaustive test but should be sufficient to catch the typical case.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_REUSE_TAIL ) ||
|
|||
|
(LfsLsnToSeqNumber( Lfcb, LastLsn ) < (ULONGLONG) Lfcb->SeqNumber) ||
|
|||
|
(Lfcb->NextLogPage == Lfcb->FirstLogPage) );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// 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 );
|
|||
|
KeBugCheckEx( FILE_SYSTEM, 4, 0, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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();
|
|||
|
Lbcb->ResourceThread = (ERESOURCE_THREAD) ((ULONG) Lbcb->ResourceThread | 3);
|
|||
|
|
|||
|
//
|
|||
|
// 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 );
|
|||
|
|
|||
|
//
|
|||
|
// Now that we have succeeded, set the owner thread to Thread + 1 so the resource
|
|||
|
// package will know not to peek in this thread. It may be deallocated before
|
|||
|
// we release the Bcb during flush.
|
|||
|
//
|
|||
|
|
|||
|
CcSetBcbOwnerPointer( Lbcb->LogPageBcb, (PVOID) Lbcb->ResourceThread );
|
|||
|
|
|||
|
} 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;
|
|||
|
}
|