|
|
/*++
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; }
|