mirror of
https://github.com/FreeRTOS/FreeRTOS.git
synced 2025-08-24 19:31:03 +08:00

* 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.
349 lines
12 KiB
C
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;
|
|
}
|