]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/Reliance-Edge/core/driver/imap.c
6375988b3046c311897804a43dd0010a774b449c
[freertos] / FreeRTOS-Plus / Source / Reliance-Edge / core / driver / imap.c
1 /*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
2 \r
3                    Copyright (c) 2014-2015 Datalight, Inc.\r
4                        All Rights Reserved Worldwide.\r
5 \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
9 \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
14 \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
18 */\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
23     more information.\r
24 */\r
25 /** @file\r
26     @brief Implements allocation routines.\r
27 \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
31 */\r
32 #include <redfs.h>\r
33 #include <redcore.h>\r
34 \r
35 \r
36 /** @brief Get the allocation bit of a block from either metaroot.\r
37 \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
40 \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
44                         of the block.\r
45 \r
46     @return A negated ::REDSTATUS code indicating the operation result.\r
47 \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
52 */\r
53 REDSTATUS RedImapBlockGet(\r
54     uint8_t     bMR,\r
55     uint32_t    ulBlock,\r
56     bool       *pfAllocated)\r
57 {\r
58     REDSTATUS   ret;\r
59 \r
60     if(    (bMR > 1U)\r
61         || (ulBlock < gpRedCoreVol->ulInodeTableStartBN)\r
62         || (ulBlock >= gpRedVolume->ulBlockCount)\r
63         || (pfAllocated == NULL))\r
64     {\r
65         REDERROR();\r
66         ret = -RED_EINVAL;\r
67     }\r
68     else\r
69     {\r
70       #if (REDCONF_IMAP_INLINE == 1) && (REDCONF_IMAP_EXTERNAL == 1)\r
71         if(gpRedCoreVol->fImapInline)\r
72         {\r
73             ret = RedImapIBlockGet(bMR, ulBlock, pfAllocated);\r
74         }\r
75         else\r
76         {\r
77             ret = RedImapEBlockGet(bMR, ulBlock, pfAllocated);\r
78         }\r
79       #elif REDCONF_IMAP_INLINE == 1\r
80         ret = RedImapIBlockGet(bMR, ulBlock, pfAllocated);\r
81       #else\r
82         ret = RedImapEBlockGet(bMR, ulBlock, pfAllocated);\r
83       #endif\r
84     }\r
85 \r
86     return ret;\r
87 }\r
88 \r
89 \r
90 #if REDCONF_READ_ONLY == 0\r
91 /** @brief Set the allocation bit of a block in the working metaroot.\r
92 \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
95 \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
98 \r
99     @return A negated ::REDSTATUS code indicating the operation result.\r
100 \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
105 */\r
106 REDSTATUS RedImapBlockSet(\r
107     uint32_t    ulBlock,\r
108     bool        fAllocated)\r
109 {\r
110     REDSTATUS   ret;\r
111 \r
112     if(    (ulBlock < gpRedCoreVol->ulInodeTableStartBN)\r
113         || (ulBlock >= gpRedVolume->ulBlockCount))\r
114     {\r
115         REDERROR();\r
116         ret = -RED_EINVAL;\r
117     }\r
118     else if(    (ulBlock >= gpRedCoreVol->ulFirstAllocableBN)\r
119              && (    (fAllocated && (gpRedMR->ulFreeBlocks == 0U))\r
120                   || ((!fAllocated) && (gpRedMR->ulFreeBlocks >= gpRedVolume->ulBlocksAllocable))))\r
121     {\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
125         */\r
126         CRITICAL_ERROR();\r
127         ret = -RED_EFUBAR;\r
128     }\r
129     else\r
130     {\r
131       #if (REDCONF_IMAP_INLINE == 1) && (REDCONF_IMAP_EXTERNAL == 1)\r
132         if(gpRedCoreVol->fImapInline)\r
133         {\r
134             ret = RedImapIBlockSet(ulBlock, fAllocated);\r
135         }\r
136         else\r
137         {\r
138             ret = RedImapEBlockSet(ulBlock, fAllocated);\r
139         }\r
140       #elif REDCONF_IMAP_INLINE == 1\r
141         ret = RedImapIBlockSet(ulBlock, fAllocated);\r
142       #else\r
143         ret = RedImapEBlockSet(ulBlock, fAllocated);\r
144       #endif\r
145 \r
146         /*  Any change to the allocation state of a block indicates that the\r
147             volume is now branched.\r
148         */\r
149         gpRedCoreVol->fBranched = true;\r
150     }\r
151 \r
152     /*  If a block was marked as no longer in use, discard it from the buffers.\r
153     */\r
154     if((ret == 0) && (!fAllocated))\r
155     {\r
156         ret = RedBufferDiscardRange(ulBlock, 1U);\r
157         CRITICAL_ASSERT(ret == 0);\r
158     }\r
159 \r
160     /*  Adjust the free/almost free block count if the block was allocable.\r
161     */\r
162     if((ret == 0) && (ulBlock >= gpRedCoreVol->ulFirstAllocableBN))\r
163     {\r
164         if(fAllocated)\r
165         {\r
166             gpRedMR->ulFreeBlocks--;\r
167         }\r
168         else\r
169         {\r
170             bool fWasAllocated;\r
171 \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
175             */\r
176             ret = RedImapBlockGet(1U - gpRedCoreVol->bCurMR, ulBlock, &fWasAllocated);\r
177             CRITICAL_ASSERT(ret == 0);\r
178 \r
179             if(ret == 0)\r
180             {\r
181                 if(fWasAllocated)\r
182                 {\r
183                     gpRedCoreVol->ulAlmostFreeBlocks++;\r
184                 }\r
185                 else\r
186                 {\r
187                     gpRedMR->ulFreeBlocks++;\r
188                 }\r
189             }\r
190         }\r
191     }\r
192 \r
193     return ret;\r
194 }\r
195 \r
196 \r
197 /** @brief Allocate one block.\r
198 \r
199     @param pulBlock On successful return, populated with the allocated block\r
200                     number.\r
201 \r
202     @return A negated ::REDSTATUS code indicating the operation result.\r
203 \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
208 */\r
209 REDSTATUS RedImapAllocBlock(\r
210     uint32_t   *pulBlock)\r
211 {\r
212     REDSTATUS   ret;\r
213 \r
214     if(pulBlock == NULL)\r
215     {\r
216         REDERROR();\r
217         ret = -RED_EINVAL;\r
218     }\r
219     else if(gpRedMR->ulFreeBlocks == 0U)\r
220     {\r
221         ret = -RED_ENOSPC;\r
222     }\r
223     else\r
224     {\r
225         uint32_t ulStopBlock = gpRedMR->ulAllocNextBlock;\r
226         bool     fAllocated = false;\r
227 \r
228         do\r
229         {\r
230             ALLOCSTATE state;\r
231 \r
232             ret = RedImapBlockState(gpRedMR->ulAllocNextBlock, &state);\r
233             CRITICAL_ASSERT(ret == 0);\r
234 \r
235             if(ret == 0)\r
236             {\r
237                 if(state == ALLOCSTATE_FREE)\r
238                 {\r
239                     ret = RedImapBlockSet(gpRedMR->ulAllocNextBlock, true);\r
240                     CRITICAL_ASSERT(ret == 0);\r
241 \r
242                     *pulBlock = gpRedMR->ulAllocNextBlock;\r
243                     fAllocated = true;\r
244                 }\r
245 \r
246                 /*  Increment the next block number, wrapping it when the end of\r
247                     the volume is reached.\r
248                 */\r
249                 gpRedMR->ulAllocNextBlock++;\r
250                 if(gpRedMR->ulAllocNextBlock == gpRedVolume->ulBlockCount)\r
251                 {\r
252                     gpRedMR->ulAllocNextBlock = gpRedCoreVol->ulFirstAllocableBN;\r
253                 }\r
254             }\r
255         }\r
256         while((ret == 0) && !fAllocated && (gpRedMR->ulAllocNextBlock != ulStopBlock));\r
257 \r
258         if((ret == 0) && !fAllocated)\r
259         {\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
263             */\r
264             CRITICAL_ERROR();\r
265             ret = -RED_EFUBAR;\r
266         }\r
267     }\r
268 \r
269     return ret;\r
270 }\r
271 #endif /* REDCONF_READ_ONLY == 0 */\r
272 \r
273 \r
274 /** @brief Get the allocation state of a block.\r
275 \r
276     Takes into account the allocation bits from both metaroots, and returns one\r
277     of four possible allocation state values:\r
278 \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
283 \r
284     @param ulBlock  The block number to query.\r
285     @param pState   On successful return, populated with the state of the block.\r
286 \r
287     @return A negated ::REDSTATUS code indicating the operation result.\r
288 \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
292 */\r
293 REDSTATUS RedImapBlockState(\r
294     uint32_t    ulBlock,\r
295     ALLOCSTATE *pState)\r
296 {\r
297     REDSTATUS   ret;\r
298 \r
299     if(    (ulBlock < gpRedCoreVol->ulInodeTableStartBN)\r
300         || (ulBlock >= gpRedVolume->ulBlockCount)\r
301         || (pState == NULL))\r
302     {\r
303         REDERROR();\r
304         ret = -RED_EINVAL;\r
305     }\r
306     else\r
307     {\r
308         bool fBitCurrent;\r
309 \r
310         ret = RedImapBlockGet(gpRedCoreVol->bCurMR, ulBlock, &fBitCurrent);\r
311 \r
312         if(ret == 0)\r
313         {\r
314             bool fBitOld;\r
315 \r
316             ret = RedImapBlockGet(1U - gpRedCoreVol->bCurMR, ulBlock, &fBitOld);\r
317 \r
318             if(ret == 0)\r
319             {\r
320                 if(fBitCurrent)\r
321                 {\r
322                     if(fBitOld)\r
323                     {\r
324                         *pState = ALLOCSTATE_USED;\r
325                     }\r
326                     else\r
327                     {\r
328                         *pState = ALLOCSTATE_NEW;\r
329                     }\r
330                 }\r
331                 else\r
332                 {\r
333                     if(fBitOld)\r
334                     {\r
335                         *pState = ALLOCSTATE_AFREE;\r
336                     }\r
337                     else\r
338                     {\r
339                         *pState = ALLOCSTATE_FREE;\r
340                     }\r
341                 }\r
342             }\r
343         }\r
344     }\r
345 \r
346     return ret;\r
347 }\r
348 \r