Home | History | Annotate | Download | only in yaffs2
      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 (at) 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