cregit-Linux how code gets into the kernel

Release 4.11 fs/jffs2/wbuf.c

Directory: fs/jffs2
/*
 * JFFS2 -- Journalling Flash File System, Version 2.
 *
 * Copyright © 2001-2007 Red Hat, Inc.
 * Copyright © 2004 Thomas Gleixner <tglx@linutronix.de>
 *
 * Created by David Woodhouse <dwmw2@infradead.org>
 * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
 *
 * For licensing information, see the file 'LICENCE' in this directory.
 *
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/crc32.h>
#include <linux/mtd/nand.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/writeback.h>

#include "nodelist.h"

/* For testing write failures */

#undef BREAKME

#undef BREAKMEHEADER

#ifdef BREAKME

static unsigned char *brokenbuf;
#endif


#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )

#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )

/* max. erase failures before we mark a block bad */

#define MAX_ERASE_FAILURES 	2


struct 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; }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse7198.61%266.67%
Andrew Morton11.39%133.33%
Total72100.00%3100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse61100.00%2100.00%
Total61100.00%2100.00%


static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inodirty *new; /* Schedule delayed write-buffer write-out */ jffs2_dirty_trigger(c); if (jffs2_wbuf_pending_for_ino(c, ino)) return; new = kmalloc(sizeof(*new), GFP_KERNEL); if (!new) { jffs2_dbg(1, "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; }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse9194.79%240.00%
Joe Perches33.12%120.00%
Joakim Tjernlund11.04%120.00%
Artem B. Bityutskiy11.04%120.00%
Total96100.00%5100.00%


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); jffs2_dbg(1, "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. */ jffs2_dbg(1, "...and adding to erase_pending_list\n"); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_garbage_collect_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ jffs2_dbg(1, "...and adding to erasable_list\n"); list_add_tail(&jeb->list, &c->erasable_list); } } }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse13793.84%266.67%
Joe Perches96.16%133.33%
Total146100.00%3100.00%

#define REFILE_NOTEMPTY 0 #define REFILE_ANYWAY 1
static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty) { jffs2_dbg(1, "About to refile bad block at %08x\n", jeb->offset); /* 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) { jffs2_dbg(1, "Refiling block at %08x to bad_used_list\n", jeb->offset); list_add(&jeb->list, &c->bad_used_list); } else { BUG_ON(allow_empty == REFILE_NOTEMPTY); /* It has to have had some nodes or we couldn't be here */ jffs2_dbg(1, "Refiling block at %08x to erase_pending_list\n", jeb->offset); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_garbage_collect_trigger(c); } if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) { uint32_t oldfree = jeb->free_size; jffs2_link_node_ref(c, jeb, (jeb->offset+c->sector_size-oldfree) | REF_OBSOLETE, oldfree, NULL); /* convert to wasted */ c->wasted_size += oldfree; jeb->wasted_size += oldfree; c->dirty_size -= oldfree; jeb->dirty_size -= oldfree; } jffs2_dbg_dump_block_lists_nolock(c); jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse19587.84%444.44%
Estelle Hammache94.05%222.22%
Joe Perches94.05%111.11%
Artem B. Bityutskiy94.05%222.22%
Total222100.00%9100.00%


static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_node_ref *raw, union jffs2_node_union *node) { struct jffs2_node_frag *frag; struct jffs2_full_dirent *fd; dbg_noderef("incore_replace_raw: node at %p is {%04x,%04x}\n", node, je16_to_cpu(node->u.magic), je16_to_cpu(node->u.nodetype)); BUG_ON(je16_to_cpu(node->u.magic) != 0x1985 && je16_to_cpu(node->u.magic) != 0); switch (je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: if (f->metadata && f->metadata->raw == raw) { dbg_noderef("Will replace ->raw in f->metadata at %p\n", f->metadata); return &f->metadata->raw; } frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset)); BUG_ON(!frag); /* Find a frag which refers to the full_dnode we want to modify */ while (!frag->node || frag->node->raw != raw) { frag = frag_next(frag); BUG_ON(!frag); } dbg_noderef("Will replace ->raw in full_dnode at %p\n", frag->node); return &frag->node->raw; case JFFS2_NODETYPE_DIRENT: for (fd = f->dents; fd; fd = fd->next) { if (fd->raw == raw) { dbg_noderef("Will replace ->raw in full_dirent at %p\n", fd); return &fd->raw; } } BUG(); default: dbg_noderef("Don't care about replacing raw for nodetype %x\n", je16_to_cpu(node->u.nodetype)); break; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse277100.00%2100.00%
Total277100.00%2100.00%

#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf, uint32_t ofs) { int ret; size_t retlen; char *eccstr; ret = mtd_read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify); if (ret && ret != -EUCLEAN && ret != -EBADMSG) { pr_warn("%s(): Read back of page at %08x failed: %d\n", __func__, c->wbuf_ofs, ret); return ret; } else if (retlen != c->wbuf_pagesize) { pr_warn("%s(): Read back of page at %08x gave short read: %zd not %d\n", __func__, ofs, retlen, c->wbuf_pagesize); return -EIO; } if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize)) return 0; if (ret == -EUCLEAN) eccstr = "corrected"; else if (ret == -EBADMSG) eccstr = "correction failed"; else eccstr = "OK or unused"; pr_warn("Write verify error (ECC %s) at %08x. Wrote:\n", eccstr, c->wbuf_ofs); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, c->wbuf, c->wbuf_pagesize, 0); pr_warn("Read back:\n"); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, c->wbuf_verify, c->wbuf_pagesize, 0); return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse21595.13%133.33%
Joe Perches104.42%133.33%
Artem B. Bityutskiy10.44%133.33%
Total226100.00%3100.00%

#else #define jffs2_verify_write(c,b,o) (0) #endif /* 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 *raw, *next, *first_raw = NULL; size_t retlen; int ret; int nr_refile = 0; unsigned char *buf; uint32_t start, end, ofs, len; jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; spin_lock(&c->erase_completion_lock); if (c->wbuf_ofs % c->mtd->erasesize) jffs2_block_refile(c, jeb, REFILE_NOTEMPTY); else jffs2_block_refile(c, jeb, REFILE_ANYWAY); spin_unlock(&c->erase_completion_lock); BUG_ON(!ref_obsolete(jeb->last_node)); /* Find the first node to be recovered, by skipping over every node which ends before the wbuf starts, or which is obsolete. */ for (next = raw = jeb->first_node; next; raw = next) { next = ref_next(raw); if (ref_obsolete(raw) || (next && ref_offset(next) <= c->wbuf_ofs)) { dbg_noderef("Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", ref_offset(raw), ref_flags(raw), (ref_offset(raw) + ref_totlen(c, jeb, raw)), c->wbuf_ofs); continue; } dbg_noderef("First node to be recovered is at 0x%08x(%d)-0x%08x\n", ref_offset(raw), ref_flags(raw), (ref_offset(raw) + ref_totlen(c, jeb, raw))); first_raw = raw; break; } if (!first_raw) { /* All nodes were obsolete. Nothing to recover. */ jffs2_dbg(1, "No non-obsolete nodes to be recovered. Just filing block bad\n"); c->wbuf_len = 0; return; } start = ref_offset(first_raw); end = ref_offset(jeb->last_node); nr_refile = 1; /* Count the number of refs which need to be copied */ while ((raw = ref_next(raw)) != jeb->last_node) nr_refile++; dbg_noderef("wbuf recover %08x-%08x (%d bytes in %d nodes)\n", start, end, end - start, nr_refile); 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) { pr_crit("Malloc failure in wbuf recovery. Data loss ensues.\n"); goto read_failed; } /* Do the read... */ ret = mtd_read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); /* ECC recovered ? */ if ((ret == -EUCLEAN || ret == -EBADMSG) && (retlen == c->wbuf_ofs - start)) ret = 0; if (ret || retlen != c->wbuf_ofs - start) { pr_crit("Old data are already lost in wbuf recovery. Data loss ensues.\n"); kfree(buf); buf = NULL; read_failed: first_raw = ref_next(first_raw); nr_refile--; while (first_raw && ref_obsolete(first_raw)) { first_raw = ref_next(first_raw); nr_refile--; } /* If this was the only node to be recovered, give up */ if (!first_raw) { c->wbuf_len = 0; return; } /* It wasn't. Go on and try to recover nodes complete in the wbuf */ start = ref_offset(first_raw); dbg_noderef("wbuf now recover %08x-%08x (%d bytes in %d nodes)\n", start, end, end - start, nr_refile); } 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, &len, JFFS2_SUMMARY_NOSUM_SIZE); if (ret) { pr_warn("Failed to allocate space for wbuf recovery. Data loss ensues.\n"); kfree(buf); return; } /* The summary is not recovered, so it must be disabled for this erase block */ jffs2_sum_disable_collecting(c->summary); ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile); if (ret) { pr_warn("Failed to allocate node refs for wbuf recovery. Data loss ensues.\n"); kfree(buf); return; } ofs = write_ofs(c); if (end-start >= c->wbuf_pagesize) { /* Need to do another write immediately, but it's possible that this is just because the wbuf itself is completely full, and there's nothing earlier read back from the flash. Hence 'buf' isn't necessarily what we're writing from. */ unsigned char *rewrite_buf = buf?:c->wbuf; uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); jffs2_dbg(1, "Write 0x%x bytes at 0x%08x in wbuf recover\n", towrite, ofs); #ifdef BREAKMEHEADER static int breakme; if (breakme++ == 20) { pr_notice("Faking write error at 0x%08x\n", ofs); breakme = 0; mtd_write(c->mtd, ofs, towrite, &retlen, brokenbuf); ret = -EIO; } else #endif ret = mtd_write(c->mtd, ofs, towrite, &retlen, rewrite_buf); if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) { /* Argh. We tried. Really we did. */ pr_crit("Recovery of wbuf failed due to a second write error\n"); kfree(buf); if (retlen) jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, first_raw), NULL); return; } pr_notice("Recovery of wbuf succeeded to %08x\n", ofs); c->wbuf_len = (end - start) - towrite; c->wbuf_ofs = ofs + towrite; memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len); /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ } else { /* OK, now we're left with the dregs in whichever buffer we're using */ if (buf) { memcpy(c->wbuf, buf, end-start); } 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); for (raw = first_raw; raw != jeb->last_node; raw = ref_next(raw)) { uint32_t rawlen = ref_totlen(c, jeb, raw); struct jffs2_inode_cache *ic; struct jffs2_raw_node_ref *new_ref; struct jffs2_raw_node_ref **adjust_ref = NULL; struct jffs2_inode_info *f = NULL; jffs2_dbg(1, "Refiling block of %08x at %08x(%d) to %08x\n", rawlen, ref_offset(raw), ref_flags(raw), ofs); ic = jffs2_raw_ref_to_ic(raw); /* Ick. This XATTR mess should be fixed shortly... */ if (ic && ic->class == RAWNODE_CLASS_XATTR_DATUM) { struct jffs2_xattr_datum *xd = (void *)ic; BUG_ON(xd->node != raw); adjust_ref = &xd->node; raw->next_in_ino = NULL; ic = NULL; } else if (ic && ic->class == RAWNODE_CLASS_XATTR_REF) { struct jffs2_xattr_datum *xr = (void *)ic; BUG_ON(xr->node != raw); adjust_ref = &xr->node; raw->next_in_ino = NULL; ic = NULL; } else if (ic && ic->class == RAWNODE_CLASS_INODE_CACHE) { struct jffs2_raw_node_ref **p = &ic->nodes; /* Remove the old node from the per-inode list */ while (*p && *p != (void *)ic) { if (*p == raw) { (*p) = (raw->next_in_ino); raw->next_in_ino = NULL; break; } p = &((*p)->next_in_ino); } if (ic->state == INO_STATE_PRESENT && !ref_obsolete(raw)) { /* If it's an in-core inode, then we have to adjust any full_dirent or full_dnode structure to point to the new version instead of the old */ f = jffs2_gc_fetch_inode(c, ic->ino, !ic->pino_nlink); if (IS_ERR(f)) { /* Should never happen; it _must_ be present */ JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n", ic->ino, PTR_ERR(f)); BUG(); } /* We don't lock f->sem. There's a number of ways we could end up in here with it already being locked, and nobody's going to modify it on us anyway because we hold the alloc_sem. We're only changing one ->raw pointer too, which we can get away with without upsetting readers. */ adjust_ref = jffs2_incore_replace_raw(c, f, raw, (void *)(buf?:c->wbuf) + (ref_offset(raw) - start)); } else if (unlikely(ic->state != INO_STATE_PRESENT && ic->state != INO_STATE_CHECKEDABSENT && ic->state != INO_STATE_GC)) { JFFS2_ERROR("Inode #%u is in strange state %d!\n", ic->ino, ic->state); BUG(); } } new_ref = jffs2_link_node_ref(c, new_jeb, ofs | ref_flags(raw), rawlen, ic); if (adjust_ref) { BUG_ON(*adjust_ref != raw); *adjust_ref = new_ref; } if (f) jffs2_gc_release_inode(c, f); if (!ref_obsolete(raw)) { jeb->dirty_size += rawlen; jeb->used_size -= rawlen; c->dirty_size += rawlen; c->used_size -= rawlen; raw->flash_offset = ref_offset(raw) | REF_OBSOLETE; BUG_ON(raw->next_in_ino); } ofs += rawlen; } kfree(buf); /* Fix up the original jeb now it's on the bad_list */ if (first_raw == jeb->first_node) { jffs2_dbg(1, "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset); list_move(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_garbage_collect_trigger(c); } jffs2_dbg_acct_sanity_check_nolock(c, jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); jffs2_dbg_acct_sanity_check_nolock(c, new_jeb); jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb); spin_unlock(&c->erase_completion_lock); jffs2_dbg(1, "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n", c->wbuf_ofs, c->wbuf_len); }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse145293.98%1856.25%
Vitaly Wool221.42%13.12%
Joe Perches221.42%26.25%
Estelle Hammache150.97%13.12%
Thomas Gleixner120.78%39.38%
Artem B. Bityutskiy110.71%412.50%
Adrian Hunter80.52%13.12%
Ferenc Havasi20.13%13.12%
Akinobu Mita10.06%13.12%
Total1545100.00%32100.00%

/* Meaning of pad argument: 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. 1: Pad, do not adjust nextblock free_size 2: Pad, adjust nextblock free_size */ #define NOPAD 0 #define PAD_NOACCOUNT 1 #define PAD_ACCOUNTING 2
static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) { struct jffs2_eraseblock *wbuf_jeb; int ret; size_t retlen; /* Nothing to do if not write-buffering the flash. In particular, we shouldn't del_timer() the timer we never initialised. */ if (!jffs2_is_writebuffered(c)) return 0; if (!mutex_is_locked(&c->alloc_sem)) { pr_crit("jffs2_flush_wbuf() called with alloc_sem not locked!\n"); BUG(); } if (!c->wbuf_len) /* already checked c->wbuf above */ return 0; wbuf_jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; if (jffs2_prealloc_raw_node_refs(c, wbuf_jeb, c->nextblock->allocated_refs + 1)) return