[PATCH] [JFFS2] Make NAND OOB usage more flexible
Vitaly Wool
vwool at ru.mvista.com
Thu Jan 5 07:31:07 EST 2006
Oh my goodness... Maybe just apply my patch for OOB handling and all
that stuff will go away?
Vitaly
Juha Yrjölä wrote:
>Many (if not all) OneNAND devices have the free OOB bytes scattered
>around the whole OOB area in blocks of 2 or 3 bytes. To work around
>this, the JFFS2 wbuf code needs to consider _all_ the free OOB bytes
>specified by the oobfree array.
>
>Cheers,
>Juha
>
>
>
>------------------------------------------------------------------------
>
>Index: fs/jffs2/wbuf.c
>===================================================================
>RCS file: /home/cvs/mtd/fs/jffs2/wbuf.c,v
>retrieving revision 1.108
>diff -u -r1.108 wbuf.c
>--- fs/jffs2/wbuf.c 18 Nov 2005 07:27:45 -0000 1.108
>+++ fs/jffs2/wbuf.c 2 Jan 2006 20:52:49 -0000
>@@ -6,6 +6,7 @@
> *
> * Created by David Woodhouse <dwmw2 at infradead.org>
> * Modified debugged and enhanced by Thomas Gleixner <tglx at linutronix.de>
>+ * More flexible OOB usage by Juha Yrjölä <juha.yrjola at nokia.com>
> *
> * For licensing information, see the file 'LICENCE' in this directory.
> *
>@@ -948,31 +949,51 @@
> return 0;
> }
>
>+static int is_within_clean_marker_area(struct jffs2_sb_info *c, int pos)
>+{
>+ struct jffs2_nand_oob_info *oinfo;
>+ int i;
>+
>+ oinfo = c->fsdata;
>+ for (i = 0; i < c->fsdata_count; i++) {
>+ /* Only check entries for the first page */
>+ if (oinfo[i].page_nr > 0)
>+ break;
>+ if (pos < oinfo[i].pos)
>+ continue;
>+ if (pos >= oinfo[i].pos + oinfo[i].len)
>+ continue;
>+ return 1;
>+ }
>+ return 0;
>+}
>+
> /*
> * Check, if the out of band area is empty
> */
>-
>-int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t data_len)
>+int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t data_len)
> {
> size_t offset, retlen;
>- uint32_t i = 0, j, oob_nr;
> unsigned char *buf;
>- int oob_size, ret;
>+ int oob_size, oob_nr, ret, i;
>
> offset = jeb->offset;
> oob_size = c->mtd->oobsize;
>- oob_nr = (data_len+c->fsdata_len-1)/c->fsdata_len;
>- if (oob_nr < 4) oob_nr = 4;
>+ oob_nr = c->fsdata_page_count;
>+ if (oob_nr < 4)
>+ oob_nr = 4;
> buf = kmalloc(oob_size * oob_nr, GFP_KERNEL);
> ret = c->mtd->read_oob(c->mtd, offset, oob_size * oob_nr, &retlen, buf);
>
>- for (i=0; i<oob_nr; i++) {
>- for (j=0; j<oob_size; j++) {
>- if (data_len && j>=c->fsdata_pos && j<c->fsdata_pos + c->fsdata_len) {
>+ for (i = 0; i < oob_nr; i++) {
>+ int j;
>+
>+ for (j = 0; j < oob_size; j++) {
>+ if (data_len && is_within_clean_marker_area(c, j)) {
> data_len--;
> continue;
> }
>- if (buf[i*oob_size+j] != 0xFF) {
>+ if (buf[i * oob_size + j] != 0xff) {
> ret = 1;
> goto out;
> }
>@@ -984,22 +1005,44 @@
> return ret;
> }
>
>+static void jffs2_copy_nand_fsdata(struct jffs2_sb_info *c, u8 *to, const u8 *from,
>+ int len)
>+{
>+ struct jffs2_nand_oob_info *oinfo;
>+
>+ oinfo = c->fsdata;
>+ while (len) {
>+ int cnt, oob_idx;
>+
>+ BUG_ON(oinfo - c->fsdata >= c->fsdata_count);
>+
>+ oob_idx = oinfo->page_nr * c->mtd->oobsize + oinfo->pos;
>+ cnt = oinfo->len;
>+ if (cnt > len)
>+ cnt = len;
>+ memcpy(to, from + oob_idx, len);
>+ to += cnt;
>+ len -= cnt;
>+ oinfo++;
>+ }
>+}
>+
> /*
> * Scan for a valid cleanmarker and for bad blocks
> * For virtual blocks (concatenated physical blocks) check the cleanmarker
> * only in the first page of the first physical block, but scan for bad blocks in all
> * physical blocks
> */
>-int jffs2_check_nand_cleanmarker_ebh (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *data_len)
>+int jffs2_check_nand_cleanmarker_ebh(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *data_len)
> {
> size_t offset, retlen;
> int oob_size;
> uint32_t oob_nr, total_len;
>- unsigned char *buf;
>+ unsigned char *buf, fsdata[sizeof(struct jffs2_raw_ebh)];
> int ret;
> struct jffs2_unknown_node *n;
>- struct jffs2_raw_ebh eh;
>- uint32_t read_in = 0, i = 0, copy_len, node_crc;
>+ struct jffs2_raw_ebh *eh;
>+ uint32_t node_crc;
>
> offset = jeb->offset;
> *data_len = 0;
>@@ -1010,25 +1053,26 @@
> }
>
> oob_size = c->mtd->oobsize;
>- oob_nr = (sizeof(struct jffs2_raw_ebh)+c->fsdata_len-1)/c->fsdata_len;
>+ oob_nr = c->fsdata_page_count;
> total_len = oob_size * oob_nr;
>
> buf = kmalloc(total_len, GFP_KERNEL);
>- if (!buf) {
>+ if (!buf)
> return -ENOMEM;
>- }
>+
> ret = c->mtd->read_oob(c->mtd, offset, total_len, &retlen, buf);
> if (ret) {
> D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker_ebh(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
> goto out;
> }
> if (retlen < total_len) {
>- D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker_ebh(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, total_len, jeb->offset));
>+ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker_ebh(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, total_len, jeb->offset));
> ret = -EIO;
> goto out;
> }
>
>- n = (struct jffs2_unknown_node *) &buf[c->fsdata_pos];
>+ jffs2_copy_nand_fsdata(c, fsdata, buf, sizeof(fsdata));
>+ n = (struct jffs2_unknown_node *) &fsdata;
> if (je16_to_cpu(n->magic) != JFFS2_MAGIC_BITMASK) {
> D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker_ebh(): Cleanmarker node not detected in block at %08x\n", jeb->offset));
> ret = 1;
>@@ -1043,28 +1087,21 @@
> ret = 1;
> }
> goto out;
>- }else if (je16_to_cpu(n->nodetype) == JFFS2_NODETYPE_ERASEBLOCK_HEADER) {
>- /* Read the scattered data(in buf[]) into struct jffs2_raw_ebh */
>- while (read_in < sizeof(struct jffs2_raw_ebh)) {
>- copy_len = min_t(uint32_t, c->fsdata_len, sizeof(struct jffs2_raw_ebh) - read_in);
>- memcpy((unsigned char *)&eh + read_in, &buf[oob_size*i + c->fsdata_pos], copy_len);
>- read_in += copy_len;
>- i++;
>- }
>-
>- node_crc = crc32(0, &eh, sizeof(struct jffs2_raw_ebh)-8);
>- if (node_crc != je32_to_cpu(eh.node_crc)) {
>+ } else if (je16_to_cpu(n->nodetype) == JFFS2_NODETYPE_ERASEBLOCK_HEADER) {
>+ eh = (struct jffs2_raw_ebh *) fsdata;
>+ node_crc = crc32(0, eh, sizeof(struct jffs2_raw_ebh) - 8);
>+ if (node_crc != je32_to_cpu(eh->node_crc)) {
> ret = 1;
> goto out;
> }
>
>- if ((JFFS2_EBH_INCOMPAT_FSET | eh.incompat_fset) != JFFS2_EBH_INCOMPAT_FSET) {
>- printk(KERN_NOTICE "The incompat_fset of fs image EBH %d execeed the incompat_fset \
>- of JFFS2 module %d. Reject to mount.\n", eh.incompat_fset, JFFS2_EBH_INCOMPAT_FSET);
>+ if ((JFFS2_EBH_INCOMPAT_FSET | eh->incompat_fset) != JFFS2_EBH_INCOMPAT_FSET) {
>+ printk(KERN_NOTICE "The incompat_fset of fs image EBH %d exceed the incompat_fset \
>+ of JFFS2 module %d. Reject to mount.\n", eh->incompat_fset, JFFS2_EBH_INCOMPAT_FSET);
> ret = -EINVAL;
> goto out;
> }
>- if ((JFFS2_EBH_ROCOMPAT_FSET | eh.rocompat_fset) != JFFS2_EBH_ROCOMPAT_FSET) {
>+ if ((JFFS2_EBH_ROCOMPAT_FSET | eh->rocompat_fset) != JFFS2_EBH_ROCOMPAT_FSET) {
> printk(KERN_NOTICE "Read-only compatible EBH feature found at offset 0x%08x\n ", jeb->offset);
> if (!(jffs2_is_readonly(c))) {
> ret = -EROFS;
>@@ -1073,9 +1110,8 @@
> }
>
> EBFLAGS_SET_EBH(jeb);
>- jeb->erase_count = je32_to_cpu(eh.erase_count);
>- record_erase_count(c, jeb);
>- *data_len = je32_to_cpu(eh.totlen);
>+ jeb->erase_count = je32_to_cpu(eh->erase_count);
>+ *data_len = je32_to_cpu(eh->totlen);
> ret = 0;
> }else {
> ret = 1;
>@@ -1087,9 +1123,10 @@
>
> int jffs2_write_nand_ebh(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
> {
>- uint32_t i = 0, written = 0, write_len = 0;
>+ uint32_t written = 0, write_len = 0;
> int ret;
> size_t retlen;
>+ struct jffs2_nand_oob_info *oinfo;
> struct jffs2_raw_ebh ebh = {
> .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
> .nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASEBLOCK_HEADER),
>@@ -1106,16 +1143,21 @@
> ebh.node_crc = cpu_to_je32(crc32(0, (unsigned char *)&ebh + sizeof(struct jffs2_unknown_node) + 4,
> sizeof(struct jffs2_raw_ebh) - sizeof(struct jffs2_unknown_node) - 4));
>
>+ oinfo = c->fsdata;
> while (written < sizeof(struct jffs2_raw_ebh)) {
>- write_len = min_t(uint32_t, c->fsdata_len, sizeof(struct jffs2_raw_ebh) - written);
>- ret = jffs2_flash_write_oob(c, jeb->offset + c->mtd->oobblock*i + c->fsdata_pos,
>- write_len, &retlen, (unsigned char *)&ebh + written);
>+ unsigned int ofs;
>+
>+ BUG_ON(oinfo - c->fsdata >= c->fsdata_count);
>+ write_len = min_t(uint32_t, oinfo->len, sizeof(struct jffs2_raw_ebh) - written);
>+ ofs = c->mtd->oobblock * oinfo->page_nr + oinfo->pos;
>+ ret = jffs2_flash_write_oob(c, ofs, write_len, &retlen,
>+ (unsigned char *)&ebh + written);
> if (ret || retlen != write_len) {
> D1(printk(KERN_WARNING "jffs2_write_nand_ebh(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
> return ret;
> }
> written += write_len;
>- i++;
>+ oinfo++;
> }
> return 0;
> }
>@@ -1149,18 +1191,11 @@
> return 1;
> }
>
>-#define NAND_JFFS2_OOB16_FSDALEN 8
>-
>-static struct nand_oobinfo jffs2_oobinfo_docecc = {
>- .useecc = MTD_NANDECC_PLACE,
>- .eccbytes = 6,
>- .eccpos = {0,1,2,3,4,5}
>-};
>-
>-
> static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
> {
> struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
>+ struct jffs2_nand_oob_info *jinfo;
>+ int i, free, need, oobb_count, page_count;
>
> /* Do this only, if we have an oob buffer */
> if (!c->mtd->oobsize)
>@@ -1171,46 +1206,54 @@
> c->ebh_size = 0;
>
> /* Should we use autoplacement ? */
>- if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
>- D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
>- /* Get the position of the free bytes */
>- if (!oinfo->oobfree[0][1]) {
>- printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
>- return -ENOSPC;
>- }
>- c->fsdata_pos = oinfo->oobfree[0][0];
>- c->fsdata_len = oinfo->oobfree[0][1];
>- } else {
>- /* This is just a legacy fallback and should go away soon */
>- switch(c->mtd->ecctype) {
>- case MTD_ECC_RS_DiskOnChip:
>- printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
>- c->oobinfo = &jffs2_oobinfo_docecc;
>- c->fsdata_pos = 6;
>- c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
>- c->badblock_pos = 15;
>- break;
>+ if (oinfo == NULL || oinfo->useecc != MTD_NANDECC_AUTOPLACE) {
>+ D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
>+ return -EINVAL;
>+ }
>
>- default:
>- D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
>- return -EINVAL;
>- }
>+ D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
>+
>+ /* Calculate the amount of free OOB bytes in a page. */
>+ free = 0;
>+ need = sizeof(struct jffs2_raw_ebh);
>+ for (i = 0; oinfo->oobfree[i][1] > 0 && free < need; i++)
>+ free += oinfo->oobfree[i][1];
>+ if (free == 0) {
>+ printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
>+ return -ENOSPC;
> }
>- return 0;
>-}
>+ oobb_count = i;
>
>-/* To check if the OOB area has enough space for eraseblock header */
>-static int jffs2_nand_check_oobspace_for_ebh(struct jffs2_sb_info *c)
>-{
>- uint32_t pages_per_eraseblock, available_oob_space;
>+ if (free < need)
>+ page_count = (need + free - 1) / free;
>+ else
>+ page_count = 1;
>
>- pages_per_eraseblock = c->sector_size/c->mtd->oobblock;
>- available_oob_space = c->fsdata_len * pages_per_eraseblock;
>- if (available_oob_space < sizeof(struct jffs2_raw_ebh)) {
>- printk(KERN_NOTICE "The OOB area(%d) is not big enough to hold eraseblock_header(%d), reject to mount.\n",
>- available_oob_space, sizeof(struct jffs2_raw_ebh));
>+ /* Check if the OOB area has enough space for eraseblock header */
>+ if (page_count > c->sector_size / c->mtd->oobblock) {
>+ printk(KERN_ERR "The OOB area is not big enough to hold eraseblock header (%d), reject to mount.\n",
>+ sizeof(struct jffs2_raw_ebh));
> return -EINVAL;
> }
>+
>+ c->fsdata_count = page_count * oobb_count;
>+ c->fsdata = kmalloc(c->fsdata_count * sizeof(*c->fsdata), GFP_KERNEL);
>+ if (c->fsdata == NULL)
>+ return -ENOMEM;
>+ jinfo = c->fsdata;
>+ for (i = 0; i < page_count; i++) {
>+ int j;
>+
>+ for (j = 0; j < oobb_count; j++) {
>+ jinfo->page_nr = i;
>+ jinfo->pos = oinfo->oobfree[j][0];
>+ jinfo->len = oinfo->oobfree[j][1];
>+ jinfo++;
>+ }
>+ }
>+ c->fsdata_len = page_count * free;
>+ c->fsdata_page_count = page_count;
>+
> return 0;
> }
>
>@@ -1229,16 +1272,16 @@
>
> res = jffs2_nand_set_oobinfo(c);
> if (res) {
>+ kfree(c->wbuf);
> return res;
> }
>
>- res = jffs2_nand_check_oobspace_for_ebh(c);
>-
> #ifdef BREAKME
> if (!brokenbuf)
> brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
> if (!brokenbuf) {
> kfree(c->wbuf);
>+ kfree(c->fsdata);
> return -ENOMEM;
> }
> memset(brokenbuf, 0xdb, c->wbuf_pagesize);
>@@ -1249,6 +1292,7 @@
> void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
> {
> kfree(c->wbuf);
>+ kfree(c->fsdata);
> }
>
> int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
>Index: include/linux/jffs2_fs_sb.h
>===================================================================
>RCS file: /home/cvs/mtd/include/linux/jffs2_fs_sb.h,v
>retrieving revision 1.60
>diff -u -r1.60 jffs2_fs_sb.h
>--- include/linux/jffs2_fs_sb.h 29 Nov 2005 14:34:37 -0000 1.60
>+++ include/linux/jffs2_fs_sb.h 2 Jan 2006 20:52:49 -0000
>@@ -33,6 +33,12 @@
>
> struct jffs2_inodirty;
>
>+struct jffs2_nand_oob_info {
>+ uint8_t page_nr; /* Page number within an erase block */
>+ uint8_t pos;
>+ uint8_t len;
>+};
>+
> /* A struct for the overall file system control. Pointers to
> jffs2_sb_info structs are named `c' in the source code.
> Nee jffs_control
>@@ -122,9 +128,11 @@
>
> /* Information about out-of-band area usage... */
> struct nand_oobinfo *oobinfo;
>- uint32_t badblock_pos;
>- uint32_t fsdata_pos;
>- uint32_t fsdata_len;
>+ uint8_t badblock_pos;
>+ struct jffs2_nand_oob_info *fsdata;
>+ uint8_t fsdata_count;
>+ uint8_t fsdata_page_count;
>+ uint8_t fsdata_len;
> #endif
>
> struct jffs2_summary *summary; /* Summary information */
>
>
>------------------------------------------------------------------------
>
>______________________________________________________
>Linux MTD discussion mailing list
>http://lists.infradead.org/mailman/listinfo/linux-mtd/
>
>
More information about the linux-mtd
mailing list