/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
- * Copyright (C) 2001-2003 Red Hat, Inc.
- * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
+ * Copyright © 2001-2007 Red Hat, Inc.
+ * Copyright © 2004 Thomas Gleixner <tglx@linutronix.de>
*
* Created by David Woodhouse <dwmw2@infradead.org>
* Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: wbuf.c,v 1.100 2005/09/30 13:59:13 dedekind Exp $
- *
*/
#include <linux/kernel.h>
#include <linux/crc32.h>
#include <linux/mtd/nand.h>
#include <linux/jiffies.h>
+#include <linux/sched.h>
#include "nodelist.h"
switch (je16_to_cpu(node->u.nodetype)) {
case JFFS2_NODETYPE_INODE:
+ if (f->metadata && f->metadata->raw == raw) {
+ dbg_noderef("Will replace ->raw in f->metadata at %p\n", f->metadata);
+ return &f->metadata->raw;
+ }
frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset));
BUG_ON(!frag);
/* Find a frag which refers to the full_dnode we want to modify */
}
dbg_noderef("Will replace ->raw in full_dnode at %p\n", frag->node);
return &frag->node->raw;
- break;
case JFFS2_NODETYPE_DIRENT:
for (fd = f->dents; fd; fd = fd->next) {
}
}
BUG();
+
default:
dbg_noderef("Don't care about replacing raw for nodetype %x\n",
je16_to_cpu(node->u.nodetype));
return NULL;
}
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
+ uint32_t ofs)
+{
+ int ret;
+ size_t retlen;
+ char *eccstr;
+
+ ret = c->mtd->read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify);
+ if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
+ printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret);
+ return ret;
+ } else if (retlen != c->wbuf_pagesize) {
+ printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize);
+ return -EIO;
+ }
+ if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize))
+ return 0;
+
+ if (ret == -EUCLEAN)
+ eccstr = "corrected";
+ else if (ret == -EBADMSG)
+ eccstr = "correction failed";
+ else
+ eccstr = "OK or unused";
+
+ printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n",
+ eccstr, c->wbuf_ofs);
+ print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
+ c->wbuf, c->wbuf_pagesize, 0);
+
+ printk(KERN_WARNING "Read back:\n");
+ print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
+ c->wbuf_verify, c->wbuf_pagesize, 0);
+
+ return -EIO;
+}
+#else
+#define jffs2_verify_write(c,b,o) (0)
+#endif
+
/* Recover from failure to write wbuf. Recover the nodes up to the
* wbuf, not the one which we were starting to try to write. */
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
spin_lock(&c->erase_completion_lock);
- jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
+ if (c->wbuf_ofs % c->mtd->erasesize)
+ jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
+ else
+ jffs2_block_refile(c, jeb, REFILE_ANYWAY);
spin_unlock(&c->erase_completion_lock);
BUG_ON(!ref_obsolete(jeb->last_node));
/* Do the read... */
ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
- if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) {
- /* ECC recovered */
+ /* ECC recovered ? */
+ if ((ret == -EUCLEAN || ret == -EBADMSG) &&
+ (retlen == c->wbuf_ofs - start))
ret = 0;
- }
+
if (ret || retlen != c->wbuf_ofs - start) {
printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
return;
}
+ /* The summary is not recovered, so it must be disabled for this erase block */
+ jffs2_sum_disable_collecting(c->summary);
+
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile);
if (ret) {
printk(KERN_WARNING "Failed to allocate node refs for wbuf recovery. Data loss ensues.\n");
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen,
rewrite_buf);
- if (ret || retlen != towrite) {
+ if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) {
/* Argh. We tried. Really we did. */
printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
kfree(buf);
/* If it's an in-core inode, then we have to adjust any
full_dirent or full_dnode structure to point to the
new version instead of the old */
- f = jffs2_gc_fetch_inode(c, ic->ino, ic->nlink);
+ f = jffs2_gc_fetch_inode(c, ic->ino, !ic->pino_nlink);
if (IS_ERR(f)) {
/* Should never happen; it _must_ be present */
JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n",
/* Fix up the original jeb now it's on the bad_list */
if (first_raw == jeb->first_node) {
D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
- list_del(&jeb->list);
- list_add(&jeb->list, &c->erase_pending_list);
+ list_move(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
jffs2_erase_pending_trigger(c);
}
if (!jffs2_is_writebuffered(c))
return 0;
- if (!down_trylock(&c->alloc_sem)) {
- up(&c->alloc_sem);
+ if (mutex_trylock(&c->alloc_sem)) {
+ mutex_unlock(&c->alloc_sem);
printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
BUG();
}
ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
- if (ret || retlen != c->wbuf_pagesize) {
- if (ret)
- printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
- else {
- printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
- retlen, c->wbuf_pagesize);
- ret = -EIO;
- }
-
+ if (ret) {
+ printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret);
+ goto wfail;
+ } else if (retlen != c->wbuf_pagesize) {
+ printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
+ retlen, c->wbuf_pagesize);
+ ret = -EIO;
+ goto wfail;
+ } else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) {
+ wfail:
jffs2_wbuf_recover(c);
return ret;
if (!c->wbuf)
return 0;
- down(&c->alloc_sem);
+ mutex_lock(&c->alloc_sem);
if (!jffs2_wbuf_pending_for_ino(c, ino)) {
D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
- up(&c->alloc_sem);
+ mutex_unlock(&c->alloc_sem);
return 0;
}
} else while (old_wbuf_len &&
old_wbuf_ofs == c->wbuf_ofs) {
- up(&c->alloc_sem);
+ mutex_unlock(&c->alloc_sem);
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
ret = jffs2_garbage_collect_pass(c);
if (ret) {
/* GC failed. Flush it with padding instead */
- down(&c->alloc_sem);
+ mutex_lock(&c->alloc_sem);
down_write(&c->wbuf_sem);
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
/* retry flushing wbuf in case jffs2_wbuf_recover
up_write(&c->wbuf_sem);
break;
}
- down(&c->alloc_sem);
+ mutex_lock(&c->alloc_sem);
}
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
- up(&c->alloc_sem);
+ mutex_unlock(&c->alloc_sem);
return ret;
}
down_read(&c->wbuf_sem);
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
- if ( (ret == -EBADMSG) && (*retlen == len) ) {
- printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
- len, ofs);
+ if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) {
+ if (ret == -EBADMSG)
+ printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx)"
+ " returned ECC error\n", len, ofs);
/*
- * We have the raw data without ECC correction in the buffer, maybe
- * we are lucky and all data or parts are correct. We check the node.
- * If data are corrupted node check will sort it out.
- * We keep this block, it will fail on write or erase and the we
- * mark it bad. Or should we do that now? But we should give him a chance.
- * Maybe we had a system crash or power loss before the ecc write or
- * a erase was completed.
+ * We have the raw data without ECC correction in the buffer,
+ * maybe we are lucky and all data or parts are correct. We
+ * check the node. If data are corrupted node check will sort
+ * it out. We keep this block, it will fail on write or erase
+ * and the we mark it bad. Or should we do that now? But we
+ * should give him a chance. Maybe we had a system crash or
+ * power loss before the ecc write or a erase was completed.
* So we return success. :)
*/
- ret = 0;
+ ret = 0;
}
/* if no writebuffer available or write buffer empty, return */
orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */
if (orbf > len) /* is write beyond write buffer ? */
goto exit;
- lwbf = len - orbf; /* number of bytes to copy */
+ lwbf = len - orbf; /* number of bytes to copy */
if (lwbf > c->wbuf_len)
lwbf = c->wbuf_len;
}
return ret;
}
+#define NR_OOB_SCAN_PAGES 4
+
+/* For historical reasons we use only 8 bytes for OOB clean marker */
+#define OOB_CM_SIZE 8
+
+static const struct jffs2_unknown_node oob_cleanmarker =
+{
+ .magic = constant_cpu_to_je16(JFFS2_MAGIC_BITMASK),
+ .nodetype = constant_cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+ .totlen = constant_cpu_to_je32(8)
+};
+
/*
- * Check, if the out of band area is empty
+ * Check, if the out of band area is empty. This function knows about the clean
+ * marker and if it is present in OOB, treats the OOB as empty anyway.
*/
-int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode)
+int jffs2_check_oob_empty(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb, int mode)
{
- unsigned char *buf;
- int ret = 0;
- int i,len,page;
- size_t retlen;
- int oob_size;
-
- /* allocate a buffer for all oob data in this sector */
- oob_size = c->mtd->oobsize;
- len = 4 * oob_size;
- buf = kmalloc(len, GFP_KERNEL);
- if (!buf) {
- printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n");
- return -ENOMEM;
- }
- /*
- * if mode = 0, we scan for a total empty oob area, else we have
- * to take care of the cleanmarker in the first page of the block
- */
- ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf);
- if (ret) {
- D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
- goto out;
- }
-
- if (retlen < len) {
- D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read "
- "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
- ret = -EIO;
- goto out;
+ int i, ret;
+ int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
+ struct mtd_oob_ops ops;
+
+ ops.mode = MTD_OOB_AUTO;
+ ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
+ ops.oobbuf = c->oobbuf;
+ ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
+ ops.datbuf = NULL;
+
+ ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
+ if (ret || ops.oobretlen != ops.ooblen) {
+ printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
+ " bytes, read %zd bytes, error %d\n",
+ jeb->offset, ops.ooblen, ops.oobretlen, ret);
+ if (!ret)
+ ret = -EIO;
+ return ret;
}
- /* Special check for first page */
- for(i = 0; i < oob_size ; i++) {
- /* Yeah, we know about the cleanmarker. */
- if (mode && i >= c->fsdata_pos &&
- i < c->fsdata_pos + c->fsdata_len)
+ for(i = 0; i < ops.ooblen; i++) {
+ if (mode && i < cmlen)
+ /* Yeah, we know about the cleanmarker */
continue;
- if (buf[i] != 0xFF) {
- D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
- buf[i], i, jeb->offset));
- ret = 1;
- goto out;
- }
- }
-
- /* we know, we are aligned :) */
- for (page = oob_size; page < len; page += sizeof(long)) {
- unsigned long dat = *(unsigned long *)(&buf[page]);
- if(dat != -1) {
- ret = 1;
- goto out;
+ if (ops.oobbuf[i] != 0xFF) {
+ D2(printk(KERN_DEBUG "Found %02x at %x in OOB for "
+ "%08x\n", ops.oobbuf[i], i, jeb->offset));
+ return 1;
}
}
-out:
- kfree(buf);
-
- return ret;
+ return 0;
}
/*
-* 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 (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ * Check for a valid cleanmarker.
+ * Returns: 0 if a valid cleanmarker was found
+ * 1 if no cleanmarker was found
+ * negative error code if an error occurred
+ */
+int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb)
{
- struct jffs2_unknown_node n;
- unsigned char buf[2 * NAND_MAX_OOBSIZE];
- unsigned char *p;
- int ret, i, cnt, retval = 0;
- size_t retlen, offset;
- int oob_size;
-
- offset = jeb->offset;
- oob_size = c->mtd->oobsize;
-
- /* Loop through the physical blocks */
- for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
- /* Check first if the block is bad. */
- if (c->mtd->block_isbad (c->mtd, offset)) {
- D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
- return 2;
- }
- /*
- * We read oob data from page 0 and 1 of the block.
- * page 0 contains cleanmarker and badblock info
- * page 1 contains failure count of this block
- */
- ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf);
-
- if (ret) {
- D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
- return ret;
- }
- if (retlen < (oob_size << 1)) {
- D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset));
- return -EIO;
- }
-
- /* Check cleanmarker only on the first physical block */
- if (!cnt) {
- n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
- n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
- n.totlen = cpu_to_je32 (8);
- p = (unsigned char *) &n;
-
- for (i = 0; i < c->fsdata_len; i++) {
- if (buf[c->fsdata_pos + i] != p[i]) {
- retval = 1;
- }
- }
- D1(if (retval == 1) {
- printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
- printk(KERN_WARNING "OOB at %08zx was ", offset);
- for (i=0; i < oob_size; i++) {
- printk("%02x ", buf[i]);
- }
- printk("\n");
- })
- }
- offset += c->mtd->erasesize;
+ struct mtd_oob_ops ops;
+ int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
+
+ ops.mode = MTD_OOB_AUTO;
+ ops.ooblen = cmlen;
+ ops.oobbuf = c->oobbuf;
+ ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
+ ops.datbuf = NULL;
+
+ ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
+ if (ret || ops.oobretlen != ops.ooblen) {
+ printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
+ " bytes, read %zd bytes, error %d\n",
+ jeb->offset, ops.ooblen, ops.oobretlen, ret);
+ if (!ret)
+ ret = -EIO;
+ return ret;
}
- return retval;
+
+ return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen);
}
-int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb)
{
- struct jffs2_unknown_node n;
- int ret;
- size_t retlen;
-
- n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
- n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
- n.totlen = cpu_to_je32(8);
-
- ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n);
-
- if (ret) {
- D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
- return ret;
- }
- if (retlen != c->fsdata_len) {
- D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
+ int ret;
+ struct mtd_oob_ops ops;
+ int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
+
+ ops.mode = MTD_OOB_AUTO;
+ ops.ooblen = cmlen;
+ ops.oobbuf = (uint8_t *)&oob_cleanmarker;
+ ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
+ ops.datbuf = NULL;
+
+ ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops);
+ if (ret || ops.oobretlen != ops.ooblen) {
+ printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd"
+ " bytes, read %zd bytes, error %d\n",
+ jeb->offset, ops.ooblen, ops.oobretlen, ret);
+ if (!ret)
+ ret = -EIO;
return ret;
}
+
return 0;
}
if (!c->mtd->block_markbad)
return 1; // What else can we do?
- D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
+ printk(KERN_WARNING "JFFS2: marking eraseblock at %08x\n as bad", bad_offset);
ret = c->mtd->block_markbad(c->mtd, bad_offset);
if (ret) {
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)
+int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
{
- struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
+ struct nand_ecclayout *oinfo = c->mtd->ecclayout;
- /* Do this only, if we have an oob buffer */
if (!c->mtd->oobsize)
return 0;
/* Cleanmarker is out-of-band, so inline size zero */
c->cleanmarker_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];
- if (c->fsdata_len > 8)
- c->fsdata_len = 8;
- } 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;
-
- default:
- D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
- return -EINVAL;
- }
+ if (!oinfo || oinfo->oobavail == 0) {
+ printk(KERN_ERR "inconsistent device description\n");
+ return -EINVAL;
}
- return 0;
-}
-int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
-{
- int res;
+ D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n"));
+
+ c->oobavail = oinfo->oobavail;
/* Initialise write buffer */
init_rwsem(&c->wbuf_sem);
if (!c->wbuf)
return -ENOMEM;
- res = jffs2_nand_set_oobinfo(c);
+ c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL);
+ if (!c->oobbuf) {
+ kfree(c->wbuf);
+ return -ENOMEM;
+ }
-#ifdef BREAKME
- if (!brokenbuf)
- brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
- if (!brokenbuf) {
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+ if (!c->wbuf_verify) {
+ kfree(c->oobbuf);
kfree(c->wbuf);
return -ENOMEM;
}
- memset(brokenbuf, 0xdb, c->wbuf_pagesize);
#endif
- return res;
+ return 0;
}
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
{
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ kfree(c->wbuf_verify);
+#endif
kfree(c->wbuf);
+ kfree(c->oobbuf);
}
int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
if (!c->wbuf)
return -ENOMEM;
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+ if (!c->wbuf_verify) {
+ kfree(c->oobbuf);
+ kfree(c->wbuf);
+ return -ENOMEM;
+ }
+#endif
+
printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size);
return 0;
}
void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ kfree(c->wbuf_verify);
+#endif
kfree(c->wbuf);
}
if (!c->wbuf)
return -ENOMEM;
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+ if (!c->wbuf_verify) {
+ kfree(c->wbuf);
+ return -ENOMEM;
+ }
+#endif
return 0;
}
void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) {
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ kfree(c->wbuf_verify);
+#endif
+ kfree(c->wbuf);
+}
+
+int jffs2_ubivol_setup(struct jffs2_sb_info *c) {
+ c->cleanmarker_size = 0;
+
+ if (c->mtd->writesize == 1)
+ /* We do not need write-buffer */
+ return 0;
+
+ init_rwsem(&c->wbuf_sem);
+
+ c->wbuf_pagesize = c->mtd->writesize;
+ c->wbuf_ofs = 0xFFFFFFFF;
+ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+ if (!c->wbuf)
+ return -ENOMEM;
+
+ printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size);
+
+ return 0;
+}
+
+void jffs2_ubivol_cleanup(struct jffs2_sb_info *c) {
kfree(c->wbuf);
}