text: /*++
  
  Copyright (c) 1990  Microsoft Corporation
  
  Module Name:
  
      LogPgSup.c
  
  Abstract:
  
      This module implements support for manipulating log pages.
  
  Author:
  
      Brian Andrew    [BrianAn]   20-June-1991
  
  Revision History:
  
  --*/
  
  #include "lfsprocs.h"
  
  //
  //  The debug trace level
  //
  
  #define Dbg                              (DEBUG_TRACE_LOG_PAGE_SUP)
  
  #undef MODULE_POOL_TAG
  #define MODULE_POOL_TAG                  ('PsfL')
  
  #ifdef ALLOC_PRAGMA
  #pragma alloc_text(PAGE, LfsAllocateSpanningBuffer)
  #pragma alloc_text(PAGE, LfsFreeSpanningBuffer)
  #pragma alloc_text(PAGE, LfsNextLogPageOffset)
  #endif
  
  
  VOID
  LfsNextLogPageOffset (
      IN PLFCB Lfcb,
      IN LONGLONG CurrentLogPageOffset,
      OUT PLONGLONG NextLogPageOffset,
      OUT PBOOLEAN Wrapped
      )
  
  /*++
  
  Routine Description:
  
      This routine will compute the offset in the log file of the next log
      page.
  
  Arguments:
  
      Lfcb - This is the file control block for the log file.
  
      CurrentLogPageOffset - This is the file offset of the current log page.
  
      NextLogPageOffset - Address to store the next log page to use.
  
      Wrapped - This is a pointer to a boolean variable that, if present,
                we use to indicate whether we wrapped in the log file.
  
  Return Value:
  
      None.
  
  --*/
  
  {
      PAGED_CODE();
  
      DebugTrace( +1, Dbg, "LfsNextLogPageOffset:  Entered\n", 0 );
      DebugTrace(  0, Dbg, "Lfcb                          ->  %08lx\n", Lfcb );
      DebugTrace(  0, Dbg, "CurrentLogPageOffset (Low)    ->  %08lx\n", CurrentLogPageOffset.LowPart );
      DebugTrace(  0, Dbg, "CurrentLogPageOffset (High)   ->  %08lx\n", CurrentLogPageOffset.HighPart );
      DebugTrace(  0, Dbg, "Wrapped                       ->  %08lx\n", Wrapped );
  
      //
      //  We add the log page size to the current log offset.
      //
  
      LfsTruncateOffsetToLogPage( Lfcb, CurrentLogPageOffset, &CurrentLogPageOffset );
      *NextLogPageOffset = CurrentLogPageOffset + Lfcb->LogPageSize;                                                     //**** xxAdd( CurrentLogPageOffset, Lfcb->LogPageSize );
  
      //
      //  If the result is larger than the file, we use the first page offset
      //  in the file.
      //
  
      if ( *NextLogPageOffset >= Lfcb->FileSize ) {                                                                      //**** xxGeq( *NextLogPageOffset, Lfcb->FileSize )
  
          *NextLogPageOffset = Lfcb->FirstLogPage;
  
          *Wrapped = TRUE;
  
      } else {
  
          *Wrapped = FALSE;
      }
  
      DebugTrace(  0, Dbg, "NextLogPageOffset (Low)    ->  %08lx\n", NextLogPageOffset->LowPart );
      DebugTrace(  0, Dbg, "NextLogPageOffset (High)   ->  %08lx\n", NextLogPageOffset->HighPart );
      DebugTrace(  0, Dbg, "Wrapped                    ->  %08x\n", *Wrapped );
      DebugTrace( -1, Dbg, "LfsNextLogPageOffset:  Exit\n", 0 );
  
      return;
  }
  
  
  PVOID
  LfsAllocateSpanningBuffer (
      IN PLFCB Lfcb,
      IN ULONG Length
      )
  
  /*++
  
  Routine Description:
  
      This routine is called to allocate a spare buffer to read a file record
      which spans a log page.  We will first try to allocate one.  If that
      fails we will use one of the existing spare buffers.  If that fails then
      we will raise.
  
  Arguments:
  
      Lfcb - This is the file control block for the log file.
  
      Length - Length of the buffer required.
  
  Return Value:
  
      PVOID - Pointer to the buffer to use for reading the log record.
          May be either from pool or from the auxilary buffer pool.
  
  --*/
  
  {
      PVOID NewBuffer = NULL;
      ERESOURCE_THREAD Thread;
      BOOLEAN Wait = FALSE;
  
      PAGED_CODE();
  
      DebugTrace( +1, Dbg, "LfsAllocateSpanningBuffer:  Entered\n", 0 );
  
      //
      //  Loop while we don't have a buffer.  First try to get our reserved buffer
      //  without waiting.  Then try to allocate a buffer.  Finally wait for the reserved
      //  buffer as the final alternative.
      //
  
      do {
  
          //
          //  Skip the reserved buffer if the request is larger than we can read into it.
          //
  
          if (Length <= LFS_BUFFER_SIZE) {
  
              //
              //  If this thread already owns one buffer it can get the second directly.
              //
  
              Thread = ExGetCurrentResourceThread();
  
              if (Thread == LfsData.BufferOwner) {
  
                  if (!FlagOn( LfsData.BufferFlags, LFS_BUFFER1_OWNED )) {
  
                      SetFlag( LfsData.BufferFlags, LFS_BUFFER1_OWNED );
                      NewBuffer = LfsData.Buffer1;
                      break;
  
                  } else if (!FlagOn( LfsData.BufferFlags, LFS_BUFFER2_OWNED )) {
  
                      SetFlag( LfsData.BufferFlags, LFS_BUFFER2_OWNED );
                      NewBuffer = LfsData.Buffer2;
                      break;
  
                  } else if (Wait) {
  
                      //
                      //  This shouldn't happen but handle anyway.
                      //
  
                      DebugTrace( -1, Dbg, "LfsAllocateSpanningBuffer:  Exit\n", 0 );
                      ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
                  }
  
              //
              //  Otherwise acquire the buffer lock and check the state of the buffers.
              //
  
              } else {
  
                  BOOLEAN LfcbOwned = TRUE;
  
                  while (TRUE) {
  
                      LfsAcquireBufferLock();
  
                      //
                      //  Check to see if the buffers are available.  No
                      //  need to drop the Lfcb in the typical case.
                      //
  
                      if (LfsData.BufferOwner == (ERESOURCE_THREAD) NULL) {
  
                          ASSERT( !FlagOn( LfsData.BufferFlags, LFS_BUFFER1_OWNED | LFS_BUFFER2_OWNED ));
                          NewBuffer = LfsData.Buffer1;
                          LfsData.BufferOwner = Thread;
                          SetFlag( LfsData.BufferFlags, LFS_BUFFER1_OWNED );
                          LfsBlockBufferWaiters();
                          
                          //
                          //  Reacquire the Lfcb if needed.
                          //
  
                          if (!LfcbOwned) { 
  
                              LfsAcquireLfcbExclusive( Lfcb );
                          }
  
                          //
                          //  Break out.
                          //
  
                          LfsReleaseBufferLock();
                          break;
                      }
  
                      //
                      //  Release the Lfcb and wait on the notification for the buffers.
                      //
  
                      if (Wait) {
  
                          if (LfcbOwned) { 
                              LfsReleaseLfcb( Lfcb );
                              LfcbOwned = FALSE;
                          }
  
                          LfsReleaseBufferLock();
                          LfsWaitForBufferNotification();
  
                      } else {
  
                          //
                          //  Go ahead and try to allocate a buffer from pool next.
                          //
  
                          LfsReleaseBufferLock();
                          break;
                      }
                  }
              }
  
          //
          //  Raise if we already tried the allocate path.
          //
  
          } else if (Wait) {
  
              DebugTrace( -1, Dbg, "LfsAllocateSpanningBuffer:  Exit\n", 0 );
              ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
          }
  
          //
          //  Try pool if we didn't get a buffer above.
          //
  
          if (NewBuffer == NULL) {
  
              //
              //  Try pool next but don't let this fail on pool allocation.
              //
  
              NewBuffer = LfsAllocatePoolNoRaise( PagedPool, Length );
          }
  
          //
          //  Wait on the next pass through the loop.
          //
  
          Wait = TRUE;
  
      } while (NewBuffer == NULL);
  
      DebugTrace( -1, Dbg, "LfsAllocateSpanningBuffer:  Exit\n", 0 );
      return NewBuffer;
  }
  
  VOID
  LfsFreeSpanningBuffer (
      IN PVOID Buffer
      )
  
  /*++
  
  Routine Description:
  
      This routine is called to free a buffer used to read a log record
      which spans pages.  We will check if it is one of our special buffers
      and deal with synchronization in that case.
  
  Arguments:
  
      Buffer - Buffer to free.
  
  Return Value:
  
      None.
  
  --*/
  
  {
      ERESOURCE_THREAD Thread;
      ULONG BufferFlag;
  
      PAGED_CODE();
  
      DebugTrace( +1, Dbg, "LfsFreeSpanningBuffer:  Entered\n", 0 );
  
      //
      //  Check if either buffer1 or buffer2 are being freed.
      //
  
      if (Buffer == LfsData.Buffer1) {
  
          BufferFlag = LFS_BUFFER1_OWNED;
          goto ReservedBuffers;
  
      } else if (Buffer == LfsData.Buffer2) {
  
          BufferFlag = LFS_BUFFER2_OWNED;
  
  ReservedBuffers:
  
          //
          //  Acquire the buffer lock and clear the correct flag.
          //
  
          LfsAcquireBufferLock();
          ClearFlag( LfsData.BufferFlags, BufferFlag );
  
          //
          //  If no buffers owned then signal the waiters.
          //
  
          if (!FlagOn( LfsData.BufferFlags, LFS_BUFFER1_OWNED | LFS_BUFFER2_OWNED )) {
  
              LfsData.BufferOwner = (ERESOURCE_THREAD) NULL;
              LfsNotifyBufferWaiters();
          }
  
          LfsReleaseBufferLock();
  
      } else {
  
          //
          //  Simply free the buffer.
          //
  
          LfsFreePool( Buffer );
      }
  
      DebugTrace( -1, Dbg, "LfsFreeSpanningBuffer:  Exit\n", 0 );
  
      return;
  }
  
  