# HG changeset patch # User asl # Date 1122737440 0 # Node ID 5bcbb50b501e1d1e5bca70e504f08ab3f84acae3 # Parent 9ea31f9f22549426cc4dbad7cceee5adae8686fc * Merge to public MTD diff --git a/packages/fs/jffs2/current/ChangeLog b/packages/fs/jffs2/current/ChangeLog --- a/packages/fs/jffs2/current/ChangeLog +++ b/packages/fs/jffs2/current/ChangeLog @@ -1,8 +1,6 @@ -2005-03-27 Andrew Lunn +2005-07-30 Andrew Lunn - * tests/jffs2_[123].c: Fixed compiler warnings for parameter - mismatch for diag_printf(). - + * Merge to public MTD 2005-02-25 Andrew Lunn diff --git a/packages/fs/jffs2/current/cdl/jffs2.cdl b/packages/fs/jffs2/current/cdl/jffs2.cdl --- a/packages/fs/jffs2/current/cdl/jffs2.cdl +++ b/packages/fs/jffs2/current/cdl/jffs2.cdl @@ -4,7 +4,7 @@ # # JFFS2 Filesystem configuration data # -# $Id: jffs2.cdl,v 1.18 2005/01/22 16:01:12 lunn Exp $ +# $Id: jffs2.cdl,v 1.19 2005/02/09 09:23:54 pavlov Exp $ # # ==================================================================== #####ECOSGPLCOPYRIGHTBEGIN#### @@ -73,7 +73,7 @@ cdl_package CYGPKG_FS_JFFS2 { implements CYGINT_IO_FILEIO_FS compile -library=libextras.a fs-ecos.c - compile build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c dir-ecos.c read.c compr.c + compile build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c dir-ecos.c read.c compr.c debug.c # This could be overridden by an alternative direct I/O method. compile flashio.c @@ -132,7 +132,7 @@ cdl_package CYGPKG_FS_JFFS2 { cdl_option CYGOPT_FS_JFFS2_NAND { display "Support for NAND flash" flavor bool - define CONFIG_JFFS2_FS_NAND + define CONFIG_JFFS2_FS_WRITEBUFFER compile wbuf.c requires 0 description " diff --git a/packages/fs/jffs2/current/doc/README.Locking b/packages/fs/jffs2/current/doc/README.Locking --- a/packages/fs/jffs2/current/doc/README.Locking +++ b/packages/fs/jffs2/current/doc/README.Locking @@ -1,4 +1,4 @@ - $Id: README.Locking,v 1.11 2004/11/24 09:25:09 dedekind Exp $ + $Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $ JFFS2 LOCKING DOCUMENTATION --------------------------- @@ -108,6 +108,10 @@ in-core jffs2_inode_cache objects (each correspondent jffs2_inode_cache object). So, the inocache_lock has to be locked while walking the c->inocache_list hash buckets. +This spinlock also covers allocation of new inode numbers, which is +currently just '++->highest_ino++', but might one day get more complicated +if we need to deal with wrapping after 4 milliard inode numbers are used. + Note, the f->sem guarantees that the correspondent jffs2_inode_cache will not be removed. So, it is allowed to access it without locking the inocache_lock spinlock. diff --git a/packages/fs/jffs2/current/include/linux/jffs2.h b/packages/fs/jffs2/current/include/linux/jffs2.h --- a/packages/fs/jffs2/current/include/linux/jffs2.h +++ b/packages/fs/jffs2/current/include/linux/jffs2.h @@ -8,7 +8,7 @@ * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.34 2004/11/16 20:36:14 dwmw2 Exp $ + * $Id: jffs2.h,v 1.36 2005/07/26 13:19:36 havasi Exp $ * */ @@ -43,8 +43,6 @@ #define JFFS2_COMPR_COPY 0x04 #define JFFS2_COMPR_DYNRUBIN 0x05 #define JFFS2_COMPR_ZLIB 0x06 -#define JFFS2_COMPR_LZO 0x07 -#define JFFS2_COMPR_LZARI 0x08 /* Compatibility flags. */ #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ #define JFFS2_NODE_ACCURATE 0x2000 @@ -101,7 +99,7 @@ struct jffs2_unknown_node struct jffs2_raw_dirent { jint16_t magic; - jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ + jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */ jint32_t totlen; jint32_t hdr_crc; jint32_t pino; @@ -125,7 +123,7 @@ struct jffs2_raw_dirent struct jffs2_raw_inode { jint16_t magic; /* A constant magic number. */ - jint16_t nodetype; /* == JFFS_NODETYPE_INODE */ + jint16_t nodetype; /* == JFFS2_NODETYPE_INODE */ jint32_t totlen; /* Total length of this node (inc data, etc.) */ jint32_t hdr_crc; jint32_t ino; /* Inode number. */ diff --git a/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h b/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h --- a/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h +++ b/packages/fs/jffs2/current/include/linux/jffs2_fs_i.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_i.h,v 1.17 2004/11/11 23:51:27 dwmw2 Exp $ */ +/* $Id: jffs2_fs_i.h,v 1.18 2005/07/17 11:13:48 dedekind Exp $ */ #ifndef _JFFS2_FS_I #define _JFFS2_FS_I @@ -32,6 +32,9 @@ struct jffs2_inode_info { /* Directory entries */ struct jffs2_full_dirent *dents; + /* The target path if this is the inode of a symlink */ + unsigned char *target; + /* Some stuff we just have to keep in-core at all times, for each inode. */ struct jffs2_inode_cache *inocache; diff --git a/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h b/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h --- a/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h +++ b/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.48 2004/11/20 10:41:12 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.52 2005/05/19 16:12:17 gleixner Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -14,7 +14,8 @@ #include #define JFFS2_SB_FLAG_RO 1 -#define JFFS2_SB_FLAG_MOUNTING 2 +#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */ +#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */ struct jffs2_inodirty; @@ -31,7 +32,7 @@ struct jffs2_sb_info { unsigned int flags; struct task_struct *gc_task; /* GC task struct */ - struct semaphore gc_thread_start; /* GC thread start mutex */ + struct completion gc_thread_start; /* GC thread start completion */ struct completion gc_thread_exit; /* GC thread exit completion port */ struct semaphore alloc_sem; /* Used to protect all the following @@ -94,7 +95,7 @@ struct jffs2_sb_info { to an obsoleted node. I don't like this. Alternatives welcomed. */ struct semaphore erase_free_sem; -#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* Write-behind buffer for NAND flash */ unsigned char *wbuf; uint32_t wbuf_ofs; diff --git a/packages/fs/jffs2/current/src/build.c b/packages/fs/jffs2/current/src/build.c --- a/packages/fs/jffs2/current/src/build.c +++ b/packages/fs/jffs2/current/src/build.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.69 2004/12/16 20:22:18 dmarlin Exp $ + * $Id: build.c,v 1.75 2005/07/22 10:32:07 dedekind Exp $ * */ @@ -97,14 +97,16 @@ static int jffs2_build_filesystem(struct /* First, scan the medium and build all the inode caches with lists of physical nodes */ - c->flags |= JFFS2_SB_FLAG_MOUNTING; + c->flags |= JFFS2_SB_FLAG_SCANNING; ret = jffs2_scan_medium(c); + c->flags &= ~JFFS2_SB_FLAG_SCANNING; if (ret) goto exit; D1(printk(KERN_DEBUG "Scanned flash completely\n")); - D2(jffs2_dump_block_lists(c)); + jffs2_dbg_dump_block_lists_nolock(c); + c->flags |= JFFS2_SB_FLAG_BUILDING; /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino)); @@ -116,7 +118,6 @@ static int jffs2_build_filesystem(struct cond_resched(); } } - c->flags &= ~JFFS2_SB_FLAG_MOUNTING; D1(printk(KERN_DEBUG "Pass 1 complete\n")); @@ -164,8 +165,10 @@ static int jffs2_build_filesystem(struct ic->scan_dents = NULL; cond_resched(); } + c->flags &= ~JFFS2_SB_FLAG_BUILDING; + D1(printk(KERN_DEBUG "Pass 3 complete\n")); - D2(jffs2_dump_block_lists(c)); + jffs2_dbg_dump_block_lists_nolock(c); /* Rotate the lists by some number to ensure wear levelling */ jffs2_rotate_lists(c); @@ -335,13 +338,6 @@ int jffs2_do_mount_fs(struct jffs2_sb_in c->blocks[i].bad_count = 0; } - init_MUTEX(&c->alloc_sem); - init_MUTEX(&c->erase_free_sem); - init_waitqueue_head(&c->erase_wait); - init_waitqueue_head(&c->inocache_wq); - spin_lock_init(&c->erase_completion_lock); - spin_lock_init(&c->inocache_lock); - INIT_LIST_HEAD(&c->clean_list); INIT_LIST_HEAD(&c->very_dirty_list); INIT_LIST_HEAD(&c->dirty_list); diff --git a/packages/fs/jffs2/current/src/compr.c b/packages/fs/jffs2/current/src/compr.c --- a/packages/fs/jffs2/current/src/compr.c +++ b/packages/fs/jffs2/current/src/compr.c @@ -9,7 +9,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.43 2005/01/12 22:34:35 gleixner Exp $ + * $Id: compr.c,v 1.45 2005/07/26 13:24:40 havasi Exp $ * */ @@ -23,16 +23,6 @@ static LIST_HEAD(jffs2_compressor_list); /* Actual compression mode */ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; -void jffs2_set_compression_mode(int mode) -{ - jffs2_compression_mode = mode; -} - -int jffs2_get_compression_mode(void) -{ - return jffs2_compression_mode; -} - /* Statistics for blocks stored without compression */ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; @@ -435,12 +425,6 @@ int jffs2_compressors_init(void) jffs2_rubinmips_init(); jffs2_dynrubin_init(); #endif -#ifdef CONFIG_JFFS2_LZARI - jffs2_lzari_init(); -#endif -#ifdef CONFIG_JFFS2_LZO - jffs2_lzo_init(); -#endif /* Setting default compression mode */ #ifdef CONFIG_JFFS2_CMODE_NONE jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; @@ -459,12 +443,6 @@ int jffs2_compressors_init(void) int jffs2_compressors_exit(void) { /* Unregistering compressors */ -#ifdef CONFIG_JFFS2_LZO - jffs2_lzo_exit(); -#endif -#ifdef CONFIG_JFFS2_LZARI - jffs2_lzari_exit(); -#endif #ifdef CONFIG_JFFS2_RUBIN jffs2_dynrubin_exit(); jffs2_rubinmips_exit(); diff --git a/packages/fs/jffs2/current/src/compr.h b/packages/fs/jffs2/current/src/compr.h --- a/packages/fs/jffs2/current/src/compr.h +++ b/packages/fs/jffs2/current/src/compr.h @@ -7,7 +7,7 @@ * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: compr.h,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $ + * $Id: compr.h,v 1.8 2005/07/26 13:24:40 havasi Exp $ * */ @@ -41,9 +41,6 @@ #define JFFS2_COMPR_MODE_PRIORITY 1 #define JFFS2_COMPR_MODE_SIZE 2 -void jffs2_set_compression_mode(int mode); -int jffs2_get_compression_mode(void); - struct jffs2_compressor { struct list_head list; int priority; /* used by prirority comr. mode */ @@ -106,13 +103,5 @@ void jffs2_rtime_exit(void); int jffs2_zlib_init(void); void jffs2_zlib_exit(void); #endif -#ifdef CONFIG_JFFS2_LZARI -int jffs2_lzari_init(void); -void jffs2_lzari_exit(void); -#endif -#ifdef CONFIG_JFFS2_LZO -int jffs2_lzo_init(void); -void jffs2_lzo_exit(void); -#endif #endif /* __JFFS2_COMPR_H__ */ diff --git a/packages/fs/jffs2/current/src/compr_rtime.c b/packages/fs/jffs2/current/src/compr_rtime.c --- a/packages/fs/jffs2/current/src/compr_rtime.c +++ b/packages/fs/jffs2/current/src/compr_rtime.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_rtime.c,v 1.15 2005/03/17 20:23:06 gleixner Exp $ * * * Very simple lz77-ish encoder. @@ -29,8 +29,10 @@ #include "compr.h" /* _compress returns the compressed size, -1 if bigger */ -int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen, void *model) +static int jffs2_rtime_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) { short positions[256]; int outpos = 0; @@ -69,8 +71,10 @@ int jffs2_rtime_compress(unsigned char * } -int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen, void *model) +static int jffs2_rtime_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, + void *model) { short positions[256]; int outpos = 0; diff --git a/packages/fs/jffs2/current/src/compr_rubin.c b/packages/fs/jffs2/current/src/compr_rubin.c --- a/packages/fs/jffs2/current/src/compr_rubin.c +++ b/packages/fs/jffs2/current/src/compr_rubin.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_rubin.c,v 1.21 2005/05/20 15:39:54 gleixner Exp $ * */ @@ -228,8 +228,10 @@ int jffs2_rubinmips_compress(unsigned ch return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); } #endif -int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen, void *model) +static int jffs2_dynrubin_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) { int bits[8]; unsigned char histo[256]; @@ -306,15 +308,19 @@ static void rubin_do_decompress(int bit_ } -int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t sourcelen, uint32_t dstlen, void *model) +static int jffs2_rubinmips_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t sourcelen, uint32_t dstlen, + void *model) { rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); return 0; } -int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t sourcelen, uint32_t dstlen, void *model) +static int jffs2_dynrubin_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t sourcelen, uint32_t dstlen, + void *model) { int bits[8]; int c; diff --git a/packages/fs/jffs2/current/src/compr_zlib.c b/packages/fs/jffs2/current/src/compr_zlib.c --- a/packages/fs/jffs2/current/src/compr_zlib.c +++ b/packages/fs/jffs2/current/src/compr_zlib.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.31 2005/05/20 19:30:06 gleixner Exp $ * */ @@ -17,10 +17,10 @@ #include #include +#include #include #include #include -#include #include "nodelist.h" #include "compr.h" @@ -69,8 +69,10 @@ static void free_workspaces(void) #define free_workspaces() do { } while(0) #endif /* __KERNEL__ */ -int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen, void *model) +static int jffs2_zlib_compress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t *sourcelen, uint32_t *dstlen, + void *model) { int ret; @@ -135,8 +137,10 @@ int jffs2_zlib_compress(unsigned char *d return ret; } -int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen, void *model) +static int jffs2_zlib_decompress(unsigned char *data_in, + unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, + void *model) { int ret; int wbits = MAX_WBITS; diff --git a/packages/fs/jffs2/current/src/debug.c b/packages/fs/jffs2/current/src/debug.c new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/src/debug.c @@ -0,0 +1,710 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: debug.c,v 1.1 2005/07/30 15:30:42 asl Exp $ + * + */ +#include +#include +#include +#include +#include +#include "nodelist.h" +#include "debug.h" + +#ifdef JFFS2_DBG_PARANOIA_CHECKS +/* + * Check the fragtree. + */ +void +__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f) +{ + down(&f->sem); + __jffs2_dbg_fragtree_paranoia_check_nolock(f); + up(&f->sem); +} + +void +__jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f) +{ + struct jffs2_node_frag *frag; + int bitched = 0; + + for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { + struct jffs2_full_dnode *fn = frag->node; + + if (!fn || !fn->raw) + continue; + + if (ref_flags(fn->raw) == REF_PRISTINE) { + if (fn->frags > 1) { + JFFS2_ERROR("REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2.\n", + ref_offset(fn->raw), fn->frags); + bitched = 1; + } + + /* A hole node which isn't multi-page should be garbage-collected + and merged anyway, so we just check for the frag size here, + rather than mucking around with actually reading the node + and checking the compression type, which is the real way + to tell a hole node. */ + if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) + && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) { + JFFS2_ERROR("REF_PRISTINE node at 0x%08x had a previous non-hole frag " + "in the same page. Tell dwmw2.\n", ref_offset(fn->raw)); + bitched = 1; + } + + if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) + && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { + JFFS2_ERROR("REF_PRISTINE node at 0x%08x (%08x-%08x) had a following " + "non-hole frag in the same page. Tell dwmw2.\n", + ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); + bitched = 1; + } + } + } + + if (bitched) { + JFFS2_ERROR("fragtree is corrupted.\n"); + __jffs2_dbg_dump_fragtree_nolock(f); + BUG(); + } +} + +/* + * Check if the flash contains all 0xFF before we start writing. + */ +void +__jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, + uint32_t ofs, int len) +{ + size_t retlen; + int ret, i; + unsigned char *buf; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return; + + ret = jffs2_flash_read(c, ofs, len, &retlen, buf); + if (ret || (retlen != len)) { + JFFS2_WARNING("read %d bytes failed or short. ret %d, retlen %zd.\n", + len, ret, retlen); + kfree(buf); + return; + } + + ret = 0; + for (i = 0; i < len; i++) + if (buf[i] != 0xff) + ret = 1; + + if (ret) { + JFFS2_ERROR("argh, about to write node to %#08x on flash, but there are data " + "already there. The first corrupted byte is at %#08x offset.\n", ofs, ofs + i); + __jffs2_dbg_dump_buffer(buf, len, ofs); + kfree(buf); + BUG(); + } + + kfree(buf); +} + +/* + * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'. + */ +void +__jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + spin_lock(&c->erase_completion_lock); + __jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); +} + +void +__jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + uint32_t my_used_size = 0; + uint32_t my_unchecked_size = 0; + uint32_t my_dirty_size = 0; + struct jffs2_raw_node_ref *ref2 = jeb->first_node; + + while (ref2) { + uint32_t totlen = ref_totlen(c, jeb, ref2); + + if (ref2->flash_offset < jeb->offset || + ref2->flash_offset > jeb->offset + c->sector_size) { + JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n", + ref_offset(ref2), jeb->offset); + goto error; + + } + if (ref_flags(ref2) == REF_UNCHECKED) + my_unchecked_size += totlen; + else if (!ref_obsolete(ref2)) + my_used_size += totlen; + else + my_dirty_size += totlen; + + if ((!ref2->next_phys) != (ref2 == jeb->last_node)) { + JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next_phys at %#08x (mem %p), " + "last_node is at %#08x (mem %p).\n", + ref_offset(ref2), ref2, ref_offset(ref2->next_phys), ref2->next_phys, + ref_offset(jeb->last_node), jeb->last_node); + goto error; + } + ref2 = ref2->next_phys; + } + + if (my_used_size != jeb->used_size) { + JFFS2_ERROR("Calculated used size %#08x != stored used size %#08x.\n", + my_used_size, jeb->used_size); + goto error; + } + + if (my_unchecked_size != jeb->unchecked_size) { + JFFS2_ERROR("Calculated unchecked size %#08x != stored unchecked size %#08x.\n", + my_unchecked_size, jeb->unchecked_size); + goto error; + } + +#if 0 + /* This should work when we implement ref->__totlen elemination */ + if (my_dirty_size != jeb->dirty_size + jeb->wasted_size) { + JFFS2_ERROR("Calculated dirty+wasted size %#08x != stored dirty + wasted size %#08x\n", + my_dirty_size, jeb->dirty_size + jeb->wasted_size); + goto error; + } + + if (jeb->free_size == 0 + && my_used_size + my_unchecked_size + my_dirty_size != c->sector_size) { + JFFS2_ERROR("The sum of all nodes in block (%#x) != size of block (%#x)\n", + my_used_size + my_unchecked_size + my_dirty_size, + c->sector_size); + goto error; + } +#endif + + return; + +error: + __jffs2_dbg_dump_node_refs_nolock(c, jeb); + __jffs2_dbg_dump_jeb_nolock(jeb); + __jffs2_dbg_dump_block_lists_nolock(c); + BUG(); + +} +#endif /* JFFS2_DBG_PARANOIA_CHECKS */ + +#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS) +/* + * Dump the node_refs of the 'jeb' JFFS2 eraseblock. + */ +void +__jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + spin_lock(&c->erase_completion_lock); + __jffs2_dbg_dump_node_refs_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); +} + +void +__jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + struct jffs2_raw_node_ref *ref; + int i = 0; + + JFFS2_DEBUG("Dump node_refs of the eraseblock %#08x\n", jeb->offset); + if (!jeb->first_node) { + JFFS2_DEBUG("no nodes in the eraseblock %#08x\n", jeb->offset); + return; + } + + printk(JFFS2_DBG_LVL); + for (ref = jeb->first_node; ; ref = ref->next_phys) { + printk("%#08x(%#x)", ref_offset(ref), ref->__totlen); + if (ref->next_phys) + printk("->"); + else + break; + if (++i == 4) { + i = 0; + printk("\n" JFFS2_DBG_LVL); + } + } + printk("\n"); +} + +/* + * Dump an eraseblock's space accounting. + */ +void +__jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + spin_lock(&c->erase_completion_lock); + __jffs2_dbg_dump_jeb_nolock(jeb); + spin_unlock(&c->erase_completion_lock); +} + +void +__jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb) +{ + if (!jeb) + return; + + JFFS2_DEBUG("dump space accounting for the eraseblock at %#08x:\n", + jeb->offset); + + printk(JFFS2_DBG_LVL "used_size: %#08x\n", jeb->used_size); + printk(JFFS2_DBG_LVL "dirty_size: %#08x\n", jeb->dirty_size); + printk(JFFS2_DBG_LVL "wasted_size: %#08x\n", jeb->wasted_size); + printk(JFFS2_DBG_LVL "unchecked_size: %#08x\n", jeb->unchecked_size); + printk(JFFS2_DBG_LVL "free_size: %#08x\n", jeb->free_size); +} + +void +__jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c) +{ + spin_lock(&c->erase_completion_lock); + __jffs2_dbg_dump_block_lists_nolock(c); + spin_unlock(&c->erase_completion_lock); +} + +void +__jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) +{ + JFFS2_DEBUG("dump JFFS2 blocks lists:\n"); + + printk(JFFS2_DBG_LVL "flash_size: %#08x\n", c->flash_size); + printk(JFFS2_DBG_LVL "used_size: %#08x\n", c->used_size); + printk(JFFS2_DBG_LVL "dirty_size: %#08x\n", c->dirty_size); + printk(JFFS2_DBG_LVL "wasted_size: %#08x\n", c->wasted_size); + printk(JFFS2_DBG_LVL "unchecked_size: %#08x\n", c->unchecked_size); + printk(JFFS2_DBG_LVL "free_size: %#08x\n", c->free_size); + printk(JFFS2_DBG_LVL "erasing_size: %#08x\n", c->erasing_size); + printk(JFFS2_DBG_LVL "bad_size: %#08x\n", c->bad_size); + printk(JFFS2_DBG_LVL "sector_size: %#08x\n", c->sector_size); + printk(JFFS2_DBG_LVL "jffs2_reserved_blocks size: %#08x\n", + c->sector_size * c->resv_blocks_write); + + if (c->nextblock) + printk(JFFS2_DBG_LVL "nextblock: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + c->nextblock->offset, c->nextblock->used_size, + c->nextblock->dirty_size, c->nextblock->wasted_size, + c->nextblock->unchecked_size, c->nextblock->free_size); + else + printk(JFFS2_DBG_LVL "nextblock: NULL\n"); + + if (c->gcblock) + printk(JFFS2_DBG_LVL "gcblock: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, + c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size); + else + printk(JFFS2_DBG_LVL "gcblock: NULL\n"); + + if (list_empty(&c->clean_list)) { + printk(JFFS2_DBG_LVL "clean_list: empty\n"); + } else { + struct list_head *this; + int numblocks = 0; + uint32_t dirty = 0; + + list_for_each(this, &c->clean_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + numblocks ++; + dirty += jeb->wasted_size; + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "clean_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + + printk (JFFS2_DBG_LVL "Contains %d blocks with total wasted size %u, average wasted size: %u\n", + numblocks, dirty, dirty / numblocks); + } + + if (list_empty(&c->very_dirty_list)) { + printk(JFFS2_DBG_LVL "very_dirty_list: empty\n"); + } else { + struct list_head *this; + int numblocks = 0; + uint32_t dirty = 0; + + list_for_each(this, &c->very_dirty_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + numblocks ++; + dirty += jeb->dirty_size; + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "very_dirty_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + + printk (JFFS2_DBG_LVL "Contains %d blocks with total dirty size %u, average dirty size: %u\n", + numblocks, dirty, dirty / numblocks); + } + + if (list_empty(&c->dirty_list)) { + printk(JFFS2_DBG_LVL "dirty_list: empty\n"); + } else { + struct list_head *this; + int numblocks = 0; + uint32_t dirty = 0; + + list_for_each(this, &c->dirty_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + numblocks ++; + dirty += jeb->dirty_size; + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "dirty_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + + printk (JFFS2_DBG_LVL "contains %d blocks with total dirty size %u, average dirty size: %u\n", + numblocks, dirty, dirty / numblocks); + } + + if (list_empty(&c->erasable_list)) { + printk(JFFS2_DBG_LVL "erasable_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasable_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "erasable_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } + + if (list_empty(&c->erasing_list)) { + printk(JFFS2_DBG_LVL "erasing_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasing_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "erasing_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } + + if (list_empty(&c->erase_pending_list)) { + printk(JFFS2_DBG_LVL "erase_pending_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erase_pending_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "erase_pending_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } + + if (list_empty(&c->erasable_pending_wbuf_list)) { + printk(JFFS2_DBG_LVL "erasable_pending_wbuf_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasable_pending_wbuf_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "erasable_pending_wbuf_list: %#08x (used %#08x, dirty %#08x, " + "wasted %#08x, unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } + + if (list_empty(&c->free_list)) { + printk(JFFS2_DBG_LVL "free_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->free_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "free_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } + + if (list_empty(&c->bad_list)) { + printk(JFFS2_DBG_LVL "bad_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->bad_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "bad_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } + + if (list_empty(&c->bad_used_list)) { + printk(JFFS2_DBG_LVL "bad_used_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->bad_used_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG_LVL "bad_used_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, " + "unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } +} + +void +__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f) +{ + down(&f->sem); + jffs2_dbg_dump_fragtree_nolock(f); + up(&f->sem); +} + +void +__jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f) +{ + struct jffs2_node_frag *this = frag_first(&f->fragtree); + uint32_t lastofs = 0; + int buggy = 0; + + JFFS2_DEBUG("dump fragtree of ino #%u\n", f->inocache->ino); + while(this) { + if (this->node) + printk(JFFS2_DBG_LVL "frag %#04x-%#04x: %#08x(%d) on flash (*%p), left (%p), " + "right (%p), parent (%p)\n", + this->ofs, this->ofs+this->size, ref_offset(this->node->raw), + ref_flags(this->node->raw), this, frag_left(this), frag_right(this), + frag_parent(this)); + else + printk(JFFS2_DBG_LVL "frag %#04x-%#04x: hole (*%p). left (%p), right (%p), parent (%p)\n", + this->ofs, this->ofs+this->size, this, frag_left(this), + frag_right(this), frag_parent(this)); + if (this->ofs != lastofs) + buggy = 1; + lastofs = this->ofs + this->size; + this = frag_next(this); + } + + if (f->metadata) + printk(JFFS2_DBG_LVL "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); + + if (buggy) { + JFFS2_ERROR("frag tree got a hole in it.\n"); + BUG(); + } +} + +#define JFFS2_BUFDUMP_BYTES_PER_LINE 32 +void +__jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs) +{ + int skip; + int i; + + JFFS2_DEBUG("dump from offset %#08x to offset %#08x (%x bytes).\n", + offs, offs + len, len); + i = skip = offs % JFFS2_BUFDUMP_BYTES_PER_LINE; + offs = offs & ~(JFFS2_BUFDUMP_BYTES_PER_LINE - 1); + + if (skip != 0) + printk(JFFS2_DBG_LVL "%#08x: ", offs); + + while (skip--) + printk(" "); + + while (i < len) { + if ((i % JFFS2_BUFDUMP_BYTES_PER_LINE) == 0 && i != len -1) { + if (i != 0) + printk("\n"); + offs += JFFS2_BUFDUMP_BYTES_PER_LINE; + printk(JFFS2_DBG_LVL "%0#8x: ", offs); + } + + printk("%02x ", buf[i]); + + i += 1; + } + + printk("\n"); +} + +/* + * Dump a JFFS2 node. + */ +void +__jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs) +{ + union jffs2_node_union node; + int len = sizeof(union jffs2_node_union); + size_t retlen; + uint32_t crc; + int ret; + + JFFS2_DEBUG("dump node at offset %#08x.\n", ofs); + + ret = jffs2_flash_read(c, ofs, len, &retlen, (unsigned char *)&node); + if (ret || (retlen != len)) { + JFFS2_ERROR("read %d bytes failed or short. ret %d, retlen %zd.\n", + len, ret, retlen); + return; + } + + printk(JFFS2_DBG_LVL "magic:\t%#04x\n", + je16_to_cpu(node.u.magic)); + printk(JFFS2_DBG_LVL "nodetype:\t%#04x\n", + je16_to_cpu(node.u.nodetype)); + printk(JFFS2_DBG_LVL "totlen:\t%#08x\n", + je32_to_cpu(node.u.totlen)); + printk(JFFS2_DBG_LVL "hdr_crc:\t%#08x\n", + je32_to_cpu(node.u.hdr_crc)); + + crc = crc32(0, &node.u, sizeof(node.u) - 4); + if (crc != je32_to_cpu(node.u.hdr_crc)) { + JFFS2_ERROR("wrong common header CRC.\n"); + return; + } + + if (je16_to_cpu(node.u.magic) != JFFS2_MAGIC_BITMASK && + je16_to_cpu(node.u.magic) != JFFS2_OLD_MAGIC_BITMASK) + { + JFFS2_ERROR("wrong node magic: %#04x instead of %#04x.\n", + je16_to_cpu(node.u.magic), JFFS2_MAGIC_BITMASK); + return; + } + + switch(je16_to_cpu(node.u.nodetype)) { + + case JFFS2_NODETYPE_INODE: + + printk(JFFS2_DBG_LVL "the node is inode node\n"); + printk(JFFS2_DBG_LVL "ino:\t%#08x\n", + je32_to_cpu(node.i.ino)); + printk(JFFS2_DBG_LVL "version:\t%#08x\n", + je32_to_cpu(node.i.version)); + printk(JFFS2_DBG_LVL "mode:\t%#08x\n", + node.i.mode.m); + printk(JFFS2_DBG_LVL "uid:\t%#04x\n", + je16_to_cpu(node.i.uid)); + printk(JFFS2_DBG_LVL "gid:\t%#04x\n", + je16_to_cpu(node.i.gid)); + printk(JFFS2_DBG_LVL "isize:\t%#08x\n", + je32_to_cpu(node.i.isize)); + printk(JFFS2_DBG_LVL "atime:\t%#08x\n", + je32_to_cpu(node.i.atime)); + printk(JFFS2_DBG_LVL "mtime:\t%#08x\n", + je32_to_cpu(node.i.mtime)); + printk(JFFS2_DBG_LVL "ctime:\t%#08x\n", + je32_to_cpu(node.i.ctime)); + printk(JFFS2_DBG_LVL "offset:\t%#08x\n", + je32_to_cpu(node.i.offset)); + printk(JFFS2_DBG_LVL "csize:\t%#08x\n", + je32_to_cpu(node.i.csize)); + printk(JFFS2_DBG_LVL "dsize:\t%#08x\n", + je32_to_cpu(node.i.dsize)); + printk(JFFS2_DBG_LVL "compr:\t%#02x\n", + node.i.compr); + printk(JFFS2_DBG_LVL "usercompr:\t%#02x\n", + node.i.usercompr); + printk(JFFS2_DBG_LVL "flags:\t%#04x\n", + je16_to_cpu(node.i.flags)); + printk(JFFS2_DBG_LVL "data_crc:\t%#08x\n", + je32_to_cpu(node.i.data_crc)); + printk(JFFS2_DBG_LVL "node_crc:\t%#08x\n", + je32_to_cpu(node.i.node_crc)); + crc = crc32(0, &node.i, sizeof(node.i) - 8); + if (crc != je32_to_cpu(node.i.node_crc)) { + JFFS2_ERROR("wrong node header CRC.\n"); + return; + } + break; + + case JFFS2_NODETYPE_DIRENT: + + printk(JFFS2_DBG_LVL "the node is dirent node\n"); + printk(JFFS2_DBG_LVL "pino:\t%#08x\n", + je32_to_cpu(node.d.pino)); + printk(JFFS2_DBG_LVL "version:\t%#08x\n", + je32_to_cpu(node.d.version)); + printk(JFFS2_DBG_LVL "ino:\t%#08x\n", + je32_to_cpu(node.d.ino)); + printk(JFFS2_DBG_LVL "mctime:\t%#08x\n", + je32_to_cpu(node.d.mctime)); + printk(JFFS2_DBG_LVL "nsize:\t%#02x\n", + node.d.nsize); + printk(JFFS2_DBG_LVL "type:\t%#02x\n", + node.d.type); + printk(JFFS2_DBG_LVL "node_crc:\t%#08x\n", + je32_to_cpu(node.d.node_crc)); + printk(JFFS2_DBG_LVL "name_crc:\t%#08x\n", + je32_to_cpu(node.d.name_crc)); + + node.d.name[node.d.nsize] = '\0'; + printk(JFFS2_DBG_LVL "name:\t\"%s\"\n", node.d.name); + + crc = crc32(0, &node.d, sizeof(node.d) - 8); + if (crc != je32_to_cpu(node.d.node_crc)) { + JFFS2_ERROR("wrong node header CRC.\n"); + return; + } + break; + + default: + printk(JFFS2_DBG_LVL "node type is unknown\n"); + break; + } +} +#endif /* JFFS2_DBG_DUMPS || JFFS2_DBG_PARANOIA_CHECKS */ diff --git a/packages/fs/jffs2/current/src/debug.h b/packages/fs/jffs2/current/src/debug.h new file mode 100644 --- /dev/null +++ b/packages/fs/jffs2/current/src/debug.h @@ -0,0 +1,276 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: debug.h,v 1.1 2005/07/30 15:30:42 asl Exp $ + * + */ +#ifndef _JFFS2_DEBUG_H_ +#define _JFFS2_DEBUG_H_ + +#include + +#ifndef CONFIG_JFFS2_FS_DEBUG +#define CONFIG_JFFS2_FS_DEBUG 0 +#endif + +#if CONFIG_JFFS2_FS_DEBUG == 1 +/* Enable "paranoia" checks and dumps */ +#define JFFS2_DBG_PARANOIA_CHECKS +#define JFFS2_DBG_DUMPS +#define JFFS2_DBG_READINODE_MESSAGES +#define JFFS2_DBG_FRAGTREE_MESSAGES +#define JFFS2_DBG_DENTLIST_MESSAGES +#define JFFS2_DBG_NODEREF_MESSAGES +#define JFFS2_DBG_INOCACHE_MESSAGES +#endif + +#if CONFIG_JFFS2_FS_DEBUG == 2 +#define JFFS2_DBG_FRAGTREE2_MESSAGES +#endif + +/* Enable JFFS2 sanity checks by default */ +#define JFFS2_DBG_SANITY_CHECKS + +/* + * Dx() are mainly used for debugging messages, they must go away and be + * superseded by nicer JFFS2_DBG_XXX() macros... + */ +#if CONFIG_JFFS2_FS_DEBUG > 0 +#define D1(x) x +#else +#define D1(x) +#endif + +#if CONFIG_JFFS2_FS_DEBUG > 1 +#define D2(x) x +#else +#define D2(x) +#endif + +/* The prefixes of JFFS2 messages */ +#define JFFS2_DBG_MSG_PREFIX "[JFFS2 DBG]" +#define JFFS2_ERR_MSG_PREFIX "JFFS2 error: " +#define JFFS2_WARN_MSG_PREFIX "JFFS2 warning: " +#define JFFS2_NOTICE_MSG_PREFIX "JFFS2 notice: " + +#define JFFS2_ERR_LVL KERN_ERR +#define JFFS2_WARN_LVL KERN_WARNING +#define JFFS2_NOTICE_LVL KERN_NOTICE +#define JFFS2_DBG_LVL KERN_DEBUG + +/* JFFS2 message macros */ +#define JFFS2_ERROR(fmt, ...) \ + do { \ + printk(JFFS2_ERR_LVL JFFS2_ERR_MSG_PREFIX " %s: " \ + fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while(0) + +#define JFFS2_WARNING(fmt, ...) \ + do { \ + printk(JFFS2_WARN_LVL JFFS2_WARN_MSG_PREFIX " %s: " \ + fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while(0) + +#define JFFS2_NOTICE(fmt, ...) \ + do { \ + printk(JFFS2_NOTICE_LVL JFFS2_NOTICE_MSG_PREFIX " %s: " \ + fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while(0) + +#define JFFS2_DEBUG(fmt, ...) \ + do { \ + printk(JFFS2_DBG_LVL JFFS2_DBG_MSG_PREFIX " %s: " \ + fmt, __FUNCTION__, ##__VA_ARGS__); \ + } while(0) + +/* + * We split our debugging messages on several parts, depending on the JFFS2 + * subsystem the message belongs to. + */ +/* Read inode debugging messages */ +#ifdef JFFS2_DBG_READINODE_MESSAGES +#define JFFS2_DBG_READINODE(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_READINODE(fmt, ...) +#endif + +/* Fragtree build debugging messages */ +#ifdef JFFS2_DBG_FRAGTREE_MESSAGES +#define JFFS2_DBG_FRAGTREE(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_FRAGTREE(fmt, ...) +#endif + +/* Directory entry list manilulation debugging messages */ +#ifdef JFFS2_DBG_DENTLIST_MESSAGES +#define JFFS2_DBG_DENTLIST(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_DENTLIST(fmt, ...) +#endif +#ifdef JFFS2_DBG_FRAGTREE2_MESSAGES +#define JFFS2_DBG_FRAGTREE2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_FRAGTREE2(fmt, ...) +#endif + +/* Print the messages about manipulating node_refs */ +#ifdef JFFS2_DBG_NODEREF_MESSAGES +#define JFFS2_DBG_NODEREF(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_NODEREF(fmt, ...) +#endif + +/* Manipulations with the list of inodes (JFFS2 inocache) */ +#ifdef JFFS2_DBG_INOCACHE_MESSAGES +#define JFFS2_DBG_INOCACHE(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_INOCACHE(fmt, ...) +#endif + +/* Watch the object allocations */ +#ifdef JFFS2_DBG_MEMALLOC_MESSAGES +#define JFFS2_DBG_MEMALLOC(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define JFFS2_DBG_MEMALLOC(fmt, ...) +#endif + + +/* "Paranoia" checks */ +void +__jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f); +void +__jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f); +void +__jffs2_dbg_acct_paranoia_check(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, + uint32_t ofs, int len); + +/* "Dump" functions */ +void +__jffs2_dbg_dump_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_dump_jeb_nolock(struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_dump_block_lists(struct jffs2_sb_info *c); +void +__jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c); +void +__jffs2_dbg_dump_node_refs(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb); +void +__jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f); +void +__jffs2_dbg_dump_fragtree_nolock(struct jffs2_inode_info *f); +void +__jffs2_dbg_dump_buffer(unsigned char *buf, int len, uint32_t offs); +void +__jffs2_dbg_dump_node(struct jffs2_sb_info *c, uint32_t ofs); + +#ifdef JFFS2_DBG_PARANOIA_CHECKS +#define jffs2_dbg_fragtree_paranoia_check(f) \ + __jffs2_dbg_fragtree_paranoia_check(f) +#define jffs2_dbg_fragtree_paranoia_check_nolock(f) \ + __jffs2_dbg_fragtree_paranoia_check_nolock(f) +#define jffs2_dbg_acct_paranoia_check(c, jeb) \ + __jffs2_dbg_acct_paranoia_check(c,jeb) +#define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) \ + __jffs2_dbg_acct_paranoia_check_nolock(c,jeb) +#define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) \ + __jffs2_dbg_prewrite_paranoia_check(c, ofs, len) +#else +#define jffs2_dbg_fragtree_paranoia_check(f) +#define jffs2_dbg_fragtree_paranoia_check_nolock(f) +#define jffs2_dbg_acct_paranoia_check(c, jeb) +#define jffs2_dbg_acct_paranoia_check_nolock(c, jeb) +#define jffs2_dbg_prewrite_paranoia_check(c, ofs, len) +#endif /* !JFFS2_PARANOIA_CHECKS */ + +#ifdef JFFS2_DBG_DUMPS +#define jffs2_dbg_dump_jeb(c, jeb) \ + __jffs2_dbg_dump_jeb(c, jeb); +#define jffs2_dbg_dump_jeb_nolock(jeb) \ + __jffs2_dbg_dump_jeb_nolock(jeb); +#define jffs2_dbg_dump_block_lists(c) \ + __jffs2_dbg_dump_block_lists(c) +#define jffs2_dbg_dump_block_lists_nolock(c) \ + __jffs2_dbg_dump_block_lists_nolock(c) +#define jffs2_dbg_dump_fragtree(f) \ + __jffs2_dbg_dump_fragtree(f); +#define jffs2_dbg_dump_fragtree_nolock(f) \ + __jffs2_dbg_dump_fragtree_nolock(f); +#define jffs2_dbg_dump_buffer(buf, len, offs) \ + __jffs2_dbg_dump_buffer(*buf, len, offs); +#define jffs2_dbg_dump_node(c, ofs) \ + __jffs2_dbg_dump_node(c, ofs); +#else +#define jffs2_dbg_dump_jeb(c, jeb) +#define jffs2_dbg_dump_jeb_nolock(jeb) +#define jffs2_dbg_dump_block_lists(c) +#define jffs2_dbg_dump_block_lists_nolock(c) +#define jffs2_dbg_dump_fragtree(f) +#define jffs2_dbg_dump_fragtree_nolock(f) +#define jffs2_dbg_dump_buffer(buf, len, offs) +#define jffs2_dbg_dump_node(c, ofs) +#endif /* !JFFS2_DBG_DUMPS */ + +/* + * Sanity checks are supposed to be light-weight and enabled by default. + */ +#ifdef JFFS2_DBG_SANITY_CHECKS +/* + * Check the space accounting of the file system and of + * the JFFS2 erasable block 'jeb'. + */ +static inline void +jffs2_dbg_acct_sanity_check_nolock(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + if (unlikely(jeb && jeb->used_size + jeb->dirty_size + + jeb->free_size + jeb->wasted_size + + jeb->unchecked_size != c->sector_size)) { + JFFS2_ERROR("eeep, space accounting for block at 0x%08x is screwed.\n", jeb->offset); + JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + wasted %#08x + unchecked " + "%#08x != total %#08x.\n", jeb->free_size, jeb->dirty_size, jeb->used_size, + jeb->wasted_size, jeb->unchecked_size, c->sector_size); + BUG(); + } + + if (unlikely(c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + + c->wasted_size + c->unchecked_size != c->flash_size)) { + JFFS2_ERROR("eeep, space accounting superblock info is screwed.\n"); + JFFS2_ERROR("free %#08x + dirty %#08x + used %#08x + erasing %#08x + bad %#08x + " + "wasted %#08x + unchecked %#08x != total %#08x.\n", + c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, + c->wasted_size, c->unchecked_size, c->flash_size); + BUG(); + } +} + +static inline void +jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) +{ + spin_lock(&c->erase_completion_lock); + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); +} +#else +#define jffs2_dbg_acct_sanity_check(c, jeb) +#define jffs2_dbg_acct_sanity_check_nolock(c, jeb) +#endif /* !JFFS2_DBG_SANITY_CHECKS */ + +#endif /* _JFFS2_DEBUG_H_ */ diff --git a/packages/fs/jffs2/current/src/dir-ecos.c b/packages/fs/jffs2/current/src/dir-ecos.c --- a/packages/fs/jffs2/current/src/dir-ecos.c +++ b/packages/fs/jffs2/current/src/dir-ecos.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: dir-ecos.c,v 1.10 2003/11/26 15:55:35 dwmw2 Exp $ + * $Id: dir-ecos.c,v 1.11 2005/02/08 19:36:27 lunn Exp $ * */ @@ -38,8 +38,8 @@ struct _inode *jffs2_lookup(struct _inod for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= hash; fd_list = fd_list->next) { if (fd_list->nhash == hash && (!fd || fd_list->version > fd->version) && - strlen(fd_list->name) == namelen && - !strncmp(fd_list->name, d_name, namelen)) { + strlen((char *)fd_list->name) == namelen && + !strncmp((char *)fd_list->name, (char *)d_name, namelen)) { fd = fd_list; } } @@ -90,7 +90,8 @@ int jffs2_create(struct _inode *dir_i, c dir_f = JFFS2_INODE_INFO(dir_i); ret = jffs2_do_create(c, dir_f, f, ri, - d_name, strlen(d_name)); + (const char *)d_name, + strlen((char *)d_name)); if (ret) { inode->i_nlink = 0; @@ -117,8 +118,8 @@ int jffs2_unlink(struct _inode *dir_i, s struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(d_inode); int ret; - ret = jffs2_do_unlink(c, dir_f, d_name, - strlen(d_name), dead_f); + ret = jffs2_do_unlink(c, dir_f, (const char *)d_name, + strlen((char *)d_name), dead_f); if (dead_f->inocache) d_inode->i_nlink = dead_f->inocache->nlink; return ret; @@ -137,7 +138,9 @@ int jffs2_link (struct _inode *old_d_ino uint8_t type = (old_d_inode->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; - ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, d_name, strlen(d_name)); + ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, + (const char * )d_name, + strlen((char *)d_name)); if (!ret) { down(&f->sem); @@ -171,7 +174,7 @@ int jffs2_mkdir (struct _inode *dir_i, c /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ - namelen = strlen(d_name); + namelen = strlen((char *)d_name); ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL); if (ret) { @@ -331,7 +334,8 @@ int jffs2_rename (struct _inode *old_dir ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), d_inode->i_ino, type, - new_d_name, strlen(new_d_name)); + (const char *)new_d_name, + strlen((char *)new_d_name)); if (ret) return ret; @@ -349,7 +353,8 @@ int jffs2_rename (struct _inode *old_dir /* Unlink the original */ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), - old_d_name, strlen(old_d_name), NULL); + (const char *)old_d_name, + strlen((char *)old_d_name), NULL); if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */ diff --git a/packages/fs/jffs2/current/src/erase.c b/packages/fs/jffs2/current/src/erase.c --- a/packages/fs/jffs2/current/src/erase.c +++ b/packages/fs/jffs2/current/src/erase.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.69 2004/12/06 10:17:48 dedekind Exp $ + * $Id: erase.c,v 1.83 2005/07/22 10:32:08 dedekind Exp $ * */ @@ -33,7 +33,8 @@ static void jffs2_erase_succeeded(struct static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +static void jffs2_erase_block(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { int ret; uint32_t bad_offset; @@ -47,7 +48,8 @@ void jffs2_erase_block(struct jffs2_sb_i #else /* Linux */ struct erase_info *instr; - D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size)); + D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#08x (range %#08x-%#08x)\n", + jeb->offset, jeb->offset, jeb->offset + c->sector_size)); instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); @@ -233,7 +235,7 @@ static inline void jffs2_remove_node_ref continue; } - if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) { + if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) { /* It's in the block we're erasing */ struct jffs2_raw_node_ref *this; @@ -277,11 +279,8 @@ static inline void jffs2_remove_node_ref printk("\n"); }); - if (ic->nodes == (void *)ic) { - D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino)); + if (ic->nodes == (void *)ic && ic->nlink == 0) jffs2_del_ino_cache(c, ic); - jffs2_free_inode_cache(ic); - } } static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) @@ -302,92 +301,86 @@ static void jffs2_free_all_node_refs(str jeb->last_node = NULL; } +static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset) +{ + void *ebuf; + uint32_t ofs; + size_t retlen; + int ret = -EIO; + + ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!ebuf) { + printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset); + return -EAGAIN; + } + + D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); + + for (ofs = jeb->offset; ofs < jeb->offset + c->sector_size; ) { + uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); + int i; + + *bad_offset = ofs; + + ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); + if (ret) { + printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); + goto fail; + } + if (retlen != readlen) { + printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + goto fail; + } + for (i=0; ierase_completion_lock); - list_add(&jeb->list, &c->erase_complete_list); - spin_unlock(&c->erase_completion_lock); - return; - } + switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { + case -EAGAIN: goto refile; + case -EIO: goto filebad; } - ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!ebuf) { - printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset); - } else { - uint32_t ofs = jeb->offset; - - D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); - while(ofs < jeb->offset + c->sector_size) { - uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); - int i; - - bad_offset = ofs; - - ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); - if (ret) { - printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); - goto bad; - } - if (retlen != readlen) { - printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); - goto bad; - } - for (i=0; ierase_completion_lock); - /* Stick it on a list (any list) so - erase_failed can take it right off - again. Silly, but shouldn't happen - often. */ - list_add(&jeb->list, &c->erasing_list); - spin_unlock(&c->erase_completion_lock); - jffs2_erase_failed(c, jeb, bad_offset); - return; - } - } - ofs += readlen; - cond_resched(); - } - kfree(ebuf); - } - - bad_offset = jeb->offset; /* Write the erase complete marker */ D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); - if (jffs2_cleanmarker_oob(c)) { + bad_offset = jeb->offset; + + /* Cleanmarker in oob area or no cleanmarker at all ? */ + if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) { - if (jffs2_write_nand_cleanmarker(c, jeb)) - goto bad2; - + if (jffs2_cleanmarker_oob(c)) { + if (jffs2_write_nand_cleanmarker(c, jeb)) + goto filebad; + } + jeb->first_node = jeb->last_node = NULL; - jeb->free_size = c->sector_size; jeb->used_size = 0; jeb->dirty_size = 0; jeb->wasted_size = 0; + } else { + struct kvec vecs[1]; struct jffs2_unknown_node marker = { .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), @@ -395,21 +388,28 @@ static void jffs2_mark_erased_block(stru .totlen = cpu_to_je32(c->cleanmarker_size) }; + marker_ref = jffs2_alloc_raw_node_ref(); + if (!marker_ref) { + printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n"); + goto refile; + } + marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); vecs[0].iov_base = (unsigned char *) ▮ vecs[0].iov_len = sizeof(marker); ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen); - if (ret) { - printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", - jeb->offset, ret); - goto bad2; - } - if (retlen != sizeof(marker)) { - printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", - jeb->offset, sizeof(marker), retlen); - goto bad2; + if (ret || retlen != sizeof(marker)) { + if (ret) + printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", + jeb->offset, ret); + else + printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", + jeb->offset, sizeof(marker), retlen); + + jffs2_free_raw_node_ref(marker_ref); + goto filebad; } marker_ref->next_in_ino = NULL; @@ -430,13 +430,30 @@ static void jffs2_mark_erased_block(stru c->free_size += jeb->free_size; c->used_size += jeb->used_size; - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); list_add_tail(&jeb->list, &c->free_list); c->nr_erasing_blocks--; c->nr_free_blocks++; spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); + return; + +filebad: + spin_lock(&c->erase_completion_lock); + /* Stick it on a list (any list) so erase_failed can take it + right off again. Silly, but shouldn't happen often. */ + list_add(&jeb->list, &c->erasing_list); + spin_unlock(&c->erase_completion_lock); + jffs2_erase_failed(c, jeb, bad_offset); + return; + +refile: + /* Stick it back on the list from whence it came and come back later */ + jffs2_erase_pending_trigger(c); + spin_lock(&c->erase_completion_lock); + list_add(&jeb->list, &c->erase_complete_list); + spin_unlock(&c->erase_completion_lock); + return; } - diff --git a/packages/fs/jffs2/current/src/flashio.c b/packages/fs/jffs2/current/src/flashio.c --- a/packages/fs/jffs2/current/src/flashio.c +++ b/packages/fs/jffs2/current/src/flashio.c @@ -21,7 +21,7 @@ cyg_bool jffs2_flash_read(struct jffs2_sb_info * c, cyg_uint32 read_buffer_offset, const size_t size, - size_t * return_size, char *write_buffer) + size_t * return_size, unsigned char *write_buffer) { Cyg_ErrNo err; cyg_uint32 len = size; @@ -39,7 +39,7 @@ cyg_bool jffs2_flash_read(struct jffs2_s cyg_bool jffs2_flash_write(struct jffs2_sb_info * c, cyg_uint32 write_buffer_offset, const size_t size, - size_t * return_size, char *read_buffer) + size_t * return_size, unsigned char *read_buffer) { Cyg_ErrNo err; diff --git a/packages/fs/jffs2/current/src/fs-ecos.c b/packages/fs/jffs2/current/src/fs-ecos.c --- a/packages/fs/jffs2/current/src/fs-ecos.c +++ b/packages/fs/jffs2/current/src/fs-ecos.c @@ -8,7 +8,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs-ecos.c,v 1.41 2005/01/22 16:01:12 lunn Exp $ + * $Id: fs-ecos.c,v 1.44 2005/07/24 15:29:57 dedekind Exp $ * */ @@ -167,12 +167,12 @@ static unsigned char n_fs_mounted = 0; // Directory operations struct jffs2_dirsearch { - struct _inode *dir; // directory to search - const char *path; // path to follow - struct _inode *node; // Node found - const char *name; // last name fragment used - int namelen; // name fragment length - cyg_bool last; // last name in path? + struct _inode *dir; // directory to search + const unsigned char *path; // path to follow + struct _inode *node; // Node found + const unsigned char *name; // last name fragment used + int namelen; // name fragment length + cyg_bool last; // last name in path? }; typedef struct jffs2_dirsearch jffs2_dirsearch; @@ -221,7 +221,7 @@ static void icache_evict(struct _inode * // Initialize a dirsearch object to start a search static void init_dirsearch(jffs2_dirsearch * ds, - struct _inode *dir, const char *name) + struct _inode *dir, const unsigned char *name) { D2(printf("init_dirsearch name = %s\n", name)); D2(printf("init_dirsearch dir = %x\n", dir)); @@ -243,8 +243,8 @@ static void init_dirsearch(jffs2_dirsear static int find_entry(jffs2_dirsearch * ds) { struct _inode *dir = ds->dir; - const char *name = ds->path; - const char *n = name; + const unsigned char *name = ds->path; + const unsigned char *n = name; char namelen = 0; struct _inode *d; @@ -693,7 +693,8 @@ static int jffs2_open(cyg_mtab_entry * m if (mode & (O_CREAT|O_TRUNC|O_WRONLY)) return EROFS; #endif - init_dirsearch(&ds, (struct _inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); err = jffs2_find(&ds); @@ -776,7 +777,8 @@ static int jffs2_ops_unlink(cyg_mtab_ent D2(printf("jffs2_ops_unlink\n")); - init_dirsearch(&ds, (struct _inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *)name); err = jffs2_find(&ds); @@ -812,7 +814,8 @@ static int jffs2_ops_mkdir(cyg_mtab_entr D2(printf("jffs2_ops_mkdir\n")); - init_dirsearch(&ds, (struct _inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *)name); err = jffs2_find(&ds); @@ -848,7 +851,8 @@ static int jffs2_ops_rmdir(cyg_mtab_entr D2(printf("jffs2_ops_rmdir\n")); - init_dirsearch(&ds, (struct _inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *)name); err = jffs2_find(&ds); @@ -883,7 +887,8 @@ static int jffs2_ops_rename(cyg_mtab_ent D2(printf("jffs2_ops_rename\n")); - init_dirsearch(&ds1, (struct _inode *) dir1, name1); + init_dirsearch(&ds1, (struct _inode *) dir1, + (const unsigned char *)name1); err = jffs2_find(&ds1); @@ -892,7 +897,8 @@ static int jffs2_ops_rename(cyg_mtab_ent return err; } - init_dirsearch(&ds2, (struct _inode *) dir2, name2); + init_dirsearch(&ds2, (struct _inode *) dir2, + (const unsigned char *)name2); err = jffs2_find(&ds2); @@ -983,7 +989,8 @@ static int jffs2_ops_link(cyg_mtab_entry if (type != CYG_FSLINK_HARD) return EINVAL; - init_dirsearch(&ds1, (struct _inode *) dir1, name1); + init_dirsearch(&ds1, (struct _inode *) dir1, + (const unsigned char *) name1); err = jffs2_find(&ds1); @@ -992,7 +999,8 @@ static int jffs2_ops_link(cyg_mtab_entry return err; } - init_dirsearch(&ds2, (struct _inode *) dir2, name2); + init_dirsearch(&ds2, (struct _inode *) dir2, + (const unsigned char *) name2); err = jffs2_find(&ds2); @@ -1046,7 +1054,8 @@ static int jffs2_opendir(cyg_mtab_entry D2(printf("jffs2_opendir\n")); - init_dirsearch(&ds, (struct _inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); err = jffs2_find(&ds); @@ -1090,7 +1099,8 @@ static int jffs2_chdir(cyg_mtab_entry * jffs2_dirsearch ds; int err; - init_dirsearch(&ds, (struct _inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); err = jffs2_find(&ds); jffs2_iput(ds.dir); @@ -1132,7 +1142,8 @@ static int jffs2_stat(cyg_mtab_entry * m D2(printf("jffs2_stat\n")); - init_dirsearch(&ds, (struct _inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); err = jffs2_find(&ds); jffs2_iput(ds.dir); @@ -1169,7 +1180,8 @@ static int jffs2_getinfo(cyg_mtab_entry D2(printf("jffs2_getinfo\n")); - init_dirsearch(&ds, (struct _inode *) dir, name); + init_dirsearch(&ds, (struct _inode *) dir, + (const unsigned char *) name); err = jffs2_find(&ds); jffs2_iput(ds.dir); @@ -1377,7 +1389,7 @@ static int jffs2_truncate_file (struct _ inode->i_mtime = cyg_timestamp(); inode->i_size = 0; old_metadata = f->metadata; - jffs2_truncate_fraglist (c, &f->fragtree, 0); + jffs2_truncate_fragtree (c, &f->fragtree, 0); f->metadata = new_metadata; if (old_metadata) { jffs2_mark_node_obsolete(c, old_metadata->raw); @@ -1430,7 +1442,7 @@ static int jffs2_fo_write(struct CYG_FIL // we get an error. for (i = 0; i < uio->uio_iovcnt; i++) { cyg_iovec *iov = &uio->uio_iov[i]; - char *buf = (char *) iov->iov_base; + unsigned char *buf = iov->iov_base; off_t len = iov->iov_len; uint32_t writtenlen; @@ -1621,7 +1633,7 @@ static int jffs2_fo_setinfo(struct CYG_F // jffs2_fo_dirread() // Read a single directory entry from a file. -static __inline void filldir(char *nbuf, int nlen, const char *name, int namlen) +static __inline void filldir(char *nbuf, int nlen, const unsigned char *name, int namlen) { int len = nlen < namlen ? nlen : namlen; memcpy(nbuf, name, len); @@ -1656,11 +1668,11 @@ static int jffs2_fo_dirread(struct CYG_F if (offset == 0) { D1(printk (KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino)); - filldir(nbuf, nlen, ".", 1); + filldir(nbuf, nlen, (const unsigned char *) ".", 1); goto out; } if (offset == 1) { - filldir(nbuf, nlen, "..", 2); + filldir(nbuf, nlen, (const unsigned char *) "..", 2); goto out; } @@ -1687,7 +1699,7 @@ static int jffs2_fo_dirread(struct CYG_F D2(printk (KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type)); - filldir(nbuf, nlen, fd->name, strlen(fd->name)); + filldir(nbuf, nlen, fd->name, strlen((char *)fd->name)); goto out_sem; } /* Reached the end of the directory */ diff --git a/packages/fs/jffs2/current/src/gc.c b/packages/fs/jffs2/current/src/gc.c --- a/packages/fs/jffs2/current/src/gc.c +++ b/packages/fs/jffs2/current/src/gc.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.144 2004/12/21 11:18:50 dwmw2 Exp $ + * $Id: gc.c,v 1.152 2005/07/24 15:14:14 dedekind Exp $ * */ @@ -50,6 +50,7 @@ static struct jffs2_eraseblock *jffs2_fi put the clever wear-levelling algorithms. Eventually. */ /* We possibly want to favour the dirtier blocks more when the number of free blocks is low. */ +again: if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) { D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); nextlist = &c->bad_used_list; @@ -79,6 +80,13 @@ static struct jffs2_eraseblock *jffs2_fi D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n")); nextlist = &c->erasable_list; + } else if (!list_empty(&c->erasable_pending_wbuf_list)) { + /* There are blocks are wating for the wbuf sync */ + D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n")); + spin_unlock(&c->erase_completion_lock); + jffs2_flush_wbuf_pad(c); + spin_lock(&c->erase_completion_lock); + goto again; } else { /* Eep. All were empty */ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n")); @@ -103,7 +111,6 @@ static struct jffs2_eraseblock *jffs2_fi ret->wasted_size = 0; } - D2(jffs2_dump_block_lists(c)); return ret; } @@ -134,7 +141,7 @@ int jffs2_garbage_collect_pass(struct jf if (c->checked_ino > c->highest_ino) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); - D2(jffs2_dump_block_lists(c)); + jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); BUG(); } @@ -477,7 +484,8 @@ static int jffs2_garbage_collect_live(st if (ref_obsolete(raw)) { printk(KERN_WARNING "But it's obsolete so we don't mind too much\n"); } else { - ret = -EIO; + jffs2_dbg_dump_node(c, ref_offset(raw)); + BUG(); } } upnout: @@ -611,16 +619,16 @@ static int jffs2_garbage_collect_pristin D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n")); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy); if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs)); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } @@ -661,9 +669,10 @@ static int jffs2_garbage_collect_metadat { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; + struct jffs2_node_frag *last_frag; jint16_t dev; char *mdata = NULL, mdatalen = 0; - uint32_t alloclen, phys_ofs; + uint32_t alloclen, phys_ofs, ilen; int ret; if (S_ISBLK(JFFS2_F_I_MODE(f)) || @@ -699,6 +708,14 @@ static int jffs2_garbage_collect_metadat goto out; } + last_frag = frag_last(&f->fragtree); + if (last_frag) + /* Fetch the inode length from the fragtree rather then + * from i_size since i_size may have not been updated yet */ + ilen = last_frag->ofs + last_frag->size; + else + ilen = JFFS2_F_I_SIZE(f); + memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); @@ -710,7 +727,7 @@ static int jffs2_garbage_collect_metadat ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); - ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); + ri.isize = cpu_to_je32(ilen); ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); @@ -816,8 +833,7 @@ static int jffs2_garbage_collect_deletio /* Doesn't matter if there's one in the same erase block. We're going to delete it too at the same time. */ - if ((raw->flash_offset & ~(c->sector_size-1)) == - (fd->raw->flash_offset & ~(c->sector_size-1))) + if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset)) continue; D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); @@ -891,7 +907,7 @@ static int jffs2_garbage_collect_hole(st struct jffs2_raw_inode ri; struct jffs2_node_frag *frag; struct jffs2_full_dnode *new_fn; - uint32_t alloclen, phys_ofs; + uint32_t alloclen, phys_ofs, ilen; int ret; D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n", @@ -951,10 +967,19 @@ static int jffs2_garbage_collect_hole(st ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; } + + frag = frag_last(&f->fragtree); + if (frag) + /* Fetch the inode length from the fragtree rather then + * from i_size since i_size may have not been updated yet */ + ilen = frag->ofs + frag->size; + else + ilen = JFFS2_F_I_SIZE(f); + ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); - ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); + ri.isize = cpu_to_je32(ilen); ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); @@ -1161,7 +1186,7 @@ static int jffs2_garbage_collect_dnode(s D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n", orig_start, orig_end, start, end)); - BUG_ON(end > JFFS2_F_I_SIZE(f)); + D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size)); BUG_ON(end < orig_end); BUG_ON(start > orig_start); } diff --git a/packages/fs/jffs2/current/src/gcthread.c b/packages/fs/jffs2/current/src/gcthread.c --- a/packages/fs/jffs2/current/src/gcthread.c +++ b/packages/fs/jffs2/current/src/gcthread.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gcthread.c,v 1.1 2003/12/01 17:02:31 affe Exp $ + * $Id: gcthread.c,v 1.3 2005/01/22 16:01:12 lunn Exp $ * */ #include diff --git a/packages/fs/jffs2/current/src/nodelist.c b/packages/fs/jffs2/current/src/nodelist.c --- a/packages/fs/jffs2/current/src/nodelist.c +++ b/packages/fs/jffs2/current/src/nodelist.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.92 2005/01/19 19:22:00 tpoynor Exp $ + * $Id: nodelist.c,v 1.102 2005/07/28 12:45:10 dedekind Exp $ * */ @@ -24,437 +24,307 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) { struct jffs2_full_dirent **prev = list; - D1(printk(KERN_DEBUG "jffs2_add_fd_to_list( %p, %p (->%p))\n", new, list, *list)); + + JFFS2_DBG_DENTLIST("add dirent \"%s\", ino #%u\n", new->name, new->ino); while ((*prev) && (*prev)->nhash <= new->nhash) { if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) { /* Duplicate. Free one */ if (new->version < (*prev)->version) { - D1(printk(KERN_DEBUG "Eep! Marking new dirent node obsolete\n")); - D1(printk(KERN_DEBUG "New dirent is \"%s\"->ino #%u. Old is \"%s\"->ino #%u\n", new->name, new->ino, (*prev)->name, (*prev)->ino)); + JFFS2_DBG_DENTLIST("Eep! Marking new dirent node is obsolete, old is \"%s\", ino #%u\n", + (*prev)->name, (*prev)->ino); jffs2_mark_node_obsolete(c, new->raw); jffs2_free_full_dirent(new); } else { - D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) obsolete\n", (*prev)->ino)); + JFFS2_DBG_DENTLIST("marking old dirent \"%s\", ino #%u bsolete\n", + (*prev)->name, (*prev)->ino); new->next = (*prev)->next; jffs2_mark_node_obsolete(c, ((*prev)->raw)); jffs2_free_full_dirent(*prev); *prev = new; } - goto out; + return; } prev = &((*prev)->next); } new->next = *prev; *prev = new; - - out: - D2(while(*list) { - printk(KERN_DEBUG "Dirent \"%s\" (hash 0x%08x, ino #%u\n", (*list)->name, (*list)->nhash, (*list)->ino); - list = &(*list)->next; - }); } -/* Put a new tmp_dnode_info into the list, keeping the list in - order of increasing version -*/ -static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) +void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) { - struct jffs2_tmp_dnode_info **prev = list; - - while ((*prev) && (*prev)->version < tn->version) { - prev = &((*prev)->next); + if (this->node) { + this->node->frags--; + if (!this->node->frags) { + /* The node has no valid frags left. It's totally obsoleted */ + JFFS2_DBG_FRAGTREE2("marking old node @0x%08x (0x%04x-0x%04x) obsolete\n", + ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size); + jffs2_mark_node_obsolete(c, this->node->raw); + jffs2_free_full_dnode(this->node); + } else { + JFFS2_DBG_FRAGTREE2("marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n", + ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size, this->node->frags); + mark_ref_normal(this->node->raw); + } + } - tn->next = (*prev); - *prev = tn; + jffs2_free_node_frag(this); } -static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn) +static void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) { - struct jffs2_tmp_dnode_info *next; + struct rb_node *parent = &base->rb; + struct rb_node **link = &parent; - while (tn) { - next = tn; - tn = tn->next; - jffs2_free_full_dnode(next->fn); - jffs2_free_tmp_dnode_info(next); - } -} - -static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) -{ - struct jffs2_full_dirent *next; + JFFS2_DBG_FRAGTREE2("insert frag (0x%04x-0x%04x)\n", newfrag->ofs, newfrag->ofs + newfrag->size); - while (fd) { - next = fd->next; - jffs2_free_full_dirent(fd); - fd = next; + while (*link) { + parent = *link; + base = rb_entry(parent, struct jffs2_node_frag, rb); + + JFFS2_DBG_FRAGTREE2("considering frag at 0x%08x\n", base->ofs); + if (newfrag->ofs > base->ofs) + link = &base->rb.rb_right; + else if (newfrag->ofs < base->ofs) + link = &base->rb.rb_left; + else { + JFFS2_ERROR("duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); + BUG(); + } } -} -/* Returns first valid node after 'ref'. May return 'ref' */ -static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref) -{ - while (ref && ref->next_in_ino) { - if (!ref_obsolete(ref)) - return ref; - D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); - ref = ref->next_in_ino; - } - return NULL; + rb_link_node(&newfrag->rb, &base->rb, link); } -/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated - with this ino, returning the former in order of version */ - -int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, - uint32_t *highest_version, uint32_t *latest_mctime, - uint32_t *mctime_ver) +/* Doesn't set inode->i_size */ +static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag) { - struct jffs2_raw_node_ref *ref, *valid_ref; - struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL; - struct jffs2_full_dirent *fd, *ret_fd = NULL; - union jffs2_node_union node; - size_t retlen; - int err; + struct jffs2_node_frag *this; + uint32_t lastend; - *mctime_ver = 0; - - D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino)); - - spin_lock(&c->erase_completion_lock); - - valid_ref = jffs2_first_valid_node(f->inocache->nodes); - - if (!valid_ref && (f->inocache->ino != 1)) - printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino); - - while (valid_ref) { - /* We can hold a pointer to a non-obsolete node without the spinlock, - but _obsolete_ nodes may disappear at any time, if the block - they're in gets erased. So if we mark 'ref' obsolete while we're - not holding the lock, it can go away immediately. For that reason, - we find the next valid node first, before processing 'ref'. - */ - ref = valid_ref; - valid_ref = jffs2_first_valid_node(ref->next_in_ino); - spin_unlock(&c->erase_completion_lock); + /* Skip all the nodes which are completed before this one starts */ + this = jffs2_lookup_node_frag(list, newfrag->node->ofs); - cond_resched(); + if (this) { + JFFS2_DBG_FRAGTREE2("lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", + this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this); + lastend = this->ofs + this->size; + } else { + JFFS2_DBG_FRAGTREE2("lookup gave no frag\n"); + lastend = 0; + } + + /* See if we ran off the end of the list */ + if (lastend <= newfrag->ofs) { + /* We did */ - /* FIXME: point() */ - err = jffs2_flash_read(c, (ref_offset(ref)), - min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)), - &retlen, (void *)&node); - if (err) { - printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref)); - goto free_out; - } - - - /* Check we've managed to read at least the common node header */ - if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) { - printk(KERN_WARNING "short read in get_inode_nodes()\n"); - err = -EIO; - goto free_out; + /* Check if 'this' node was on the same page as the new node. + If so, both 'this' and the new node get marked REF_NORMAL so + the GC can take a look. + */ + if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { + if (this->node) + mark_ref_normal(this->node->raw); + mark_ref_normal(newfrag->node->raw); } - - switch (je16_to_cpu(node.u.nodetype)) { - case JFFS2_NODETYPE_DIRENT: - D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref))); - if (ref_flags(ref) == REF_UNCHECKED) { - printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref)); - BUG(); - } - if (retlen < sizeof(node.d)) { - printk(KERN_WARNING "short read in get_inode_nodes()\n"); - err = -EIO; - goto free_out; - } - /* sanity check */ - if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) { - printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n", - ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen)); - jffs2_mark_node_obsolete(c, ref); - spin_lock(&c->erase_completion_lock); - continue; + + if (lastend < newfrag->node->ofs) { + /* ... and we need to put a hole in before the new node */ + struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag(); + if (!holefrag) { + jffs2_free_node_frag(newfrag); + return -ENOMEM; } - if (je32_to_cpu(node.d.version) > *highest_version) - *highest_version = je32_to_cpu(node.d.version); - if (ref_obsolete(ref)) { - /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ - printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n", - ref_offset(ref)); - BUG(); + holefrag->ofs = lastend; + holefrag->size = newfrag->node->ofs - lastend; + holefrag->node = NULL; + if (this) { + /* By definition, the 'this' node has no right-hand child, + because there are no frags with offset greater than it. + So that's where we want to put the hole */ + JFFS2_DBG_FRAGTREE2("adding hole frag (%p) on right of node at (%p)\n", holefrag, this); + rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); + } else { + JFFS2_DBG_FRAGTREE2("adding hole frag (%p) at root of tree\n", holefrag); + rb_link_node(&holefrag->rb, NULL, &list->rb_node); } - - fd = jffs2_alloc_full_dirent(node.d.nsize+1); - if (!fd) { - err = -ENOMEM; - goto free_out; - } - fd->raw = ref; - fd->version = je32_to_cpu(node.d.version); - fd->ino = je32_to_cpu(node.d.ino); - fd->type = node.d.type; - - /* Pick out the mctime of the latest dirent */ - if(fd->version > *mctime_ver) { - *mctime_ver = fd->version; - *latest_mctime = je32_to_cpu(node.d.mctime); - } + rb_insert_color(&holefrag->rb, list); + this = holefrag; + } + if (this) { + /* By definition, the 'this' node has no right-hand child, + because there are no frags with offset greater than it. + So that's where we want to put new fragment */ + JFFS2_DBG_FRAGTREE2("adding new frag (%p) on right of node at (%p)\n", newfrag, this); + rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); + } else { + JFFS2_DBG_FRAGTREE2("adding new frag (%p) at root of tree\n", newfrag); + rb_link_node(&newfrag->rb, NULL, &list->rb_node); + } + rb_insert_color(&newfrag->rb, list); + return 0; + } - /* memcpy as much of the name as possible from the raw - dirent we've already read from the flash - */ - if (retlen > sizeof(struct jffs2_raw_dirent)) - memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); - - /* Do we need to copy any more of the name directly - from the flash? - */ - if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) { - /* FIXME: point() */ - int already = retlen - sizeof(struct jffs2_raw_dirent); - - err = jffs2_flash_read(c, (ref_offset(ref)) + retlen, - node.d.nsize - already, &retlen, &fd->name[already]); - if (!err && retlen != node.d.nsize - already) - err = -EIO; - - if (err) { - printk(KERN_WARNING "Read remainder of name in jffs2_get_inode_nodes(): error %d\n", err); - jffs2_free_full_dirent(fd); - goto free_out; - } - } - fd->nhash = full_name_hash(fd->name, node.d.nsize); - fd->next = NULL; - fd->name[node.d.nsize] = '\0'; - /* Wheee. We now have a complete jffs2_full_dirent structure, with - the name in it and everything. Link it into the list - */ - D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino)); - jffs2_add_fd_to_list(c, fd, &ret_fd); - break; + JFFS2_DBG_FRAGTREE2("dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", + this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this); - case JFFS2_NODETYPE_INODE: - D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref))); - if (retlen < sizeof(node.i)) { - printk(KERN_WARNING "read too short for dnode\n"); - err = -EIO; - goto free_out; - } - if (je32_to_cpu(node.i.version) > *highest_version) - *highest_version = je32_to_cpu(node.i.version); - D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version)); + /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes, + * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs + */ + if (newfrag->ofs > this->ofs) { + /* This node isn't completely obsoleted. The start of it remains valid */ - if (ref_obsolete(ref)) { - /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ - printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n", - ref_offset(ref)); - BUG(); - } - - /* If we've never checked the CRCs on this node, check them now. */ - if (ref_flags(ref) == REF_UNCHECKED) { - uint32_t crc, len; - struct jffs2_eraseblock *jeb; + /* Mark the new node and the partially covered node REF_NORMAL -- let + the GC take a look at them */ + mark_ref_normal(newfrag->node->raw); + if (this->node) + mark_ref_normal(this->node->raw); - crc = crc32(0, &node, sizeof(node.i)-8); - if (crc != je32_to_cpu(node.i.node_crc)) { - printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", - ref_offset(ref), je32_to_cpu(node.i.node_crc), crc); - jffs2_mark_node_obsolete(c, ref); - spin_lock(&c->erase_completion_lock); - continue; - } - - /* sanity checks */ - if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) || - PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) { - printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino %d, version %d, isize %d, csize %d, dsize %d \n", - ref_offset(ref), je32_to_cpu(node.i.totlen), je32_to_cpu(node.i.ino), - je32_to_cpu(node.i.version), je32_to_cpu(node.i.isize), - je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize)); - jffs2_mark_node_obsolete(c, ref); - spin_lock(&c->erase_completion_lock); - continue; - } + if (this->ofs + this->size > newfrag->ofs + newfrag->size) { + /* The new node splits 'this' frag into two */ + struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag(); + if (!newfrag2) { + jffs2_free_node_frag(newfrag); + return -ENOMEM; + } + if (this->node) + JFFS2_DBG_FRAGTREE2("split old frag 0x%04x-0x%04x, phys 0x%08x\n", + this->ofs, this->ofs+this->size, ref_offset(this->node->raw)); + else + JFFS2_DBG_FRAGTREE2("split old hole frag 0x%04x-0x%04x\n", + this->ofs, this->ofs+this->size, ref_offset(this->node->raw)); + + /* New second frag pointing to this's node */ + newfrag2->ofs = newfrag->ofs + newfrag->size; + newfrag2->size = (this->ofs+this->size) - newfrag2->ofs; + newfrag2->node = this->node; + if (this->node) + this->node->frags++; - if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) { - unsigned char *buf=NULL; - uint32_t pointed = 0; -#ifndef __ECOS - if (c->mtd->point) { - err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize), - &retlen, &buf); - if (!err && retlen < je32_to_cpu(node.i.csize)) { - D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen)); - c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); - } else if (err){ - D1(printk(KERN_DEBUG "MTD point failed %d\n", err)); - } else - pointed = 1; /* succefully pointed to device */ - } -#endif - if(!pointed){ - buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize), - &retlen, buf); - if (!err && retlen != je32_to_cpu(node.i.csize)) - err = -EIO; - if (err) { - kfree(buf); - return err; - } - } - crc = crc32(0, buf, je32_to_cpu(node.i.csize)); - if(!pointed) - kfree(buf); -#ifndef __ECOS - else - c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); -#endif + /* Adjust size of original 'this' */ + this->size = newfrag->ofs - this->ofs; - if (crc != je32_to_cpu(node.i.data_crc)) { - printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", - ref_offset(ref), je32_to_cpu(node.i.data_crc), crc); - jffs2_mark_node_obsolete(c, ref); - spin_lock(&c->erase_completion_lock); - continue; - } - - } - - /* Mark the node as having been checked and fix the accounting accordingly */ - spin_lock(&c->erase_completion_lock); - jeb = &c->blocks[ref->flash_offset / c->sector_size]; - len = ref_totlen(c, jeb, ref); - - jeb->used_size += len; - jeb->unchecked_size -= len; - c->used_size += len; - c->unchecked_size -= len; - - /* If node covers at least a whole page, or if it starts at the - beginning of a page and runs to the end of the file, or if - it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. + /* Now, we know there's no node with offset + greater than this->ofs but smaller than + newfrag2->ofs or newfrag->ofs, for obvious + reasons. So we can do a tree insert from + 'this' to insert newfrag, and a tree insert + from newfrag to insert newfrag2. */ + jffs2_fragtree_insert(newfrag, this); + rb_insert_color(&newfrag->rb, list); + + jffs2_fragtree_insert(newfrag2, newfrag); + rb_insert_color(&newfrag2->rb, list); + + return 0; + } + /* New node just reduces 'this' frag in size, doesn't split it */ + this->size = newfrag->ofs - this->ofs; - If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) - when the overlapping node(s) get added to the tree anyway. - */ - if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) || - ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) && - (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) { - D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref))); - ref->flash_offset = ref_offset(ref) | REF_PRISTINE; - } else { - D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref))); - ref->flash_offset = ref_offset(ref) | REF_NORMAL; - } - spin_unlock(&c->erase_completion_lock); - } - - tn = jffs2_alloc_tmp_dnode_info(); - if (!tn) { - D1(printk(KERN_DEBUG "alloc tn failed\n")); - err = -ENOMEM; - goto free_out; - } - - tn->fn = jffs2_alloc_full_dnode(); - if (!tn->fn) { - D1(printk(KERN_DEBUG "alloc fn failed\n")); - err = -ENOMEM; - jffs2_free_tmp_dnode_info(tn); - goto free_out; - } - tn->version = je32_to_cpu(node.i.version); - tn->fn->ofs = je32_to_cpu(node.i.offset); - /* There was a bug where we wrote hole nodes out with - csize/dsize swapped. Deal with it */ - if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize)) - tn->fn->size = je32_to_cpu(node.i.csize); - else // normal case... - tn->fn->size = je32_to_cpu(node.i.dsize); - tn->fn->raw = ref; - D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", - ref_offset(ref), je32_to_cpu(node.i.version), - je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize))); - jffs2_add_tn_to_list(tn, &ret_tn); - break; - - default: - if (ref_flags(ref) == REF_UNCHECKED) { - struct jffs2_eraseblock *jeb; - uint32_t len; + /* Again, we know it lives down here in the tree */ + jffs2_fragtree_insert(newfrag, this); + rb_insert_color(&newfrag->rb, list); + } else { + /* New frag starts at the same point as 'this' used to. Replace + it in the tree without doing a delete and insertion */ + JFFS2_DBG_FRAGTREE2("inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n", + newfrag, newfrag->ofs, newfrag->ofs+newfrag->size, this, this->ofs, this->ofs+this->size); + + rb_replace_node(&this->rb, &newfrag->rb, list); + + if (newfrag->ofs + newfrag->size >= this->ofs+this->size) { + JFFS2_DBG_FRAGTREE2("obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size); + jffs2_obsolete_node_frag(c, this); + } else { + this->ofs += newfrag->size; + this->size -= newfrag->size; - printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n", - je16_to_cpu(node.u.nodetype), ref_offset(ref)); - - /* Mark the node as having been checked and fix the accounting accordingly */ - spin_lock(&c->erase_completion_lock); - jeb = &c->blocks[ref->flash_offset / c->sector_size]; - len = ref_totlen(c, jeb, ref); - - jeb->used_size += len; - jeb->unchecked_size -= len; - c->used_size += len; - c->unchecked_size -= len; + jffs2_fragtree_insert(this, newfrag); + rb_insert_color(&this->rb, list); + return 0; + } + } + /* OK, now we have newfrag added in the correct place in the tree, but + frag_next(newfrag) may be a fragment which is overlapped by it + */ + while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) { + /* 'this' frag is obsoleted completely. */ + JFFS2_DBG_FRAGTREE2("obsoleting node frag %p (%x-%x) and removing from tree\n", + this, this->ofs, this->ofs+this->size); + rb_erase(&this->rb, list); + jffs2_obsolete_node_frag(c, this); + } + /* Now we're pointing at the first frag which isn't totally obsoleted by + the new frag */ - mark_ref_normal(ref); - spin_unlock(&c->erase_completion_lock); - } - node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype)); - if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) { - /* Hmmm. This should have been caught at scan time. */ - printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n", - ref_offset(ref)); - printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n", - je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen), - je32_to_cpu(node.u.hdr_crc)); - jffs2_mark_node_obsolete(c, ref); - } else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) { - case JFFS2_FEATURE_INCOMPAT: - printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); - /* EEP */ - BUG(); - break; - case JFFS2_FEATURE_ROCOMPAT: - printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); - if (!(c->flags & JFFS2_SB_FLAG_RO)) - BUG(); - break; - case JFFS2_FEATURE_RWCOMPAT_COPY: - printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); - break; - case JFFS2_FEATURE_RWCOMPAT_DELETE: - printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); - jffs2_mark_node_obsolete(c, ref); - break; - } + if (!this || newfrag->ofs + newfrag->size == this->ofs) { + return 0; + } + /* Still some overlap but we don't need to move it in the tree */ + this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); + this->ofs = newfrag->ofs + newfrag->size; - } - spin_lock(&c->erase_completion_lock); - - } - spin_unlock(&c->erase_completion_lock); - *tnp = ret_tn; - *fdp = ret_fd; + /* And mark them REF_NORMAL so the GC takes a look at them */ + if (this->node) + mark_ref_normal(this->node->raw); + mark_ref_normal(newfrag->node->raw); return 0; +} - free_out: - jffs2_free_tmp_dnode_info_list(ret_tn); - jffs2_free_full_dirent_list(ret_fd); - return err; +/* Given an inode, probably with existing list of fragments, add the new node + * to the fragment list. + */ +int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) +{ + int ret; + struct jffs2_node_frag *newfrag; + + if (unlikely(!fn->size)) + return 0; + + newfrag = jffs2_alloc_node_frag(); + if (unlikely(!newfrag)) + return -ENOMEM; + + JFFS2_DBG_FRAGTREE("adding node %#04x-%#04x @0x%08x on flash, newfrag *%p\n", + fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); + + newfrag->ofs = fn->ofs; + newfrag->size = fn->size; + newfrag->node = fn; + newfrag->node->frags = 1; + + ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); + if (unlikely(ret)) + return ret; + + /* If we now share a page with other nodes, mark either previous + or next node REF_NORMAL, as appropriate. */ + if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) { + struct jffs2_node_frag *prev = frag_prev(newfrag); + + mark_ref_normal(fn->raw); + /* If we don't start at zero there's _always_ a previous */ + if (prev->node) + mark_ref_normal(prev->node->raw); + } + + if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { + struct jffs2_node_frag *next = frag_next(newfrag); + + if (next) { + mark_ref_normal(fn->raw); + if (next->node) + mark_ref_normal(next->node->raw); + } + } + jffs2_dbg_fragtree_paranoia_check_nolock(f); + jffs2_dbg_dump_fragtree_nolock(f); + return 0; } + void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state) { spin_lock(&c->inocache_lock); @@ -472,8 +342,6 @@ struct jffs2_inode_cache *jffs2_get_ino_ { struct jffs2_inode_cache *ret; - D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino)); - ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; while (ret && ret->ino < ino) { ret = ret->next; @@ -482,18 +350,19 @@ struct jffs2_inode_cache *jffs2_get_ino_ if (ret && ret->ino != ino) ret = NULL; - D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino)); return ret; } void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new) { struct jffs2_inode_cache **prev; - D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino)); - BUG_ON(!new->ino); spin_lock(&c->inocache_lock); - + if (!new->ino) + new->ino = ++c->highest_ino; + + JFFS2_DBG_INOCACHE("add %p (ino #%u)\n", new, new->ino); + prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE]; while ((*prev) && (*prev)->ino < new->ino) { @@ -508,7 +377,8 @@ void jffs2_add_ino_cache (struct jffs2_s void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old) { struct jffs2_inode_cache **prev; - D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino)); + + JFFS2_DBG_INOCACHE("del %p (ino #%u)\n", old, old->ino); spin_lock(&c->inocache_lock); prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE]; @@ -520,6 +390,14 @@ void jffs2_del_ino_cache(struct jffs2_sb *prev = old->next; } + /* Free it now unless it's in READING or CLEARING state, which + are the transitions upon read_inode() and clear_inode(). The + rest of the time we know nobody else is looking at it, and + if it's held by read_inode() or clear_inode() they'll free it + for themselves. */ + if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING) + jffs2_free_inode_cache(old); + spin_unlock(&c->inocache_lock); } @@ -532,7 +410,6 @@ void jffs2_free_ino_caches(struct jffs2_ this = c->inocache_list[i]; while (this) { next = this->next; - D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this)); jffs2_free_inode_cache(this); this = next; } @@ -564,29 +441,29 @@ struct jffs2_node_frag *jffs2_lookup_nod struct jffs2_node_frag *prev = NULL; struct jffs2_node_frag *frag = NULL; - D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset)); + JFFS2_DBG_FRAGTREE2("root %p, offset %d\n", fragtree, offset); next = fragtree->rb_node; while(next) { frag = rb_entry(next, struct jffs2_node_frag, rb); - D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n", - frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right)); + JFFS2_DBG_FRAGTREE2("considering frag %#04x-%#04x (%p). left %p, right %p\n", + frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right); if (frag->ofs + frag->size <= offset) { - D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n", - frag->ofs, frag->ofs+frag->size)); + JFFS2_DBG_FRAGTREE2("going right from frag %#04x-%#04x, before the region we care about\n", + frag->ofs, frag->ofs+frag->size); /* Remember the closest smaller match on the way down */ if (!prev || frag->ofs > prev->ofs) prev = frag; next = frag->rb.rb_right; } else if (frag->ofs > offset) { - D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n", - frag->ofs, frag->ofs+frag->size)); + JFFS2_DBG_FRAGTREE2("going left from frag %#04x-%#04x, after the region we care about\n", + frag->ofs, frag->ofs+frag->size); next = frag->rb.rb_left; } else { - D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n", - frag->ofs, frag->ofs+frag->size)); + JFFS2_DBG_FRAGTREE2("returning frag %#04x-%#04x, matched\n", + frag->ofs, frag->ofs+frag->size); return frag; } } @@ -595,10 +472,10 @@ struct jffs2_node_frag *jffs2_lookup_nod and return the closest smaller one */ if (prev) - D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n", - prev->ofs, prev->ofs+prev->size)); + JFFS2_DBG_FRAGTREE2("no match. Returning frag %#04x-%#04x, closest previous\n", + prev->ofs, prev->ofs+prev->size); else - D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n")); + JFFS2_DBG_FRAGTREE2("returning NULL, empty fragtree\n"); return prev; } @@ -613,25 +490,25 @@ void jffs2_kill_fragtree(struct rb_root if (!root->rb_node) return; + JFFS2_DBG_FRAGTREE("killing\n"); + frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); - while(frag) { if (frag->rb.rb_left) { - D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n", - frag, frag->ofs, frag->ofs+frag->size)); + JFFS2_DBG_FRAGTREE2("going left from frag (%p) %#04x-%#04x\n", + frag, frag->ofs, frag->ofs+frag->size); frag = frag_left(frag); continue; } if (frag->rb.rb_right) { - D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n", - frag, frag->ofs, frag->ofs+frag->size)); + JFFS2_DBG_FRAGTREE2("going right from frag (%p) %#04x-%#04x\n", + frag, frag->ofs, frag->ofs+frag->size); frag = frag_right(frag); continue; } - D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n", - frag->ofs, frag->ofs+frag->size, frag->node, - frag->node?frag->node->frags:0)); + JFFS2_DBG_FRAGTREE2("frag %#04x-%#04x: node %p, frags %d\n", + frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0); if (frag->node && !(--frag->node->frags)) { /* Not a hole, and it's the final remaining frag @@ -655,29 +532,3 @@ void jffs2_kill_fragtree(struct rb_root cond_resched(); } } - -void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) -{ - struct rb_node *parent = &base->rb; - struct rb_node **link = &parent; - - D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, - newfrag->ofs, newfrag->ofs+newfrag->size, base)); - - while (*link) { - parent = *link; - base = rb_entry(parent, struct jffs2_node_frag, rb); - - D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs)); - if (newfrag->ofs > base->ofs) - link = &base->rb.rb_right; - else if (newfrag->ofs < base->ofs) - link = &base->rb.rb_left; - else { - printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); - BUG(); - } - } - - rb_link_node(&newfrag->rb, &base->rb, link); -} diff --git a/packages/fs/jffs2/current/src/nodelist.h b/packages/fs/jffs2/current/src/nodelist.h --- a/packages/fs/jffs2/current/src/nodelist.h +++ b/packages/fs/jffs2/current/src/nodelist.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.126 2004/11/19 15:06:29 dedekind Exp $ + * $Id: nodelist.h,v 1.135 2005/07/27 14:46:11 dedekind Exp $ * */ @@ -24,26 +24,10 @@ #ifdef __ECOS #include "os-ecos.h" #else -#include /* For min/max in older kernels */ +#include /* For compatibility with older kernels */ #include "os-linux.h" #endif -#ifndef CONFIG_JFFS2_FS_DEBUG -#define CONFIG_JFFS2_FS_DEBUG 1 -#endif - -#if CONFIG_JFFS2_FS_DEBUG > 0 -#define D1(x) x -#else -#define D1(x) -#endif - -#if CONFIG_JFFS2_FS_DEBUG > 1 -#define D2(x) x -#else -#define D2(x) -#endif - #define JFFS2_NATIVE_ENDIAN /* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from @@ -135,6 +119,7 @@ struct jffs2_inode_cache { #define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */ #define INO_STATE_GC 4 /* GCing a 'pristine' node */ #define INO_STATE_READING 5 /* In read_inode() */ +#define INO_STATE_CLEARING 6 /* In clear_inode() */ #define INOCACHE_HASHSIZE 128 @@ -160,7 +145,7 @@ struct jffs2_full_dnode */ struct jffs2_tmp_dnode_info { - struct jffs2_tmp_dnode_info *next; + struct rb_node rb; struct jffs2_full_dnode *fn; uint32_t version; }; @@ -206,79 +191,6 @@ struct jffs2_eraseblock struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */ }; -#define ACCT_SANITY_CHECK(c, jeb) do { \ - struct jffs2_eraseblock *___j = jeb; \ - if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \ - printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \ - printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \ - ___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \ - BUG(); \ - } \ - if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \ - printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \ - printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \ - c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \ - BUG(); \ - } \ -} while(0) - -static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) -{ - struct jffs2_raw_node_ref *ref; - int i=0; - - printk(KERN_NOTICE); - for (ref = jeb->first_node; ref; ref = ref->next_phys) { - printk("%08x->", ref_offset(ref)); - if (++i == 8) { - i = 0; - printk("\n" KERN_NOTICE); - } - } - printk("\n"); -} - - -#define ACCT_PARANOIA_CHECK(jeb) do { \ - uint32_t my_used_size = 0; \ - uint32_t my_unchecked_size = 0; \ - struct jffs2_raw_node_ref *ref2 = jeb->first_node; \ - while (ref2) { \ - if (unlikely(ref2->flash_offset < jeb->offset || \ - ref2->flash_offset > jeb->offset + c->sector_size)) { \ - printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \ - ref_offset(ref2), jeb->offset); \ - paranoia_failed_dump(jeb); \ - BUG(); \ - } \ - if (ref_flags(ref2) == REF_UNCHECKED) \ - my_unchecked_size += ref_totlen(c, jeb, ref2); \ - else if (!ref_obsolete(ref2)) \ - my_used_size += ref_totlen(c, jeb, ref2); \ - if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \ - if (!ref2->next_phys) \ - printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \ - ref2, ref_offset(ref2), ref2->next_phys, \ - jeb->last_node, ref_offset(jeb->last_node)); \ - else \ - printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \ - ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \ - jeb->last_node, ref_offset(jeb->last_node)); \ - paranoia_failed_dump(jeb); \ - BUG(); \ - } \ - ref2 = ref2->next_phys; \ - } \ - if (my_used_size != jeb->used_size) { \ - printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \ - BUG(); \ - } \ - if (my_unchecked_size != jeb->unchecked_size) { \ - printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \ - BUG(); \ - } \ - } while(0) - /* Calculate totlen from surrounding nodes or eraseblock */ static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, @@ -305,11 +217,13 @@ static inline uint32_t ref_totlen(struct { uint32_t ret; - D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { +#if CONFIG_JFFS2_FS_DEBUG > 0 + if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n", jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref)); BUG(); - }) + } +#endif #if 1 ret = ref->__totlen; @@ -322,14 +236,13 @@ static inline uint32_t ref_totlen(struct ret, ref->__totlen); if (!jeb) jeb = &c->blocks[ref->flash_offset / c->sector_size]; - paranoia_failed_dump(jeb); + jffs2_dbg_dump_node_refs_nolock(c, jeb); BUG(); } #endif return ret; } - #define ALLOC_NORMAL 0 /* Normal allocation */ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ #define ALLOC_GC 2 /* Space requested for GC. Give it or die */ @@ -362,6 +275,18 @@ static inline struct jffs2_node_frag *fr node = node->rb_left; return rb_entry(node, struct jffs2_node_frag, rb); } + +static inline struct jffs2_node_frag *frag_last(struct rb_root *root) +{ + struct rb_node *node = root->rb_node; + + if (!node) + return NULL; + while(node->rb_right) + node = node->rb_right; + return rb_entry(node, struct jffs2_node_frag, rb); +} + #define rb_parent(rb) ((rb)->rb_parent) #define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb) @@ -371,12 +296,7 @@ static inline struct jffs2_node_frag *fr #define frag_erase(frag, list) rb_erase(&frag->rb, list); /* nodelist.c */ -D2(void jffs2_print_frag_list(struct jffs2_inode_info *f)); void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); -int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, - uint32_t *highest_version, uint32_t *latest_mctime, - uint32_t *mctime_ver); void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state); struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino); void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new); @@ -385,10 +305,11 @@ void jffs2_free_ino_caches(struct jffs2_ void jffs2_free_raw_node_refs(struct jffs2_sb_info *c); struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset); void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete); -void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base); struct rb_node *rb_next(struct rb_node *); struct rb_node *rb_prev(struct rb_node *); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); +void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this); +int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); /* nodemgmt.c */ int jffs2_thread_should_wake(struct jffs2_sb_info *c); @@ -397,7 +318,6 @@ int jffs2_reserve_space_gc(struct jffs2_ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); void jffs2_complete_reservation(struct jffs2_sb_info *c); void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); -void jffs2_dump_block_lists(struct jffs2_sb_info *c); /* write.c */ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri); @@ -413,8 +333,7 @@ int jffs2_do_link (struct jffs2_sb_info /* readinode.c */ -void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); -int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); +void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t ino, struct jffs2_raw_inode *latest_node); int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); @@ -460,10 +379,9 @@ void jffs2_rotate_lists(struct jffs2_sb_ int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ -void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* wbuf.c */ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); @@ -471,4 +389,6 @@ int jffs2_check_nand_cleanmarker(struct int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); #endif +#include "debug.h" + #endif /* __JFFS2_NODELIST_H__ */ diff --git a/packages/fs/jffs2/current/src/nodemgmt.c b/packages/fs/jffs2/current/src/nodemgmt.c --- a/packages/fs/jffs2/current/src/nodemgmt.c +++ b/packages/fs/jffs2/current/src/nodemgmt.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.115 2004/11/22 11:07:21 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.124 2005/07/20 15:32:28 dedekind Exp $ * */ @@ -75,7 +75,7 @@ int jffs2_reserve_space(struct jffs2_sb_ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; if (dirty < c->nospc_dirty_size) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { - printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n"); + D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", @@ -98,7 +98,7 @@ int jffs2_reserve_space(struct jffs2_sb_ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; if ( (avail / c->sector_size) <= blocksneeded) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { - printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n"); + D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n")); break; } @@ -308,7 +308,10 @@ int jffs2_add_physical_node_ref(struct j D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); #if 1 - if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) { + /* we could get some obsolete nodes after nextblock was refiled + in wbuf.c */ + if ((c->nextblock || !ref_obsolete(new)) + &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) { printk(KERN_WARNING "argh. node added in wrong place\n"); jffs2_free_raw_node_ref(new); return -EINVAL; @@ -332,7 +335,7 @@ int jffs2_add_physical_node_ref(struct j c->used_size += len; } - if (!jeb->free_size && !jeb->dirty_size) { + if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) { /* If it lives on the dirty_list, jffs2_reserve_space will put it there */ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); @@ -346,8 +349,8 @@ int jffs2_add_physical_node_ref(struct j list_add_tail(&jeb->list, &c->clean_list); c->nextblock = NULL; } - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check_nolock(c,jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); spin_unlock(&c->erase_completion_lock); @@ -400,7 +403,7 @@ void jffs2_mark_node_obsolete(struct jff jeb = &c->blocks[blocknr]; if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) && - !(c->flags & JFFS2_SB_FLAG_MOUNTING)) { + !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) { /* Hm. This may confuse static lock analysis. If any of the above three conditions is false, we're going to return from this function without actually obliterating any nodes or freeing @@ -427,14 +430,14 @@ void jffs2_mark_node_obsolete(struct jff ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); BUG(); }) - D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); + D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); jeb->used_size -= ref_totlen(c, jeb, ref); c->used_size -= ref_totlen(c, jeb, ref); } // Take care, that wasted size is taken into concern if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { - D1(printk("Dirtying\n")); + D1(printk(KERN_DEBUG "Dirtying\n")); addedsize = ref_totlen(c, jeb, ref); jeb->dirty_size += ref_totlen(c, jeb, ref); c->dirty_size += ref_totlen(c, jeb, ref); @@ -456,19 +459,18 @@ void jffs2_mark_node_obsolete(struct jff } } } else { - D1(printk("Wasting\n")); + D1(printk(KERN_DEBUG "Wasting\n")); addedsize = 0; jeb->wasted_size += ref_totlen(c, jeb, ref); c->wasted_size += ref_totlen(c, jeb, ref); } ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; - ACCT_SANITY_CHECK(c, jeb); + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); - - if (c->flags & JFFS2_SB_FLAG_MOUNTING) { - /* Mount in progress. Don't muck about with the block + if (c->flags & JFFS2_SB_FLAG_SCANNING) { + /* Flash scanning is in progress. Don't muck about with the block lists because they're not ready yet, and don't actually obliterate nodes that look obsolete. If they weren't marked obsolete on the flash at the time they _became_ @@ -527,7 +529,8 @@ void jffs2_mark_node_obsolete(struct jff spin_unlock(&c->erase_completion_lock); - if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c)) { + if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) || + (c->flags & JFFS2_SB_FLAG_BUILDING)) { /* We didn't lock the erase_free_sem */ return; } @@ -590,11 +593,8 @@ void jffs2_mark_node_obsolete(struct jff *p = ref->next_in_ino; ref->next_in_ino = NULL; - if (ic->nodes == (void *)ic) { - D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino)); + if (ic->nodes == (void *)ic && ic->nlink == 0) jffs2_del_ino_cache(c, ic); - jffs2_free_inode_cache(ic); - } spin_unlock(&c->erase_completion_lock); } @@ -648,164 +648,6 @@ void jffs2_mark_node_obsolete(struct jff up(&c->erase_free_sem); } -#if CONFIG_JFFS2_FS_DEBUG >= 2 -void jffs2_dump_block_lists(struct jffs2_sb_info *c) -{ - - - printk(KERN_DEBUG "jffs2_dump_block_lists:\n"); - printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); - printk(KERN_DEBUG "used_size: %08x\n", c->used_size); - printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); - printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size); - printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size); - printk(KERN_DEBUG "free_size: %08x\n", c->free_size); - printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); - printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); - printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); - printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write); - - if (c->nextblock) { - printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size); - } else { - printk(KERN_DEBUG "nextblock: NULL\n"); - } - if (c->gcblock) { - printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size); - } else { - printk(KERN_DEBUG "gcblock: NULL\n"); - } - if (list_empty(&c->clean_list)) { - printk(KERN_DEBUG "clean_list: empty\n"); - } else { - struct list_head *this; - int numblocks = 0; - uint32_t dirty = 0; - - list_for_each(this, &c->clean_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - numblocks ++; - dirty += jeb->wasted_size; - printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks); - } - if (list_empty(&c->very_dirty_list)) { - printk(KERN_DEBUG "very_dirty_list: empty\n"); - } else { - struct list_head *this; - int numblocks = 0; - uint32_t dirty = 0; - - list_for_each(this, &c->very_dirty_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - numblocks ++; - dirty += jeb->dirty_size; - printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", - numblocks, dirty, dirty / numblocks); - } - if (list_empty(&c->dirty_list)) { - printk(KERN_DEBUG "dirty_list: empty\n"); - } else { - struct list_head *this; - int numblocks = 0; - uint32_t dirty = 0; - - list_for_each(this, &c->dirty_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - numblocks ++; - dirty += jeb->dirty_size; - printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", - numblocks, dirty, dirty / numblocks); - } - if (list_empty(&c->erasable_list)) { - printk(KERN_DEBUG "erasable_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasable_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->erasing_list)) { - printk(KERN_DEBUG "erasing_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasing_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->erase_pending_list)) { - printk(KERN_DEBUG "erase_pending_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erase_pending_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->erasable_pending_wbuf_list)) { - printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasable_pending_wbuf_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->free_list)) { - printk(KERN_DEBUG "free_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->free_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->bad_list)) { - printk(KERN_DEBUG "bad_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->bad_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } - if (list_empty(&c->bad_used_list)) { - printk(KERN_DEBUG "bad_used_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->bad_used_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", - jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); - } - } -} -#endif /* CONFIG_JFFS2_FS_DEBUG */ - int jffs2_thread_should_wake(struct jffs2_sb_info *c) { int ret = 0; diff --git a/packages/fs/jffs2/current/src/os-ecos.h b/packages/fs/jffs2/current/src/os-ecos.h --- a/packages/fs/jffs2/current/src/os-ecos.h +++ b/packages/fs/jffs2/current/src/os-ecos.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-ecos.h,v 1.20 2004/05/05 12:00:31 dwmw2 Exp $ + * $Id: os-ecos.h,v 1.24 2005/02/09 09:23:55 pavlov Exp $ * */ @@ -175,9 +175,9 @@ uint32_t jffs2_to_os_mode (uint32_t jmod /* flashio.c */ cyg_bool jffs2_flash_read(struct jffs2_sb_info *c, cyg_uint32 read_buffer_offset, - const size_t size, size_t * return_size, char * write_buffer); + const size_t size, size_t * return_size, unsigned char * write_buffer); cyg_bool jffs2_flash_write(struct jffs2_sb_info *c, cyg_uint32 write_buffer_offset, - const size_t size, size_t * return_size, char * read_buffer); + const size_t size, size_t * return_size, unsigned char * read_buffer); int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); cyg_bool jffs2_flash_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); @@ -196,12 +196,13 @@ int jffs2_rename (struct _inode *old_dir static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) { } -#ifndef CONFIG_JFFS2_FS_NAND +#ifndef CONFIG_JFFS2_FS_WRITEBUFFER +#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) ) #define jffs2_can_mark_obsolete(c) (1) #define jffs2_cleanmarker_oob(c) (0) #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) -#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; }) +#define jffs2_flush_wbuf_pad(c) (c=c) #define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; }) #define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0) #define jffs2_write_nand_badblock(c,jeb,p) (0) diff --git a/packages/fs/jffs2/current/src/read.c b/packages/fs/jffs2/current/src/read.c --- a/packages/fs/jffs2/current/src/read.c +++ b/packages/fs/jffs2/current/src/read.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.38 2004/11/16 20:36:12 dwmw2 Exp $ + * $Id: read.c,v 1.41 2005/07/22 10:32:08 dedekind Exp $ * */ @@ -174,7 +174,6 @@ int jffs2_read_inode_range(struct jffs2_ if (frag) { D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); holesize = min(holesize, frag->ofs - offset); - D2(jffs2_print_frag_list(f)); } D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); memset(buf, 0, holesize); @@ -214,33 +213,3 @@ int jffs2_read_inode_range(struct jffs2_ return 0; } -/* Core function to read symlink target. */ -char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f) -{ - char *buf; - int ret; - - down(&f->sem); - - if (!f->metadata) { - printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino); - up(&f->sem); - return ERR_PTR(-EINVAL); - } - buf = kmalloc(f->metadata->size+1, GFP_USER); - if (!buf) { - up(&f->sem); - return ERR_PTR(-ENOMEM); - } - buf[f->metadata->size]=0; - - ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size); - - up(&f->sem); - - if (ret) { - kfree(buf); - return ERR_PTR(ret); - } - return buf; -} diff --git a/packages/fs/jffs2/current/src/readinode.c b/packages/fs/jffs2/current/src/readinode.c --- a/packages/fs/jffs2/current/src/readinode.c +++ b/packages/fs/jffs2/current/src/readinode.c @@ -7,11 +7,12 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.117 2004/11/20 18:06:54 dwmw2 Exp $ + * $Id: readinode.c,v 1.132 2005/07/28 14:46:40 dedekind Exp $ * */ #include +#include #include #include #include @@ -20,373 +21,16 @@ #include #include "nodelist.h" -static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag); - -#if CONFIG_JFFS2_FS_DEBUG >= 2 -static void jffs2_print_fragtree(struct rb_root *list, int permitbug) -{ - struct jffs2_node_frag *this = frag_first(list); - uint32_t lastofs = 0; - int buggy = 0; - - while(this) { - if (this->node) - printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n", - this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw), - this, frag_left(this), frag_right(this), frag_parent(this)); - else - printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs, - this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this)); - if (this->ofs != lastofs) - buggy = 1; - lastofs = this->ofs+this->size; - this = frag_next(this); - } - if (buggy && !permitbug) { - printk(KERN_CRIT "Frag tree got a hole in it\n"); - BUG(); - } -} - -void jffs2_print_frag_list(struct jffs2_inode_info *f) -{ - jffs2_print_fragtree(&f->fragtree, 0); - - if (f->metadata) { - printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); - } -} -#endif - -#if CONFIG_JFFS2_FS_DEBUG >= 1 -static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f) -{ - struct jffs2_node_frag *frag; - int bitched = 0; - - for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { - - struct jffs2_full_dnode *fn = frag->node; - if (!fn || !fn->raw) - continue; - - if (ref_flags(fn->raw) == REF_PRISTINE) { - - if (fn->frags > 1) { - printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags); - bitched = 1; - } - /* A hole node which isn't multi-page should be garbage-collected - and merged anyway, so we just check for the frag size here, - rather than mucking around with actually reading the node - and checking the compression type, which is the real way - to tell a hole node. */ - if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) { - printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n", - ref_offset(fn->raw)); - bitched = 1; - } - - if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { - printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n", - ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); - bitched = 1; - } - } - } - - if (bitched) { - struct jffs2_node_frag *thisfrag; - - printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino); - thisfrag = frag_first(&f->fragtree); - while (thisfrag) { - if (!thisfrag->node) { - printk("Frag @0x%x-0x%x; node-less hole\n", - thisfrag->ofs, thisfrag->size + thisfrag->ofs); - } else if (!thisfrag->node->raw) { - printk("Frag @0x%x-0x%x; raw-less hole\n", - thisfrag->ofs, thisfrag->size + thisfrag->ofs); - } else { - printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n", - thisfrag->ofs, thisfrag->size + thisfrag->ofs, - ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw), - thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size); - } - thisfrag = frag_next(thisfrag); - } - } - return bitched; -} -#endif /* D1 */ - -static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) -{ - if (this->node) { - this->node->frags--; - if (!this->node->frags) { - /* The node has no valid frags left. It's totally obsoleted */ - D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n", - ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size)); - jffs2_mark_node_obsolete(c, this->node->raw); - jffs2_free_full_dnode(this->node); - } else { - D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n", - ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size, - this->node->frags)); - mark_ref_normal(this->node->raw); - } - - } - jffs2_free_node_frag(this); -} - -/* Given an inode, probably with existing list of fragments, add the new node - * to the fragment list. - */ -int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) -{ - int ret; - struct jffs2_node_frag *newfrag; - - D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); - - newfrag = jffs2_alloc_node_frag(); - if (unlikely(!newfrag)) - return -ENOMEM; - - D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", - fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag)); - - if (unlikely(!fn->size)) { - jffs2_free_node_frag(newfrag); - return 0; - } - - newfrag->ofs = fn->ofs; - newfrag->size = fn->size; - newfrag->node = fn; - newfrag->node->frags = 1; - - ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); - if (ret) - return ret; - - /* If we now share a page with other nodes, mark either previous - or next node REF_NORMAL, as appropriate. */ - if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) { - struct jffs2_node_frag *prev = frag_prev(newfrag); - - mark_ref_normal(fn->raw); - /* If we don't start at zero there's _always_ a previous */ - if (prev->node) - mark_ref_normal(prev->node->raw); - } - - if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { - struct jffs2_node_frag *next = frag_next(newfrag); - - if (next) { - mark_ref_normal(fn->raw); - if (next->node) - mark_ref_normal(next->node->raw); - } - } - D2(if (jffs2_sanitycheck_fragtree(f)) { - printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n", - fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); - return 0; - }) - D2(jffs2_print_frag_list(f)); - return 0; -} - -/* Doesn't set inode->i_size */ -static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag) -{ - struct jffs2_node_frag *this; - uint32_t lastend; - - /* Skip all the nodes which are completed before this one starts */ - this = jffs2_lookup_node_frag(list, newfrag->node->ofs); - - if (this) { - D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", - this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this)); - lastend = this->ofs + this->size; - } else { - D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n")); - lastend = 0; - } - - /* See if we ran off the end of the list */ - if (lastend <= newfrag->ofs) { - /* We did */ - - /* Check if 'this' node was on the same page as the new node. - If so, both 'this' and the new node get marked REF_NORMAL so - the GC can take a look. - */ - if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { - if (this->node) - mark_ref_normal(this->node->raw); - mark_ref_normal(newfrag->node->raw); - } - - if (lastend < newfrag->node->ofs) { - /* ... and we need to put a hole in before the new node */ - struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag(); - if (!holefrag) { - jffs2_free_node_frag(newfrag); - return -ENOMEM; - } - holefrag->ofs = lastend; - holefrag->size = newfrag->node->ofs - lastend; - holefrag->node = NULL; - if (this) { - /* By definition, the 'this' node has no right-hand child, - because there are no frags with offset greater than it. - So that's where we want to put the hole */ - D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this)); - rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); - } else { - D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag)); - rb_link_node(&holefrag->rb, NULL, &list->rb_node); - } - rb_insert_color(&holefrag->rb, list); - this = holefrag; - } - if (this) { - /* By definition, the 'this' node has no right-hand child, - because there are no frags with offset greater than it. - So that's where we want to put the hole */ - D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this)); - rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); - } else { - D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag)); - rb_link_node(&newfrag->rb, NULL, &list->rb_node); - } - rb_insert_color(&newfrag->rb, list); - return 0; - } - - D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", - this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this)); - - /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes, - * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs - */ - if (newfrag->ofs > this->ofs) { - /* This node isn't completely obsoleted. The start of it remains valid */ - - /* Mark the new node and the partially covered node REF_NORMAL -- let - the GC take a look at them */ - mark_ref_normal(newfrag->node->raw); - if (this->node) - mark_ref_normal(this->node->raw); - - if (this->ofs + this->size > newfrag->ofs + newfrag->size) { - /* The new node splits 'this' frag into two */ - struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag(); - if (!newfrag2) { - jffs2_free_node_frag(newfrag); - return -ENOMEM; - } - D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); - if (this->node) - printk("phys 0x%08x\n", ref_offset(this->node->raw)); - else - printk("hole\n"); - ) - - /* New second frag pointing to this's node */ - newfrag2->ofs = newfrag->ofs + newfrag->size; - newfrag2->size = (this->ofs+this->size) - newfrag2->ofs; - newfrag2->node = this->node; - if (this->node) - this->node->frags++; - - /* Adjust size of original 'this' */ - this->size = newfrag->ofs - this->ofs; - - /* Now, we know there's no node with offset - greater than this->ofs but smaller than - newfrag2->ofs or newfrag->ofs, for obvious - reasons. So we can do a tree insert from - 'this' to insert newfrag, and a tree insert - from newfrag to insert newfrag2. */ - jffs2_fragtree_insert(newfrag, this); - rb_insert_color(&newfrag->rb, list); - - jffs2_fragtree_insert(newfrag2, newfrag); - rb_insert_color(&newfrag2->rb, list); - - return 0; - } - /* New node just reduces 'this' frag in size, doesn't split it */ - this->size = newfrag->ofs - this->ofs; - - /* Again, we know it lives down here in the tree */ - jffs2_fragtree_insert(newfrag, this); - rb_insert_color(&newfrag->rb, list); - } else { - /* New frag starts at the same point as 'this' used to. Replace - it in the tree without doing a delete and insertion */ - D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n", - newfrag, newfrag->ofs, newfrag->ofs+newfrag->size, - this, this->ofs, this->ofs+this->size)); - - rb_replace_node(&this->rb, &newfrag->rb, list); - - if (newfrag->ofs + newfrag->size >= this->ofs+this->size) { - D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size)); - jffs2_obsolete_node_frag(c, this); - } else { - this->ofs += newfrag->size; - this->size -= newfrag->size; - - jffs2_fragtree_insert(this, newfrag); - rb_insert_color(&this->rb, list); - return 0; - } - } - /* OK, now we have newfrag added in the correct place in the tree, but - frag_next(newfrag) may be a fragment which is overlapped by it - */ - while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) { - /* 'this' frag is obsoleted completely. */ - D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size)); - rb_erase(&this->rb, list); - jffs2_obsolete_node_frag(c, this); - } - /* Now we're pointing at the first frag which isn't totally obsoleted by - the new frag */ - - if (!this || newfrag->ofs + newfrag->size == this->ofs) { - return 0; - } - /* Still some overlap but we don't need to move it in the tree */ - this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); - this->ofs = newfrag->ofs + newfrag->size; - - /* And mark them REF_NORMAL so the GC takes a look at them */ - if (this->node) - mark_ref_normal(this->node->raw); - mark_ref_normal(newfrag->node->raw); - - return 0; -} - -void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) +void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) { struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); - D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size)); + JFFS2_DBG_FRAGTREE("truncating fragtree to 0x%08x bytes\n", size); /* We know frag->ofs <= size. That's what lookup does for us */ if (frag && frag->ofs != size) { if (frag->ofs+frag->size >= size) { - D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); + JFFS2_DBG_FRAGTREE2("truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size); frag->size = size - frag->ofs; } frag = frag_next(frag); @@ -394,30 +38,745 @@ void jffs2_truncate_fraglist (struct jff while (frag && frag->ofs >= size) { struct jffs2_node_frag *next = frag_next(frag); - D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); + JFFS2_DBG_FRAGTREE("removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size); frag_erase(frag, list); jffs2_obsolete_node_frag(c, frag); frag = next; } } -/* Scan the list of all nodes present for this ino, build map of versions, etc. */ +/* + * Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in + * order of increasing version. + */ +static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list) +{ + struct rb_node **p = &list->rb_node; + struct rb_node * parent = NULL; + struct jffs2_tmp_dnode_info *this; + + while (*p) { + parent = *p; + this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb); + + /* There may actually be a collision here, but it doesn't + actually matter. As long as the two nodes with the same + version are together, it's all fine. */ + if (tn->version < this->version) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&tn->rb, parent, p); + rb_insert_color(&tn->rb, list); +} + +static void jffs2_free_tmp_dnode_info_list(struct rb_root *list) +{ + struct rb_node *this; + struct jffs2_tmp_dnode_info *tn; + + this = list->rb_node; + + /* Now at bottom of tree */ + while (this) { + if (this->rb_left) + this = this->rb_left; + else if (this->rb_right) + this = this->rb_right; + else { + tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb); + jffs2_free_full_dnode(tn->fn); + jffs2_free_tmp_dnode_info(tn); + + this = this->rb_parent; + if (!this) + break; + + if (this->rb_left == &tn->rb) + this->rb_left = NULL; + else if (this->rb_right == &tn->rb) + this->rb_right = NULL; + else BUG(); + } + } + list->rb_node = NULL; +} + +static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) +{ + struct jffs2_full_dirent *next; + + while (fd) { + next = fd->next; + jffs2_free_full_dirent(fd); + fd = next; + } +} + +/* Returns first valid node after 'ref'. May return 'ref' */ +static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref) +{ + while (ref && ref->next_in_ino) { + if (!ref_obsolete(ref)) + return ref; + JFFS2_DBG_NODEREF("node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)); + ref = ref->next_in_ino; + } + return NULL; +} + +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an directory entry node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int +read_direntry(struct jffs2_sb_info *c, + struct jffs2_raw_node_ref *ref, + struct jffs2_raw_dirent *rd, + uint32_t read, + struct jffs2_full_dirent **fdp, + int32_t *latest_mctime, + uint32_t *mctime_ver) +{ + struct jffs2_full_dirent *fd; + + /* The direntry nodes are checked during the flash scanning */ + BUG_ON(ref_flags(ref) == REF_UNCHECKED); + /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ + BUG_ON(ref_obsolete(ref)); + + /* Sanity check */ + if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { + JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", + ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); + return 1; + } + + fd = jffs2_alloc_full_dirent(rd->nsize + 1); + if (unlikely(!fd)) + return -ENOMEM; + + fd->raw = ref; + fd->version = je32_to_cpu(rd->version); + fd->ino = je32_to_cpu(rd->ino); + fd->type = rd->type; + + /* Pick out the mctime of the latest dirent */ + if(fd->version > *mctime_ver) { + *mctime_ver = fd->version; + *latest_mctime = je32_to_cpu(rd->mctime); + } + + /* + * Copy as much of the name as possible from the raw + * dirent we've already read from the flash. + */ + if (read > sizeof(*rd)) + memcpy(&fd->name[0], &rd->name[0], + min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) )); + + /* Do we need to copy any more of the name directly from the flash? */ + if (rd->nsize + sizeof(*rd) > read) { + /* FIXME: point() */ + int err; + int already = read - sizeof(*rd); + + err = jffs2_flash_read(c, (ref_offset(ref)) + read, + rd->nsize - already, &read, &fd->name[already]); + if (unlikely(read != rd->nsize - already) && likely(!err)) + return -EIO; + + if (unlikely(err)) { + JFFS2_ERROR("read remainder of name: error %d\n", err); + jffs2_free_full_dirent(fd); + return -EIO; + } + } + + fd->nhash = full_name_hash(fd->name, rd->nsize); + fd->next = NULL; + fd->name[rd->nsize] = '\0'; + + /* + * Wheee. We now have a complete jffs2_full_dirent structure, with + * the name in it and everything. Link it into the list + */ + jffs2_add_fd_to_list(c, fd, fdp); + + return 0; +} + +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an inode node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int +read_dnode(struct jffs2_sb_info *c, + struct jffs2_raw_node_ref *ref, + struct jffs2_raw_inode *rd, + uint32_t read, + struct rb_root *tnp, + int32_t *latest_mctime, + uint32_t *mctime_ver) +{ + struct jffs2_eraseblock *jeb; + struct jffs2_tmp_dnode_info *tn; + + /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ + BUG_ON(ref_obsolete(ref)); + + /* If we've never checked the CRCs on this node, check them now */ + if (ref_flags(ref) == REF_UNCHECKED) { + uint32_t crc, len; + + crc = crc32(0, rd, sizeof(*rd) - 8); + if (unlikely(crc != je32_to_cpu(rd->node_crc))) { + JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->node_crc), crc); + return 1; + } + + /* Sanity checks */ + if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) || + unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) { + JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref)); + jffs2_dbg_dump_node(c, ref_offset(ref)); + return 1; + } + + if (rd->compr != JFFS2_COMPR_ZERO && je32_to_cpu(rd->csize)) { + unsigned char *buf = NULL; + uint32_t pointed = 0; + int err; +#ifndef __ECOS + if (c->mtd->point) { + err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize), + &read, &buf); + if (unlikely(read < je32_to_cpu(rd->csize)) && likely(!err)) { + JFFS2_ERROR("MTD point returned len too short: 0x%zx\n", read); + c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd), + je32_to_cpu(rd->csize)); + } else if (unlikely(err)){ + JFFS2_ERROR("MTD point failed %d\n", err); + } else + pointed = 1; /* succefully pointed to device */ + } +#endif + if(!pointed){ + buf = kmalloc(je32_to_cpu(rd->csize), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = jffs2_flash_read(c, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize), + &read, buf); + if (unlikely(read != je32_to_cpu(rd->csize)) && likely(!err)) + err = -EIO; + if (err) { + kfree(buf); + return err; + } + } + crc = crc32(0, buf, je32_to_cpu(rd->csize)); + if(!pointed) + kfree(buf); +#ifndef __ECOS + else + c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize)); +#endif + + if (crc != je32_to_cpu(rd->data_crc)) { + JFFS2_NOTICE("data CRC failed on node at %#08x: read %#08x, calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->data_crc), crc); + return 1; + } + + } + + /* Mark the node as having been checked and fix the accounting accordingly */ + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + len = ref_totlen(c, jeb, ref); + + spin_lock(&c->erase_completion_lock); + jeb->used_size += len; + jeb->unchecked_size -= len; + c->used_size += len; + c->unchecked_size -= len; + + /* If node covers at least a whole page, or if it starts at the + beginning of a page and runs to the end of the file, or if + it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. + + If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) + when the overlapping node(s) get added to the tree anyway. + */ + if ((je32_to_cpu(rd->dsize) >= PAGE_CACHE_SIZE) || + ( ((je32_to_cpu(rd->offset) & (PAGE_CACHE_SIZE-1))==0) && + (je32_to_cpu(rd->dsize) + je32_to_cpu(rd->offset) == je32_to_cpu(rd->isize)))) { + JFFS2_DBG_READINODE("marking node at %#08x REF_PRISTINE\n", ref_offset(ref)); + ref->flash_offset = ref_offset(ref) | REF_PRISTINE; + } else { + JFFS2_DBG_READINODE("marking node at %#08x REF_NORMAL\n", ref_offset(ref)); + ref->flash_offset = ref_offset(ref) | REF_NORMAL; + } + spin_unlock(&c->erase_completion_lock); + } + + tn = jffs2_alloc_tmp_dnode_info(); + if (!tn) { + JFFS2_ERROR("alloc tn failed\n"); + return -ENOMEM; + } + + tn->fn = jffs2_alloc_full_dnode(); + if (!tn->fn) { + JFFS2_ERROR("alloc fn failed\n"); + jffs2_free_tmp_dnode_info(tn); + return -ENOMEM; + } + + tn->version = je32_to_cpu(rd->version); + tn->fn->ofs = je32_to_cpu(rd->offset); + tn->fn->raw = ref; + + /* There was a bug where we wrote hole nodes out with + csize/dsize swapped. Deal with it */ + if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && je32_to_cpu(rd->csize)) + tn->fn->size = je32_to_cpu(rd->csize); + else // normal case... + tn->fn->size = je32_to_cpu(rd->dsize); + + JFFS2_DBG_READINODE("dnode @%08x: ver %u, offset %#04x, dsize %#04x\n", + ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize)); + + jffs2_add_tn_to_tree(tn, tnp); + + return 0; +} + +/* + * Helper function for jffs2_get_inode_nodes(). + * It is called every time an unknown node is found. + * + * Returns: 0 on succes; + * 1 if the node should be marked obsolete; + * negative error code on failure. + */ +static inline int +read_unknown(struct jffs2_sb_info *c, + struct jffs2_raw_node_ref *ref, + struct jffs2_unknown_node *un, + uint32_t read) +{ + /* We don't mark unknown nodes as REF_UNCHECKED */ + BUG_ON(ref_flags(ref) == REF_UNCHECKED); + + un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); + + if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) { + /* Hmmm. This should have been caught at scan time. */ + JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref)); + jffs2_dbg_dump_node(c, ref_offset(ref)); + return 1; + } else { + switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { + + case JFFS2_FEATURE_INCOMPAT: + JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + /* EEP */ + BUG(); + break; + + case JFFS2_FEATURE_ROCOMPAT: + JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); + break; + + case JFFS2_FEATURE_RWCOMPAT_COPY: + JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + break; + + case JFFS2_FEATURE_RWCOMPAT_DELETE: + JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + return 1; + } + } + + return 0; +} + +/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated + with this ino, returning the former in order of version */ + +static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct rb_root *tnp, struct jffs2_full_dirent **fdp, + uint32_t *highest_version, uint32_t *latest_mctime, + uint32_t *mctime_ver) +{ + struct jffs2_raw_node_ref *ref, *valid_ref; + struct rb_root ret_tn = RB_ROOT; + struct jffs2_full_dirent *ret_fd = NULL; + union jffs2_node_union node; + size_t retlen; + int err; + + *mctime_ver = 0; + + JFFS2_DBG_READINODE("ino #%u\n", f->inocache->ino); + + spin_lock(&c->erase_completion_lock); + + valid_ref = jffs2_first_valid_node(f->inocache->nodes); + + if (!valid_ref && (f->inocache->ino != 1)) + JFFS2_WARNING("no valid nodes for ino #%u\n", f->inocache->ino); + + while (valid_ref) { + /* We can hold a pointer to a non-obsolete node without the spinlock, + but _obsolete_ nodes may disappear at any time, if the block + they're in gets erased. So if we mark 'ref' obsolete while we're + not holding the lock, it can go away immediately. For that reason, + we find the next valid node first, before processing 'ref'. + */ + ref = valid_ref; + valid_ref = jffs2_first_valid_node(ref->next_in_ino); + spin_unlock(&c->erase_completion_lock); + + cond_resched(); + + /* FIXME: point() */ + err = jffs2_flash_read(c, (ref_offset(ref)), + min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)), + &retlen, (void *)&node); + if (err) { + JFFS2_ERROR("error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref)); + goto free_out; + } + + switch (je16_to_cpu(node.u.nodetype)) { + + case JFFS2_NODETYPE_DIRENT: + JFFS2_DBG_READINODE("node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)); + + if (retlen < sizeof(node.d)) { + JFFS2_ERROR("short read dirent at %#08x\n", ref_offset(ref)); + err = -EIO; + goto free_out; + } + + err = read_direntry(c, ref, &node.d, retlen, &ret_fd, latest_mctime, mctime_ver); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); + break; + } else if (unlikely(err)) + goto free_out; + + if (je32_to_cpu(node.d.version) > *highest_version) + *highest_version = je32_to_cpu(node.d.version); + + break; + + case JFFS2_NODETYPE_INODE: + JFFS2_DBG_READINODE("node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)); + + if (retlen < sizeof(node.i)) { + JFFS2_ERROR("short read dnode at %#08x\n", ref_offset(ref)); + err = -EIO; + goto free_out; + } + + err = read_dnode(c, ref, &node.i, retlen, &ret_tn, latest_mctime, mctime_ver); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); + break; + } else if (unlikely(err)) + goto free_out; + + if (je32_to_cpu(node.i.version) > *highest_version) + *highest_version = je32_to_cpu(node.i.version); + + JFFS2_DBG_READINODE("version %d, highest_version now %d\n", + je32_to_cpu(node.i.version), *highest_version); + + break; + + default: + /* Check we've managed to read at least the common node header */ + if (retlen < sizeof(struct jffs2_unknown_node)) { + JFFS2_ERROR("short read unknown node at %#08x\n", ref_offset(ref)); + return -EIO; + } + + err = read_unknown(c, ref, &node.u, retlen); + if (err == 1) { + jffs2_mark_node_obsolete(c, ref); + break; + } else if (unlikely(err)) + goto free_out; + + } + spin_lock(&c->erase_completion_lock); + + } + spin_unlock(&c->erase_completion_lock); + *tnp = ret_tn; + *fdp = ret_fd; + + return 0; + + free_out: + jffs2_free_tmp_dnode_info_list(&ret_tn); + jffs2_free_full_dirent_list(ret_fd); + return err; +} static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, struct jffs2_inode_info *f, - struct jffs2_raw_inode *latest_node); + struct jffs2_raw_inode *latest_node) +{ + struct jffs2_tmp_dnode_info *tn = NULL; + struct rb_root tn_list; + struct rb_node *rb, *repl_rb; + struct jffs2_full_dirent *fd_list; + struct jffs2_full_dnode *fn = NULL; + uint32_t crc; + uint32_t latest_mctime, mctime_ver; + uint32_t mdata_ver = 0; + size_t retlen; + int ret; + + JFFS2_DBG_READINODE("ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink); + + /* Grab all nodes relevant to this ino */ + ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); + + if (ret) { + JFFS2_ERROR("cannot read nodes for ino %u, returned error is %d\n", f->inocache->ino, ret); + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); + return ret; + } + f->dents = fd_list; + + rb = rb_first(&tn_list); + + while (rb) { + tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb); + fn = tn->fn; + + if (f->metadata) { + if (likely(tn->version >= mdata_ver)) { + JFFS2_DBG_READINODE("obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)); + jffs2_mark_node_obsolete(c, f->metadata->raw); + jffs2_free_full_dnode(f->metadata); + f->metadata = NULL; + + mdata_ver = 0; + } else { + /* This should never happen. */ + JFFS2_ERROR("Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n", + ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw)); + jffs2_mark_node_obsolete(c, fn->raw); + jffs2_free_full_dnode(fn); + /* Fill in latest_node from the metadata, not this one we're about to free... */ + fn = f->metadata; + goto next_tn; + } + } + + if (fn->size) { + jffs2_add_full_dnode_to_inode(c, f, fn); + } else { + /* Zero-sized node at end of version list. Just a metadata update */ + JFFS2_DBG_READINODE("metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version); + f->metadata = fn; + mdata_ver = tn->version; + } + next_tn: + BUG_ON(rb->rb_left); + if (rb->rb_parent && rb->rb_parent->rb_left == rb) { + /* We were then left-hand child of our parent. We need + to move our own right-hand child into our place. */ + repl_rb = rb->rb_right; + if (repl_rb) + repl_rb->rb_parent = rb->rb_parent; + } else + repl_rb = NULL; + + rb = rb_next(rb); + + /* Remove the spent tn from the tree; don't bother rebalancing + but put our right-hand child in our own place. */ + if (tn->rb.rb_parent) { + if (tn->rb.rb_parent->rb_left == &tn->rb) + tn->rb.rb_parent->rb_left = repl_rb; + else if (tn->rb.rb_parent->rb_right == &tn->rb) + tn->rb.rb_parent->rb_right = repl_rb; + else BUG(); + } else if (tn->rb.rb_right) + tn->rb.rb_right->rb_parent = NULL; + + jffs2_free_tmp_dnode_info(tn); + } + jffs2_dbg_fragtree_paranoia_check_nolock(f); + + if (!fn) { + /* No data nodes for this inode. */ + if (f->inocache->ino != 1) { + JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino); + if (!fd_list) { + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); + return -EIO; + } + JFFS2_NOTICE("but it has children so we fake some modes for it\n"); + } + latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); + latest_node->version = cpu_to_je32(0); + latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0); + latest_node->isize = cpu_to_je32(0); + latest_node->gid = cpu_to_je16(0); + latest_node->uid = cpu_to_je16(0); + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); + return 0; + } + ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); + if (ret || retlen != sizeof(*latest_node)) { + JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", + ret, retlen, sizeof(*latest_node)); + /* FIXME: If this fails, there seems to be a memory leak. Find it. */ + up(&f->sem); + jffs2_do_clear_inode(c, f); + return ret?ret:-EIO; + } + + crc = crc32(0, latest_node, sizeof(*latest_node)-8); + if (crc != je32_to_cpu(latest_node->node_crc)) { + JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", + f->inocache->ino, ref_offset(fn->raw)); + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -EIO; + } + + switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { + case S_IFDIR: + if (mctime_ver > je32_to_cpu(latest_node->version)) { + /* The times in the latest_node are actually older than + mctime in the latest dirent. Cheat. */ + latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); + } + break; + + + case S_IFREG: + /* If it was a regular file, truncate it to the latest node's isize */ + jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize)); + break; + + case S_IFLNK: + /* Hack to work around broken isize in old symlink code. + Remove this when dwmw2 comes to his senses and stops + symlinks from being an entirely gratuitous special + case. */ + if (!je32_to_cpu(latest_node->isize)) + latest_node->isize = latest_node->dsize; + + if (f->inocache->state != INO_STATE_CHECKING) { + /* Symlink's inode data is the target path. Read it and + * keep in RAM to facilitate quick follow symlink + * operation. */ + f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); + if (!f->target) { + JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -ENOMEM; + } + + ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node), + je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); + + if (ret || retlen != je32_to_cpu(latest_node->csize)) { + if (retlen != je32_to_cpu(latest_node->csize)) + ret = -EIO; + kfree(f->target); + f->target = NULL; + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -ret; + } + + f->target[je32_to_cpu(latest_node->csize)] = '\0'; + JFFS2_DBG_READINODE("symlink's target '%s' cached\n", f->target); + } + + /* fall through... */ + + case S_IFBLK: + case S_IFCHR: + /* Certain inode types should have only one data node, and it's + kept as the metadata node */ + if (f->metadata) { + JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n", + f->inocache->ino, jemode_to_cpu(latest_node->mode)); + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -EIO; + } + if (!frag_first(&f->fragtree)) { + JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n", + f->inocache->ino, jemode_to_cpu(latest_node->mode)); + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -EIO; + } + /* ASSERT: f->fraglist != NULL */ + if (frag_next(frag_first(&f->fragtree))) { + JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n", + f->inocache->ino, jemode_to_cpu(latest_node->mode)); + /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ + up(&f->sem); + jffs2_do_clear_inode(c, f); + return -EIO; + } + /* OK. We're happy */ + f->metadata = frag_first(&f->fragtree)->node; + jffs2_free_node_frag(frag_first(&f->fragtree)); + f->fragtree = RB_ROOT; + break; + } + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); + + return 0; +} + +/* Scan the list of all nodes present for this ino, build map of versions, etc. */ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t ino, struct jffs2_raw_inode *latest_node) { - D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n")); + JFFS2_DBG_READINODE("read inode #%u\n", ino); retry_inocache: spin_lock(&c->inocache_lock); f->inocache = jffs2_get_ino_cache(c, ino); - D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache)); - if (f->inocache) { /* Check its state. We may need to wait before we can use it */ switch(f->inocache->state) { @@ -431,8 +790,7 @@ int jffs2_do_read_inode(struct jffs2_sb_ /* If it's in either of these states, we need to wait for whoever's got it to finish and put it back. */ - D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n", - ino, f->inocache->state)); + JFFS2_DBG_READINODE("waiting for ino #%u in state %d\n", ino, f->inocache->state); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); goto retry_inocache; @@ -441,7 +799,7 @@ int jffs2_do_read_inode(struct jffs2_sb_ /* Eep. This should never happen. It can happen if Linux calls read_inode() again before clear_inode() has finished though. */ - printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state); + JFFS2_ERROR("Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state); /* Fail. That's probably better than allowing it to succeed */ f->inocache = NULL; break; @@ -456,10 +814,10 @@ int jffs2_do_read_inode(struct jffs2_sb_ /* Special case - no root inode on medium */ f->inocache = jffs2_alloc_inode_cache(); if (!f->inocache) { - printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n"); + JFFS2_ERROR("cannot allocate inocache for root inode\n"); return -ENOMEM; } - D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n")); + JFFS2_DBG_READINODE("creating inocache for root inode\n"); memset(f->inocache, 0, sizeof(struct jffs2_inode_cache)); f->inocache->ino = f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; @@ -467,7 +825,7 @@ int jffs2_do_read_inode(struct jffs2_sb_ jffs2_add_ino_cache(c, f->inocache); } if (!f->inocache) { - printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino); + JFFS2_ERROR("requestied to read an nonexistent ino %u\n", ino); return -ENOENT; } @@ -496,174 +854,6 @@ int jffs2_do_crccheck_inode(struct jffs2 return ret; } -static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, - struct jffs2_inode_info *f, - struct jffs2_raw_inode *latest_node) -{ - struct jffs2_tmp_dnode_info *tn_list, *tn; - struct jffs2_full_dirent *fd_list; - struct jffs2_full_dnode *fn = NULL; - uint32_t crc; - uint32_t latest_mctime, mctime_ver; - uint32_t mdata_ver = 0; - size_t retlen; - int ret; - - D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink)); - - /* Grab all nodes relevant to this ino */ - ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); - - if (ret) { - printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret); - if (f->inocache->state == INO_STATE_READING) - jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); - return ret; - } - f->dents = fd_list; - - while (tn_list) { - tn = tn_list; - - fn = tn->fn; - - if (f->metadata) { - if (likely(tn->version >= mdata_ver)) { - D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw))); - jffs2_mark_node_obsolete(c, f->metadata->raw); - jffs2_free_full_dnode(f->metadata); - f->metadata = NULL; - - mdata_ver = 0; - } else { - /* This should never happen. */ - printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n", - ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw)); - jffs2_mark_node_obsolete(c, fn->raw); - jffs2_free_full_dnode(fn); - /* Fill in latest_node from the metadata, not this one we're about to free... */ - fn = f->metadata; - goto next_tn; - } - } - - if (fn->size) { - jffs2_add_full_dnode_to_inode(c, f, fn); - } else { - /* Zero-sized node at end of version list. Just a metadata update */ - D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version)); - f->metadata = fn; - mdata_ver = tn->version; - } - next_tn: - tn_list = tn->next; - jffs2_free_tmp_dnode_info(tn); - } - D1(jffs2_sanitycheck_fragtree(f)); - - if (!fn) { - /* No data nodes for this inode. */ - if (f->inocache->ino != 1) { - printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino); - if (!fd_list) { - if (f->inocache->state == INO_STATE_READING) - jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); - return -EIO; - } - printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n"); - } - latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); - latest_node->version = cpu_to_je32(0); - latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0); - latest_node->isize = cpu_to_je32(0); - latest_node->gid = cpu_to_je16(0); - latest_node->uid = cpu_to_je16(0); - if (f->inocache->state == INO_STATE_READING) - jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); - return 0; - } - - ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); - if (ret || retlen != sizeof(*latest_node)) { - printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n", - ret, retlen, sizeof(*latest_node)); - /* FIXME: If this fails, there seems to be a memory leak. Find it. */ - up(&f->sem); - jffs2_do_clear_inode(c, f); - return ret?ret:-EIO; - } - - crc = crc32(0, latest_node, sizeof(*latest_node)-8); - if (crc != je32_to_cpu(latest_node->node_crc)) { - printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw)); - up(&f->sem); - jffs2_do_clear_inode(c, f); - return -EIO; - } - - switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { - case S_IFDIR: - if (mctime_ver > je32_to_cpu(latest_node->version)) { - /* The times in the latest_node are actually older than - mctime in the latest dirent. Cheat. */ - latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); - } - break; - - - case S_IFREG: - /* If it was a regular file, truncate it to the latest node's isize */ - jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize)); - break; - - case S_IFLNK: - /* Hack to work around broken isize in old symlink code. - Remove this when dwmw2 comes to his senses and stops - symlinks from being an entirely gratuitous special - case. */ - if (!je32_to_cpu(latest_node->isize)) - latest_node->isize = latest_node->dsize; - /* fall through... */ - - case S_IFBLK: - case S_IFCHR: - /* Certain inode types should have only one data node, and it's - kept as the metadata node */ - if (f->metadata) { - printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", - f->inocache->ino, jemode_to_cpu(latest_node->mode)); - up(&f->sem); - jffs2_do_clear_inode(c, f); - return -EIO; - } - if (!frag_first(&f->fragtree)) { - printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", - f->inocache->ino, jemode_to_cpu(latest_node->mode)); - up(&f->sem); - jffs2_do_clear_inode(c, f); - return -EIO; - } - /* ASSERT: f->fraglist != NULL */ - if (frag_next(frag_first(&f->fragtree))) { - printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n", - f->inocache->ino, jemode_to_cpu(latest_node->mode)); - /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ - up(&f->sem); - jffs2_do_clear_inode(c, f); - return -EIO; - } - /* OK. We're happy */ - f->metadata = frag_first(&f->fragtree)->node; - jffs2_free_node_frag(frag_first(&f->fragtree)); - f->fragtree = RB_ROOT; - break; - } - if (f->inocache->state == INO_STATE_READING) - jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); - - return 0; -} - void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { struct jffs2_full_dirent *fd, *fds; @@ -672,6 +862,9 @@ void jffs2_do_clear_inode(struct jffs2_s down(&f->sem); deleted = f->inocache && !f->inocache->nlink; + if (f->inocache && f->inocache->state != INO_STATE_CHECKING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING); + if (f->metadata) { if (deleted) jffs2_mark_node_obsolete(c, f->metadata->raw); @@ -680,16 +873,23 @@ void jffs2_do_clear_inode(struct jffs2_s jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); + if (f->target) { + kfree(f->target); + f->target = NULL; + } + fds = f->dents; - while(fds) { fd = fds; fds = fd->next; jffs2_free_full_dirent(fd); } - if (f->inocache && f->inocache->state != INO_STATE_CHECKING) + if (f->inocache && f->inocache->state != INO_STATE_CHECKING) { jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); + if (f->inocache->nodes == (void *)f->inocache) + jffs2_del_ino_cache(c, f->inocache); + } up(&f->sem); } diff --git a/packages/fs/jffs2/current/src/scan.c b/packages/fs/jffs2/current/src/scan.c --- a/packages/fs/jffs2/current/src/scan.c +++ b/packages/fs/jffs2/current/src/scan.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.115 2004/11/17 12:59:08 dedekind Exp $ + * $Id: scan.c,v 1.121 2005/07/20 15:32:28 dedekind Exp $ * */ #include @@ -19,7 +19,7 @@ #include #include "nodelist.h" -#define EMPTY_SCAN_SIZE 1024 +#define DEFAULT_EMPTY_SCAN_SIZE 1024 #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ c->free_size -= _x; c->dirty_size += _x; \ @@ -68,13 +68,21 @@ static int jffs2_scan_dirent_node(struct static inline int min_free(struct jffs2_sb_info *c) { uint32_t min = 2 * sizeof(struct jffs2_raw_inode); -#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) return c->wbuf_pagesize; #endif return min; } + +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; +} + int jffs2_scan_medium(struct jffs2_sb_info *c) { int i, ret; @@ -122,7 +130,7 @@ int jffs2_scan_medium(struct jffs2_sb_in if (ret < 0) goto out; - ACCT_PARANOIA_CHECK(jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); /* Now decide which list to put it on */ switch(ret) { @@ -220,7 +228,7 @@ int jffs2_scan_medium(struct jffs2_sb_in c->dirty_size -= c->nextblock->dirty_size; c->nextblock->dirty_size = 0; } -#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { /* If we're going to start writing into a block which already contains data, and the end of the data isn't page-aligned, @@ -286,7 +294,7 @@ static int jffs2_scan_eraseblock (struct uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER int cleanmarkerfound = 0; #endif @@ -295,7 +303,7 @@ static int jffs2_scan_eraseblock (struct D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); -#ifdef CONFIG_JFFS2_FS_NAND +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { int ret = jffs2_check_nand_cleanmarker(c, jeb); D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); @@ -316,7 +324,7 @@ static int jffs2_scan_eraseblock (struct if (!buf_size) { buf_len = c->sector_size; } else { - buf_len = EMPTY_SCAN_SIZE; + buf_len = EMPTY_SCAN_SIZE(c->sector_size); err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); if (err) return err; @@ -326,11 +334,11 @@ static int jffs2_scan_eraseblock (struct ofs = 0; /* Scan only 4KiB of 0xFF before declaring it's empty */ - while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) + while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ofs += 4; - if (ofs == EMPTY_SCAN_SIZE) { -#ifdef CONFIG_JFFS2_FS_NAND + if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) { +#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); @@ -343,7 +351,10 @@ static int jffs2_scan_eraseblock (struct } #endif D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); - return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ + 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) { D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, @@ -359,7 +370,7 @@ static int jffs2_scan_eraseblock (struct scan_more: while(ofs < jeb->offset + c->sector_size) { - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); cond_resched(); @@ -422,8 +433,8 @@ scan_more: /* If we're only checking the beginning of a block with a cleanmarker, bail now */ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && - c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) { - D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE)); + c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) { + D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size))); return BLK_STATE_CLEANMARKER; } @@ -618,7 +629,7 @@ scan_more: } if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size - && (!jeb->first_node || !jeb->first_node->next_in_ino) ) + && (!jeb->first_node || !jeb->first_node->next_phys) ) return BLK_STATE_CLEANMARKER; /* move blocks with max 4 byte dirty space to cleanlist */ diff --git a/packages/fs/jffs2/current/src/write.c b/packages/fs/jffs2/current/src/write.c --- a/packages/fs/jffs2/current/src/write.c +++ b/packages/fs/jffs2/current/src/write.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.87 2004/11/16 20:36:12 dwmw2 Exp $ + * $Id: write.c,v 1.94 2005/07/20 15:50:51 dedekind Exp $ * */ @@ -35,14 +35,13 @@ int jffs2_do_new_inode(struct jffs2_sb_i f->inocache = ic; f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; - f->inocache->ino = ++c->highest_ino; f->inocache->state = INO_STATE_PRESENT; + + jffs2_add_ino_cache(c, f->inocache); + D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); ri->ino = cpu_to_je32(f->inocache->ino); - D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); - jffs2_add_ino_cache(c, f->inocache); - ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(PAD(sizeof(*ri))); @@ -55,34 +54,6 @@ int jffs2_do_new_inode(struct jffs2_sb_i return 0; } -#if CONFIG_JFFS2_FS_DEBUG > 0 -static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) -{ - unsigned char buf[16]; - size_t retlen; - int ret, i; - - ret = jffs2_flash_read(c, ofs, 16, &retlen, buf); - if (ret || (retlen != 16)) { - D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen)); - return; - } - ret = 0; - for (i=0; i<16; i++) { - if (buf[i] != 0xff) - ret = 1; - } - if (ret) { - printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs); - printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - ofs, - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - } -} -#endif - - /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, write it to the flash, link it into the existing inode/fragment list */ @@ -107,7 +78,7 @@ struct jffs2_full_dnode *jffs2_write_dno vecs[1].iov_base = (unsigned char *)data; vecs[1].iov_len = datalen; - D1(writecheck(c, flash_ofs)); + jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) { printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen); @@ -136,6 +107,15 @@ struct jffs2_full_dnode *jffs2_write_dno raw->__totlen = PAD(sizeof(*ri)+datalen); raw->next_phys = NULL; + if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) { + BUG_ON(!retried); + D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, " + "highest version %d -> updating dnode\n", + je32_to_cpu(ri->version), f->highest_version)); + ri->version = cpu_to_je32(++f->highest_version); + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + } + ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:f->inocache->ino); @@ -169,8 +149,8 @@ struct jffs2_full_dnode *jffs2_write_dno D1(printk(KERN_DEBUG "Retrying failed write.\n")); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy); @@ -186,8 +166,8 @@ struct jffs2_full_dnode *jffs2_write_dno if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } @@ -224,7 +204,7 @@ struct jffs2_full_dnode *jffs2_write_dno je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); if (retried) { - ACCT_SANITY_CHECK(c,NULL); + jffs2_dbg_acct_sanity_check(c,NULL); } return fn; @@ -242,7 +222,6 @@ struct jffs2_full_dirent *jffs2_write_di D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino), je32_to_cpu(rd->name_crc))); - D1(writecheck(c, flash_ofs)); D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n"); @@ -255,6 +234,8 @@ struct jffs2_full_dirent *jffs2_write_di vecs[1].iov_base = (unsigned char *)name; vecs[1].iov_len = namelen; + jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); + raw = jffs2_alloc_raw_node_ref(); if (!raw) @@ -280,6 +261,16 @@ struct jffs2_full_dirent *jffs2_write_di raw->__totlen = PAD(sizeof(*rd)+namelen); raw->next_phys = NULL; + if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) { + BUG_ON(!retried); + D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, " + "highest version %d -> updating dirent\n", + je32_to_cpu(rd->version), f->highest_version)); + rd->version = cpu_to_je32(++f->highest_version); + fd->version = je32_to_cpu(rd->version); + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + } + ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); if (ret || (retlen != sizeof(*rd) + namelen)) { @@ -304,8 +295,8 @@ struct jffs2_full_dirent *jffs2_write_di D1(printk(KERN_DEBUG "Retrying failed write.\n")); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy); @@ -320,8 +311,8 @@ struct jffs2_full_dirent *jffs2_write_di if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); - ACCT_SANITY_CHECK(c,jeb); - D1(ACCT_PARANOIA_CHECK(jeb)); + jffs2_dbg_acct_sanity_check(c,jeb); + jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); @@ -341,7 +332,7 @@ struct jffs2_full_dirent *jffs2_write_di spin_unlock(&c->erase_completion_lock); if (retried) { - ACCT_SANITY_CHECK(c,NULL); + jffs2_dbg_acct_sanity_check(c,NULL); } return fd; @@ -625,20 +616,23 @@ int jffs2_do_unlink(struct jffs2_sb_info down(&dead_f->sem); - while (dead_f->dents) { - /* There can be only deleted ones */ - fd = dead_f->dents; - - dead_f->dents = fd->next; - - if (fd->ino) { - printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", - dead_f->inocache->ino, fd->name, fd->ino); - } else { - D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino)); + if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { + while (dead_f->dents) { + /* There can be only deleted ones */ + fd = dead_f->dents; + + dead_f->dents = fd->next; + + if (fd->ino) { + printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", + dead_f->inocache->ino, fd->name, fd->ino); + } else { + D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", + fd->name, dead_f->inocache->ino)); + } + jffs2_mark_node_obsolete(c, fd->raw); + jffs2_free_full_dirent(fd); } - jffs2_mark_node_obsolete(c, fd->raw); - jffs2_free_full_dirent(fd); } dead_f->inocache->nlink--; diff --git a/packages/fs/jffs2/current/tests/jffs2_3.c b/packages/fs/jffs2/current/tests/jffs2_3.c --- a/packages/fs/jffs2/current/tests/jffs2_3.c +++ b/packages/fs/jffs2/current/tests/jffs2_3.c @@ -68,7 +68,7 @@ #define NELEM(_x_) (sizeof(_x_)/sizeof(*(_x_))) #define SHOW_RESULT( _fn, _res ) \ -diag_printf("FAIL: " #_fn "() returned %ld %s\n", _res, \ +diag_printf("FAIL: " #_fn "() returned %d %s\n", _res, \ (unsigned long) _res<0?strerror(errno):""); //========================================================================== @@ -91,7 +91,7 @@ static void create_file(int i) } buffer[0] = 0; - buffer[0] = cyg_posix_crc32((char *)buffer, sizeof(buffer)); + buffer[0] = cyg_posix_crc32((unsigned char *)buffer, sizeof(buffer)); err = write(fd, buffer, sizeof(buffer)); if (err == -1) SHOW_RESULT( write, err ); @@ -129,7 +129,7 @@ static void check_file(int i) crc = buffer[0]; buffer[0] = 0; - if (crc != cyg_posix_crc32((char *)buffer, sizeof(buffer))) { + if (crc != cyg_posix_crc32((unsigned char *)buffer, sizeof(buffer))) { CYG_TEST_FAIL("File corrupt"); } @@ -144,7 +144,6 @@ static void check_file(int i) int main( int argc, char **argv ) { int err, iteration; - cyg_tick_count_t ticks; struct mallinfo minfo ; CYG_TEST_INIT();