549 lines
15 KiB
C
549 lines
15 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
logsup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the special cache manager support for logging
|
||
file systems.
|
||
|
||
Author:
|
||
|
||
Tom Miller [TomM] 30-Jul-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "cc.h"
|
||
|
||
//
|
||
// Define our debug constant
|
||
//
|
||
|
||
#define me 0x0000040
|
||
|
||
|
||
VOID
|
||
CcSetAdditionalCacheAttributes (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN BOOLEAN DisableReadAhead,
|
||
IN BOOLEAN DisableWriteBehind
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine supports the setting of disable read ahead or disable write
|
||
behind flags to control Cache Manager operation. This routine may be
|
||
called any time after calling CcInitializeCacheMap. Initially both
|
||
read ahead and write behind are enabled. Note that the state of both
|
||
of these flags must be specified on each call to this routine.
|
||
|
||
Arguments:
|
||
|
||
FileObject - File object for which the respective flags are to be set.
|
||
|
||
DisableReadAhead - FALSE to enable read ahead, TRUE to disable it.
|
||
|
||
DisableWriteBehind - FALSE to enable write behind, TRUE to disable it.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARED_CACHE_MAP SharedCacheMap;
|
||
KIRQL OldIrql;
|
||
|
||
//
|
||
// Get pointer to SharedCacheMap.
|
||
//
|
||
|
||
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
||
|
||
//
|
||
// Now set the flags and return.
|
||
//
|
||
|
||
ExAcquireFastLock( &CcMasterSpinLock, &OldIrql );
|
||
if (DisableReadAhead) {
|
||
SetFlag(SharedCacheMap->Flags, DISABLE_READ_AHEAD);
|
||
} else {
|
||
ClearFlag(SharedCacheMap->Flags, DISABLE_READ_AHEAD);
|
||
}
|
||
if (DisableWriteBehind) {
|
||
SetFlag(SharedCacheMap->Flags, DISABLE_WRITE_BEHIND | MODIFIED_WRITE_DISABLED);
|
||
} else {
|
||
ClearFlag(SharedCacheMap->Flags, DISABLE_WRITE_BEHIND);
|
||
}
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
}
|
||
|
||
|
||
VOID
|
||
CcSetLogHandleForFile (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PVOID LogHandle,
|
||
IN PFLUSH_TO_LSN FlushToLsnRoutine
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to instruct the Cache Manager to store the
|
||
specified log handle with the shared cache map for a file, to support
|
||
subsequent calls to the other routines in this module which effectively
|
||
perform an associative search for files by log handle.
|
||
|
||
Arguments:
|
||
|
||
FileObject - File for which the log handle should be stored.
|
||
|
||
LogHandle - Log Handle to store.
|
||
|
||
FlushToLsnRoutine - A routine to call before flushing buffers for this
|
||
file, to insure a log file is flushed to the most
|
||
recent Lsn for any Bcb being flushed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARED_CACHE_MAP SharedCacheMap;
|
||
|
||
//
|
||
// Get pointer to SharedCacheMap.
|
||
//
|
||
|
||
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
||
|
||
//
|
||
// Now set the log file handle and flush routine
|
||
//
|
||
|
||
SharedCacheMap->LogHandle = LogHandle;
|
||
SharedCacheMap->FlushToLsnRoutine = FlushToLsnRoutine;
|
||
}
|
||
|
||
|
||
LARGE_INTEGER
|
||
CcGetDirtyPages (
|
||
IN PVOID LogHandle,
|
||
IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,
|
||
IN PVOID Context1,
|
||
IN PVOID Context2
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine may be called to return all of the dirty pages in all files
|
||
for a given log handle. Each page is returned by an individual call to
|
||
the Dirty Page Routine. The Dirty Page Routine is defined by a prototype
|
||
in ntos\inc\cache.h.
|
||
|
||
Arguments:
|
||
|
||
LogHandle - Log Handle which must match the log handle previously stored
|
||
for all files which are to be returned.
|
||
|
||
DirtyPageRoutine -- The routine to call as each dirty page for this log
|
||
handle is found.
|
||
|
||
Context1 - First context parameter to be passed to the Dirty Page Routine.
|
||
|
||
Context2 - First context parameter to be passed to the Dirty Page Routine.
|
||
|
||
Return Value:
|
||
|
||
LARGE_INTEGER - Oldest Lsn found of all the dirty pages, or 0 if no dirty pages
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARED_CACHE_MAP SharedCacheMap;
|
||
PBCB Bcb, BcbToUnpin;
|
||
KIRQL OldIrql;
|
||
NTSTATUS ExceptionStatus;
|
||
LARGE_INTEGER SavedFileOffset, SavedOldestLsn, SavedNewestLsn;
|
||
ULONG SavedByteLength;
|
||
ULONG LoopsWithLockHeld = 0;
|
||
LARGE_INTEGER OldestLsn = {0,0};
|
||
|
||
//
|
||
// Synchronize with changes to the SharedCacheMap list.
|
||
//
|
||
|
||
ExAcquireFastLock( &CcMasterSpinLock, &OldIrql );
|
||
|
||
SharedCacheMap = CONTAINING_RECORD( CcDirtySharedCacheMapList.SharedCacheMapLinks.Flink,
|
||
SHARED_CACHE_MAP,
|
||
SharedCacheMapLinks );
|
||
|
||
BcbToUnpin = NULL;
|
||
while (&SharedCacheMap->SharedCacheMapLinks != &CcDirtySharedCacheMapList.SharedCacheMapLinks) {
|
||
|
||
//
|
||
// Skip over cursors, SharedCacheMaps for other LogHandles, and ones with
|
||
// no dirty pages
|
||
//
|
||
|
||
if (!FlagOn(SharedCacheMap->Flags, IS_CURSOR) && (SharedCacheMap->LogHandle == LogHandle) &&
|
||
(SharedCacheMap->DirtyPages != 0)) {
|
||
|
||
//
|
||
// This SharedCacheMap should stick around for a while in the dirty list.
|
||
//
|
||
|
||
SharedCacheMap->OpenCount += 1;
|
||
SharedCacheMap->DirtyPages += 1;
|
||
|
||
//
|
||
// Set our initial resume point and point to first Bcb in List.
|
||
//
|
||
|
||
Bcb = CONTAINING_RECORD( SharedCacheMap->BcbList.Flink, BCB, BcbLinks );
|
||
|
||
//
|
||
// Scan to the end of the Bcb list.
|
||
//
|
||
|
||
while (&Bcb->BcbLinks != &SharedCacheMap->BcbList) {
|
||
|
||
//
|
||
// If the Bcb is dirty, then capture the inputs for the
|
||
// callback routine so we can call without holding a spinlock.
|
||
//
|
||
|
||
LoopsWithLockHeld += 1;
|
||
if ((Bcb->NodeTypeCode == CACHE_NTC_BCB) && Bcb->Dirty) {
|
||
|
||
SavedFileOffset = Bcb->FileOffset;
|
||
SavedByteLength = Bcb->ByteLength;
|
||
SavedOldestLsn = Bcb->OldestLsn;
|
||
SavedNewestLsn = Bcb->NewestLsn;
|
||
|
||
//
|
||
// Increment PinCount so the Bcb sticks around
|
||
//
|
||
|
||
Bcb->PinCount += 1;
|
||
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
|
||
//
|
||
// Any Bcb to unpin from a previous loop?
|
||
//
|
||
|
||
if (BcbToUnpin != NULL) {
|
||
CcUnpinFileData( BcbToUnpin, TRUE, UNPIN );
|
||
BcbToUnpin = NULL;
|
||
}
|
||
|
||
//
|
||
// Call the file system
|
||
//
|
||
|
||
(*DirtyPageRoutine)( SharedCacheMap->FileObject,
|
||
&SavedFileOffset,
|
||
SavedByteLength,
|
||
&SavedOldestLsn,
|
||
&SavedNewestLsn,
|
||
Context1,
|
||
Context2 );
|
||
|
||
//
|
||
// Possibly update OldestLsn
|
||
//
|
||
|
||
if ((SavedOldestLsn.QuadPart != 0) &&
|
||
((OldestLsn.QuadPart == 0) || (SavedOldestLsn.QuadPart < OldestLsn.QuadPart ))) {
|
||
OldestLsn = SavedOldestLsn;
|
||
}
|
||
|
||
//
|
||
// Now reacquire the spinlock and scan from the resume point
|
||
// point to the next Bcb to return in the descending list.
|
||
//
|
||
|
||
ExAcquireFastLock( &CcMasterSpinLock, &OldIrql );
|
||
|
||
//
|
||
// Normally the Bcb can stay around a while, but if not,
|
||
// we will just remember it for the next time we do not
|
||
// have the spin lock. We cannot unpin it now, because
|
||
// we would lose our place in the list.
|
||
//
|
||
|
||
if (Bcb->Dirty || (Bcb->PinCount > 1)) {
|
||
Bcb->PinCount -= 1;
|
||
} else {
|
||
BcbToUnpin = Bcb;
|
||
}
|
||
|
||
//
|
||
// Normally the Bcb is not going away now, but if it is
|
||
// we need to free it by calling the normal routine
|
||
|
||
LoopsWithLockHeld = 0;
|
||
}
|
||
|
||
Bcb = CONTAINING_RECORD( Bcb->BcbLinks.Flink, BCB, BcbLinks );
|
||
}
|
||
|
||
//
|
||
// We need to unpin any Bcb we are holding before moving on to
|
||
// the next SharedCacheMap, or else CcDeleteSharedCacheMap will
|
||
// also delete this Bcb.
|
||
//
|
||
|
||
if (BcbToUnpin != NULL) {
|
||
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
|
||
CcUnpinFileData( BcbToUnpin, TRUE, UNPIN );
|
||
BcbToUnpin = NULL;
|
||
|
||
ExAcquireFastLock( &CcMasterSpinLock, &OldIrql );
|
||
}
|
||
|
||
//
|
||
// Now release the SharedCacheMap, leaving it in the dirty list.
|
||
//
|
||
|
||
SharedCacheMap->OpenCount -= 1;
|
||
SharedCacheMap->DirtyPages -= 1;
|
||
}
|
||
|
||
//
|
||
// Make sure we occassionally drop the lock. Set WRITE_QUEUED
|
||
// to keep the guy from going away, and increment DirtyPages to
|
||
// keep in in this list.
|
||
//
|
||
|
||
if ((++LoopsWithLockHeld >= 20) &&
|
||
!FlagOn(SharedCacheMap->Flags, WRITE_QUEUED | IS_CURSOR)) {
|
||
|
||
SetFlag(SharedCacheMap->Flags, WRITE_QUEUED);
|
||
SharedCacheMap->DirtyPages += 1;
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
LoopsWithLockHeld = 0;
|
||
ExAcquireFastLock( &CcMasterSpinLock, &OldIrql );
|
||
ClearFlag(SharedCacheMap->Flags, WRITE_QUEUED);
|
||
SharedCacheMap->DirtyPages -= 1;
|
||
}
|
||
|
||
//
|
||
// Now loop back for the next cache map.
|
||
//
|
||
|
||
SharedCacheMap =
|
||
CONTAINING_RECORD( SharedCacheMap->SharedCacheMapLinks.Flink,
|
||
SHARED_CACHE_MAP,
|
||
SharedCacheMapLinks );
|
||
}
|
||
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
|
||
return OldestLsn;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
CcIsThereDirtyData (
|
||
IN PVPB Vpb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns TRUE if the specified Vcb has any unwritten dirty
|
||
data in the cache.
|
||
|
||
Arguments:
|
||
|
||
Vpb - specifies Vpb to check for
|
||
|
||
Return Value:
|
||
|
||
FALSE - if the Vpb has no dirty data
|
||
TRUE - if the Vpb has dirty data
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARED_CACHE_MAP SharedCacheMap;
|
||
KIRQL OldIrql;
|
||
ULONG LoopsWithLockHeld = 0;
|
||
|
||
//
|
||
// Synchronize with changes to the SharedCacheMap list.
|
||
//
|
||
|
||
ExAcquireFastLock( &CcMasterSpinLock, &OldIrql );
|
||
|
||
SharedCacheMap = CONTAINING_RECORD( CcDirtySharedCacheMapList.SharedCacheMapLinks.Flink,
|
||
SHARED_CACHE_MAP,
|
||
SharedCacheMapLinks );
|
||
|
||
while (&SharedCacheMap->SharedCacheMapLinks != &CcDirtySharedCacheMapList.SharedCacheMapLinks) {
|
||
|
||
//
|
||
// Look at this one if the Vpb matches and if there is dirty data.
|
||
// For what it's worth, don't worry about dirty data in temporary files,
|
||
// as that should not concern the caller if it wants to dismount.
|
||
//
|
||
|
||
if (!FlagOn(SharedCacheMap->Flags, IS_CURSOR) &&
|
||
(SharedCacheMap->FileObject->Vpb == Vpb) &&
|
||
(SharedCacheMap->DirtyPages != 0) &&
|
||
!FlagOn(SharedCacheMap->FileObject->Flags, FO_TEMPORARY_FILE)) {
|
||
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Make sure we occassionally drop the lock. Set WRITE_QUEUED
|
||
// to keep the guy from going away, and increment DirtyPages to
|
||
// keep in in this list.
|
||
//
|
||
|
||
if ((++LoopsWithLockHeld >= 20) &&
|
||
!FlagOn(SharedCacheMap->Flags, WRITE_QUEUED | IS_CURSOR)) {
|
||
|
||
SetFlag(SharedCacheMap->Flags, WRITE_QUEUED);
|
||
SharedCacheMap->DirtyPages += 1;
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
LoopsWithLockHeld = 0;
|
||
ExAcquireFastLock( &CcMasterSpinLock, &OldIrql );
|
||
ClearFlag(SharedCacheMap->Flags, WRITE_QUEUED);
|
||
SharedCacheMap->DirtyPages -= 1;
|
||
}
|
||
|
||
//
|
||
// Now loop back for the next cache map.
|
||
//
|
||
|
||
SharedCacheMap =
|
||
CONTAINING_RECORD( SharedCacheMap->SharedCacheMapLinks.Flink,
|
||
SHARED_CACHE_MAP,
|
||
SharedCacheMapLinks );
|
||
}
|
||
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
LARGE_INTEGER
|
||
CcGetLsnForFileObject(
|
||
IN PFILE_OBJECT FileObject,
|
||
OUT PLARGE_INTEGER OldestLsn OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the oldest and newest LSNs for a file object.
|
||
|
||
Arguments:
|
||
|
||
FileObject - File for which the log handle should be stored.
|
||
|
||
OldestLsn - pointer to location to store oldest LSN for file object.
|
||
|
||
Return Value:
|
||
|
||
The newest LSN for the file object.
|
||
|
||
--*/
|
||
|
||
{
|
||
PBCB Bcb;
|
||
KIRQL OldIrql;
|
||
LARGE_INTEGER Oldest, Newest;
|
||
PSHARED_CACHE_MAP SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
||
|
||
//
|
||
// initialize lsn variables
|
||
//
|
||
|
||
Oldest.LowPart = 0;
|
||
Oldest.HighPart = 0;
|
||
Newest.LowPart = 0;
|
||
Newest.HighPart = 0;
|
||
|
||
if(SharedCacheMap == NULL) {
|
||
return Oldest;
|
||
}
|
||
|
||
ExAcquireFastLock(&CcMasterSpinLock, &OldIrql);
|
||
|
||
//
|
||
// Now point to first Bcb in List, and loop through it.
|
||
//
|
||
|
||
Bcb = CONTAINING_RECORD( SharedCacheMap->BcbList.Flink, BCB, BcbLinks );
|
||
|
||
while (&Bcb->BcbLinks != &SharedCacheMap->BcbList) {
|
||
|
||
//
|
||
// If the Bcb is dirty then capture the oldest and newest lsn
|
||
//
|
||
|
||
|
||
if ((Bcb->NodeTypeCode == CACHE_NTC_BCB) && Bcb->Dirty) {
|
||
|
||
LARGE_INTEGER BcbLsn, BcbNewest;
|
||
|
||
BcbLsn = Bcb->OldestLsn;
|
||
BcbNewest = Bcb->NewestLsn;
|
||
|
||
if ((BcbLsn.QuadPart != 0) &&
|
||
((Oldest.QuadPart == 0) ||
|
||
(BcbLsn.QuadPart < Oldest.QuadPart))) {
|
||
|
||
Oldest = BcbLsn;
|
||
}
|
||
|
||
if ((BcbLsn.QuadPart != 0) && (BcbNewest.QuadPart > Newest.QuadPart)) {
|
||
|
||
Newest = BcbNewest;
|
||
}
|
||
}
|
||
|
||
|
||
Bcb = CONTAINING_RECORD( Bcb->BcbLinks.Flink, BCB, BcbLinks );
|
||
}
|
||
|
||
//
|
||
// Now release the spin lock for this Bcb list and generate a callback
|
||
// if we got something.
|
||
//
|
||
|
||
ExReleaseFastLock( &CcMasterSpinLock, OldIrql );
|
||
|
||
if (ARGUMENT_PRESENT(OldestLsn)) {
|
||
|
||
*OldestLsn = Oldest;
|
||
}
|
||
|
||
return Newest;
|
||
}
|