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-2010 David Woodhouse <dwmw2@infradead.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Created by David Woodhouse <dwmw2@infradead.org>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * For licensing information, see the file 'LICENCE' in this directory.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1862306a36Sopenharmony_ci#include <linux/compiler.h>
1962306a36Sopenharmony_ci#include <linux/crc32.h>
2062306a36Sopenharmony_ci#include <linux/sched.h>
2162306a36Sopenharmony_ci#include <linux/pagemap.h>
2262306a36Sopenharmony_ci#include "nodelist.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
2562306a36Sopenharmony_cistatic void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
2662306a36Sopenharmony_cistatic void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void jffs2_erase_block(struct jffs2_sb_info *c,
2962306a36Sopenharmony_ci			      struct jffs2_eraseblock *jeb)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	int ret;
3262306a36Sopenharmony_ci	uint32_t bad_offset;
3362306a36Sopenharmony_ci#ifdef __ECOS
3462306a36Sopenharmony_ci       ret = jffs2_flash_erase(c, jeb);
3562306a36Sopenharmony_ci       if (!ret) {
3662306a36Sopenharmony_ci	       jffs2_erase_succeeded(c, jeb);
3762306a36Sopenharmony_ci	       return;
3862306a36Sopenharmony_ci       }
3962306a36Sopenharmony_ci       bad_offset = jeb->offset;
4062306a36Sopenharmony_ci#else /* Linux */
4162306a36Sopenharmony_ci	struct erase_info *instr;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n",
4462306a36Sopenharmony_ci		  __func__,
4562306a36Sopenharmony_ci		  jeb->offset, jeb->offset, jeb->offset + c->sector_size);
4662306a36Sopenharmony_ci	instr = kzalloc(sizeof(struct erase_info), GFP_KERNEL);
4762306a36Sopenharmony_ci	if (!instr) {
4862306a36Sopenharmony_ci		pr_warn("kzalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
4962306a36Sopenharmony_ci		mutex_lock(&c->erase_free_sem);
5062306a36Sopenharmony_ci		spin_lock(&c->erase_completion_lock);
5162306a36Sopenharmony_ci		list_move(&jeb->list, &c->erase_pending_list);
5262306a36Sopenharmony_ci		c->erasing_size -= c->sector_size;
5362306a36Sopenharmony_ci		c->dirty_size += c->sector_size;
5462306a36Sopenharmony_ci		jeb->dirty_size = c->sector_size;
5562306a36Sopenharmony_ci		spin_unlock(&c->erase_completion_lock);
5662306a36Sopenharmony_ci		mutex_unlock(&c->erase_free_sem);
5762306a36Sopenharmony_ci		return;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	instr->addr = jeb->offset;
6162306a36Sopenharmony_ci	instr->len = c->sector_size;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ret = mtd_erase(c->mtd, instr);
6462306a36Sopenharmony_ci	if (!ret) {
6562306a36Sopenharmony_ci		jffs2_erase_succeeded(c, jeb);
6662306a36Sopenharmony_ci		kfree(instr);
6762306a36Sopenharmony_ci		return;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	bad_offset = instr->fail_addr;
7162306a36Sopenharmony_ci	kfree(instr);
7262306a36Sopenharmony_ci#endif /* __ECOS */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (ret == -ENOMEM || ret == -EAGAIN) {
7562306a36Sopenharmony_ci		/* Erase failed immediately. Refile it on the list */
7662306a36Sopenharmony_ci		jffs2_dbg(1, "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n",
7762306a36Sopenharmony_ci			  jeb->offset, ret);
7862306a36Sopenharmony_ci		mutex_lock(&c->erase_free_sem);
7962306a36Sopenharmony_ci		spin_lock(&c->erase_completion_lock);
8062306a36Sopenharmony_ci		list_move(&jeb->list, &c->erase_pending_list);
8162306a36Sopenharmony_ci		c->erasing_size -= c->sector_size;
8262306a36Sopenharmony_ci		c->dirty_size += c->sector_size;
8362306a36Sopenharmony_ci		jeb->dirty_size = c->sector_size;
8462306a36Sopenharmony_ci		spin_unlock(&c->erase_completion_lock);
8562306a36Sopenharmony_ci		mutex_unlock(&c->erase_free_sem);
8662306a36Sopenharmony_ci		return;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (ret == -EROFS)
9062306a36Sopenharmony_ci		pr_warn("Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n",
9162306a36Sopenharmony_ci			jeb->offset);
9262306a36Sopenharmony_ci	else
9362306a36Sopenharmony_ci		pr_warn("Erase at 0x%08x failed immediately: errno %d\n",
9462306a36Sopenharmony_ci			jeb->offset, ret);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	jffs2_erase_failed(c, jeb, bad_offset);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ciint jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct jffs2_eraseblock *jeb;
10262306a36Sopenharmony_ci	int work_done = 0;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	mutex_lock(&c->erase_free_sem);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	spin_lock(&c->erase_completion_lock);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	while (!list_empty(&c->erase_complete_list) ||
10962306a36Sopenharmony_ci	       !list_empty(&c->erase_pending_list)) {
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		if (!list_empty(&c->erase_complete_list)) {
11262306a36Sopenharmony_ci			jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
11362306a36Sopenharmony_ci			list_move(&jeb->list, &c->erase_checking_list);
11462306a36Sopenharmony_ci			spin_unlock(&c->erase_completion_lock);
11562306a36Sopenharmony_ci			mutex_unlock(&c->erase_free_sem);
11662306a36Sopenharmony_ci			jffs2_mark_erased_block(c, jeb);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci			work_done++;
11962306a36Sopenharmony_ci			if (!--count) {
12062306a36Sopenharmony_ci				jffs2_dbg(1, "Count reached. jffs2_erase_pending_blocks leaving\n");
12162306a36Sopenharmony_ci				goto done;
12262306a36Sopenharmony_ci			}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		} else if (!list_empty(&c->erase_pending_list)) {
12562306a36Sopenharmony_ci			jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
12662306a36Sopenharmony_ci			jffs2_dbg(1, "Starting erase of pending block 0x%08x\n",
12762306a36Sopenharmony_ci				  jeb->offset);
12862306a36Sopenharmony_ci			list_del(&jeb->list);
12962306a36Sopenharmony_ci			c->erasing_size += c->sector_size;
13062306a36Sopenharmony_ci			c->wasted_size -= jeb->wasted_size;
13162306a36Sopenharmony_ci			c->free_size -= jeb->free_size;
13262306a36Sopenharmony_ci			c->used_size -= jeb->used_size;
13362306a36Sopenharmony_ci			c->dirty_size -= jeb->dirty_size;
13462306a36Sopenharmony_ci			jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
13562306a36Sopenharmony_ci			jffs2_free_jeb_node_refs(c, jeb);
13662306a36Sopenharmony_ci			list_add(&jeb->list, &c->erasing_list);
13762306a36Sopenharmony_ci			spin_unlock(&c->erase_completion_lock);
13862306a36Sopenharmony_ci			mutex_unlock(&c->erase_free_sem);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci			jffs2_erase_block(c, jeb);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		} else {
14362306a36Sopenharmony_ci			BUG();
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		/* Be nice */
14762306a36Sopenharmony_ci		cond_resched();
14862306a36Sopenharmony_ci		mutex_lock(&c->erase_free_sem);
14962306a36Sopenharmony_ci		spin_lock(&c->erase_completion_lock);
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
15362306a36Sopenharmony_ci	mutex_unlock(&c->erase_free_sem);
15462306a36Sopenharmony_ci done:
15562306a36Sopenharmony_ci	jffs2_dbg(1, "jffs2_erase_pending_blocks completed\n");
15662306a36Sopenharmony_ci	return work_done;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	jffs2_dbg(1, "Erase completed successfully at 0x%08x\n", jeb->offset);
16262306a36Sopenharmony_ci	mutex_lock(&c->erase_free_sem);
16362306a36Sopenharmony_ci	spin_lock(&c->erase_completion_lock);
16462306a36Sopenharmony_ci	list_move_tail(&jeb->list, &c->erase_complete_list);
16562306a36Sopenharmony_ci	/* Wake the GC thread to mark them clean */
16662306a36Sopenharmony_ci	jffs2_garbage_collect_trigger(c);
16762306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
16862306a36Sopenharmony_ci	mutex_unlock(&c->erase_free_sem);
16962306a36Sopenharmony_ci	wake_up(&c->erase_wait);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	/* For NAND, if the failure did not occur at the device level for a
17562306a36Sopenharmony_ci	   specific physical page, don't bother updating the bad block table. */
17662306a36Sopenharmony_ci	if (jffs2_cleanmarker_oob(c) && (bad_offset != (uint32_t)MTD_FAIL_ADDR_UNKNOWN)) {
17762306a36Sopenharmony_ci		/* We had a device-level failure to erase.  Let's see if we've
17862306a36Sopenharmony_ci		   failed too many times. */
17962306a36Sopenharmony_ci		if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
18062306a36Sopenharmony_ci			/* We'd like to give this block another try. */
18162306a36Sopenharmony_ci			mutex_lock(&c->erase_free_sem);
18262306a36Sopenharmony_ci			spin_lock(&c->erase_completion_lock);
18362306a36Sopenharmony_ci			list_move(&jeb->list, &c->erase_pending_list);
18462306a36Sopenharmony_ci			c->erasing_size -= c->sector_size;
18562306a36Sopenharmony_ci			c->dirty_size += c->sector_size;
18662306a36Sopenharmony_ci			jeb->dirty_size = c->sector_size;
18762306a36Sopenharmony_ci			spin_unlock(&c->erase_completion_lock);
18862306a36Sopenharmony_ci			mutex_unlock(&c->erase_free_sem);
18962306a36Sopenharmony_ci			return;
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	mutex_lock(&c->erase_free_sem);
19462306a36Sopenharmony_ci	spin_lock(&c->erase_completion_lock);
19562306a36Sopenharmony_ci	c->erasing_size -= c->sector_size;
19662306a36Sopenharmony_ci	c->bad_size += c->sector_size;
19762306a36Sopenharmony_ci	list_move(&jeb->list, &c->bad_list);
19862306a36Sopenharmony_ci	c->nr_erasing_blocks--;
19962306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
20062306a36Sopenharmony_ci	mutex_unlock(&c->erase_free_sem);
20162306a36Sopenharmony_ci	wake_up(&c->erase_wait);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/* Hmmm. Maybe we should accept the extra space it takes and make
20562306a36Sopenharmony_ci   this a standard doubly-linked list? */
20662306a36Sopenharmony_cistatic inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
20762306a36Sopenharmony_ci			struct jffs2_raw_node_ref *ref, struct jffs2_eraseblock *jeb)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct jffs2_inode_cache *ic = NULL;
21062306a36Sopenharmony_ci	struct jffs2_raw_node_ref **prev;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	prev = &ref->next_in_ino;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Walk the inode's list once, removing any nodes from this eraseblock */
21562306a36Sopenharmony_ci	while (1) {
21662306a36Sopenharmony_ci		if (!(*prev)->next_in_ino) {
21762306a36Sopenharmony_ci			/* We're looking at the jffs2_inode_cache, which is
21862306a36Sopenharmony_ci			   at the end of the linked list. Stash it and continue
21962306a36Sopenharmony_ci			   from the beginning of the list */
22062306a36Sopenharmony_ci			ic = (struct jffs2_inode_cache *)(*prev);
22162306a36Sopenharmony_ci			prev = &ic->nodes;
22262306a36Sopenharmony_ci			continue;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
22662306a36Sopenharmony_ci			/* It's in the block we're erasing */
22762306a36Sopenharmony_ci			struct jffs2_raw_node_ref *this;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci			this = *prev;
23062306a36Sopenharmony_ci			*prev = this->next_in_ino;
23162306a36Sopenharmony_ci			this->next_in_ino = NULL;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci			if (this == ref)
23462306a36Sopenharmony_ci				break;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci			continue;
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci		/* Not to be deleted. Skip */
23962306a36Sopenharmony_ci		prev = &((*prev)->next_in_ino);
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* PARANOIA */
24362306a36Sopenharmony_ci	if (!ic) {
24462306a36Sopenharmony_ci		JFFS2_WARNING("inode_cache/xattr_datum/xattr_ref"
24562306a36Sopenharmony_ci			      " not found in remove_node_refs()!!\n");
24662306a36Sopenharmony_ci		return;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	jffs2_dbg(1, "Removed nodes in range 0x%08x-0x%08x from ino #%u\n",
25062306a36Sopenharmony_ci		  jeb->offset, jeb->offset + c->sector_size, ic->ino);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	D2({
25362306a36Sopenharmony_ci		int i=0;
25462306a36Sopenharmony_ci		struct jffs2_raw_node_ref *this;
25562306a36Sopenharmony_ci		printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n");
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		this = ic->nodes;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		printk(KERN_DEBUG);
26062306a36Sopenharmony_ci		while(this) {
26162306a36Sopenharmony_ci			pr_cont("0x%08x(%d)->",
26262306a36Sopenharmony_ci			       ref_offset(this), ref_flags(this));
26362306a36Sopenharmony_ci			if (++i == 5) {
26462306a36Sopenharmony_ci				printk(KERN_DEBUG);
26562306a36Sopenharmony_ci				i=0;
26662306a36Sopenharmony_ci			}
26762306a36Sopenharmony_ci			this = this->next_in_ino;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci		pr_cont("\n");
27062306a36Sopenharmony_ci	});
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	switch (ic->class) {
27362306a36Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_XATTR
27462306a36Sopenharmony_ci		case RAWNODE_CLASS_XATTR_DATUM:
27562306a36Sopenharmony_ci			jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
27662306a36Sopenharmony_ci			break;
27762306a36Sopenharmony_ci		case RAWNODE_CLASS_XATTR_REF:
27862306a36Sopenharmony_ci			jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
27962306a36Sopenharmony_ci			break;
28062306a36Sopenharmony_ci#endif
28162306a36Sopenharmony_ci		default:
28262306a36Sopenharmony_ci			if (ic->nodes == (void *)ic && ic->pino_nlink == 0)
28362306a36Sopenharmony_ci				jffs2_del_ino_cache(c, ic);
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_civoid jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct jffs2_raw_node_ref *block, *ref;
29062306a36Sopenharmony_ci	jffs2_dbg(1, "Freeing all node refs for eraseblock offset 0x%08x\n",
29162306a36Sopenharmony_ci		  jeb->offset);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	block = ref = jeb->first_node;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	while (ref) {
29662306a36Sopenharmony_ci		if (ref->flash_offset == REF_LINK_NODE) {
29762306a36Sopenharmony_ci			ref = ref->next_in_ino;
29862306a36Sopenharmony_ci			jffs2_free_refblock(block);
29962306a36Sopenharmony_ci			block = ref;
30062306a36Sopenharmony_ci			continue;
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci		if (ref->flash_offset != REF_EMPTY_NODE && ref->next_in_ino)
30362306a36Sopenharmony_ci			jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
30462306a36Sopenharmony_ci		/* else it was a non-inode node or already removed, so don't bother */
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		ref++;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci	jeb->first_node = jeb->last_node = NULL;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	void *ebuf;
31462306a36Sopenharmony_ci	uint32_t ofs;
31562306a36Sopenharmony_ci	size_t retlen;
31662306a36Sopenharmony_ci	int ret;
31762306a36Sopenharmony_ci	unsigned long *wordebuf;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	ret = mtd_point(c->mtd, jeb->offset, c->sector_size, &retlen,
32062306a36Sopenharmony_ci			&ebuf, NULL);
32162306a36Sopenharmony_ci	if (ret != -EOPNOTSUPP) {
32262306a36Sopenharmony_ci		if (ret) {
32362306a36Sopenharmony_ci			jffs2_dbg(1, "MTD point failed %d\n", ret);
32462306a36Sopenharmony_ci			goto do_flash_read;
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci		if (retlen < c->sector_size) {
32762306a36Sopenharmony_ci			/* Don't muck about if it won't let us point to the whole erase sector */
32862306a36Sopenharmony_ci			jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n",
32962306a36Sopenharmony_ci				  retlen);
33062306a36Sopenharmony_ci			mtd_unpoint(c->mtd, jeb->offset, retlen);
33162306a36Sopenharmony_ci			goto do_flash_read;
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci		wordebuf = ebuf-sizeof(*wordebuf);
33462306a36Sopenharmony_ci		retlen /= sizeof(*wordebuf);
33562306a36Sopenharmony_ci		do {
33662306a36Sopenharmony_ci		   if (*++wordebuf != ~0)
33762306a36Sopenharmony_ci			   break;
33862306a36Sopenharmony_ci		} while(--retlen);
33962306a36Sopenharmony_ci		mtd_unpoint(c->mtd, jeb->offset, c->sector_size);
34062306a36Sopenharmony_ci		if (retlen) {
34162306a36Sopenharmony_ci			pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08tx\n",
34262306a36Sopenharmony_ci				*wordebuf,
34362306a36Sopenharmony_ci				jeb->offset +
34462306a36Sopenharmony_ci				c->sector_size-retlen * sizeof(*wordebuf));
34562306a36Sopenharmony_ci			return -EIO;
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci		return 0;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci do_flash_read:
35062306a36Sopenharmony_ci	ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
35162306a36Sopenharmony_ci	if (!ebuf) {
35262306a36Sopenharmony_ci		pr_warn("Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n",
35362306a36Sopenharmony_ci			jeb->offset);
35462306a36Sopenharmony_ci		return -EAGAIN;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	jffs2_dbg(1, "Verifying erase at 0x%08x\n", jeb->offset);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) {
36062306a36Sopenharmony_ci		uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
36162306a36Sopenharmony_ci		int i;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		*bad_offset = ofs;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		ret = mtd_read(c->mtd, ofs, readlen, &retlen, ebuf);
36662306a36Sopenharmony_ci		if (ret) {
36762306a36Sopenharmony_ci			pr_warn("Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n",
36862306a36Sopenharmony_ci				ofs, ret);
36962306a36Sopenharmony_ci			ret = -EIO;
37062306a36Sopenharmony_ci			goto fail;
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci		if (retlen != readlen) {
37362306a36Sopenharmony_ci			pr_warn("Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n",
37462306a36Sopenharmony_ci				ofs, readlen, retlen);
37562306a36Sopenharmony_ci			ret = -EIO;
37662306a36Sopenharmony_ci			goto fail;
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci		for (i=0; i<readlen; i += sizeof(unsigned long)) {
37962306a36Sopenharmony_ci			/* It's OK. We know it's properly aligned */
38062306a36Sopenharmony_ci			unsigned long *datum = ebuf + i;
38162306a36Sopenharmony_ci			if (*datum + 1) {
38262306a36Sopenharmony_ci				*bad_offset += i;
38362306a36Sopenharmony_ci				pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08x\n",
38462306a36Sopenharmony_ci					*datum, *bad_offset);
38562306a36Sopenharmony_ci				ret = -EIO;
38662306a36Sopenharmony_ci				goto fail;
38762306a36Sopenharmony_ci			}
38862306a36Sopenharmony_ci		}
38962306a36Sopenharmony_ci		ofs += readlen;
39062306a36Sopenharmony_ci		cond_resched();
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	ret = 0;
39362306a36Sopenharmony_cifail:
39462306a36Sopenharmony_ci	kfree(ebuf);
39562306a36Sopenharmony_ci	return ret;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	size_t retlen;
40162306a36Sopenharmony_ci	int ret;
40262306a36Sopenharmony_ci	uint32_t bad_offset;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
40562306a36Sopenharmony_ci	case -EAGAIN:	goto refile;
40662306a36Sopenharmony_ci	case -EIO:	goto filebad;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* Write the erase complete marker */
41062306a36Sopenharmony_ci	jffs2_dbg(1, "Writing erased marker to block at 0x%08x\n", jeb->offset);
41162306a36Sopenharmony_ci	bad_offset = jeb->offset;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* Cleanmarker in oob area or no cleanmarker at all ? */
41462306a36Sopenharmony_ci	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		if (jffs2_cleanmarker_oob(c)) {
41762306a36Sopenharmony_ci			if (jffs2_write_nand_cleanmarker(c, jeb))
41862306a36Sopenharmony_ci				goto filebad;
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci	} else {
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		struct kvec vecs[1];
42362306a36Sopenharmony_ci		struct jffs2_unknown_node marker = {
42462306a36Sopenharmony_ci			.magic =	cpu_to_je16(JFFS2_MAGIC_BITMASK),
42562306a36Sopenharmony_ci			.nodetype =	cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
42662306a36Sopenharmony_ci			.totlen =	cpu_to_je32(c->cleanmarker_size)
42762306a36Sopenharmony_ci		};
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		jffs2_prealloc_raw_node_refs(c, jeb, 1);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		vecs[0].iov_base = (unsigned char *) &marker;
43462306a36Sopenharmony_ci		vecs[0].iov_len = sizeof(marker);
43562306a36Sopenharmony_ci		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		if (ret || retlen != sizeof(marker)) {
43862306a36Sopenharmony_ci			if (ret)
43962306a36Sopenharmony_ci				pr_warn("Write clean marker to block at 0x%08x failed: %d\n",
44062306a36Sopenharmony_ci				       jeb->offset, ret);
44162306a36Sopenharmony_ci			else
44262306a36Sopenharmony_ci				pr_warn("Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
44362306a36Sopenharmony_ci				       jeb->offset, sizeof(marker), retlen);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci			goto filebad;
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci	/* Everything else got zeroed before the erase */
44962306a36Sopenharmony_ci	jeb->free_size = c->sector_size;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	mutex_lock(&c->erase_free_sem);
45262306a36Sopenharmony_ci	spin_lock(&c->erase_completion_lock);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	c->erasing_size -= c->sector_size;
45562306a36Sopenharmony_ci	c->free_size += c->sector_size;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* Account for cleanmarker now, if it's in-band */
45862306a36Sopenharmony_ci	if (c->cleanmarker_size && !jffs2_cleanmarker_oob(c))
45962306a36Sopenharmony_ci		jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	list_move_tail(&jeb->list, &c->free_list);
46262306a36Sopenharmony_ci	c->nr_erasing_blocks--;
46362306a36Sopenharmony_ci	c->nr_free_blocks++;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	jffs2_dbg_acct_sanity_check_nolock(c, jeb);
46662306a36Sopenharmony_ci	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
46962306a36Sopenharmony_ci	mutex_unlock(&c->erase_free_sem);
47062306a36Sopenharmony_ci	wake_up(&c->erase_wait);
47162306a36Sopenharmony_ci	return;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cifilebad:
47462306a36Sopenharmony_ci	jffs2_erase_failed(c, jeb, bad_offset);
47562306a36Sopenharmony_ci	return;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cirefile:
47862306a36Sopenharmony_ci	/* Stick it back on the list from whence it came and come back later */
47962306a36Sopenharmony_ci	mutex_lock(&c->erase_free_sem);
48062306a36Sopenharmony_ci	spin_lock(&c->erase_completion_lock);
48162306a36Sopenharmony_ci	jffs2_garbage_collect_trigger(c);
48262306a36Sopenharmony_ci	list_move(&jeb->list, &c->erase_complete_list);
48362306a36Sopenharmony_ci	spin_unlock(&c->erase_completion_lock);
48462306a36Sopenharmony_ci	mutex_unlock(&c->erase_free_sem);
48562306a36Sopenharmony_ci	return;
48662306a36Sopenharmony_ci}
487