X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_yaffs2.c;h=8c31a661ff1d756778b25931233efa575f838b28;hp=77101e72e1bdda16913bc4ba3ac4e34f49251667;hb=6f398a8b6eab8d0c61010c144f01d706dc0dad8b;hpb=46eb953ecab322d3385ccf84c75d969de8542317 diff --git a/yaffs_yaffs2.c b/yaffs_yaffs2.c index 77101e7..8c31a66 100644 --- a/yaffs_yaffs2.c +++ b/yaffs_yaffs2.c @@ -1,7 +1,7 @@ /* * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. * - * Copyright (C) 2002-2010 Aleph One Ltd. + * Copyright (C) 2002-2011 Aleph One Ltd. * for Toby Churchill Ltd and Brightstar Engineering * * Created by Charles Manning @@ -20,6 +20,7 @@ #include "yaffs_getblockinfo.h" #include "yaffs_verify.h" #include "yaffs_attribs.h" +#include "yaffs_summary.h" /* * Checkpoints are really no benefit on very small partitions. @@ -54,7 +55,8 @@ void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { if (b->block_state == YAFFS_BLOCK_STATE_FULL && (b->pages_in_use - b->soft_del_pages) < - dev->param.chunks_per_block && b->seq_number < seq) { + dev->param.chunks_per_block && + b->seq_number < seq) { seq = b->seq_number; block_no = i; } @@ -206,17 +208,16 @@ int yaffs2_checkpt_required(struct yaffs_dev *dev) int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) { int retval; + int n_bytes = 0; + int n_blocks; + int dev_blocks; if (!dev->param.is_yaffs2) return 0; if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { /* Not a valid value so recalculate */ - int n_bytes = 0; - int n_blocks; - int dev_blocks = - (dev->param.end_block - dev->param.start_block + 1); - + dev_blocks = dev->param.end_block - dev->param.start_block + 1; n_bytes += sizeof(struct yaffs_checkpt_validity); n_bytes += sizeof(struct yaffs_checkpt_dev); n_bytes += dev_blocks * sizeof(struct yaffs_block_info); @@ -309,8 +310,7 @@ static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) { struct yaffs_checkpt_dev cp; u32 n_bytes; - u32 n_blocks = - (dev->internal_end_block - dev->internal_start_block + 1); + u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; int ok; /* Write device runtime values */ @@ -318,20 +318,19 @@ static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) cp.struct_type = sizeof(cp); ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (!ok) + return 0; /* Write block info */ - if (ok) { - n_bytes = n_blocks * sizeof(struct yaffs_block_info); - ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == - n_bytes); - } + n_bytes = n_blocks * sizeof(struct yaffs_block_info); + ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); + if (!ok) + return 0; /* Write chunk bits */ - if (ok) { - n_bytes = n_blocks * dev->chunk_bit_stride; - ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == - n_bytes); - } + n_bytes = n_blocks * dev->chunk_bit_stride; + ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); + return ok ? 1 : 0; } @@ -358,6 +357,7 @@ static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) if (!ok) return 0; + n_bytes = n_blocks * dev->chunk_bit_stride; ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); @@ -387,7 +387,7 @@ static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; } -static int taffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, +static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, struct yaffs_checkpt_obj *cp) { struct yaffs_obj *parent; @@ -449,33 +449,32 @@ static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, int i; struct yaffs_dev *dev = in->my_dev; int ok = 1; - - if (tn) { - if (level > 0) { - - for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { - if (tn->internal[i]) { - ok = yaffs2_checkpt_tnode_worker(in, - tn->internal[i], - level - 1, - (chunk_offset << - YAFFS_TNODES_INTERNAL_BITS) - + i); - } - } - } else if (level == 0) { - u32 base_offset = - chunk_offset << YAFFS_TNODES_LEVEL0_BITS; - ok = (yaffs2_checkpt_wr - (dev, &base_offset, - sizeof(base_offset)) == sizeof(base_offset)); - if (ok) - ok = (yaffs2_checkpt_wr - (dev, tn, - dev->tnode_size) == dev->tnode_size); + u32 base_offset; + + if (!tn) + return 1; + + if (level > 0) { + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { + if (!tn->internal[i]) + continue; + ok = yaffs2_checkpt_tnode_worker(in, + tn->internal[i], + level - 1, + (chunk_offset << + YAFFS_TNODES_INTERNAL_BITS) + i); } + return ok; } + /* Level 0 tnode */ + base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; + ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == + sizeof(base_offset)); + if (ok) + ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == + dev->tnode_size); + return ok; } @@ -484,16 +483,16 @@ static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) u32 end_marker = ~0; int ok = 1; - if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) { - ok = yaffs2_checkpt_tnode_worker(obj, - obj->variant.file_variant.top, - obj->variant.file_variant. - top_level, 0); - if (ok) - ok = (yaffs2_checkpt_wr - (obj->my_dev, &end_marker, - sizeof(end_marker)) == sizeof(end_marker)); - } + if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) + return ok; + + ok = yaffs2_checkpt_tnode_worker(obj, + obj->variant.file_variant.top, + obj->variant.file_variant. + top_level, 0); + if (ok) + ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, + sizeof(end_marker)) == sizeof(end_marker)); return ok ? 1 : 0; } @@ -575,7 +574,7 @@ static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) } /* Dump end of list */ - memset(&cp, 0xFF, sizeof(struct yaffs_checkpt_obj)); + memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); cp.struct_type = sizeof(cp); if (ok) @@ -590,7 +589,8 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) struct yaffs_checkpt_obj cp; int ok = 1; int done = 0; - struct yaffs_obj *hard_list = NULL; + LIST_HEAD(hard_list); + while (ok && !done) { ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); @@ -613,7 +613,7 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) yaffs_find_or_create_by_number(dev, cp.obj_id, cp.variant_type); if (obj) { - ok = taffs2_checkpt_obj_to_obj(obj, &cp); + ok = yaffs2_checkpt_obj_to_obj(obj, &cp); if (!ok) break; if (obj->variant_type == @@ -621,9 +621,7 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ok = yaffs2_rd_checkpt_tnodes(obj); } else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { - obj->hard_links.next = - (struct list_head *)hard_list; - hard_list = obj; + list_add(&obj->hard_links, &hard_list); } } else { ok = 0; @@ -632,7 +630,7 @@ static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) } if (ok) - yaffs_link_fixup(dev, hard_list); + yaffs_link_fixup(dev, &hard_list); return ok ? 1 : 0; } @@ -838,7 +836,7 @@ int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) * of hole marker. */ loff_t old_file_size; - int increase; + loff_t increase; int small_hole; int result = YAFFS_OK; struct yaffs_dev *dev = NULL; @@ -871,11 +869,11 @@ int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) small_hole = 0; if (small_hole) - local_buffer = yaffs_get_temp_buffer(dev, __LINE__); + local_buffer = yaffs_get_temp_buffer(dev); if (local_buffer) { /* fill hole with zero bytes */ - int pos = old_file_size; + loff_t pos = old_file_size; int this_write; int written; memset(local_buffer, 0, dev->data_bytes_per_chunk); @@ -896,7 +894,7 @@ int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) } } - yaffs_release_temp_buffer(dev, local_buffer, __LINE__); + yaffs_release_temp_buffer(dev, local_buffer); /* If out of space then reverse any chunks we've added */ if (!small_increase_ok) @@ -928,8 +926,8 @@ static int yaffs2_ybicmp(const void *a, const void *b) if (aseq == bseq) return ablock - bblock; - else - return aseq - bseq; + + return aseq - bseq; } static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, @@ -937,13 +935,14 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, int blk, int chunk_in_block, int *found_chunks, u8 *chunk_data, - struct yaffs_obj **hard_list) + struct list_head *hard_list, + int summary_available) { struct yaffs_obj_hdr *oh; struct yaffs_obj *in; struct yaffs_obj *parent; int equiv_id; - int file_size; + loff_t file_size; int is_shrink; int is_unlinked; struct yaffs_ext_tags tags; @@ -954,7 +953,17 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, struct yaffs_hardlink_var *hl_var; struct yaffs_symlink_var *sl_var; - result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); + if (summary_available) { + result = yaffs_summary_fetch(dev, &tags, chunk_in_block); + tags.seq_number = bi->seq_number; + } + + if (!summary_available || tags.obj_id == 0) { + result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); + dev->tags_used++; + } else { + dev->summary_used++; + } /* Let's have a good look at this chunk... */ @@ -979,8 +988,8 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, bi->block_state = YAFFS_BLOCK_STATE_EMPTY; dev->n_erased_blocks++; } else { - if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCANNING - || bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { + if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { if (dev->seq_number == bi->seq_number) { /* Allocating from this block*/ yaffs_trace(YAFFS_TRACE_SCAN, @@ -1014,6 +1023,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, dev->n_free_chunks++; } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || tags.chunk_id > YAFFS_MAX_CHUNK_ID || + tags.obj_id == YAFFS_OBJECTID_SUMMARY || (tags.chunk_id > 0 && tags.n_bytes > dev->data_bytes_per_chunk) || tags.seq_number != bi->seq_number) { @@ -1024,8 +1034,8 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, dev->n_free_chunks++; } else if (tags.chunk_id > 0) { /* chunk_id > 0 so it is a data chunk... */ - unsigned int endpos; - u32 chunk_base = (tags.chunk_id - 1) * + loff_t endpos; + loff_t chunk_base = (tags.chunk_id - 1) * dev->data_bytes_per_chunk; *found_chunks = 1; @@ -1092,12 +1102,10 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, } if (!in || - (!in->valid && dev->param.disable_lazy_load) - || tags.extra_shadows || - (!in->valid && (tags.obj_id == - YAFFS_OBJECTID_ROOT - || tags.obj_id == - YAFFS_OBJECTID_LOSTNFOUND))) { + (!in->valid && dev->param.disable_lazy_load) || + tags.extra_shadows || + (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || + tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { /* If we don't have valid info then we * need to read the chunk @@ -1149,9 +1157,9 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, (tags.extra_available && tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) )) { - u32 this_size = (oh) ? - oh->file_size : - tags.extra_length; + loff_t this_size = (oh) ? + yaffs_oh_to_size(oh) : + tags.extra_file_size; u32 parent_obj_id = (oh) ? oh->parent_obj_id : tags.extra_parent_id; @@ -1185,12 +1193,14 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, } if (!in->valid && in->variant_type != - (oh ? oh->type : tags.extra_obj_type)) + (oh ? oh->type : tags.extra_obj_type)) { yaffs_trace(YAFFS_TRACE_ERROR, - "yaffs tragedy: Bad object type, %d != %d, for object %d at chunk %d during scan", + "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", oh ? oh->type : tags.extra_obj_type, in->variant_type, tags.obj_id, chunk); + in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); + } if (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || @@ -1225,7 +1235,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, parent = yaffs_find_or_create_by_number(dev, oh->parent_obj_id, YAFFS_OBJECT_TYPE_DIRECTORY); - file_size = oh->file_size; + file_size = yaffs_oh_to_size(oh); is_shrink = oh->is_shrink; equiv_id = oh->equiv_id; } else { @@ -1233,7 +1243,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, parent = yaffs_find_or_create_by_number(dev, tags.extra_parent_id, YAFFS_OBJECT_TYPE_DIRECTORY); - file_size = tags.extra_length; + file_size = tags.extra_file_size; is_shrink = tags.extra_is_shrink; equiv_id = tags.extra_equiv_id; in->lazy_loaded = 1; @@ -1308,9 +1318,7 @@ static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, hl_var = &in->variant.hardlink_variant; if (!is_unlinked) { hl_var->equiv_id = equiv_id; - in->hard_links.next = - (struct list_head *) *hard_list; - *hard_list = in; + list_add(&in->hard_links, hard_list); } break; case YAFFS_OBJECT_TYPE_DIRECTORY: @@ -1344,7 +1352,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) enum yaffs_block_state state; int c; int deleted; - struct yaffs_obj *hard_list = NULL; + LIST_HEAD(hard_list); struct yaffs_block_info *bi; u32 seq_number; int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; @@ -1353,6 +1361,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) int alloc_failed = 0; struct yaffs_block_index *block_index = NULL; int alt_block_index = 0; + int summary_available; yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", @@ -1360,8 +1369,8 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; - block_index = kmalloc(n_blocks * sizeof(struct yaffs_block_index), - GFP_NOFS); + block_index = + kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); if (!block_index) { block_index = @@ -1378,7 +1387,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) dev->blocks_in_checkpt = 0; - chunk_data = yaffs_get_temp_buffer(dev, __LINE__); + chunk_data = yaffs_get_temp_buffer(dev); /* Scan all the blocks to determine their state */ bi = dev->block_info; @@ -1413,7 +1422,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) dev->n_erased_blocks++; dev->n_free_chunks += dev->param.chunks_per_block; } else if (bi->block_state == - YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + YAFFS_BLOCK_STATE_NEEDS_SCAN) { /* Determine the highest sequence number */ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { @@ -1432,7 +1441,7 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) bi++; } - yaffs_trace(YAFFS_TRACE_SCAN, "%d blocks to be sorted...", n_to_scan); + yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); cond_resched(); @@ -1450,7 +1459,8 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); /* For each block.... backwards */ - for (block_iter = end_iter; !alloc_failed && block_iter >= start_iter; + for (block_iter = end_iter; + !alloc_failed && block_iter >= start_iter; block_iter--) { /* Cooperative multitasking! This loop can run for so long that watchdog timers expire. */ @@ -1461,23 +1471,31 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) bi = yaffs_get_block_info(dev, blk); deleted = 0; + summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); + /* For each chunk in each block that needs scanning.... */ found_chunks = 0; - for (c = dev->param.chunks_per_block - 1; + if (summary_available) + c = dev->chunks_per_summary - 1; + else + c = dev->param.chunks_per_block - 1; + + for (/* c is already initialised */; !alloc_failed && c >= 0 && - (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || - bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { + (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); + c--) { /* Scan backwards... * Read the tags and decide what to do */ if (yaffs2_scan_chunk(dev, bi, blk, c, &found_chunks, chunk_data, - &hard_list) == - YAFFS_FAIL) + &hard_list, summary_available) == + YAFFS_FAIL) alloc_failed = 1; } - if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { /* If we got this far while scanning, then the block * is fully allocated. */ bi->block_state = YAFFS_BLOCK_STATE_FULL; @@ -1503,9 +1521,9 @@ int yaffs2_scan_backwards(struct yaffs_dev *dev) * We have scanned all the objects, now it's time to add these * hardlinks. */ - yaffs_link_fixup(dev, hard_list); + yaffs_link_fixup(dev, &hard_list); - yaffs_release_temp_buffer(dev, chunk_data, __LINE__); + yaffs_release_temp_buffer(dev, chunk_data); if (alloc_failed) return YAFFS_FAIL;