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 *) ▮ 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