From 51f439d8acfb1ad17a474538c1d2d958f2dd9f50 Mon Sep 17 00:00:00 2001 From: juhosg Date: Tue, 28 Jan 2014 17:30:25 +0000 Subject: [PATCH] kernel/3.1x: yaffs: fix handling of small-page NAND devices Since the yaffs code update (r39084), it is not possible to install OpenWrt on RouterBoards with small-page NAND chips. Fix the yaffs code to make it work again. Signed-off-by: Gabor Juhos git-svn-id: svn://svn.openwrt.org/openwrt/trunk@39409 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- ...3-yaffs-add-tags-9bytes-mount-option.patch | 115 +++++++++ .../504-yaffs-fix-compat-tags-handling.patch | 239 ++++++++++++++++++ ...3-yaffs-add-tags-9bytes-mount-option.patch | 115 +++++++++ .../504-yaffs-fix-compat-tags-handling.patch | 239 ++++++++++++++++++ ...3-yaffs-add-tags-9bytes-mount-option.patch | 115 +++++++++ .../504-yaffs-fix-compat-tags-handling.patch | 239 ++++++++++++++++++ 6 files changed, 1062 insertions(+) create mode 100644 target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch create mode 100644 target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch create mode 100644 target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch create mode 100644 target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch create mode 100644 target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch create mode 100644 target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch diff --git a/target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch b/target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch new file mode 100644 index 0000000000..904ad7ec46 --- /dev/null +++ b/target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch @@ -0,0 +1,115 @@ +Subject: yaffs: add support for tags-9bytes mount option + +Signed-off-by: Gabor Juhos +--- +--- a/fs/yaffs2/yaffs_vfs.c ++++ b/fs/yaffs2/yaffs_vfs.c +@@ -2525,6 +2525,7 @@ static const struct super_operations yaf + + struct yaffs_options { + int inband_tags; ++ int tags_9bytes; + int skip_checkpoint_read; + int skip_checkpoint_write; + int no_cache; +@@ -2564,6 +2565,8 @@ static int yaffs_parse_options(struct ya + + if (!strcmp(cur_opt, "inband-tags")) { + options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-9bytes")) { ++ options->tags_9bytes = 1; + } else if (!strcmp(cur_opt, "tags-ecc-off")) { + options->tags_ecc_on = 0; + options->tags_ecc_overridden = 1; +@@ -2637,7 +2640,6 @@ static struct super_block *yaffs_interna + struct yaffs_param *param; + + int read_only = 0; +- int inband_tags = 0; + + struct yaffs_options options; + +@@ -2677,6 +2679,9 @@ static struct super_block *yaffs_interna + + memset(&options, 0, sizeof(options)); + ++ if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS)) ++ options.tags_9bytes = 1; ++ + if (yaffs_parse_options(&options, data_str)) { + /* Option parsing failed */ + return NULL; +@@ -2710,17 +2715,22 @@ static struct super_block *yaffs_interna + } + + /* Added NCB 26/5/2006 for completeness */ +- if (yaffs_version == 2 && !options.inband_tags +- && WRITE_SIZE(mtd) == 512) { ++ if (yaffs_version == 2 && ++ (!options.inband_tags || options.tags_9bytes) && ++ WRITE_SIZE(mtd) == 512) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); + yaffs_version = 1; + } + +- if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || +- options.inband_tags) +- inband_tags = 1; ++ if (yaffs_version == 2 && ++ mtd->oobavail < sizeof(struct yaffs_packed_tags2)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags"); ++ options.inband_tags = 1; ++ } + +- if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags, ++ options.tags_9bytes); ++ if (err < 0) + return NULL; + + /* OK, so if we got here, we have an MTD that's NAND and looks +@@ -2781,7 +2791,8 @@ static struct super_block *yaffs_interna + + param->n_reserved_blocks = 5; + param->n_caches = (options.no_cache) ? 0 : 10; +- param->inband_tags = inband_tags; ++ param->inband_tags = options.inband_tags; ++ param->tags_9bytes = options.tags_9bytes; + + param->enable_xattr = 1; + if (options.lazy_loading_overridden) +--- a/fs/yaffs2/yaffs_mtdif.c ++++ b/fs/yaffs2/yaffs_mtdif.c +@@ -276,7 +276,8 @@ struct mtd_info * yaffs_get_mtd_device(d + return mtd; + } + +-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags, ++ int tags_9bytes) + { + if (yaffs_version == 2) { + if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +@@ -295,6 +296,12 @@ int yaffs_verify_mtd(struct mtd_info *mt + ); + return -1; + } ++ ++ if (tags_9bytes && mtd->oobavail < 9) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support 9-byte tags"); ++ return -1; ++ } + } + + return 0; +--- a/fs/yaffs2/yaffs_mtdif.h ++++ b/fs/yaffs2/yaffs_mtdif.h +@@ -21,5 +21,6 @@ + void yaffs_mtd_drv_install(struct yaffs_dev *dev); + struct mtd_info * yaffs_get_mtd_device(dev_t sdev); + void yaffs_put_mtd_device(struct mtd_info *mtd); +-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags, ++ int tags_9bytes); + #endif diff --git a/target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch b/target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch new file mode 100644 index 0000000000..a18cf6fd7b --- /dev/null +++ b/target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch @@ -0,0 +1,239 @@ +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 diff --git a/target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch b/target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch new file mode 100644 index 0000000000..5a15b516b5 --- /dev/null +++ b/target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch @@ -0,0 +1,115 @@ +Subject: yaffs: add support for tags-9bytes mount option + +Signed-off-by: Gabor Juhos +--- +--- a/fs/yaffs2/yaffs_vfs.c ++++ b/fs/yaffs2/yaffs_vfs.c +@@ -2634,6 +2634,7 @@ static const struct super_operations yaf + + struct yaffs_options { + int inband_tags; ++ int tags_9bytes; + int skip_checkpoint_read; + int skip_checkpoint_write; + int no_cache; +@@ -2673,6 +2674,8 @@ static int yaffs_parse_options(struct ya + + if (!strcmp(cur_opt, "inband-tags")) { + options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-9bytes")) { ++ options->tags_9bytes = 1; + } else if (!strcmp(cur_opt, "tags-ecc-off")) { + options->tags_ecc_on = 0; + options->tags_ecc_overridden = 1; +@@ -2746,7 +2749,6 @@ static struct super_block *yaffs_interna + struct yaffs_param *param; + + int read_only = 0; +- int inband_tags = 0; + + struct yaffs_options options; + +@@ -2786,6 +2788,9 @@ static struct super_block *yaffs_interna + + memset(&options, 0, sizeof(options)); + ++ if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS)) ++ options.tags_9bytes = 1; ++ + if (yaffs_parse_options(&options, data_str)) { + /* Option parsing failed */ + return NULL; +@@ -2819,17 +2824,22 @@ static struct super_block *yaffs_interna + } + + /* Added NCB 26/5/2006 for completeness */ +- if (yaffs_version == 2 && !options.inband_tags +- && WRITE_SIZE(mtd) == 512) { ++ if (yaffs_version == 2 && ++ (!options.inband_tags || options.tags_9bytes) && ++ WRITE_SIZE(mtd) == 512) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); + yaffs_version = 1; + } + +- if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || +- options.inband_tags) +- inband_tags = 1; ++ if (yaffs_version == 2 && ++ mtd->oobavail < sizeof(struct yaffs_packed_tags2)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags"); ++ options.inband_tags = 1; ++ } + +- if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags, ++ options.tags_9bytes); ++ if (err < 0) + return NULL; + + /* OK, so if we got here, we have an MTD that's NAND and looks +@@ -2890,7 +2900,8 @@ static struct super_block *yaffs_interna + + param->n_reserved_blocks = 5; + param->n_caches = (options.no_cache) ? 0 : 10; +- param->inband_tags = inband_tags; ++ param->inband_tags = options.inband_tags; ++ param->tags_9bytes = options.tags_9bytes; + + param->enable_xattr = 1; + if (options.lazy_loading_overridden) +--- a/fs/yaffs2/yaffs_mtdif.c ++++ b/fs/yaffs2/yaffs_mtdif.c +@@ -276,7 +276,8 @@ struct mtd_info * yaffs_get_mtd_device(d + return mtd; + } + +-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags, ++ int tags_9bytes) + { + if (yaffs_version == 2) { + if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +@@ -295,6 +296,12 @@ int yaffs_verify_mtd(struct mtd_info *mt + ); + return -1; + } ++ ++ if (tags_9bytes && mtd->oobavail < 9) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support 9-byte tags"); ++ return -1; ++ } + } + + return 0; +--- a/fs/yaffs2/yaffs_mtdif.h ++++ b/fs/yaffs2/yaffs_mtdif.h +@@ -21,5 +21,6 @@ + void yaffs_mtd_drv_install(struct yaffs_dev *dev); + struct mtd_info * yaffs_get_mtd_device(dev_t sdev); + void yaffs_put_mtd_device(struct mtd_info *mtd); +-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags, ++ int tags_9bytes); + #endif diff --git a/target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch b/target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch new file mode 100644 index 0000000000..a18cf6fd7b --- /dev/null +++ b/target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch @@ -0,0 +1,239 @@ +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 diff --git a/target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch b/target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch new file mode 100644 index 0000000000..5a15b516b5 --- /dev/null +++ b/target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch @@ -0,0 +1,115 @@ +Subject: yaffs: add support for tags-9bytes mount option + +Signed-off-by: Gabor Juhos +--- +--- a/fs/yaffs2/yaffs_vfs.c ++++ b/fs/yaffs2/yaffs_vfs.c +@@ -2634,6 +2634,7 @@ static const struct super_operations yaf + + struct yaffs_options { + int inband_tags; ++ int tags_9bytes; + int skip_checkpoint_read; + int skip_checkpoint_write; + int no_cache; +@@ -2673,6 +2674,8 @@ static int yaffs_parse_options(struct ya + + if (!strcmp(cur_opt, "inband-tags")) { + options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-9bytes")) { ++ options->tags_9bytes = 1; + } else if (!strcmp(cur_opt, "tags-ecc-off")) { + options->tags_ecc_on = 0; + options->tags_ecc_overridden = 1; +@@ -2746,7 +2749,6 @@ static struct super_block *yaffs_interna + struct yaffs_param *param; + + int read_only = 0; +- int inband_tags = 0; + + struct yaffs_options options; + +@@ -2786,6 +2788,9 @@ static struct super_block *yaffs_interna + + memset(&options, 0, sizeof(options)); + ++ if (IS_ENABLED(CONFIG_YAFFS_9BYTE_TAGS)) ++ options.tags_9bytes = 1; ++ + if (yaffs_parse_options(&options, data_str)) { + /* Option parsing failed */ + return NULL; +@@ -2819,17 +2824,22 @@ static struct super_block *yaffs_interna + } + + /* Added NCB 26/5/2006 for completeness */ +- if (yaffs_version == 2 && !options.inband_tags +- && WRITE_SIZE(mtd) == 512) { ++ if (yaffs_version == 2 && ++ (!options.inband_tags || options.tags_9bytes) && ++ WRITE_SIZE(mtd) == 512) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); + yaffs_version = 1; + } + +- if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || +- options.inband_tags) +- inband_tags = 1; ++ if (yaffs_version == 2 && ++ mtd->oobavail < sizeof(struct yaffs_packed_tags2)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting inband tags"); ++ options.inband_tags = 1; ++ } + +- if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ err = yaffs_verify_mtd(mtd, yaffs_version, options.inband_tags, ++ options.tags_9bytes); ++ if (err < 0) + return NULL; + + /* OK, so if we got here, we have an MTD that's NAND and looks +@@ -2890,7 +2900,8 @@ static struct super_block *yaffs_interna + + param->n_reserved_blocks = 5; + param->n_caches = (options.no_cache) ? 0 : 10; +- param->inband_tags = inband_tags; ++ param->inband_tags = options.inband_tags; ++ param->tags_9bytes = options.tags_9bytes; + + param->enable_xattr = 1; + if (options.lazy_loading_overridden) +--- a/fs/yaffs2/yaffs_mtdif.c ++++ b/fs/yaffs2/yaffs_mtdif.c +@@ -276,7 +276,8 @@ struct mtd_info * yaffs_get_mtd_device(d + return mtd; + } + +-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags, ++ int tags_9bytes) + { + if (yaffs_version == 2) { + if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +@@ -295,6 +296,12 @@ int yaffs_verify_mtd(struct mtd_info *mt + ); + return -1; + } ++ ++ if (tags_9bytes && mtd->oobavail < 9) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support 9-byte tags"); ++ return -1; ++ } + } + + return 0; +--- a/fs/yaffs2/yaffs_mtdif.h ++++ b/fs/yaffs2/yaffs_mtdif.h +@@ -21,5 +21,6 @@ + void yaffs_mtd_drv_install(struct yaffs_dev *dev); + struct mtd_info * yaffs_get_mtd_device(dev_t sdev); + void yaffs_put_mtd_device(struct mtd_info *mtd); +-int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags, ++ int tags_9bytes); + #endif diff --git a/target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch b/target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch new file mode 100644 index 0000000000..a18cf6fd7b --- /dev/null +++ b/target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch @@ -0,0 +1,239 @@ +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 -- 2.30.2