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 * \file uffs_buf.c
34 * \brief uffs page buffers manipulations
35 * \author Ricky Zheng
36 * \note Created in 11th May, 2005
37 */
38 #include "uffs_config.h"
39 #include "uffs/uffs_types.h"
40 #include "uffs/uffs_buf.h"
41 #include "uffs/uffs_device.h"
42 #include "uffs/uffs_os.h"
43 #include "uffs/uffs_public.h"
44 #include "uffs/uffs_pool.h"
45 #include "uffs/uffs_ecc.h"
46 #include "uffs/uffs_badblock.h"
47 #include <string.h>
48
49 #define PFX "pbuf: "
50
51
52 URET _BufFlush(struct uffs_DeviceSt *dev, UBOOL force_block_recover, int slot);
53
54
55 /**
56 * \brief inspect (print) uffs page buffers.
57 * \param[in] dev uffs device to be inspected.
58 */
uffs_BufInspect(uffs_Device * dev)59 void uffs_BufInspect(uffs_Device *dev)
60 {
61 struct uffs_PageBufDescSt *pb = &dev->buf;
62 uffs_Buf *buf;
63
64 uffs_PerrorRaw(UFFS_MSG_NORMAL,
65 "------------- page buffer inspect ---------" TENDSTR);
66 uffs_PerrorRaw(UFFS_MSG_NORMAL, "all buffers: " TENDSTR);
67 for (buf = pb->head; buf; buf = buf->next) {
68 if (buf->mark != 0) {
69 uffs_PerrorRaw(UFFS_MSG_NORMAL,
70 "\tF:%04x S:%04x P:%02d R:%02d D:%03d M:%c EM:%d" TENDSTR,
71 buf->parent, buf->serial,
72 buf->page_id, buf->ref_count,
73 buf->data_len, buf->mark == UFFS_BUF_VALID ? 'V' : 'D',
74 buf->ext_mark);
75 }
76 }
77 uffs_PerrorRaw(UFFS_MSG_NORMAL,
78 "--------------------------------------------" TENDSTR);
79 }
80
81 /**
82 * \brief initialize page buffers for device
83 * in UFFS, each device has one buffer pool
84 * \param[in] dev uffs device
85 * \param[in] buf_max maximum buffer number, normally use #MAX_PAGE_BUFFERS
86 * \param[in] dirty_buf_max maximum dirty buffer allowed,
87 * if the dirty buffer over this number,
88 * than need to be flush to flash
89 */
uffs_BufInit(uffs_Device * dev,int buf_max,int dirty_buf_max)90 URET uffs_BufInit(uffs_Device *dev, int buf_max, int dirty_buf_max)
91 {
92 void *pool;
93 u8 *data;
94 uffs_Buf *buf;
95 int size;
96 int i, slot;
97
98 if (!dev)
99 return U_FAIL;
100
101 //init device common parameters, which are needed by page buffers
102 dev->com.pg_size = dev->attr->page_data_size; // we use the whole page.
103 dev->com.header_size = sizeof(struct uffs_MiniHeaderSt); // mini header
104 dev->com.pg_data_size = dev->com.pg_size - dev->com.header_size;
105
106 if (dev->buf.pool != NULL) {
107 uffs_Perror(UFFS_MSG_NORMAL,
108 "buf.pool is not NULL, buf already inited ?");
109 return U_FAIL;
110 }
111
112 size = (sizeof(uffs_Buf) + dev->com.pg_size) * buf_max;
113 if (dev->mem.pagebuf_pool_size == 0) {
114 if (dev->mem.malloc) {
115 dev->mem.pagebuf_pool_buf = dev->mem.malloc(dev, size);
116 if (dev->mem.pagebuf_pool_buf)
117 dev->mem.pagebuf_pool_size = size;
118 }
119 }
120 if (size > dev->mem.pagebuf_pool_size) {
121 uffs_Perror(UFFS_MSG_DEAD,
122 "page buffers require %d but only %d available.",
123 size, dev->mem.pagebuf_pool_size);
124 return U_FAIL;
125 }
126 pool = dev->mem.pagebuf_pool_buf;
127
128 uffs_Perror(UFFS_MSG_NOISY, "alloc %d bytes.", size);
129 dev->buf.pool = pool;
130
131 for (i = 0; i < buf_max; i++) {
132 buf = (uffs_Buf *)((u8 *)pool + (sizeof(uffs_Buf) * i));
133 memset(buf, 0, sizeof(uffs_Buf));
134 data = (u8 *)pool + (sizeof(uffs_Buf) * buf_max) + (dev->com.pg_size * i);
135 buf->header = data;
136 buf->data = data + dev->com.header_size;
137 buf->mark = UFFS_BUF_EMPTY;
138 memset(buf->header, 0, dev->com.pg_size);
139 if (i == 0) {
140 buf->prev = NULL;
141 dev->buf.head = buf;
142 }
143 else {
144 buf->prev = (uffs_Buf *)((u8 *)buf - sizeof(uffs_Buf));
145 }
146
147 if (i == (buf_max - 1)) {
148 buf->next = NULL;
149 dev->buf.tail = buf;
150 }
151 else {
152 buf->next = (uffs_Buf *)((u8 *)buf + sizeof(uffs_Buf));
153 }
154 }
155
156 dev->buf.buf_max = buf_max;
157 dev->buf.dirty_buf_max = (dirty_buf_max > dev->attr->pages_per_block ?
158 dev->attr->pages_per_block : dirty_buf_max);
159
160 for (slot = 0; slot < dev->cfg.dirty_groups; slot++) {
161 dev->buf.dirtyGroup[slot].dirty = NULL;
162 dev->buf.dirtyGroup[slot].count = 0;
163 }
164 return U_SUCC;
165 }
166
167 /**
168 * \brief flush all buffers
169 */
uffs_BufFlushAll(struct uffs_DeviceSt * dev)170 URET uffs_BufFlushAll(struct uffs_DeviceSt *dev)
171 {
172 int slot;
173 for (slot = 0; slot < dev->cfg.dirty_groups; slot++) {
174 if(_BufFlush(dev, FALSE, slot) != U_SUCC) {
175 uffs_Perror(UFFS_MSG_NORMAL,
176 "fail to flush buffer(slot %d)", slot);
177 return U_FAIL;
178 }
179 }
180 return U_SUCC;
181 }
182
183 /**
184 * \brief release all page buffer, this function should be called
185 when unmounting a uffs device
186 * \param[in] dev uffs device
187 * \note if there are page buffers in used, it may cause fail to release
188 */
uffs_BufReleaseAll(uffs_Device * dev)189 URET uffs_BufReleaseAll(uffs_Device *dev)
190 {
191 uffs_Buf *p;
192
193 if (!dev)
194 return U_FAIL;
195
196 //now release all buffer
197 p = dev->buf.head;
198 while (p) {
199 if (p->ref_count != 0) {
200 uffs_Perror(UFFS_MSG_NORMAL, PFX
201 "can't release buffers, parent:%d, serial:%d, \
202 page_id:%d still in used.\n",
203 p->parent, p->serial, p->page_id);
204 return U_FAIL;
205 }
206 p = p->next;
207 }
208
209 if (uffs_BufFlushAll(dev) != U_SUCC) {
210 uffs_Perror(UFFS_MSG_NORMAL,
211 "can't release buf, fail to flush buffer");
212 return U_FAIL;
213 }
214
215 if (dev->mem.free) {
216 dev->mem.free(dev, dev->buf.pool);
217 dev->mem.pagebuf_pool_size = 0;
218 }
219
220 dev->buf.pool = NULL;
221 dev->buf.head = dev->buf.tail = NULL;
222
223 return U_SUCC;
224 }
225
226
_BreakFromBufList(uffs_Device * dev,uffs_Buf * buf)227 static void _BreakFromBufList(uffs_Device *dev, uffs_Buf *buf)
228 {
229 if(buf->next)
230 buf->next->prev = buf->prev;
231
232 if(buf->prev)
233 buf->prev->next = buf->next;
234
235 if(dev->buf.head == buf)
236 dev->buf.head = buf->next;
237
238 if(dev->buf.tail == buf)
239 dev->buf.tail = buf->prev;
240
241 }
242
_LinkToBufListHead(uffs_Device * dev,uffs_Buf * buf)243 static void _LinkToBufListHead(uffs_Device *dev, uffs_Buf *buf)
244 {
245 if (buf == dev->buf.head)
246 return;
247
248 buf->prev = NULL;
249 buf->next = dev->buf.head;
250
251 if (dev->buf.head)
252 dev->buf.head->prev = buf;
253
254 if (dev->buf.tail == NULL)
255 dev->buf.tail = buf;
256
257 dev->buf.head = buf;
258 }
259
_LinkToBufListTail(uffs_Device * dev,uffs_Buf * buf)260 static void _LinkToBufListTail(uffs_Device *dev, uffs_Buf *buf)
261 {
262 if (dev->buf.tail == buf)
263 return;
264
265 buf->prev = dev->buf.tail;
266 buf->next = NULL;
267
268 if (dev->buf.tail)
269 dev->buf.tail->next = buf;
270
271 if (dev->buf.head == NULL)
272 dev->buf.head = buf;
273
274 dev->buf.tail = buf;
275 }
276
277 //move a node which linked in the list to the head of list
_MoveNodeToHead(uffs_Device * dev,uffs_Buf * p)278 static void _MoveNodeToHead(uffs_Device *dev, uffs_Buf *p)
279 {
280 if (p == dev->buf.head)
281 return;
282
283 //break from list
284 _BreakFromBufList(dev, p);
285
286 //link to head
287 _LinkToBufListHead(dev, p);
288 }
289
290 // check if the buf is already in dirty list
_IsBufInInDirtyList(uffs_Device * dev,int slot,uffs_Buf * buf)291 static UBOOL _IsBufInInDirtyList(uffs_Device *dev, int slot, uffs_Buf *buf)
292 {
293 uffs_Buf *work;
294 work = dev->buf.dirtyGroup[slot].dirty;
295 while (work) {
296 if (work == buf)
297 return U_TRUE;
298 work = work->next_dirty;
299 }
300
301 return U_FALSE;
302 }
303
_LinkToDirtyList(uffs_Device * dev,int slot,uffs_Buf * buf)304 static void _LinkToDirtyList(uffs_Device *dev, int slot, uffs_Buf *buf)
305 {
306
307 if (buf == NULL) {
308 uffs_Perror(UFFS_MSG_SERIOUS,
309 "Try to insert a NULL node into dirty list ?");
310 return;
311 }
312
313 buf->mark = UFFS_BUF_DIRTY;
314 buf->prev_dirty = NULL;
315 buf->next_dirty = dev->buf.dirtyGroup[slot].dirty;
316
317 if (dev->buf.dirtyGroup[slot].dirty)
318 dev->buf.dirtyGroup[slot].dirty->prev_dirty = buf;
319
320 dev->buf.dirtyGroup[slot].dirty = buf;
321 dev->buf.dirtyGroup[slot].count++;
322 }
323
CountFreeBuf(uffs_Device * dev)324 static int CountFreeBuf(uffs_Device *dev)
325 {
326 int count = 0;
327
328 uffs_Buf *buf = dev->buf.head;
329
330 while (buf) {
331
332 if (buf->ref_count == 0 &&
333 buf->mark != UFFS_BUF_DIRTY)
334 count++;
335
336 buf = buf->next;
337 }
338
339 return count;
340 }
341
_FindFreeBufEx(uffs_Device * dev,int clone)342 static uffs_Buf * _FindFreeBufEx(uffs_Device *dev, int clone)
343 {
344 uffs_Buf *buf;
345
346 if (!clone && CountFreeBuf(dev) <= CLONE_BUFFERS_THRESHOLD)
347 return NULL;
348
349 #if 0
350 buf = dev->buf.head;
351 while (buf) {
352
353 if (buf->ref_count == 0 &&
354 buf->mark != UFFS_BUF_DIRTY)
355 return buf;
356
357 buf = buf->next;
358 }
359 #else
360 buf = dev->buf.tail;
361 while (buf) {
362
363 if(buf->ref_count == 0 &&
364 buf->mark != UFFS_BUF_DIRTY)
365 return buf;
366
367 buf = buf->prev;
368 }
369 #endif
370
371 return buf;
372 }
373
_FindFreeBuf(uffs_Device * dev)374 static uffs_Buf * _FindFreeBuf(uffs_Device *dev)
375 {
376 return _FindFreeBufEx(dev, 0);
377 }
378
379
380 /**
381 * load psychical page data into buf and do ecc check
382 * \param[in] dev uffs device
383 * \param[in] buf buf to be load in
384 * \param[in] block psychical block number
385 * \param[in] page psychical page number
386 * \return return U_SUCC if no error,
387 * return U_FAIL if I/O error or ecc check fail
388 */
uffs_BufLoadPhyData(uffs_Device * dev,uffs_Buf * buf,u32 block,u32 page)389 URET uffs_BufLoadPhyData(uffs_Device *dev, uffs_Buf *buf, u32 block, u32 page)
390 {
391 int ret;
392
393 ret = uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
394
395 if (UFFS_FLASH_HAVE_ERR(ret)) {
396 buf->mark = UFFS_BUF_EMPTY;
397 return U_FAIL;
398 }
399 else {
400 buf->mark = UFFS_BUF_VALID;
401 return U_SUCC;
402 }
403 }
404
405 /**
406 * \brief load psychical page data into buf and ignore ECC result
407 *
408 * \param[in] dev uffs device
409 * \param[in] buf buf to be load in
410 * \param[in] block psychical block number
411 * \param[in] page psychical page number
412 *
413 * \return return U_SUCC if no error, return U_FAIL if I/O error
414 * \note this function should be only used when doing bad block recover.
415 */
uffs_LoadPhyDataToBufEccUnCare(uffs_Device * dev,uffs_Buf * buf,u32 block,u32 page)416 URET uffs_LoadPhyDataToBufEccUnCare(uffs_Device *dev,
417 uffs_Buf *buf, u32 block, u32 page)
418 {
419 int ret;
420
421 ret = uffs_FlashReadPage(dev, block, page, buf, U_TRUE);
422
423 if (ret == UFFS_FLASH_IO_ERR) {
424 buf->mark = UFFS_BUF_EMPTY;
425 return U_FAIL;
426 }
427 else {
428 buf->mark = UFFS_BUF_VALID;
429 return U_SUCC;
430 }
431 }
432
433 /**
434 * find a buffer in the pool
435 * \param[in] dev uffs device
436 * \param[in] start buf to search from
437 * \param[in] parent parent serial num
438 * \param[in] serial serial num
439 * \param[in] page_id page_id (if page_id == UFFS_ALL_PAGES then any page would match)
440 * \return return found buffer, return NULL if buffer not found
441 */
uffs_BufFindFrom(uffs_Device * dev,uffs_Buf * start,u16 parent,u16 serial,u16 page_id)442 uffs_Buf * uffs_BufFindFrom(uffs_Device *dev, uffs_Buf *start,
443 u16 parent, u16 serial, u16 page_id)
444 {
445 uffs_Buf *p = start;
446
447 while (p) {
448 if( p->parent == parent &&
449 p->serial == serial &&
450 (page_id == UFFS_ALL_PAGES || p->page_id == page_id) &&
451 p->mark != UFFS_BUF_EMPTY)
452 {
453 //they have match one
454 return p;
455 }
456 p = p->next;
457 }
458
459 return NULL; //buffer not found
460 }
461
462 /**
463 * find a buffer in the pool
464 * \param[in] dev uffs device
465 * \param[in] parent parent serial num
466 * \param[in] serial serial num
467 * \param[in] page_id page_id (if page_id == UFFS_ALL_PAGES then any page would match)
468 * \return return found buffer, return NULL if buffer not found
469 */
uffs_BufFind(uffs_Device * dev,u16 parent,u16 serial,u16 page_id)470 uffs_Buf * uffs_BufFind(uffs_Device *dev,
471 u16 parent, u16 serial, u16 page_id)
472 {
473 uffs_Buf *p = dev->buf.head;
474
475 return uffs_BufFindFrom(dev, p, parent, serial, page_id);
476 }
477
478
_FindBufInDirtyList(uffs_Buf * dirty,u16 page_id)479 static uffs_Buf * _FindBufInDirtyList(uffs_Buf *dirty, u16 page_id)
480 {
481 while(dirty) {
482 if (dirty->page_id == page_id)
483 return dirty;
484 dirty = dirty->next_dirty;
485 }
486 return NULL;
487 }
488
_BreakFromDirty(uffs_Device * dev,uffs_Buf * dirtyBuf)489 static URET _BreakFromDirty(uffs_Device *dev, uffs_Buf *dirtyBuf)
490 {
491 int slot = -1;
492
493 if (dirtyBuf->mark != UFFS_BUF_DIRTY) {
494 uffs_Perror(UFFS_MSG_NORMAL,
495 "try to break a non-dirty buf from dirty list ?");
496 return U_FAIL;
497 }
498
499 slot = uffs_BufFindGroupSlot(dev, dirtyBuf->parent, dirtyBuf->serial);
500 if (slot < 0) {
501 uffs_Perror(UFFS_MSG_NORMAL, "no dirty list exit ?");
502 return U_FAIL;
503 }
504
505 // break from the link
506 if (dirtyBuf->next_dirty) {
507 dirtyBuf->next_dirty->prev_dirty = dirtyBuf->prev_dirty;
508 }
509
510 if (dirtyBuf->prev_dirty) {
511 dirtyBuf->prev_dirty->next_dirty = dirtyBuf->next_dirty;
512 }
513
514 // check if it's the link head ...
515 if (dev->buf.dirtyGroup[slot].dirty == dirtyBuf) {
516 dev->buf.dirtyGroup[slot].dirty = dirtyBuf->next_dirty;
517 }
518
519 dirtyBuf->next_dirty = dirtyBuf->prev_dirty = NULL; // clear dirty link
520
521 dev->buf.dirtyGroup[slot].count--;
522
523 return U_SUCC;
524 }
525
_GetDirOrFileNameSum(uffs_Device * dev,uffs_Buf * buf)526 static u16 _GetDirOrFileNameSum(uffs_Device *dev, uffs_Buf *buf)
527 {
528 u16 data_sum = 0; //default: 0
529 uffs_FileInfo *fi;
530
531 dev = dev;
532 //FIXME: We use the same schema for both dir and file.
533 if (buf->type == UFFS_TYPE_FILE || buf->type == UFFS_TYPE_DIR) {
534 if (buf->page_id == 0) {
535 fi = (uffs_FileInfo *)(buf->data);
536 data_sum = uffs_MakeSum16(fi->name, fi->name_len);
537 }
538 }
539
540 return data_sum;
541 }
542
543
_CheckDirtyList(uffs_Buf * dirty)544 static URET _CheckDirtyList(uffs_Buf *dirty)
545 {
546 u16 parent;
547 u16 serial;
548
549 if (dirty == NULL) {
550 return U_SUCC;
551 }
552
553 parent = dirty->parent;
554 serial = dirty->serial;
555 dirty = dirty->next_dirty;
556
557 while (dirty) {
558 if (parent != dirty->parent ||
559 serial != dirty->serial) {
560 uffs_Perror(UFFS_MSG_SERIOUS,
561 "parent or serial in dirty pages buffer are not the same ?");
562 return U_FAIL;
563 }
564 if (dirty->mark != UFFS_BUF_DIRTY) {
565 uffs_Perror(UFFS_MSG_SERIOUS,
566 "non-dirty page buffer in dirty buffer list ?");
567 return U_FAIL;
568 }
569 dirty = dirty->next_dirty;
570 }
571 return U_SUCC;
572 }
573
574 /** find a page in dirty list, which has minimum page_id */
_FindMinimunPageIdFromDirtyList(uffs_Buf * dirtyList)575 uffs_Buf * _FindMinimunPageIdFromDirtyList(uffs_Buf *dirtyList)
576 {
577 uffs_Buf * work = dirtyList;
578 uffs_Buf * buf = dirtyList;
579
580 if (buf) {
581 work = work->next_dirty;
582 while (work) {
583 if (work->page_id < buf->page_id)
584 buf = work;
585 work = work->next_dirty;
586 }
587
588 uffs_Assert(buf->mark == UFFS_BUF_DIRTY,
589 "buf (serial = %d, parent = %d, page_id = %d, type = %d) in dirty list but mark is 0x%x ?",
590 buf->serial, buf->parent, buf->page_id, buf->type, buf->mark);
591 }
592
593 return buf;
594 }
595
596
597 /**
598 * \brief flush buffer with block recover
599 *
600 * Scenario:
601 * 1. get a free (erased) block --> newNode <br>
602 * 2. copy from old block ---> oldNode, or copy from dirty list, <br>
603 * sorted by page_id, to new block. Skips the invalid pages when copy pages.<br>
604 * 3. erased old block. set new info to oldNode, set newNode->block = old block,<br>
605 * and put newNode to erased list.<br>
606 * \note IT'S IMPORTANT TO KEEP OLD NODE IN THE LIST,
607 * so you don't need to update the obj->node :-)
608 */
uffs_BufFlush_Exist_With_BlockCover(uffs_Device * dev,int slot,TreeNode * node,uffs_BlockInfo * bc)609 static URET uffs_BufFlush_Exist_With_BlockCover(
610 uffs_Device *dev,
611 int slot, //!< dirty group slot
612 TreeNode *node, //!< old data node on tree
613 uffs_BlockInfo *bc //!< old data block info
614 )
615 {
616 u16 i;
617 u8 type, timeStamp;
618 u16 page, parent, serial;
619 uffs_Buf *buf;
620 TreeNode *newNode;
621 uffs_BlockInfo *newBc;
622 uffs_Tags *tag, *oldTag;
623 int x;
624 u16 newBlock;
625 UBOOL succRecover; //U_TRUE: recover successful, erase old block,
626 //U_FALSE: fail to recover, erase new block
627 UBOOL flash_op_err;
628 u16 data_sum = 0xFFFF;
629
630 UBOOL useCloneBuf;
631
632 type = dev->buf.dirtyGroup[slot].dirty->type;
633 parent = dev->buf.dirtyGroup[slot].dirty->parent;
634 serial = dev->buf.dirtyGroup[slot].dirty->serial;
635
636 retry:
637 uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
638
639 flash_op_err = UFFS_FLASH_NO_ERR;
640 succRecover = U_FALSE;
641
642 newNode = uffs_TreeGetErasedNode(dev);
643 if (newNode == NULL) {
644 uffs_Perror(UFFS_MSG_NOISY, "no enough erased block!");
645 goto ext;
646 }
647 newBlock = newNode->u.list.block;
648 newBc = uffs_BlockInfoGet(dev, newBlock);
649 if (newBc == NULL) {
650 uffs_Perror(UFFS_MSG_SERIOUS, "get block info fail!");
651 uffs_InsertToErasedListHead(dev, newNode); //put node back to erased list
652 //because it doesn't use, so put to head
653 goto ext;
654 }
655
656 //uffs_Perror(UFFS_MSG_NOISY, "flush buffer with block cover to %d", newBlock);
657
658 #if 0
659 // this assert seems not necessary ...
660 if (!uffs_Assert(newBc->expired_count == dev->attr->pages_per_block,
661 "We have block cache for erased block ? expired_count = %d, block = %d\n",
662 newBc->expired_count, newBc->block)) {
663 uffs_BlockInfoExpire(dev, newBc, UFFS_ALL_PAGES);
664 }
665 #endif
666
667 uffs_BlockInfoLoad(dev, newBc, UFFS_ALL_PAGES);
668 timeStamp = uffs_GetNextBlockTimeStamp(uffs_GetBlockTimeStamp(dev, bc));
669
670 // uffs_Perror(UFFS_MSG_NOISY, "Flush buffers with Block Recover, from %d to %d",
671 // bc->block, newBc->block);
672
673 for (i = 0; i < dev->attr->pages_per_block; i++) {
674 tag = GET_TAG(newBc, i);
675 TAG_DIRTY_BIT(tag) = TAG_DIRTY;
676 TAG_VALID_BIT(tag) = TAG_VALID;
677 TAG_BLOCK_TS(tag) = timeStamp;
678 TAG_PARENT(tag) = parent;
679 TAG_SERIAL(tag) = serial;
680 TAG_TYPE(tag) = type;
681 TAG_PAGE_ID(tag) = (u8)(i & 0xFF); // now, page_id = page.
682 // FIX ME!! if more than 256 pages in a block
683
684 SEAL_TAG(tag);
685
686 buf = _FindBufInDirtyList(dev->buf.dirtyGroup[slot].dirty, i);
687 if (buf != NULL) {
688 if (i == 0)
689 data_sum = _GetDirOrFileNameSum(dev, buf);
690
691 TAG_DATA_LEN(tag) = buf->data_len;
692
693 if (buf->data_len == 0 || (buf->ext_mark & UFFS_BUF_EXT_MARK_TRUNC_TAIL)) { // this only happen when truncating a file
694
695 // when truncating a file, the last dirty buf will be
696 // set as UFFS_BUF_EXT_MARK_TAIL. so that we don't do page recovery
697 // for the rest pages in the block. (file is ended at this page)
698
699 if (!uffs_Assert((buf->ext_mark & UFFS_BUF_EXT_MARK_TRUNC_TAIL) != 0,
700 "buf->data == 0 but not the last page of truncating ? block = %d, page_id = %d",
701 bc->block, i)) {
702
703 // We can't do more about it for now ...
704 }
705
706 if (buf->data_len > 0) {
707 flash_op_err = uffs_FlashWritePageCombine(dev, newBlock, i, buf, tag);
708 }
709 else {
710 // data_len == 0, no I/O needed.
711 flash_op_err = UFFS_FLASH_NO_ERR;
712 }
713 succRecover = U_TRUE;
714 break;
715 }
716 else
717 flash_op_err = uffs_FlashWritePageCombine(dev, newBlock, i, buf, tag);
718
719 if (flash_op_err != UFFS_FLASH_NO_ERR) {
720 if (flash_op_err == UFFS_FLASH_BAD_BLK) {
721 uffs_Perror(UFFS_MSG_NORMAL,
722 "new bad block %d discovered.", newBlock);
723 break;
724 }
725 else if (flash_op_err == UFFS_FLASH_IO_ERR) {
726 uffs_Perror(UFFS_MSG_NORMAL,
727 "writing to block %d page %d, I/O error ?",
728 (int)newBlock, (int)i);
729 break;
730 }
731 else {
732 uffs_Perror(UFFS_MSG_SERIOUS, "Unhandled flash op result: %d", flash_op_err);
733 break;
734 }
735 }
736 }
737 else {
738 page = uffs_FindPageInBlockWithPageId(dev, bc, i);
739 if (page == UFFS_INVALID_PAGE) {
740 succRecover = U_TRUE;
741 break; //end of last page, normal break
742 }
743 page = uffs_FindBestPageInBlock(dev, bc, page);
744
745 if (!uffs_Assert(page != UFFS_INVALID_PAGE, "got an invalid page ?\n"))
746 break;
747
748 oldTag = GET_TAG(bc, page);
749
750 // First, try to find existing cached buffer.
751 // Note: do not call uffs_BufGetEx() as it may trigger buf flush and result in infinite loop
752 buf = uffs_BufGet(dev, parent, serial, i);
753
754 if (buf == NULL) { // no cached page buffer, use clone buffer.
755 useCloneBuf = U_TRUE;
756 buf = uffs_BufClone(dev, NULL);
757 if (buf == NULL) {
758 uffs_Perror(UFFS_MSG_SERIOUS, "Can't clone a new buf!");
759 break;
760 }
761 x = uffs_BufLoadPhyData(dev, buf, bc->block, page);
762 if (x == U_FAIL) {
763 if (HAVE_BADBLOCK(dev) && dev->bad.block == bc->block) {
764 // the old block is a bad block, we'll process it later.
765 uffs_Perror(UFFS_MSG_SERIOUS,
766 "the old block %d is a bad block, \
767 but ignore it for now.",
768 bc->block);
769 }
770 else {
771 uffs_Perror(UFFS_MSG_SERIOUS, "I/O error ?");
772 uffs_BufFreeClone(dev, buf);
773 flash_op_err = UFFS_FLASH_IO_ERR;
774 break;
775 }
776 }
777
778 buf->type = type;
779 buf->parent = parent;
780 buf->serial = serial;
781 buf->page_id = TAG_PAGE_ID(oldTag);
782 buf->data_len = TAG_DATA_LEN(oldTag);
783
784 }
785 else {
786 useCloneBuf = U_FALSE;
787
788 uffs_Assert(buf->page_id == TAG_PAGE_ID(oldTag), "buf->page_id = %d, tag page id: %d", buf->page_id, TAG_PAGE_ID(oldTag));
789 uffs_Assert(buf->data_len == TAG_DATA_LEN(oldTag), "buf->data_len = %d, tag data len: %d", buf->data_len, TAG_DATA_LEN(oldTag));
790 }
791
792 if (buf->data_len > dev->com.pg_data_size) {
793 uffs_Perror(UFFS_MSG_NOISY, "data length over flow, truncated !");
794 buf->data_len = dev->com.pg_data_size;
795 }
796
797 if (!uffs_Assert(buf->data_len != 0, "data_len == 0 ? block %d, page %d, serial %d, parent %d",
798 bc->block, page, buf->serial, buf->parent)) {
799 // this could be some error on flash ? we can't do more about it for now ...
800 }
801
802 TAG_DATA_LEN(tag) = buf->data_len;
803
804 if (i == 0)
805 data_sum = _GetDirOrFileNameSum(dev, buf);
806
807 flash_op_err = uffs_FlashWritePageCombine(dev, newBlock, i, buf, tag);
808
809 if (buf) {
810 if (useCloneBuf)
811 uffs_BufFreeClone(dev, buf);
812 else
813 uffs_BufPut(dev, buf);
814 }
815
816 if (flash_op_err == UFFS_FLASH_BAD_BLK) {
817 uffs_Perror(UFFS_MSG_NORMAL,
818 "new bad block %d discovered.", newBlock);
819 break;
820 }
821 else if (flash_op_err == UFFS_FLASH_IO_ERR) {
822 uffs_Perror(UFFS_MSG_NORMAL, "I/O error ?", newBlock);
823 break;
824 }
825 }
826 } //end of for
827
828 if (i == dev->attr->pages_per_block)
829 succRecover = U_TRUE;
830 else {
831 // expire last page info cache in case the 'tag' is not written.
832 uffs_BlockInfoExpire(dev, newBc, i);
833 }
834
835 if (flash_op_err == UFFS_FLASH_BAD_BLK) {
836 uffs_BlockInfoExpire(dev, newBc, UFFS_ALL_PAGES);
837 uffs_BlockInfoPut(dev, newBc);
838 if (newNode->u.list.block == dev->bad.block) {
839 // the recovered block is a BAD block (buy me a lotto, please :-), we need to
840 // deal with it immediately (mark it as 'bad' and put into bad block list).
841 uffs_BadBlockProcess(dev, newNode);
842 }
843
844 uffs_Perror(UFFS_MSG_NORMAL, "Retry block cover ...");
845
846 goto retry; // retry on a new erased block ...
847 }
848
849 if (succRecover == U_TRUE) {
850 // now it's time to clean the dirty buffers
851 for (i = 0; i < dev->attr->pages_per_block; i++) {
852 buf = _FindBufInDirtyList(dev->buf.dirtyGroup[slot].dirty, i);
853 if (buf) {
854 if (_BreakFromDirty(dev, buf) == U_SUCC) {
855 buf->mark = UFFS_BUF_VALID;
856 buf->ext_mark &= ~UFFS_BUF_EXT_MARK_TRUNC_TAIL;
857 _MoveNodeToHead(dev, buf);
858 }
859 }
860 }
861
862 // swap the old block node and new block node.
863 // it's important that we 'swap' the block and keep the node unchanged
864 // so that allowing someone hold the node pointer unawared.
865 switch (type) {
866 case UFFS_TYPE_DIR:
867 node->u.dir.parent = parent;
868 node->u.dir.serial = serial;
869 node->u.dir.block = newBlock;
870 node->u.dir.checksum = data_sum;
871 break;
872 case UFFS_TYPE_FILE:
873 node->u.file.parent = parent;
874 node->u.file.serial = serial;
875 node->u.file.block = newBlock;
876 node->u.file.checksum = data_sum;
877 break;
878 case UFFS_TYPE_DATA:
879 node->u.data.parent = parent;
880 node->u.data.serial = serial;
881 node->u.data.block = newBlock;
882 break;
883 default:
884 uffs_Perror(UFFS_MSG_SERIOUS, "UNKNOW TYPE");
885 break;
886 }
887
888 newNode->u.list.block = bc->block;
889
890 // if the recovered block is a bad block, it's time to process it.
891 if (HAVE_BADBLOCK(dev) && dev->bad.block == newNode->u.list.block) {
892 //uffs_Perror(UFFS_MSG_SERIOUS, "Still have bad block ?");
893 uffs_BadBlockProcess(dev, newNode);
894 }
895 else {
896 // erase recovered block, put it back to erased block list.
897 if (uffs_IsThisBlockUsed(dev, bc)) {
898 uffs_FlashEraseBlock(dev, bc->block);
899 }
900 if (HAVE_BADBLOCK(dev))
901 uffs_BadBlockProcess(dev, newNode);
902 else
903 uffs_TreeInsertToErasedListTail(dev, newNode);
904 }
905 }
906 else {
907
908 uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES); // FIXME: this might not be necessary ...
909
910 uffs_FlashEraseBlock(dev, newBlock);
911 newNode->u.list.block = newBlock;
912 if (HAVE_BADBLOCK(dev))
913 uffs_BadBlockProcess(dev, newNode);
914 else
915 uffs_TreeInsertToErasedListTail(dev, newNode);
916 }
917
918 if (dev->buf.dirtyGroup[slot].dirty != NULL ||
919 dev->buf.dirtyGroup[slot].count != 0) {
920 uffs_Perror(UFFS_MSG_NORMAL, "still have dirty buffer ?");
921 }
922
923 uffs_BlockInfoPut(dev, newBc);
924
925 ext:
926 return (succRecover == U_TRUE ? U_SUCC : U_FAIL);
927
928 }
929
930
931
932 /**
933 * \brief flush buffer to a new block which is not registered in tree
934 *
935 * Scenario:
936 * 1. get a new block
937 * 2. write pages in dirty list to new block, sorted by page_id
938 * 3. insert new block to tree
939 */
_BufFlush_NewBlock(uffs_Device * dev,int slot)940 static URET _BufFlush_NewBlock(uffs_Device *dev, int slot)
941 {
942 u8 type;
943 TreeNode *node;
944 uffs_BlockInfo *bc;
945 URET ret;
946
947 ret = U_FAIL;
948
949 node = uffs_TreeGetErasedNode(dev);
950 if (node == NULL) {
951 uffs_Perror(UFFS_MSG_NOISY, "no erased block!");
952 goto ext;
953 }
954 bc = uffs_BlockInfoGet(dev, node->u.list.block);
955 if (bc == NULL) {
956 uffs_Perror(UFFS_MSG_SERIOUS, "get block info fail!");
957 uffs_InsertToErasedListHead(dev, node); //put node back to erased list
958 goto ext;
959 }
960
961 type = dev->buf.dirtyGroup[slot].dirty->type;
962
963 ret = uffs_BufFlush_Exist_With_BlockCover(dev, slot, node, bc);
964
965 if (ret == U_SUCC)
966 uffs_InsertNodeToTree(dev, type, node);
967 else {
968 uffs_FlashEraseBlock(dev, bc->block);
969 uffs_TreeInsertToErasedListTail(dev, node);
970 }
971
972 uffs_BlockInfoPut(dev, bc);
973 ext:
974 return ret;
975 }
976
977
978 /**
979 * \brief flush buffer to a block with enough free pages
980 *
981 * pages in dirty list must be sorted by page_id to write to flash
982 */
983 static
984 URET
uffs_BufFlush_Exist_With_Enough_FreePage(uffs_Device * dev,int slot,TreeNode * node,uffs_BlockInfo * bc)985 uffs_BufFlush_Exist_With_Enough_FreePage(
986 uffs_Device *dev,
987 int slot, //!< dirty group slot
988 TreeNode *node, //!< tree node
989 uffs_BlockInfo *bc //!< block info (Source, also destination)
990 )
991 {
992 u16 page;
993 uffs_Buf *buf;
994 uffs_Tags *tag;
995 URET ret = U_FAIL;
996 int x;
997
998 // uffs_Perror(UFFS_MSG_NOISY,
999 // "Flush buffers with Enough Free Page to block %d",
1000 // bc->block);
1001
1002 for (page = 1; // page 0 won't be a free page, so we start from 1.
1003 page < dev->attr->pages_per_block &&
1004 dev->buf.dirtyGroup[slot].count > 0; //still has dirty pages?
1005 page++) {
1006
1007 // locate to the free page (make sure the page is a erased page, so an unclean page won't sneak in)
1008 for (; page < dev->attr->pages_per_block; page++) {
1009 if (uffs_IsPageErased(dev, bc, page))
1010 break;
1011 }
1012
1013 if (!uffs_Assert(page < dev->attr->pages_per_block, "no free page? buf flush not finished."))
1014 break;
1015
1016 buf = _FindMinimunPageIdFromDirtyList(dev->buf.dirtyGroup[slot].dirty);
1017 if (buf == NULL) {
1018 uffs_Perror(UFFS_MSG_SERIOUS,
1019 "count > 0, but no dirty pages in list ?");
1020 goto ext;
1021 }
1022
1023 //write the dirty page (id: buf->page_id) to page (free page)
1024 tag = GET_TAG(bc, page);
1025 TAG_DIRTY_BIT(tag) = TAG_DIRTY;
1026 TAG_VALID_BIT(tag) = TAG_VALID;
1027 TAG_BLOCK_TS(tag) = uffs_GetBlockTimeStamp(dev, bc);
1028 TAG_DATA_LEN(tag) = buf->data_len;
1029 TAG_TYPE(tag) = buf->type;
1030 TAG_PARENT(tag) = buf->parent;
1031 TAG_SERIAL(tag) = buf->serial;
1032 TAG_PAGE_ID(tag) = (u8)(buf->page_id);
1033
1034 SEAL_TAG(tag);
1035
1036 x = uffs_FlashWritePageCombine(dev, bc->block, page, buf, tag);
1037 if (x == UFFS_FLASH_IO_ERR) {
1038 uffs_Perror(UFFS_MSG_NORMAL, "I/O error <1>?");
1039 goto ext;
1040 }
1041 else if (x == UFFS_FLASH_BAD_BLK) {
1042 uffs_Perror(UFFS_MSG_NORMAL, "Bad blcok found, start block cover ...");
1043 ret = uffs_BufFlush_Exist_With_BlockCover(dev, slot, node, bc);
1044 goto ext;
1045 }
1046 else {
1047 if(_BreakFromDirty(dev, buf) == U_SUCC) {
1048 buf->mark = UFFS_BUF_VALID;
1049 _MoveNodeToHead(dev, buf);
1050 }
1051 }
1052 } //end of for
1053
1054 if (dev->buf.dirtyGroup[slot].dirty != NULL ||
1055 dev->buf.dirtyGroup[slot].count != 0) {
1056 uffs_Perror(UFFS_MSG_NORMAL, "still has dirty buffer ?");
1057 }
1058 else {
1059 ret = U_SUCC;
1060 }
1061
1062 ext:
1063 return ret;
1064 }
1065
1066
_BufFlush(struct uffs_DeviceSt * dev,UBOOL force_block_recover,int slot)1067 URET _BufFlush(struct uffs_DeviceSt *dev,
1068 UBOOL force_block_recover, int slot)
1069 {
1070 uffs_Buf *dirty;
1071 TreeNode *node;
1072 uffs_BlockInfo *bc;
1073 u16 n;
1074 URET ret;
1075 u8 type;
1076 u16 parent;
1077 u16 serial;
1078 int block;
1079
1080 if (dev->buf.dirtyGroup[slot].count == 0) {
1081 return U_SUCC;
1082 }
1083
1084 dirty = dev->buf.dirtyGroup[slot].dirty;
1085
1086 if (_CheckDirtyList(dirty) == U_FAIL)
1087 return U_FAIL;
1088
1089 type = dirty->type;
1090 parent = dirty->parent;
1091 serial = dirty->serial;
1092
1093 switch (type) {
1094 case UFFS_TYPE_DIR:
1095 node = uffs_TreeFindDirNode(dev, serial);
1096 break;
1097 case UFFS_TYPE_FILE:
1098 node = uffs_TreeFindFileNode(dev, serial);
1099 break;
1100 case UFFS_TYPE_DATA:
1101 node = uffs_TreeFindDataNode(dev, parent, serial);
1102 break;
1103 default:
1104 uffs_Perror(UFFS_MSG_SERIOUS, "unknown type");
1105 return U_FAIL;
1106 }
1107
1108 if (node == NULL) {
1109 //not found in the tree, need to generate a new block
1110 ret = _BufFlush_NewBlock(dev, slot);
1111 }
1112 else {
1113 switch (type) {
1114 case UFFS_TYPE_DIR:
1115 block = node->u.dir.block;
1116 break;
1117 case UFFS_TYPE_FILE:
1118 block = node->u.file.block;
1119 break;
1120 case UFFS_TYPE_DATA:
1121 block = node->u.data.block;
1122 break;
1123 default:
1124 uffs_Perror(UFFS_MSG_SERIOUS, "unknown type.");
1125 return U_FAIL;
1126 }
1127 bc = uffs_BlockInfoGet(dev, block);
1128 if(bc == NULL) {
1129 uffs_Perror(UFFS_MSG_SERIOUS, "get block info fail.");
1130 return U_FAIL;
1131 }
1132
1133 ret = uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
1134 if (ret == U_SUCC) {
1135
1136 n = uffs_GetFreePagesCount(dev, bc);
1137
1138 if (n >= dev->buf.dirtyGroup[slot].count && !force_block_recover) {
1139 //The free pages are enough for the dirty pages
1140 ret = uffs_BufFlush_Exist_With_Enough_FreePage(dev, slot, node, bc);
1141 }
1142 else {
1143 ret = uffs_BufFlush_Exist_With_BlockCover(dev, slot, node, bc);
1144 }
1145 }
1146 uffs_BlockInfoPut(dev, bc);
1147 }
1148
1149 return ret;
1150 }
1151
_FindMostDirtyGroup(struct uffs_DeviceSt * dev)1152 static int _FindMostDirtyGroup(struct uffs_DeviceSt *dev)
1153 {
1154 int i, slot = -1;
1155 int max_count = 0;
1156
1157 for (i = 0; i < dev->cfg.dirty_groups; i++) {
1158 if (dev->buf.dirtyGroup[i].dirty &&
1159 dev->buf.dirtyGroup[i].lock == 0) {
1160 if (dev->buf.dirtyGroup[i].count > max_count) {
1161 max_count = dev->buf.dirtyGroup[i].count;
1162 slot = i;
1163 }
1164 }
1165 }
1166
1167 return slot;
1168 }
1169
1170 /** lock dirty group */
uffs_BufLockGroup(struct uffs_DeviceSt * dev,int slot)1171 URET uffs_BufLockGroup(struct uffs_DeviceSt *dev, int slot)
1172 {
1173 URET ret = U_FAIL;
1174 if (slot >= 0 && slot < dev->cfg.dirty_groups) {
1175 dev->buf.dirtyGroup[slot].lock++;
1176 ret = U_SUCC;
1177 }
1178 return ret;
1179 }
1180
1181 /** unlock dirty group */
uffs_BufUnLockGroup(struct uffs_DeviceSt * dev,int slot)1182 URET uffs_BufUnLockGroup(struct uffs_DeviceSt *dev, int slot)
1183 {
1184 URET ret = U_FAIL;
1185
1186 if (slot >= 0 && slot < dev->cfg.dirty_groups) {
1187 if (dev->buf.dirtyGroup[slot].lock > 0)
1188 dev->buf.dirtyGroup[slot].lock--;
1189 else {
1190 uffs_Perror(UFFS_MSG_SERIOUS, "Try to unlock an unlocked group ?");
1191 }
1192 ret = U_SUCC;
1193 }
1194 return ret;
1195 }
1196
1197
1198 /**
1199 * flush buffers to flash.
1200 * this will flush all dirty groups.
1201 * \param[in] dev uffs device
1202 */
uffs_BufFlush(struct uffs_DeviceSt * dev)1203 URET uffs_BufFlush(struct uffs_DeviceSt *dev)
1204 {
1205 int slot;
1206
1207 slot = uffs_BufFindFreeGroupSlot(dev);
1208 if (slot >= 0)
1209 return U_SUCC; // do nothing if there is free slot
1210 else
1211 return uffs_BufFlushMostDirtyGroup(dev);
1212 }
1213
1214 /**
1215 * flush most dirty group
1216 * \param[in] dev uffs device
1217 */
uffs_BufFlushMostDirtyGroup(struct uffs_DeviceSt * dev)1218 URET uffs_BufFlushMostDirtyGroup(struct uffs_DeviceSt *dev)
1219 {
1220 int slot;
1221
1222 slot = _FindMostDirtyGroup(dev);
1223 if (slot >= 0) {
1224 return _BufFlush(dev, U_FALSE, slot);
1225 }
1226 return U_SUCC;
1227 }
1228
1229 /**
1230 * flush buffers to flash
1231 * this will pick up a most dirty group,
1232 * and flush it if there is no free dirty group slot.
1233 *
1234 * \param[in] dev uffs device
1235 * \param[in] force_block_recover #U_TRUE: force a block recover
1236 * even there are enough free pages
1237 */
uffs_BufFlushEx(struct uffs_DeviceSt * dev,UBOOL force_block_recover)1238 URET uffs_BufFlushEx(struct uffs_DeviceSt *dev, UBOOL force_block_recover)
1239 {
1240 int slot;
1241
1242 slot = uffs_BufFindFreeGroupSlot(dev);
1243 if (slot >= 0) {
1244 return U_SUCC; //there is free slot, do nothing.
1245 }
1246 else {
1247 slot = _FindMostDirtyGroup(dev);
1248 return _BufFlush(dev, force_block_recover, slot);
1249 }
1250 }
1251
1252 /**
1253 * flush buffer group with given parent/serial num.
1254 *
1255 * \param[in] dev uffs device
1256 * \param[in] parent parent num of the group
1257 * \param[in] serial serial num of the group
1258 */
uffs_BufFlushGroup(struct uffs_DeviceSt * dev,u16 parent,u16 serial)1259 URET uffs_BufFlushGroup(struct uffs_DeviceSt *dev, u16 parent, u16 serial)
1260 {
1261 int slot;
1262
1263 slot = uffs_BufFindGroupSlot(dev, parent, serial);
1264 if (slot >= 0) {
1265 return _BufFlush(dev, U_FALSE, slot);
1266 }
1267
1268 return U_SUCC;
1269 }
1270
1271 /**
1272 * flush buffer group with given parent/serial num
1273 * and force_block_recover indicator.
1274 *
1275 * \param[in] dev uffs device
1276 * \param[in] parent parent num of the group
1277 * \param[in] serial serial num of group
1278 * \param[in] force_block_recover indicator
1279 */
uffs_BufFlushGroupEx(struct uffs_DeviceSt * dev,u16 parent,u16 serial,UBOOL force_block_recover)1280 URET uffs_BufFlushGroupEx(struct uffs_DeviceSt *dev,
1281 u16 parent, u16 serial, UBOOL force_block_recover)
1282 {
1283 int slot;
1284
1285 slot = uffs_BufFindGroupSlot(dev, parent, serial);
1286 if (slot >= 0) {
1287 return _BufFlush(dev, force_block_recover, slot);
1288 }
1289
1290 return U_SUCC;
1291 }
1292
1293
1294 /**
1295 * flush buffer group/groups which match given parent num.
1296 *
1297 * \param[in] dev uffs device
1298 * \param[in] parent parent num of the group
1299 * \param[in] serial serial num of group
1300 * \param[in] force_block_recover indicator
1301 */
uffs_BufFlushGroupMatchParent(struct uffs_DeviceSt * dev,u16 parent)1302 URET uffs_BufFlushGroupMatchParent(struct uffs_DeviceSt *dev, u16 parent)
1303 {
1304 int slot;
1305 uffs_Buf *buf;
1306 URET ret = U_SUCC;
1307
1308 for (slot = 0; slot < dev->cfg.dirty_groups && ret == U_SUCC; slot++) {
1309 if (dev->buf.dirtyGroup[slot].dirty) {
1310 buf = dev->buf.dirtyGroup[slot].dirty;
1311 if (buf->parent == parent) {
1312 ret = _BufFlush(dev, U_FALSE, slot);
1313 }
1314 }
1315 }
1316
1317 return ret;
1318 }
1319
1320 /**
1321 * find a free dirty group slot
1322 *
1323 * \param[in] dev uffs device
1324 * \return slot index (0 to MAX_DIRTY_BUF_GROUPS - 1) if found one,
1325 * otherwise return -1.
1326 */
uffs_BufFindFreeGroupSlot(struct uffs_DeviceSt * dev)1327 int uffs_BufFindFreeGroupSlot(struct uffs_DeviceSt *dev)
1328 {
1329 int i, slot = -1;
1330
1331 for (i = 0; i < dev->cfg.dirty_groups; i++) {
1332 if (dev->buf.dirtyGroup[i].dirty == NULL) {
1333 slot = i;
1334 break;
1335 }
1336 }
1337 return slot;
1338 }
1339
1340 /**
1341 * find a dirty group slot with given parent/serial num.
1342 *
1343 * \param[in] dev uffs device
1344 * \param[in] parent parent num of the group
1345 * \param[in] serial serial num of group
1346 * \return slot index (0 to MAX_DIRTY_BUF_GROUPS - 1) if found one,
1347 * otherwise return -1.
1348 */
uffs_BufFindGroupSlot(struct uffs_DeviceSt * dev,u16 parent,u16 serial)1349 int uffs_BufFindGroupSlot(struct uffs_DeviceSt *dev, u16 parent, u16 serial)
1350 {
1351 uffs_Buf *buf;
1352 int i, slot = -1;
1353
1354 for (i = 0; i < dev->cfg.dirty_groups; i++) {
1355 if (dev->buf.dirtyGroup[i].dirty) {
1356 buf = dev->buf.dirtyGroup[i].dirty;
1357 if (buf->parent == parent && buf->serial == serial) {
1358 slot = i;
1359 break;
1360 }
1361 }
1362 }
1363 return slot;
1364 }
1365
1366 /**
1367 * \brief get a page buffer
1368 * \param[in] dev uffs device
1369 * \param[in] parent parent serial num
1370 * \param[in] serial serial num
1371 * \param[in] page_id page_id
1372 * \return return the buffer found in buffer list, if not found, return NULL.
1373 */
uffs_BufGet(struct uffs_DeviceSt * dev,u16 parent,u16 serial,u16 page_id)1374 uffs_Buf * uffs_BufGet(struct uffs_DeviceSt *dev,
1375 u16 parent, u16 serial, u16 page_id)
1376 {
1377 uffs_Buf *p;
1378
1379 //first, check whether the buffer exist in buf list ?
1380 p = uffs_BufFind(dev, parent, serial, page_id);
1381
1382 if (p) {
1383 p->ref_count++;
1384 _MoveNodeToHead(dev, p);
1385 }
1386
1387 return p;
1388 }
1389
1390 /**
1391 * New generate a buffer
1392 */
uffs_BufNew(struct uffs_DeviceSt * dev,u8 type,u16 parent,u16 serial,u16 page_id)1393 uffs_Buf *uffs_BufNew(struct uffs_DeviceSt *dev,
1394 u8 type, u16 parent, u16 serial, u16 page_id)
1395 {
1396 uffs_Buf *buf;
1397
1398 buf = uffs_BufGet(dev, parent, serial, page_id);
1399 if (buf) {
1400 if (buf->ref_count > 1) {
1401 uffs_Perror(UFFS_MSG_SERIOUS, "When create new buf, \
1402 an exist buffer has ref count %d, possibly bug!",
1403 buf->ref_count);
1404 }
1405 else {
1406 buf->data_len = 0;
1407 }
1408 _MoveNodeToHead(dev, buf);
1409 return buf;
1410 }
1411
1412 buf = _FindFreeBuf(dev);
1413 if (buf == NULL) {
1414 uffs_BufFlushMostDirtyGroup(dev);
1415 buf = _FindFreeBuf(dev);
1416 if (buf == NULL) {
1417 uffs_Perror(UFFS_MSG_SERIOUS, "no free page buf!");
1418 return NULL;
1419 }
1420 }
1421
1422 buf->mark = UFFS_BUF_EMPTY;
1423 buf->type = type;
1424 buf->parent = parent;
1425 buf->serial = serial;
1426 buf->page_id = page_id;
1427 buf->data_len = 0;
1428 buf->ref_count++;
1429 memset(buf->data, 0xff, dev->com.pg_data_size);
1430
1431 _MoveNodeToHead(dev, buf);
1432
1433 return buf;
1434 }
1435
1436
1437
1438 /**
1439 * get a page buffer
1440 * \param[in] dev uffs device
1441 * \param[in] type dir, file or data ?
1442 * \param[in] node node on the tree
1443 * \param[in] page_id page_id
1444 * \param[in] oflag the open flag of current file/dir object
1445 * \return return the buffer if found in buffer list, if not found in
1446 * buffer list, it will get a free buffer, and load data from flash.
1447 * return NULL if not free buffer.
1448 */
uffs_BufGetEx(struct uffs_DeviceSt * dev,u8 type,TreeNode * node,u16 page_id,int oflag)1449 uffs_Buf *uffs_BufGetEx(struct uffs_DeviceSt *dev,
1450 u8 type, TreeNode *node, u16 page_id, int oflag)
1451 {
1452 uffs_Buf *buf;
1453 u16 parent, serial, block, page;
1454 uffs_BlockInfo *bc;
1455
1456 switch (type) {
1457 case UFFS_TYPE_DIR:
1458 parent = node->u.dir.parent;
1459 serial = node->u.dir.serial;
1460 block = node->u.dir.block;
1461 break;
1462 case UFFS_TYPE_FILE:
1463 parent = node->u.file.parent;
1464 serial = node->u.file.serial;
1465 block = node->u.file.block;
1466 break;
1467 case UFFS_TYPE_DATA:
1468 parent = node->u.data.parent;
1469 serial = node->u.data.serial;
1470 block = node->u.data.block;
1471 break;
1472 default:
1473 uffs_Perror(UFFS_MSG_SERIOUS, "unknown type");
1474 return NULL;
1475 }
1476
1477 buf = uffs_BufFind(dev, parent, serial, page_id);
1478 if (buf) {
1479 buf->ref_count++;
1480 return buf;
1481 }
1482
1483 buf = _FindFreeBuf(dev);
1484 if (buf == NULL) {
1485 uffs_BufFlushMostDirtyGroup(dev);
1486 buf = _FindFreeBuf(dev);
1487 if (buf == NULL) {
1488 uffs_Perror(UFFS_MSG_SERIOUS, "no free page buf!");
1489 return NULL;
1490 }
1491
1492 /* Note: if uffs_BufFlushMostDirtyGroup() flush the same block as we'll write to,
1493 * the block will be changed to a new one! (and the content of 'node' is changed).
1494 * So here we need to update block number from the new 'node'.
1495 */
1496 switch (type) {
1497 case UFFS_TYPE_DIR:
1498 block = node->u.dir.block;
1499 break;
1500 case UFFS_TYPE_FILE:
1501 block = node->u.file.block;
1502 break;
1503 case UFFS_TYPE_DATA:
1504 block = node->u.data.block;
1505 break;
1506 default:
1507 uffs_Perror(UFFS_MSG_SERIOUS, "unknown type");
1508 return NULL;
1509 }
1510 }
1511
1512 bc = uffs_BlockInfoGet(dev, block);
1513 if (bc == NULL) {
1514 uffs_Perror(UFFS_MSG_SERIOUS, "Can't get block info!");
1515 return NULL;
1516 }
1517
1518 page = uffs_FindPageInBlockWithPageId(dev, bc, page_id);
1519 if (page == UFFS_INVALID_PAGE) {
1520 uffs_BlockInfoPut(dev, bc);
1521 uffs_Perror(UFFS_MSG_SERIOUS, "can't find right page ? block %d page_id %d", bc->block, page_id);
1522 return NULL;
1523 }
1524 page = uffs_FindBestPageInBlock(dev, bc, page);
1525 if (!uffs_Assert(page != UFFS_INVALID_PAGE, "got an invalid page?\n"))
1526 return NULL;
1527
1528 uffs_BlockInfoPut(dev, bc);
1529
1530 buf->mark = UFFS_BUF_EMPTY;
1531 buf->type = type;
1532 buf->parent = parent;
1533 buf->serial = serial;
1534 buf->page_id = page_id;
1535
1536 if (UFFS_FLASH_HAVE_ERR(uffs_FlashReadPage(dev, block, page, buf, oflag & UO_NOECC ? U_TRUE : U_FALSE))) {
1537 uffs_Perror(UFFS_MSG_SERIOUS, "can't load page from flash !");
1538 return NULL;
1539 }
1540
1541 buf->data_len = TAG_DATA_LEN(GET_TAG(bc, page));
1542 buf->mark = UFFS_BUF_VALID;
1543 buf->ref_count++;
1544
1545 _MoveNodeToHead(dev, buf);
1546
1547 return buf;
1548
1549 }
1550
1551 /**
1552 * \brief Put back a page buffer, make reference count decrease by one
1553 * \param[in] dev uffs device
1554 * \param[in] buf buffer to be put back
1555 */
uffs_BufPut(uffs_Device * dev,uffs_Buf * buf)1556 URET uffs_BufPut(uffs_Device *dev, uffs_Buf *buf)
1557 {
1558 URET ret = U_FAIL;
1559
1560 dev = dev;
1561 if (buf == NULL) {
1562 uffs_Perror(UFFS_MSG_NORMAL, "Can't put an NULL buffer!");
1563 }
1564 else if (buf->ref_count == 0) {
1565 uffs_Perror(UFFS_MSG_NORMAL, "Putting an unused page buffer ? ");
1566 }
1567 else if (buf->ref_count == CLONE_BUF_MARK) {
1568 uffs_Perror(UFFS_MSG_NORMAL, "Putting an cloned page buffer ? ");
1569 ret = uffs_BufFreeClone(dev, buf);
1570 }
1571 else {
1572 buf->ref_count--;
1573 ret = U_SUCC;
1574 }
1575
1576 return ret;
1577 }
1578
1579
1580 /**
1581 * \brief clone from an exist buffer.
1582 allocate memory for new buffer, and copy data from original buffer if
1583 original buffer is not NULL.
1584 * \param[in] dev uffs device
1585 * \param[in] buf page buffer to be clone from.
1586 * if NULL presented here, data copy will not be processed
1587 * \return return the cloned page buffer, all data copied from source
1588 * \note the cloned buffer is not linked in page buffer list in uffs device,
1589 * so you should use #uffs_BufFreeClone instead of #uffs_BufPut
1590 * when you put back or release buffer
1591 */
uffs_BufClone(uffs_Device * dev,uffs_Buf * buf)1592 uffs_Buf * uffs_BufClone(uffs_Device *dev, uffs_Buf *buf)
1593 {
1594 uffs_Buf *p;
1595
1596 p = _FindFreeBufEx(dev, 1);
1597 if (p == NULL) {
1598 uffs_Perror(UFFS_MSG_SERIOUS,
1599 "no enough free pages for clone! " \
1600 "Please increase Clone Buffer Count threshold.");
1601 }
1602 else {
1603 _BreakFromBufList(dev, p);
1604
1605 if (buf) {
1606 p->parent = buf->parent;
1607 p->type = buf->type;
1608 p->serial = buf->serial;
1609 p->page_id = buf->page_id;
1610
1611 p->data_len = buf->data_len;
1612 //athough the valid data length is .data_len,
1613 //but we still need copy the whole buffer, include header
1614 memcpy(p->header, buf->header, dev->com.pg_size);
1615 }
1616 p->next = p->prev = NULL; // the cloned one is not linked to device buffer
1617 p->next_dirty = p->prev_dirty = NULL;
1618 p->ref_count = CLONE_BUF_MARK; // CLONE_BUF_MARK indicates that
1619 // this is an cloned buffer
1620 }
1621
1622 return p;
1623 }
1624
1625 /**
1626 * \brief release cloned buffer
1627 * \param[in] dev uffs device
1628 * \param[in] buf cloned buffer
1629 */
uffs_BufFreeClone(uffs_Device * dev,uffs_Buf * buf)1630 URET uffs_BufFreeClone(uffs_Device *dev, uffs_Buf *buf)
1631 {
1632 dev = dev; //make compiler happy
1633 if (!buf)
1634 return U_FAIL;
1635
1636 if (buf->ref_count != CLONE_BUF_MARK) {
1637 /* a cloned buffer must have a ref_count of CLONE_BUF_MARK */
1638 uffs_Perror(UFFS_MSG_SERIOUS,
1639 "Try to release a non-cloned page buffer ?");
1640 return U_FAIL;
1641 }
1642
1643 buf->ref_count = 0;
1644 buf->mark = UFFS_BUF_EMPTY;
1645 _LinkToBufListTail(dev, buf);
1646
1647 return U_SUCC;
1648 }
1649
1650
1651
uffs_BufIsAllFree(struct uffs_DeviceSt * dev)1652 UBOOL uffs_BufIsAllFree(struct uffs_DeviceSt *dev)
1653 {
1654 uffs_Buf *buf = dev->buf.head;
1655
1656 while (buf) {
1657 if(buf->ref_count != 0) return U_FALSE;
1658 buf = buf->next;
1659 }
1660
1661 return U_TRUE;
1662 }
1663
uffs_BufIsAllEmpty(struct uffs_DeviceSt * dev)1664 UBOOL uffs_BufIsAllEmpty(struct uffs_DeviceSt *dev)
1665 {
1666 uffs_Buf *buf = dev->buf.head;
1667
1668 while (buf) {
1669 if(buf->mark != UFFS_BUF_EMPTY) return U_FALSE;
1670 buf = buf->next;
1671 }
1672
1673 return U_TRUE;
1674 }
1675
1676
uffs_BufSetAllEmpty(struct uffs_DeviceSt * dev)1677 URET uffs_BufSetAllEmpty(struct uffs_DeviceSt *dev)
1678 {
1679 uffs_Buf *buf = dev->buf.head;
1680
1681 while (buf) {
1682 buf->mark = UFFS_BUF_EMPTY;
1683 buf = buf->next;
1684 }
1685 return U_SUCC;
1686 }
1687
1688
uffs_BufIncRef(uffs_Buf * buf)1689 void uffs_BufIncRef(uffs_Buf *buf)
1690 {
1691 buf->ref_count++;
1692 }
1693
uffs_BufDecRef(uffs_Buf * buf)1694 void uffs_BufDecRef(uffs_Buf *buf)
1695 {
1696 if (buf->ref_count > 0)
1697 buf->ref_count--;
1698 }
1699
1700 /** mark buffer as #UFFS_BUF_EMPTY if ref_count == 0,
1701 and discard all data it holds */
uffs_BufMarkEmpty(uffs_Device * dev,uffs_Buf * buf)1702 void uffs_BufMarkEmpty(uffs_Device *dev, uffs_Buf *buf)
1703 {
1704 if (buf->mark != UFFS_BUF_EMPTY) {
1705 if (buf->ref_count == 0) {
1706 if (buf->mark == UFFS_BUF_DIRTY)
1707 _BreakFromDirty(dev, buf);
1708 buf->mark = UFFS_BUF_EMPTY;
1709 }
1710 }
1711 }
1712
1713 #if 0
1714 static UBOOL _IsBufInDirtyList(struct uffs_DeviceSt *dev, uffs_Buf *buf)
1715 {
1716 uffs_Buf *p = dev->buf.dirtyGroup[slot].dirty;
1717
1718 while (p) {
1719 if(p == buf) return U_TRUE;
1720 p = p->next_dirty;
1721 }
1722
1723 return U_FALSE;
1724 }
1725 #endif
1726
uffs_BufWrite(struct uffs_DeviceSt * dev,uffs_Buf * buf,void * data,u32 ofs,u32 len)1727 URET uffs_BufWrite(struct uffs_DeviceSt *dev,
1728 uffs_Buf *buf, void *data, u32 ofs, u32 len)
1729 {
1730 int slot;
1731
1732 if(ofs + len > dev->com.pg_data_size) {
1733 uffs_Perror(UFFS_MSG_SERIOUS,
1734 "data length out of range! %d+%d", ofs, len);
1735 return U_FAIL;
1736 }
1737
1738 slot = uffs_BufFindGroupSlot(dev, buf->parent, buf->serial);
1739
1740 if (slot < 0) {
1741 // need to take a free slot
1742 slot = uffs_BufFindFreeGroupSlot(dev);
1743 if (slot < 0) {
1744 // no free slot ? flush buffer
1745 if (uffs_BufFlushMostDirtyGroup(dev) != U_SUCC)
1746 return U_FAIL;
1747
1748 slot = uffs_BufFindFreeGroupSlot(dev);
1749 if (slot < 0) {
1750 // still no free slot ??
1751 uffs_Perror(UFFS_MSG_SERIOUS, "no free slot ?");
1752 return U_FAIL;
1753 }
1754 }
1755 }
1756
1757 if (data)
1758 memcpy(buf->data + ofs, data, len);
1759 else
1760 memset(buf->data + ofs, 0, len); // if data == NULL, then fill all '\0'.
1761
1762 if (ofs + len > buf->data_len)
1763 buf->data_len = ofs + len;
1764
1765 if (_IsBufInInDirtyList(dev, slot, buf) == U_FALSE) {
1766 _LinkToDirtyList(dev, slot, buf);
1767 }
1768
1769 if (dev->buf.dirtyGroup[slot].count >= dev->buf.dirty_buf_max) {
1770 if (uffs_BufFlushGroup(dev, buf->parent, buf->serial) != U_SUCC) {
1771 return U_FAIL;
1772 }
1773 }
1774
1775 return U_SUCC;
1776 }
1777
uffs_BufRead(struct uffs_DeviceSt * dev,uffs_Buf * buf,void * data,u32 ofs,u32 len)1778 URET uffs_BufRead(struct uffs_DeviceSt *dev,
1779 uffs_Buf *buf, void *data, u32 ofs, u32 len)
1780 {
1781 u32 readSize;
1782 u32 pg_data_size = dev->com.pg_data_size;
1783
1784 readSize = (ofs >= pg_data_size ?
1785 0 : (ofs + len >= pg_data_size ? pg_data_size - ofs : len)
1786 );
1787
1788 if (readSize > 0)
1789 memcpy(data, buf->data + ofs, readSize);
1790
1791 return U_SUCC;
1792 }
1793
1794
1795
1796
1797
1798
1799
1800