kernel: backport fib_trie improvements/fixes from 4.0-rc
[openwrt/svn-archive/archive.git] / target / linux / generic / patches-3.18 / 504-yaffs-fix-compat-tags-handling.patch
1 Subject: yaffs: fix compat tags handling
2
3 Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
4 ---
5 --- a/fs/yaffs2/yaffs_tagscompat.c
6 +++ b/fs/yaffs2/yaffs_tagscompat.c
7 @@ -17,7 +17,9 @@
8 #include "yaffs_getblockinfo.h"
9 #include "yaffs_trace.h"
10
11 +#if 0
12 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
13 +#endif
14
15
16 /********** Tags ECC calculations *********/
17 @@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta
18 return 0;
19 }
20
21 +#if 0
22 /********** Tags **********/
23
24 static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
25 @@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya
26 if(!dev->tagger.mark_bad_fn)
27 dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
28 }
29 +#else
30 +
31 +#include "yaffs_packedtags1.h"
32 +
33 +static int yaffs_tags_compat_write(struct yaffs_dev *dev,
34 + int nand_chunk,
35 + const u8 *data,
36 + const struct yaffs_ext_tags *tags)
37 +{
38 + struct yaffs_packed_tags1 pt1;
39 + u8 tag_buf[9];
40 + int retval;
41 +
42 + /* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */
43 + compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
44 + compile_time_assertion(sizeof(struct yaffs_tags) == 8);
45 +
46 + yaffs_pack_tags1(&pt1, tags);
47 + yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);
48 +
49 + /* When deleting a chunk, the upper layer provides only skeletal
50 + * tags, one with is_deleted set. However, we need to update the
51 + * tags, not erase them completely. So we use the NAND write property
52 + * that only zeroed-bits stick and set tag bytes to all-ones and
53 + * zero just the (not) deleted bit.
54 + */
55 + if (!dev->param.tags_9bytes) {
56 + if (tags->is_deleted) {
57 + memset(&pt1, 0xff, 8);
58 + /* clear delete status bit to indicate deleted */
59 + pt1.deleted = 0;
60 + }
61 + memcpy(tag_buf, &pt1, 8);
62 + } else {
63 + if (tags->is_deleted) {
64 + memset(tag_buf, 0xff, 8);
65 + tag_buf[8] = 0;
66 + } else {
67 + memcpy(tag_buf, &pt1, 8);
68 + tag_buf[8] = 0xff;
69 + }
70 + }
71 +
72 + retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk,
73 + data,
74 + (data) ? dev->data_bytes_per_chunk : 0,
75 + tag_buf,
76 + (dev->param.tags_9bytes) ? 9 : 8);
77 +
78 + return retval;
79 +}
80 +
81 +/* Return with empty extended tags but add ecc_result.
82 + */
83 +static int return_empty_tags(struct yaffs_ext_tags *tags,
84 + enum yaffs_ecc_result ecc_result,
85 + int retval)
86 +{
87 + if (tags) {
88 + memset(tags, 0, sizeof(*tags));
89 + tags->ecc_result = ecc_result;
90 + }
91 +
92 + return retval;
93 +}
94 +
95 +static int yaffs_tags_compat_read(struct yaffs_dev *dev,
96 + int nand_chunk,
97 + u8 *data,
98 + struct yaffs_ext_tags *tags)
99 +{
100 + struct yaffs_packed_tags1 pt1;
101 + enum yaffs_ecc_result ecc_result;
102 + int retval;
103 + int deleted;
104 + u8 tag_buf[9];
105 +
106 + retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
107 + data, dev->param.total_bytes_per_chunk,
108 + tag_buf,
109 + (dev->param.tags_9bytes) ? 9 : 8,
110 + &ecc_result);
111 +
112 + switch (ecc_result) {
113 + case YAFFS_ECC_RESULT_NO_ERROR:
114 + case YAFFS_ECC_RESULT_FIXED:
115 + break;
116 +
117 + case YAFFS_ECC_RESULT_UNFIXED:
118 + default:
119 + return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0);
120 + tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk);
121 + return YAFFS_FAIL;
122 + }
123 +
124 + /* Check for a blank/erased chunk. */
125 + if (yaffs_check_ff(tag_buf, 8)) {
126 + /* when blank, upper layers want ecc_result to be <= NO_ERROR */
127 + return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR,
128 + YAFFS_OK);
129 + }
130 +
131 + memcpy(&pt1, tag_buf, 8);
132 +
133 + if (!dev->param.tags_9bytes) {
134 + /* Read deleted status (bit) then return it to it's non-deleted
135 + * state before performing tags mini-ECC check. pt1.deleted is
136 + * inverted.
137 + */
138 + deleted = !pt1.deleted;
139 + pt1.deleted = 1;
140 + } else {
141 + deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0;
142 + }
143 +
144 + /* Check the packed tags mini-ECC and correct if necessary/possible. */
145 + retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1);
146 + switch (retval) {
147 + case 0:
148 + /* no tags error, use MTD result */
149 + break;
150 + case 1:
151 + /* recovered tags-ECC error */
152 + dev->n_tags_ecc_fixed++;
153 + if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
154 + ecc_result = YAFFS_ECC_RESULT_FIXED;
155 + break;
156 + default:
157 + /* unrecovered tags-ECC error */
158 + dev->n_tags_ecc_unfixed++;
159 + return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED,
160 + YAFFS_FAIL);
161 + }
162 +
163 + /* Unpack the tags to extended form and set ECC result.
164 + * [set should_be_ff just to keep yaffs_unpack_tags1 happy]
165 + */
166 + pt1.should_be_ff = 0xffffffff;
167 + yaffs_unpack_tags1(tags, &pt1);
168 + tags->ecc_result = ecc_result;
169 +
170 + /* Set deleted state */
171 + tags->is_deleted = deleted;
172 + return YAFFS_OK;
173 +}
174 +
175 +static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no)
176 +{
177 + return dev->drv.drv_mark_bad_fn(dev, block_no);
178 +}
179 +
180 +static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
181 + int block_no,
182 + enum yaffs_block_state *state,
183 + u32 *seq_number)
184 +{
185 + struct yaffs_ext_tags tags;
186 + int retval;
187 +
188 + yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no);
189 +
190 + *seq_number = 0;
191 +
192 + retval = dev->drv.drv_check_bad_fn(dev, block_no);
193 + if (retval == YAFFS_FAIL) {
194 + *state = YAFFS_BLOCK_STATE_DEAD;
195 + goto out;
196 + }
197 +
198 + yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block,
199 + NULL, &tags);
200 +
201 + if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) {
202 + yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad",
203 + block_no);
204 + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
205 + } else if (tags.chunk_used) {
206 + *seq_number = tags.seq_number;
207 + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
208 + } else {
209 + *state = YAFFS_BLOCK_STATE_EMPTY;
210 + }
211 +
212 + retval = YAFFS_OK;
213 +
214 +out:
215 + yaffs_trace(YAFFS_TRACE_MTD,
216 + "block query returns seq %u state %d",
217 + *seq_number, *state);
218 +
219 + return retval;
220 +}
221 +
222 +void yaffs_tags_compat_install(struct yaffs_dev *dev)
223 +{
224 + if (dev->param.is_yaffs2)
225 + return;
226 +
227 + if (!dev->tagger.write_chunk_tags_fn)
228 + dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write;
229 +
230 + if (!dev->tagger.read_chunk_tags_fn)
231 + dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read;
232 +
233 + if (!dev->tagger.query_block_fn)
234 + dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
235 +
236 + if (!dev->tagger.mark_bad_fn)
237 + dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
238 +}
239 +#endif