diff options
Diffstat (limited to 'linux-2.4.x/fs/jffs2/erase.c')
-rw-r--r-- | linux-2.4.x/fs/jffs2/erase.c | 487 |
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; } |