1 /*
2 This file is part of UFFS, the Ultra-low-cost Flash File System.
3
4 Copyright (C) 2005-2009 Ricky Zheng <[email protected]>
5
6 UFFS is free software; you can redistribute it and/or modify it under
7 the GNU Library General Public License as published by the Free Software
8 Foundation; either version 2 of the License, or (at your option) any
9 later version.
10
11 UFFS is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 or GNU Library General Public License, as applicable, for more details.
15
16 You should have received a copy of the GNU General Public License
17 and GNU Library General Public License along with UFFS; if not, write
18 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 As a special exception, if other files instantiate templates or use
22 macros or inline functions from this file, or you compile this file
23 and link it with other works to produce a work based on this file,
24 this file does not by itself cause the resulting work to be covered
25 by the GNU General Public License. However the source code for this
26 file must still be made available in accordance with section (3) of
27 the GNU General Public License v2.
28
29 This exception does not invalidate any other reasons why a work based
30 on this file might be covered by the GNU General Public License.
31 */
32
33 /**
34 * \file uffs_blockinfo.c
35 * \brief block information cache system manipulations
36 * \author Ricky Zheng, created 10th May, 2005
37 */
38
39 #include "uffs_config.h"
40 #include "uffs/uffs_blockinfo.h"
41 #include "uffs/uffs_public.h"
42 #include "uffs/uffs_os.h"
43
44 #include <string.h>
45
46 #define PFX "bc : "
47
48 #define UFFS_CLONE_BLOCK_INFO_NEXT ((uffs_BlockInfo *)(-2))
49
50 /**
51 * \brief before block info cache is enable,
52 * this function should be called to initialize it
53 *
54 * \param[in] dev uffs device
55 * \param[in] maxCachedBlocks maximum cache buffers to be allocated
56 * \return result of initialization
57 * \retval U_SUCC successful
58 * \retval U_FAIL failed
59 */
uffs_BlockInfoInitCache(uffs_Device * dev,int maxCachedBlocks)60 URET uffs_BlockInfoInitCache(uffs_Device *dev, int maxCachedBlocks)
61 {
62 uffs_BlockInfo * blockInfos = NULL;
63 uffs_PageSpare * pageSpares = NULL;
64 void * buf = NULL;
65 uffs_BlockInfo *work = NULL;
66 int size, i, j;
67
68 if (dev->bc.head != NULL) {
69 uffs_Perror(UFFS_MSG_NOISY,
70 "block info cache has been inited already, "
71 "now release it first.");
72 uffs_BlockInfoReleaseCache(dev);
73 }
74
75 size = (
76 sizeof(uffs_BlockInfo) +
77 sizeof(uffs_PageSpare) * dev->attr->pages_per_block
78 ) * maxCachedBlocks;
79
80 if (dev->mem.blockinfo_pool_size == 0) {
81 if (dev->mem.malloc) {
82 dev->mem.blockinfo_pool_buf = dev->mem.malloc(dev, size);
83 if (dev->mem.blockinfo_pool_buf)
84 dev->mem.blockinfo_pool_size = size;
85 }
86 }
87 if (size > dev->mem.blockinfo_pool_size) {
88 uffs_Perror(UFFS_MSG_DEAD,
89 "Block cache buffer require %d but only %d available.",
90 size, dev->mem.blockinfo_pool_size);
91 return U_FAIL;
92 }
93
94 uffs_Perror(UFFS_MSG_NOISY, "alloc info cache %d bytes.", size);
95
96 buf = dev->mem.blockinfo_pool_buf;
97
98 memset(buf, 0, size);
99
100 dev->bc.mem_pool = buf;
101
102 size = 0;
103 blockInfos = (uffs_BlockInfo *)buf;
104 size += sizeof(uffs_BlockInfo) * maxCachedBlocks;
105
106 pageSpares = (uffs_PageSpare *)((char *)buf + size);
107
108 //initialize block info
109 work = &(blockInfos[0]);
110 dev->bc.head = work;
111 work->ref_count = 0;
112 work->prev = NULL;
113 work->next = &(blockInfos[1]);
114 work->block = UFFS_INVALID_BLOCK;
115
116 for (i = 0; i < maxCachedBlocks - 2; i++) {
117 work = &(blockInfos[i+1]);
118 work->prev = &(blockInfos[i]);
119 work->next = &(blockInfos[i+2]);
120 work->ref_count = 0;
121 work->block = UFFS_INVALID_BLOCK;
122 }
123 //the last node
124 work = &(blockInfos[i+1]);
125 work->prev = &(blockInfos[i]);
126 work->next = NULL;
127 work->block = UFFS_INVALID_BLOCK;
128 work->ref_count = 0;
129 dev->bc.tail = work;
130
131 //initialize spares
132 work = dev->bc.head;
133 for (i = 0; i < maxCachedBlocks; i++) {
134 work->spares = &(pageSpares[i*dev->attr->pages_per_block]);
135 for (j = 0; j < dev->attr->pages_per_block; j++) {
136 work->spares[j].expired = 1;
137 }
138 work->expired_count = dev->attr->pages_per_block;
139 work = work->next;
140 }
141 return U_SUCC;
142 }
143
144 /**
145 * \brief release all allocated memory of block info cache,
146 * this function should be called when unmount file system
147 * \param[in] dev uffs device
148 */
uffs_BlockInfoReleaseCache(uffs_Device * dev)149 URET uffs_BlockInfoReleaseCache(uffs_Device *dev)
150 {
151 uffs_BlockInfo *work;
152
153 if (dev->bc.head) {
154 for (work = dev->bc.head; work != NULL; work = work->next) {
155 if (work->ref_count != 0) {
156 uffs_Perror(UFFS_MSG_SERIOUS,
157 "There have refed block info cache, release cache fail.");
158 return U_FAIL;
159 }
160 }
161 if (dev->mem.free) {
162 dev->mem.free(dev, dev->bc.mem_pool);
163 dev->mem.blockinfo_pool_size = 0;
164 }
165 }
166
167 dev->bc.head = dev->bc.tail = NULL;
168 dev->bc.mem_pool = NULL;
169
170 return U_SUCC;
171 }
172
_BreakBcFromList(uffs_Device * dev,uffs_BlockInfo * bc)173 static void _BreakBcFromList(uffs_Device *dev, uffs_BlockInfo *bc)
174 {
175 if (bc->prev)
176 bc->prev->next = bc->next;
177
178 if (bc->next)
179 bc->next->prev = bc->prev;
180
181 if (dev->bc.head == bc)
182 dev->bc.head = bc->next;
183
184 if (dev->bc.tail == bc)
185 dev->bc.tail = bc->prev;
186 }
187
_InsertToBcListTail(uffs_Device * dev,uffs_BlockInfo * bc)188 static void _InsertToBcListTail(uffs_Device *dev, uffs_BlockInfo *bc)
189 {
190 bc->next = NULL;
191 bc->prev = dev->bc.tail;
192 bc->prev->next = bc;
193 dev->bc.tail = bc;
194 }
195
_MoveBcToTail(uffs_Device * dev,uffs_BlockInfo * bc)196 static void _MoveBcToTail(uffs_Device *dev, uffs_BlockInfo *bc)
197 {
198 _BreakBcFromList(dev, bc);
199 _InsertToBcListTail(dev, bc);
200 }
201
202
203 /**
204 * \brief load page spare data to given block info structure
205 * with given page number
206 * \param[in] dev uffs device
207 * \param[in] work given block info to be filled with
208 * \param[in] page given page number to be read from,
209 * if #UFFS_ALL_PAGES is presented, it will read
210 * all pages, otherwise it will read only one given page.
211 * \return load result
212 * \retval U_SUCC successful
213 * \retval U_FAIL fail to load
214 * \note work->block must be set before load block info
215 */
uffs_BlockInfoLoad(uffs_Device * dev,uffs_BlockInfo * work,int page)216 URET uffs_BlockInfoLoad(uffs_Device *dev, uffs_BlockInfo *work, int page)
217 {
218 int i, ret;
219 uffs_PageSpare *spare;
220
221 if (page == UFFS_ALL_PAGES) {
222 for (i = 0; i < dev->attr->pages_per_block; i++) {
223 spare = &(work->spares[i]);
224 if (spare->expired == 0)
225 continue;
226
227 ret = uffs_FlashReadPageTag(dev, work->block, i,
228 &(spare->tag));
229 if (UFFS_FLASH_HAVE_ERR(ret)) {
230 uffs_Perror(UFFS_MSG_SERIOUS,
231 "load block %d page %d spare fail.",
232 work->block, i);
233 return U_FAIL;
234 }
235 spare->expired = 0;
236 work->expired_count--;
237 }
238 }
239 else {
240 if (page < 0 || page >= dev->attr->pages_per_block) {
241 uffs_Perror(UFFS_MSG_SERIOUS, "page out of range !");
242 return U_FAIL;
243 }
244 spare = &(work->spares[page]);
245 if (spare->expired != 0) {
246 ret = uffs_FlashReadPageTag(dev, work->block, page,
247 &(spare->tag));
248 if (UFFS_FLASH_HAVE_ERR(ret)) {
249 uffs_Perror(UFFS_MSG_SERIOUS,
250 "load block %d page %d spare fail.",
251 work->block, page);
252 return U_FAIL;
253 }
254 spare->expired = 0;
255 work->expired_count--;
256 }
257 }
258 return U_SUCC;
259 }
260
261
262 /**
263 * \brief find a block cache with given block number
264 * \param[in] dev uffs device
265 * \param[in] block block number
266 * \return found block cache
267 * \retval NULL cache not found
268 * \retval non-NULL found cache pointer
269 */
uffs_BlockInfoFindInCache(uffs_Device * dev,int block)270 uffs_BlockInfo * uffs_BlockInfoFindInCache(uffs_Device *dev, int block)
271 {
272 uffs_BlockInfo *work;
273
274 //search cached block
275 for (work = dev->bc.head; work != NULL; work = work->next) {
276 if (work->block == block) {
277 work->ref_count++;
278 return work;
279 }
280 }
281 return NULL;
282 }
283
284
285 /**
286 * \brief Find a cached block in cache pool,
287 * if the cached block exist then return the pointer,
288 * if the block does not cached already, find a non-used cache.
289 * if all of cached are used out, return NULL.
290 * \param[in] dev uffs device
291 * \param[in] block block number to be found
292 * \return found block cache buffer
293 * \retval NULL caches used out
294 * \retval non-NULL buffer pointer of given block
295 */
uffs_BlockInfoGet(uffs_Device * dev,int block)296 uffs_BlockInfo * uffs_BlockInfoGet(uffs_Device *dev, int block)
297 {
298 uffs_BlockInfo *work;
299 int i;
300
301 //search cached block
302 if ((work = uffs_BlockInfoFindInCache(dev, block)) != NULL) {
303 _MoveBcToTail(dev, work);
304 return work;
305 }
306
307 //can't find block from cache, need to find a free(unlocked) cache
308 for (work = dev->bc.head; work != NULL; work = work->next) {
309 if(work->ref_count == 0) break;
310 }
311 if (work == NULL) {
312 //caches used out !
313 uffs_Perror(UFFS_MSG_SERIOUS, "insufficient block info cache");
314 return NULL;
315 }
316
317 work->block = block;
318 work->expired_count = dev->attr->pages_per_block;
319 for (i = 0; i < dev->attr->pages_per_block; i++) {
320 work->spares[i].expired = 1;
321
322 // TODO: init tag
323 }
324
325 work->ref_count = 1;
326
327 _MoveBcToTail(dev, work);
328
329 return work;
330 }
331
332 /**
333 * \brief put block info buffer back to pool,
334 * should be called with #uffs_BlockInfoGet in pairs.
335 * \param[in] dev uffs device
336 * \param[in] p pointer of block info buffer
337 */
uffs_BlockInfoPut(uffs_Device * dev,uffs_BlockInfo * p)338 void uffs_BlockInfoPut(uffs_Device *dev, uffs_BlockInfo *p)
339 {
340 dev = dev;
341 if (p)
342 {
343 if (p->ref_count == 0) {
344 uffs_Perror(UFFS_MSG_SERIOUS,
345 "Put an unused block info cache back ?");
346 }
347 else {
348 p->ref_count--;
349 }
350 }
351 }
352
353
354 /**
355 * \brief make the given pages expired in given block info buffer
356 * \param[in] dev uffs device
357 * \param[in] p pointer of block info buffer
358 * \param[in] page given page number.
359 * if #UFFS_ALL_PAGES presented, all pages in the block should be made expired.
360 */
uffs_BlockInfoExpire(uffs_Device * dev,uffs_BlockInfo * p,int page)361 void uffs_BlockInfoExpire(uffs_Device *dev, uffs_BlockInfo *p, int page)
362 {
363 int i;
364 uffs_PageSpare *spare;
365
366 if (page == UFFS_ALL_PAGES) {
367 for (i = 0; i < dev->attr->pages_per_block; i++) {
368 spare = &(p->spares[i]);
369 if (spare->expired == 0) {
370 spare->expired = 1;
371 p->expired_count++;
372 }
373 }
374 }
375 else {
376 if (page >= 0 && page < dev->attr->pages_per_block) {
377 spare = &(p->spares[page]);
378 if (spare->expired == 0) {
379 spare->expired = 1;
380 p->expired_count++;
381 }
382 }
383 }
384 }
385
386 /**
387 * Is all blcok info cache free (not referenced) ?
388 */
uffs_BlockInfoIsAllFree(uffs_Device * dev)389 UBOOL uffs_BlockInfoIsAllFree(uffs_Device *dev)
390 {
391 uffs_BlockInfo *work;
392
393 work = dev->bc.head;
394 while (work) {
395 if (work->ref_count != 0)
396 return U_FALSE;
397 work = work->next;
398 }
399
400 return U_TRUE;
401 }
402
uffs_BlockInfoExpireAll(uffs_Device * dev)403 void uffs_BlockInfoExpireAll(uffs_Device *dev)
404 {
405 uffs_BlockInfo *bc;
406
407 bc = dev->bc.head;
408 while (bc) {
409 uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);
410 bc = bc->next;
411 }
412 return;
413 }
414