xref: /nrf52832-nimble/rt-thread/components/dfs/filesystems/uffs/src/uffs/uffs_badblock.c (revision 104654410c56c573564690304ae786df310c91fc)
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, &region);
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