| /* ----> 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 | |
| ret = RedImapEBlockGet(bMR, ulBlock, pfAllocated); | |
| #endif | |
| } | |
| 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 | |
| ret = RedImapEBlockSet(ulBlock, fAllocated); | |
| #endif | |
| /* 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; | |
| } | |