1 /*
2 This file is part of UFFS, the Ultra-low-cost Flash File System.
3
4 Copyright (C) 2005-2009 Ricky Zheng <[email protected]>
5
6 UFFS is free software; you can redistribute it and/or modify it under
7 the GNU Library General Public License as published by the Free Software
8 Foundation; either version 2 of the License, or (at your option) any
9 later version.
10
11 UFFS is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 or GNU Library General Public License, as applicable, for more details.
15
16 You should have received a copy of the GNU General Public License
17 and GNU Library General Public License along with UFFS; if not, write
18 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 As a special exception, if other files instantiate templates or use
22 macros or inline functions from this file, or you compile this file
23 and link it with other works to produce a work based on this file,
24 this file does not by itself cause the resulting work to be covered
25 by the GNU General Public License. However the source code for this
26 file must still be made available in accordance with section (3) of
27 the GNU General Public License v2.
28
29 This exception does not invalidate any other reasons why a work based
30 on this file might be covered by the GNU General Public License.
31 */
32
33 /**
34 * \file uffs_badblock.c
35 * \brief bad block checking and recovering
36 * \author Ricky Zheng, created in 13th Jun, 2005
37 */
38 #include "uffs_config.h"
39 #include "uffs/uffs_fs.h"
40 #include "uffs/uffs_ecc.h"
41 #include "uffs/uffs_badblock.h"
42 #include <string.h>
43
44 #define PFX "bbl : "
45
uffs_BadBlockInit(uffs_Device * dev)46 void uffs_BadBlockInit(uffs_Device *dev)
47 {
48 dev->bad.block = UFFS_INVALID_BLOCK;
49 }
50
51
52 /**
53 * \brief process bad block: erase bad block, mark it as 'bad'
54 * and put the node to bad block list.
55 * \param[in] dev uffs device
56 * \param[in] node bad block tree node
57 * (before the block turn 'bad', it must belong to something ...)
58 */
uffs_BadBlockProcess(uffs_Device * dev,TreeNode * node)59 void uffs_BadBlockProcess(uffs_Device *dev, TreeNode *node)
60 {
61 if (HAVE_BADBLOCK(dev)) {
62 // mark the bad block
63 uffs_FlashMarkBadBlock(dev, dev->bad.block);
64
65 // and put it into bad block list
66 if (node != NULL)
67 uffs_TreeInsertToBadBlockList(dev, node);
68
69 //clear bad block mark.
70 dev->bad.block = UFFS_INVALID_BLOCK;
71
72 }
73 }
74
75 /**
76 * \brief process bad block and put the node in 'suspend' list.
77 * \param[in] dev uffs device
78 * \param[in] node bad block tree node
79 */
uffs_BadBlockProcessSuspend(uffs_Device * dev,TreeNode * node)80 void uffs_BadBlockProcessSuspend(uffs_Device *dev, TreeNode *node)
81 {
82 if (HAVE_BADBLOCK(dev)) {
83 // mark the bad block
84 uffs_FlashMarkBadBlock(dev, dev->bad.block);
85
86 // and put it into bad block list
87 if (node != NULL)
88 uffs_TreeSuspendAdd(dev, node);
89
90 //clear bad block mark.
91 dev->bad.block = UFFS_INVALID_BLOCK;
92 }
93 }
94
95 /**
96 * \brief recover bad block
97 * \param[in] dev uffs device
98 */
uffs_BadBlockRecover(uffs_Device * dev)99 void uffs_BadBlockRecover(uffs_Device *dev)
100 {
101 TreeNode *good, *bad;
102 uffs_Buf *buf;
103 u16 i;
104 u16 page;
105 uffs_BlockInfo *bc = NULL;
106 uffs_Tags *tag;
107 uffs_Tags newTag;
108 UBOOL succRecov;
109 UBOOL goodBlockIsDirty = U_FALSE;
110 int ret;
111 int region;
112 u8 type;
113
114 if (dev->bad.block == UFFS_INVALID_BLOCK)
115 return;
116
117 // pick up an erased good block
118 good = uffs_TreeGetErasedNode(dev);
119 if (good == NULL) {
120 uffs_Perror(UFFS_MSG_SERIOUS, "no free block to replace bad block!");
121 return;
122 }
123
124 //recover block
125 bc = uffs_BlockInfoGet(dev, dev->bad.block);
126
127 if (bc == NULL) {
128 uffs_Perror(UFFS_MSG_SERIOUS, "can't get bad block info");
129 return;
130 }
131
132 succRecov = U_TRUE;
133 for (i = 0; i < dev->attr->pages_per_block; i++) {
134 page = uffs_FindPageInBlockWithPageId(dev, bc, i);
135 if (page == UFFS_INVALID_PAGE) {
136 break; //end of last valid page, normal break
137 }
138 page = uffs_FindBestPageInBlock(dev, bc, page);
139 if (page == UFFS_INVALID_PAGE) {
140 // got an invalid page ? it's bad block anyway ...
141 uffs_Perror(UFFS_MSG_SERIOUS, "bad block recover (block %d) not finished", bc->block);
142 break;
143 }
144 tag = GET_TAG(bc, page);
145 buf = uffs_BufClone(dev, NULL);
146 if (buf == NULL) {
147 uffs_Perror(UFFS_MSG_SERIOUS, "Can't clone a new buf!");
148 succRecov = U_FALSE;
149 break;
150 }
151 //NOTE: since this is a bad block, we can't guarantee the data is ECC ok,
152 // so just load data even ECC is not OK.
153 ret = uffs_LoadPhyDataToBufEccUnCare(dev, buf, bc->block, page);
154 if (ret == U_FAIL) {
155 uffs_Perror(UFFS_MSG_SERIOUS, "I/O error ?");
156 uffs_BufFreeClone(dev, buf);
157 succRecov = U_FALSE;
158 break;
159 }
160 buf->data_len = TAG_DATA_LEN(tag);
161 if (buf->data_len > dev->com.pg_data_size) {
162 uffs_Perror(UFFS_MSG_NOISY, "data length over flow!!!");
163 buf->data_len = dev->com.pg_data_size;
164 }
165
166 buf->parent = TAG_PARENT(tag);
167 buf->serial = TAG_SERIAL(tag);
168 buf->type = TAG_TYPE(tag);
169 buf->page_id = TAG_PAGE_ID(tag);
170
171 // new tag copied from old tag, and increase time-stamp.
172 newTag = *tag;
173 TAG_BLOCK_TS(&newTag) = uffs_GetNextBlockTimeStamp(TAG_BLOCK_TS(tag));
174
175 ret = uffs_FlashWritePageCombine(dev, good->u.list.block, i, buf, &newTag);
176
177 goodBlockIsDirty = U_TRUE;
178 uffs_BufFreeClone(dev, buf);
179
180 if (ret == UFFS_FLASH_IO_ERR) {
181 uffs_Perror(UFFS_MSG_NORMAL, "I/O error ?");
182 succRecov = U_FALSE;
183 break;
184 }
185 }
186
187
188 if (succRecov == U_TRUE) {
189 // successful recover bad block, so need to mark bad block,
190 // and replace with good one
191
192 region = SEARCH_REGION_DIR|SEARCH_REGION_FILE|SEARCH_REGION_DATA;
193 bad = uffs_TreeFindNodeByBlock(dev, dev->bad.block, ®ion);
194 if (bad != NULL) {
195 switch (region) {
196 case SEARCH_REGION_DIR:
197 bad->u.dir.block = good->u.list.block;
198 type = UFFS_TYPE_DIR;
199 break;
200 case SEARCH_REGION_FILE:
201 bad->u.file.block = good->u.list.block;
202 type = UFFS_TYPE_FILE;
203 break;
204 case SEARCH_REGION_DATA:
205 bad->u.data.block = good->u.list.block;
206 type = UFFS_TYPE_DATA;
207 }
208
209 //from now, the 'bad' is actually good block :)))
210 uffs_Perror(UFFS_MSG_NOISY,
211 "new bad block %d found, and replaced by %d, type %d!",
212 dev->bad.block, good->u.list.block, type);
213 uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);
214 //we reuse the 'good' node as bad block node, and process the bad block.
215 good->u.list.block = dev->bad.block;
216 uffs_BadBlockProcess(dev, good);
217 }
218 else {
219 uffs_Perror(UFFS_MSG_SERIOUS,
220 "can't find the reported bad block(%d) in the tree???",
221 dev->bad.block);
222 if (goodBlockIsDirty == U_TRUE)
223 dev->ops->EraseBlock(dev, good->u.list.block);
224 uffs_TreeInsertToErasedListTail(dev, good);
225 }
226 }
227 else {
228 if (goodBlockIsDirty == U_TRUE)
229 dev->ops->EraseBlock(dev, good->u.list.block);
230 uffs_TreeInsertToErasedListTail(dev, good); //put back to erased list
231 }
232
233 uffs_BlockInfoPut(dev, bc);
234
235 }
236
237
238 /** put a new block to the bad block waiting list */
uffs_BadBlockAdd(uffs_Device * dev,int block)239 void uffs_BadBlockAdd(uffs_Device *dev, int block)
240 {
241 if (dev->bad.block == block)
242 return;
243
244 if (dev->bad.block != UFFS_INVALID_BLOCK)
245 uffs_Perror(UFFS_MSG_SERIOUS, "Can't add more then one bad block !");
246 else
247 dev->bad.block = block;
248 }
249
250