summaryrefslogtreecommitdiffstats
path: root/linux-2.4.x/fs/jffs2/erase.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-2.4.x/fs/jffs2/erase.c')
-rw-r--r--linux-2.4.x/fs/jffs2/erase.c487
1 files changed, 293 insertions, 194 deletions
diff --git a/linux-2.4.x/fs/jffs2/erase.c b/linux-2.4.x/fs/jffs2/erase.c
index 9179d32..a18e930 100644
--- a/linux-2.4.x/fs/jffs2/erase.c
+++ b/linux-2.4.x/fs/jffs2/erase.c
@@ -1,68 +1,65 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
*
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
*
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
*
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence. You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above. If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL. If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: erase.c,v 1.24 2001/12/06 16:38:38 dwmw2 Exp $
+ * $Id: erase.c,v 1.92 2006/01/21 21:50:44 havasi Exp $
*
*/
+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/jffs2.h>
-#include <linux/interrupt.h>
+#include <linux/compiler.h>
+#include <linux/crc32.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
#include "nodelist.h"
-#include "crc32.h"
struct erase_priv_struct {
struct jffs2_eraseblock *jeb;
struct jffs2_sb_info *c;
};
-
+
+#ifndef __ECOS
static void jffs2_erase_callback(struct erase_info *);
+#endif
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
+static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+static void jffs2_erase_block(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb)
{
- struct erase_info *instr;
int ret;
+ uint32_t bad_offset;
+#ifdef __ECOS
+ ret = jffs2_flash_erase(c, jeb);
+ if (!ret) {
+ jffs2_erase_succeeded(c, jeb);
+ return;
+ }
+ bad_offset = jeb->offset;
+#else /* Linux */
+ struct erase_info *instr;
+ D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#08x (range %#08x-%#08x)\n",
+ jeb->offset, jeb->offset, jeb->offset + c->sector_size));
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
- spin_unlock_bh(&c->erase_completion_lock);
+ c->dirty_size += c->sector_size;
+ jeb->dirty_size = c->sector_size;
+ spin_unlock(&c->erase_completion_lock);
return;
}
@@ -73,98 +70,164 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
instr->len = c->sector_size;
instr->callback = jffs2_erase_callback;
instr->priv = (unsigned long)(&instr[1]);
-
+ instr->fail_addr = 0xffffffff;
+
((struct erase_priv_struct *)instr->priv)->jeb = jeb;
((struct erase_priv_struct *)instr->priv)->c = c;
ret = c->mtd->erase(c->mtd, instr);
- if (!ret) {
+ if (!ret)
return;
- }
+
+ bad_offset = instr->fail_addr;
+ kfree(instr);
+#endif /* __ECOS */
+
if (ret == -ENOMEM || ret == -EAGAIN) {
/* Erase failed immediately. Refile it on the list */
D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
- spin_unlock_bh(&c->erase_completion_lock);
- kfree(instr);
+ c->dirty_size += c->sector_size;
+ jeb->dirty_size = c->sector_size;
+ spin_unlock(&c->erase_completion_lock);
return;
}
- if (ret == -EROFS)
+ if (ret == -EROFS)
printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
else
printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
- spin_lock_bh(&c->erase_completion_lock);
- list_del(&jeb->list);
- list_add(&jeb->list, &c->bad_list);
- c->nr_erasing_blocks--;
- c->bad_size += c->sector_size;
- c->erasing_size -= c->sector_size;
- spin_unlock_bh(&c->erase_completion_lock);
- wake_up(&c->erase_wait);
- kfree(instr);
+
+ jffs2_erase_failed(c, jeb, bad_offset);
}
-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
{
struct jffs2_eraseblock *jeb;
- spin_lock_bh(&c->erase_completion_lock);
- while (!list_empty(&c->erase_pending_list)) {
+ down(&c->erase_free_sem);
- jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
+ spin_lock(&c->erase_completion_lock);
- D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+ while (!list_empty(&c->erase_complete_list) ||
+ !list_empty(&c->erase_pending_list)) {
+
+ if (!list_empty(&c->erase_complete_list)) {
+ jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
+ list_del(&jeb->list);
+ spin_unlock(&c->erase_completion_lock);
+ jffs2_mark_erased_block(c, jeb);
+
+ if (!--count) {
+ D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
+ goto done;
+ }
+
+ } else if (!list_empty(&c->erase_pending_list)) {
+ jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
+ D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+ list_del(&jeb->list);
+ c->erasing_size += c->sector_size;
+ c->wasted_size -= jeb->wasted_size;
+ c->free_size -= jeb->free_size;
+ c->used_size -= jeb->used_size;
+ c->dirty_size -= jeb->dirty_size;
+ jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+ jffs2_free_all_node_refs(c, jeb);
+ list_add(&jeb->list, &c->erasing_list);
+ spin_unlock(&c->erase_completion_lock);
+
+ jffs2_erase_block(c, jeb);
+
+ } else {
+ BUG();
+ }
- list_del(&jeb->list);
- c->erasing_size += c->sector_size;
- c->free_size -= jeb->free_size;
- c->used_size -= jeb->used_size;
- c->dirty_size -= jeb->dirty_size;
- jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
- jffs2_free_all_node_refs(c, jeb);
- list_add(&jeb->list, &c->erasing_list);
- spin_unlock_bh(&c->erase_completion_lock);
-
- jffs2_erase_block(c, jeb);
/* Be nice */
- if (current->need_resched)
- schedule();
- spin_lock_bh(&c->erase_completion_lock);
+ cond_resched();
+ spin_lock(&c->erase_completion_lock);
}
- spin_unlock_bh(&c->erase_completion_lock);
+
+ spin_unlock(&c->erase_completion_lock);
+ done:
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
+
+ up(&c->erase_free_sem);
}
+static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+ D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
+ spin_lock(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add_tail(&jeb->list, &c->erase_complete_list);
+ spin_unlock(&c->erase_completion_lock);
+ if (!EBFLAGS_HAS_EBH(jeb)) {
+ if (c->nr_blocks_with_ebh != 0) {
+ jeb->erase_count = c->total_erase_count/c->nr_blocks_with_ebh;
+ } else {
+ jeb->erase_count = 0;
+ }
+ EBFLAGS_SET_EBH(jeb);
+ c->nr_blocks_with_ebh++;
+ c->total_erase_count += jeb->erase_count;
+ }
+ jeb->erase_count++;
+ if (jeb->erase_count > c->max_erase_count) {
+ c->max_erase_count = jeb->erase_count;
+ }
+ c->total_erase_count++;
+ /* Ensure that kupdated calls us again to mark them clean */
+ jffs2_erase_pending_trigger(c);
+}
+
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
+{
+ /* For NAND, if the failure did not occur at the device level for a
+ specific physical page, don't bother updating the bad block table. */
+ if (jffs2_ebh_oob(c) && (bad_offset != 0xffffffff)) {
+ /* We had a device-level failure to erase. Let's see if we've
+ failed too many times. */
+ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
+ /* We'd like to give this block another try. */
+ spin_lock(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->erasing_size -= c->sector_size;
+ c->dirty_size += c->sector_size;
+ jeb->dirty_size = c->sector_size;
+ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
+ }
+
+ spin_lock(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->bad_size += c->sector_size;
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->bad_list);
+ c->nr_erasing_blocks--;
+ spin_unlock(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
+}
+#ifndef __ECOS
static void jffs2_erase_callback(struct erase_info *instr)
{
struct erase_priv_struct *priv = (void *)instr->priv;
if(instr->state != MTD_ERASE_DONE) {
printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
- spin_lock(&priv->c->erase_completion_lock);
- priv->c->erasing_size -= priv->c->sector_size;
- priv->c->bad_size += priv->c->sector_size;
- list_del(&priv->jeb->list);
- list_add(&priv->jeb->list, &priv->c->bad_list);
- priv->c->nr_erasing_blocks--;
- spin_unlock(&priv->c->erase_completion_lock);
- wake_up(&priv->c->erase_wait);
+ jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
} else {
- D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr));
- spin_lock(&priv->c->erase_completion_lock);
- list_del(&priv->jeb->list);
- list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list);
- spin_unlock(&priv->c->erase_completion_lock);
- }
- /* Make sure someone picks up the block off the erase_complete list */
- OFNI_BS_2SFFJ(priv->c)->s_dirt = 1;
+ jffs2_erase_succeeded(priv->c, priv->jeb);
+ }
kfree(instr);
}
+#endif /* !__ECOS */
/* Hmmm. Maybe we should accept the extra space it takes and make
this a standard doubly-linked list? */
@@ -179,15 +242,15 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
/* Walk the inode's list once, removing any nodes from this eraseblock */
while (1) {
if (!(*prev)->next_in_ino) {
- /* We're looking at the jffs2_inode_cache, which is
+ /* We're looking at the jffs2_inode_cache, which is
at the end of the linked list. Stash it and continue
from the beginning of the list */
ic = (struct jffs2_inode_cache *)(*prev);
prev = &ic->nodes;
continue;
- }
+ }
- if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
+ if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
/* It's in the block we're erasing */
struct jffs2_raw_node_ref *this;
@@ -219,9 +282,9 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG);
this = ic->nodes;
-
+
while(this) {
- printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3);
+ printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this));
if (++i == 5) {
printk("\n" KERN_DEBUG);
i=0;
@@ -231,11 +294,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
printk("\n");
});
- if (ic->nodes == (void *)ic) {
- D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
+ if (ic->nodes == (void *)ic && ic->nlink == 0)
jffs2_del_ino_cache(c, ic);
- jffs2_free_inode_cache(ic);
- }
}
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
@@ -245,7 +305,7 @@ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_erase
while(jeb->first_node) {
ref = jeb->first_node;
jeb->first_node = ref->next_phys;
-
+
/* Remove from the inode-list */
if (ref->next_in_ino)
jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
@@ -256,118 +316,157 @@ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_erase
jeb->last_node = NULL;
}
-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
+static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
{
- OFNI_BS_2SFFJ(c)->s_dirt = 1;
+ void *ebuf;
+ uint32_t ofs;
+ int ret = -EIO;
+
+ ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!ebuf) {
+ printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset);
+ return -EAGAIN;
+ }
+
+ D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+
+ for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) {
+ uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
+ int i;
+
+ *bad_offset = ofs;
+
+ if (jffs2_flash_read_safe(c, ofs, readlen, (void *)ebuf))
+ goto fail;
+
+ for (i=0; i<readlen; i += sizeof(unsigned long)) {
+ /* It's OK. We know it's properly aligned */
+ unsigned long *datum = ebuf + i;
+ if (*datum + 1) {
+ *bad_offset += i;
+ printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", *datum, *bad_offset);
+ goto fail;
+ }
+ }
+ ofs += readlen;
+ cond_resched();
+ }
+ ret = 0;
+fail:
+ kfree(ebuf);
+ return ret;
}
-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
- static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)};
- struct jffs2_eraseblock *jeb;
- struct jffs2_raw_node_ref *marker_ref;
- unsigned char *ebuf;
- ssize_t retlen;
+ struct jffs2_raw_node_ref *ebh_ref = NULL;
+ size_t retlen;
int ret;
+ uint32_t bad_offset;
- marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
+ switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
+ case -EAGAIN: goto refile;
+ case -EIO: goto filebad;
+ }
- spin_lock_bh(&c->erase_completion_lock);
- while (!list_empty(&c->erase_complete_list)) {
- jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
- list_del(&jeb->list);
- spin_unlock_bh(&c->erase_completion_lock);
+ /* Write the erase complete marker */
+ D1(printk(KERN_DEBUG "Writing eraseblock header to block at 0x%08x\n", jeb->offset));
+ bad_offset = jeb->offset;
- marker_ref = jffs2_alloc_raw_node_ref();
- if (!marker_ref) {
- printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
- /* Come back later */
- jffs2_erase_pending_trigger(c);
- return;
- }
+ /* Cleanmarker in oob area or no cleanmarker at all ? */
+ if (jffs2_ebh_oob(c)) {
- ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!ebuf) {
- printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
- } else {
- __u32 ofs = jeb->offset;
-
- D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
- while(ofs < jeb->offset + c->sector_size) {
- __u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
- int i;
-
- ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
- if (ret < 0) {
- printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
- goto bad;
- }
- if (retlen != readlen) {
- printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen);
- goto bad;
- }
- for (i=0; i<readlen; i += sizeof(unsigned long)) {
- /* It's OK. We know it's properly aligned */
- unsigned long datum = *(unsigned long *)(&ebuf[i]);
- if (datum + 1) {
- printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
- bad:
- jffs2_free_raw_node_ref(marker_ref);
- kfree(ebuf);
- bad2:
- spin_lock_bh(&c->erase_completion_lock);
- c->erasing_size -= c->sector_size;
- c->bad_size += c->sector_size;
-
- list_add_tail(&jeb->list, &c->bad_list);
- c->nr_erasing_blocks--;
- spin_unlock_bh(&c->erase_completion_lock);
- wake_up(&c->erase_wait);
- return;
- }
- }
- ofs += readlen;
- }
- kfree(ebuf);
- }
-
- /* Write the erase complete marker */
- D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
- ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
- if (ret) {
- printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
- jeb->offset, ret);
- goto bad2;
+ if (jffs2_write_nand_ebh(c, jeb))
+ goto filebad;
+
+ jeb->first_node = jeb->last_node = NULL;
+ jeb->free_size = c->sector_size;
+ jeb->used_size = 0;
+ jeb->dirty_size = 0;
+ jeb->wasted_size = 0;
+
+ } else {
+
+ struct kvec vecs[1];
+ struct jffs2_raw_ebh ebh = {
+ .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
+ .nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASEBLOCK_HEADER),
+ .totlen = cpu_to_je32(c->ebh_size),
+ .reserved = 0,
+ .compat_fset = JFFS2_EBH_COMPAT_FSET,
+ .incompat_fset = JFFS2_EBH_INCOMPAT_FSET,
+ .rocompat_fset = JFFS2_EBH_ROCOMPAT_FSET,
+ };
+
+ ebh_ref = jffs2_alloc_raw_node_ref();
+ if (!ebh_ref) {
+ printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
+ goto refile;
}
- if (retlen != sizeof(marker)) {
- printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n",
- jeb->offset, sizeof(marker), retlen);
- goto bad2;
+ ebh.erase_count = cpu_to_je32(jeb->erase_count);
+ ebh.hdr_crc = cpu_to_je32(crc32(0, &ebh, sizeof(struct jffs2_unknown_node)-4));
+ ebh.node_crc = cpu_to_je32(crc32(0, (unsigned char *)&ebh + sizeof(struct jffs2_unknown_node) + 4,
+ sizeof(struct jffs2_raw_ebh) - sizeof(struct jffs2_unknown_node) - 4));
+
+ vecs[0].iov_base = (unsigned char *) &ebh;
+ vecs[0].iov_len = sizeof(ebh);
+ ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
+
+ if (ret || retlen != sizeof(ebh)) {
+ if (ret)
+ printk(KERN_WARNING "Write eraseblock header to block at 0x%08x failed: %d\n",
+ jeb->offset, ret);
+ else
+ printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+ jeb->offset, sizeof(ebh), retlen);
+
+ jffs2_free_raw_node_ref(ebh_ref);
+ goto filebad;
}
- marker_ref->next_in_ino = NULL;
- marker_ref->next_phys = NULL;
- marker_ref->flash_offset = jeb->offset;
- marker_ref->totlen = PAD(sizeof(marker));
+ ebh_ref->next_in_ino = NULL;
+ ebh_ref->next_phys = NULL;
+ ebh_ref->flash_offset = jeb->offset | REF_NORMAL;
+ ebh_ref->__totlen = c->ebh_size;
- jeb->first_node = jeb->last_node = marker_ref;
+ jeb->first_node = jeb->last_node = ebh_ref;
- jeb->free_size = c->sector_size - marker_ref->totlen;
- jeb->used_size = marker_ref->totlen;
+ jeb->free_size = c->sector_size - c->ebh_size;
+ jeb->used_size = c->ebh_size;
jeb->dirty_size = 0;
+ jeb->wasted_size = 0;
+ }
- spin_lock_bh(&c->erase_completion_lock);
- c->erasing_size -= c->sector_size;
- c->free_size += jeb->free_size;
- c->used_size += jeb->used_size;
+ spin_lock(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->free_size += jeb->free_size;
+ c->used_size += jeb->used_size;
- ACCT_SANITY_CHECK(c,jeb);
- ACCT_PARANOIA_CHECK(jeb);
+ jffs2_dbg_acct_sanity_check_nolock(c,jeb);
+ jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
- list_add_tail(&jeb->list, &c->free_list);
- c->nr_erasing_blocks--;
- c->nr_free_blocks++;
- wake_up(&c->erase_wait);
- }
- spin_unlock_bh(&c->erase_completion_lock);
+ list_add_tail(&jeb->list, &c->free_list);
+ jffs2_add_to_hash_table(c, jeb, 2);
+ c->nr_erasing_blocks--;
+ c->nr_free_blocks++;
+ spin_unlock(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
+ return;
+
+filebad:
+ spin_lock(&c->erase_completion_lock);
+ /* Stick it on a list (any list) so erase_failed can take it
+ right off again. Silly, but shouldn't happen often. */
+ list_add(&jeb->list, &c->erasing_list);
+ spin_unlock(&c->erase_completion_lock);
+ jffs2_erase_failed(c, jeb, bad_offset);
+ return;
+
+refile:
+ /* Stick it back on the list from whence it came and come back later */
+ jffs2_erase_pending_trigger(c);
+ spin_lock(&c->erase_completion_lock);
+ list_add(&jeb->list, &c->erase_complete_list);
+ spin_unlock(&c->erase_completion_lock);
+ return;
}