Files
Soren Ptak 3a2f6646f0 Use CI-CD-Github-Actions for spelling and formatting, add in the bot formatting action, update the CI-CD workflow files. Fix incorrect spelling and formatting on files. (#1083)
* Use new version of CI-CD Actions,  checkout@v3 instead of checkout@v2 on all jobs
* Use cSpell spell check, and use ubuntu-20.04 for formatting check
* Add in bot formatting action
* Update freertos_demo.yml and freertos_plus_demo.yml files to increase github log readability
* Add in a Qemu demo onto the workflows.
2023-09-06 12:35:37 -07:00

349 lines
12 KiB
C

/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
*
* Copyright (c) 2014-2015 Datalight, Inc.
* All Rights Reserved Worldwide.
*
* 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; use version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but "AS-IS," 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Businesses and individuals that for commercial or other reasons cannot
* comply with the terms of the GPLv2 license may obtain a commercial license
* before incorporating Reliance Edge into proprietary software for
* distribution in any form. Visit http://www.datalight.com/reliance-edge for
* more information.
*/
/** @file
* @brief Implements allocation routines.
*
* This module implements routines for working with the imap, a bitmap which
* tracks which blocks are allocated or free. Some of the functionality is
* delegated to imapinline.c and imapextern.c.
*/
#include <redfs.h>
#include <redcore.h>
/** @brief Get the allocation bit of a block from either metaroot.
*
* Will pass the call down either to the inline imap or to the external imap
* implementation, whichever is appropriate for the current volume.
*
* @param bMR The metaroot index: either 0 or 1.
* @param ulBlock The block number to query.
* @param pfAllocated On successful return, populated with the allocation bit
* of the block.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p bMR is out of range; or @p ulBlock is out of range;
* or @p pfAllocated is `NULL`.
* @retval -RED_EIO A disk I/O error occurred.
*/
REDSTATUS RedImapBlockGet( uint8_t bMR,
uint32_t ulBlock,
bool * pfAllocated )
{
REDSTATUS ret;
if( ( bMR > 1U ) ||
( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) ||
( ulBlock >= gpRedVolume->ulBlockCount ) ||
( pfAllocated == NULL ) )
{
REDERROR();
ret = -RED_EINVAL;
}
else
{
#if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 )
if( gpRedCoreVol->fImapInline )
{
ret = RedImapIBlockGet( bMR, ulBlock, pfAllocated );
}
else
{
ret = RedImapEBlockGet( bMR, ulBlock, pfAllocated );
}
#elif REDCONF_IMAP_INLINE == 1
ret = RedImapIBlockGet( bMR, ulBlock, pfAllocated );
#else /* if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 ) */
ret = RedImapEBlockGet( bMR, ulBlock, pfAllocated );
#endif /* if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 ) */
}
return ret;
}
#if REDCONF_READ_ONLY == 0
/** @brief Set the allocation bit of a block in the working metaroot.
*
* Will pass the call down either to the inline imap or to the external imap
* implementation, whichever is appropriate for the current volume.
*
* @param ulBlock The block number to allocate or free.
* @param fAllocated Whether to allocate the block (true) or free it (false).
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p ulBlock is out of range; or @p ulBlock is allocable
* and @p fAllocated is 1.
* @retval -RED_EIO A disk I/O error occurred.
*/
REDSTATUS RedImapBlockSet( uint32_t ulBlock,
bool fAllocated )
{
REDSTATUS ret;
if( ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) ||
( ulBlock >= gpRedVolume->ulBlockCount ) )
{
REDERROR();
ret = -RED_EINVAL;
}
else if( ( ulBlock >= gpRedCoreVol->ulFirstAllocableBN ) &&
( ( fAllocated && ( gpRedMR->ulFreeBlocks == 0U ) ) ||
( ( !fAllocated ) && ( gpRedMR->ulFreeBlocks >= gpRedVolume->ulBlocksAllocable ) ) ) )
{
/* Attempting either to free more blocks than are allocable, or
* allocate a block when there are none available. This could indicate
* metadata corruption.
*/
CRITICAL_ERROR();
ret = -RED_EFUBAR;
}
else
{
#if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 )
if( gpRedCoreVol->fImapInline )
{
ret = RedImapIBlockSet( ulBlock, fAllocated );
}
else
{
ret = RedImapEBlockSet( ulBlock, fAllocated );
}
#elif REDCONF_IMAP_INLINE == 1
ret = RedImapIBlockSet( ulBlock, fAllocated );
#else /* if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 ) */
ret = RedImapEBlockSet( ulBlock, fAllocated );
#endif /* if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 ) */
/* Any change to the allocation state of a block indicates that the
* volume is now branched.
*/
gpRedCoreVol->fBranched = true;
}
/* If a block was marked as no longer in use, discard it from the buffers.
*/
if( ( ret == 0 ) && ( !fAllocated ) )
{
ret = RedBufferDiscardRange( ulBlock, 1U );
CRITICAL_ASSERT( ret == 0 );
}
/* Adjust the free/almost free block count if the block was allocable.
* Discard the block if required.
*/
if( ( ret == 0 ) && ( ulBlock >= gpRedCoreVol->ulFirstAllocableBN ) )
{
if( fAllocated )
{
gpRedMR->ulFreeBlocks--;
}
else
{
bool fWasAllocated;
/* Whether the block became free or almost free depends on its
* previous allocation state. If it was used, then it is now
* almost free. Otherwise, it was new and is now free.
*/
ret = RedImapBlockGet( 1U - gpRedCoreVol->bCurMR, ulBlock, &fWasAllocated );
CRITICAL_ASSERT( ret == 0 );
if( ret == 0 )
{
if( fWasAllocated )
{
gpRedCoreVol->ulAlmostFreeBlocks++;
}
else
{
gpRedMR->ulFreeBlocks++;
}
}
}
}
return ret;
}
/** @brief Allocate one block.
*
* @param pulBlock On successful return, populated with the allocated block
* number.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p pulBlock is `NULL`.
* @retval -RED_EIO A disk I/O error occurred.
* @retval -RED_ENOSPC Insufficient free space to perform the allocation.
*/
REDSTATUS RedImapAllocBlock( uint32_t * pulBlock )
{
REDSTATUS ret;
if( pulBlock == NULL )
{
REDERROR();
ret = -RED_EINVAL;
}
else if( gpRedMR->ulFreeBlocks == 0U )
{
ret = -RED_ENOSPC;
}
else
{
uint32_t ulStopBlock = gpRedMR->ulAllocNextBlock;
bool fAllocated = false;
do
{
ALLOCSTATE state;
ret = RedImapBlockState( gpRedMR->ulAllocNextBlock, &state );
CRITICAL_ASSERT( ret == 0 );
if( ret == 0 )
{
if( state == ALLOCSTATE_FREE )
{
ret = RedImapBlockSet( gpRedMR->ulAllocNextBlock, true );
CRITICAL_ASSERT( ret == 0 );
*pulBlock = gpRedMR->ulAllocNextBlock;
fAllocated = true;
}
/* Increment the next block number, wrapping it when the end of
* the volume is reached.
*/
gpRedMR->ulAllocNextBlock++;
if( gpRedMR->ulAllocNextBlock == gpRedVolume->ulBlockCount )
{
gpRedMR->ulAllocNextBlock = gpRedCoreVol->ulFirstAllocableBN;
}
}
}
while( ( ret == 0 ) && !fAllocated && ( gpRedMR->ulAllocNextBlock != ulStopBlock ) );
if( ( ret == 0 ) && !fAllocated )
{
/* The free block count was already determined to be non-zero, no
* error occurred while looking for free blocks, but no free blocks
* were found. This indicates metadata corruption.
*/
CRITICAL_ERROR();
ret = -RED_EFUBAR;
}
}
return ret;
}
#endif /* REDCONF_READ_ONLY == 0 */
/** @brief Get the allocation state of a block.
*
* Takes into account the allocation bits from both metaroots, and returns one
* of four possible allocation state values:
*
* - ::ALLOCSTATE_FREE Free and may be allocated; writeable.
* - ::ALLOCSTATE_USED In-use and transacted; not writeable.
* - ::ALLOCSTATE_NEW In-use but not transacted; writeable.
* - ::ALLOCSTATE_AFREE Will become free after a transaction; not writeable.
*
* @param ulBlock The block number to query.
* @param pState On successful return, populated with the state of the block.
*
* @return A negated ::REDSTATUS code indicating the operation result.
*
* @retval 0 Operation was successful.
* @retval -RED_EINVAL @p ulBlock is out of range; or @p pState is `NULL`.
* @retval -RED_EIO A disk I/O error occurred.
*/
REDSTATUS RedImapBlockState( uint32_t ulBlock,
ALLOCSTATE * pState )
{
REDSTATUS ret;
if( ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) ||
( ulBlock >= gpRedVolume->ulBlockCount ) ||
( pState == NULL ) )
{
REDERROR();
ret = -RED_EINVAL;
}
else
{
bool fBitCurrent;
ret = RedImapBlockGet( gpRedCoreVol->bCurMR, ulBlock, &fBitCurrent );
if( ret == 0 )
{
bool fBitOld;
ret = RedImapBlockGet( 1U - gpRedCoreVol->bCurMR, ulBlock, &fBitOld );
if( ret == 0 )
{
if( fBitCurrent )
{
if( fBitOld )
{
*pState = ALLOCSTATE_USED;
}
else
{
*pState = ALLOCSTATE_NEW;
}
}
else
{
if( fBitOld )
{
*pState = ALLOCSTATE_AFREE;
}
else
{
*pState = ALLOCSTATE_FREE;
}
}
}
}
}
return ret;
}