/*!**************************************************************************

  module      : FBM_Manager.cpp

  -------------------------------------------------------------------------

  responsible : TorstenS

  auhtor      : AlexanderK

  special area: FreeBlockManagement (DDBM)
  description : 


  last changed: 2000-03-10  11:00
  see also    : 

  -------------------------------------------------------------------------

  copyright:    (c) 2000-2004 SAP AG



    ========== licence begin  GPL
    Copyright (c) 2000-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


*****************************************************************************/

/*===========================================================================*
*  INCLUDES                                                                 *
*===========================================================================*/

#include <new.h>                                  // SYSTEM: required for placement new
#include "hgg08.h"                                // region identifier
#include "hgg01.h"                                // region identifier
#include "Container/Container_Vector.hpp"
#include "Logging/Log_Types.hpp"
#include "Logging/Log_Savepoint.hpp"
#include "FreeBlockManagement/FBM_Manager.hpp"
#include "FreeBlockManagement/FBM_Dump.hpp"
#include "FreeBlockManagement/FBM_Types.hpp"
#include "FreeBlockManagement/FBM_Exception.hpp"
#include "FreeBlockManagement/FBM_DataVolume.hpp"
#include "FreeBlockManagement/FBM_SequentialDataVolume.hpp"
#include "RunTime/RTE_Crash.hpp"
#include "RunTime/RTE_Message.hpp"
#include "RunTime/MemoryManagement/RTEMem_Allocator.hpp"
#include "RunTime/Configuration/RTEConf_ParameterAccessKernelInterface.hpp"

/*===========================================================================*
 *  DEFINES                                                                   *
 *===========================================================================*/

#define FBM_MEM_INITIAL_SIZE            (8192 * 8)
#define FBM_MEM_SUPPLEMENT_SIZE         (8192 * 8)

/*===========================================================================*
 *  PUBLIC METHODS                                                           *
 *===========================================================================*/

FBM_Manager* FBM_Manager::m_Instance = NULL;

/*---------------------------------------------------------------------------*/

FBM_Manager&
FBM_Manager::CreateInstance (tgg00_BasisError TrError)
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::CreateInstance", FBM_Trace, 5);

    if (m_Instance == NULL)
    {
        m_Instance = new (RTEMem_Allocator::Instance())
                     FBM_Manager(RTEMem_Allocator::Instance());
        if (NULL == m_Instance)
            TrError = e_sysbuf_storage_exceeded;
    }
    return *m_Instance;
}

/*---------------------------------------------------------------------------*/

FBM_Manager::FBM_Manager(SAPDBMem_IRawAllocator &Allocator)
        :
        m_Allocator( UTF8( "FBM_Manager" ), Allocator ),
        m_DataVolume(m_Allocator),
        m_ArchiveVolumes(m_Allocator),
        m_ArchiveVolumeGroups(m_Allocator)
{
    ResetMembersToNullAndZero();
}

/*---------------------------------------------------------------------------*/

FBM_IManager&
FBM_IManager::Instance ()
{
    return FBM_Manager::Instance();
}

/*---------------------------------------------------------------------------*/

bool
FBM_Manager::Restart(
    const tsp00_TaskId                TaskId,
    const SAPDB_Int4                  MaxNumDev,
    const IOMan_ReservedBlockAddress  &ReservedBlocks)

{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::Restart", FBM_Trace, 5);

    FBM_SynchObject SynchronizeFBM (TaskId);

    /* check that the FBM_Manager is not already restarted */
    SAPDBERR_ASSERT_ARGUMENT (m_MaxNumDev  == FBM_UNDEFINED &&
                              m_NumDev                     == 0                   &&
                              m_TotalNumBlocks             == 0                   &&
                              m_TotalNumBlocksFree         == 0                   &&
                              m_TotalNumBlocksFreeAfterSVP == 0                   &&
                              m_SavepointIsRequested       == false               &&
                              m_NextDevToUseForNewBlock    == FBM_UNDEFINED       &&
                              m_NextDevToUseForFragBlock   == FBM_UNDEFINED);

    m_MaxNumDev      = MaxNumDev;
    m_ReservedBlocks = ReservedBlocks;

    if (g01is_archive())
    {
        m_ArchiveVolumes.Reserve(m_MaxNumDev);

        RTEConf_Parameter::Integer value;
        SAPDBErr_MessageList errorList;
        RTEConf_ParameterAccess::Instance()->GetInteger((RTEConf_Parameter::Name)"DATA_VOLUME_GROUPS", value, errorList);
        m_NumberVolumeGroups = (SAPDB_Int) value;

        m_ArchiveVolumeGroups.Initialize(m_NumberVolumeGroups, 0);
    }

    /* construct array containing the number of free pages for each section */
    SAPDBTRACE_WRITELN( FBM_Trace, 5, "MaxNumDev: " << m_MaxNumDev);

    m_DataVolume.Initialize (m_MaxNumDev, FirstDevNo());
    m_Active = SAPDB_TRUE;
    return m_Active;
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::Shutdown( const tsp00_TaskId TaskId )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::Shutdown", FBM_Trace, 5);

    /* this method releases all memory resources */

    FBM_SynchObject SynchronizeFBM (TaskId);

    /* release memory allocated by the FBM_Manager and its member */
    m_DataVolume.Shutdown();

    /* reset all member variables, usage of the FBM_Manager leads to errors now */
    ResetMembersToNullAndZero();
}

/*---------------------------------------------------------------------------*/

bool
FBM_Manager::AddVolume(
    const tsp00_TaskId         TaskId,
    const SAPDB_Int2           DevNo,
    const SAPDB_Int4           DevSize,
    const RTE_VolumeAccessMode VolMode)
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::RegisterDevice", FBM_Trace, 5);

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "DevNo: " << DevNo << " DevSize: "  << DevSize);

    /* check validity of the device number */
    if ((DevNo < FirstDevNo()) || (DevNo > MaxDevNo()))
    {
        /* write error message into knldiag and vtrace */
        FBM_WrongDevNo WrongDevNo(__CONTEXT__, DevNo, FirstDevNo(), MaxDevNo());
        RTE_Message (WrongDevNo);
        return false;
    }
    {
        FBM_SynchObject SynchronizeFBM (TaskId);

        if (RTE_VolumeAccessModeSequential == VolMode)
        {
            m_DataVolume.InsertEnd(new (m_Allocator) FBM_SequentialDataVolume(m_Allocator, DevSize, VolMode));
        }
        else
        {
            m_DataVolume.InsertEnd(new (m_Allocator) FBM_DataVolume(m_Allocator, DevSize, VolMode));
        }

        /* initialize device description and fill it with start values */
        m_DataVolume [DevNo].InitVolume();

        // update archiveVolumes
        if (RTE_VolumeAccessModeSequential == VolMode)
        {
            SAPDBERR_ASSERT_STATE (g01is_archive());

            m_ArchiveVolumes.InsertEnd(DevNo);
            // recalc VolumeGroup startOffsets
            for (SAPDB_UInt loop=0; loop < m_NumberVolumeGroups; loop++)
            {
                m_ArchiveVolumeGroups[loop] = (m_ArchiveVolumes.GetSize() / m_NumberVolumeGroups) * loop;
            }
        }

        /* update global block counters */
        m_NumDev             ++;
        m_TotalNumBlocks     += DevSize;
        m_TotalNumBlocksFree += DevSize;


        // Security limit is 4 percent of all blocks and not more as 4000 blocks
        m_SecuritySpaceLimit = ( m_TotalNumBlocks - SAPDB_MIN( m_TotalNumBlocks/25, 4000));

        /* reserve blocks on the device for example for the restart record */
        IOMan_ReservedBlockAddressIterator Iter( m_ReservedBlocks, DevNo );

        for( Iter.Begin(); !Iter.End(); ++Iter )
        {
            m_TotalNumBlocksFree --;
            m_DataVolume[DevNo].SetBlockStateToOccupied( Iter.GetBlockAddress().GetBlockNo());
        }

        // After marking the reserved blocks the devspace is usable
        m_NextDevToUseForNewBlock = DevNo;
    }
    return true;
}


/*---------------------------------------------------------------------------*/

void
FBM_Manager::SetClusterStateToFree(
    const tsp00_TaskId         TaskId,
    const IOMan_ClusterAddress &ClusterAddress )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::SetClusterStateToFree", FBM_Trace, 5);

    IOMan_BlockCount pageCnt = ClusterAddress.GetBlockCount();

    for (SAPDB_Int4 loop=0; loop < pageCnt; loop++){
        SetBlockStateToFree(TaskId,ClusterAddress.GetBlockAddress(loop));
    }
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::SetBlockStateToFree(
    const tsp00_TaskId       TaskId,
    const IOMan_BlockAddress &BlockAddress )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::SetBlockStateToFree", FBM_Trace, 5);

    const SAPDB_UInt  DevNo   = BlockAddress.GetDeviceNo();
    const SAPDB_UInt  BlockNo = BlockAddress.GetBlockNo();

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "SetFree: DevNo: " << DevNo << " BlockNo: " << BlockNo);

    /* check that the block containing the restart record is never set to free */
    SAPDBERR_ASSERT_STATE ( ! m_ReservedBlocks.IsMember( BlockAddress ));

    FBM_SynchObject SynchronizeFBM (TaskId);

    /* check device number and get a reference to the current device */
    FBM_IDataVolume & IVolume = m_DataVolume [DevNo];

    if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
        return;

    FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;

    /* check that the current state is  occupied */
    switch (Volume.GetBlockState (BlockNo))
    {
    case BlockState_Occupied:
        /* set block state to free and update all auxilliary structures */
        m_TotalNumBlocksFree ++;
        Volume.SetBlockStateToFree (BlockNo);
        return;
    case BlockState_BackUp:
        if (Volume.GetBlockStateAfterBackup (BlockNo) == BlockState_BackUp)
        {
            /* set  back up block state to free */
            Volume.SetBlockStateAfterBackup (BlockNo, BlockState_Free);
            return;
        }
    }
    /* if none of the cases above was fulfilled an error occured */
    /* write error message into knldiag and vtrace               */
    FBM_IllegalTransition ErrorMessage (__CONTEXT__, DevNo, BlockNo,
                                        Volume.GetBlockState (BlockNo),
                                        Volume.GetBlockStateAfterBackup (BlockNo),
                                        BlockState_Free,
                                        BlockState_Free);
    RTE_Crash(ErrorMessage);
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool
FBM_Manager::SetBlockStateToOccupied (
    const tsp00_TaskId        TaskId,
    IOMan_IBlockAddressIterator &BlockIterator,
    const SAPDB_Bool          bAbortIfErrorOccured)
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::SetBlockStateToOccupied", FBM_Trace, 5);

    FBM_SynchObject SynchronizeFBM (TaskId);

    while (BlockIterator.hasMoreElements()){
        IOMan_BlockAddress BlockAddress = BlockIterator.getNextElement();

        if (!SetBlockStateToOccupiedUnsynched ( BlockAddress, bAbortIfErrorOccured))
        {
            return SAPDB_FALSE;
        }
    }

    return SAPDB_TRUE;
}
SAPDB_Bool
FBM_Manager::SetBlockStateToOccupied(
    const tsp00_TaskId        TaskId,
    const IOMan_BlockAddress &BlockAddress,
    const SAPDB_Bool          bAbortIfErrorOccured)
{
    FBM_SynchObject SynchronizeFBM (TaskId);
    return SetBlockStateToOccupiedUnsynched ( BlockAddress, bAbortIfErrorOccured);
}

SAPDB_Bool
FBM_Manager::SetBlockStateToOccupiedUnsynched (
    const IOMan_BlockAddress &BlockAddress,
    const SAPDB_Bool          bAbortIfErrorOccured)
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::SetBlockStateToOccupied", FBM_Trace, 5);

    const SAPDB_UInt  DevNo   = BlockAddress.GetDeviceNo();
    const SAPDB_UInt  BlockNo = BlockAddress.GetBlockNo();

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "SetOcc: DevNo: " << DevNo << " BlockNo: " << BlockNo );

    /* check device number and get a reference to the current device */
    FBM_IDataVolume & IVolume = m_DataVolume [DevNo];

    /* check that the current state is free */
    if (IVolume.GetBlockState (BlockNo) == BlockState_Free)
    {
        if(RTE_VolumeAccessModeNormal == IVolume.VolMode()) {
            SAPDBERR_ASSERT_STATE ((((FBM_DataVolume&)IVolume).GetBlockStateAfterBackup (BlockNo) == BlockState_Free))
        }

        m_TotalNumBlocksFree --;

        /* set block state to occupied and update all auxilliary structures */
        IVolume.SetBlockStateToOccupied (BlockNo);
        return( SAPDB_TRUE );
    }

    if(RTE_VolumeAccessModeSequential == IVolume.VolMode()) {
        return( SAPDB_TRUE );
    }
    else
    {
        /* if none of the cases above was fulfilled an error occured */
        /* write error message into knldiag and vtrace               */

        FBM_BlockState backupState;
        if(RTE_VolumeAccessModeNormal == IVolume.VolMode())
        {
            backupState = ((FBM_DataVolume&)IVolume).GetBlockStateAfterBackup (BlockNo);
        }
        else
        {
            backupState = BlockState_Free;
        }

        FBM_IllegalTransition errMsg (__CONTEXT__, DevNo, BlockNo,
                                      IVolume.GetBlockState (BlockNo), backupState,
                                      BlockState_Occupied, BlockState_Free);

        if( FBM_ABORT_IF_ERROR == bAbortIfErrorOccured )
            RTE_Crash( errMsg );
        else
            RTE_Message( errMsg );

        return( SAPDB_FALSE );
    }
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool
FBM_Manager::SetBlockStateToBackup(
    const tsp00_TaskId        TaskId,
    const IOMan_BlockAddress &BlockAddress,
    const SAPDB_Bool          bAbortIfErrorOccured)
{
    FBM_SynchObject SynchronizeFBM (TaskId);
    return SetBlockStateToBackupUnsynched ( BlockAddress, bAbortIfErrorOccured);
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool
FBM_Manager::SetBlockStateToBackup (
    const tsp00_TaskId        TaskId,
    IOMan_IBlockAddressIterator &BlockIterator,
    const SAPDB_Bool          bAbortIfErrorOccured)
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::SetBlockStateToBackup", FBM_Trace, 5);

    FBM_SynchObject SynchronizeFBM (TaskId);

    while (BlockIterator.hasMoreElements()){
        IOMan_BlockAddress BlockAddress = BlockIterator.getNextElement();

        if (!SetBlockStateToBackupUnsynched ( BlockAddress, bAbortIfErrorOccured))
        {
            return SAPDB_FALSE;
        }
    }
    return SAPDB_TRUE;
}

/*---------------------------------------------------------------------------*/


SAPDB_Bool
FBM_Manager::SetBlockStateToBackupUnsynched (
    const IOMan_BlockAddress &BlockAddress,
    const SAPDB_Bool          bAbortIfErrorOccured )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::SetBlockStateToBackup", FBM_Trace, 5);

    const SAPDB_UInt  DevNo   = BlockAddress.GetDeviceNo();
    const SAPDB_UInt  BlockNo = BlockAddress.GetBlockNo();

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "SetBackU DevNo " << DevNo << " BlockNo: " << BlockNo );

    /* check that the block containing the restart record is never set to backup */
    SAPDBERR_ASSERT_STATE ( ! m_ReservedBlocks.IsMember( BlockAddress ));

    /* check device number and get a reference to the current device */
    FBM_IDataVolume & IVolume = m_DataVolume [DevNo];

    if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
        return( SAPDB_TRUE );

    FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;

    /* check that no more blocks are marked after reading of the marked blocks has started     */
    /* which is displayed by the iterator m_ActBlockNoForBackup indicating the last read block */
    SAPDBERR_ASSERT_STATE (Volume.m_ActBlockNoForBackup == FBM_UNDEFINED);

    /* check that the current state is occupied */
    if (BlockState_Occupied == Volume.GetBlockState (BlockNo))
    {
        Volume.SetBlockStateToBackUp(BlockNo);
        return( SAPDB_TRUE );
    }

    /* if none of the cases above was fulfilled an error occured */
    /* write error message into knldiag and vtrace */
    FBM_IllegalTransition errMsg (__CONTEXT__, DevNo, BlockNo,
                                  Volume.GetBlockState (BlockNo),
                                  Volume.GetBlockStateAfterBackup (BlockNo),
                                  BlockState_BackUp,
                                  BlockState_Occupied);

    if( FBM_ABORT_IF_ERROR == bAbortIfErrorOccured )
        RTE_Crash( errMsg );
    else
        RTE_Message( errMsg );

    return( SAPDB_FALSE );
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::SetBlockStateToFreeAfterSVP(
    const tsp00_TaskId       TaskId,
    const IOMan_BlockAddress &BlockAddress )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::SetBlockStateToFreeAfterSVP", FBM_Trace, 5);

    const SAPDB_UInt  DevNo   = BlockAddress.GetDeviceNo();
    const SAPDB_UInt  BlockNo = BlockAddress.GetBlockNo();

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "SetFSVP: DevNo: " << DevNo << " BlockNo: " << BlockNo );

    FBM_SynchObject SynchronizeFBM (TaskId);

    /* check that the block containing the restart record is never set to fsvp */
    SAPDBERR_ASSERT_STATE ( ! m_ReservedBlocks.IsMember( BlockAddress ));

    /* check volume number and get a reference to the current volume */
    FBM_IDataVolume & IVolume = m_DataVolume [DevNo];

    if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
        return;

    FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;

    /* check that the current state is  occupied */
    switch (Volume.GetBlockState (BlockNo))
    {
    case BlockState_Occupied:
        Volume.SetBlockStateToFreeAfterSVP (BlockNo);
        ++m_TotalNumBlocksFreeAfterSVP;
        return;
    case BlockState_BackUp:
        {
            switch (Volume.GetBlockStateAfterBackup (BlockNo))
            {
            case BlockState_Occupied:
                Volume.SetBlockStateAfterBackup (BlockNo, BlockState_FreeAfterSVP);
                return;
            case BlockState_BackUp:
                ++m_TotalNumBlocksFreeAfterSVP;
                Volume.SetBlockStateMarkedForCompressionToFreeAfterSVP (BlockNo);
                return;
            }
        }
    }

    /* if none of the cases above was fulfilled an error occured */
    /* write error message into knldiag and vtrace */
    FBM_IllegalTransitionToFreeAfterSVP ErrorMessage (__CONTEXT__, DevNo, BlockNo,
            Volume.GetBlockState (BlockNo),
            Volume.GetBlockStateAfterBackup (BlockNo));
    RTE_Crash(ErrorMessage);
}

/*---------------------------------------------------------------------------*/

IOMan_ClusterAddress
FBM_Manager::GetMultFreeBlocks(
    const tsp00_TaskId  taskId,
    const SAPDB_Int4    NumFreeBlocksWanted,
    const SAPDB_Bool    bReqSequential,
    tgg00_BasisError   &trError)
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::GetMultFreeBlocks", FBM_Trace, 5);

    IOMan_BlockAddress      block;
    IOMan_ClusterAddress    cluster;
    SAPDB_Bool              bTriggerSavepoint = false;
    SAPDB_Int4              NumFreeBlocksSupplied = 0;

    if( ! m_Active )
    {
        trError = e_shutdown;
        return cluster;
    }

    /* This following block is needed to leave the FBM region before entering the     */
    /* log region in the block below. This is done to avoid a dead lock. The FBM      */
    /* region is left when the FBM_SynchObject is destructed at the end of this block */
    {
        FBM_SynchObject SynchronizeFBM( taskId );

        /* get number of a device containing free blocks */
        const SAPDB_Int2 DevNo = GetDeviceWithFreeBlocks (NumFreeBlocksWanted, bReqSequential);

        if (DevNo == FBM_UNDEFINED)
        {
            /* no free block could be found */
            FBM_Exception NoMoreSpace(__CONTEXT__, FBM_NO_MORE_FREE_BLOCKS);
            RTE_Message(NoMoreSpace);
            trError = e_no_more_perm_space;
            return cluster;
        }

        block.SetAddress( DevNo, m_DataVolume [DevNo].GetMultFreeBlocks( NumFreeBlocksWanted,
                          NumFreeBlocksSupplied ));
        cluster.SetCluster( block, NumFreeBlocksSupplied );

        SAPDBTRACE_WRITELN( FBM_Trace, 5, "Free DevNo  " << cluster.GetDeviceNo()
                            << " Free BlockNo: " << cluster.GetBlockNo( 0 ));

        SAPDBTRACE_WRITELN( FBM_Trace, 5, "Wanted Num: " << NumFreeBlocksWanted
                            << " Suppl.Num: " << NumFreeBlocksSupplied );

        /* decrease the counter of free blocks and check if there are too */
        /* few free blocks which means that a savepoint must be triggered */
        m_TotalNumBlocksFree -= NumFreeBlocksSupplied;
        if( SavepointNeeded() )
        {
            bTriggerSavepoint      = true;
            m_SavepointIsRequested = true;
        }
    }

    if( bTriggerSavepoint )
        TriggerSavepoint( taskId );

    return cluster;
}


/*---------------------------------------------------------------------------*/

IOMan_BlockAddress
FBM_Manager::GetFreeBlock( const tsp00_TaskId  taskId,
                           const SAPDB_Bool    bReqSequential )

{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::GetFreeBlock", FBM_Trace, 5);

    SAPDB_Bool         bTriggerSavepoint = false;
    IOMan_BlockAddress BlockAddress;

    /* This following block is needed to leave the FBM region before entering the     */
    /* log region in the block below. This is done to avoid a dead lock. The FBM      */
    /* region is left when the FBM_SynchObject is destructed at the end of this block */
    {
        FBM_SynchObject SynchronizeFBM( taskId );

        /* get number of a device containing free blocks */
        const SAPDB_Int4 cNumFreeBlocksWanted = 1;
        const SAPDB_Int4 DevNo = GetDeviceWithFreeBlocks (cNumFreeBlocksWanted, bReqSequential);

        if (DevNo == FBM_UNDEFINED)
        {
            /* no free block could be found */
            FBM_Exception NoMoreSpace(__CONTEXT__, FBM_NO_MORE_FREE_BLOCKS);
            RTE_Crash(NoMoreSpace);
        }

        BlockAddress.SetAddress (DevNo, m_DataVolume[DevNo].GetFreeBlock());

        SAPDBTRACE_WRITELN( FBM_Trace, 5, "Free DevNo: " << BlockAddress.GetDeviceNo()
                            << " Free BlockNo: " << BlockAddress.GetBlockNo() );

        /* decrease the counter of free blocks and check if there are too */
        /* few free blocks which means that a savepoint must be triggered */
        -- m_TotalNumBlocksFree;

        if( SavepointNeeded() )
        {
            bTriggerSavepoint      = true;
            m_SavepointIsRequested = true;
        }
    }

    if( bTriggerSavepoint )
        TriggerSavepoint( taskId );

    return BlockAddress;
}

/*---------------------------------------------------------------------------*/

SAPDB_Int4
FBM_Manager::GetFreeVolumeInGroup(
    const SAPDB_UInt CurrentVolumeGroup,
    const SAPDB_Int2 NumRequestedBlocks )
{
    SAPDB_UInt VolumesPerGroup;
    SAPDB_Int4 VolWithFreeBlocks = FBM_UNDEFINED;
    SAPDB_UInt VolIndex;

    SAPDBERR_ASSERT_STATE (g01is_archive());

    if (0 == m_ArchiveVolumes.GetSize() % m_NumberVolumeGroups)
    {
        VolumesPerGroup = m_ArchiveVolumes.GetSize() / m_NumberVolumeGroups;
    }
    else
    {
        VolumesPerGroup = m_ArchiveVolumes.GetSize() / m_NumberVolumeGroups + 1;
    }


    if (CurrentVolumeGroup * VolumesPerGroup >= m_ArchiveVolumes.GetSize())
    {
        return FBM_UNDEFINED;
    }

    VolIndex = m_ArchiveVolumeGroups[CurrentVolumeGroup];

    do
    {
        SAPDBERR_ASSERT_STATE (RTE_VolumeAccessModeSequential == m_DataVolume[m_ArchiveVolumes[VolIndex]].VolMode())

        if (m_DataVolume[m_ArchiveVolumes[VolIndex]].GetNumBlocksFree() < NumRequestedBlocks)
        {
            SAPDBTRACE_WRITELN( FBM_Trace, 5, "Archive Volume Full: " << m_ArchiveVolumes[VolIndex] );
            VolIndex++;
            if ((VolIndex >= ((CurrentVolumeGroup+1)*VolumesPerGroup)) || (VolIndex >=m_ArchiveVolumes.GetSize()))
            {
                VolIndex = CurrentVolumeGroup*VolumesPerGroup;
            }
        }
        else
        {
            VolWithFreeBlocks = m_ArchiveVolumes[VolIndex];
        }
    } while ((FBM_UNDEFINED == VolWithFreeBlocks) && (VolIndex != m_ArchiveVolumeGroups[CurrentVolumeGroup]));

    m_ArchiveVolumeGroups[CurrentVolumeGroup] = VolIndex;

    return VolWithFreeBlocks;
}

/*---------------------------------------------------------------------------*/

SAPDB_Int2
FBM_Manager::GetDeviceWithFreeBlocks(
    const SAPDB_Int2 NumRequestedBlocks,
    const SAPDB_Bool bReqSequential )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::GetDeviceWithFreeBlocks", FBM_Trace, 5);

    SAPDB_Int2  iDevNo;
    SAPDB_Int4  VolWithFreeBlocks  = FBM_UNDEFINED;
    SAPDB_UInt  DevicesFound       = 0;

    if (bReqSequential)// archive Volume
    {
        SAPDB_UInt StartVolumeGroup = m_CurArchiveVolumeGroup;

        do
        {
            m_CurArchiveVolumeGroup++;
            if (m_CurArchiveVolumeGroup >= m_NumberVolumeGroups)
            {
                m_CurArchiveVolumeGroup = 0;
            }

            VolWithFreeBlocks = GetFreeVolumeInGroup(m_CurArchiveVolumeGroup, NumRequestedBlocks);

            if (FBM_UNDEFINED != VolWithFreeBlocks)
            {
                DevicesFound++;
            }
        }while((0 == DevicesFound) && (m_CurArchiveVolumeGroup != StartVolumeGroup));
    }
    else
    {
        /* search a data device which has still NumRequestedBlocks free blocks */
        SAPDB_Int2  SearchBegDevNo     = m_NextDevToUseForNewBlock;

        iDevNo                         = SearchBegDevNo;

        SAPDBTRACE_WRITELN( FBM_Trace, 5, "FirstDevNo: " << FirstDevNo()
                            << " NumDevs: " << m_NumDev );

        SAPDBTRACE_WRITELN( FBM_Trace, 5, "SearchBegDev: " << SearchBegDevNo
                            << " BlocksWanted: " << NumRequestedBlocks );

        /* loop over all devices until a device with NumRequestedBlocks free blocks is found */
        do
        {
            if (RTE_VolumeAccessModeNormal == m_DataVolume[iDevNo].VolMode())
            {
                FBM_DataVolume & Volume = (FBM_DataVolume&) m_DataVolume[iDevNo];
                if (Volume.GetNumBlocksFree() < NumRequestedBlocks)
                {
                    SAPDBTRACE_WRITELN( FBM_Trace, 5, "Device Full: " << iDevNo);
                }
                else
                {
                    DevicesFound       = 1;
                    VolWithFreeBlocks = iDevNo;

                    /* if this device contains too few used blocks this device is choosen imediately.   */
                    /* Otherwise this device is skipped this time and the search is continued. However, */
                    /* m_NumBlocksToAddUntilOptIsReached is changed so that this device is choosen      */
                    /* next time it is looked at                                                        */
                    if (Volume.m_NumBlocksToAddUntilOptIsReached > 0)
                        break;
                    else
                        Volume.m_NumBlocksToAddUntilOptIsReached = 1;
                }

            }
            /* goto next device */
            if ((++iDevNo) > LastDevNo()) iDevNo = FirstDevNo();
        }
        while (iDevNo != SearchBegDevNo) ;

        /* return device or throw exception if there is no device with enough free blocks */
        if (0 != DevicesFound)
        {
            /* set device where to start the next search */
            if (VolWithFreeBlocks == LastDevNo())
                m_NextDevToUseForNewBlock = FirstDevNo();
            else
                m_NextDevToUseForNewBlock = VolWithFreeBlocks+1;
        }
    }
    if (0 == DevicesFound)
    {
        VolWithFreeBlocks = FBM_UNDEFINED;
    }

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "Free_Device: " << VolWithFreeBlocks);

    return (VolWithFreeBlocks) ;
}


/*---------------------------------------------------------------------------*/

SAPDB_Int4
FBM_Manager::NumBlocksMarkedForBackup( const tsp00_TaskId taskId ) const
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::NumBlocksMarkedForBackup", FBM_Trace, 5);

    SAPDB_Int4 NumAllBlocksMarkedForBackUp = 0;

    FBM_SynchObject SynchronizeFBM( taskId );

    for (SAPDB_Int4 iDevNo=FirstDevNo(); iDevNo<=LastDevNo(); ++iDevNo)
    {
        const FBM_IDataVolume & IVolume = m_DataVolume[iDevNo];

        if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
            continue;

        const FBM_DataVolume & Volume = (FBM_DataVolume &) IVolume;

#       ifdef SAPDB_SLOW 
        if (Volume.m_NumBlocksMarkedForBackup > 0)
            SAPDBTRACE_WRITELN( FBM_Trace, 5, "bup blocks  " <<  Volume.m_NumBlocksMarkedForBackup);
#       endif

        NumAllBlocksMarkedForBackUp += Volume.m_NumBlocksMarkedForBackup;
    }
    return NumAllBlocksMarkedForBackUp;
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::RestoreAllBlockStatesMarkedForBackup( const tsp00_TaskId TaskId )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::RestoreAllBlockStatesMarkedForBackup", FBM_Trace, 5);

    SAPDB_Int4      iDevNo;
    SAPDB_Int4      NumBlocksRestoredToFreeAfterSVP;

    FBM_SynchObject SynchronizeFBM (TaskId);

    for (iDevNo=FirstDevNo(); iDevNo<=LastDevNo(); iDevNo++)
    {
        FBM_IDataVolume &IVolume = m_DataVolume[iDevNo];

        if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
            continue;

        FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;

        if (Volume.m_NumBlocksMarkedForBackup > 0)
        {
            Volume.RestoreAllBlockStatesMarkedForBackup(NumBlocksRestoredToFreeAfterSVP);
            m_TotalNumBlocksFreeAfterSVP += NumBlocksRestoredToFreeAfterSVP;
        }
    }
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::RestoreBlockStateMarkedForBackup(
    const tsp00_TaskId       TaskId,
    const IOMan_BlockAddress &BlockAddress)
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::RestoreBlockStateMarkedForBackup", FBM_Trace, 5);

    const SAPDB_UInt DevNo   = BlockAddress.GetDeviceNo();
    const SAPDB_UInt BlockNo = BlockAddress.GetBlockNo();

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "RestoreB Dev: " << DevNo
                        << " BlockNo: " <<BlockNo );

    SAPDB_Bool bRestoredStateIsFreeAfterSVP;

    FBM_SynchObject SynchronizeFBM (TaskId);

    /* check device number and get a reference to the current device */
    FBM_IDataVolume & IVolume = m_DataVolume [DevNo];

    if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
        return;

    FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;

    if (BlockState_BackUp == Volume.GetBlockState(BlockNo))
    {
        Volume.RestoreBlockStateMarkedForBackup (BlockNo, bRestoredStateIsFreeAfterSVP);

        if (bRestoredStateIsFreeAfterSVP)
            ++m_TotalNumBlocksFreeAfterSVP;
        return;
    }

    /* if none of the cases above was fulfilled an error occured */
    /* write error message into knldiag and vtrace               */
    FBM_IllegalRestoreAfterBackup ErrorMesage (__CONTEXT__, DevNo, BlockNo,
            Volume.GetBlockState(BlockNo),
            Volume.GetBlockStateAfterBackup(BlockNo));
    RTE_Crash(ErrorMesage);
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::RestoreBlockStateMarkedForCompression(
    const tsp00_TaskId  TaskId,
    const SAPDB_Int2    DevNo,
    const SAPDB_Int4    BlockNo,
    SAPDB_Bool          &bBlockInUse )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::RestoreBlockStateMarkedForCompression", FBM_Trace, 5);

    /* this method checks if a given block is in the state  [backup,backup] */
    /* which means that it is marked for compression and still in use then  */
    /* the original state is restored, the block can also be in the state   */
    /* [freeaftersavepouint, free] which means that there is nothing to do  */

    /* initialize return value */
    bBlockInUse = false;

    FBM_SynchObject SynchronizeFBM (TaskId);

    /* check device number and get a reference to the current device */
    FBM_IDataVolume & IVolume = m_DataVolume [DevNo];

    if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
        return;

    FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;

    const FBM_BlockState BlockState            = Volume.GetBlockState           (BlockNo);
    const FBM_BlockState BlockStateAfterBackup = Volume.GetBlockStateAfterBackup(BlockNo);

    switch (BlockState)
    {
    case  BlockState_BackUp:
        {
            switch (BlockStateAfterBackup)
            {
            case BlockState_BackUp:
                /* restore the state to occupied*/
                Volume.SetBlockStateMarkedForCompressionToOccupied (BlockNo);
                bBlockInUse = true;
                return;
            case BlockState_Free:
                /* restore state to free */
                Volume.SetBlockStateMarkedForCompressionToFree (BlockNo);
                return;
            }
        }
        break;
    case BlockState_FreeAfterSVP:
        SAPDBERR_ASSERT_STATE (BlockStateAfterBackup == BlockState_Free);
        return;
    }

    /* if none of the cases above was fulfilled an error occured */
    /* write error message into knldiag and vtrace               */
    FBM_IllegalRestoreAfterCompression ErrorMesage (__CONTEXT__, DevNo, BlockNo, BlockState, BlockStateAfterBackup);
    RTE_Crash(ErrorMesage);
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::SetAllBlocksMarkedAsFreeAfterSVPToFree( const tsp00_TaskId TaskId )
{
    /* go through all registered devices an set the states of all */
    /* blocks which are marked as free after savepoint to free    */

    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::SetAllBlocksMarkedAsFreeAfterSVPToFree", FBM_Trace, 5);

    FBM_SynchObject SynchronizeFBM (TaskId);

    SAPDB_Int4      iDevNo;

    for (iDevNo=FirstDevNo(); iDevNo<=LastDevNo(); iDevNo++)
    {
        FBM_IDataVolume &IVolume = m_DataVolume [iDevNo];

        if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
            continue;

        FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;

        if (Volume.m_NumBlocksFreeAfterSVP > 0)
        {
            /* update global counters of all blocks  free and free after save point */
            m_TotalNumBlocksFreeAfterSVP -= Volume.m_NumBlocksFreeAfterSVP;
            m_TotalNumBlocksFree         += Volume.m_NumBlocksFreeAfterSVP;

            Volume.SetAllBlocksMarkedAsFreeAfterSVPToFree();
        }
    }

    /* update counters which control which devices are prefered when free blocks are   */
    /* requested  (getfreeblock) depending on the number of used blocks on each device */
    ChangeDeviceUsagePreferences ();

    /* reset the trigger flag,i.e. from now the routine GetFreeBlock */
    /* can trigger again a savepoint if there are too few blocks     */
    m_SavepointIsRequested = false;

    /* check that the global counter of all blocks which are free are svp is zero now */
    SAPDBERR_ASSERT_STATE (m_TotalNumBlocksFreeAfterSVP == 0)
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::GetNextBlocksForBackUp(
    const tsp00_TaskId taskId,
    const SAPDB_Int2   DevNo,
    const SAPDB_Int4   MaxNumBlocksWanted,
    SAPDB_Int4        &SuppliedNumBlocks,
    SAPDB_Int4        &BlockNo,
    tgg00_BasisError  &trError)
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::GetNextBlocksForBackUp", FBM_Trace, 5);

    if( ! m_Active )
    {
        trError = e_shutdown;
        return;
    }
    /* this routine supplies a set of SuppliedNumBlocks  */
    /* neighbouring blocks  marked for backup            */
    /* see method BeginReadingBlocksMarkedForBackUp      */

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "DevNo: " << DevNo);

    FBM_SynchObject SynchronizeFBM( taskId );

    /* check device number and get a reference to the current device */
    FBM_IDataVolume & iDevice = m_DataVolume [DevNo];

    if (RTE_VolumeAccessModeNormal != iDevice.VolMode())
        return;

    FBM_DataVolume &Device = (FBM_DataVolume&) iDevice;

    /* get blocks marked for backup */
    Device.GetNextBlocksForBackUp (MaxNumBlocksWanted, SuppliedNumBlocks, BlockNo);

    SAPDBTRACE_WRITELN( FBM_Trace, 5, "num_found: " << SuppliedNumBlocks
                        << " BlockNo: " << BlockNo );
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::BeginReadingBlocksMarkedForBackUp( const tsp00_TaskId TaskId )
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::BeginReadingBlocksMarkedForBackUp", FBM_Trace, 5);

    /* this routine sets the  position (iterator)                                     */
    /* m_ActBlockNoForBackup from where to start to look for blocks marked for backup */
    /* first position on the device. All blocks marked for backup can than be fetched */
    /* by calling GetNextBlocksForBackUp until this function returns no more blocks   */

    FBM_SynchObject SynchronizeFBM (TaskId);

    for (int iDevNo=FirstDevNo(); iDevNo<=LastDevNo(); ++iDevNo)
    {
        FBM_IDataVolume & IVolume = m_DataVolume[iDevNo];

        if (RTE_VolumeAccessModeNormal != IVolume.VolMode())
            continue;

        FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;

        SAPDBERR_ASSERT_STATE (Volume.m_ActBlockNoForBackup == FBM_UNDEFINED);

        if (Volume.m_NumBlocksMarkedForBackup > 0)
        {
            Volume.m_ActBlockNoForBackup = 0;
        }
    }
}
/*---------------------------------------------------------------------------*/

void
FBM_Manager::Dump(
    const tsp00_TaskId    taskId,
    Kernel_Dump           &dump ) const
{
    struct FBMManager   fbmMan;

    FBM_SynchObject SynchronizeFBM( taskId );

    fbmMan.dmpActive                     = m_Active;
    fbmMan.dmpSvpIsTriggered             = m_SavepointIsRequested;
    fbmMan.dmpFiller1                    = 0;
    fbmMan.dmpMaxNumDev                  = m_MaxNumDev;
    fbmMan.dmpNumDev                     = m_NumDev;
    fbmMan.dmpSecurityLimit              = m_SecuritySpaceLimit;
    fbmMan.dmpTotalNumBlocksFreeAfterSVP = m_TotalNumBlocksFreeAfterSVP;
    fbmMan.dmpTotalNumBlocksFree         = m_TotalNumBlocksFree;
    fbmMan.dmpTotalNumBlocks             = m_TotalNumBlocks;
    fbmMan.dmpTotalUsedBlocks            = m_TotalNumBlocks - m_TotalNumBlocksFree
                                           - m_TotalNumBlocksFreeAfterSVP;
    fbmMan.dmpNextDevToUseForNewBlock    = m_NextDevToUseForNewBlock;
    fbmMan.dmpNextDevToUseForFragBlock   = m_NextDevToUseForFragBlock;

    dump.InsertEntry( Kernel_Dump::DmpFBMManager,
                      Kernel_DumpPage::Entry( &fbmMan, sizeof( fbmMan )));

    if( m_Active )
    {
        for( SAPDB_Int4 devNo = FirstDevNo(); devNo <= LastDevNo(); ++devNo )
            m_DataVolume[ devNo ].Dump( dump, devNo );
    }
}

/*===========================================================================*
 *  PRIVATE METHODS                                                          *
 *===========================================================================*/

void
FBM_Manager::ChangeDeviceUsagePreferences ()
{
    SAPDBTRACE_ROUTINE_DEBUG ("FBM_Manager::ChangeDeviceUsagePreferences", FBM_Trace, 5);

    /* update counters which control which devices are prefered when free blocks are   */
    /* requested  (getfreeblock) depending on the number of used blocks on each device */
    /* the prefrences are calculated to get optimum parallel read accesses that means  */
    /* all devices should accomodate the same number of blocks                         */
    SAPDB_Int4 MaxNumBlocksUsed = 0;
    SAPDB_Int4 iDevNo;

    /* search maximum number of blocks on one device */
    //    MaxNumBlocksUsed = m_DataVolume [FirstDevNo()].m_NumBlocksUsed;
    for (iDevNo=FirstDevNo(); iDevNo<=LastDevNo(); ++iDevNo)
    {
        FBM_IDataVolume &IVolume = m_DataVolume [iDevNo];

        if (RTE_VolumeAccessModeNormal == IVolume.VolMode())
        {
            FBM_DataVolume & Volume = (FBM_DataVolume&) IVolume;
            if (MaxNumBlocksUsed < Volume.m_NumBlocksUsed){
                MaxNumBlocksUsed = Volume.m_NumBlocksUsed;
            }
        }
    }

    /* calculate the number of used blocks to add to each */
    /* device that all devices have the same occupation   */

    for (iDevNo=FirstDevNo(); iDevNo<=LastDevNo(); iDevNo++)
    {
        FBM_IDataVolume & IVolume = m_DataVolume [iDevNo];

        if (RTE_VolumeAccessModeNormal == IVolume.VolMode())
        {
            FBM_DataVolume &Volume = (FBM_DataVolume&) IVolume;
            Volume.m_NumBlocksToAddUntilOptIsReached = (MaxNumBlocksUsed - Volume.m_NumBlocksUsed);

            SAPDBTRACE_WRITELN( FBM_Trace, 5, "DevNo: " << iDevNo
                                << " OptAddNumBlo: " << Volume.m_NumBlocksToAddUntilOptIsReached );
        }
    }

    m_NextDevToUseForFragBlock = FirstDevNo();
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::ResetMembersToNullAndZero()
{
    m_Active                     = false;
    m_MaxNumDev                  = FBM_UNDEFINED;
    m_SecuritySpaceLimit         = 0;
    m_TotalNumBlocksFreeAfterSVP = 0;
    m_TotalNumBlocksFree         = 0;
    m_TotalNumBlocks             = 0;
    m_NumDev                     = 0;
    m_SavepointIsRequested       = false;
    m_NextDevToUseForNewBlock    = FBM_UNDEFINED;
    m_NextDevToUseForFragBlock   = FBM_UNDEFINED;
    m_LastArchiveVolumeUsed      = FBM_UNDEFINED;
    m_CurArchiveVolumeGroup      = 0;
    m_NumberVolumeGroups         = 0;

    m_ArchiveVolumes.Clear();
    m_ArchiveVolumeGroups.Clear();
}

/*---------------------------------------------------------------------------*/

void
FBM_Manager::TriggerSavepoint( const tsp00_TaskId taskId )
{
    tgg00_TransContext  trans;

    trans.trTaskId_gg00 = taskId;
    trans.trSessionId_gg00.becomes( cgg_nil_session );
    trans.trTransId_gg00.becomes( cgg_nil_trans );
    trans.trWriteTransId_gg00.becomes( cgg_nil_trans );
    trans.trSubtransId_gg00 = cgg_zero_subtrans;

    Log_SavepointManager.StartSavepoint( trans, Log_SVPReasonFBM );
}

/*===========================================================================*
 *  END OF CODE                                                              *
 *===========================================================================*/
