18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * JFFS2 -- Journalling Flash File System, Version 2. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright © 2001-2007 Red Hat, Inc. 58c2ecf20Sopenharmony_ci * Copyright © 2004-2010 David Woodhouse <dwmw2@infradead.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Created by David Woodhouse <dwmw2@infradead.org> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * For licensing information, see the file 'LICENCE' in this directory. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 188c2ecf20Sopenharmony_ci#include <linux/compiler.h> 198c2ecf20Sopenharmony_ci#include <linux/crc32.h> 208c2ecf20Sopenharmony_ci#include <linux/sched.h> 218c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 228c2ecf20Sopenharmony_ci#include "nodelist.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); 258c2ecf20Sopenharmony_cistatic void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); 268c2ecf20Sopenharmony_cistatic void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void jffs2_erase_block(struct jffs2_sb_info *c, 298c2ecf20Sopenharmony_ci struct jffs2_eraseblock *jeb) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci int ret; 328c2ecf20Sopenharmony_ci uint32_t bad_offset; 338c2ecf20Sopenharmony_ci#ifdef __ECOS 348c2ecf20Sopenharmony_ci ret = jffs2_flash_erase(c, jeb); 358c2ecf20Sopenharmony_ci if (!ret) { 368c2ecf20Sopenharmony_ci jffs2_erase_succeeded(c, jeb); 378c2ecf20Sopenharmony_ci return; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci bad_offset = jeb->offset; 408c2ecf20Sopenharmony_ci#else /* Linux */ 418c2ecf20Sopenharmony_ci struct erase_info *instr; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n", 448c2ecf20Sopenharmony_ci __func__, 458c2ecf20Sopenharmony_ci jeb->offset, jeb->offset, jeb->offset + c->sector_size); 468c2ecf20Sopenharmony_ci instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL); 478c2ecf20Sopenharmony_ci if (!instr) { 488c2ecf20Sopenharmony_ci pr_warn("kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); 498c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 508c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 518c2ecf20Sopenharmony_ci list_move(&jeb->list, &c->erase_pending_list); 528c2ecf20Sopenharmony_ci c->erasing_size -= c->sector_size; 538c2ecf20Sopenharmony_ci c->dirty_size += c->sector_size; 548c2ecf20Sopenharmony_ci jeb->dirty_size = c->sector_size; 558c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 568c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 578c2ecf20Sopenharmony_ci return; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci memset(instr, 0, sizeof(*instr)); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci instr->addr = jeb->offset; 638c2ecf20Sopenharmony_ci instr->len = c->sector_size; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret = mtd_erase(c->mtd, instr); 668c2ecf20Sopenharmony_ci if (!ret) { 678c2ecf20Sopenharmony_ci jffs2_erase_succeeded(c, jeb); 688c2ecf20Sopenharmony_ci kfree(instr); 698c2ecf20Sopenharmony_ci return; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci bad_offset = instr->fail_addr; 738c2ecf20Sopenharmony_ci kfree(instr); 748c2ecf20Sopenharmony_ci#endif /* __ECOS */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (ret == -ENOMEM || ret == -EAGAIN) { 778c2ecf20Sopenharmony_ci /* Erase failed immediately. Refile it on the list */ 788c2ecf20Sopenharmony_ci jffs2_dbg(1, "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", 798c2ecf20Sopenharmony_ci jeb->offset, ret); 808c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 818c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 828c2ecf20Sopenharmony_ci list_move(&jeb->list, &c->erase_pending_list); 838c2ecf20Sopenharmony_ci c->erasing_size -= c->sector_size; 848c2ecf20Sopenharmony_ci c->dirty_size += c->sector_size; 858c2ecf20Sopenharmony_ci jeb->dirty_size = c->sector_size; 868c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 878c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 888c2ecf20Sopenharmony_ci return; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (ret == -EROFS) 928c2ecf20Sopenharmony_ci pr_warn("Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", 938c2ecf20Sopenharmony_ci jeb->offset); 948c2ecf20Sopenharmony_ci else 958c2ecf20Sopenharmony_ci pr_warn("Erase at 0x%08x failed immediately: errno %d\n", 968c2ecf20Sopenharmony_ci jeb->offset, ret); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci jffs2_erase_failed(c, jeb, bad_offset); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciint jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct jffs2_eraseblock *jeb; 1048c2ecf20Sopenharmony_ci int work_done = 0; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci while (!list_empty(&c->erase_complete_list) || 1118c2ecf20Sopenharmony_ci !list_empty(&c->erase_pending_list)) { 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (!list_empty(&c->erase_complete_list)) { 1148c2ecf20Sopenharmony_ci jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); 1158c2ecf20Sopenharmony_ci list_move(&jeb->list, &c->erase_checking_list); 1168c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 1178c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 1188c2ecf20Sopenharmony_ci jffs2_mark_erased_block(c, jeb); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci work_done++; 1218c2ecf20Sopenharmony_ci if (!--count) { 1228c2ecf20Sopenharmony_ci jffs2_dbg(1, "Count reached. jffs2_erase_pending_blocks leaving\n"); 1238c2ecf20Sopenharmony_ci goto done; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci } else if (!list_empty(&c->erase_pending_list)) { 1278c2ecf20Sopenharmony_ci jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); 1288c2ecf20Sopenharmony_ci jffs2_dbg(1, "Starting erase of pending block 0x%08x\n", 1298c2ecf20Sopenharmony_ci jeb->offset); 1308c2ecf20Sopenharmony_ci list_del(&jeb->list); 1318c2ecf20Sopenharmony_ci c->erasing_size += c->sector_size; 1328c2ecf20Sopenharmony_ci c->wasted_size -= jeb->wasted_size; 1338c2ecf20Sopenharmony_ci c->free_size -= jeb->free_size; 1348c2ecf20Sopenharmony_ci c->used_size -= jeb->used_size; 1358c2ecf20Sopenharmony_ci c->dirty_size -= jeb->dirty_size; 1368c2ecf20Sopenharmony_ci jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; 1378c2ecf20Sopenharmony_ci jffs2_free_jeb_node_refs(c, jeb); 1388c2ecf20Sopenharmony_ci list_add(&jeb->list, &c->erasing_list); 1398c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 1408c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci jffs2_erase_block(c, jeb); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci BUG(); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Be nice */ 1498c2ecf20Sopenharmony_ci cond_resched(); 1508c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 1518c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 1558c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 1568c2ecf20Sopenharmony_ci done: 1578c2ecf20Sopenharmony_ci jffs2_dbg(1, "jffs2_erase_pending_blocks completed\n"); 1588c2ecf20Sopenharmony_ci return work_done; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci jffs2_dbg(1, "Erase completed successfully at 0x%08x\n", jeb->offset); 1648c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 1658c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 1668c2ecf20Sopenharmony_ci list_move_tail(&jeb->list, &c->erase_complete_list); 1678c2ecf20Sopenharmony_ci /* Wake the GC thread to mark them clean */ 1688c2ecf20Sopenharmony_ci jffs2_garbage_collect_trigger(c); 1698c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 1708c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 1718c2ecf20Sopenharmony_ci wake_up(&c->erase_wait); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci /* For NAND, if the failure did not occur at the device level for a 1778c2ecf20Sopenharmony_ci specific physical page, don't bother updating the bad block table. */ 1788c2ecf20Sopenharmony_ci if (jffs2_cleanmarker_oob(c) && (bad_offset != (uint32_t)MTD_FAIL_ADDR_UNKNOWN)) { 1798c2ecf20Sopenharmony_ci /* We had a device-level failure to erase. Let's see if we've 1808c2ecf20Sopenharmony_ci failed too many times. */ 1818c2ecf20Sopenharmony_ci if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { 1828c2ecf20Sopenharmony_ci /* We'd like to give this block another try. */ 1838c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 1848c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 1858c2ecf20Sopenharmony_ci list_move(&jeb->list, &c->erase_pending_list); 1868c2ecf20Sopenharmony_ci c->erasing_size -= c->sector_size; 1878c2ecf20Sopenharmony_ci c->dirty_size += c->sector_size; 1888c2ecf20Sopenharmony_ci jeb->dirty_size = c->sector_size; 1898c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 1908c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 1918c2ecf20Sopenharmony_ci return; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 1968c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 1978c2ecf20Sopenharmony_ci c->erasing_size -= c->sector_size; 1988c2ecf20Sopenharmony_ci c->bad_size += c->sector_size; 1998c2ecf20Sopenharmony_ci list_move(&jeb->list, &c->bad_list); 2008c2ecf20Sopenharmony_ci c->nr_erasing_blocks--; 2018c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 2028c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 2038c2ecf20Sopenharmony_ci wake_up(&c->erase_wait); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* Hmmm. Maybe we should accept the extra space it takes and make 2078c2ecf20Sopenharmony_ci this a standard doubly-linked list? */ 2088c2ecf20Sopenharmony_cistatic inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c, 2098c2ecf20Sopenharmony_ci struct jffs2_raw_node_ref *ref, struct jffs2_eraseblock *jeb) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct jffs2_inode_cache *ic = NULL; 2128c2ecf20Sopenharmony_ci struct jffs2_raw_node_ref **prev; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci prev = &ref->next_in_ino; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Walk the inode's list once, removing any nodes from this eraseblock */ 2178c2ecf20Sopenharmony_ci while (1) { 2188c2ecf20Sopenharmony_ci if (!(*prev)->next_in_ino) { 2198c2ecf20Sopenharmony_ci /* We're looking at the jffs2_inode_cache, which is 2208c2ecf20Sopenharmony_ci at the end of the linked list. Stash it and continue 2218c2ecf20Sopenharmony_ci from the beginning of the list */ 2228c2ecf20Sopenharmony_ci ic = (struct jffs2_inode_cache *)(*prev); 2238c2ecf20Sopenharmony_ci prev = &ic->nodes; 2248c2ecf20Sopenharmony_ci continue; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) { 2288c2ecf20Sopenharmony_ci /* It's in the block we're erasing */ 2298c2ecf20Sopenharmony_ci struct jffs2_raw_node_ref *this; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci this = *prev; 2328c2ecf20Sopenharmony_ci *prev = this->next_in_ino; 2338c2ecf20Sopenharmony_ci this->next_in_ino = NULL; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (this == ref) 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci continue; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci /* Not to be deleted. Skip */ 2418c2ecf20Sopenharmony_ci prev = &((*prev)->next_in_ino); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* PARANOIA */ 2458c2ecf20Sopenharmony_ci if (!ic) { 2468c2ecf20Sopenharmony_ci JFFS2_WARNING("inode_cache/xattr_datum/xattr_ref" 2478c2ecf20Sopenharmony_ci " not found in remove_node_refs()!!\n"); 2488c2ecf20Sopenharmony_ci return; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci jffs2_dbg(1, "Removed nodes in range 0x%08x-0x%08x from ino #%u\n", 2528c2ecf20Sopenharmony_ci jeb->offset, jeb->offset + c->sector_size, ic->ino); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci D2({ 2558c2ecf20Sopenharmony_ci int i=0; 2568c2ecf20Sopenharmony_ci struct jffs2_raw_node_ref *this; 2578c2ecf20Sopenharmony_ci printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n"); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci this = ic->nodes; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci printk(KERN_DEBUG); 2628c2ecf20Sopenharmony_ci while(this) { 2638c2ecf20Sopenharmony_ci pr_cont("0x%08x(%d)->", 2648c2ecf20Sopenharmony_ci ref_offset(this), ref_flags(this)); 2658c2ecf20Sopenharmony_ci if (++i == 5) { 2668c2ecf20Sopenharmony_ci printk(KERN_DEBUG); 2678c2ecf20Sopenharmony_ci i=0; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci this = this->next_in_ino; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci pr_cont("\n"); 2728c2ecf20Sopenharmony_ci }); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci switch (ic->class) { 2758c2ecf20Sopenharmony_ci#ifdef CONFIG_JFFS2_FS_XATTR 2768c2ecf20Sopenharmony_ci case RAWNODE_CLASS_XATTR_DATUM: 2778c2ecf20Sopenharmony_ci jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case RAWNODE_CLASS_XATTR_REF: 2808c2ecf20Sopenharmony_ci jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic); 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci#endif 2838c2ecf20Sopenharmony_ci default: 2848c2ecf20Sopenharmony_ci if (ic->nodes == (void *)ic && ic->pino_nlink == 0) 2858c2ecf20Sopenharmony_ci jffs2_del_ino_cache(c, ic); 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_civoid jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct jffs2_raw_node_ref *block, *ref; 2928c2ecf20Sopenharmony_ci jffs2_dbg(1, "Freeing all node refs for eraseblock offset 0x%08x\n", 2938c2ecf20Sopenharmony_ci jeb->offset); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci block = ref = jeb->first_node; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci while (ref) { 2988c2ecf20Sopenharmony_ci if (ref->flash_offset == REF_LINK_NODE) { 2998c2ecf20Sopenharmony_ci ref = ref->next_in_ino; 3008c2ecf20Sopenharmony_ci jffs2_free_refblock(block); 3018c2ecf20Sopenharmony_ci block = ref; 3028c2ecf20Sopenharmony_ci continue; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (ref->flash_offset != REF_EMPTY_NODE && ref->next_in_ino) 3058c2ecf20Sopenharmony_ci jffs2_remove_node_refs_from_ino_list(c, ref, jeb); 3068c2ecf20Sopenharmony_ci /* else it was a non-inode node or already removed, so don't bother */ 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ref++; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci jeb->first_node = jeb->last_node = NULL; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci void *ebuf; 3168c2ecf20Sopenharmony_ci uint32_t ofs; 3178c2ecf20Sopenharmony_ci size_t retlen; 3188c2ecf20Sopenharmony_ci int ret; 3198c2ecf20Sopenharmony_ci unsigned long *wordebuf; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ret = mtd_point(c->mtd, jeb->offset, c->sector_size, &retlen, 3228c2ecf20Sopenharmony_ci &ebuf, NULL); 3238c2ecf20Sopenharmony_ci if (ret != -EOPNOTSUPP) { 3248c2ecf20Sopenharmony_ci if (ret) { 3258c2ecf20Sopenharmony_ci jffs2_dbg(1, "MTD point failed %d\n", ret); 3268c2ecf20Sopenharmony_ci goto do_flash_read; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci if (retlen < c->sector_size) { 3298c2ecf20Sopenharmony_ci /* Don't muck about if it won't let us point to the whole erase sector */ 3308c2ecf20Sopenharmony_ci jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n", 3318c2ecf20Sopenharmony_ci retlen); 3328c2ecf20Sopenharmony_ci mtd_unpoint(c->mtd, jeb->offset, retlen); 3338c2ecf20Sopenharmony_ci goto do_flash_read; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci wordebuf = ebuf-sizeof(*wordebuf); 3368c2ecf20Sopenharmony_ci retlen /= sizeof(*wordebuf); 3378c2ecf20Sopenharmony_ci do { 3388c2ecf20Sopenharmony_ci if (*++wordebuf != ~0) 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci } while(--retlen); 3418c2ecf20Sopenharmony_ci mtd_unpoint(c->mtd, jeb->offset, c->sector_size); 3428c2ecf20Sopenharmony_ci if (retlen) { 3438c2ecf20Sopenharmony_ci pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08tx\n", 3448c2ecf20Sopenharmony_ci *wordebuf, 3458c2ecf20Sopenharmony_ci jeb->offset + 3468c2ecf20Sopenharmony_ci c->sector_size-retlen * sizeof(*wordebuf)); 3478c2ecf20Sopenharmony_ci return -EIO; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci do_flash_read: 3528c2ecf20Sopenharmony_ci ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); 3538c2ecf20Sopenharmony_ci if (!ebuf) { 3548c2ecf20Sopenharmony_ci pr_warn("Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", 3558c2ecf20Sopenharmony_ci jeb->offset); 3568c2ecf20Sopenharmony_ci return -EAGAIN; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci jffs2_dbg(1, "Verifying erase at 0x%08x\n", jeb->offset); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) { 3628c2ecf20Sopenharmony_ci uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); 3638c2ecf20Sopenharmony_ci int i; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci *bad_offset = ofs; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci ret = mtd_read(c->mtd, ofs, readlen, &retlen, ebuf); 3688c2ecf20Sopenharmony_ci if (ret) { 3698c2ecf20Sopenharmony_ci pr_warn("Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", 3708c2ecf20Sopenharmony_ci ofs, ret); 3718c2ecf20Sopenharmony_ci ret = -EIO; 3728c2ecf20Sopenharmony_ci goto fail; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci if (retlen != readlen) { 3758c2ecf20Sopenharmony_ci pr_warn("Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", 3768c2ecf20Sopenharmony_ci ofs, readlen, retlen); 3778c2ecf20Sopenharmony_ci ret = -EIO; 3788c2ecf20Sopenharmony_ci goto fail; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci for (i=0; i<readlen; i += sizeof(unsigned long)) { 3818c2ecf20Sopenharmony_ci /* It's OK. We know it's properly aligned */ 3828c2ecf20Sopenharmony_ci unsigned long *datum = ebuf + i; 3838c2ecf20Sopenharmony_ci if (*datum + 1) { 3848c2ecf20Sopenharmony_ci *bad_offset += i; 3858c2ecf20Sopenharmony_ci pr_warn("Newly-erased block contained word 0x%lx at offset 0x%08x\n", 3868c2ecf20Sopenharmony_ci *datum, *bad_offset); 3878c2ecf20Sopenharmony_ci ret = -EIO; 3888c2ecf20Sopenharmony_ci goto fail; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci ofs += readlen; 3928c2ecf20Sopenharmony_ci cond_resched(); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci ret = 0; 3958c2ecf20Sopenharmony_cifail: 3968c2ecf20Sopenharmony_ci kfree(ebuf); 3978c2ecf20Sopenharmony_ci return ret; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci size_t retlen; 4038c2ecf20Sopenharmony_ci int ret; 4048c2ecf20Sopenharmony_ci uint32_t bad_offset; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { 4078c2ecf20Sopenharmony_ci case -EAGAIN: goto refile; 4088c2ecf20Sopenharmony_ci case -EIO: goto filebad; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Write the erase complete marker */ 4128c2ecf20Sopenharmony_ci jffs2_dbg(1, "Writing erased marker to block at 0x%08x\n", jeb->offset); 4138c2ecf20Sopenharmony_ci bad_offset = jeb->offset; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Cleanmarker in oob area or no cleanmarker at all ? */ 4168c2ecf20Sopenharmony_ci if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) { 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (jffs2_cleanmarker_oob(c)) { 4198c2ecf20Sopenharmony_ci if (jffs2_write_nand_cleanmarker(c, jeb)) 4208c2ecf20Sopenharmony_ci goto filebad; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci } else { 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci struct kvec vecs[1]; 4258c2ecf20Sopenharmony_ci struct jffs2_unknown_node marker = { 4268c2ecf20Sopenharmony_ci .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), 4278c2ecf20Sopenharmony_ci .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), 4288c2ecf20Sopenharmony_ci .totlen = cpu_to_je32(c->cleanmarker_size) 4298c2ecf20Sopenharmony_ci }; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci jffs2_prealloc_raw_node_refs(c, jeb, 1); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci vecs[0].iov_base = (unsigned char *) ▮ 4368c2ecf20Sopenharmony_ci vecs[0].iov_len = sizeof(marker); 4378c2ecf20Sopenharmony_ci ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (ret || retlen != sizeof(marker)) { 4408c2ecf20Sopenharmony_ci if (ret) 4418c2ecf20Sopenharmony_ci pr_warn("Write clean marker to block at 0x%08x failed: %d\n", 4428c2ecf20Sopenharmony_ci jeb->offset, ret); 4438c2ecf20Sopenharmony_ci else 4448c2ecf20Sopenharmony_ci pr_warn("Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", 4458c2ecf20Sopenharmony_ci jeb->offset, sizeof(marker), retlen); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci goto filebad; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci /* Everything else got zeroed before the erase */ 4518c2ecf20Sopenharmony_ci jeb->free_size = c->sector_size; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 4548c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci c->erasing_size -= c->sector_size; 4578c2ecf20Sopenharmony_ci c->free_size += c->sector_size; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* Account for cleanmarker now, if it's in-band */ 4608c2ecf20Sopenharmony_ci if (c->cleanmarker_size && !jffs2_cleanmarker_oob(c)) 4618c2ecf20Sopenharmony_ci jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci list_move_tail(&jeb->list, &c->free_list); 4648c2ecf20Sopenharmony_ci c->nr_erasing_blocks--; 4658c2ecf20Sopenharmony_ci c->nr_free_blocks++; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci jffs2_dbg_acct_sanity_check_nolock(c, jeb); 4688c2ecf20Sopenharmony_ci jffs2_dbg_acct_paranoia_check_nolock(c, jeb); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 4718c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 4728c2ecf20Sopenharmony_ci wake_up(&c->erase_wait); 4738c2ecf20Sopenharmony_ci return; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cifilebad: 4768c2ecf20Sopenharmony_ci jffs2_erase_failed(c, jeb, bad_offset); 4778c2ecf20Sopenharmony_ci return; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cirefile: 4808c2ecf20Sopenharmony_ci /* Stick it back on the list from whence it came and come back later */ 4818c2ecf20Sopenharmony_ci mutex_lock(&c->erase_free_sem); 4828c2ecf20Sopenharmony_ci spin_lock(&c->erase_completion_lock); 4838c2ecf20Sopenharmony_ci jffs2_garbage_collect_trigger(c); 4848c2ecf20Sopenharmony_ci list_move(&jeb->list, &c->erase_complete_list); 4858c2ecf20Sopenharmony_ci spin_unlock(&c->erase_completion_lock); 4868c2ecf20Sopenharmony_ci mutex_unlock(&c->erase_free_sem); 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci} 489