xref: /openbmc/linux/fs/jffs2/wbuf.c (revision 0029da3b)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * JFFS2 -- Journalling Flash File System, Version 2.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 2001-2003 Red Hat, Inc.
51da177e4SLinus Torvalds  * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Created by David Woodhouse <dwmw2@infradead.org>
81da177e4SLinus Torvalds  * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * For licensing information, see the file 'LICENCE' in this directory.
111da177e4SLinus Torvalds  *
12daba5cc4SArtem B. Bityutskiy  * $Id: wbuf.c,v 1.100 2005/09/30 13:59:13 dedekind Exp $
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/slab.h>
181da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
191da177e4SLinus Torvalds #include <linux/crc32.h>
201da177e4SLinus Torvalds #include <linux/mtd/nand.h>
214e57b681STim Schmielau #include <linux/jiffies.h>
22914e2637SAl Viro #include <linux/sched.h>
234e57b681STim Schmielau 
241da177e4SLinus Torvalds #include "nodelist.h"
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds /* For testing write failures */
271da177e4SLinus Torvalds #undef BREAKME
281da177e4SLinus Torvalds #undef BREAKMEHEADER
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #ifdef BREAKME
311da177e4SLinus Torvalds static unsigned char *brokenbuf;
321da177e4SLinus Torvalds #endif
331da177e4SLinus Torvalds 
34daba5cc4SArtem B. Bityutskiy #define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
35daba5cc4SArtem B. Bityutskiy #define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
36daba5cc4SArtem B. Bityutskiy 
371da177e4SLinus Torvalds /* max. erase failures before we mark a block bad */
381da177e4SLinus Torvalds #define MAX_ERASE_FAILURES 	2
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds struct jffs2_inodirty {
411da177e4SLinus Torvalds 	uint32_t ino;
421da177e4SLinus Torvalds 	struct jffs2_inodirty *next;
431da177e4SLinus Torvalds };
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static struct jffs2_inodirty inodirty_nomem;
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
481da177e4SLinus Torvalds {
491da177e4SLinus Torvalds 	struct jffs2_inodirty *this = c->wbuf_inodes;
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	/* If a malloc failed, consider _everything_ dirty */
521da177e4SLinus Torvalds 	if (this == &inodirty_nomem)
531da177e4SLinus Torvalds 		return 1;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	/* If ino == 0, _any_ non-GC writes mean 'yes' */
561da177e4SLinus Torvalds 	if (this && !ino)
571da177e4SLinus Torvalds 		return 1;
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds 	/* Look to see if the inode in question is pending in the wbuf */
601da177e4SLinus Torvalds 	while (this) {
611da177e4SLinus Torvalds 		if (this->ino == ino)
621da177e4SLinus Torvalds 			return 1;
631da177e4SLinus Torvalds 		this = this->next;
641da177e4SLinus Torvalds 	}
651da177e4SLinus Torvalds 	return 0;
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds 	struct jffs2_inodirty *this;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	this = c->wbuf_inodes;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	if (this != &inodirty_nomem) {
751da177e4SLinus Torvalds 		while (this) {
761da177e4SLinus Torvalds 			struct jffs2_inodirty *next = this->next;
771da177e4SLinus Torvalds 			kfree(this);
781da177e4SLinus Torvalds 			this = next;
791da177e4SLinus Torvalds 		}
801da177e4SLinus Torvalds 	}
811da177e4SLinus Torvalds 	c->wbuf_inodes = NULL;
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
851da177e4SLinus Torvalds {
861da177e4SLinus Torvalds 	struct jffs2_inodirty *new;
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	/* Mark the superblock dirty so that kupdated will flush... */
894d952709SArtem B. Bityuckiy 	jffs2_erase_pending_trigger(c);
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 	if (jffs2_wbuf_pending_for_ino(c, ino))
921da177e4SLinus Torvalds 		return;
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	new = kmalloc(sizeof(*new), GFP_KERNEL);
951da177e4SLinus Torvalds 	if (!new) {
961da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n"));
971da177e4SLinus Torvalds 		jffs2_clear_wbuf_ino_list(c);
981da177e4SLinus Torvalds 		c->wbuf_inodes = &inodirty_nomem;
991da177e4SLinus Torvalds 		return;
1001da177e4SLinus Torvalds 	}
1011da177e4SLinus Torvalds 	new->ino = ino;
1021da177e4SLinus Torvalds 	new->next = c->wbuf_inodes;
1031da177e4SLinus Torvalds 	c->wbuf_inodes = new;
1041da177e4SLinus Torvalds 	return;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds 	struct list_head *this, *next;
1101da177e4SLinus Torvalds 	static int n;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	if (list_empty(&c->erasable_pending_wbuf_list))
1131da177e4SLinus Torvalds 		return;
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
1161da177e4SLinus Torvalds 		struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset));
1191da177e4SLinus Torvalds 		list_del(this);
1201da177e4SLinus Torvalds 		if ((jiffies + (n++)) & 127) {
1211da177e4SLinus Torvalds 			/* Most of the time, we just erase it immediately. Otherwise we
1221da177e4SLinus Torvalds 			   spend ages scanning it on mount, etc. */
1231da177e4SLinus Torvalds 			D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
1241da177e4SLinus Torvalds 			list_add_tail(&jeb->list, &c->erase_pending_list);
1251da177e4SLinus Torvalds 			c->nr_erasing_blocks++;
1261da177e4SLinus Torvalds 			jffs2_erase_pending_trigger(c);
1271da177e4SLinus Torvalds 		} else {
1281da177e4SLinus Torvalds 			/* Sometimes, however, we leave it elsewhere so it doesn't get
1291da177e4SLinus Torvalds 			   immediately reused, and we spread the load a bit. */
1301da177e4SLinus Torvalds 			D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
1311da177e4SLinus Torvalds 			list_add_tail(&jeb->list, &c->erasable_list);
1321da177e4SLinus Torvalds 		}
1331da177e4SLinus Torvalds 	}
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
1367f716cf3SEstelle Hammache #define REFILE_NOTEMPTY 0
1377f716cf3SEstelle Hammache #define REFILE_ANYWAY   1
1387f716cf3SEstelle Hammache 
1397f716cf3SEstelle Hammache static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds 	D1(printk("About to refile bad block at %08x\n", jeb->offset));
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	/* File the existing block on the bad_used_list.... */
1441da177e4SLinus Torvalds 	if (c->nextblock == jeb)
1451da177e4SLinus Torvalds 		c->nextblock = NULL;
1461da177e4SLinus Torvalds 	else /* Not sure this should ever happen... need more coffee */
1471da177e4SLinus Torvalds 		list_del(&jeb->list);
1481da177e4SLinus Torvalds 	if (jeb->first_node) {
1491da177e4SLinus Torvalds 		D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
1501da177e4SLinus Torvalds 		list_add(&jeb->list, &c->bad_used_list);
1511da177e4SLinus Torvalds 	} else {
1529b88f473SEstelle Hammache 		BUG_ON(allow_empty == REFILE_NOTEMPTY);
1531da177e4SLinus Torvalds 		/* It has to have had some nodes or we couldn't be here */
1541da177e4SLinus Torvalds 		D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
1551da177e4SLinus Torvalds 		list_add(&jeb->list, &c->erase_pending_list);
1561da177e4SLinus Torvalds 		c->nr_erasing_blocks++;
1571da177e4SLinus Torvalds 		jffs2_erase_pending_trigger(c);
1581da177e4SLinus Torvalds 	}
1591da177e4SLinus Torvalds 
1609bfeb691SDavid Woodhouse 	if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) {
1619bfeb691SDavid Woodhouse 		uint32_t oldfree = jeb->free_size;
1629bfeb691SDavid Woodhouse 
1639bfeb691SDavid Woodhouse 		jffs2_link_node_ref(c, jeb,
1649bfeb691SDavid Woodhouse 				    (jeb->offset+c->sector_size-oldfree) | REF_OBSOLETE,
1659bfeb691SDavid Woodhouse 				    oldfree, NULL);
1669bfeb691SDavid Woodhouse 		/* convert to wasted */
1679bfeb691SDavid Woodhouse 		c->wasted_size += oldfree;
1689bfeb691SDavid Woodhouse 		jeb->wasted_size += oldfree;
1699bfeb691SDavid Woodhouse 		c->dirty_size -= oldfree;
1709bfeb691SDavid Woodhouse 		jeb->dirty_size -= oldfree;
1719bfeb691SDavid Woodhouse 	}
1721da177e4SLinus Torvalds 
173e0c8e42fSArtem B. Bityutskiy 	jffs2_dbg_dump_block_lists_nolock(c);
174e0c8e42fSArtem B. Bityutskiy 	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
175e0c8e42fSArtem B. Bityutskiy 	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
1761da177e4SLinus Torvalds }
1771da177e4SLinus Torvalds 
1789bfeb691SDavid Woodhouse static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info *c,
1799bfeb691SDavid Woodhouse 							    struct jffs2_inode_info *f,
1809bfeb691SDavid Woodhouse 							    struct jffs2_raw_node_ref *raw,
1819bfeb691SDavid Woodhouse 							    union jffs2_node_union *node)
1829bfeb691SDavid Woodhouse {
1839bfeb691SDavid Woodhouse 	struct jffs2_node_frag *frag;
1849bfeb691SDavid Woodhouse 	struct jffs2_full_dirent *fd;
1859bfeb691SDavid Woodhouse 
1869bfeb691SDavid Woodhouse 	dbg_noderef("incore_replace_raw: node at %p is {%04x,%04x}\n",
1879bfeb691SDavid Woodhouse 		    node, je16_to_cpu(node->u.magic), je16_to_cpu(node->u.nodetype));
1889bfeb691SDavid Woodhouse 
1899bfeb691SDavid Woodhouse 	BUG_ON(je16_to_cpu(node->u.magic) != 0x1985 &&
1909bfeb691SDavid Woodhouse 	       je16_to_cpu(node->u.magic) != 0);
1919bfeb691SDavid Woodhouse 
1929bfeb691SDavid Woodhouse 	switch (je16_to_cpu(node->u.nodetype)) {
1939bfeb691SDavid Woodhouse 	case JFFS2_NODETYPE_INODE:
194ddc58bd6SDavid Woodhouse 		if (f->metadata && f->metadata->raw == raw) {
195ddc58bd6SDavid Woodhouse 			dbg_noderef("Will replace ->raw in f->metadata at %p\n", f->metadata);
196ddc58bd6SDavid Woodhouse 			return &f->metadata->raw;
197ddc58bd6SDavid Woodhouse 		}
1989bfeb691SDavid Woodhouse 		frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset));
1999bfeb691SDavid Woodhouse 		BUG_ON(!frag);
2009bfeb691SDavid Woodhouse 		/* Find a frag which refers to the full_dnode we want to modify */
2019bfeb691SDavid Woodhouse 		while (!frag->node || frag->node->raw != raw) {
2029bfeb691SDavid Woodhouse 			frag = frag_next(frag);
2039bfeb691SDavid Woodhouse 			BUG_ON(!frag);
2049bfeb691SDavid Woodhouse 		}
2059bfeb691SDavid Woodhouse 		dbg_noderef("Will replace ->raw in full_dnode at %p\n", frag->node);
2069bfeb691SDavid Woodhouse 		return &frag->node->raw;
2079bfeb691SDavid Woodhouse 
2089bfeb691SDavid Woodhouse 	case JFFS2_NODETYPE_DIRENT:
2099bfeb691SDavid Woodhouse 		for (fd = f->dents; fd; fd = fd->next) {
2109bfeb691SDavid Woodhouse 			if (fd->raw == raw) {
2119bfeb691SDavid Woodhouse 				dbg_noderef("Will replace ->raw in full_dirent at %p\n", fd);
2129bfeb691SDavid Woodhouse 				return &fd->raw;
2139bfeb691SDavid Woodhouse 			}
2149bfeb691SDavid Woodhouse 		}
2159bfeb691SDavid Woodhouse 		BUG();
216ddc58bd6SDavid Woodhouse 
2179bfeb691SDavid Woodhouse 	default:
2189bfeb691SDavid Woodhouse 		dbg_noderef("Don't care about replacing raw for nodetype %x\n",
2199bfeb691SDavid Woodhouse 			    je16_to_cpu(node->u.nodetype));
2209bfeb691SDavid Woodhouse 		break;
2219bfeb691SDavid Woodhouse 	}
2229bfeb691SDavid Woodhouse 	return NULL;
2239bfeb691SDavid Woodhouse }
2249bfeb691SDavid Woodhouse 
2251da177e4SLinus Torvalds /* Recover from failure to write wbuf. Recover the nodes up to the
2261da177e4SLinus Torvalds  * wbuf, not the one which we were starting to try to write. */
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
2291da177e4SLinus Torvalds {
2301da177e4SLinus Torvalds 	struct jffs2_eraseblock *jeb, *new_jeb;
2319bfeb691SDavid Woodhouse 	struct jffs2_raw_node_ref *raw, *next, *first_raw = NULL;
2321da177e4SLinus Torvalds 	size_t retlen;
2331da177e4SLinus Torvalds 	int ret;
2349bfeb691SDavid Woodhouse 	int nr_refile = 0;
2351da177e4SLinus Torvalds 	unsigned char *buf;
2361da177e4SLinus Torvalds 	uint32_t start, end, ofs, len;
2371da177e4SLinus Torvalds 
238046b8b98SDavid Woodhouse 	jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
239046b8b98SDavid Woodhouse 
2401da177e4SLinus Torvalds 	spin_lock(&c->erase_completion_lock);
241180bfb31SVitaly Wool 	if (c->wbuf_ofs % c->mtd->erasesize)
2427f716cf3SEstelle Hammache 		jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
243180bfb31SVitaly Wool 	else
244180bfb31SVitaly Wool 		jffs2_block_refile(c, jeb, REFILE_ANYWAY);
2459bfeb691SDavid Woodhouse 	spin_unlock(&c->erase_completion_lock);
2469bfeb691SDavid Woodhouse 
2479bfeb691SDavid Woodhouse 	BUG_ON(!ref_obsolete(jeb->last_node));
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	/* Find the first node to be recovered, by skipping over every
2501da177e4SLinus Torvalds 	   node which ends before the wbuf starts, or which is obsolete. */
2519bfeb691SDavid Woodhouse 	for (next = raw = jeb->first_node; next; raw = next) {
2529bfeb691SDavid Woodhouse 		next = ref_next(raw);
2539bfeb691SDavid Woodhouse 
2549bfeb691SDavid Woodhouse 		if (ref_obsolete(raw) ||
2559bfeb691SDavid Woodhouse 		    (next && ref_offset(next) <= c->wbuf_ofs)) {
2569bfeb691SDavid Woodhouse 			dbg_noderef("Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
2579bfeb691SDavid Woodhouse 				    ref_offset(raw), ref_flags(raw),
2589bfeb691SDavid Woodhouse 				    (ref_offset(raw) + ref_totlen(c, jeb, raw)),
2599bfeb691SDavid Woodhouse 				    c->wbuf_ofs);
2609bfeb691SDavid Woodhouse 			continue;
2619bfeb691SDavid Woodhouse 		}
2629bfeb691SDavid Woodhouse 		dbg_noderef("First node to be recovered is at 0x%08x(%d)-0x%08x\n",
2639bfeb691SDavid Woodhouse 			    ref_offset(raw), ref_flags(raw),
2649bfeb691SDavid Woodhouse 			    (ref_offset(raw) + ref_totlen(c, jeb, raw)));
2659bfeb691SDavid Woodhouse 
2669bfeb691SDavid Woodhouse 		first_raw = raw;
2679bfeb691SDavid Woodhouse 		break;
2681da177e4SLinus Torvalds 	}
2691da177e4SLinus Torvalds 
2709bfeb691SDavid Woodhouse 	if (!first_raw) {
2711da177e4SLinus Torvalds 		/* All nodes were obsolete. Nothing to recover. */
2721da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
2739bfeb691SDavid Woodhouse 		c->wbuf_len = 0;
2741da177e4SLinus Torvalds 		return;
2751da177e4SLinus Torvalds 	}
2761da177e4SLinus Torvalds 
2779bfeb691SDavid Woodhouse 	start = ref_offset(first_raw);
2789bfeb691SDavid Woodhouse 	end = ref_offset(jeb->last_node);
2799bfeb691SDavid Woodhouse 	nr_refile = 1;
2801da177e4SLinus Torvalds 
2819bfeb691SDavid Woodhouse 	/* Count the number of refs which need to be copied */
2829bfeb691SDavid Woodhouse 	while ((raw = ref_next(raw)) != jeb->last_node)
2839bfeb691SDavid Woodhouse 		nr_refile++;
2841da177e4SLinus Torvalds 
2859bfeb691SDavid Woodhouse 	dbg_noderef("wbuf recover %08x-%08x (%d bytes in %d nodes)\n",
2869bfeb691SDavid Woodhouse 		    start, end, end - start, nr_refile);
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	buf = NULL;
2891da177e4SLinus Torvalds 	if (start < c->wbuf_ofs) {
2901da177e4SLinus Torvalds 		/* First affected node was already partially written.
2911da177e4SLinus Torvalds 		 * Attempt to reread the old data into our buffer. */
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 		buf = kmalloc(end - start, GFP_KERNEL);
2941da177e4SLinus Torvalds 		if (!buf) {
2951da177e4SLinus Torvalds 			printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 			goto read_failed;
2981da177e4SLinus Torvalds 		}
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 		/* Do the read... */
3011da177e4SLinus Torvalds 		ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
3021da177e4SLinus Torvalds 
3039a1fcdfdSThomas Gleixner 		/* ECC recovered ? */
3049a1fcdfdSThomas Gleixner 		if ((ret == -EUCLEAN || ret == -EBADMSG) &&
3059a1fcdfdSThomas Gleixner 		    (retlen == c->wbuf_ofs - start))
3061da177e4SLinus Torvalds 			ret = 0;
3079a1fcdfdSThomas Gleixner 
3081da177e4SLinus Torvalds 		if (ret || retlen != c->wbuf_ofs - start) {
3091da177e4SLinus Torvalds 			printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 			kfree(buf);
3121da177e4SLinus Torvalds 			buf = NULL;
3131da177e4SLinus Torvalds 		read_failed:
3149bfeb691SDavid Woodhouse 			first_raw = ref_next(first_raw);
3159bfeb691SDavid Woodhouse 			nr_refile--;
3169bfeb691SDavid Woodhouse 			while (first_raw && ref_obsolete(first_raw)) {
3179bfeb691SDavid Woodhouse 				first_raw = ref_next(first_raw);
3189bfeb691SDavid Woodhouse 				nr_refile--;
3199bfeb691SDavid Woodhouse 			}
3209bfeb691SDavid Woodhouse 
3211da177e4SLinus Torvalds 			/* If this was the only node to be recovered, give up */
3229bfeb691SDavid Woodhouse 			if (!first_raw) {
3239bfeb691SDavid Woodhouse 				c->wbuf_len = 0;
3241da177e4SLinus Torvalds 				return;
3259bfeb691SDavid Woodhouse 			}
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds 			/* It wasn't. Go on and try to recover nodes complete in the wbuf */
3289bfeb691SDavid Woodhouse 			start = ref_offset(first_raw);
3299bfeb691SDavid Woodhouse 			dbg_noderef("wbuf now recover %08x-%08x (%d bytes in %d nodes)\n",
3309bfeb691SDavid Woodhouse 				    start, end, end - start, nr_refile);
3319bfeb691SDavid Woodhouse 
3321da177e4SLinus Torvalds 		} else {
3331da177e4SLinus Torvalds 			/* Read succeeded. Copy the remaining data from the wbuf */
3341da177e4SLinus Torvalds 			memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
3351da177e4SLinus Torvalds 		}
3361da177e4SLinus Torvalds 	}
3371da177e4SLinus Torvalds 	/* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
3381da177e4SLinus Torvalds 	   Either 'buf' contains the data, or we find it in the wbuf */
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	/* ... and get an allocation of space from a shiny new block instead */
3419fe4854cSDavid Woodhouse 	ret = jffs2_reserve_space_gc(c, end-start, &len, JFFS2_SUMMARY_NOSUM_SIZE);
3421da177e4SLinus Torvalds 	if (ret) {
3431da177e4SLinus Torvalds 		printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
3441da177e4SLinus Torvalds 		kfree(buf);
3451da177e4SLinus Torvalds 		return;
3461da177e4SLinus Torvalds 	}
3479bfeb691SDavid Woodhouse 
3489bfeb691SDavid Woodhouse 	ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile);
3499bfeb691SDavid Woodhouse 	if (ret) {
3509bfeb691SDavid Woodhouse 		printk(KERN_WARNING "Failed to allocate node refs for wbuf recovery. Data loss ensues.\n");
3519bfeb691SDavid Woodhouse 		kfree(buf);
3529bfeb691SDavid Woodhouse 		return;
3539bfeb691SDavid Woodhouse 	}
3549bfeb691SDavid Woodhouse 
3559fe4854cSDavid Woodhouse 	ofs = write_ofs(c);
3569fe4854cSDavid Woodhouse 
3571da177e4SLinus Torvalds 	if (end-start >= c->wbuf_pagesize) {
3587f716cf3SEstelle Hammache 		/* Need to do another write immediately, but it's possible
3597f716cf3SEstelle Hammache 		   that this is just because the wbuf itself is completely
3607f716cf3SEstelle Hammache 		   full, and there's nothing earlier read back from the
3617f716cf3SEstelle Hammache 		   flash. Hence 'buf' isn't necessarily what we're writing
3627f716cf3SEstelle Hammache 		   from. */
3637f716cf3SEstelle Hammache 		unsigned char *rewrite_buf = buf?:c->wbuf;
3641da177e4SLinus Torvalds 		uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
3671da177e4SLinus Torvalds 			  towrite, ofs));
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds #ifdef BREAKMEHEADER
3701da177e4SLinus Torvalds 		static int breakme;
3711da177e4SLinus Torvalds 		if (breakme++ == 20) {
3721da177e4SLinus Torvalds 			printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs);
3731da177e4SLinus Torvalds 			breakme = 0;
3749223a456SThomas Gleixner 			c->mtd->write(c->mtd, ofs, towrite, &retlen,
3759223a456SThomas Gleixner 				      brokenbuf);
3761da177e4SLinus Torvalds 			ret = -EIO;
3771da177e4SLinus Torvalds 		} else
3781da177e4SLinus Torvalds #endif
3799223a456SThomas Gleixner 			ret = c->mtd->write(c->mtd, ofs, towrite, &retlen,
3809223a456SThomas Gleixner 					    rewrite_buf);
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 		if (ret || retlen != towrite) {
3831da177e4SLinus Torvalds 			/* Argh. We tried. Really we did. */
3841da177e4SLinus Torvalds 			printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
3851da177e4SLinus Torvalds 			kfree(buf);
3861da177e4SLinus Torvalds 
3872f785402SDavid Woodhouse 			if (retlen)
3889bfeb691SDavid Woodhouse 				jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, first_raw), NULL);
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 			return;
3911da177e4SLinus Torvalds 		}
3921da177e4SLinus Torvalds 		printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 		c->wbuf_len = (end - start) - towrite;
3951da177e4SLinus Torvalds 		c->wbuf_ofs = ofs + towrite;
3967f716cf3SEstelle Hammache 		memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
3971da177e4SLinus Torvalds 		/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
3981da177e4SLinus Torvalds 	} else {
3991da177e4SLinus Torvalds 		/* OK, now we're left with the dregs in whichever buffer we're using */
4001da177e4SLinus Torvalds 		if (buf) {
4011da177e4SLinus Torvalds 			memcpy(c->wbuf, buf, end-start);
4021da177e4SLinus Torvalds 		} else {
4031da177e4SLinus Torvalds 			memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
4041da177e4SLinus Torvalds 		}
4051da177e4SLinus Torvalds 		c->wbuf_ofs = ofs;
4061da177e4SLinus Torvalds 		c->wbuf_len = end - start;
4071da177e4SLinus Torvalds 	}
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	/* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
4101da177e4SLinus Torvalds 	new_jeb = &c->blocks[ofs / c->sector_size];
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	spin_lock(&c->erase_completion_lock);
4139bfeb691SDavid Woodhouse 	for (raw = first_raw; raw != jeb->last_node; raw = ref_next(raw)) {
4149bfeb691SDavid Woodhouse 		uint32_t rawlen = ref_totlen(c, jeb, raw);
4159bfeb691SDavid Woodhouse 		struct jffs2_inode_cache *ic;
4169bfeb691SDavid Woodhouse 		struct jffs2_raw_node_ref *new_ref;
4179bfeb691SDavid Woodhouse 		struct jffs2_raw_node_ref **adjust_ref = NULL;
4189bfeb691SDavid Woodhouse 		struct jffs2_inode_info *f = NULL;
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
4219bfeb691SDavid Woodhouse 			  rawlen, ref_offset(raw), ref_flags(raw), ofs));
4221da177e4SLinus Torvalds 
4239bfeb691SDavid Woodhouse 		ic = jffs2_raw_ref_to_ic(raw);
4249bfeb691SDavid Woodhouse 
4259bfeb691SDavid Woodhouse 		/* Ick. This XATTR mess should be fixed shortly... */
4269bfeb691SDavid Woodhouse 		if (ic && ic->class == RAWNODE_CLASS_XATTR_DATUM) {
4279bfeb691SDavid Woodhouse 			struct jffs2_xattr_datum *xd = (void *)ic;
4289bfeb691SDavid Woodhouse 			BUG_ON(xd->node != raw);
4299bfeb691SDavid Woodhouse 			adjust_ref = &xd->node;
4309bfeb691SDavid Woodhouse 			raw->next_in_ino = NULL;
4319bfeb691SDavid Woodhouse 			ic = NULL;
4329bfeb691SDavid Woodhouse 		} else if (ic && ic->class == RAWNODE_CLASS_XATTR_REF) {
4339bfeb691SDavid Woodhouse 			struct jffs2_xattr_datum *xr = (void *)ic;
4349bfeb691SDavid Woodhouse 			BUG_ON(xr->node != raw);
4359bfeb691SDavid Woodhouse 			adjust_ref = &xr->node;
4369bfeb691SDavid Woodhouse 			raw->next_in_ino = NULL;
4379bfeb691SDavid Woodhouse 			ic = NULL;
4389bfeb691SDavid Woodhouse 		} else if (ic && ic->class == RAWNODE_CLASS_INODE_CACHE) {
4399bfeb691SDavid Woodhouse 			struct jffs2_raw_node_ref **p = &ic->nodes;
4409bfeb691SDavid Woodhouse 
4419bfeb691SDavid Woodhouse 			/* Remove the old node from the per-inode list */
4429bfeb691SDavid Woodhouse 			while (*p && *p != (void *)ic) {
4439bfeb691SDavid Woodhouse 				if (*p == raw) {
4449bfeb691SDavid Woodhouse 					(*p) = (raw->next_in_ino);
4459bfeb691SDavid Woodhouse 					raw->next_in_ino = NULL;
4469bfeb691SDavid Woodhouse 					break;
4479bfeb691SDavid Woodhouse 				}
4489bfeb691SDavid Woodhouse 				p = &((*p)->next_in_ino);
4499bfeb691SDavid Woodhouse 			}
4509bfeb691SDavid Woodhouse 
4519bfeb691SDavid Woodhouse 			if (ic->state == INO_STATE_PRESENT && !ref_obsolete(raw)) {
4529bfeb691SDavid Woodhouse 				/* If it's an in-core inode, then we have to adjust any
4539bfeb691SDavid Woodhouse 				   full_dirent or full_dnode structure to point to the
4549bfeb691SDavid Woodhouse 				   new version instead of the old */
4559bfeb691SDavid Woodhouse 				f = jffs2_gc_fetch_inode(c, ic->ino, ic->nlink);
4569bfeb691SDavid Woodhouse 				if (IS_ERR(f)) {
4579bfeb691SDavid Woodhouse 					/* Should never happen; it _must_ be present */
4589bfeb691SDavid Woodhouse 					JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n",
4599bfeb691SDavid Woodhouse 						    ic->ino, PTR_ERR(f));
4609bfeb691SDavid Woodhouse 					BUG();
4619bfeb691SDavid Woodhouse 				}
4629bfeb691SDavid Woodhouse 				/* We don't lock f->sem. There's a number of ways we could
4639bfeb691SDavid Woodhouse 				   end up in here with it already being locked, and nobody's
4649bfeb691SDavid Woodhouse 				   going to modify it on us anyway because we hold the
4659bfeb691SDavid Woodhouse 				   alloc_sem. We're only changing one ->raw pointer too,
4669bfeb691SDavid Woodhouse 				   which we can get away with without upsetting readers. */
4679bfeb691SDavid Woodhouse 				adjust_ref = jffs2_incore_replace_raw(c, f, raw,
4689bfeb691SDavid Woodhouse 								      (void *)(buf?:c->wbuf) + (ref_offset(raw) - start));
4699bfeb691SDavid Woodhouse 			} else if (unlikely(ic->state != INO_STATE_PRESENT &&
4709bfeb691SDavid Woodhouse 					    ic->state != INO_STATE_CHECKEDABSENT &&
4719bfeb691SDavid Woodhouse 					    ic->state != INO_STATE_GC)) {
4729bfeb691SDavid Woodhouse 				JFFS2_ERROR("Inode #%u is in strange state %d!\n", ic->ino, ic->state);
4739bfeb691SDavid Woodhouse 				BUG();
4749bfeb691SDavid Woodhouse 			}
4759bfeb691SDavid Woodhouse 		}
4769bfeb691SDavid Woodhouse 
4779bfeb691SDavid Woodhouse 		new_ref = jffs2_link_node_ref(c, new_jeb, ofs | ref_flags(raw), rawlen, ic);
4789bfeb691SDavid Woodhouse 
4799bfeb691SDavid Woodhouse 		if (adjust_ref) {
4809bfeb691SDavid Woodhouse 			BUG_ON(*adjust_ref != raw);
4819bfeb691SDavid Woodhouse 			*adjust_ref = new_ref;
4829bfeb691SDavid Woodhouse 		}
4839bfeb691SDavid Woodhouse 		if (f)
4849bfeb691SDavid Woodhouse 			jffs2_gc_release_inode(c, f);
4859bfeb691SDavid Woodhouse 
4869bfeb691SDavid Woodhouse 		if (!ref_obsolete(raw)) {
4871da177e4SLinus Torvalds 			jeb->dirty_size += rawlen;
4881da177e4SLinus Torvalds 			jeb->used_size  -= rawlen;
4891da177e4SLinus Torvalds 			c->dirty_size += rawlen;
4909bfeb691SDavid Woodhouse 			c->used_size -= rawlen;
4919bfeb691SDavid Woodhouse 			raw->flash_offset = ref_offset(raw) | REF_OBSOLETE;
4929bfeb691SDavid Woodhouse 			BUG_ON(raw->next_in_ino);
4931da177e4SLinus Torvalds 		}
4941da177e4SLinus Torvalds 		ofs += rawlen;
4951da177e4SLinus Torvalds 	}
4961da177e4SLinus Torvalds 
4979bfeb691SDavid Woodhouse 	kfree(buf);
4989bfeb691SDavid Woodhouse 
4991da177e4SLinus Torvalds 	/* Fix up the original jeb now it's on the bad_list */
5009bfeb691SDavid Woodhouse 	if (first_raw == jeb->first_node) {
5011da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
502f116629dSAkinobu Mita 		list_move(&jeb->list, &c->erase_pending_list);
5031da177e4SLinus Torvalds 		c->nr_erasing_blocks++;
5041da177e4SLinus Torvalds 		jffs2_erase_pending_trigger(c);
5051da177e4SLinus Torvalds 	}
5061da177e4SLinus Torvalds 
507e0c8e42fSArtem B. Bityutskiy 	jffs2_dbg_acct_sanity_check_nolock(c, jeb);
508e0c8e42fSArtem B. Bityutskiy 	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
5091da177e4SLinus Torvalds 
510e0c8e42fSArtem B. Bityutskiy 	jffs2_dbg_acct_sanity_check_nolock(c, new_jeb);
511e0c8e42fSArtem B. Bityutskiy 	jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb);
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds 	spin_unlock(&c->erase_completion_lock);
5141da177e4SLinus Torvalds 
5159bfeb691SDavid Woodhouse 	D1(printk(KERN_DEBUG "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n", c->wbuf_ofs, c->wbuf_len));
5169bfeb691SDavid Woodhouse 
5171da177e4SLinus Torvalds }
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds /* Meaning of pad argument:
5201da177e4SLinus Torvalds    0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
5211da177e4SLinus Torvalds    1: Pad, do not adjust nextblock free_size
5221da177e4SLinus Torvalds    2: Pad, adjust nextblock free_size
5231da177e4SLinus Torvalds */
5241da177e4SLinus Torvalds #define NOPAD		0
5251da177e4SLinus Torvalds #define PAD_NOACCOUNT	1
5261da177e4SLinus Torvalds #define PAD_ACCOUNTING	2
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
5291da177e4SLinus Torvalds {
5309bfeb691SDavid Woodhouse 	struct jffs2_eraseblock *wbuf_jeb;
5311da177e4SLinus Torvalds 	int ret;
5321da177e4SLinus Torvalds 	size_t retlen;
5331da177e4SLinus Torvalds 
5343be36675SAndrew Victor 	/* Nothing to do if not write-buffering the flash. In particular, we shouldn't
5351da177e4SLinus Torvalds 	   del_timer() the timer we never initialised. */
5363be36675SAndrew Victor 	if (!jffs2_is_writebuffered(c))
5371da177e4SLinus Torvalds 		return 0;
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds 	if (!down_trylock(&c->alloc_sem)) {
5401da177e4SLinus Torvalds 		up(&c->alloc_sem);
5411da177e4SLinus Torvalds 		printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
5421da177e4SLinus Torvalds 		BUG();
5431da177e4SLinus Torvalds 	}
5441da177e4SLinus Torvalds 
5453be36675SAndrew Victor 	if (!c->wbuf_len)	/* already checked c->wbuf above */
5461da177e4SLinus Torvalds 		return 0;
5471da177e4SLinus Torvalds 
5489bfeb691SDavid Woodhouse 	wbuf_jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
5499bfeb691SDavid Woodhouse 	if (jffs2_prealloc_raw_node_refs(c, wbuf_jeb, c->nextblock->allocated_refs + 1))
5502f785402SDavid Woodhouse 		return -ENOMEM;
5512f785402SDavid Woodhouse 
5521da177e4SLinus Torvalds 	/* claim remaining space on the page
5531da177e4SLinus Torvalds 	   this happens, if we have a change to a new block,
5541da177e4SLinus Torvalds 	   or if fsync forces us to flush the writebuffer.
5551da177e4SLinus Torvalds 	   if we have a switch to next page, we will not have
5561da177e4SLinus Torvalds 	   enough remaining space for this.
5571da177e4SLinus Torvalds 	*/
558daba5cc4SArtem B. Bityutskiy 	if (pad ) {
5591da177e4SLinus Torvalds 		c->wbuf_len = PAD(c->wbuf_len);
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 		/* Pad with JFFS2_DIRTY_BITMASK initially.  this helps out ECC'd NOR
5621da177e4SLinus Torvalds 		   with 8 byte page size */
5631da177e4SLinus Torvalds 		memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 		if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
5661da177e4SLinus Torvalds 			struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
5671da177e4SLinus Torvalds 			padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
5681da177e4SLinus Torvalds 			padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
5691da177e4SLinus Torvalds 			padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
5701da177e4SLinus Torvalds 			padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
5711da177e4SLinus Torvalds 		}
5721da177e4SLinus Torvalds 	}
5731da177e4SLinus Torvalds 	/* else jffs2_flash_writev has actually filled in the rest of the
5741da177e4SLinus Torvalds 	   buffer for us, and will deal with the node refs etc. later. */
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds #ifdef BREAKME
5771da177e4SLinus Torvalds 	static int breakme;
5781da177e4SLinus Torvalds 	if (breakme++ == 20) {
5791da177e4SLinus Torvalds 		printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs);
5801da177e4SLinus Torvalds 		breakme = 0;
5819223a456SThomas Gleixner 		c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen,
5829223a456SThomas Gleixner 			      brokenbuf);
5831da177e4SLinus Torvalds 		ret = -EIO;
5841da177e4SLinus Torvalds 	} else
5851da177e4SLinus Torvalds #endif
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 		ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	if (ret || retlen != c->wbuf_pagesize) {
5901da177e4SLinus Torvalds 		if (ret)
5911da177e4SLinus Torvalds 			printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
5921da177e4SLinus Torvalds 		else {
5931da177e4SLinus Torvalds 			printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
5941da177e4SLinus Torvalds 				retlen, c->wbuf_pagesize);
5951da177e4SLinus Torvalds 			ret = -EIO;
5961da177e4SLinus Torvalds 		}
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds 		jffs2_wbuf_recover(c);
5991da177e4SLinus Torvalds 
6001da177e4SLinus Torvalds 		return ret;
6011da177e4SLinus Torvalds 	}
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	/* Adjust free size of the block if we padded. */
604daba5cc4SArtem B. Bityutskiy 	if (pad) {
6050bcc099dSDavid Woodhouse 		uint32_t waste = c->wbuf_pagesize - c->wbuf_len;
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
6089bfeb691SDavid Woodhouse 			  (wbuf_jeb==c->nextblock)?"next":"", wbuf_jeb->offset));
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds 		/* wbuf_pagesize - wbuf_len is the amount of space that's to be
6111da177e4SLinus Torvalds 		   padded. If there is less free space in the block than that,
6121da177e4SLinus Torvalds 		   something screwed up */
6139bfeb691SDavid Woodhouse 		if (wbuf_jeb->free_size < waste) {
6141da177e4SLinus Torvalds 			printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
6150bcc099dSDavid Woodhouse 			       c->wbuf_ofs, c->wbuf_len, waste);
6161da177e4SLinus Torvalds 			printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
6179bfeb691SDavid Woodhouse 			       wbuf_jeb->offset, wbuf_jeb->free_size);
6181da177e4SLinus Torvalds 			BUG();
6191da177e4SLinus Torvalds 		}
6200bcc099dSDavid Woodhouse 
6210bcc099dSDavid Woodhouse 		spin_lock(&c->erase_completion_lock);
6220bcc099dSDavid Woodhouse 
6239bfeb691SDavid Woodhouse 		jffs2_link_node_ref(c, wbuf_jeb, (c->wbuf_ofs + c->wbuf_len) | REF_OBSOLETE, waste, NULL);
6240bcc099dSDavid Woodhouse 		/* FIXME: that made it count as dirty. Convert to wasted */
6259bfeb691SDavid Woodhouse 		wbuf_jeb->dirty_size -= waste;
6260bcc099dSDavid Woodhouse 		c->dirty_size -= waste;
6279bfeb691SDavid Woodhouse 		wbuf_jeb->wasted_size += waste;
6280bcc099dSDavid Woodhouse 		c->wasted_size += waste;
6290bcc099dSDavid Woodhouse 	} else
6300bcc099dSDavid Woodhouse 		spin_lock(&c->erase_completion_lock);
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds 	/* Stick any now-obsoleted blocks on the erase_pending_list */
6331da177e4SLinus Torvalds 	jffs2_refile_wbuf_blocks(c);
6341da177e4SLinus Torvalds 	jffs2_clear_wbuf_ino_list(c);
6351da177e4SLinus Torvalds 	spin_unlock(&c->erase_completion_lock);
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 	memset(c->wbuf,0xff,c->wbuf_pagesize);
6381da177e4SLinus Torvalds 	/* adjust write buffer offset, else we get a non contiguous write bug */
6391da177e4SLinus Torvalds 	c->wbuf_ofs += c->wbuf_pagesize;
6401da177e4SLinus Torvalds 	c->wbuf_len = 0;
6411da177e4SLinus Torvalds 	return 0;
6421da177e4SLinus Torvalds }
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds /* Trigger garbage collection to flush the write-buffer.
6451da177e4SLinus Torvalds    If ino arg is zero, do it if _any_ real (i.e. not GC) writes are
6461da177e4SLinus Torvalds    outstanding. If ino arg non-zero, do it only if a write for the
6471da177e4SLinus Torvalds    given inode is outstanding. */
6481da177e4SLinus Torvalds int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
6491da177e4SLinus Torvalds {
6501da177e4SLinus Torvalds 	uint32_t old_wbuf_ofs;
6511da177e4SLinus Torvalds 	uint32_t old_wbuf_len;
6521da177e4SLinus Torvalds 	int ret = 0;
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
6551da177e4SLinus Torvalds 
6568aee6ac1SDavid Woodhouse 	if (!c->wbuf)
6578aee6ac1SDavid Woodhouse 		return 0;
6588aee6ac1SDavid Woodhouse 
6591da177e4SLinus Torvalds 	down(&c->alloc_sem);
6601da177e4SLinus Torvalds 	if (!jffs2_wbuf_pending_for_ino(c, ino)) {
6611da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
6621da177e4SLinus Torvalds 		up(&c->alloc_sem);
6631da177e4SLinus Torvalds 		return 0;
6641da177e4SLinus Torvalds 	}
6651da177e4SLinus Torvalds 
6661da177e4SLinus Torvalds 	old_wbuf_ofs = c->wbuf_ofs;
6671da177e4SLinus Torvalds 	old_wbuf_len = c->wbuf_len;
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	if (c->unchecked_size) {
6701da177e4SLinus Torvalds 		/* GC won't make any progress for a while */
6711da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
6721da177e4SLinus Torvalds 		down_write(&c->wbuf_sem);
6731da177e4SLinus Torvalds 		ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
6747f716cf3SEstelle Hammache 		/* retry flushing wbuf in case jffs2_wbuf_recover
6757f716cf3SEstelle Hammache 		   left some data in the wbuf */
6767f716cf3SEstelle Hammache 		if (ret)
6777f716cf3SEstelle Hammache 			ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
6781da177e4SLinus Torvalds 		up_write(&c->wbuf_sem);
6791da177e4SLinus Torvalds 	} else while (old_wbuf_len &&
6801da177e4SLinus Torvalds 		      old_wbuf_ofs == c->wbuf_ofs) {
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds 		up(&c->alloc_sem);
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 		D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
6851da177e4SLinus Torvalds 
6861da177e4SLinus Torvalds 		ret = jffs2_garbage_collect_pass(c);
6871da177e4SLinus Torvalds 		if (ret) {
6881da177e4SLinus Torvalds 			/* GC failed. Flush it with padding instead */
6891da177e4SLinus Torvalds 			down(&c->alloc_sem);
6901da177e4SLinus Torvalds 			down_write(&c->wbuf_sem);
6911da177e4SLinus Torvalds 			ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
6927f716cf3SEstelle Hammache 			/* retry flushing wbuf in case jffs2_wbuf_recover
6937f716cf3SEstelle Hammache 			   left some data in the wbuf */
6947f716cf3SEstelle Hammache 			if (ret)
6957f716cf3SEstelle Hammache 				ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
6961da177e4SLinus Torvalds 			up_write(&c->wbuf_sem);
6971da177e4SLinus Torvalds 			break;
6981da177e4SLinus Torvalds 		}
6991da177e4SLinus Torvalds 		down(&c->alloc_sem);
7001da177e4SLinus Torvalds 	}
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds 	up(&c->alloc_sem);
7051da177e4SLinus Torvalds 	return ret;
7061da177e4SLinus Torvalds }
7071da177e4SLinus Torvalds 
7081da177e4SLinus Torvalds /* Pad write-buffer to end and write it, wasting space. */
7091da177e4SLinus Torvalds int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
7101da177e4SLinus Torvalds {
7111da177e4SLinus Torvalds 	int ret;
7121da177e4SLinus Torvalds 
7138aee6ac1SDavid Woodhouse 	if (!c->wbuf)
7148aee6ac1SDavid Woodhouse 		return 0;
7158aee6ac1SDavid Woodhouse 
7161da177e4SLinus Torvalds 	down_write(&c->wbuf_sem);
7171da177e4SLinus Torvalds 	ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
7187f716cf3SEstelle Hammache 	/* retry - maybe wbuf recover left some data in wbuf. */
7197f716cf3SEstelle Hammache 	if (ret)
7207f716cf3SEstelle Hammache 		ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
7211da177e4SLinus Torvalds 	up_write(&c->wbuf_sem);
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 	return ret;
7241da177e4SLinus Torvalds }
7251da177e4SLinus Torvalds 
726dcb09328SThomas Gleixner static size_t jffs2_fill_wbuf(struct jffs2_sb_info *c, const uint8_t *buf,
727dcb09328SThomas Gleixner 			      size_t len)
728dcb09328SThomas Gleixner {
729dcb09328SThomas Gleixner 	if (len && !c->wbuf_len && (len >= c->wbuf_pagesize))
730dcb09328SThomas Gleixner 		return 0;
731dcb09328SThomas Gleixner 
732dcb09328SThomas Gleixner 	if (len > (c->wbuf_pagesize - c->wbuf_len))
733dcb09328SThomas Gleixner 		len = c->wbuf_pagesize - c->wbuf_len;
734dcb09328SThomas Gleixner 	memcpy(c->wbuf + c->wbuf_len, buf, len);
735dcb09328SThomas Gleixner 	c->wbuf_len += (uint32_t) len;
736dcb09328SThomas Gleixner 	return len;
737dcb09328SThomas Gleixner }
738dcb09328SThomas Gleixner 
739dcb09328SThomas Gleixner int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs,
740dcb09328SThomas Gleixner 		       unsigned long count, loff_t to, size_t *retlen,
741dcb09328SThomas Gleixner 		       uint32_t ino)
742dcb09328SThomas Gleixner {
743dcb09328SThomas Gleixner 	struct jffs2_eraseblock *jeb;
744dcb09328SThomas Gleixner 	size_t wbuf_retlen, donelen = 0;
745dcb09328SThomas Gleixner 	uint32_t outvec_to = to;
746dcb09328SThomas Gleixner 	int ret, invec;
747dcb09328SThomas Gleixner 
748dcb09328SThomas Gleixner 	/* If not writebuffered flash, don't bother */
7493be36675SAndrew Victor 	if (!jffs2_is_writebuffered(c))
7501da177e4SLinus Torvalds 		return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 	down_write(&c->wbuf_sem);
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds 	/* If wbuf_ofs is not initialized, set it to target address */
7551da177e4SLinus Torvalds 	if (c->wbuf_ofs == 0xFFFFFFFF) {
7561da177e4SLinus Torvalds 		c->wbuf_ofs = PAGE_DIV(to);
7571da177e4SLinus Torvalds 		c->wbuf_len = PAGE_MOD(to);
7581da177e4SLinus Torvalds 		memset(c->wbuf,0xff,c->wbuf_pagesize);
7591da177e4SLinus Torvalds 	}
7601da177e4SLinus Torvalds 
761dcb09328SThomas Gleixner 	/*
762dcb09328SThomas Gleixner 	 * Sanity checks on target address.  It's permitted to write
763dcb09328SThomas Gleixner 	 * at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to
764dcb09328SThomas Gleixner 	 * write at the beginning of a new erase block. Anything else,
765dcb09328SThomas Gleixner 	 * and you die.  New block starts at xxx000c (0-b = block
766dcb09328SThomas Gleixner 	 * header)
7671da177e4SLinus Torvalds 	 */
7683be36675SAndrew Victor 	if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
7691da177e4SLinus Torvalds 		/* It's a write to a new block */
7701da177e4SLinus Torvalds 		if (c->wbuf_len) {
771dcb09328SThomas Gleixner 			D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx "
772dcb09328SThomas Gleixner 				  "causes flush of wbuf at 0x%08x\n",
773dcb09328SThomas Gleixner 				  (unsigned long)to, c->wbuf_ofs));
7741da177e4SLinus Torvalds 			ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
775dcb09328SThomas Gleixner 			if (ret)
776dcb09328SThomas Gleixner 				goto outerr;
7771da177e4SLinus Torvalds 		}
7781da177e4SLinus Torvalds 		/* set pointer to new block */
7791da177e4SLinus Torvalds 		c->wbuf_ofs = PAGE_DIV(to);
7801da177e4SLinus Torvalds 		c->wbuf_len = PAGE_MOD(to);
7811da177e4SLinus Torvalds 	}
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
7841da177e4SLinus Torvalds 		/* We're not writing immediately after the writebuffer. Bad. */
785dcb09328SThomas Gleixner 		printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write "
786dcb09328SThomas Gleixner 		       "to %08lx\n", (unsigned long)to);
7871da177e4SLinus Torvalds 		if (c->wbuf_len)
7881da177e4SLinus Torvalds 			printk(KERN_CRIT "wbuf was previously %08x-%08x\n",
7891da177e4SLinus Torvalds 			       c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len);
7901da177e4SLinus Torvalds 		BUG();
7911da177e4SLinus Torvalds 	}
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 	/* adjust alignment offset */
7941da177e4SLinus Torvalds 	if (c->wbuf_len != PAGE_MOD(to)) {
7951da177e4SLinus Torvalds 		c->wbuf_len = PAGE_MOD(to);
7961da177e4SLinus Torvalds 		/* take care of alignment to next page */
797dcb09328SThomas Gleixner 		if (!c->wbuf_len) {
7981da177e4SLinus Torvalds 			c->wbuf_len = c->wbuf_pagesize;
7991da177e4SLinus Torvalds 			ret = __jffs2_flush_wbuf(c, NOPAD);
800dcb09328SThomas Gleixner 			if (ret)
801dcb09328SThomas Gleixner 				goto outerr;
8021da177e4SLinus Torvalds 		}
8031da177e4SLinus Torvalds 	}
8041da177e4SLinus Torvalds 
805dcb09328SThomas Gleixner 	for (invec = 0; invec < count; invec++) {
806dcb09328SThomas Gleixner 		int vlen = invecs[invec].iov_len;
807dcb09328SThomas Gleixner 		uint8_t *v = invecs[invec].iov_base;
8081da177e4SLinus Torvalds 
809dcb09328SThomas Gleixner 		wbuf_retlen = jffs2_fill_wbuf(c, v, vlen);
8101da177e4SLinus Torvalds 
811dcb09328SThomas Gleixner 		if (c->wbuf_len == c->wbuf_pagesize) {
812dcb09328SThomas Gleixner 			ret = __jffs2_flush_wbuf(c, NOPAD);
813dcb09328SThomas Gleixner 			if (ret)
814dcb09328SThomas Gleixner 				goto outerr;
8151da177e4SLinus Torvalds 		}
816dcb09328SThomas Gleixner 		vlen -= wbuf_retlen;
817dcb09328SThomas Gleixner 		outvec_to += wbuf_retlen;
8181da177e4SLinus Torvalds 		donelen += wbuf_retlen;
819dcb09328SThomas Gleixner 		v += wbuf_retlen;
8201da177e4SLinus Torvalds 
821dcb09328SThomas Gleixner 		if (vlen >= c->wbuf_pagesize) {
822dcb09328SThomas Gleixner 			ret = c->mtd->write(c->mtd, outvec_to, PAGE_DIV(vlen),
823dcb09328SThomas Gleixner 					    &wbuf_retlen, v);
824dcb09328SThomas Gleixner 			if (ret < 0 || wbuf_retlen != PAGE_DIV(vlen))
825dcb09328SThomas Gleixner 				goto outfile;
826dcb09328SThomas Gleixner 
827dcb09328SThomas Gleixner 			vlen -= wbuf_retlen;
828dcb09328SThomas Gleixner 			outvec_to += wbuf_retlen;
829dcb09328SThomas Gleixner 			c->wbuf_ofs = outvec_to;
830dcb09328SThomas Gleixner 			donelen += wbuf_retlen;
831dcb09328SThomas Gleixner 			v += wbuf_retlen;
8321da177e4SLinus Torvalds 		}
8331da177e4SLinus Torvalds 
834dcb09328SThomas Gleixner 		wbuf_retlen = jffs2_fill_wbuf(c, v, vlen);
835dcb09328SThomas Gleixner 		if (c->wbuf_len == c->wbuf_pagesize) {
836dcb09328SThomas Gleixner 			ret = __jffs2_flush_wbuf(c, NOPAD);
837dcb09328SThomas Gleixner 			if (ret)
838dcb09328SThomas Gleixner 				goto outerr;
8391da177e4SLinus Torvalds 		}
8401da177e4SLinus Torvalds 
841dcb09328SThomas Gleixner 		outvec_to += wbuf_retlen;
842dcb09328SThomas Gleixner 		donelen += wbuf_retlen;
8431da177e4SLinus Torvalds 	}
8441da177e4SLinus Torvalds 
845dcb09328SThomas Gleixner 	/*
846dcb09328SThomas Gleixner 	 * If there's a remainder in the wbuf and it's a non-GC write,
847dcb09328SThomas Gleixner 	 * remember that the wbuf affects this ino
848dcb09328SThomas Gleixner 	 */
8491da177e4SLinus Torvalds 	*retlen = donelen;
8501da177e4SLinus Torvalds 
851e631ddbaSFerenc Havasi 	if (jffs2_sum_active()) {
852e631ddbaSFerenc Havasi 		int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to);
853e631ddbaSFerenc Havasi 		if (res)
854e631ddbaSFerenc Havasi 			return res;
855e631ddbaSFerenc Havasi 	}
856e631ddbaSFerenc Havasi 
8571da177e4SLinus Torvalds 	if (c->wbuf_len && ino)
8581da177e4SLinus Torvalds 		jffs2_wbuf_dirties_inode(c, ino);
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds 	ret = 0;
861dcb09328SThomas Gleixner 	up_write(&c->wbuf_sem);
862dcb09328SThomas Gleixner 	return ret;
8631da177e4SLinus Torvalds 
864dcb09328SThomas Gleixner outfile:
865dcb09328SThomas Gleixner 	/*
866dcb09328SThomas Gleixner 	 * At this point we have no problem, c->wbuf is empty. However
867dcb09328SThomas Gleixner 	 * refile nextblock to avoid writing again to same address.
868dcb09328SThomas Gleixner 	 */
869dcb09328SThomas Gleixner 
870dcb09328SThomas Gleixner 	spin_lock(&c->erase_completion_lock);
871dcb09328SThomas Gleixner 
872dcb09328SThomas Gleixner 	jeb = &c->blocks[outvec_to / c->sector_size];
873dcb09328SThomas Gleixner 	jffs2_block_refile(c, jeb, REFILE_ANYWAY);
874dcb09328SThomas Gleixner 
875dcb09328SThomas Gleixner 	spin_unlock(&c->erase_completion_lock);
876dcb09328SThomas Gleixner 
877dcb09328SThomas Gleixner outerr:
878dcb09328SThomas Gleixner 	*retlen = 0;
8791da177e4SLinus Torvalds 	up_write(&c->wbuf_sem);
8801da177e4SLinus Torvalds 	return ret;
8811da177e4SLinus Torvalds }
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds /*
8841da177e4SLinus Torvalds  *	This is the entry for flash write.
8851da177e4SLinus Torvalds  *	Check, if we work on NAND FLASH, if so build an kvec and write it via vritev
8861da177e4SLinus Torvalds */
8879bfeb691SDavid Woodhouse int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
8889bfeb691SDavid Woodhouse 		      size_t *retlen, const u_char *buf)
8891da177e4SLinus Torvalds {
8901da177e4SLinus Torvalds 	struct kvec vecs[1];
8911da177e4SLinus Torvalds 
8923be36675SAndrew Victor 	if (!jffs2_is_writebuffered(c))
893e631ddbaSFerenc Havasi 		return jffs2_flash_direct_write(c, ofs, len, retlen, buf);
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 	vecs[0].iov_base = (unsigned char *) buf;
8961da177e4SLinus Torvalds 	vecs[0].iov_len = len;
8971da177e4SLinus Torvalds 	return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0);
8981da177e4SLinus Torvalds }
8991da177e4SLinus Torvalds 
9001da177e4SLinus Torvalds /*
9011da177e4SLinus Torvalds 	Handle readback from writebuffer and ECC failure return
9021da177e4SLinus Torvalds */
9031da177e4SLinus Torvalds int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
9041da177e4SLinus Torvalds {
9051da177e4SLinus Torvalds 	loff_t	orbf = 0, owbf = 0, lwbf = 0;
9061da177e4SLinus Torvalds 	int	ret;
9071da177e4SLinus Torvalds 
9083be36675SAndrew Victor 	if (!jffs2_is_writebuffered(c))
9093be36675SAndrew Victor 		return c->mtd->read(c->mtd, ofs, len, retlen, buf);
9101da177e4SLinus Torvalds 
9113be36675SAndrew Victor 	/* Read flash */
912894214d1SArtem B. Bityuckiy 	down_read(&c->wbuf_sem);
9131da177e4SLinus Torvalds 	ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
9141da177e4SLinus Torvalds 
9159a1fcdfdSThomas Gleixner 	if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) {
9169a1fcdfdSThomas Gleixner 		if (ret == -EBADMSG)
9179a1fcdfdSThomas Gleixner 			printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx)"
9189a1fcdfdSThomas Gleixner 			       " returned ECC error\n", len, ofs);
9191da177e4SLinus Torvalds 		/*
9209a1fcdfdSThomas Gleixner 		 * We have the raw data without ECC correction in the buffer,
9219a1fcdfdSThomas Gleixner 		 * maybe we are lucky and all data or parts are correct. We
9229a1fcdfdSThomas Gleixner 		 * check the node.  If data are corrupted node check will sort
9239a1fcdfdSThomas Gleixner 		 * it out.  We keep this block, it will fail on write or erase
9249a1fcdfdSThomas Gleixner 		 * and the we mark it bad. Or should we do that now? But we
9259a1fcdfdSThomas Gleixner 		 * should give him a chance.  Maybe we had a system crash or
9269a1fcdfdSThomas Gleixner 		 * power loss before the ecc write or a erase was completed.
9271da177e4SLinus Torvalds 		 * So we return success. :)
9281da177e4SLinus Torvalds 		 */
9291da177e4SLinus Torvalds 		ret = 0;
9301da177e4SLinus Torvalds 	}
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds 	/* if no writebuffer available or write buffer empty, return */
9331da177e4SLinus Torvalds 	if (!c->wbuf_pagesize || !c->wbuf_len)
934894214d1SArtem B. Bityuckiy 		goto exit;
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	/* if we read in a different block, return */
9373be36675SAndrew Victor 	if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
938894214d1SArtem B. Bityuckiy 		goto exit;
9391da177e4SLinus Torvalds 
9401da177e4SLinus Torvalds 	if (ofs >= c->wbuf_ofs) {
9411da177e4SLinus Torvalds 		owbf = (ofs - c->wbuf_ofs);	/* offset in write buffer */
9421da177e4SLinus Torvalds 		if (owbf > c->wbuf_len)		/* is read beyond write buffer ? */
9431da177e4SLinus Torvalds 			goto exit;
9441da177e4SLinus Torvalds 		lwbf = c->wbuf_len - owbf;	/* number of bytes to copy */
9451da177e4SLinus Torvalds 		if (lwbf > len)
9461da177e4SLinus Torvalds 			lwbf = len;
9471da177e4SLinus Torvalds 	} else {
9481da177e4SLinus Torvalds 		orbf = (c->wbuf_ofs - ofs);	/* offset in read buffer */
9491da177e4SLinus Torvalds 		if (orbf > len)			/* is write beyond write buffer ? */
9501da177e4SLinus Torvalds 			goto exit;
9511da177e4SLinus Torvalds 		lwbf = len - orbf;		/* number of bytes to copy */
9521da177e4SLinus Torvalds 		if (lwbf > c->wbuf_len)
9531da177e4SLinus Torvalds 			lwbf = c->wbuf_len;
9541da177e4SLinus Torvalds 	}
9551da177e4SLinus Torvalds 	if (lwbf > 0)
9561da177e4SLinus Torvalds 		memcpy(buf+orbf,c->wbuf+owbf,lwbf);
9571da177e4SLinus Torvalds 
9581da177e4SLinus Torvalds exit:
9591da177e4SLinus Torvalds 	up_read(&c->wbuf_sem);
9601da177e4SLinus Torvalds 	return ret;
9611da177e4SLinus Torvalds }
9621da177e4SLinus Torvalds 
9638593fbc6SThomas Gleixner #define NR_OOB_SCAN_PAGES 4
9648593fbc6SThomas Gleixner 
965a7a6ace1SArtem Bityutskiy /* For historical reasons we use only 12 bytes for OOB clean marker */
966a7a6ace1SArtem Bityutskiy #define OOB_CM_SIZE 12
967a7a6ace1SArtem Bityutskiy 
968a7a6ace1SArtem Bityutskiy static const struct jffs2_unknown_node oob_cleanmarker =
969a7a6ace1SArtem Bityutskiy {
970a7a6ace1SArtem Bityutskiy 	.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
971a7a6ace1SArtem Bityutskiy 	.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
972a7a6ace1SArtem Bityutskiy 	.totlen = cpu_to_je32(8)
973a7a6ace1SArtem Bityutskiy };
974a7a6ace1SArtem Bityutskiy 
9751da177e4SLinus Torvalds /*
976a7a6ace1SArtem Bityutskiy  * Check, if the out of band area is empty. This function knows about the clean
977a7a6ace1SArtem Bityutskiy  * marker and if it is present in OOB, treats the OOB as empty anyway.
9781da177e4SLinus Torvalds  */
9798593fbc6SThomas Gleixner int jffs2_check_oob_empty(struct jffs2_sb_info *c,
9808593fbc6SThomas Gleixner 			  struct jffs2_eraseblock *jeb, int mode)
9811da177e4SLinus Torvalds {
982a7a6ace1SArtem Bityutskiy 	int i, ret;
983a7a6ace1SArtem Bityutskiy 	int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
9848593fbc6SThomas Gleixner 	struct mtd_oob_ops ops;
9851da177e4SLinus Torvalds 
986a7a6ace1SArtem Bityutskiy 	ops.mode = MTD_OOB_AUTO;
987a7a6ace1SArtem Bityutskiy 	ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
9888593fbc6SThomas Gleixner 	ops.oobbuf = c->oobbuf;
989a7a6ace1SArtem Bityutskiy 	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
9908593fbc6SThomas Gleixner 	ops.datbuf = NULL;
9918593fbc6SThomas Gleixner 
9928593fbc6SThomas Gleixner 	ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
993a7a6ace1SArtem Bityutskiy 	if (ret || ops.oobretlen != ops.ooblen) {
9947be26bfbSAndrew Morton 		printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
9957be26bfbSAndrew Morton 				" bytes, read %zd bytes, error %d\n",
9967be26bfbSAndrew Morton 				jeb->offset, ops.ooblen, ops.oobretlen, ret);
997a7a6ace1SArtem Bityutskiy 		if (!ret)
998a7a6ace1SArtem Bityutskiy 			ret = -EIO;
9998593fbc6SThomas Gleixner 		return ret;
10001da177e4SLinus Torvalds 	}
10011da177e4SLinus Torvalds 
1002a7a6ace1SArtem Bityutskiy 	for(i = 0; i < ops.ooblen; i++) {
1003a7a6ace1SArtem Bityutskiy 		if (mode && i < cmlen)
1004a7a6ace1SArtem Bityutskiy 			/* Yeah, we know about the cleanmarker */
10051da177e4SLinus Torvalds 			continue;
10061da177e4SLinus Torvalds 
10078593fbc6SThomas Gleixner 		if (ops.oobbuf[i] != 0xFF) {
10088593fbc6SThomas Gleixner 			D2(printk(KERN_DEBUG "Found %02x at %x in OOB for "
10098593fbc6SThomas Gleixner 				  "%08x\n", ops.oobbuf[i], i, jeb->offset));
10108593fbc6SThomas Gleixner 			return 1;
10111da177e4SLinus Torvalds 		}
10121da177e4SLinus Torvalds 	}
10131da177e4SLinus Torvalds 
10148593fbc6SThomas Gleixner 	return 0;
10151da177e4SLinus Torvalds }
10161da177e4SLinus Torvalds 
10171da177e4SLinus Torvalds /*
1018a7a6ace1SArtem Bityutskiy  * Check for a valid cleanmarker.
1019a7a6ace1SArtem Bityutskiy  * Returns: 0 if a valid cleanmarker was found
1020a7a6ace1SArtem Bityutskiy  *          1 if no cleanmarker was found
1021a7a6ace1SArtem Bityutskiy  *          negative error code if an error occurred
10221da177e4SLinus Torvalds  */
10238593fbc6SThomas Gleixner int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
10248593fbc6SThomas Gleixner 				 struct jffs2_eraseblock *jeb)
10251da177e4SLinus Torvalds {
10268593fbc6SThomas Gleixner 	struct mtd_oob_ops ops;
1027a7a6ace1SArtem Bityutskiy 	int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
10281da177e4SLinus Torvalds 
1029a7a6ace1SArtem Bityutskiy 	ops.mode = MTD_OOB_AUTO;
1030a7a6ace1SArtem Bityutskiy 	ops.ooblen = cmlen;
10318593fbc6SThomas Gleixner 	ops.oobbuf = c->oobbuf;
1032a7a6ace1SArtem Bityutskiy 	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
10338593fbc6SThomas Gleixner 	ops.datbuf = NULL;
10348593fbc6SThomas Gleixner 
1035a7a6ace1SArtem Bityutskiy 	ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
1036a7a6ace1SArtem Bityutskiy 	if (ret || ops.oobretlen != ops.ooblen) {
10377be26bfbSAndrew Morton 		printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
10387be26bfbSAndrew Morton 				" bytes, read %zd bytes, error %d\n",
10397be26bfbSAndrew Morton 				jeb->offset, ops.ooblen, ops.oobretlen, ret);
1040a7a6ace1SArtem Bityutskiy 		if (!ret)
1041a7a6ace1SArtem Bityutskiy 			ret = -EIO;
10421da177e4SLinus Torvalds 		return ret;
10431da177e4SLinus Torvalds 	}
10448593fbc6SThomas Gleixner 
1045a7a6ace1SArtem Bityutskiy 	return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen);
10461da177e4SLinus Torvalds }
10471da177e4SLinus Torvalds 
10488593fbc6SThomas Gleixner int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
10498593fbc6SThomas Gleixner 				 struct jffs2_eraseblock *jeb)
10501da177e4SLinus Torvalds {
10511da177e4SLinus Torvalds 	int ret;
10528593fbc6SThomas Gleixner 	struct mtd_oob_ops ops;
1053a7a6ace1SArtem Bityutskiy 	int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
10541da177e4SLinus Torvalds 
1055a7a6ace1SArtem Bityutskiy 	ops.mode = MTD_OOB_AUTO;
1056a7a6ace1SArtem Bityutskiy 	ops.ooblen = cmlen;
1057a7a6ace1SArtem Bityutskiy 	ops.oobbuf = (uint8_t *)&oob_cleanmarker;
1058a7a6ace1SArtem Bityutskiy 	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
10598593fbc6SThomas Gleixner 	ops.datbuf = NULL;
10608593fbc6SThomas Gleixner 
10618593fbc6SThomas Gleixner 	ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops);
1062a7a6ace1SArtem Bityutskiy 	if (ret || ops.oobretlen != ops.ooblen) {
10637be26bfbSAndrew Morton 		printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd"
10647be26bfbSAndrew Morton 				" bytes, read %zd bytes, error %d\n",
10657be26bfbSAndrew Morton 				jeb->offset, ops.ooblen, ops.oobretlen, ret);
1066a7a6ace1SArtem Bityutskiy 		if (!ret)
1067a7a6ace1SArtem Bityutskiy 			ret = -EIO;
10681da177e4SLinus Torvalds 		return ret;
10691da177e4SLinus Torvalds 	}
1070a7a6ace1SArtem Bityutskiy 
10711da177e4SLinus Torvalds 	return 0;
10721da177e4SLinus Torvalds }
10731da177e4SLinus Torvalds 
10741da177e4SLinus Torvalds /*
10751da177e4SLinus Torvalds  * On NAND we try to mark this block bad. If the block was erased more
10761da177e4SLinus Torvalds  * than MAX_ERASE_FAILURES we mark it finaly bad.
10771da177e4SLinus Torvalds  * Don't care about failures. This block remains on the erase-pending
10781da177e4SLinus Torvalds  * or badblock list as long as nobody manipulates the flash with
10791da177e4SLinus Torvalds  * a bootloader or something like that.
10801da177e4SLinus Torvalds  */
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
10831da177e4SLinus Torvalds {
10841da177e4SLinus Torvalds 	int 	ret;
10851da177e4SLinus Torvalds 
10861da177e4SLinus Torvalds 	/* if the count is < max, we try to write the counter to the 2nd page oob area */
10871da177e4SLinus Torvalds 	if( ++jeb->bad_count < MAX_ERASE_FAILURES)
10881da177e4SLinus Torvalds 		return 0;
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds 	if (!c->mtd->block_markbad)
10911da177e4SLinus Torvalds 		return 1; // What else can we do?
10921da177e4SLinus Torvalds 
10930feba829SArtem Bityutskiy 	printk(KERN_WARNING "JFFS2: marking eraseblock at %08x\n as bad", bad_offset);
10941da177e4SLinus Torvalds 	ret = c->mtd->block_markbad(c->mtd, bad_offset);
10951da177e4SLinus Torvalds 
10961da177e4SLinus Torvalds 	if (ret) {
10971da177e4SLinus Torvalds 		D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
10981da177e4SLinus Torvalds 		return ret;
10991da177e4SLinus Torvalds 	}
11001da177e4SLinus Torvalds 	return 1;
11011da177e4SLinus Torvalds }
11021da177e4SLinus Torvalds 
1103a7a6ace1SArtem Bityutskiy int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
11041da177e4SLinus Torvalds {
11055bd34c09SThomas Gleixner 	struct nand_ecclayout *oinfo = c->mtd->ecclayout;
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 	if (!c->mtd->oobsize)
11081da177e4SLinus Torvalds 		return 0;
11091da177e4SLinus Torvalds 
11101da177e4SLinus Torvalds 	/* Cleanmarker is out-of-band, so inline size zero */
11111da177e4SLinus Torvalds 	c->cleanmarker_size = 0;
11121da177e4SLinus Torvalds 
1113a7a6ace1SArtem Bityutskiy 	if (!oinfo || oinfo->oobavail == 0) {
1114a7a6ace1SArtem Bityutskiy 		printk(KERN_ERR "inconsistent device description\n");
11151da177e4SLinus Torvalds 		return -EINVAL;
11161da177e4SLinus Torvalds 	}
11175bd34c09SThomas Gleixner 
1118a7a6ace1SArtem Bityutskiy 	D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n"));
11195bd34c09SThomas Gleixner 
1120a7a6ace1SArtem Bityutskiy 	c->oobavail = oinfo->oobavail;
11211da177e4SLinus Torvalds 
11221da177e4SLinus Torvalds 	/* Initialise write buffer */
11231da177e4SLinus Torvalds 	init_rwsem(&c->wbuf_sem);
112428318776SJoern Engel 	c->wbuf_pagesize = c->mtd->writesize;
11251da177e4SLinus Torvalds 	c->wbuf_ofs = 0xFFFFFFFF;
11261da177e4SLinus Torvalds 
11271da177e4SLinus Torvalds 	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
11281da177e4SLinus Torvalds 	if (!c->wbuf)
11291da177e4SLinus Torvalds 		return -ENOMEM;
11301da177e4SLinus Torvalds 
1131a7a6ace1SArtem Bityutskiy 	c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL);
1132a7a6ace1SArtem Bityutskiy 	if (!c->oobbuf) {
11331da177e4SLinus Torvalds 		kfree(c->wbuf);
11341da177e4SLinus Torvalds 		return -ENOMEM;
11351da177e4SLinus Torvalds 	}
1136a7a6ace1SArtem Bityutskiy 
1137a7a6ace1SArtem Bityutskiy 	return 0;
11381da177e4SLinus Torvalds }
11391da177e4SLinus Torvalds 
11401da177e4SLinus Torvalds void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
11411da177e4SLinus Torvalds {
11421da177e4SLinus Torvalds 	kfree(c->wbuf);
11438593fbc6SThomas Gleixner 	kfree(c->oobbuf);
11441da177e4SLinus Torvalds }
11451da177e4SLinus Torvalds 
11468f15fd55SAndrew Victor int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
11478f15fd55SAndrew Victor 	c->cleanmarker_size = 0;		/* No cleanmarkers needed */
11488f15fd55SAndrew Victor 
11498f15fd55SAndrew Victor 	/* Initialize write buffer */
11508f15fd55SAndrew Victor 	init_rwsem(&c->wbuf_sem);
11518f15fd55SAndrew Victor 
1152daba5cc4SArtem B. Bityutskiy 
1153daba5cc4SArtem B. Bityutskiy 	c->wbuf_pagesize =  c->mtd->erasesize;
1154daba5cc4SArtem B. Bityutskiy 
1155daba5cc4SArtem B. Bityutskiy 	/* Find a suitable c->sector_size
1156daba5cc4SArtem B. Bityutskiy 	 * - Not too much sectors
1157daba5cc4SArtem B. Bityutskiy 	 * - Sectors have to be at least 4 K + some bytes
1158daba5cc4SArtem B. Bityutskiy 	 * - All known dataflashes have erase sizes of 528 or 1056
1159daba5cc4SArtem B. Bityutskiy 	 * - we take at least 8 eraseblocks and want to have at least 8K size
1160daba5cc4SArtem B. Bityutskiy 	 * - The concatenation should be a power of 2
1161daba5cc4SArtem B. Bityutskiy 	*/
1162daba5cc4SArtem B. Bityutskiy 
1163daba5cc4SArtem B. Bityutskiy 	c->sector_size = 8 * c->mtd->erasesize;
1164daba5cc4SArtem B. Bityutskiy 
1165daba5cc4SArtem B. Bityutskiy 	while (c->sector_size < 8192) {
1166daba5cc4SArtem B. Bityutskiy 		c->sector_size *= 2;
1167daba5cc4SArtem B. Bityutskiy 	}
1168daba5cc4SArtem B. Bityutskiy 
1169daba5cc4SArtem B. Bityutskiy 	/* It may be necessary to adjust the flash size */
1170daba5cc4SArtem B. Bityutskiy 	c->flash_size = c->mtd->size;
1171daba5cc4SArtem B. Bityutskiy 
1172daba5cc4SArtem B. Bityutskiy 	if ((c->flash_size % c->sector_size) != 0) {
1173daba5cc4SArtem B. Bityutskiy 		c->flash_size = (c->flash_size / c->sector_size) * c->sector_size;
1174daba5cc4SArtem B. Bityutskiy 		printk(KERN_WARNING "JFFS2 flash size adjusted to %dKiB\n", c->flash_size);
1175daba5cc4SArtem B. Bityutskiy 	};
1176daba5cc4SArtem B. Bityutskiy 
1177daba5cc4SArtem B. Bityutskiy 	c->wbuf_ofs = 0xFFFFFFFF;
11788f15fd55SAndrew Victor 	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
11798f15fd55SAndrew Victor 	if (!c->wbuf)
11808f15fd55SAndrew Victor 		return -ENOMEM;
11818f15fd55SAndrew Victor 
1182daba5cc4SArtem B. Bityutskiy 	printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size);
11838f15fd55SAndrew Victor 
11848f15fd55SAndrew Victor 	return 0;
11858f15fd55SAndrew Victor }
11868f15fd55SAndrew Victor 
11878f15fd55SAndrew Victor void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
11888f15fd55SAndrew Victor 	kfree(c->wbuf);
11898f15fd55SAndrew Victor }
11908f15fd55SAndrew Victor 
119159da721aSNicolas Pitre int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) {
1192c8b229deSJoern Engel 	/* Cleanmarker currently occupies whole programming regions,
1193c8b229deSJoern Engel 	 * either one or 2 for 8Byte STMicro flashes. */
1194c8b229deSJoern Engel 	c->cleanmarker_size = max(16u, c->mtd->writesize);
119559da721aSNicolas Pitre 
119659da721aSNicolas Pitre 	/* Initialize write buffer */
119759da721aSNicolas Pitre 	init_rwsem(&c->wbuf_sem);
119828318776SJoern Engel 	c->wbuf_pagesize = c->mtd->writesize;
119959da721aSNicolas Pitre 	c->wbuf_ofs = 0xFFFFFFFF;
120059da721aSNicolas Pitre 
120159da721aSNicolas Pitre 	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
120259da721aSNicolas Pitre 	if (!c->wbuf)
120359da721aSNicolas Pitre 		return -ENOMEM;
120459da721aSNicolas Pitre 
120559da721aSNicolas Pitre 	return 0;
120659da721aSNicolas Pitre }
120759da721aSNicolas Pitre 
120859da721aSNicolas Pitre void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) {
120959da721aSNicolas Pitre 	kfree(c->wbuf);
121059da721aSNicolas Pitre }
12110029da3bSArtem Bityutskiy 
12120029da3bSArtem Bityutskiy int jffs2_ubivol_setup(struct jffs2_sb_info *c) {
12130029da3bSArtem Bityutskiy 	c->cleanmarker_size = 0;
12140029da3bSArtem Bityutskiy 
12150029da3bSArtem Bityutskiy 	if (c->mtd->writesize == 1)
12160029da3bSArtem Bityutskiy 		/* We do not need write-buffer */
12170029da3bSArtem Bityutskiy 		return 0;
12180029da3bSArtem Bityutskiy 
12190029da3bSArtem Bityutskiy 	init_rwsem(&c->wbuf_sem);
12200029da3bSArtem Bityutskiy 
12210029da3bSArtem Bityutskiy 	c->wbuf_pagesize =  c->mtd->writesize;
12220029da3bSArtem Bityutskiy 	c->wbuf_ofs = 0xFFFFFFFF;
12230029da3bSArtem Bityutskiy 	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
12240029da3bSArtem Bityutskiy 	if (!c->wbuf)
12250029da3bSArtem Bityutskiy 		return -ENOMEM;
12260029da3bSArtem Bityutskiy 
12270029da3bSArtem Bityutskiy 	printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size);
12280029da3bSArtem Bityutskiy 
12290029da3bSArtem Bityutskiy 	return 0;
12300029da3bSArtem Bityutskiy }
12310029da3bSArtem Bityutskiy 
12320029da3bSArtem Bityutskiy void jffs2_ubivol_cleanup(struct jffs2_sb_info *c) {
12330029da3bSArtem Bityutskiy 	kfree(c->wbuf);
12340029da3bSArtem Bityutskiy }
1235