Subject: yaffs: fix compat tags handling Signed-off-by: Gabor Juhos --- --- a/fs/yaffs2/yaffs_tagscompat.c +++ b/fs/yaffs2/yaffs_tagscompat.c @@ -17,7 +17,9 @@ #include "yaffs_getblockinfo.h" #include "yaffs_trace.h" +#if 0 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); +#endif /********** Tags ECC calculations *********/ @@ -71,6 +73,7 @@ int yaffs_check_tags_ecc(struct yaffs_ta return 0; } +#if 0 /********** Tags **********/ static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, @@ -379,3 +382,214 @@ void yaffs_tags_compat_install(struct ya if(!dev->tagger.mark_bad_fn) dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; } +#else + +#include "yaffs_packedtags1.h" + +static int yaffs_tags_compat_write(struct yaffs_dev *dev, + int nand_chunk, + const u8 *data, + const struct yaffs_ext_tags *tags) +{ + struct yaffs_packed_tags1 pt1; + u8 tag_buf[9]; + int retval; + + /* we assume that yaffs_packed_tags1 and yaffs_tags are compatible */ + compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12); + compile_time_assertion(sizeof(struct yaffs_tags) == 8); + + yaffs_pack_tags1(&pt1, tags); + yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1); + + /* When deleting a chunk, the upper layer provides only skeletal + * tags, one with is_deleted set. However, we need to update the + * tags, not erase them completely. So we use the NAND write property + * that only zeroed-bits stick and set tag bytes to all-ones and + * zero just the (not) deleted bit. + */ + if (!dev->param.tags_9bytes) { + if (tags->is_deleted) { + memset(&pt1, 0xff, 8); + /* clear delete status bit to indicate deleted */ + pt1.deleted = 0; + } + memcpy(tag_buf, &pt1, 8); + } else { + if (tags->is_deleted) { + memset(tag_buf, 0xff, 8); + tag_buf[8] = 0; + } else { + memcpy(tag_buf, &pt1, 8); + tag_buf[8] = 0xff; + } + } + + retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, + data, + (data) ? dev->data_bytes_per_chunk : 0, + tag_buf, + (dev->param.tags_9bytes) ? 9 : 8); + + return retval; +} + +/* Return with empty extended tags but add ecc_result. + */ +static int return_empty_tags(struct yaffs_ext_tags *tags, + enum yaffs_ecc_result ecc_result, + int retval) +{ + if (tags) { + memset(tags, 0, sizeof(*tags)); + tags->ecc_result = ecc_result; + } + + return retval; +} + +static int yaffs_tags_compat_read(struct yaffs_dev *dev, + int nand_chunk, + u8 *data, + struct yaffs_ext_tags *tags) +{ + struct yaffs_packed_tags1 pt1; + enum yaffs_ecc_result ecc_result; + int retval; + int deleted; + u8 tag_buf[9]; + + retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, + data, dev->param.total_bytes_per_chunk, + tag_buf, + (dev->param.tags_9bytes) ? 9 : 8, + &ecc_result); + + switch (ecc_result) { + case YAFFS_ECC_RESULT_NO_ERROR: + case YAFFS_ECC_RESULT_FIXED: + break; + + case YAFFS_ECC_RESULT_UNFIXED: + default: + return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, 0); + tags->block_bad = dev->drv.drv_check_bad_fn(dev, nand_chunk); + return YAFFS_FAIL; + } + + /* Check for a blank/erased chunk. */ + if (yaffs_check_ff(tag_buf, 8)) { + /* when blank, upper layers want ecc_result to be <= NO_ERROR */ + return return_empty_tags(tags, YAFFS_ECC_RESULT_NO_ERROR, + YAFFS_OK); + } + + memcpy(&pt1, tag_buf, 8); + + if (!dev->param.tags_9bytes) { + /* Read deleted status (bit) then return it to it's non-deleted + * state before performing tags mini-ECC check. pt1.deleted is + * inverted. + */ + deleted = !pt1.deleted; + pt1.deleted = 1; + } else { + deleted = (hweight8(tag_buf[8]) < 7) ? 1 : 0; + } + + /* Check the packed tags mini-ECC and correct if necessary/possible. */ + retval = yaffs_check_tags_ecc((struct yaffs_tags *)&pt1); + switch (retval) { + case 0: + /* no tags error, use MTD result */ + break; + case 1: + /* recovered tags-ECC error */ + dev->n_tags_ecc_fixed++; + if (ecc_result == YAFFS_ECC_RESULT_NO_ERROR) + ecc_result = YAFFS_ECC_RESULT_FIXED; + break; + default: + /* unrecovered tags-ECC error */ + dev->n_tags_ecc_unfixed++; + return return_empty_tags(tags, YAFFS_ECC_RESULT_UNFIXED, + YAFFS_FAIL); + } + + /* Unpack the tags to extended form and set ECC result. + * [set should_be_ff just to keep yaffs_unpack_tags1 happy] + */ + pt1.should_be_ff = 0xffffffff; + yaffs_unpack_tags1(tags, &pt1); + tags->ecc_result = ecc_result; + + /* Set deleted state */ + tags->is_deleted = deleted; + return YAFFS_OK; +} + +static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no) +{ + return dev->drv.drv_mark_bad_fn(dev, block_no); +} + +static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + u32 *seq_number) +{ + struct yaffs_ext_tags tags; + int retval; + + yaffs_trace(YAFFS_TRACE_MTD, "%s %d", __func__, block_no); + + *seq_number = 0; + + retval = dev->drv.drv_check_bad_fn(dev, block_no); + if (retval == YAFFS_FAIL) { + *state = YAFFS_BLOCK_STATE_DEAD; + goto out; + } + + yaffs_tags_compat_read(dev, block_no * dev->param.chunks_per_block, + NULL, &tags); + + if (tags.ecc_result != YAFFS_ECC_RESULT_NO_ERROR) { + yaffs_trace(YAFFS_TRACE_MTD, "block %d is marked bad", + block_no); + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; + } else if (tags.chunk_used) { + *seq_number = tags.seq_number; + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; + } else { + *state = YAFFS_BLOCK_STATE_EMPTY; + } + + retval = YAFFS_OK; + +out: + yaffs_trace(YAFFS_TRACE_MTD, + "block query returns seq %u state %d", + *seq_number, *state); + + return retval; +} + +void yaffs_tags_compat_install(struct yaffs_dev *dev) +{ + if (dev->param.is_yaffs2) + return; + + if (!dev->tagger.write_chunk_tags_fn) + dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_write; + + if (!dev->tagger.read_chunk_tags_fn) + dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_read; + + if (!dev->tagger.query_block_fn) + dev->tagger.query_block_fn = yaffs_tags_compat_query_block; + + if (!dev->tagger.mark_bad_fn) + dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; +} +#endif