162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * JFFS2 -- Journalling Flash File System, Version 2.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright © 2001-2007 Red Hat, Inc.
562306a36Sopenharmony_ci * Copyright © 2004 Thomas Gleixner <tglx@linutronix.de>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Created by David Woodhouse <dwmw2@infradead.org>
862306a36Sopenharmony_ci * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * For licensing information, see the file 'LICENCE' in this directory.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1962306a36Sopenharmony_ci#include <linux/crc32.h>
2062306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
2162306a36Sopenharmony_ci#include <linux/jiffies.h>
2262306a36Sopenharmony_ci#include <linux/sched.h>
2362306a36Sopenharmony_ci#include <linux/writeback.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "nodelist.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* For testing write failures */
2862306a36Sopenharmony_ci#undef BREAKME
2962306a36Sopenharmony_ci#undef BREAKMEHEADER
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#ifdef BREAKME
3262306a36Sopenharmony_cistatic unsigned char *brokenbuf;
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
3662306a36Sopenharmony_ci#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* max. erase failures before we mark a block bad */
3962306a36Sopenharmony_ci#define MAX_ERASE_FAILURES 	2
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct jffs2_inodirty {
4262306a36Sopenharmony_ci	uint32_t ino;
4362306a36Sopenharmony_ci	struct jffs2_inodirty *next;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic struct jffs2_inodirty inodirty_nomem;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct jffs2_inodirty *this = c->wbuf_inodes;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* If a malloc failed, consider _everything_ dirty */
5362306a36Sopenharmony_ci	if (this == &inodirty_nomem)
5462306a36Sopenharmony_ci		return 1;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* If ino == 0, _any_ non-GC writes mean 'yes' */
5762306a36Sopenharmony_ci	if (this && !ino)
5862306a36Sopenharmony_ci		return 1;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Look to see if the inode in question is pending in the wbuf */
6162306a36Sopenharmony_ci	while (this) {
6262306a36Sopenharmony_ci		if (this->ino == ino)
6362306a36Sopenharmony_ci			return 1;
6462306a36Sopenharmony_ci		this = this->next;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct jffs2_inodirty *this;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	this = c->wbuf_inodes;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (this != &inodirty_nomem) {
7662306a36Sopenharmony_ci		while (this) {
7762306a36Sopenharmony_ci			struct jffs2_inodirty *next = this->next;
7862306a36Sopenharmony_ci			kfree(this);
7962306a36Sopenharmony_ci			this = next;
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci	c->wbuf_inodes = NULL;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct jffs2_inodirty *new;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Schedule delayed write-buffer write-out */
9062306a36Sopenharmony_ci	jffs2_dirty_trigger(c);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (jffs2_wbuf_pending_for_ino(c, ino))
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	new = kmalloc(sizeof(*new), GFP_KERNEL);
9662306a36Sopenharmony_ci	if (!new) {
9762306a36Sopenharmony_ci		jffs2_dbg(1, "No memory to allocate inodirty. Fallback to all considered dirty\n");
9862306a36Sopenharmony_ci		jffs2_clear_wbuf_ino_list(c);
9962306a36Sopenharmony_ci		c->wbuf_inodes = &inodirty_nomem;
10062306a36Sopenharmony_ci		return;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci	new->ino = ino;
10362306a36Sopenharmony_ci	new->next = c->wbuf_inodes;
10462306a36Sopenharmony_ci	c->wbuf_inodes = new;
10562306a36Sopenharmony_ci	return;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct list_head *this, *next;
11162306a36Sopenharmony_ci	static int n;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (list_empty(&c->erasable_pending_wbuf_list))
11462306a36Sopenharmony_ci		return;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
11762306a36Sopenharmony_ci		struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		jffs2_dbg(1, "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n",
12062306a36Sopenharmony_ci			  jeb->offset);
12162306a36Sopenharmony_ci		list_del(this);
12262306a36Sopenharmony_ci		if ((jiffies + (n++)) & 127) {
12362306a36Sopenharmony_ci			/* Most of the time, we just erase it immediately. Otherwise we
12462306a36Sopenharmony_ci			   spend ages scanning it on mount, etc. */
12562306a36Sopenharmony_ci			jffs2_dbg(1, "...and adding to erase_pending_list\n");
12662306a36Sopenharmony_ci			list_add_tail(&jeb->list, &c->erase_pending_list);
12762306a36Sopenharmony_ci			c->nr_erasing_blocks++;
12862306a36Sopenharmony_ci			jffs2_garbage_collect_trigger(c);
12962306a36Sopenharmony_ci		} else {
13062306a36Sopenharmony_ci			/* Sometimes, however, we leave it elsewhere so it doesn't get
13162306a36Sopenharmony_ci			   immediately reused, and we spread the load a bit. */
13262306a36Sopenharmony_ci			jffs2_dbg(1, "...and adding to erasable_list\n");
13362306a36Sopenharmony_ci			list_add_tail(&jeb->list, &c->erasable_list);
13462306a36Sopenharmony_ci		}
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci#define REFILE_NOTEMPTY 0
13962306a36Sopenharmony_ci#define REFILE_ANYWAY   1
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	jffs2_dbg(1, "About to refile bad block at %08x\n", jeb->offset);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* File the existing block on the bad_used_list.... */
14662306a36Sopenharmony_ci	if (c->nextblock == jeb)
14762306a36Sopenharmony_ci		c->nextblock = NULL;
14862306a36Sopenharmony_ci	else /* Not sure this should ever happen... need more coffee */
14962306a36Sopenharmony_ci		list_del(&jeb->list);
15062306a36Sopenharmony_ci	if (jeb->first_node) {
15162306a36Sopenharmony_ci		jffs2_dbg(1, "Refiling block at %08x to bad_used_list\n",
15262306a36Sopenharmony_ci			  jeb->offset);
15362306a36Sopenharmony_ci		list_add(&jeb->list, &c->bad_used_list);
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		BUG_ON(allow_empty == REFILE_NOTEMPTY);
15662306a36Sopenharmony_ci		/* It has to have had some nodes or we couldn't be here */
15762306a36Sopenharmony_ci		jffs2_dbg(1, "Refiling block at %08x to erase_pending_list\n",
15862306a36Sopenharmony_ci			  jeb->offset);
15962306a36Sopenharmony_ci		list_add(&jeb->list, &c->erase_pending_list);
16062306a36Sopenharmony_ci		c->nr_erasing_blocks++;
16162306a36Sopenharmony_ci		jffs2_garbage_collect_trigger(c);
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) {
16562306a36Sopenharmony_ci		uint32_t oldfree = jeb->free_size;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		jffs2_link_node_ref(c, jeb,
16862306a36Sopenharmony_ci				    (jeb->offset+c->sector_size-oldfree) | REF_OBSOLETE,
16962306a36Sopenharmony_ci				    oldfree, NULL);
17062306a36Sopenharmony_ci		/* convert to wasted */
17162306a36Sopenharmony_ci		c->wasted_size += oldfree;
17262306a36Sopenharmony_ci		jeb->wasted_size += oldfree;
17362306a36Sopenharmony_ci		c->dirty_size -= oldfree;
17462306a36Sopenharmony_ci		jeb->dirty_size -= oldfree;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	jffs2_dbg_dump_block_lists_nolock(c);
17862306a36Sopenharmony_ci	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
17962306a36Sopenharmony_ci	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info *c,
18362306a36Sopenharmony_ci							    struct jffs2_inode_info *f,
18462306a36Sopenharmony_ci							    struct jffs2_raw_node_ref *raw,
18562306a36Sopenharmony_ci							    union jffs2_node_union *node)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct jffs2_node_frag *frag;
18862306a36Sopenharmony_ci	struct jffs2_full_dirent *fd;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	dbg_noderef("incore_replace_raw: node at %p is {%04x,%04x}\n",
19162306a36Sopenharmony_ci		    node, je16_to_cpu(node->u.magic), je16_to_cpu(node->u.nodetype));
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	BUG_ON(je16_to_cpu(node->u.magic) != 0x1985 &&
19462306a36Sopenharmony_ci	       je16_to_cpu(node->u.magic) != 0);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	switch (je16_to_cpu(node->u.nodetype)) {
19762306a36Sopenharmony_ci	case JFFS2_NODETYPE_INODE:
19862306a36Sopenharmony_ci		if (f->metadata && f->metadata->raw == raw) {
19962306a36Sopenharmony_ci			dbg_noderef("Will replace ->raw in f->metadata at %p\n", f->metadata);
20062306a36Sopenharmony_ci			return &f->metadata->raw;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci		frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset));
20362306a36Sopenharmony_ci		BUG_ON(!frag);
20462306a36Sopenharmony_ci		/* Find a frag which refers to the full_dnode we want to modify */
20562306a36Sopenharmony_ci		while (!frag->node || frag->node->raw != raw) {
20662306a36Sopenharmony_ci			frag = frag_next(frag);
20762306a36Sopenharmony_ci			BUG_ON(!frag);
20862306a36Sopenharmony_ci		}
20962306a36Sopenharmony_ci		dbg_noderef("Will replace ->raw in full_dnode at %p\n", frag->node);
21062306a36Sopenharmony_ci		return &frag->node->raw;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	case JFFS2_NODETYPE_DIRENT:
21362306a36Sopenharmony_ci		for (fd = f->dents; fd; fd = fd->next) {
21462306a36Sopenharmony_ci			if (fd->raw == raw) {
21562306a36Sopenharmony_ci				dbg_noderef("Will replace ->raw in full_dirent at %p\n", fd);
21662306a36Sopenharmony_ci				return &fd->raw;
21762306a36Sopenharmony_ci			}
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci		BUG();
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	default:
22262306a36Sopenharmony_ci		dbg_noderef("Don't care about replacing raw for nodetype %x\n",
22362306a36Sopenharmony_ci			    je16_to_cpu(node->u.nodetype));
22462306a36Sopenharmony_ci		break;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	return NULL;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
23062306a36Sopenharmony_cistatic int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
23162306a36Sopenharmony_ci			      uint32_t ofs)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	int ret;
23462306a36Sopenharmony_ci	size_t retlen;
23562306a36Sopenharmony_ci	char *eccstr;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	ret = mtd_read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify);
23862306a36Sopenharmony_ci	if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
23962306a36Sopenharmony_ci		pr_warn("%s(): Read back of page at %08x failed: %d\n",
24062306a36Sopenharmony_ci			__func__, c->wbuf_ofs, ret);
24162306a36Sopenharmony_ci		return ret;
24262306a36Sopenharmony_ci	} else if (retlen != c->wbuf_pagesize) {
24362306a36Sopenharmony_ci		pr_warn("%s(): Read back of page at %08x gave short read: %zd not %d\n",
24462306a36Sopenharmony_ci			__func__, ofs, retlen, c->wbuf_pagesize);
24562306a36Sopenharmony_ci		return -EIO;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize))
24862306a36Sopenharmony_ci		return 0;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (ret == -EUCLEAN)
25162306a36Sopenharmony_ci		eccstr = "corrected";
25262306a36Sopenharmony_ci	else if (ret == -EBADMSG)
25362306a36Sopenharmony_ci		eccstr = "correction failed";
25462306a36Sopenharmony_ci	else
25562306a36Sopenharmony_ci		eccstr = "OK or unused";
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	pr_warn("Write verify error (ECC %s) at %08x. Wrote:\n",
25862306a36Sopenharmony_ci		eccstr, c->wbuf_ofs);
25962306a36Sopenharmony_ci	print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
26062306a36Sopenharmony_ci		       c->wbuf, c->wbuf_pagesize, 0);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	pr_warn("Read back:\n");
26362306a36Sopenharmony_ci	print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
26462306a36Sopenharmony_ci		       c->wbuf_verify, c->wbuf_pagesize, 0);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return -EIO;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci#else
26962306a36Sopenharmony_ci#define jffs2_verify_write(c,b,o) (0)
27062306a36Sopenharmony_ci#endif
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/* Recover from failure to write wbuf. Recover the nodes up to the
27362306a36Sopenharmony_ci * wbuf, not the one which we were starting to try to write. */
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void jffs2_wbuf_recover(struct jffs2_sb_info *c)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct jffs2_eraseblock *jeb, *new_jeb;
27862306a36Sopenharmony_ci	struct jffs2_raw_node_ref *raw, *next, *first_raw = NULL;
27962306a36Sopenharmony_ci	size_t retlen;
28062306a36Sopenharmony_ci	int ret;
28162306a36Sopenharmony_ci	int nr_refile = 0;
28262306a36Sopenharmony_ci	unsigned char *buf;
28362306a36Sopenharmony_ci	uint32_t start, end, ofs, len;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	spin_lock(&c->erase_completion_lock);
28862306a36Sopenharmony_ci	if (c->wbuf_ofs % c->mtd->erasesize)
28962306a36Sopenharmony_ci		jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
29062306a36Sopenharmony_ci	else
29162306a36Sopenharmony_ci		jffs2_block_refile(c, jeb, REFILE_ANYWAY);
29262306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	BUG_ON(!ref_obsolete(jeb->last_node));
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* Find the first node to be recovered, by skipping over every
29762306a36Sopenharmony_ci	   node which ends before the wbuf starts, or which is obsolete. */
29862306a36Sopenharmony_ci	for (next = raw = jeb->first_node; next; raw = next) {
29962306a36Sopenharmony_ci		next = ref_next(raw);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		if (ref_obsolete(raw) ||
30262306a36Sopenharmony_ci		    (next && ref_offset(next) <= c->wbuf_ofs)) {
30362306a36Sopenharmony_ci			dbg_noderef("Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
30462306a36Sopenharmony_ci				    ref_offset(raw), ref_flags(raw),
30562306a36Sopenharmony_ci				    (ref_offset(raw) + ref_totlen(c, jeb, raw)),
30662306a36Sopenharmony_ci				    c->wbuf_ofs);
30762306a36Sopenharmony_ci			continue;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci		dbg_noderef("First node to be recovered is at 0x%08x(%d)-0x%08x\n",
31062306a36Sopenharmony_ci			    ref_offset(raw), ref_flags(raw),
31162306a36Sopenharmony_ci			    (ref_offset(raw) + ref_totlen(c, jeb, raw)));
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		first_raw = raw;
31462306a36Sopenharmony_ci		break;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (!first_raw) {
31862306a36Sopenharmony_ci		/* All nodes were obsolete. Nothing to recover. */
31962306a36Sopenharmony_ci		jffs2_dbg(1, "No non-obsolete nodes to be recovered. Just filing block bad\n");
32062306a36Sopenharmony_ci		c->wbuf_len = 0;
32162306a36Sopenharmony_ci		return;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	start = ref_offset(first_raw);
32562306a36Sopenharmony_ci	end = ref_offset(jeb->last_node);
32662306a36Sopenharmony_ci	nr_refile = 1;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* Count the number of refs which need to be copied */
32962306a36Sopenharmony_ci	while ((raw = ref_next(raw)) != jeb->last_node)
33062306a36Sopenharmony_ci		nr_refile++;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	dbg_noderef("wbuf recover %08x-%08x (%d bytes in %d nodes)\n",
33362306a36Sopenharmony_ci		    start, end, end - start, nr_refile);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	buf = NULL;
33662306a36Sopenharmony_ci	if (start < c->wbuf_ofs) {
33762306a36Sopenharmony_ci		/* First affected node was already partially written.
33862306a36Sopenharmony_ci		 * Attempt to reread the old data into our buffer. */
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		buf = kmalloc(end - start, GFP_KERNEL);
34162306a36Sopenharmony_ci		if (!buf) {
34262306a36Sopenharmony_ci			pr_crit("Malloc failure in wbuf recovery. Data loss ensues.\n");
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci			goto read_failed;
34562306a36Sopenharmony_ci		}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		/* Do the read... */
34862306a36Sopenharmony_ci		ret = mtd_read(c->mtd, start, c->wbuf_ofs - start, &retlen,
34962306a36Sopenharmony_ci			       buf);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		/* ECC recovered ? */
35262306a36Sopenharmony_ci		if ((ret == -EUCLEAN || ret == -EBADMSG) &&
35362306a36Sopenharmony_ci		    (retlen == c->wbuf_ofs - start))
35462306a36Sopenharmony_ci			ret = 0;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		if (ret || retlen != c->wbuf_ofs - start) {
35762306a36Sopenharmony_ci			pr_crit("Old data are already lost in wbuf recovery. Data loss ensues.\n");
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci			kfree(buf);
36062306a36Sopenharmony_ci			buf = NULL;
36162306a36Sopenharmony_ci		read_failed:
36262306a36Sopenharmony_ci			first_raw = ref_next(first_raw);
36362306a36Sopenharmony_ci			nr_refile--;
36462306a36Sopenharmony_ci			while (first_raw && ref_obsolete(first_raw)) {
36562306a36Sopenharmony_ci				first_raw = ref_next(first_raw);
36662306a36Sopenharmony_ci				nr_refile--;
36762306a36Sopenharmony_ci			}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci			/* If this was the only node to be recovered, give up */
37062306a36Sopenharmony_ci			if (!first_raw) {
37162306a36Sopenharmony_ci				c->wbuf_len = 0;
37262306a36Sopenharmony_ci				return;
37362306a36Sopenharmony_ci			}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci			/* It wasn't. Go on and try to recover nodes complete in the wbuf */
37662306a36Sopenharmony_ci			start = ref_offset(first_raw);
37762306a36Sopenharmony_ci			dbg_noderef("wbuf now recover %08x-%08x (%d bytes in %d nodes)\n",
37862306a36Sopenharmony_ci				    start, end, end - start, nr_refile);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		} else {
38162306a36Sopenharmony_ci			/* Read succeeded. Copy the remaining data from the wbuf */
38262306a36Sopenharmony_ci			memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
38362306a36Sopenharmony_ci		}
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	/* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
38662306a36Sopenharmony_ci	   Either 'buf' contains the data, or we find it in the wbuf */
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* ... and get an allocation of space from a shiny new block instead */
38962306a36Sopenharmony_ci	ret = jffs2_reserve_space_gc(c, end-start, &len, JFFS2_SUMMARY_NOSUM_SIZE);
39062306a36Sopenharmony_ci	if (ret) {
39162306a36Sopenharmony_ci		pr_warn("Failed to allocate space for wbuf recovery. Data loss ensues.\n");
39262306a36Sopenharmony_ci		kfree(buf);
39362306a36Sopenharmony_ci		return;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* The summary is not recovered, so it must be disabled for this erase block */
39762306a36Sopenharmony_ci	jffs2_sum_disable_collecting(c->summary);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile);
40062306a36Sopenharmony_ci	if (ret) {
40162306a36Sopenharmony_ci		pr_warn("Failed to allocate node refs for wbuf recovery. Data loss ensues.\n");
40262306a36Sopenharmony_ci		kfree(buf);
40362306a36Sopenharmony_ci		return;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	ofs = write_ofs(c);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (end-start >= c->wbuf_pagesize) {
40962306a36Sopenharmony_ci		/* Need to do another write immediately, but it's possible
41062306a36Sopenharmony_ci		   that this is just because the wbuf itself is completely
41162306a36Sopenharmony_ci		   full, and there's nothing earlier read back from the
41262306a36Sopenharmony_ci		   flash. Hence 'buf' isn't necessarily what we're writing
41362306a36Sopenharmony_ci		   from. */
41462306a36Sopenharmony_ci		unsigned char *rewrite_buf = buf?:c->wbuf;
41562306a36Sopenharmony_ci		uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		jffs2_dbg(1, "Write 0x%x bytes at 0x%08x in wbuf recover\n",
41862306a36Sopenharmony_ci			  towrite, ofs);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci#ifdef BREAKMEHEADER
42162306a36Sopenharmony_ci		static int breakme;
42262306a36Sopenharmony_ci		if (breakme++ == 20) {
42362306a36Sopenharmony_ci			pr_notice("Faking write error at 0x%08x\n", ofs);
42462306a36Sopenharmony_ci			breakme = 0;
42562306a36Sopenharmony_ci			mtd_write(c->mtd, ofs, towrite, &retlen, brokenbuf);
42662306a36Sopenharmony_ci			ret = -EIO;
42762306a36Sopenharmony_ci		} else
42862306a36Sopenharmony_ci#endif
42962306a36Sopenharmony_ci			ret = mtd_write(c->mtd, ofs, towrite, &retlen,
43062306a36Sopenharmony_ci					rewrite_buf);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) {
43362306a36Sopenharmony_ci			/* Argh. We tried. Really we did. */
43462306a36Sopenharmony_ci			pr_crit("Recovery of wbuf failed due to a second write error\n");
43562306a36Sopenharmony_ci			kfree(buf);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci			if (retlen)
43862306a36Sopenharmony_ci				jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, first_raw), NULL);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci			return;
44162306a36Sopenharmony_ci		}
44262306a36Sopenharmony_ci		pr_notice("Recovery of wbuf succeeded to %08x\n", ofs);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		c->wbuf_len = (end - start) - towrite;
44562306a36Sopenharmony_ci		c->wbuf_ofs = ofs + towrite;
44662306a36Sopenharmony_ci		memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
44762306a36Sopenharmony_ci		/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
44862306a36Sopenharmony_ci	} else {
44962306a36Sopenharmony_ci		/* OK, now we're left with the dregs in whichever buffer we're using */
45062306a36Sopenharmony_ci		if (buf) {
45162306a36Sopenharmony_ci			memcpy(c->wbuf, buf, end-start);
45262306a36Sopenharmony_ci		} else {
45362306a36Sopenharmony_ci			memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci		c->wbuf_ofs = ofs;
45662306a36Sopenharmony_ci		c->wbuf_len = end - start;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
46062306a36Sopenharmony_ci	new_jeb = &c->blocks[ofs / c->sector_size];
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	spin_lock(&c->erase_completion_lock);
46362306a36Sopenharmony_ci	for (raw = first_raw; raw != jeb->last_node; raw = ref_next(raw)) {
46462306a36Sopenharmony_ci		uint32_t rawlen = ref_totlen(c, jeb, raw);
46562306a36Sopenharmony_ci		struct jffs2_inode_cache *ic;
46662306a36Sopenharmony_ci		struct jffs2_raw_node_ref *new_ref;
46762306a36Sopenharmony_ci		struct jffs2_raw_node_ref **adjust_ref = NULL;
46862306a36Sopenharmony_ci		struct jffs2_inode_info *f = NULL;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		jffs2_dbg(1, "Refiling block of %08x at %08x(%d) to %08x\n",
47162306a36Sopenharmony_ci			  rawlen, ref_offset(raw), ref_flags(raw), ofs);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		ic = jffs2_raw_ref_to_ic(raw);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		/* Ick. This XATTR mess should be fixed shortly... */
47662306a36Sopenharmony_ci		if (ic && ic->class == RAWNODE_CLASS_XATTR_DATUM) {
47762306a36Sopenharmony_ci			struct jffs2_xattr_datum *xd = (void *)ic;
47862306a36Sopenharmony_ci			BUG_ON(xd->node != raw);
47962306a36Sopenharmony_ci			adjust_ref = &xd->node;
48062306a36Sopenharmony_ci			raw->next_in_ino = NULL;
48162306a36Sopenharmony_ci			ic = NULL;
48262306a36Sopenharmony_ci		} else if (ic && ic->class == RAWNODE_CLASS_XATTR_REF) {
48362306a36Sopenharmony_ci			struct jffs2_xattr_datum *xr = (void *)ic;
48462306a36Sopenharmony_ci			BUG_ON(xr->node != raw);
48562306a36Sopenharmony_ci			adjust_ref = &xr->node;
48662306a36Sopenharmony_ci			raw->next_in_ino = NULL;
48762306a36Sopenharmony_ci			ic = NULL;
48862306a36Sopenharmony_ci		} else if (ic && ic->class == RAWNODE_CLASS_INODE_CACHE) {
48962306a36Sopenharmony_ci			struct jffs2_raw_node_ref **p = &ic->nodes;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci			/* Remove the old node from the per-inode list */
49262306a36Sopenharmony_ci			while (*p && *p != (void *)ic) {
49362306a36Sopenharmony_ci				if (*p == raw) {
49462306a36Sopenharmony_ci					(*p) = (raw->next_in_ino);
49562306a36Sopenharmony_ci					raw->next_in_ino = NULL;
49662306a36Sopenharmony_ci					break;
49762306a36Sopenharmony_ci				}
49862306a36Sopenharmony_ci				p = &((*p)->next_in_ino);
49962306a36Sopenharmony_ci			}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci			if (ic->state == INO_STATE_PRESENT && !ref_obsolete(raw)) {
50262306a36Sopenharmony_ci				/* If it's an in-core inode, then we have to adjust any
50362306a36Sopenharmony_ci				   full_dirent or full_dnode structure to point to the
50462306a36Sopenharmony_ci				   new version instead of the old */
50562306a36Sopenharmony_ci				f = jffs2_gc_fetch_inode(c, ic->ino, !ic->pino_nlink);
50662306a36Sopenharmony_ci				if (IS_ERR(f)) {
50762306a36Sopenharmony_ci					/* Should never happen; it _must_ be present */
50862306a36Sopenharmony_ci					JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n",
50962306a36Sopenharmony_ci						    ic->ino, PTR_ERR(f));
51062306a36Sopenharmony_ci					BUG();
51162306a36Sopenharmony_ci				}
51262306a36Sopenharmony_ci				/* We don't lock f->sem. There's a number of ways we could
51362306a36Sopenharmony_ci				   end up in here with it already being locked, and nobody's
51462306a36Sopenharmony_ci				   going to modify it on us anyway because we hold the
51562306a36Sopenharmony_ci				   alloc_sem. We're only changing one ->raw pointer too,
51662306a36Sopenharmony_ci				   which we can get away with without upsetting readers. */
51762306a36Sopenharmony_ci				adjust_ref = jffs2_incore_replace_raw(c, f, raw,
51862306a36Sopenharmony_ci								      (void *)(buf?:c->wbuf) + (ref_offset(raw) - start));
51962306a36Sopenharmony_ci			} else if (unlikely(ic->state != INO_STATE_PRESENT &&
52062306a36Sopenharmony_ci					    ic->state != INO_STATE_CHECKEDABSENT &&
52162306a36Sopenharmony_ci					    ic->state != INO_STATE_GC)) {
52262306a36Sopenharmony_ci				JFFS2_ERROR("Inode #%u is in strange state %d!\n", ic->ino, ic->state);
52362306a36Sopenharmony_ci				BUG();
52462306a36Sopenharmony_ci			}
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		new_ref = jffs2_link_node_ref(c, new_jeb, ofs | ref_flags(raw), rawlen, ic);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		if (adjust_ref) {
53062306a36Sopenharmony_ci			BUG_ON(*adjust_ref != raw);
53162306a36Sopenharmony_ci			*adjust_ref = new_ref;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci		if (f)
53462306a36Sopenharmony_ci			jffs2_gc_release_inode(c, f);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		if (!ref_obsolete(raw)) {
53762306a36Sopenharmony_ci			jeb->dirty_size += rawlen;
53862306a36Sopenharmony_ci			jeb->used_size  -= rawlen;
53962306a36Sopenharmony_ci			c->dirty_size += rawlen;
54062306a36Sopenharmony_ci			c->used_size -= rawlen;
54162306a36Sopenharmony_ci			raw->flash_offset = ref_offset(raw) | REF_OBSOLETE;
54262306a36Sopenharmony_ci			BUG_ON(raw->next_in_ino);
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci		ofs += rawlen;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	kfree(buf);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* Fix up the original jeb now it's on the bad_list */
55062306a36Sopenharmony_ci	if (first_raw == jeb->first_node) {
55162306a36Sopenharmony_ci		jffs2_dbg(1, "Failing block at %08x is now empty. Moving to erase_pending_list\n",
55262306a36Sopenharmony_ci			  jeb->offset);
55362306a36Sopenharmony_ci		list_move(&jeb->list, &c->erase_pending_list);
55462306a36Sopenharmony_ci		c->nr_erasing_blocks++;
55562306a36Sopenharmony_ci		jffs2_garbage_collect_trigger(c);
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	jffs2_dbg_acct_sanity_check_nolock(c, jeb);
55962306a36Sopenharmony_ci	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	jffs2_dbg_acct_sanity_check_nolock(c, new_jeb);
56262306a36Sopenharmony_ci	jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	jffs2_dbg(1, "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n",
56762306a36Sopenharmony_ci		  c->wbuf_ofs, c->wbuf_len);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/* Meaning of pad argument:
57262306a36Sopenharmony_ci   0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
57362306a36Sopenharmony_ci   1: Pad, do not adjust nextblock free_size
57462306a36Sopenharmony_ci   2: Pad, adjust nextblock free_size
57562306a36Sopenharmony_ci*/
57662306a36Sopenharmony_ci#define NOPAD		0
57762306a36Sopenharmony_ci#define PAD_NOACCOUNT	1
57862306a36Sopenharmony_ci#define PAD_ACCOUNTING	2
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct jffs2_eraseblock *wbuf_jeb;
58362306a36Sopenharmony_ci	int ret;
58462306a36Sopenharmony_ci	size_t retlen;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* Nothing to do if not write-buffering the flash. In particular, we shouldn't
58762306a36Sopenharmony_ci	   del_timer() the timer we never initialised. */
58862306a36Sopenharmony_ci	if (!jffs2_is_writebuffered(c))
58962306a36Sopenharmony_ci		return 0;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (!mutex_is_locked(&c->alloc_sem)) {
59262306a36Sopenharmony_ci		pr_crit("jffs2_flush_wbuf() called with alloc_sem not locked!\n");
59362306a36Sopenharmony_ci		BUG();
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (!c->wbuf_len)	/* already checked c->wbuf above */
59762306a36Sopenharmony_ci		return 0;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	wbuf_jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
60062306a36Sopenharmony_ci	if (jffs2_prealloc_raw_node_refs(c, wbuf_jeb, c->nextblock->allocated_refs + 1))
60162306a36Sopenharmony_ci		return -ENOMEM;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* claim remaining space on the page
60462306a36Sopenharmony_ci	   this happens, if we have a change to a new block,
60562306a36Sopenharmony_ci	   or if fsync forces us to flush the writebuffer.
60662306a36Sopenharmony_ci	   if we have a switch to next page, we will not have
60762306a36Sopenharmony_ci	   enough remaining space for this.
60862306a36Sopenharmony_ci	*/
60962306a36Sopenharmony_ci	if (pad ) {
61062306a36Sopenharmony_ci		c->wbuf_len = PAD(c->wbuf_len);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		/* Pad with JFFS2_DIRTY_BITMASK initially.  this helps out ECC'd NOR
61362306a36Sopenharmony_ci		   with 8 byte page size */
61462306a36Sopenharmony_ci		memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
61762306a36Sopenharmony_ci			struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
61862306a36Sopenharmony_ci			padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
61962306a36Sopenharmony_ci			padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
62062306a36Sopenharmony_ci			padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
62162306a36Sopenharmony_ci			padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci	/* else jffs2_flash_writev has actually filled in the rest of the
62562306a36Sopenharmony_ci	   buffer for us, and will deal with the node refs etc. later. */
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci#ifdef BREAKME
62862306a36Sopenharmony_ci	static int breakme;
62962306a36Sopenharmony_ci	if (breakme++ == 20) {
63062306a36Sopenharmony_ci		pr_notice("Faking write error at 0x%08x\n", c->wbuf_ofs);
63162306a36Sopenharmony_ci		breakme = 0;
63262306a36Sopenharmony_ci		mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen,
63362306a36Sopenharmony_ci			  brokenbuf);
63462306a36Sopenharmony_ci		ret = -EIO;
63562306a36Sopenharmony_ci	} else
63662306a36Sopenharmony_ci#endif
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		ret = mtd_write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize,
63962306a36Sopenharmony_ci				&retlen, c->wbuf);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (ret) {
64262306a36Sopenharmony_ci		pr_warn("jffs2_flush_wbuf(): Write failed with %d\n", ret);
64362306a36Sopenharmony_ci		goto wfail;
64462306a36Sopenharmony_ci	} else if (retlen != c->wbuf_pagesize) {
64562306a36Sopenharmony_ci		pr_warn("jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
64662306a36Sopenharmony_ci			retlen, c->wbuf_pagesize);
64762306a36Sopenharmony_ci		ret = -EIO;
64862306a36Sopenharmony_ci		goto wfail;
64962306a36Sopenharmony_ci	} else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) {
65062306a36Sopenharmony_ci	wfail:
65162306a36Sopenharmony_ci		jffs2_wbuf_recover(c);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		return ret;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/* Adjust free size of the block if we padded. */
65762306a36Sopenharmony_ci	if (pad) {
65862306a36Sopenharmony_ci		uint32_t waste = c->wbuf_pagesize - c->wbuf_len;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		jffs2_dbg(1, "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
66162306a36Sopenharmony_ci			  (wbuf_jeb == c->nextblock) ? "next" : "",
66262306a36Sopenharmony_ci			  wbuf_jeb->offset);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		/* wbuf_pagesize - wbuf_len is the amount of space that's to be
66562306a36Sopenharmony_ci		   padded. If there is less free space in the block than that,
66662306a36Sopenharmony_ci		   something screwed up */
66762306a36Sopenharmony_ci		if (wbuf_jeb->free_size < waste) {
66862306a36Sopenharmony_ci			pr_crit("jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
66962306a36Sopenharmony_ci				c->wbuf_ofs, c->wbuf_len, waste);
67062306a36Sopenharmony_ci			pr_crit("jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
67162306a36Sopenharmony_ci				wbuf_jeb->offset, wbuf_jeb->free_size);
67262306a36Sopenharmony_ci			BUG();
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		spin_lock(&c->erase_completion_lock);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		jffs2_link_node_ref(c, wbuf_jeb, (c->wbuf_ofs + c->wbuf_len) | REF_OBSOLETE, waste, NULL);
67862306a36Sopenharmony_ci		/* FIXME: that made it count as dirty. Convert to wasted */
67962306a36Sopenharmony_ci		wbuf_jeb->dirty_size -= waste;
68062306a36Sopenharmony_ci		c->dirty_size -= waste;
68162306a36Sopenharmony_ci		wbuf_jeb->wasted_size += waste;
68262306a36Sopenharmony_ci		c->wasted_size += waste;
68362306a36Sopenharmony_ci	} else
68462306a36Sopenharmony_ci		spin_lock(&c->erase_completion_lock);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* Stick any now-obsoleted blocks on the erase_pending_list */
68762306a36Sopenharmony_ci	jffs2_refile_wbuf_blocks(c);
68862306a36Sopenharmony_ci	jffs2_clear_wbuf_ino_list(c);
68962306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	memset(c->wbuf,0xff,c->wbuf_pagesize);
69262306a36Sopenharmony_ci	/* adjust write buffer offset, else we get a non contiguous write bug */
69362306a36Sopenharmony_ci	c->wbuf_ofs += c->wbuf_pagesize;
69462306a36Sopenharmony_ci	c->wbuf_len = 0;
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci/* Trigger garbage collection to flush the write-buffer.
69962306a36Sopenharmony_ci   If ino arg is zero, do it if _any_ real (i.e. not GC) writes are
70062306a36Sopenharmony_ci   outstanding. If ino arg non-zero, do it only if a write for the
70162306a36Sopenharmony_ci   given inode is outstanding. */
70262306a36Sopenharmony_ciint jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	uint32_t old_wbuf_ofs;
70562306a36Sopenharmony_ci	uint32_t old_wbuf_len;
70662306a36Sopenharmony_ci	int ret = 0;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	jffs2_dbg(1, "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (!c->wbuf)
71162306a36Sopenharmony_ci		return 0;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	mutex_lock(&c->alloc_sem);
71462306a36Sopenharmony_ci	if (!jffs2_wbuf_pending_for_ino(c, ino)) {
71562306a36Sopenharmony_ci		jffs2_dbg(1, "Ino #%d not pending in wbuf. Returning\n", ino);
71662306a36Sopenharmony_ci		mutex_unlock(&c->alloc_sem);
71762306a36Sopenharmony_ci		return 0;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	old_wbuf_ofs = c->wbuf_ofs;
72162306a36Sopenharmony_ci	old_wbuf_len = c->wbuf_len;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (c->unchecked_size) {
72462306a36Sopenharmony_ci		/* GC won't make any progress for a while */
72562306a36Sopenharmony_ci		jffs2_dbg(1, "%s(): padding. Not finished checking\n",
72662306a36Sopenharmony_ci			  __func__);
72762306a36Sopenharmony_ci		down_write(&c->wbuf_sem);
72862306a36Sopenharmony_ci		ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
72962306a36Sopenharmony_ci		/* retry flushing wbuf in case jffs2_wbuf_recover
73062306a36Sopenharmony_ci		   left some data in the wbuf */
73162306a36Sopenharmony_ci		if (ret)
73262306a36Sopenharmony_ci			ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
73362306a36Sopenharmony_ci		up_write(&c->wbuf_sem);
73462306a36Sopenharmony_ci	} else while (old_wbuf_len &&
73562306a36Sopenharmony_ci		      old_wbuf_ofs == c->wbuf_ofs) {
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		mutex_unlock(&c->alloc_sem);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		jffs2_dbg(1, "%s(): calls gc pass\n", __func__);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		ret = jffs2_garbage_collect_pass(c);
74262306a36Sopenharmony_ci		if (ret) {
74362306a36Sopenharmony_ci			/* GC failed. Flush it with padding instead */
74462306a36Sopenharmony_ci			mutex_lock(&c->alloc_sem);
74562306a36Sopenharmony_ci			down_write(&c->wbuf_sem);
74662306a36Sopenharmony_ci			ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
74762306a36Sopenharmony_ci			/* retry flushing wbuf in case jffs2_wbuf_recover
74862306a36Sopenharmony_ci			   left some data in the wbuf */
74962306a36Sopenharmony_ci			if (ret)
75062306a36Sopenharmony_ci				ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
75162306a36Sopenharmony_ci			up_write(&c->wbuf_sem);
75262306a36Sopenharmony_ci			break;
75362306a36Sopenharmony_ci		}
75462306a36Sopenharmony_ci		mutex_lock(&c->alloc_sem);
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	jffs2_dbg(1, "%s(): ends...\n", __func__);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	mutex_unlock(&c->alloc_sem);
76062306a36Sopenharmony_ci	return ret;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci/* Pad write-buffer to end and write it, wasting space. */
76462306a36Sopenharmony_ciint jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	int ret;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (!c->wbuf)
76962306a36Sopenharmony_ci		return 0;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	down_write(&c->wbuf_sem);
77262306a36Sopenharmony_ci	ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
77362306a36Sopenharmony_ci	/* retry - maybe wbuf recover left some data in wbuf. */
77462306a36Sopenharmony_ci	if (ret)
77562306a36Sopenharmony_ci		ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
77662306a36Sopenharmony_ci	up_write(&c->wbuf_sem);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	return ret;
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic size_t jffs2_fill_wbuf(struct jffs2_sb_info *c, const uint8_t *buf,
78262306a36Sopenharmony_ci			      size_t len)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	if (len && !c->wbuf_len && (len >= c->wbuf_pagesize))
78562306a36Sopenharmony_ci		return 0;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (len > (c->wbuf_pagesize - c->wbuf_len))
78862306a36Sopenharmony_ci		len = c->wbuf_pagesize - c->wbuf_len;
78962306a36Sopenharmony_ci	memcpy(c->wbuf + c->wbuf_len, buf, len);
79062306a36Sopenharmony_ci	c->wbuf_len += (uint32_t) len;
79162306a36Sopenharmony_ci	return len;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ciint jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs,
79562306a36Sopenharmony_ci		       unsigned long count, loff_t to, size_t *retlen,
79662306a36Sopenharmony_ci		       uint32_t ino)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	struct jffs2_eraseblock *jeb;
79962306a36Sopenharmony_ci	size_t wbuf_retlen, donelen = 0;
80062306a36Sopenharmony_ci	uint32_t outvec_to = to;
80162306a36Sopenharmony_ci	int ret, invec;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	/* If not writebuffered flash, don't bother */
80462306a36Sopenharmony_ci	if (!jffs2_is_writebuffered(c))
80562306a36Sopenharmony_ci		return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	down_write(&c->wbuf_sem);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* If wbuf_ofs is not initialized, set it to target address */
81062306a36Sopenharmony_ci	if (c->wbuf_ofs == 0xFFFFFFFF) {
81162306a36Sopenharmony_ci		c->wbuf_ofs = PAGE_DIV(to);
81262306a36Sopenharmony_ci		c->wbuf_len = PAGE_MOD(to);
81362306a36Sopenharmony_ci		memset(c->wbuf,0xff,c->wbuf_pagesize);
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/*
81762306a36Sopenharmony_ci	 * Sanity checks on target address.  It's permitted to write
81862306a36Sopenharmony_ci	 * at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to
81962306a36Sopenharmony_ci	 * write at the beginning of a new erase block. Anything else,
82062306a36Sopenharmony_ci	 * and you die.  New block starts at xxx000c (0-b = block
82162306a36Sopenharmony_ci	 * header)
82262306a36Sopenharmony_ci	 */
82362306a36Sopenharmony_ci	if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
82462306a36Sopenharmony_ci		/* It's a write to a new block */
82562306a36Sopenharmony_ci		if (c->wbuf_len) {
82662306a36Sopenharmony_ci			jffs2_dbg(1, "%s(): to 0x%lx causes flush of wbuf at 0x%08x\n",
82762306a36Sopenharmony_ci				  __func__, (unsigned long)to, c->wbuf_ofs);
82862306a36Sopenharmony_ci			ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
82962306a36Sopenharmony_ci			if (ret)
83062306a36Sopenharmony_ci				goto outerr;
83162306a36Sopenharmony_ci		}
83262306a36Sopenharmony_ci		/* set pointer to new block */
83362306a36Sopenharmony_ci		c->wbuf_ofs = PAGE_DIV(to);
83462306a36Sopenharmony_ci		c->wbuf_len = PAGE_MOD(to);
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
83862306a36Sopenharmony_ci		/* We're not writing immediately after the writebuffer. Bad. */
83962306a36Sopenharmony_ci		pr_crit("%s(): Non-contiguous write to %08lx\n",
84062306a36Sopenharmony_ci			__func__, (unsigned long)to);
84162306a36Sopenharmony_ci		if (c->wbuf_len)
84262306a36Sopenharmony_ci			pr_crit("wbuf was previously %08x-%08x\n",
84362306a36Sopenharmony_ci				c->wbuf_ofs, c->wbuf_ofs + c->wbuf_len);
84462306a36Sopenharmony_ci		BUG();
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* adjust alignment offset */
84862306a36Sopenharmony_ci	if (c->wbuf_len != PAGE_MOD(to)) {
84962306a36Sopenharmony_ci		c->wbuf_len = PAGE_MOD(to);
85062306a36Sopenharmony_ci		/* take care of alignment to next page */
85162306a36Sopenharmony_ci		if (!c->wbuf_len) {
85262306a36Sopenharmony_ci			c->wbuf_len = c->wbuf_pagesize;
85362306a36Sopenharmony_ci			ret = __jffs2_flush_wbuf(c, NOPAD);
85462306a36Sopenharmony_ci			if (ret)
85562306a36Sopenharmony_ci				goto outerr;
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	for (invec = 0; invec < count; invec++) {
86062306a36Sopenharmony_ci		int vlen = invecs[invec].iov_len;
86162306a36Sopenharmony_ci		uint8_t *v = invecs[invec].iov_base;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		wbuf_retlen = jffs2_fill_wbuf(c, v, vlen);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		if (c->wbuf_len == c->wbuf_pagesize) {
86662306a36Sopenharmony_ci			ret = __jffs2_flush_wbuf(c, NOPAD);
86762306a36Sopenharmony_ci			if (ret)
86862306a36Sopenharmony_ci				goto outerr;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci		vlen -= wbuf_retlen;
87162306a36Sopenharmony_ci		outvec_to += wbuf_retlen;
87262306a36Sopenharmony_ci		donelen += wbuf_retlen;
87362306a36Sopenharmony_ci		v += wbuf_retlen;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		if (vlen >= c->wbuf_pagesize) {
87662306a36Sopenharmony_ci			ret = mtd_write(c->mtd, outvec_to, PAGE_DIV(vlen),
87762306a36Sopenharmony_ci					&wbuf_retlen, v);
87862306a36Sopenharmony_ci			if (ret < 0 || wbuf_retlen != PAGE_DIV(vlen))
87962306a36Sopenharmony_ci				goto outfile;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci			vlen -= wbuf_retlen;
88262306a36Sopenharmony_ci			outvec_to += wbuf_retlen;
88362306a36Sopenharmony_ci			c->wbuf_ofs = outvec_to;
88462306a36Sopenharmony_ci			donelen += wbuf_retlen;
88562306a36Sopenharmony_ci			v += wbuf_retlen;
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci		wbuf_retlen = jffs2_fill_wbuf(c, v, vlen);
88962306a36Sopenharmony_ci		if (c->wbuf_len == c->wbuf_pagesize) {
89062306a36Sopenharmony_ci			ret = __jffs2_flush_wbuf(c, NOPAD);
89162306a36Sopenharmony_ci			if (ret)
89262306a36Sopenharmony_ci				goto outerr;
89362306a36Sopenharmony_ci		}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		outvec_to += wbuf_retlen;
89662306a36Sopenharmony_ci		donelen += wbuf_retlen;
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	/*
90062306a36Sopenharmony_ci	 * If there's a remainder in the wbuf and it's a non-GC write,
90162306a36Sopenharmony_ci	 * remember that the wbuf affects this ino
90262306a36Sopenharmony_ci	 */
90362306a36Sopenharmony_ci	*retlen = donelen;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (jffs2_sum_active()) {
90662306a36Sopenharmony_ci		int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to);
90762306a36Sopenharmony_ci		if (res)
90862306a36Sopenharmony_ci			return res;
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if (c->wbuf_len && ino)
91262306a36Sopenharmony_ci		jffs2_wbuf_dirties_inode(c, ino);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	ret = 0;
91562306a36Sopenharmony_ci	up_write(&c->wbuf_sem);
91662306a36Sopenharmony_ci	return ret;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cioutfile:
91962306a36Sopenharmony_ci	/*
92062306a36Sopenharmony_ci	 * At this point we have no problem, c->wbuf is empty. However
92162306a36Sopenharmony_ci	 * refile nextblock to avoid writing again to same address.
92262306a36Sopenharmony_ci	 */
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	spin_lock(&c->erase_completion_lock);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	jeb = &c->blocks[outvec_to / c->sector_size];
92762306a36Sopenharmony_ci	jffs2_block_refile(c, jeb, REFILE_ANYWAY);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ciouterr:
93262306a36Sopenharmony_ci	*retlen = 0;
93362306a36Sopenharmony_ci	up_write(&c->wbuf_sem);
93462306a36Sopenharmony_ci	return ret;
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/*
93862306a36Sopenharmony_ci *	This is the entry for flash write.
93962306a36Sopenharmony_ci *	Check, if we work on NAND FLASH, if so build an kvec and write it via vritev
94062306a36Sopenharmony_ci*/
94162306a36Sopenharmony_ciint jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
94262306a36Sopenharmony_ci		      size_t *retlen, const u_char *buf)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct kvec vecs[1];
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (!jffs2_is_writebuffered(c))
94762306a36Sopenharmony_ci		return jffs2_flash_direct_write(c, ofs, len, retlen, buf);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	vecs[0].iov_base = (unsigned char *) buf;
95062306a36Sopenharmony_ci	vecs[0].iov_len = len;
95162306a36Sopenharmony_ci	return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0);
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci/*
95562306a36Sopenharmony_ci	Handle readback from writebuffer and ECC failure return
95662306a36Sopenharmony_ci*/
95762306a36Sopenharmony_ciint jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	loff_t	orbf = 0, owbf = 0, lwbf = 0;
96062306a36Sopenharmony_ci	int	ret;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (!jffs2_is_writebuffered(c))
96362306a36Sopenharmony_ci		return mtd_read(c->mtd, ofs, len, retlen, buf);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	/* Read flash */
96662306a36Sopenharmony_ci	down_read(&c->wbuf_sem);
96762306a36Sopenharmony_ci	ret = mtd_read(c->mtd, ofs, len, retlen, buf);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) {
97062306a36Sopenharmony_ci		if (ret == -EBADMSG)
97162306a36Sopenharmony_ci			pr_warn("mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
97262306a36Sopenharmony_ci				len, ofs);
97362306a36Sopenharmony_ci		/*
97462306a36Sopenharmony_ci		 * We have the raw data without ECC correction in the buffer,
97562306a36Sopenharmony_ci		 * maybe we are lucky and all data or parts are correct. We
97662306a36Sopenharmony_ci		 * check the node.  If data are corrupted node check will sort
97762306a36Sopenharmony_ci		 * it out.  We keep this block, it will fail on write or erase
97862306a36Sopenharmony_ci		 * and the we mark it bad. Or should we do that now? But we
97962306a36Sopenharmony_ci		 * should give him a chance.  Maybe we had a system crash or
98062306a36Sopenharmony_ci		 * power loss before the ecc write or a erase was completed.
98162306a36Sopenharmony_ci		 * So we return success. :)
98262306a36Sopenharmony_ci		 */
98362306a36Sopenharmony_ci		ret = 0;
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	/* if no writebuffer available or write buffer empty, return */
98762306a36Sopenharmony_ci	if (!c->wbuf_pagesize || !c->wbuf_len)
98862306a36Sopenharmony_ci		goto exit;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	/* if we read in a different block, return */
99162306a36Sopenharmony_ci	if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
99262306a36Sopenharmony_ci		goto exit;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (ofs >= c->wbuf_ofs) {
99562306a36Sopenharmony_ci		owbf = (ofs - c->wbuf_ofs);	/* offset in write buffer */
99662306a36Sopenharmony_ci		if (owbf > c->wbuf_len)		/* is read beyond write buffer ? */
99762306a36Sopenharmony_ci			goto exit;
99862306a36Sopenharmony_ci		lwbf = c->wbuf_len - owbf;	/* number of bytes to copy */
99962306a36Sopenharmony_ci		if (lwbf > len)
100062306a36Sopenharmony_ci			lwbf = len;
100162306a36Sopenharmony_ci	} else {
100262306a36Sopenharmony_ci		orbf = (c->wbuf_ofs - ofs);	/* offset in read buffer */
100362306a36Sopenharmony_ci		if (orbf > len)			/* is write beyond write buffer ? */
100462306a36Sopenharmony_ci			goto exit;
100562306a36Sopenharmony_ci		lwbf = len - orbf;		/* number of bytes to copy */
100662306a36Sopenharmony_ci		if (lwbf > c->wbuf_len)
100762306a36Sopenharmony_ci			lwbf = c->wbuf_len;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci	if (lwbf > 0)
101062306a36Sopenharmony_ci		memcpy(buf+orbf,c->wbuf+owbf,lwbf);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ciexit:
101362306a36Sopenharmony_ci	up_read(&c->wbuf_sem);
101462306a36Sopenharmony_ci	return ret;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci#define NR_OOB_SCAN_PAGES 4
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci/* For historical reasons we use only 8 bytes for OOB clean marker */
102062306a36Sopenharmony_ci#define OOB_CM_SIZE 8
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cistatic const struct jffs2_unknown_node oob_cleanmarker =
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	.magic = constant_cpu_to_je16(JFFS2_MAGIC_BITMASK),
102562306a36Sopenharmony_ci	.nodetype = constant_cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
102662306a36Sopenharmony_ci	.totlen = constant_cpu_to_je32(8)
102762306a36Sopenharmony_ci};
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci/*
103062306a36Sopenharmony_ci * Check, if the out of band area is empty. This function knows about the clean
103162306a36Sopenharmony_ci * marker and if it is present in OOB, treats the OOB as empty anyway.
103262306a36Sopenharmony_ci */
103362306a36Sopenharmony_ciint jffs2_check_oob_empty(struct jffs2_sb_info *c,
103462306a36Sopenharmony_ci			  struct jffs2_eraseblock *jeb, int mode)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	int i, ret;
103762306a36Sopenharmony_ci	int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
103862306a36Sopenharmony_ci	struct mtd_oob_ops ops = { };
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	ops.mode = MTD_OPS_AUTO_OOB;
104162306a36Sopenharmony_ci	ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
104262306a36Sopenharmony_ci	ops.oobbuf = c->oobbuf;
104362306a36Sopenharmony_ci	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
104462306a36Sopenharmony_ci	ops.datbuf = NULL;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	ret = mtd_read_oob(c->mtd, jeb->offset, &ops);
104762306a36Sopenharmony_ci	if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != ops.ooblen) {
104862306a36Sopenharmony_ci		pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n",
104962306a36Sopenharmony_ci		       jeb->offset, ops.ooblen, ops.oobretlen, ret);
105062306a36Sopenharmony_ci		if (!ret || mtd_is_bitflip(ret))
105162306a36Sopenharmony_ci			ret = -EIO;
105262306a36Sopenharmony_ci		return ret;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	for(i = 0; i < ops.ooblen; i++) {
105662306a36Sopenharmony_ci		if (mode && i < cmlen)
105762306a36Sopenharmony_ci			/* Yeah, we know about the cleanmarker */
105862306a36Sopenharmony_ci			continue;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci		if (ops.oobbuf[i] != 0xFF) {
106162306a36Sopenharmony_ci			jffs2_dbg(2, "Found %02x at %x in OOB for "
106262306a36Sopenharmony_ci				  "%08x\n", ops.oobbuf[i], i, jeb->offset);
106362306a36Sopenharmony_ci			return 1;
106462306a36Sopenharmony_ci		}
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	return 0;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci/*
107162306a36Sopenharmony_ci * Check for a valid cleanmarker.
107262306a36Sopenharmony_ci * Returns: 0 if a valid cleanmarker was found
107362306a36Sopenharmony_ci *	    1 if no cleanmarker was found
107462306a36Sopenharmony_ci *	    negative error code if an error occurred
107562306a36Sopenharmony_ci */
107662306a36Sopenharmony_ciint jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
107762306a36Sopenharmony_ci				 struct jffs2_eraseblock *jeb)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct mtd_oob_ops ops = { };
108062306a36Sopenharmony_ci	int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	ops.mode = MTD_OPS_AUTO_OOB;
108362306a36Sopenharmony_ci	ops.ooblen = cmlen;
108462306a36Sopenharmony_ci	ops.oobbuf = c->oobbuf;
108562306a36Sopenharmony_ci	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
108662306a36Sopenharmony_ci	ops.datbuf = NULL;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	ret = mtd_read_oob(c->mtd, jeb->offset, &ops);
108962306a36Sopenharmony_ci	if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != ops.ooblen) {
109062306a36Sopenharmony_ci		pr_err("cannot read OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n",
109162306a36Sopenharmony_ci		       jeb->offset, ops.ooblen, ops.oobretlen, ret);
109262306a36Sopenharmony_ci		if (!ret || mtd_is_bitflip(ret))
109362306a36Sopenharmony_ci			ret = -EIO;
109462306a36Sopenharmony_ci		return ret;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen);
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ciint jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
110162306a36Sopenharmony_ci				 struct jffs2_eraseblock *jeb)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	int ret;
110462306a36Sopenharmony_ci	struct mtd_oob_ops ops = { };
110562306a36Sopenharmony_ci	int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	ops.mode = MTD_OPS_AUTO_OOB;
110862306a36Sopenharmony_ci	ops.ooblen = cmlen;
110962306a36Sopenharmony_ci	ops.oobbuf = (uint8_t *)&oob_cleanmarker;
111062306a36Sopenharmony_ci	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
111162306a36Sopenharmony_ci	ops.datbuf = NULL;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	ret = mtd_write_oob(c->mtd, jeb->offset, &ops);
111462306a36Sopenharmony_ci	if (ret || ops.oobretlen != ops.ooblen) {
111562306a36Sopenharmony_ci		pr_err("cannot write OOB for EB at %08x, requested %zd bytes, read %zd bytes, error %d\n",
111662306a36Sopenharmony_ci		       jeb->offset, ops.ooblen, ops.oobretlen, ret);
111762306a36Sopenharmony_ci		if (!ret)
111862306a36Sopenharmony_ci			ret = -EIO;
111962306a36Sopenharmony_ci		return ret;
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	return 0;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci/*
112662306a36Sopenharmony_ci * On NAND we try to mark this block bad. If the block was erased more
112762306a36Sopenharmony_ci * than MAX_ERASE_FAILURES we mark it finally bad.
112862306a36Sopenharmony_ci * Don't care about failures. This block remains on the erase-pending
112962306a36Sopenharmony_ci * or badblock list as long as nobody manipulates the flash with
113062306a36Sopenharmony_ci * a bootloader or something like that.
113162306a36Sopenharmony_ci */
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ciint jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	int 	ret;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	/* if the count is < max, we try to write the counter to the 2nd page oob area */
113862306a36Sopenharmony_ci	if( ++jeb->bad_count < MAX_ERASE_FAILURES)
113962306a36Sopenharmony_ci		return 0;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	pr_warn("marking eraseblock at %08x as bad\n", bad_offset);
114262306a36Sopenharmony_ci	ret = mtd_block_markbad(c->mtd, bad_offset);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (ret) {
114562306a36Sopenharmony_ci		jffs2_dbg(1, "%s(): Write failed for block at %08x: error %d\n",
114662306a36Sopenharmony_ci			  __func__, jeb->offset, ret);
114762306a36Sopenharmony_ci		return ret;
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci	return 1;
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cistatic struct jffs2_sb_info *work_to_sb(struct work_struct *work)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct delayed_work *dwork;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	dwork = to_delayed_work(work);
115762306a36Sopenharmony_ci	return container_of(dwork, struct jffs2_sb_info, wbuf_dwork);
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic void delayed_wbuf_sync(struct work_struct *work)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	struct jffs2_sb_info *c = work_to_sb(work);
116362306a36Sopenharmony_ci	struct super_block *sb = OFNI_BS_2SFFJ(c);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	if (!sb_rdonly(sb)) {
116662306a36Sopenharmony_ci		jffs2_dbg(1, "%s()\n", __func__);
116762306a36Sopenharmony_ci		jffs2_flush_wbuf_gc(c, 0);
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_civoid jffs2_dirty_trigger(struct jffs2_sb_info *c)
117262306a36Sopenharmony_ci{
117362306a36Sopenharmony_ci	struct super_block *sb = OFNI_BS_2SFFJ(c);
117462306a36Sopenharmony_ci	unsigned long delay;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (sb_rdonly(sb))
117762306a36Sopenharmony_ci		return;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	delay = msecs_to_jiffies(dirty_writeback_interval * 10);
118062306a36Sopenharmony_ci	if (queue_delayed_work(system_long_wq, &c->wbuf_dwork, delay))
118162306a36Sopenharmony_ci		jffs2_dbg(1, "%s()\n", __func__);
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ciint jffs2_nand_flash_setup(struct jffs2_sb_info *c)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	if (!c->mtd->oobsize)
118762306a36Sopenharmony_ci		return 0;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	/* Cleanmarker is out-of-band, so inline size zero */
119062306a36Sopenharmony_ci	c->cleanmarker_size = 0;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (c->mtd->oobavail == 0) {
119362306a36Sopenharmony_ci		pr_err("inconsistent device description\n");
119462306a36Sopenharmony_ci		return -EINVAL;
119562306a36Sopenharmony_ci	}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	jffs2_dbg(1, "using OOB on NAND\n");
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	c->oobavail = c->mtd->oobavail;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/* Initialise write buffer */
120262306a36Sopenharmony_ci	init_rwsem(&c->wbuf_sem);
120362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
120462306a36Sopenharmony_ci	c->wbuf_pagesize = c->mtd->writesize;
120562306a36Sopenharmony_ci	c->wbuf_ofs = 0xFFFFFFFF;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
120862306a36Sopenharmony_ci	if (!c->wbuf)
120962306a36Sopenharmony_ci		return -ENOMEM;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	c->oobbuf = kmalloc_array(NR_OOB_SCAN_PAGES, c->oobavail, GFP_KERNEL);
121262306a36Sopenharmony_ci	if (!c->oobbuf) {
121362306a36Sopenharmony_ci		kfree(c->wbuf);
121462306a36Sopenharmony_ci		return -ENOMEM;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
121862306a36Sopenharmony_ci	c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
121962306a36Sopenharmony_ci	if (!c->wbuf_verify) {
122062306a36Sopenharmony_ci		kfree(c->oobbuf);
122162306a36Sopenharmony_ci		kfree(c->wbuf);
122262306a36Sopenharmony_ci		return -ENOMEM;
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci#endif
122562306a36Sopenharmony_ci	return 0;
122662306a36Sopenharmony_ci}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_civoid jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
122962306a36Sopenharmony_ci{
123062306a36Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
123162306a36Sopenharmony_ci	kfree(c->wbuf_verify);
123262306a36Sopenharmony_ci#endif
123362306a36Sopenharmony_ci	kfree(c->wbuf);
123462306a36Sopenharmony_ci	kfree(c->oobbuf);
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ciint jffs2_dataflash_setup(struct jffs2_sb_info *c) {
123862306a36Sopenharmony_ci	c->cleanmarker_size = 0;		/* No cleanmarkers needed */
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	/* Initialize write buffer */
124162306a36Sopenharmony_ci	init_rwsem(&c->wbuf_sem);
124262306a36Sopenharmony_ci	INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
124362306a36Sopenharmony_ci	c->wbuf_pagesize =  c->mtd->erasesize;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* Find a suitable c->sector_size
124662306a36Sopenharmony_ci	 * - Not too much sectors
124762306a36Sopenharmony_ci	 * - Sectors have to be at least 4 K + some bytes
124862306a36Sopenharmony_ci	 * - All known dataflashes have erase sizes of 528 or 1056
124962306a36Sopenharmony_ci	 * - we take at least 8 eraseblocks and want to have at least 8K size
125062306a36Sopenharmony_ci	 * - The concatenation should be a power of 2
125162306a36Sopenharmony_ci	*/
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	c->sector_size = 8 * c->mtd->erasesize;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	while (c->sector_size < 8192) {
125662306a36Sopenharmony_ci		c->sector_size *= 2;
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	/* It may be necessary to adjust the flash size */
126062306a36Sopenharmony_ci	c->flash_size = c->mtd->size;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	if ((c->flash_size % c->sector_size) != 0) {
126362306a36Sopenharmony_ci		c->flash_size = (c->flash_size / c->sector_size) * c->sector_size;
126462306a36Sopenharmony_ci		pr_warn("flash size adjusted to %dKiB\n", c->flash_size);
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	c->wbuf_ofs = 0xFFFFFFFF;
126862306a36Sopenharmony_ci	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
126962306a36Sopenharmony_ci	if (!c->wbuf)
127062306a36Sopenharmony_ci		return -ENOMEM;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
127362306a36Sopenharmony_ci	c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
127462306a36Sopenharmony_ci	if (!c->wbuf_verify) {
127562306a36Sopenharmony_ci		kfree(c->wbuf);
127662306a36Sopenharmony_ci		return -ENOMEM;
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci#endif
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	pr_info("write-buffering enabled buffer (%d) erasesize (%d)\n",
128162306a36Sopenharmony_ci		c->wbuf_pagesize, c->sector_size);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	return 0;
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_civoid jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
128762306a36Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
128862306a36Sopenharmony_ci	kfree(c->wbuf_verify);
128962306a36Sopenharmony_ci#endif
129062306a36Sopenharmony_ci	kfree(c->wbuf);
129162306a36Sopenharmony_ci}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ciint jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) {
129462306a36Sopenharmony_ci	/* Cleanmarker currently occupies whole programming regions,
129562306a36Sopenharmony_ci	 * either one or 2 for 8Byte STMicro flashes. */
129662306a36Sopenharmony_ci	c->cleanmarker_size = max(16u, c->mtd->writesize);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/* Initialize write buffer */
129962306a36Sopenharmony_ci	init_rwsem(&c->wbuf_sem);
130062306a36Sopenharmony_ci	INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	c->wbuf_pagesize = c->mtd->writesize;
130362306a36Sopenharmony_ci	c->wbuf_ofs = 0xFFFFFFFF;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
130662306a36Sopenharmony_ci	if (!c->wbuf)
130762306a36Sopenharmony_ci		return -ENOMEM;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
131062306a36Sopenharmony_ci	c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
131162306a36Sopenharmony_ci	if (!c->wbuf_verify) {
131262306a36Sopenharmony_ci		kfree(c->wbuf);
131362306a36Sopenharmony_ci		return -ENOMEM;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci#endif
131662306a36Sopenharmony_ci	return 0;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_civoid jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) {
132062306a36Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
132162306a36Sopenharmony_ci	kfree(c->wbuf_verify);
132262306a36Sopenharmony_ci#endif
132362306a36Sopenharmony_ci	kfree(c->wbuf);
132462306a36Sopenharmony_ci}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ciint jffs2_ubivol_setup(struct jffs2_sb_info *c) {
132762306a36Sopenharmony_ci	c->cleanmarker_size = 0;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (c->mtd->writesize == 1)
133062306a36Sopenharmony_ci		/* We do not need write-buffer */
133162306a36Sopenharmony_ci		return 0;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	init_rwsem(&c->wbuf_sem);
133462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	c->wbuf_pagesize =  c->mtd->writesize;
133762306a36Sopenharmony_ci	c->wbuf_ofs = 0xFFFFFFFF;
133862306a36Sopenharmony_ci	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
133962306a36Sopenharmony_ci	if (!c->wbuf)
134062306a36Sopenharmony_ci		return -ENOMEM;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	pr_info("write-buffering enabled buffer (%d) erasesize (%d)\n",
134362306a36Sopenharmony_ci		c->wbuf_pagesize, c->sector_size);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	return 0;
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_civoid jffs2_ubivol_cleanup(struct jffs2_sb_info *c) {
134962306a36Sopenharmony_ci	kfree(c->wbuf);
135062306a36Sopenharmony_ci}
1351