123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- 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
|