kernel/3.1x: yaffs: fix handling of small-page NAND devices
authorGabor Juhos <juhosg@openwrt.org>
Tue, 28 Jan 2014 17:30:25 +0000 (17:30 +0000)
committerGabor Juhos <juhosg@openwrt.org>
Tue, 28 Jan 2014 17:30:25 +0000 (17:30 +0000)
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 <juhosg@openwrt.org>
SVN-Revision: 39409

target/linux/generic/patches-3.10/503-yaffs-add-tags-9bytes-mount-option.patch [new file with mode: 0644]
target/linux/generic/patches-3.10/504-yaffs-fix-compat-tags-handling.patch [new file with mode: 0644]
target/linux/generic/patches-3.12/503-yaffs-add-tags-9bytes-mount-option.patch [new file with mode: 0644]
target/linux/generic/patches-3.12/504-yaffs-fix-compat-tags-handling.patch [new file with mode: 0644]
target/linux/generic/patches-3.13/503-yaffs-add-tags-9bytes-mount-option.patch [new file with mode: 0644]
target/linux/generic/patches-3.13/504-yaffs-fix-compat-tags-handling.patch [new file with mode: 0644]

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 (file)
index 0000000..904ad7e
--- /dev/null
@@ -0,0 +1,115 @@
+Subject: yaffs: add support for tags-9bytes mount option
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- 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 (file)
index 0000000..a18cf6f
--- /dev/null
@@ -0,0 +1,239 @@
+Subject: yaffs: fix compat tags handling
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- 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 (file)
index 0000000..5a15b51
--- /dev/null
@@ -0,0 +1,115 @@
+Subject: yaffs: add support for tags-9bytes mount option
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- 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 (file)
index 0000000..a18cf6f
--- /dev/null
@@ -0,0 +1,239 @@
+Subject: yaffs: fix compat tags handling
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- 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 (file)
index 0000000..5a15b51
--- /dev/null
@@ -0,0 +1,115 @@
+Subject: yaffs: add support for tags-9bytes mount option
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- 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 (file)
index 0000000..a18cf6f
--- /dev/null
@@ -0,0 +1,239 @@
+Subject: yaffs: fix compat tags handling
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- 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