?? wbuf.c
字號:
/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de> * * Created by David Woodhouse <dwmw2@redhat.com> * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de> * * For licensing information, see the file 'LICENCE' in this directory. * * $Id: wbuf.c,v 1.72 2004/09/11 19:22:43 gleixner Exp $ * */#include <linux/kernel.h>#include <linux/slab.h>#include <linux/mtd/mtd.h>#include <linux/crc32.h>#include <linux/mtd/nand.h>#include "nodelist.h"/* For testing write failures */#undef BREAKME#undef BREAKMEHEADER#ifdef BREAKMEstatic unsigned char *brokenbuf;#endif/* max. erase failures before we mark a block bad */#define MAX_ERASE_FAILURES 2/* two seconds timeout for timed wbuf-flushing */#define WBUF_FLUSH_TIMEOUT 2 * HZstruct jffs2_inodirty { uint32_t ino; struct jffs2_inodirty *next;};static struct jffs2_inodirty inodirty_nomem;static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino){ struct jffs2_inodirty *this = c->wbuf_inodes; /* If a malloc failed, consider _everything_ dirty */ if (this == &inodirty_nomem) return 1; /* If ino == 0, _any_ non-GC writes mean 'yes' */ if (this && !ino) return 1; /* Look to see if the inode in question is pending in the wbuf */ while (this) { if (this->ino == ino) return 1; this = this->next; } return 0;}static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c){ struct jffs2_inodirty *this; this = c->wbuf_inodes; if (this != &inodirty_nomem) { while (this) { struct jffs2_inodirty *next = this->next; kfree(this); this = next; } } c->wbuf_inodes = NULL;}static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino){ struct jffs2_inodirty *new; /* Mark the superblock dirty so that kupdated will flush... */ OFNI_BS_2SFFJ(c)->s_dirt = 1; if (jffs2_wbuf_pending_for_ino(c, ino)) return; new = kmalloc(sizeof(*new), GFP_KERNEL); if (!new) { D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n")); jffs2_clear_wbuf_ino_list(c); c->wbuf_inodes = &inodirty_nomem; return; } new->ino = ino; new->next = c->wbuf_inodes; c->wbuf_inodes = new; return;}static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c){ struct list_head *this, *next; static int n; if (list_empty(&c->erasable_pending_wbuf_list)) return; list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset)); list_del(this); if ((jiffies + (n++)) & 127) { /* Most of the time, we just erase it immediately. Otherwise we spend ages scanning it on mount, etc. */ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); list_add_tail(&jeb->list, &c->erasable_list); } }}/* Recover from failure to write wbuf. Recover the nodes up to the * wbuf, not the one which we were starting to try to write. */static void jffs2_wbuf_recover(struct jffs2_sb_info *c){ struct jffs2_eraseblock *jeb, *new_jeb; struct jffs2_raw_node_ref **first_raw, **raw; size_t retlen; int ret; unsigned char *buf; uint32_t start, end, ofs, len; spin_lock(&c->erase_completion_lock); jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; D1(printk("About to refile bad block at %08x\n", jeb->offset)); D2(jffs2_dump_block_lists(c)); /* File the existing block on the bad_used_list.... */ if (c->nextblock == jeb) c->nextblock = NULL; else /* Not sure this should ever happen... need more coffee */ list_del(&jeb->list); if (jeb->first_node) { D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset)); list_add(&jeb->list, &c->bad_used_list); } else { BUG(); /* It has to have had some nodes or we couldn't be here */ D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } D2(jffs2_dump_block_lists(c)); /* Adjust its size counts accordingly */ c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->wasted_size += jeb->free_size; jeb->free_size = 0; ACCT_SANITY_CHECK(c,jeb); D1(ACCT_PARANOIA_CHECK(jeb)); /* Find the first node to be recovered, by skipping over every node which ends before the wbuf starts, or which is obsolete. */ first_raw = &jeb->first_node; while (*first_raw && (ref_obsolete(*first_raw) || (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) { D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", ref_offset(*first_raw), ref_flags(*first_raw), (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)), c->wbuf_ofs)); first_raw = &(*first_raw)->next_phys; } if (!*first_raw) { /* All nodes were obsolete. Nothing to recover. */ D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n")); spin_unlock(&c->erase_completion_lock); return; } start = ref_offset(*first_raw); end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw); /* Find the last node to be recovered */ raw = first_raw; while ((*raw)) { if (!ref_obsolete(*raw)) end = ref_offset(*raw) + ref_totlen(c, jeb, *raw); raw = &(*raw)->next_phys; } spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end)); buf = NULL; if (start < c->wbuf_ofs) { /* First affected node was already partially written. * Attempt to reread the old data into our buffer. */ buf = kmalloc(end - start, GFP_KERNEL); if (!buf) { printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n"); goto read_failed; } /* Do the read... */ ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { /* ECC recovered */ ret = 0; } if (ret || retlen != c->wbuf_ofs - start) { printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); kfree(buf); buf = NULL; read_failed: first_raw = &(*first_raw)->next_phys; /* If this was the only node to be recovered, give up */ if (!(*first_raw)) return; /* It wasn't. Go on and try to recover nodes complete in the wbuf */ start = ref_offset(*first_raw); } else { /* Read succeeded. Copy the remaining data from the wbuf */ memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); } } /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. Either 'buf' contains the data, or we find it in the wbuf */ /* ... and get an allocation of space from a shiny new block instead */ ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len); if (ret) { printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); if (buf) kfree(buf); return; } if (end-start >= c->wbuf_pagesize) { /* Need to do another write immediately. This, btw, means that we'll be writing from 'buf' and not from the wbuf. Since if we're writing from the wbuf there won't be more than a wbuf full of data, now will there? :) */ uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n", towrite, ofs)); #ifdef BREAKMEHEADER static int breakme; if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs); breakme = 0; c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, brokenbuf, NULL, c->oobinfo); ret = -EIO; } else#endif ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, buf, NULL, c->oobinfo); if (ret || retlen != towrite) { /* Argh. We tried. Really we did. */ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); kfree(buf); if (retlen) { struct jffs2_raw_node_ref *raw2; raw2 = jffs2_alloc_raw_node_ref(); if (!raw2) return; raw2->flash_offset = ofs | REF_OBSOLETE; raw2->__totlen = ref_totlen(c, jeb, *first_raw); raw2->next_phys = NULL; raw2->next_in_ino = NULL; jffs2_add_physical_node_ref(c, raw2); } return; } printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs); c->wbuf_len = (end - start) - towrite; c->wbuf_ofs = ofs + towrite; memcpy(c->wbuf, buf + towrite, c->wbuf_len); /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ kfree(buf); } else { /* OK, now we're left with the dregs in whichever buffer we're using */ if (buf) { memcpy(c->wbuf, buf, end-start); kfree(buf); } else { memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); } c->wbuf_ofs = ofs; c->wbuf_len = end - start; } /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */ new_jeb = &c->blocks[ofs / c->sector_size]; spin_lock(&c->erase_completion_lock); if (new_jeb->first_node) { /* Odd, but possible with ST flash later maybe */ new_jeb->last_node->next_phys = *first_raw; } else { new_jeb->first_node = *first_raw; } raw = first_raw; while (*raw) { uint32_t rawlen = ref_totlen(c, jeb, *raw); D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", rawlen, ref_offset(*raw), ref_flags(*raw), ofs)); if (ref_obsolete(*raw)) { /* Shouldn't really happen much */ new_jeb->dirty_size += rawlen; new_jeb->free_size -= rawlen; c->dirty_size += rawlen; } else { new_jeb->used_size += rawlen; new_jeb->free_size -= rawlen; jeb->dirty_size += rawlen; jeb->used_size -= rawlen; c->dirty_size += rawlen; } c->free_size -= rawlen; (*raw)->flash_offset = ofs | ref_flags(*raw); ofs += rawlen; new_jeb->last_node = *raw; raw = &(*raw)->next_phys; } /* Fix up the original jeb now it's on the bad_list */ *first_raw = NULL; if (first_raw == &jeb->first_node) { jeb->last_node = NULL; D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -