1 /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
\r
3 Copyright (c) 2014-2015 Datalight, Inc.
\r
4 All Rights Reserved Worldwide.
\r
6 This program is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; use version 2 of the License.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
\r
12 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU General Public License for more details.
\r
15 You should have received a copy of the GNU General Public License along
\r
16 with this program; if not, write to the Free Software Foundation, Inc.,
\r
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
19 /* Businesses and individuals that for commercial or other reasons cannot
\r
20 comply with the terms of the GPLv2 license may obtain a commercial license
\r
21 before incorporating Reliance Edge into proprietary software for
\r
22 distribution in any form. Visit http://www.datalight.com/reliance-edge for
\r
26 @brief Implements allocation routines.
\r
28 This module implements routines for working with the imap, a bitmap which
\r
29 tracks which blocks are allocated or free. Some of the functionality is
\r
30 delegated to imapinline.c and imapextern.c.
\r
33 #include <redcore.h>
\r
36 /** @brief Get the allocation bit of a block from either metaroot.
\r
38 Will pass the call down either to the inline imap or to the external imap
\r
39 implementation, whichever is appropriate for the current volume.
\r
41 @param bMR The metaroot index: either 0 or 1.
\r
42 @param ulBlock The block number to query.
\r
43 @param pfAllocated On successful return, populated with the allocation bit
\r
46 @return A negated ::REDSTATUS code indicating the operation result.
\r
48 @retval 0 Operation was successful.
\r
49 @retval -RED_EINVAL @p bMR is out of range; or @p ulBlock is out of range;
\r
50 or @p pfAllocated is `NULL`.
\r
51 @retval -RED_EIO A disk I/O error occurred.
\r
53 REDSTATUS RedImapBlockGet(
\r
61 || (ulBlock < gpRedCoreVol->ulInodeTableStartBN)
\r
62 || (ulBlock >= gpRedVolume->ulBlockCount)
\r
63 || (pfAllocated == NULL))
\r
70 #if (REDCONF_IMAP_INLINE == 1) && (REDCONF_IMAP_EXTERNAL == 1)
\r
71 if(gpRedCoreVol->fImapInline)
\r
73 ret = RedImapIBlockGet(bMR, ulBlock, pfAllocated);
\r
77 ret = RedImapEBlockGet(bMR, ulBlock, pfAllocated);
\r
79 #elif REDCONF_IMAP_INLINE == 1
\r
80 ret = RedImapIBlockGet(bMR, ulBlock, pfAllocated);
\r
82 ret = RedImapEBlockGet(bMR, ulBlock, pfAllocated);
\r
90 #if REDCONF_READ_ONLY == 0
\r
91 /** @brief Set the allocation bit of a block in the working metaroot.
\r
93 Will pass the call down either to the inline imap or to the external imap
\r
94 implementation, whichever is appropriate for the current volume.
\r
96 @param ulBlock The block number to allocate or free.
\r
97 @param fAllocated Whether to allocate the block (true) or free it (false).
\r
99 @return A negated ::REDSTATUS code indicating the operation result.
\r
101 @retval 0 Operation was successful.
\r
102 @retval -RED_EINVAL @p ulBlock is out of range; or @p ulBlock is allocable
\r
103 and @p fAllocated is 1.
\r
104 @retval -RED_EIO A disk I/O error occurred.
\r
106 REDSTATUS RedImapBlockSet(
\r
112 if( (ulBlock < gpRedCoreVol->ulInodeTableStartBN)
\r
113 || (ulBlock >= gpRedVolume->ulBlockCount))
\r
118 else if( (ulBlock >= gpRedCoreVol->ulFirstAllocableBN)
\r
119 && ( (fAllocated && (gpRedMR->ulFreeBlocks == 0U))
\r
120 || ((!fAllocated) && (gpRedMR->ulFreeBlocks >= gpRedVolume->ulBlocksAllocable))))
\r
122 /* Attempting either to free more blocks than are allocable, or
\r
123 allocate a block when there are none available. This could indicate
\r
124 metadata corruption.
\r
131 #if (REDCONF_IMAP_INLINE == 1) && (REDCONF_IMAP_EXTERNAL == 1)
\r
132 if(gpRedCoreVol->fImapInline)
\r
134 ret = RedImapIBlockSet(ulBlock, fAllocated);
\r
138 ret = RedImapEBlockSet(ulBlock, fAllocated);
\r
140 #elif REDCONF_IMAP_INLINE == 1
\r
141 ret = RedImapIBlockSet(ulBlock, fAllocated);
\r
143 ret = RedImapEBlockSet(ulBlock, fAllocated);
\r
146 /* Any change to the allocation state of a block indicates that the
\r
147 volume is now branched.
\r
149 gpRedCoreVol->fBranched = true;
\r
152 /* If a block was marked as no longer in use, discard it from the buffers.
\r
154 if((ret == 0) && (!fAllocated))
\r
156 ret = RedBufferDiscardRange(ulBlock, 1U);
\r
157 CRITICAL_ASSERT(ret == 0);
\r
160 /* Adjust the free/almost free block count if the block was allocable.
\r
162 if((ret == 0) && (ulBlock >= gpRedCoreVol->ulFirstAllocableBN))
\r
166 gpRedMR->ulFreeBlocks--;
\r
170 bool fWasAllocated;
\r
172 /* Whether the block became free or almost free depends on its
\r
173 previous allocation state. If it was used, then it is now
\r
174 almost free. Otherwise, it was new and is now free.
\r
176 ret = RedImapBlockGet(1U - gpRedCoreVol->bCurMR, ulBlock, &fWasAllocated);
\r
177 CRITICAL_ASSERT(ret == 0);
\r
183 gpRedCoreVol->ulAlmostFreeBlocks++;
\r
187 gpRedMR->ulFreeBlocks++;
\r
197 /** @brief Allocate one block.
\r
199 @param pulBlock On successful return, populated with the allocated block
\r
202 @return A negated ::REDSTATUS code indicating the operation result.
\r
204 @retval 0 Operation was successful.
\r
205 @retval -RED_EINVAL @p pulBlock is `NULL`.
\r
206 @retval -RED_EIO A disk I/O error occurred.
\r
207 @retval -RED_ENOSPC Insufficient free space to perform the allocation.
\r
209 REDSTATUS RedImapAllocBlock(
\r
210 uint32_t *pulBlock)
\r
214 if(pulBlock == NULL)
\r
219 else if(gpRedMR->ulFreeBlocks == 0U)
\r
225 uint32_t ulStopBlock = gpRedMR->ulAllocNextBlock;
\r
226 bool fAllocated = false;
\r
232 ret = RedImapBlockState(gpRedMR->ulAllocNextBlock, &state);
\r
233 CRITICAL_ASSERT(ret == 0);
\r
237 if(state == ALLOCSTATE_FREE)
\r
239 ret = RedImapBlockSet(gpRedMR->ulAllocNextBlock, true);
\r
240 CRITICAL_ASSERT(ret == 0);
\r
242 *pulBlock = gpRedMR->ulAllocNextBlock;
\r
246 /* Increment the next block number, wrapping it when the end of
\r
247 the volume is reached.
\r
249 gpRedMR->ulAllocNextBlock++;
\r
250 if(gpRedMR->ulAllocNextBlock == gpRedVolume->ulBlockCount)
\r
252 gpRedMR->ulAllocNextBlock = gpRedCoreVol->ulFirstAllocableBN;
\r
256 while((ret == 0) && !fAllocated && (gpRedMR->ulAllocNextBlock != ulStopBlock));
\r
258 if((ret == 0) && !fAllocated)
\r
260 /* The free block count was already determined to be non-zero, no
\r
261 error occurred while looking for free blocks, but no free blocks
\r
262 were found. This indicates metadata corruption.
\r
271 #endif /* REDCONF_READ_ONLY == 0 */
\r
274 /** @brief Get the allocation state of a block.
\r
276 Takes into account the allocation bits from both metaroots, and returns one
\r
277 of four possible allocation state values:
\r
279 - ::ALLOCSTATE_FREE Free and may be allocated; writeable.
\r
280 - ::ALLOCSTATE_USED In-use and transacted; not writeable.
\r
281 - ::ALLOCSTATE_NEW In-use but not transacted; writeable.
\r
282 - ::ALLOCSTATE_AFREE Will become free after a transaction; not writeable.
\r
284 @param ulBlock The block number to query.
\r
285 @param pState On successful return, populated with the state of the block.
\r
287 @return A negated ::REDSTATUS code indicating the operation result.
\r
289 @retval 0 Operation was successful.
\r
290 @retval -RED_EINVAL @p ulBlock is out of range; or @p pState is `NULL`.
\r
291 @retval -RED_EIO A disk I/O error occurred.
\r
293 REDSTATUS RedImapBlockState(
\r
295 ALLOCSTATE *pState)
\r
299 if( (ulBlock < gpRedCoreVol->ulInodeTableStartBN)
\r
300 || (ulBlock >= gpRedVolume->ulBlockCount)
\r
301 || (pState == NULL))
\r
310 ret = RedImapBlockGet(gpRedCoreVol->bCurMR, ulBlock, &fBitCurrent);
\r
316 ret = RedImapBlockGet(1U - gpRedCoreVol->bCurMR, ulBlock, &fBitOld);
\r
324 *pState = ALLOCSTATE_USED;
\r
328 *pState = ALLOCSTATE_NEW;
\r
335 *pState = ALLOCSTATE_AFREE;
\r
339 *pState = ALLOCSTATE_FREE;
\r