X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fjffs2%2Ferase.c;h=f6efdbabbf3e20b674cf8ac086a9574356265fd1;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=f7a940962e5ff2254633d48c706cba1c0e02a1f1;hpb=db216c3d5e4c040e557a50f8f5d35d5c415e8c1c;p=linux-2.6.git diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index f7a940962..f6efdbabb 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.53 2003/10/08 17:22:54 dwmw2 Exp $ + * $Id: erase.c,v 1.60 2004/06/30 17:26:15 dbrown Exp $ * */ @@ -28,7 +28,7 @@ struct erase_priv_struct { #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); +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); @@ -36,6 +36,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; + uint32_t bad_offset; #ifdef __ECOS ret = jffs2_flash_erase(c, jeb); if (!ret) { @@ -65,18 +66,16 @@ 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; - /* NAND , read out the fail counter, if possible */ - if (!jffs2_can_mark_obsolete(c)) - jffs2_nand_read_failcnt(c,jeb); - ret = c->mtd->erase(c->mtd, instr); if (!ret) return; + bad_offset = instr->fail_addr; kfree(instr); #endif /* __ECOS */ @@ -98,10 +97,10 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) else printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret); - jffs2_erase_failed(c, jeb); + 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; @@ -118,6 +117,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) 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)); @@ -144,6 +148,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) } spin_unlock(&c->erase_completion_lock); + done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); up(&c->erase_free_sem); @@ -160,16 +165,34 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo jffs2_erase_pending_trigger(c); } -static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { - 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); + /* 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_cleanmarker_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 @@ -179,7 +202,7 @@ static void jffs2_erase_callback(struct erase_info *instr) 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); - jffs2_erase_failed(priv->c, priv->jeb); + jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr); } else { jffs2_erase_succeeded(priv->c, priv->jeb); } @@ -277,17 +300,13 @@ 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) -{ - OFNI_BS_2SFFJ(c)->s_dirt = 1; -} - static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *marker_ref = NULL; unsigned char *ebuf; size_t retlen; int ret; + uint32_t bad_offset; if (!jffs2_cleanmarker_oob(c)) { marker_ref = jffs2_alloc_raw_node_ref(); @@ -312,6 +331,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); int i; + bad_offset = ofs; + ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); @@ -325,22 +346,21 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb /* 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_offset += i; + printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset); bad: if (!jffs2_cleanmarker_oob(c)) jffs2_free_raw_node_ref(marker_ref); - else - jffs2_write_nand_badblock( c ,jeb ); kfree(ebuf); bad2: spin_lock(&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--; + /* 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); - wake_up(&c->erase_wait); + jffs2_erase_failed(c, jeb, bad_offset); return; } } @@ -349,7 +369,9 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb } kfree(ebuf); } - + + bad_offset = jeb->offset; + /* Write the erase complete marker */ D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); if (jffs2_cleanmarker_oob(c)) { @@ -370,29 +392,30 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb .totlen = cpu_to_je32(c->cleanmarker_size) }; - marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4)); + marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); - ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker); + /* We only write the header; the rest was noise or padding anyway */ + ret = jffs2_flash_write(c, 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 (retlen != je32_to_cpu(marker.totlen)) { - printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n", - jeb->offset, je32_to_cpu(marker.totlen), retlen); + if (retlen != sizeof(marker)) { + printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", + jeb->offset, sizeof(marker), retlen); goto bad2; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = jeb->offset | REF_NORMAL; - marker_ref->totlen = PAD(je32_to_cpu(marker.totlen)); + marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; - jeb->free_size = c->sector_size - marker_ref->totlen; - jeb->used_size = marker_ref->totlen; + jeb->free_size = c->sector_size - c->cleanmarker_size; + jeb->used_size = c->cleanmarker_size; jeb->dirty_size = 0; jeb->wasted_size = 0; }