cregit-Linux how code gets into the kernel

Release 4.10 fs/jffs2/scan.c

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


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include <linux/compiler.h>
#include "nodelist.h"
#include "summary.h"
#include "debug.h"


#define DEFAULT_EMPTY_SCAN_SIZE 256


#define noisy_printk(noise, fmt, ...)					\
do {                                                                    \
        if (*(noise)) {                                                 \
                pr_notice(fmt, ##__VA_ARGS__);                          \
                (*(noise))--;                                           \
                if (!(*(noise)))                                        \
                        pr_notice("Further such events for this erase block will not be printed\n"); \
        }                                                               \
} while (0)


static uint32_t pseudo_random;

static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
				  unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s);

/* These helper functions _must_ increase ofs and also do the dirty/used space accounting.
 * Returning an error will abort the mount - bad checksums etc. should just mark the space
 * as dirty.
 */
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
				 struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s);
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
				 struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s);


static inline int min_free(struct jffs2_sb_info *c) { uint32_t min = 2 * sizeof(struct jffs2_raw_inode); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) return c->wbuf_pagesize; #endif return min; }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse4794.00%150.00%
andrew victorandrew victor36.00%150.00%
Total50100.00%2100.00%


static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) { if (sector_size < DEFAULT_EMPTY_SCAN_SIZE) return sector_size; else return DEFAULT_EMPTY_SCAN_SIZE; }

Contributors

PersonTokensPropCommitsCommitProp
andrew victorandrew victor23100.00%1100.00%
Total23100.00%1100.00%


static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; if ((ret = jffs2_prealloc_raw_node_refs(c, jeb, 1))) return ret; if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size))) return ret; /* Turned wasted size into dirty, since we apparently think it's recoverable now. */ jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { list_add(&jeb->list, &c->very_dirty_list); } else { list_add(&jeb->list, &c->dirty_list); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse133100.00%2100.00%
Total133100.00%2100.00%


int jffs2_scan_medium(struct jffs2_sb_info *c) { int i, ret; uint32_t empty_blocks = 0, bad_blocks = 0; unsigned char *flashbuf = NULL; uint32_t buf_size = 0; struct jffs2_summary *s = NULL; /* summary info collected by the scan process */ #ifndef __ECOS size_t pointlen, try_size; ret = mtd_point(c->mtd, 0, c->mtd->size, &pointlen, (void **)&flashbuf, NULL); if (!ret && pointlen < c->mtd->size) { /* Don't muck about if it won't let us point to the whole flash */ jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n", pointlen); mtd_unpoint(c->mtd, 0, pointlen); flashbuf = NULL; } if (ret && ret != -EOPNOTSUPP) jffs2_dbg(1, "MTD point failed %d\n", ret); #endif if (!flashbuf) { /* For NAND it's quicker to read a whole eraseblock at a time, apparently */ if (jffs2_cleanmarker_oob(c)) try_size = c->sector_size; else try_size = PAGE_SIZE; jffs2_dbg(1, "Trying to allocate readbuf of %zu " "bytes\n", try_size); flashbuf = mtd_kmalloc_up_to(c->mtd, &try_size); if (!flashbuf) return -ENOMEM; jffs2_dbg(1, "Allocated readbuf of %zu bytes\n", try_size); buf_size = (uint32_t)try_size; } if (jffs2_sum_active()) { s = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL); if (!s) { JFFS2_WARNING("Can't allocate memory for summary\n"); ret = -ENOMEM; goto out; } } for (i=0; i<c->nr_blocks; i++) { struct jffs2_eraseblock *jeb = &c->blocks[i]; cond_resched(); /* reset summary info for next eraseblock scan */ jffs2_sum_reset_collected(s); ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size, s); if (ret < 0) goto out; jffs2_dbg_acct_paranoia_check_nolock(c, jeb); /* Now decide which list to put it on */ switch(ret) { case BLK_STATE_ALLFF: /* * Empty block. Since we can't be sure it * was entirely erased, we just queue it for erase * again. It will be marked as such when the erase * is complete. Meanwhile we still count it as empty * for later checks. */ empty_blocks++; list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; break; case BLK_STATE_CLEANMARKER: /* Only a CLEANMARKER node is valid */ if (!jeb->dirty_size) { /* It's actually free */ list_add(&jeb->list, &c->free_list); c->nr_free_blocks++; } else { /* Dirt */ jffs2_dbg(1, "Adding all-dirty block at 0x%08x to erase_pending_list\n", jeb->offset); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; } break; case BLK_STATE_CLEAN: /* Full (or almost full) of clean data. Clean list */ list_add(&jeb->list, &c->clean_list); break; case BLK_STATE_PARTDIRTY: /* Some data, but not full. Dirty list. */ /* We want to remember the block with most free space and stick it in the 'nextblock' position to start writing to it. */ if (jeb->free_size > min_free(c) && (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ if (c->nextblock) { ret = file_dirty(c, c->nextblock); if (ret) goto out; /* deleting summary information of the old nextblock */ jffs2_sum_reset_collected(c->summary); } /* update collected summary information for the current nextblock */ jffs2_sum_move_collected(c, s); jffs2_dbg(1, "%s(): new nextblock = 0x%08x\n", __func__, jeb->offset); c->nextblock = jeb; } else { ret = file_dirty(c, jeb); if (ret) goto out; } break; case BLK_STATE_ALLDIRTY: /* Nothing valid - not even a clean marker. Needs erasing. */ /* For now we just put it on the erasing list. We'll start the erases later */ jffs2_dbg(1, "Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; break; case BLK_STATE_BADBLOCK: jffs2_dbg(1, "Block at 0x%08x is bad\n", jeb->offset); list_add(&jeb->list, &c->bad_list); c->bad_size += c->sector_size; c->free_size -= c->sector_size; bad_blocks++; break; default: pr_warn("%s(): unknown block state\n", __func__); BUG(); } } /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */ if (c->nextblock && (c->nextblock->dirty_size)) { c->nextblock->wasted_size += c->nextblock->dirty_size; c->wasted_size += c->nextblock->dirty_size; c->dirty_size -= c->nextblock->dirty_size; c->nextblock->dirty_size = 0; } #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && c->wbuf_pagesize && c->nextblock && (c->nextblock->free_size % c->wbuf_pagesize)) { /* If we're going to start writing into a block which already contains data, and the end of the data isn't page-aligned, skip a little and align it. */ uint32_t skip = c->nextblock->free_size % c->wbuf_pagesize; jffs2_dbg(1, "%s(): Skipping %d bytes in nextblock to ensure page alignment\n", __func__, skip); jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); jffs2_scan_dirty_space(c, c->nextblock, skip); } #endif if (c->nr_erasing_blocks) { if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { pr_notice("Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); pr_notice("empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n", empty_blocks, bad_blocks, c->nr_blocks); ret = -EIO; goto out; } spin_lock(&c->erase_completion_lock); jffs2_garbage_collect_trigger(c); spin_unlock(&c->erase_completion_lock); } ret = 0; out: if (buf_size) kfree(flashbuf); #ifndef __ECOS else mtd_unpoint(c->mtd, 0, c->mtd->size); #endif kfree(s); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse48852.42%1337.14%
linus torvaldslinus torvalds25927.82%25.71%
ferenc havasiferenc havasi727.73%12.86%
joe perchesjoe perches414.40%38.57%
grant ericksongrant erickson262.79%12.86%
artem bityutskiyartem bityutskiy161.72%720.00%
jared hulbertjared hulbert70.75%12.86%
christian engelmayerchristian engelmayer60.64%12.86%
florin malitaflorin malita50.54%12.86%
dave jonesdave jones40.43%12.86%
andrew victorandrew victor30.32%12.86%
thomas gleixnerthomas gleixner20.21%12.86%
andy loweandy lowe10.11%12.86%
yan burmanyan burman10.11%12.86%
Total931100.00%35100.00%


static int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf, uint32_t ofs, uint32_t len) { int ret; size_t retlen; ret = jffs2_flash_read(c, ofs, len, &retlen, buf); if (ret) { jffs2_dbg(1, "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret); return ret; } if (retlen < len) { jffs2_dbg(1, "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen); return -EIO; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse7178.02%342.86%
linus torvaldslinus torvalds1213.19%114.29%
joe perchesjoe perches66.59%114.29%
adrian bunkadrian bunk11.10%114.29%
ferenc havasiferenc havasi11.10%114.29%
Total91100.00%7100.00%


int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size && (!jeb->first_node || !ref_next(jeb->first_node)) ) return BLK_STATE_CLEANMARKER; /* move blocks with max 4 byte dirty space to cleanlist */ else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) { c->dirty_size -= jeb->dirty_size; c->wasted_size += jeb->dirty_size; jeb->wasted_size += jeb->dirty_size; jeb->dirty_size = 0; return BLK_STATE_CLEAN; } else if (jeb->used_size || jeb->unchecked_size) return BLK_STATE_PARTDIRTY; else return BLK_STATE_ALLDIRTY; }

Contributors

PersonTokensPropCommitsCommitProp
ferenc havasiferenc havasi12997.73%150.00%
david woodhousedavid woodhouse32.27%150.00%
Total132100.00%2100.00%

#ifdef CONFIG_JFFS2_FS_XATTR
static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_xattr *rx, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_xattr_datum *xd; uint32_t xid, version, totlen, crc; int err; crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4); if (crc != je32_to_cpu(rx->node_crc)) { JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", ofs, je32_to_cpu(rx->node_crc), crc); if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen)))) return err; return 0; } xid = je32_to_cpu(rx->xid); version = je32_to_cpu(rx->version); totlen = PAD(sizeof(struct jffs2_raw_xattr) + rx->name_len + 1 + je16_to_cpu(rx->value_len)); if (totlen != je32_to_cpu(rx->totlen)) { JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n", ofs, je32_to_cpu(rx->totlen), totlen); if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen)))) return err; return 0; } xd = jffs2_setup_xattr_datum(c, xid, version); if (IS_ERR(xd)) return PTR_ERR(xd); if (xd->version > version) { struct jffs2_raw_node_ref *raw = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL); raw->next_in_ino = xd->node->next_in_ino; xd->node->next_in_ino = raw; } else { xd->version = version; xd->xprefix = rx->xprefix; xd->name_len = rx->name_len; xd->value_len = je16_to_cpu(rx->value_len); xd->data_crc = je32_to_cpu(rx->data_crc); jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, (void *)xd); } if (jffs2_sum_active()) jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset); dbg_xattr("scanning xdatum at %#08x (xid=%u, version=%u)\n", ofs, xd->xid, xd->version); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei33385.60%337.50%
david woodhousedavid woodhouse5514.14%450.00%
masanari iidamasanari iida10.26%112.50%
Total389100.00%8100.00%


static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_xref *rr, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_xattr_ref *ref; uint32_t crc; int err; crc = crc32(0, rr, sizeof(*rr) - 4); if (crc != je32_to_cpu(rr->node_crc)) { JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", ofs, je32_to_cpu(rr->node_crc), crc); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rr->totlen))))) return err; return 0; } if (PAD(sizeof(struct jffs2_raw_xref)) != je32_to_cpu(rr->totlen)) { JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%zd\n", ofs, je32_to_cpu(rr->totlen), PAD(sizeof(struct jffs2_raw_xref))); if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rr->totlen)))) return err; return 0; } ref = jffs2_alloc_xattr_ref(); if (!ref) return -ENOMEM; /* BEFORE jffs2_build_xattr_subsystem() called, * and AFTER xattr_ref is marked as a dead xref, * ref->xid is used to store 32bit xid, xd is not used * ref->ino is used to store 32bit inode-number, ic is not used * Thoes variables are declared as union, thus using those * are exclusive. In a similar way, ref->next is temporarily * used to chain all xattr_ref object. It's re-chained to * jffs2_inode_cache in jffs2_build_xattr_subsystem() correctly. */ ref->ino = je32_to_cpu(rr->ino); ref->xid = je32_to_cpu(rr->xid); ref->xseqno = je32_to_cpu(rr->xseqno); if (ref->xseqno > c->highest_xseqno) c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER); ref->next = c->xref_temp; c->xref_temp = ref; jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), (void *)ref); if (jffs2_sum_active()) jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset); dbg_xattr("scan xref at %#08x (xid=%u, ino=%u)\n", ofs, ref->xid, ref->ino); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei28284.94%337.50%
david woodhousedavid woodhouse5015.06%562.50%
Total332100.00%8100.00%

#endif /* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into the flash, XIP-style */
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { struct jffs2_unknown_node *node; struct jffs2_unknown_node crcnode; uint32_t ofs, prevofs, max_ofs; uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; #ifdef CONFIG_JFFS2_FS_WRITEBUFFER int cleanmarkerfound = 0; #endif ofs = jeb->offset; prevofs = jeb->offset - 1; jffs2_dbg(1, "%s(): Scanning block at 0x%x\n", __func__, ofs); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { int ret; if (mtd_block_isbad(c->mtd, jeb->offset)) return BLK_STATE_BADBLOCK; ret = jffs2_check_nand_cleanmarker(c, jeb); jffs2_dbg(2, "jffs_check_nand_cleanmarker returned %d\n", ret); /* Even if it's not found, we still scan to see if the block is empty. We use this information to decide whether to erase it or not. */ switch (ret) { case 0: cleanmarkerfound = 1; break; case 1: break; default: return ret; } } #endif if (jffs2_sum_active()) { struct jffs2_sum_marker *sm; void *sumptr = NULL; uint32_t sumlen; if (!buf_size) { /* XIP case. Just look, point at the summary if it's there */ sm = (void *)buf + c->sector_size - sizeof(*sm); if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) { sumptr = buf + je32_to_cpu(sm->offset); sumlen = c->sector_size - je32_to_cpu(sm->offset); } } else { /* If NAND flash, read a whole page of it. Else just the end */ if (c->wbuf_pagesize) buf_len = c->wbuf_pagesize; else buf_len = sizeof(*sm); /* Read as much as we want into the _end_ of the preallocated buffer */ err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, jeb->offset + c->sector_size - buf_len, buf_len); if (err) return err; sm = (void *)buf + buf_size - sizeof(*sm); if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) { sumlen = c->sector_size - je32_to_cpu(sm->offset); sumptr = buf + buf_size - sumlen; /* sm->offset maybe wrong but MAGIC maybe right */ if (sumlen > c->sector_size) goto full_scan; /* Now, make sure the summary itself is available */ if (sumlen > buf_size) { /* Need to kmalloc for this. */ sumptr = kmalloc(sumlen, GFP_KERNEL); if (!sumptr) return -ENOMEM; memcpy(sumptr + sumlen - buf_len, buf + buf_size - buf_len, buf_len); } if (buf_len < sumlen) { /* Need to read more so that the entire summary node is present */ err = jffs2_fill_scan_buf(c, sumptr, jeb->offset + c->sector_size - sumlen, sumlen - buf_len); if (err) return err; } } } if (sumptr) { err = jffs2_sum_scan_sumnode(c, jeb, sumptr, sumlen, &pseudo_random); if (buf_size && sumlen > buf_size) kfree(sumptr); /* If it returns with a real error, bail. If it returns positive, that's a block classification (i.e. BLK_STATE_xxx) so return that too. If it returns zero, fall through to full scan. */ if (err) return err; } } full_scan: buf_ofs = jeb->offset; if (!buf_size) { /* This is the XIP case -- we're reading _directly_ from the flash chip */ buf_len = c->sector_size; } else { buf_len = EMPTY_SCAN_SIZE(c->sector_size); err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); if (err) return err; } /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ ofs = 0; max_ofs = EMPTY_SCAN_SIZE(c->sector_size); /* Scan only EMPTY_SCAN_SIZE of 0xFF before declaring it's empty */ while(ofs < max_ofs && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ofs += 4; if (ofs == max_ofs) { #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { /* scan oob, take care of cleanmarker */ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); jffs2_dbg(2, "jffs2_check_oob_empty returned %d\n", ret); switch (ret) { case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; case 1: return BLK_STATE_ALLDIRTY; default: return ret; } } #endif jffs2_dbg(1, "Block at 0x%08x is empty (erased)\n", jeb->offset); if (c->cleanmarker_size == 0) return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */ else return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ } if (ofs) { jffs2_dbg(1, "Free space at %08x ends at %08x\n", jeb->offset, jeb->offset + ofs); if ((err = jffs2_prealloc_raw_node_refs(c, jeb,