You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
6.1 KiB
239 lines
6.1 KiB
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
|
|
|