summaryrefslogtreecommitdiff
path: root/FreeRTOS-Plus/Source/Reliance-Edge/core/driver/imap.c
blob: 87664d12dc351e4bb9211b9d20784b7fb2e5f749 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                   Copyright (c) 2014-2015 Datalight, Inc.
                       All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license may obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
    @brief Implements allocation routines.

    This module implements routines for working with the imap, a bitmap which
    tracks which blocks are allocated or free.  Some of the functionality is
    delegated to imapinline.c and imapextern.c.
*/
#include <redfs.h>
#include <redcore.h>


/** @brief Get the allocation bit of a block from either metaroot.

    Will pass the call down either to the inline imap or to the external imap
    implementation, whichever is appropriate for the current volume.

    @param bMR          The metaroot index: either 0 or 1.
    @param ulBlock      The block number to query.
    @param pfAllocated  On successful return, populated with the allocation bit
                        of the block.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p bMR is out of range; or @p ulBlock is out of range;
                        or @p pfAllocated is `NULL`.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS RedImapBlockGet(
    uint8_t     bMR,
    uint32_t    ulBlock,
    bool       *pfAllocated)
{
    REDSTATUS   ret;

    if(    (bMR > 1U)
        || (ulBlock < gpRedCoreVol->ulInodeTableStartBN)
        || (ulBlock >= gpRedVolume->ulBlockCount)
        || (pfAllocated == NULL))
    {
        REDERROR();
        ret = -RED_EINVAL;
    }
    else
    {
      #if (REDCONF_IMAP_INLINE == 1) && (REDCONF_IMAP_EXTERNAL == 1)
        if(gpRedCoreVol->fImapInline)
        {
            ret = RedImapIBlockGet(bMR, ulBlock, pfAllocated);
        }
        else
        {
            ret = RedImapEBlockGet(bMR, ulBlock, pfAllocated);
        }
      #elif REDCONF_IMAP_INLINE == 1
        ret = RedImapIBlockGet(bMR, ulBlock, pfAllocated);
      #else
        ret = RedImapEBlockGet(bMR, ulBlock, pfAllocated);
      #endif
    }

    return ret;
}


#if REDCONF_READ_ONLY == 0
/** @brief Set the allocation bit of a block in the working metaroot.

    Will pass the call down either to the inline imap or to the external imap
    implementation, whichever is appropriate for the current volume.

    @param ulBlock      The block number to allocate or free.
    @param fAllocated   Whether to allocate the block (true) or free it (false).

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p ulBlock is out of range; or @p ulBlock is allocable
                        and @p fAllocated is 1.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS RedImapBlockSet(
    uint32_t    ulBlock,
    bool        fAllocated)
{
    REDSTATUS   ret;

    if(    (ulBlock < gpRedCoreVol->ulInodeTableStartBN)
        || (ulBlock >= gpRedVolume->ulBlockCount))
    {
        REDERROR();
        ret = -RED_EINVAL;
    }
    else if(    (ulBlock >= gpRedCoreVol->ulFirstAllocableBN)
             && (    (fAllocated && (gpRedMR->ulFreeBlocks == 0U))
                  || ((!fAllocated) && (gpRedMR->ulFreeBlocks >= gpRedVolume->ulBlocksAllocable))))
    {
        /*  Attempting either to free more blocks than are allocable, or
            allocate a block when there are none available.  This could indicate
            metadata corruption.
        */
        CRITICAL_ERROR();
        ret = -RED_EFUBAR;
    }
    else
    {
      #if (REDCONF_IMAP_INLINE == 1) && (REDCONF_IMAP_EXTERNAL == 1)
        if(gpRedCoreVol->fImapInline)
        {
            ret = RedImapIBlockSet(ulBlock, fAllocated);
        }
        else
        {
            ret = RedImapEBlockSet(ulBlock, fAllocated);
        }
      #elif REDCONF_IMAP_INLINE == 1
        ret = RedImapIBlockSet(ulBlock, fAllocated);
      #else
        ret = RedImapEBlockSet(ulBlock, fAllocated);
      #endif

        /*  Any change to the allocation state of a block indicates that the
            volume is now branched.
        */
        gpRedCoreVol->fBranched = true;
    }

    /*  If a block was marked as no longer in use, discard it from the buffers.
    */
    if((ret == 0) && (!fAllocated))
    {
        ret = RedBufferDiscardRange(ulBlock, 1U);
        CRITICAL_ASSERT(ret == 0);
    }

    /*  Adjust the free/almost free block count if the block was allocable.
        Discard the block if required.
    */
    if((ret == 0) && (ulBlock >= gpRedCoreVol->ulFirstAllocableBN))
    {
        if(fAllocated)
        {
            gpRedMR->ulFreeBlocks--;
        }
        else
        {
            bool fWasAllocated;

            /*  Whether the block became free or almost free depends on its
                previous allocation state.  If it was used, then it is now
                almost free.  Otherwise, it was new and is now free.
            */
            ret = RedImapBlockGet(1U - gpRedCoreVol->bCurMR, ulBlock, &fWasAllocated);
            CRITICAL_ASSERT(ret == 0);

            if(ret == 0)
            {
                if(fWasAllocated)
                {
                    gpRedCoreVol->ulAlmostFreeBlocks++;
                }
                else
                {
                    gpRedMR->ulFreeBlocks++;
                }
            }
        }
    }

    return ret;
}


/** @brief Allocate one block.

    @param pulBlock On successful return, populated with the allocated block
                    number.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p pulBlock is `NULL`.
    @retval -RED_EIO    A disk I/O error occurred.
    @retval -RED_ENOSPC Insufficient free space to perform the allocation.
*/
REDSTATUS RedImapAllocBlock(
    uint32_t   *pulBlock)
{
    REDSTATUS   ret;

    if(pulBlock == NULL)
    {
        REDERROR();
        ret = -RED_EINVAL;
    }
    else if(gpRedMR->ulFreeBlocks == 0U)
    {
        ret = -RED_ENOSPC;
    }
    else
    {
        uint32_t ulStopBlock = gpRedMR->ulAllocNextBlock;
        bool     fAllocated = false;

        do
        {
            ALLOCSTATE state;

            ret = RedImapBlockState(gpRedMR->ulAllocNextBlock, &state);
            CRITICAL_ASSERT(ret == 0);

            if(ret == 0)
            {
                if(state == ALLOCSTATE_FREE)
                {
                    ret = RedImapBlockSet(gpRedMR->ulAllocNextBlock, true);
                    CRITICAL_ASSERT(ret == 0);

                    *pulBlock = gpRedMR->ulAllocNextBlock;
                    fAllocated = true;
                }

                /*  Increment the next block number, wrapping it when the end of
                    the volume is reached.
                */
                gpRedMR->ulAllocNextBlock++;
                if(gpRedMR->ulAllocNextBlock == gpRedVolume->ulBlockCount)
                {
                    gpRedMR->ulAllocNextBlock = gpRedCoreVol->ulFirstAllocableBN;
                }
            }
        }
        while((ret == 0) && !fAllocated && (gpRedMR->ulAllocNextBlock != ulStopBlock));

        if((ret == 0) && !fAllocated)
        {
            /*  The free block count was already determined to be non-zero, no
                error occurred while looking for free blocks, but no free blocks
                were found.  This indicates metadata corruption.
            */
            CRITICAL_ERROR();
            ret = -RED_EFUBAR;
        }
    }

    return ret;
}
#endif /* REDCONF_READ_ONLY == 0 */


/** @brief Get the allocation state of a block.

    Takes into account the allocation bits from both metaroots, and returns one
    of four possible allocation state values:

    - ::ALLOCSTATE_FREE Free and may be allocated; writeable.
    - ::ALLOCSTATE_USED In-use and transacted; not writeable.
    - ::ALLOCSTATE_NEW In-use but not transacted; writeable.
    - ::ALLOCSTATE_AFREE Will become free after a transaction; not writeable.

    @param ulBlock  The block number to query.
    @param pState   On successful return, populated with the state of the block.

    @return A negated ::REDSTATUS code indicating the operation result.

    @retval 0           Operation was successful.
    @retval -RED_EINVAL @p ulBlock is out of range; or @p pState is `NULL`.
    @retval -RED_EIO    A disk I/O error occurred.
*/
REDSTATUS RedImapBlockState(
    uint32_t    ulBlock,
    ALLOCSTATE *pState)
{
    REDSTATUS   ret;

    if(    (ulBlock < gpRedCoreVol->ulInodeTableStartBN)
        || (ulBlock >= gpRedVolume->ulBlockCount)
        || (pState == NULL))
    {
        REDERROR();
        ret = -RED_EINVAL;
    }
    else
    {
        bool fBitCurrent;

        ret = RedImapBlockGet(gpRedCoreVol->bCurMR, ulBlock, &fBitCurrent);

        if(ret == 0)
        {
            bool fBitOld;

            ret = RedImapBlockGet(1U - gpRedCoreVol->bCurMR, ulBlock, &fBitOld);

            if(ret == 0)
            {
                if(fBitCurrent)
                {
                    if(fBitOld)
                    {
                        *pState = ALLOCSTATE_USED;
                    }
                    else
                    {
                        *pState = ALLOCSTATE_NEW;
                    }
                }
                else
                {
                    if(fBitOld)
                    {
                        *pState = ALLOCSTATE_AFREE;
                    }
                    else
                    {
                        *pState = ALLOCSTATE_FREE;
                    }
                }
            }
        }
    }

    return ret;
}