404 lines
10 KiB
C
404 lines
10 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, LfsFlushToLsnPriv)
|
||
#pragma alloc_text(PAGE, LfsGetLbcb)
|
||
#endif
|
||
|
||
extern LARGE_INTEGER LiMinus1;
|
||
|
||
|
||
VOID
|
||
LfsFlushToLsnPriv (
|
||
IN PLFCB Lfcb,
|
||
IN LSN Lsn,
|
||
IN BOOLEAN RestartLsn
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
RestartLsn - whether this lsn is a lfs restart lsn
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
LSN FlushedLsn;
|
||
volatile LARGE_INTEGER StartTime;
|
||
LFS_WAITER LfsWaiter;
|
||
BOOLEAN OwnedExclusive;
|
||
BOOLEAN Flush;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "LfsFlushLbcb: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
|
||
DebugTrace( 0, Dbg, "Lbcb -> %08lx\n", Lbcb );
|
||
|
||
KeQueryTickCount( &StartTime );
|
||
|
||
//
|
||
// Convert max lsn to the current lsn which will not change since we hold the
|
||
// lfcb at least shared at this point and writers need it exclusive
|
||
// We do not care if the log progresses beyond this point
|
||
//
|
||
|
||
if (!RestartLsn && (Lsn.QuadPart > Lfcb->RestartArea->CurrentLsn.QuadPart)) {
|
||
|
||
Lsn = Lfcb->RestartArea->CurrentLsn;
|
||
}
|
||
|
||
//
|
||
// Init a wait entry - this is a lightweight operation
|
||
//
|
||
|
||
KeInitializeEvent( &LfsWaiter.Event, SynchronizationEvent, FALSE );
|
||
LfsWaiter.Lsn.QuadPart = Lsn.QuadPart;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
OwnedExclusive = ExIsResourceAcquiredExclusiveLite( &Lfcb->Sync->Resource );
|
||
|
||
while (TRUE) {
|
||
|
||
Flush = FALSE;
|
||
|
||
ExAcquireFastMutexUnsafe( &Lfcb->Sync->Mutex );
|
||
|
||
if (RestartLsn) {
|
||
FlushedLsn = Lfcb->LastFlushedRestartLsn;
|
||
} else {
|
||
FlushedLsn = Lfcb->LastFlushedLsn;
|
||
}
|
||
|
||
//
|
||
// Check if we still need to flush or can immediately return
|
||
//
|
||
|
||
if (Lsn.QuadPart <= FlushedLsn.QuadPart) {
|
||
|
||
ExReleaseFastMutexUnsafe( &Lfcb->Sync->Mutex );
|
||
break;
|
||
}
|
||
|
||
if (Lfcb->Sync->LfsIoState == LfsNoIoInProgress) {
|
||
|
||
Lfcb->Sync->LfsIoState = LfsClientThreadIo;
|
||
Lfcb->LfsIoThread = ExGetCurrentResourceThread();
|
||
Flush = TRUE;
|
||
|
||
} else {
|
||
|
||
PLFS_WAITER TempWaiter = (PLFS_WAITER)Lfcb->WaiterList.Flink;
|
||
|
||
//
|
||
// Insert the wait entry in the sorted list of waiters -
|
||
// find its place first
|
||
//
|
||
|
||
while ((PVOID)TempWaiter != &Lfcb->WaiterList) {
|
||
|
||
if (TempWaiter->Lsn.QuadPart > Lsn.QuadPart) {
|
||
break;
|
||
}
|
||
TempWaiter = (PLFS_WAITER)TempWaiter->Waiters.Flink;
|
||
}
|
||
|
||
InsertTailList( &TempWaiter->Waiters, &LfsWaiter.Waiters );
|
||
|
||
}
|
||
ExReleaseFastMutexUnsafe( &Lfcb->Sync->Mutex );
|
||
|
||
//
|
||
//
|
||
// If we can do the Io, call down to flush the Lfcb.
|
||
//
|
||
|
||
if (Flush) {
|
||
LfsFlushLfcb( Lfcb, Lsn, RestartLsn );
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Otherwise we release the Lfcb and immediately wait on the event.
|
||
//
|
||
|
||
InterlockedIncrement( &Lfcb->Waiters );
|
||
LfsReleaseLfcb( Lfcb );
|
||
|
||
KeWaitForSingleObject( &LfsWaiter.Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
if (OwnedExclusive) {
|
||
LfsAcquireLfcbExclusive( Lfcb );
|
||
} else {
|
||
LfsAcquireLfcbShared( Lfcb );
|
||
}
|
||
InterlockedDecrement( &Lfcb->Waiters );
|
||
}
|
||
|
||
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;
|
||
}
|