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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 */ 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 */ 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 */ 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 1689 void uffs_BufIncRef(uffs_Buf *buf) 1690 { 1691 buf->ref_count++; 1692 } 1693 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 */ 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 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 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