]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/Reliance-Edge/core/driver/imapextern.c
Update version numbers in preparation for new release.
[freertos] / FreeRTOS-Plus / Source / Reliance-Edge / core / driver / imapextern.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 routines for the external imap.\r
27 \r
28     The external imap is used on volumes that are too big for the imap bitmap\r
29     to be stored entirely in the metaroot, so instead the bitmap is stored in\r
30     imap nodes on disk, and the metaroot bitmap is used to toggle between imap\r
31     nodes.\r
32 */\r
33 #include <redfs.h>\r
34 \r
35 #if REDCONF_IMAP_EXTERNAL == 1\r
36 \r
37 #include <redcore.h>\r
38 \r
39 \r
40 #if REDCONF_READ_ONLY == 0\r
41 static REDSTATUS ImapNodeBranch(uint32_t ulImapNode, IMAPNODE **ppImap);\r
42 static bool ImapNodeIsBranched(uint32_t ulImapNode);\r
43 #endif\r
44 \r
45 \r
46 /** @brief Get the allocation bit of a block from the imap as it exists in\r
47            either metaroot.\r
48 \r
49     @param bMR          The metaroot index: either 0 or 1.\r
50     @param ulBlock      The block number to query.\r
51     @param pfAllocated  On successful exit, populated with the allocation bit\r
52                         of the block.\r
53 \r
54     @return A negated ::REDSTATUS code indicating the operation result.\r
55 \r
56     @retval 0           Operation was successful.\r
57     @retval -RED_EINVAL @p bMR is out of range; or @p ulBlock is out of range;\r
58                         or @p pfAllocated is `NULL`.\r
59     @retval -RED_EIO    A disk I/O error occurred.\r
60 */\r
61 REDSTATUS RedImapEBlockGet(\r
62     uint8_t     bMR,\r
63     uint32_t    ulBlock,\r
64     bool       *pfAllocated)\r
65 {\r
66     REDSTATUS   ret;\r
67 \r
68     if(    gpRedCoreVol->fImapInline\r
69         || (bMR > 1U)\r
70         || (ulBlock < gpRedCoreVol->ulInodeTableStartBN)\r
71         || (ulBlock >= gpRedVolume->ulBlockCount)\r
72         || (pfAllocated == NULL))\r
73     {\r
74         REDERROR();\r
75         ret = -RED_EINVAL;\r
76     }\r
77     else\r
78     {\r
79         uint32_t    ulOffset = ulBlock - gpRedCoreVol->ulInodeTableStartBN;\r
80         uint32_t    ulImapNode = ulOffset / IMAPNODE_ENTRIES;\r
81         uint8_t     bMRToRead = bMR;\r
82         IMAPNODE   *pImap;\r
83 \r
84       #if REDCONF_READ_ONLY == 0\r
85         /*  If the imap node is not branched, then both copies of the imap are\r
86             identical.  If the old metaroot copy is requested, use the current\r
87             copy instead, since it is more likely to be buffered.\r
88         */\r
89         if(bMR == (1U - gpRedCoreVol->bCurMR))\r
90         {\r
91             if(!ImapNodeIsBranched(ulImapNode))\r
92             {\r
93                 bMRToRead = 1U - bMR;\r
94             }\r
95         }\r
96       #endif\r
97 \r
98         ret = RedBufferGet(RedImapNodeBlock(bMRToRead, ulImapNode), BFLAG_META_IMAP, CAST_VOID_PTR_PTR(&pImap));\r
99 \r
100         if(ret == 0)\r
101         {\r
102             *pfAllocated = RedBitGet(pImap->abEntries, ulOffset % IMAPNODE_ENTRIES);\r
103 \r
104             RedBufferPut(pImap);\r
105         }\r
106     }\r
107 \r
108     return ret;\r
109 }\r
110 \r
111 \r
112 #if REDCONF_READ_ONLY == 0\r
113 /** @brief Set the allocation bit of a block in the working-state imap.\r
114 \r
115     @param ulBlock      The block number to allocate or free.\r
116     @param fAllocated   Whether to allocate the block (true) or free it (false).\r
117 \r
118     @return A negated ::REDSTATUS code indicating the operation result.\r
119 \r
120     @retval 0           Operation was successful.\r
121     @retval -RED_EINVAL @p ulBlock is out of range.\r
122     @retval -RED_EIO    A disk I/O error occurred.\r
123 */\r
124 REDSTATUS RedImapEBlockSet(\r
125     uint32_t    ulBlock,\r
126     bool        fAllocated)\r
127 {\r
128     REDSTATUS   ret;\r
129 \r
130     if(    gpRedCoreVol->fImapInline\r
131         || (ulBlock < gpRedCoreVol->ulInodeTableStartBN)\r
132         || (ulBlock >= gpRedVolume->ulBlockCount))\r
133     {\r
134         REDERROR();\r
135         ret = -RED_EINVAL;\r
136     }\r
137     else\r
138     {\r
139         uint32_t    ulOffset = ulBlock - gpRedCoreVol->ulInodeTableStartBN;\r
140         uint32_t    ulImapNode = ulOffset / IMAPNODE_ENTRIES;\r
141         IMAPNODE   *pImap;\r
142 \r
143         ret = ImapNodeBranch(ulImapNode, &pImap);\r
144 \r
145         if(ret == 0)\r
146         {\r
147             uint32_t ulImapEntry = ulOffset % IMAPNODE_ENTRIES;\r
148 \r
149             if(RedBitGet(pImap->abEntries, ulImapEntry) == fAllocated)\r
150             {\r
151                 /*  The driver shouldn't ever set a bit in the imap to its\r
152                     current value.  That shouldn't ever be needed, and it\r
153                     indicates that the driver is doing unnecessary I/O, or\r
154                     that the imap is corrupt.\r
155                 */\r
156                 CRITICAL_ERROR();\r
157                 ret = -RED_EFUBAR;\r
158             }\r
159             else if(fAllocated)\r
160             {\r
161                 RedBitSet(pImap->abEntries, ulImapEntry);\r
162             }\r
163             else\r
164             {\r
165                 RedBitClear(pImap->abEntries, ulImapEntry);\r
166             }\r
167 \r
168             RedBufferPut(pImap);\r
169         }\r
170     }\r
171 \r
172     return ret;\r
173 }\r
174 \r
175 \r
176 /** @brief Branch an imap node and get a buffer for it.\r
177 \r
178     If the imap node is already branched, it can be overwritten in its current\r
179     location, and this function just gets it buffered dirty.  If the node is not\r
180     already branched, the metaroot must be updated to toggle the imap node to\r
181     its alternate location, thereby preserving the committed state copy of the\r
182     imap node.\r
183 \r
184     @param ulImapNode   The imap node to branch and buffer.\r
185     @param ppImap       On successful return, populated with the imap node\r
186                         buffer, which will be marked dirty.\r
187 \r
188     @return A negated ::REDSTATUS code indicating the operation result.\r
189 \r
190     @retval 0           Operation was successful.\r
191     @retval -RED_EINVAL @p ulImapNode is out of range; or @p ppImap is `NULL`.\r
192     @retval -RED_EIO    A disk I/O error occurred.\r
193 */\r
194 static REDSTATUS ImapNodeBranch(\r
195     uint32_t    ulImapNode,\r
196     IMAPNODE  **ppImap)\r
197 {\r
198     REDSTATUS   ret;\r
199 \r
200     if((ulImapNode >= gpRedCoreVol->ulImapNodeCount) || (ppImap == NULL))\r
201     {\r
202         REDERROR();\r
203         ret = -RED_EINVAL;\r
204     }\r
205     else if(ImapNodeIsBranched(ulImapNode))\r
206     {\r
207         /*  Imap node is already branched, so just get it buffered dirty.\r
208         */\r
209         ret = RedBufferGet(RedImapNodeBlock(gpRedCoreVol->bCurMR, ulImapNode), BFLAG_META_IMAP | BFLAG_DIRTY, CAST_VOID_PTR_PTR(ppImap));\r
210     }\r
211     else\r
212     {\r
213         uint32_t    ulBlockCurrent;\r
214         uint32_t    ulBlockOld;\r
215 \r
216         /*  The metaroot currently points to the committed state imap node.\r
217             Toggle the metaroot to point at the alternate, writeable location.\r
218         */\r
219         if(RedBitGet(gpRedMR->abEntries, ulImapNode))\r
220         {\r
221             RedBitClear(gpRedMR->abEntries, ulImapNode);\r
222         }\r
223         else\r
224         {\r
225             RedBitSet(gpRedMR->abEntries, ulImapNode);\r
226         }\r
227 \r
228         ulBlockCurrent = RedImapNodeBlock(gpRedCoreVol->bCurMR, ulImapNode);\r
229         ulBlockOld     = RedImapNodeBlock(1U - gpRedCoreVol->bCurMR, ulImapNode);\r
230 \r
231         ret = RedBufferDiscardRange(ulBlockCurrent, 1U);\r
232 \r
233         /*  Buffer the committed copy then reassign the block number to the\r
234             writeable location.  This also dirties the buffer.\r
235         */\r
236         if(ret == 0)\r
237         {\r
238             ret = RedBufferGet(ulBlockOld, BFLAG_META_IMAP, CAST_VOID_PTR_PTR(ppImap));\r
239 \r
240             if(ret == 0)\r
241             {\r
242                 RedBufferBranch(*ppImap, ulBlockCurrent);\r
243             }\r
244         }\r
245     }\r
246 \r
247     return ret;\r
248 }\r
249 \r
250 \r
251 /** @brief Determine whether an imap node is branched.\r
252 \r
253     If the imap node is branched, it can be overwritten in its current location.\r
254 \r
255     @param ulImapNode   The imap node to examine.\r
256 \r
257     @return Whether the imap node is branched.\r
258 */\r
259 static bool ImapNodeIsBranched(\r
260     uint32_t    ulImapNode)\r
261 {\r
262     bool        fNodeBitSetInMetaroot0 = RedBitGet(gpRedCoreVol->aMR[0U].abEntries, ulImapNode);\r
263     bool        fNodeBitSetInMetaroot1 = RedBitGet(gpRedCoreVol->aMR[1U].abEntries, ulImapNode);\r
264 \r
265     /*  If the imap node is not branched, both metaroots will point to the same\r
266         copy of the node.\r
267     */\r
268     return fNodeBitSetInMetaroot0 != fNodeBitSetInMetaroot1;\r
269 }\r
270 #endif /* REDCONF_READ_ONLY == 0 */\r
271 \r
272 \r
273 /** @brief Calculate the block number of the imap node location indicated by the\r
274            given metaroot.\r
275 \r
276     An imap node has two locations on disk.  A bit in the metaroot bitmap\r
277     indicates which location is the valid one, according to that metaroot.  This\r
278     function returns the block number of the imap node which is valid in the\r
279     given metaroot.\r
280 \r
281     @param bMR          Which metaroot to examine.\r
282     @param ulImapNode   The imap node for which to calculate the block number.\r
283 \r
284     @return Block number of the imap node, as indicated by the given metaroot.\r
285 */\r
286 uint32_t RedImapNodeBlock(\r
287     uint8_t     bMR,\r
288     uint32_t    ulImapNode)\r
289 {\r
290     uint32_t    ulBlock;\r
291 \r
292     REDASSERT(ulImapNode < gpRedCoreVol->ulImapNodeCount);\r
293 \r
294     ulBlock = gpRedCoreVol->ulImapStartBN + (ulImapNode * 2U);\r
295 \r
296     if(bMR > 1U)\r
297     {\r
298         REDERROR();\r
299     }\r
300     else if(RedBitGet(gpRedCoreVol->aMR[bMR].abEntries, ulImapNode))\r
301     {\r
302         /*  Bit is set, so point ulBlock at the second copy of the node.\r
303         */\r
304         ulBlock++;\r
305     }\r
306     else\r
307     {\r
308         /*  ulBlock already points at the first copy of the node.\r
309         */\r
310     }\r
311 \r
312     return ulBlock;\r
313 }\r
314 \r
315 #endif /* REDCONF_IMAP_EXTERNAL == 1 */\r
316 \r