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