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 17struct yaffs_checkpt_chunk_hdr { 18 int version; 19 int seq; 20 u32 sum; 21 u32 xor; 22} ; 23 24 25static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) 26{ 27 return chunk - dev->chunk_offset; 28} 29 30static int apply_block_offset(struct yaffs_dev *dev, int block) 31{ 32 return block - dev->block_offset; 33} 34 35static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) 36{ 37 struct yaffs_checkpt_chunk_hdr hdr; 38 39 hdr.version = YAFFS_CHECKPOINT_VERSION; 40 hdr.seq = dev->checkpt_page_seq; 41 hdr.sum = dev->checkpt_sum; 42 hdr.xor = dev->checkpt_xor; 43 44 dev->checkpt_byte_offs = sizeof(hdr); 45 46 memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); 47} 48 49static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) 50{ 51 struct yaffs_checkpt_chunk_hdr hdr; 52 53 memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); 54 55 dev->checkpt_byte_offs = sizeof(hdr); 56 57 return hdr.version == YAFFS_CHECKPOINT_VERSION && 58 hdr.seq == dev->checkpt_page_seq && 59 hdr.sum == dev->checkpt_sum && 60 hdr.xor == dev->checkpt_xor; 61} 62 63static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) 64{ 65 int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; 66 67 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 68 "checkpt blocks_avail = %d", blocks_avail); 69 70 return (blocks_avail <= 0) ? 0 : 1; 71} 72 73static int yaffs_checkpt_erase(struct yaffs_dev *dev) 74{ 75 int i; 76 77 if (!dev->drv.drv_erase_fn) 78 return 0; 79 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 80 "checking blocks %d to %d", 81 dev->internal_start_block, dev->internal_end_block); 82 83 for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { 84 struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); 85 int offset_i = apply_block_offset(dev, i); 86 int result; 87 88 if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { 89 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 90 "erasing checkpt block %d", i); 91 92 dev->n_erasures++; 93 94 result = dev->drv.drv_erase_fn(dev, offset_i); 95 if(result) { 96 bi->block_state = YAFFS_BLOCK_STATE_EMPTY; 97 dev->n_erased_blocks++; 98 dev->n_free_chunks += 99 dev->param.chunks_per_block; 100 } else { 101 dev->drv.drv_mark_bad_fn(dev, offset_i); 102 bi->block_state = YAFFS_BLOCK_STATE_DEAD; 103 } 104 } 105 } 106 107 dev->blocks_in_checkpt = 0; 108 109 return 1; 110} 111 112static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) 113{ 114 int i; 115 int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; 116 117 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 118 "allocating checkpt block: erased %d reserved %d avail %d next %d ", 119 dev->n_erased_blocks, dev->param.n_reserved_blocks, 120 blocks_avail, dev->checkpt_next_block); 121 122 if (dev->checkpt_next_block >= 0 && 123 dev->checkpt_next_block <= dev->internal_end_block && 124 blocks_avail > 0) { 125 126 for (i = dev->checkpt_next_block; i <= dev->internal_end_block; 127 i++) { 128 struct yaffs_block_info *bi; 129 130 bi = yaffs_get_block_info(dev, i); 131 if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { 132 dev->checkpt_next_block = i + 1; 133 dev->checkpt_cur_block = i; 134 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 135 "allocating checkpt block %d", i); 136 return; 137 } 138 } 139 } 140 yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); 141 142 dev->checkpt_next_block = -1; 143 dev->checkpt_cur_block = -1; 144} 145 146static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) 147{ 148 int i; 149 struct yaffs_ext_tags tags; 150 151 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 152 "find next checkpt block: start: blocks %d next %d", 153 dev->blocks_in_checkpt, dev->checkpt_next_block); 154 155 if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) 156 for (i = dev->checkpt_next_block; i <= dev->internal_end_block; 157 i++) { 158 int chunk = i * dev->param.chunks_per_block; 159 enum yaffs_block_state state; 160 u32 seq; 161 162 dev->tagger.read_chunk_tags_fn(dev, 163 apply_chunk_offset(dev, chunk), 164 NULL, &tags); 165 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 166 "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", 167 i, (int) state, 168 tags.obj_id, tags.seq_number, 169 tags.ecc_result); 170 171 if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) 172 continue; 173 174 dev->tagger.query_block_fn(dev, 175 apply_block_offset(dev, i), 176 &state, &seq); 177 if (state == YAFFS_BLOCK_STATE_DEAD) 178 continue; 179 180 /* Right kind of block */ 181 dev->checkpt_next_block = tags.obj_id; 182 dev->checkpt_cur_block = i; 183 dev->checkpt_block_list[dev->blocks_in_checkpt] = i; 184 dev->blocks_in_checkpt++; 185 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 186 "found checkpt block %d", i); 187 return; 188 } 189 190 yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); 191 192 dev->checkpt_next_block = -1; 193 dev->checkpt_cur_block = -1; 194} 195 196int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) 197{ 198 int i; 199 200 dev->checkpt_open_write = writing; 201 202 /* Got the functions we need? */ 203 if (!dev->tagger.write_chunk_tags_fn || 204 !dev->tagger.read_chunk_tags_fn || 205 !dev->drv.drv_erase_fn || 206 !dev->drv.drv_mark_bad_fn) 207 return 0; 208 209 if (writing && !yaffs2_checkpt_space_ok(dev)) 210 return 0; 211 212 if (!dev->checkpt_buffer) 213 dev->checkpt_buffer = 214 kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); 215 if (!dev->checkpt_buffer) 216 return 0; 217 218 dev->checkpt_page_seq = 0; 219 dev->checkpt_byte_count = 0; 220 dev->checkpt_sum = 0; 221 dev->checkpt_xor = 0; 222 dev->checkpt_cur_block = -1; 223 dev->checkpt_cur_chunk = -1; 224 dev->checkpt_next_block = dev->internal_start_block; 225 226 if (writing) { 227 memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); 228 yaffs2_checkpt_init_chunk_hdr(dev); 229 return yaffs_checkpt_erase(dev); 230 } 231 232 /* Opening for a read */ 233 /* Set to a value that will kick off a read */ 234 dev->checkpt_byte_offs = dev->data_bytes_per_chunk; 235 /* A checkpoint block list of 1 checkpoint block per 16 block is 236 * (hopefully) going to be way more than we need */ 237 dev->blocks_in_checkpt = 0; 238 dev->checkpt_max_blocks = 239 (dev->internal_end_block - dev->internal_start_block) / 16 + 2; 240 if (!dev->checkpt_block_list) 241 dev->checkpt_block_list = 242 kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); 243 244 if (!dev->checkpt_block_list) 245 return 0; 246 247 for (i = 0; i < dev->checkpt_max_blocks; i++) 248 dev->checkpt_block_list[i] = -1; 249 250 return 1; 251} 252 253int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) 254{ 255 u32 composite_sum; 256 257 composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); 258 *sum = composite_sum; 259 return 1; 260} 261 262static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) 263{ 264 int chunk; 265 int offset_chunk; 266 struct yaffs_ext_tags tags; 267 268 if (dev->checkpt_cur_block < 0) { 269 yaffs2_checkpt_find_erased_block(dev); 270 dev->checkpt_cur_chunk = 0; 271 } 272 273 if (dev->checkpt_cur_block < 0) 274 return 0; 275 276 tags.is_deleted = 0; 277 tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ 278 tags.chunk_id = dev->checkpt_page_seq + 1; 279 tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; 280 tags.n_bytes = dev->data_bytes_per_chunk; 281 if (dev->checkpt_cur_chunk == 0) { 282 /* First chunk we write for the block? Set block state to 283 checkpoint */ 284 struct yaffs_block_info *bi = 285 yaffs_get_block_info(dev, dev->checkpt_cur_block); 286 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; 287 dev->blocks_in_checkpt++; 288 } 289 290 chunk = 291 dev->checkpt_cur_block * dev->param.chunks_per_block + 292 dev->checkpt_cur_chunk; 293 294 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 295 "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", 296 chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, 297 tags.obj_id, tags.chunk_id); 298 299 offset_chunk = apply_chunk_offset(dev, chunk); 300 301 dev->n_page_writes++; 302 303 dev->tagger.write_chunk_tags_fn(dev, offset_chunk, 304 dev->checkpt_buffer, &tags); 305 dev->checkpt_page_seq++; 306 dev->checkpt_cur_chunk++; 307 if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { 308 dev->checkpt_cur_chunk = 0; 309 dev->checkpt_cur_block = -1; 310 } 311 memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); 312 313 yaffs2_checkpt_init_chunk_hdr(dev); 314 315 316 return 1; 317} 318 319int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) 320{ 321 int i = 0; 322 int ok = 1; 323 u8 *data_bytes = (u8 *) data; 324 325 if (!dev->checkpt_buffer) 326 return 0; 327 328 if (!dev->checkpt_open_write) 329 return -1; 330 331 while (i < n_bytes && ok) { 332 dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; 333 dev->checkpt_sum += *data_bytes; 334 dev->checkpt_xor ^= *data_bytes; 335 336 dev->checkpt_byte_offs++; 337 i++; 338 data_bytes++; 339 dev->checkpt_byte_count++; 340 341 if (dev->checkpt_byte_offs < 0 || 342 dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) 343 ok = yaffs2_checkpt_flush_buffer(dev); 344 } 345 346 return i; 347} 348 349int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) 350{ 351 int i = 0; 352 struct yaffs_ext_tags tags; 353 int chunk; 354 int offset_chunk; 355 u8 *data_bytes = (u8 *) data; 356 357 if (!dev->checkpt_buffer) 358 return 0; 359 360 if (dev->checkpt_open_write) 361 return -1; 362 363 while (i < n_bytes) { 364 365 if (dev->checkpt_byte_offs < 0 || 366 dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { 367 368 if (dev->checkpt_cur_block < 0) { 369 yaffs2_checkpt_find_block(dev); 370 dev->checkpt_cur_chunk = 0; 371 } 372 373 /* Bail out if we can't find a checpoint block */ 374 if (dev->checkpt_cur_block < 0) 375 break; 376 377 chunk = dev->checkpt_cur_block * 378 dev->param.chunks_per_block + 379 dev->checkpt_cur_chunk; 380 381 offset_chunk = apply_chunk_offset(dev, chunk); 382 dev->n_page_reads++; 383 384 /* Read in the next chunk */ 385 dev->tagger.read_chunk_tags_fn(dev, 386 offset_chunk, 387 dev->checkpt_buffer, 388 &tags); 389 390 /* Bail out if the chunk is corrupted. */ 391 if (tags.chunk_id != (dev->checkpt_page_seq + 1) || 392 tags.ecc_result > YAFFS_ECC_RESULT_FIXED || 393 tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) 394 break; 395 396 /* Bail out if it is not a checkpoint chunk. */ 397 if(!yaffs2_checkpt_check_chunk_hdr(dev)) 398 break; 399 400 dev->checkpt_page_seq++; 401 dev->checkpt_cur_chunk++; 402 403 if (dev->checkpt_cur_chunk >= 404 dev->param.chunks_per_block) 405 dev->checkpt_cur_block = -1; 406 407 } 408 409 *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; 410 dev->checkpt_sum += *data_bytes; 411 dev->checkpt_xor ^= *data_bytes; 412 dev->checkpt_byte_offs++; 413 i++; 414 data_bytes++; 415 dev->checkpt_byte_count++; 416 } 417 418 return i; /* Number of bytes read */ 419} 420 421int yaffs_checkpt_close(struct yaffs_dev *dev) 422{ 423 int i; 424 425 if (dev->checkpt_open_write) { 426 if (dev->checkpt_byte_offs != 427 sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) 428 yaffs2_checkpt_flush_buffer(dev); 429 } else if (dev->checkpt_block_list) { 430 for (i = 0; 431 i < dev->blocks_in_checkpt && 432 dev->checkpt_block_list[i] >= 0; i++) { 433 int blk = dev->checkpt_block_list[i]; 434 struct yaffs_block_info *bi = NULL; 435 436 if (dev->internal_start_block <= blk && 437 blk <= dev->internal_end_block) 438 bi = yaffs_get_block_info(dev, blk); 439 if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) 440 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; 441 } 442 } 443 444 dev->n_free_chunks -= 445 dev->blocks_in_checkpt * dev->param.chunks_per_block; 446 dev->n_erased_blocks -= dev->blocks_in_checkpt; 447 448 yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", 449 dev->checkpt_byte_count); 450 451 if (dev->checkpt_buffer) 452 return 1; 453 else 454 return 0; 455} 456 457int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) 458{ 459 /* Erase the checkpoint data */ 460 461 yaffs_trace(YAFFS_TRACE_CHECKPOINT, 462 "checkpoint invalidate of %d blocks", 463 dev->blocks_in_checkpt); 464 465 return yaffs_checkpt_erase(dev); 466} 467