1 /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
2 *
3 * Copyright (c) 2014-2015 Datalight, Inc.
4 * All Rights Reserved Worldwide.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; use version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /* Businesses and individuals that for commercial or other reasons cannot
21 * comply with the terms of the GPLv2 license may obtain a commercial license
22 * before incorporating Reliance Edge into proprietary software for
23 * distribution in any form. Visit http://www.datalight.com/reliance-edge for
24 * more information.
25 */
26
27 /** @file
28 * @brief Implements allocation routines.
29 *
30 * This module implements routines for working with the imap, a bitmap which
31 * tracks which blocks are allocated or free. Some of the functionality is
32 * delegated to imapinline.c and imapextern.c.
33 */
34 #include <redfs.h>
35 #include <redcore.h>
36
37
38 /** @brief Get the allocation bit of a block from either metaroot.
39 *
40 * Will pass the call down either to the inline imap or to the external imap
41 * implementation, whichever is appropriate for the current volume.
42 *
43 * @param bMR The metaroot index: either 0 or 1.
44 * @param ulBlock The block number to query.
45 * @param pfAllocated On successful return, populated with the allocation bit
46 * of the block.
47 *
48 * @return A negated ::REDSTATUS code indicating the operation result.
49 *
50 * @retval 0 Operation was successful.
51 * @retval -RED_EINVAL @p bMR is out of range; or @p ulBlock is out of range;
52 * or @p pfAllocated is `NULL`.
53 * @retval -RED_EIO A disk I/O error occurred.
54 */
RedImapBlockGet(uint8_t bMR,uint32_t ulBlock,bool * pfAllocated)55 REDSTATUS RedImapBlockGet( uint8_t bMR,
56 uint32_t ulBlock,
57 bool * pfAllocated )
58 {
59 REDSTATUS ret;
60
61 if( ( bMR > 1U ) ||
62 ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) ||
63 ( ulBlock >= gpRedVolume->ulBlockCount ) ||
64 ( pfAllocated == NULL ) )
65 {
66 REDERROR();
67 ret = -RED_EINVAL;
68 }
69 else
70 {
71 #if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 )
72 if( gpRedCoreVol->fImapInline )
73 {
74 ret = RedImapIBlockGet( bMR, ulBlock, pfAllocated );
75 }
76 else
77 {
78 ret = RedImapEBlockGet( bMR, ulBlock, pfAllocated );
79 }
80 #elif REDCONF_IMAP_INLINE == 1
81 ret = RedImapIBlockGet( bMR, ulBlock, pfAllocated );
82 #else /* if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 ) */
83 ret = RedImapEBlockGet( bMR, ulBlock, pfAllocated );
84 #endif /* if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 ) */
85 }
86
87 return ret;
88 }
89
90
91 #if REDCONF_READ_ONLY == 0
92
93 /** @brief Set the allocation bit of a block in the working metaroot.
94 *
95 * Will pass the call down either to the inline imap or to the external imap
96 * implementation, whichever is appropriate for the current volume.
97 *
98 * @param ulBlock The block number to allocate or free.
99 * @param fAllocated Whether to allocate the block (true) or free it (false).
100 *
101 * @return A negated ::REDSTATUS code indicating the operation result.
102 *
103 * @retval 0 Operation was successful.
104 * @retval -RED_EINVAL @p ulBlock is out of range; or @p ulBlock is allocable
105 * and @p fAllocated is 1.
106 * @retval -RED_EIO A disk I/O error occurred.
107 */
RedImapBlockSet(uint32_t ulBlock,bool fAllocated)108 REDSTATUS RedImapBlockSet( uint32_t ulBlock,
109 bool fAllocated )
110 {
111 REDSTATUS ret;
112
113 if( ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) ||
114 ( ulBlock >= gpRedVolume->ulBlockCount ) )
115 {
116 REDERROR();
117 ret = -RED_EINVAL;
118 }
119 else if( ( ulBlock >= gpRedCoreVol->ulFirstAllocableBN ) &&
120 ( ( fAllocated && ( gpRedMR->ulFreeBlocks == 0U ) ) ||
121 ( ( !fAllocated ) && ( gpRedMR->ulFreeBlocks >= gpRedVolume->ulBlocksAllocable ) ) ) )
122 {
123 /* Attempting either to free more blocks than are allocable, or
124 * allocate a block when there are none available. This could indicate
125 * metadata corruption.
126 */
127 CRITICAL_ERROR();
128 ret = -RED_EFUBAR;
129 }
130 else
131 {
132 #if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 )
133 if( gpRedCoreVol->fImapInline )
134 {
135 ret = RedImapIBlockSet( ulBlock, fAllocated );
136 }
137 else
138 {
139 ret = RedImapEBlockSet( ulBlock, fAllocated );
140 }
141 #elif REDCONF_IMAP_INLINE == 1
142 ret = RedImapIBlockSet( ulBlock, fAllocated );
143 #else /* if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 ) */
144 ret = RedImapEBlockSet( ulBlock, fAllocated );
145 #endif /* if ( REDCONF_IMAP_INLINE == 1 ) && ( REDCONF_IMAP_EXTERNAL == 1 ) */
146
147 /* Any change to the allocation state of a block indicates that the
148 * volume is now branched.
149 */
150 gpRedCoreVol->fBranched = true;
151 }
152
153 /* If a block was marked as no longer in use, discard it from the buffers.
154 */
155 if( ( ret == 0 ) && ( !fAllocated ) )
156 {
157 ret = RedBufferDiscardRange( ulBlock, 1U );
158 CRITICAL_ASSERT( ret == 0 );
159 }
160
161 /* Adjust the free/almost free block count if the block was allocable.
162 * Discard the block if required.
163 */
164 if( ( ret == 0 ) && ( ulBlock >= gpRedCoreVol->ulFirstAllocableBN ) )
165 {
166 if( fAllocated )
167 {
168 gpRedMR->ulFreeBlocks--;
169 }
170 else
171 {
172 bool fWasAllocated;
173
174 /* Whether the block became free or almost free depends on its
175 * previous allocation state. If it was used, then it is now
176 * almost free. Otherwise, it was new and is now free.
177 */
178 ret = RedImapBlockGet( 1U - gpRedCoreVol->bCurMR, ulBlock, &fWasAllocated );
179 CRITICAL_ASSERT( ret == 0 );
180
181 if( ret == 0 )
182 {
183 if( fWasAllocated )
184 {
185 gpRedCoreVol->ulAlmostFreeBlocks++;
186 }
187 else
188 {
189 gpRedMR->ulFreeBlocks++;
190 }
191 }
192 }
193 }
194
195 return ret;
196 }
197
198
199 /** @brief Allocate one block.
200 *
201 * @param pulBlock On successful return, populated with the allocated block
202 * number.
203 *
204 * @return A negated ::REDSTATUS code indicating the operation result.
205 *
206 * @retval 0 Operation was successful.
207 * @retval -RED_EINVAL @p pulBlock is `NULL`.
208 * @retval -RED_EIO A disk I/O error occurred.
209 * @retval -RED_ENOSPC Insufficient free space to perform the allocation.
210 */
RedImapAllocBlock(uint32_t * pulBlock)211 REDSTATUS RedImapAllocBlock( uint32_t * pulBlock )
212 {
213 REDSTATUS ret;
214
215 if( pulBlock == NULL )
216 {
217 REDERROR();
218 ret = -RED_EINVAL;
219 }
220 else if( gpRedMR->ulFreeBlocks == 0U )
221 {
222 ret = -RED_ENOSPC;
223 }
224 else
225 {
226 uint32_t ulStopBlock = gpRedMR->ulAllocNextBlock;
227 bool fAllocated = false;
228
229 do
230 {
231 ALLOCSTATE state;
232
233 ret = RedImapBlockState( gpRedMR->ulAllocNextBlock, &state );
234 CRITICAL_ASSERT( ret == 0 );
235
236 if( ret == 0 )
237 {
238 if( state == ALLOCSTATE_FREE )
239 {
240 ret = RedImapBlockSet( gpRedMR->ulAllocNextBlock, true );
241 CRITICAL_ASSERT( ret == 0 );
242
243 *pulBlock = gpRedMR->ulAllocNextBlock;
244 fAllocated = true;
245 }
246
247 /* Increment the next block number, wrapping it when the end of
248 * the volume is reached.
249 */
250 gpRedMR->ulAllocNextBlock++;
251
252 if( gpRedMR->ulAllocNextBlock == gpRedVolume->ulBlockCount )
253 {
254 gpRedMR->ulAllocNextBlock = gpRedCoreVol->ulFirstAllocableBN;
255 }
256 }
257 }
258 while( ( ret == 0 ) && !fAllocated && ( gpRedMR->ulAllocNextBlock != ulStopBlock ) );
259
260 if( ( ret == 0 ) && !fAllocated )
261 {
262 /* The free block count was already determined to be non-zero, no
263 * error occurred while looking for free blocks, but no free blocks
264 * were found. This indicates metadata corruption.
265 */
266 CRITICAL_ERROR();
267 ret = -RED_EFUBAR;
268 }
269 }
270
271 return ret;
272 }
273 #endif /* REDCONF_READ_ONLY == 0 */
274
275
276 /** @brief Get the allocation state of a block.
277 *
278 * Takes into account the allocation bits from both metaroots, and returns one
279 * of four possible allocation state values:
280 *
281 * - ::ALLOCSTATE_FREE Free and may be allocated; writeable.
282 * - ::ALLOCSTATE_USED In-use and transacted; not writeable.
283 * - ::ALLOCSTATE_NEW In-use but not transacted; writeable.
284 * - ::ALLOCSTATE_AFREE Will become free after a transaction; not writeable.
285 *
286 * @param ulBlock The block number to query.
287 * @param pState On successful return, populated with the state of the block.
288 *
289 * @return A negated ::REDSTATUS code indicating the operation result.
290 *
291 * @retval 0 Operation was successful.
292 * @retval -RED_EINVAL @p ulBlock is out of range; or @p pState is `NULL`.
293 * @retval -RED_EIO A disk I/O error occurred.
294 */
RedImapBlockState(uint32_t ulBlock,ALLOCSTATE * pState)295 REDSTATUS RedImapBlockState( uint32_t ulBlock,
296 ALLOCSTATE * pState )
297 {
298 REDSTATUS ret;
299
300 if( ( ulBlock < gpRedCoreVol->ulInodeTableStartBN ) ||
301 ( ulBlock >= gpRedVolume->ulBlockCount ) ||
302 ( pState == NULL ) )
303 {
304 REDERROR();
305 ret = -RED_EINVAL;
306 }
307 else
308 {
309 bool fBitCurrent;
310
311 ret = RedImapBlockGet( gpRedCoreVol->bCurMR, ulBlock, &fBitCurrent );
312
313 if( ret == 0 )
314 {
315 bool fBitOld;
316
317 ret = RedImapBlockGet( 1U - gpRedCoreVol->bCurMR, ulBlock, &fBitOld );
318
319 if( ret == 0 )
320 {
321 if( fBitCurrent )
322 {
323 if( fBitOld )
324 {
325 *pState = ALLOCSTATE_USED;
326 }
327 else
328 {
329 *pState = ALLOCSTATE_NEW;
330 }
331 }
332 else
333 {
334 if( fBitOld )
335 {
336 *pState = ALLOCSTATE_AFREE;
337 }
338 else
339 {
340 *pState = ALLOCSTATE_FREE;
341 }
342 }
343 }
344 }
345 }
346
347 return ret;
348 }
349