xref: /openbmc/u-boot/fs/yaffs2/yaffs_checkptrw.c (revision c9af6673)
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2011 Aleph One Ltd.
5  *   for Toby Churchill Ltd and Brightstar Engineering
6  *
7  * Created by Charles Manning <charles@aleph1.co.uk>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13 
14 #include "yaffs_checkptrw.h"
15 #include "yaffs_getblockinfo.h"
16 
17 static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)
18 {
19 	int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
20 
21 	yaffs_trace(YAFFS_TRACE_CHECKPOINT,
22 		"checkpt blocks_avail = %d", blocks_avail);
23 
24 	return (blocks_avail <= 0) ? 0 : 1;
25 }
26 
27 static int yaffs_checkpt_erase(struct yaffs_dev *dev)
28 {
29 	int i;
30 
31 	if (!dev->param.erase_fn)
32 		return 0;
33 	yaffs_trace(YAFFS_TRACE_CHECKPOINT,
34 		"checking blocks %d to %d",
35 		dev->internal_start_block, dev->internal_end_block);
36 
37 	for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
38 		struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
39 		if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
40 			yaffs_trace(YAFFS_TRACE_CHECKPOINT,
41 			"erasing checkpt block %d", i);
42 
43 			dev->n_erasures++;
44 
45 			if (dev->param.
46 			    erase_fn(dev,
47 				     i - dev->block_offset /* realign */)) {
48 				bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
49 				dev->n_erased_blocks++;
50 				dev->n_free_chunks +=
51 				    dev->param.chunks_per_block;
52 			} else {
53 				dev->param.bad_block_fn(dev, i);
54 				bi->block_state = YAFFS_BLOCK_STATE_DEAD;
55 			}
56 		}
57 	}
58 
59 	dev->blocks_in_checkpt = 0;
60 
61 	return 1;
62 }
63 
64 static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)
65 {
66 	int i;
67 	int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
68 
69 	yaffs_trace(YAFFS_TRACE_CHECKPOINT,
70 		"allocating checkpt block: erased %d reserved %d avail %d next %d ",
71 		dev->n_erased_blocks, dev->param.n_reserved_blocks,
72 		blocks_avail, dev->checkpt_next_block);
73 
74 	if (dev->checkpt_next_block >= 0 &&
75 	    dev->checkpt_next_block <= dev->internal_end_block &&
76 	    blocks_avail > 0) {
77 
78 		for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
79 		     i++) {
80 			struct yaffs_block_info *bi =
81 			    yaffs_get_block_info(dev, i);
82 			if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
83 				dev->checkpt_next_block = i + 1;
84 				dev->checkpt_cur_block = i;
85 				yaffs_trace(YAFFS_TRACE_CHECKPOINT,
86 					"allocating checkpt block %d", i);
87 				return;
88 			}
89 		}
90 	}
91 	yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");
92 
93 	dev->checkpt_next_block = -1;
94 	dev->checkpt_cur_block = -1;
95 }
96 
97 static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)
98 {
99 	int i;
100 	struct yaffs_ext_tags tags;
101 
102 	yaffs_trace(YAFFS_TRACE_CHECKPOINT,
103 		"find next checkpt block: start:  blocks %d next %d",
104 		dev->blocks_in_checkpt, dev->checkpt_next_block);
105 
106 	if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)
107 		for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
108 		     i++) {
109 			int chunk = i * dev->param.chunks_per_block;
110 			int realigned_chunk = chunk - dev->chunk_offset;
111 
112 			dev->param.read_chunk_tags_fn(dev, realigned_chunk,
113 						      NULL, &tags);
114 			yaffs_trace(YAFFS_TRACE_CHECKPOINT,
115 				"find next checkpt block: search: block %d oid %d seq %d eccr %d",
116 				i, tags.obj_id, tags.seq_number,
117 				tags.ecc_result);
118 
119 			if (tags.seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) {
120 				/* Right kind of block */
121 				dev->checkpt_next_block = tags.obj_id;
122 				dev->checkpt_cur_block = i;
123 				dev->checkpt_block_list[dev->
124 							blocks_in_checkpt] = i;
125 				dev->blocks_in_checkpt++;
126 				yaffs_trace(YAFFS_TRACE_CHECKPOINT,
127 					"found checkpt block %d", i);
128 				return;
129 			}
130 		}
131 
132 	yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");
133 
134 	dev->checkpt_next_block = -1;
135 	dev->checkpt_cur_block = -1;
136 }
137 
138 int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)
139 {
140 	int i;
141 
142 	dev->checkpt_open_write = writing;
143 
144 	/* Got the functions we need? */
145 	if (!dev->param.write_chunk_tags_fn ||
146 	    !dev->param.read_chunk_tags_fn ||
147 	    !dev->param.erase_fn || !dev->param.bad_block_fn)
148 		return 0;
149 
150 	if (writing && !yaffs2_checkpt_space_ok(dev))
151 		return 0;
152 
153 	if (!dev->checkpt_buffer)
154 		dev->checkpt_buffer =
155 		    kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
156 	if (!dev->checkpt_buffer)
157 		return 0;
158 
159 	dev->checkpt_page_seq = 0;
160 	dev->checkpt_byte_count = 0;
161 	dev->checkpt_sum = 0;
162 	dev->checkpt_xor = 0;
163 	dev->checkpt_cur_block = -1;
164 	dev->checkpt_cur_chunk = -1;
165 	dev->checkpt_next_block = dev->internal_start_block;
166 
167 	/* Erase all the blocks in the checkpoint area */
168 	if (writing) {
169 		memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
170 		dev->checkpt_byte_offs = 0;
171 		return yaffs_checkpt_erase(dev);
172 	}
173 
174 	/* Set to a value that will kick off a read */
175 	dev->checkpt_byte_offs = dev->data_bytes_per_chunk;
176 	/* A checkpoint block list of 1 checkpoint block per 16 block is
177 	 * (hopefully) going to be way more than we need */
178 	dev->blocks_in_checkpt = 0;
179 	dev->checkpt_max_blocks =
180 	    (dev->internal_end_block - dev->internal_start_block) / 16 + 2;
181 	dev->checkpt_block_list =
182 	    kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
183 
184 	if (!dev->checkpt_block_list)
185 		return 0;
186 
187 	for (i = 0; i < dev->checkpt_max_blocks; i++)
188 		dev->checkpt_block_list[i] = -1;
189 
190 	return 1;
191 }
192 
193 int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
194 {
195 	u32 composite_sum;
196 
197 	composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
198 	*sum = composite_sum;
199 	return 1;
200 }
201 
202 static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
203 {
204 	int chunk;
205 	int realigned_chunk;
206 	struct yaffs_ext_tags tags;
207 
208 	if (dev->checkpt_cur_block < 0) {
209 		yaffs2_checkpt_find_erased_block(dev);
210 		dev->checkpt_cur_chunk = 0;
211 	}
212 
213 	if (dev->checkpt_cur_block < 0)
214 		return 0;
215 
216 	tags.is_deleted = 0;
217 	tags.obj_id = dev->checkpt_next_block;	/* Hint to next place to look */
218 	tags.chunk_id = dev->checkpt_page_seq + 1;
219 	tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
220 	tags.n_bytes = dev->data_bytes_per_chunk;
221 	if (dev->checkpt_cur_chunk == 0) {
222 		/* First chunk we write for the block? Set block state to
223 		   checkpoint */
224 		struct yaffs_block_info *bi =
225 		    yaffs_get_block_info(dev, dev->checkpt_cur_block);
226 		bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
227 		dev->blocks_in_checkpt++;
228 	}
229 
230 	chunk =
231 	    dev->checkpt_cur_block * dev->param.chunks_per_block +
232 	    dev->checkpt_cur_chunk;
233 
234 	yaffs_trace(YAFFS_TRACE_CHECKPOINT,
235 		"checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
236 		chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
237 		tags.obj_id, tags.chunk_id);
238 
239 	realigned_chunk = chunk - dev->chunk_offset;
240 
241 	dev->n_page_writes++;
242 
243 	dev->param.write_chunk_tags_fn(dev, realigned_chunk,
244 				       dev->checkpt_buffer, &tags);
245 	dev->checkpt_byte_offs = 0;
246 	dev->checkpt_page_seq++;
247 	dev->checkpt_cur_chunk++;
248 	if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) {
249 		dev->checkpt_cur_chunk = 0;
250 		dev->checkpt_cur_block = -1;
251 	}
252 	memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
253 
254 	return 1;
255 }
256 
257 int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
258 {
259 	int i = 0;
260 	int ok = 1;
261 	u8 *data_bytes = (u8 *) data;
262 
263 	if (!dev->checkpt_buffer)
264 		return 0;
265 
266 	if (!dev->checkpt_open_write)
267 		return -1;
268 
269 	while (i < n_bytes && ok) {
270 		dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
271 		dev->checkpt_sum += *data_bytes;
272 		dev->checkpt_xor ^= *data_bytes;
273 
274 		dev->checkpt_byte_offs++;
275 		i++;
276 		data_bytes++;
277 		dev->checkpt_byte_count++;
278 
279 		if (dev->checkpt_byte_offs < 0 ||
280 		    dev->checkpt_byte_offs >= dev->data_bytes_per_chunk)
281 			ok = yaffs2_checkpt_flush_buffer(dev);
282 	}
283 
284 	return i;
285 }
286 
287 int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
288 {
289 	int i = 0;
290 	int ok = 1;
291 	struct yaffs_ext_tags tags;
292 	int chunk;
293 	int realigned_chunk;
294 	u8 *data_bytes = (u8 *) data;
295 
296 	if (!dev->checkpt_buffer)
297 		return 0;
298 
299 	if (dev->checkpt_open_write)
300 		return -1;
301 
302 	while (i < n_bytes && ok) {
303 
304 		if (dev->checkpt_byte_offs < 0 ||
305 		    dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) {
306 
307 			if (dev->checkpt_cur_block < 0) {
308 				yaffs2_checkpt_find_block(dev);
309 				dev->checkpt_cur_chunk = 0;
310 			}
311 
312 			if (dev->checkpt_cur_block < 0) {
313 				ok = 0;
314 				break;
315 			}
316 
317 			chunk = dev->checkpt_cur_block *
318 			    dev->param.chunks_per_block +
319 			    dev->checkpt_cur_chunk;
320 
321 			realigned_chunk = chunk - dev->chunk_offset;
322 			dev->n_page_reads++;
323 
324 			/* read in the next chunk */
325 			dev->param.read_chunk_tags_fn(dev,
326 						realigned_chunk,
327 						dev->checkpt_buffer,
328 						&tags);
329 
330 			if (tags.chunk_id != (dev->checkpt_page_seq + 1) ||
331 			    tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
332 			    tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) {
333 				ok = 0;
334 				break;
335 			}
336 
337 			dev->checkpt_byte_offs = 0;
338 			dev->checkpt_page_seq++;
339 			dev->checkpt_cur_chunk++;
340 
341 			if (dev->checkpt_cur_chunk >=
342 					dev->param.chunks_per_block)
343 				dev->checkpt_cur_block = -1;
344 		}
345 
346 		*data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
347 		dev->checkpt_sum += *data_bytes;
348 		dev->checkpt_xor ^= *data_bytes;
349 		dev->checkpt_byte_offs++;
350 		i++;
351 		data_bytes++;
352 		dev->checkpt_byte_count++;
353 	}
354 
355 	return i;
356 }
357 
358 int yaffs_checkpt_close(struct yaffs_dev *dev)
359 {
360 	int i;
361 
362 	if (dev->checkpt_open_write) {
363 		if (dev->checkpt_byte_offs != 0)
364 			yaffs2_checkpt_flush_buffer(dev);
365 	} else if (dev->checkpt_block_list) {
366 		for (i = 0;
367 		     i < dev->blocks_in_checkpt &&
368 		     dev->checkpt_block_list[i] >= 0; i++) {
369 			int blk = dev->checkpt_block_list[i];
370 			struct yaffs_block_info *bi = NULL;
371 
372 			if (dev->internal_start_block <= blk &&
373 			    blk <= dev->internal_end_block)
374 				bi = yaffs_get_block_info(dev, blk);
375 			if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
376 				bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
377 		}
378 		kfree(dev->checkpt_block_list);
379 		dev->checkpt_block_list = NULL;
380 	}
381 
382 	dev->n_free_chunks -=
383 		dev->blocks_in_checkpt * dev->param.chunks_per_block;
384 	dev->n_erased_blocks -= dev->blocks_in_checkpt;
385 
386 	yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
387 		dev->checkpt_byte_count);
388 
389 	if (dev->checkpt_buffer) {
390 		/* free the buffer */
391 		kfree(dev->checkpt_buffer);
392 		dev->checkpt_buffer = NULL;
393 		return 1;
394 	} else {
395 		return 0;
396 	}
397 }
398 
399 int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
400 {
401 	/* Erase the checkpoint data */
402 
403 	yaffs_trace(YAFFS_TRACE_CHECKPOINT,
404 		"checkpoint invalidate of %d blocks",
405 		dev->blocks_in_checkpt);
406 
407 	return yaffs_checkpt_erase(dev);
408 }
409