Index: fs/jffs2/current/ChangeLog =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/ChangeLog,v retrieving revision 1.44 diff -u -r1.44 ChangeLog --- fs/jffs2/current/ChangeLog 22 Jan 2005 15:06:12 -0000 1.44 +++ fs/jffs2/current/ChangeLog 22 Jan 2005 17:12:53 -0000 @@ -1,3 +1,7 @@ +2005-01-22 Andrew Lunn + + * Merge from public MTD. + 2004-11-14 Per Hedblom Andrew Lunn Index: fs/jffs2/current/cdl/jffs2.cdl =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/cdl/jffs2.cdl,v retrieving revision 1.21 diff -u -r1.21 jffs2.cdl --- fs/jffs2/current/cdl/jffs2.cdl 22 Jan 2005 14:05:31 -0000 1.21 +++ fs/jffs2/current/cdl/jffs2.cdl 22 Jan 2005 17:12:53 -0000 @@ -4,7 +4,7 @@ # # JFFS2 Filesystem configuration data # -# $Id: jffs2.cdl,v 1.16 2004/11/11 20:48:54 lunn Exp $ +# $Id: jffs2.cdl,v 1.18 2005/01/22 16:01:12 lunn Exp $ # # ==================================================================== #####ECOSGPLCOPYRIGHTBEGIN#### Index: fs/jffs2/current/doc/README.Locking =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/doc/README.Locking,v retrieving revision 1.1 diff -u -r1.1 README.Locking --- fs/jffs2/current/doc/README.Locking 11 Dec 2003 23:38:20 -0000 1.1 +++ fs/jffs2/current/doc/README.Locking 22 Jan 2005 17:12:53 -0000 @@ -1,4 +1,4 @@ - $Id: README.Locking,v 1.6 2003/11/02 19:27:54 dwmw2 Exp $ + $Id: README.Locking,v 1.11 2004/11/24 09:25:09 dedekind Exp $ JFFS2 LOCKING DOCUMENTATION --------------------------- @@ -99,18 +99,53 @@ GC thread locks it, sends the signal, then unlocks it - while the GC thread itself locks it, zeroes c->gc_task, then unlocks on the exit path. + + inocache_lock spinlock + ---------------------- + +This spinlock protects the hashed list (c->inocache_list) of the +in-core jffs2_inode_cache objects (each inode in JFFS2 has the +correspondent jffs2_inode_cache object). So, the inocache_lock +has to be locked while walking the c->inocache_list hash buckets. + +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. + +Ordering constraints: + + c->erase_completion_lock and c->inocache_lock has special ordering: + 1. c->erase_completion_lock (must be locked first) + 2. c->inocache_lock + + erase_free_sem -------------- -This semaphore is only used by the erase code which frees obsolete +This semaphore is used by the erase code which frees obsolete node references and the jffs2_garbage_collect_deletion_dirent() function. The latter function on NAND flash must read _obsolete_ nodes to determine whether the 'deletion dirent' under consideration can be discarded or whether it is still required to show that an inode has been unlinked. Because reading from the flash may sleep, the -erase_completion_lock cannot be held, so an alternative, more +erase_completion_lock can not be held, so an alternative, more heavyweight lock was required to prevent the erase code from freeing the jffs2_raw_node_ref structures in question while the garbage collection code is looking at them. -Suggestions for alternative solutions to this problem would be welcomed. +The erase_free_sem mutex is also used in the jffs2_mark_node_obsolete() +function which manipulates obsolete nodes (which may be removed +from the list and freed any time) and may sleep (since it reads flash). + + + wbuf_sem + -------- + +This read/write semaphore protects against concurrent access to the +write-behind buffer ('wbuf') used for flash chips where we must write +in blocks. It protects both the contents of the wbuf and the metadata +which indicates which flash region (if any) is currently covered by +the buffer. + +Ordering constraints: + Lock wbuf_sem last, after the alloc_sem or and f->sem. Index: fs/jffs2/current/doc/readme.txt =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/doc/readme.txt,v retrieving revision 1.1 diff -u -r1.1 readme.txt --- fs/jffs2/current/doc/readme.txt 20 May 2002 22:21:29 -0000 1.1 +++ fs/jffs2/current/doc/readme.txt 22 Jan 2005 17:12:53 -0000 @@ -15,13 +15,18 @@ devs/flash/intel/strata/current/src/flash_program_buf.c -to allow byte aligned rather than word aligned writes, and to ensure overwriting an existing -word is successful (these are supplied in jffs2/current/src). +to allow byte aligned rather than word aligned writes, and to ensure +overwriting an existing word is successful (these are supplied in +jffs2/current/src). -Two test files are included fileio1.c (which performs the same tests as used for eCos RamFS), -and romfileio1.c (tests as eCos RomFS). +Three test files are included jffs2_1.c (which performs the same tests +as used for eCos RamFS), jffs2_2.c (tests is seeking works) and jffs2_3.c +(garbage collection and memory leak detection) -romfileio1.c requires that a jffs2 filesystem image jffs2.img is present at the desired mount point. -This image was prepared on Linux with the tools originating with JFFS2 source from -www.infradead.org \ No newline at end of file +jffs2_2.c requires that a jffs2 filesystem image jffs2.img is present +at the desired mount point. This image was prepared on Linux with the +tools originating with JFFS2 source from www.infradead.org. Note that +this image is little endian and will only work on little endian +targets. For big endian targets it will be necassary to generate a new +image. Index: fs/jffs2/current/include/linux/jffs2.h =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/include/linux/jffs2.h,v retrieving revision 1.4 diff -u -r1.4 jffs2.h --- fs/jffs2/current/include/linux/jffs2.h 12 Aug 2004 21:35:28 -0000 1.4 +++ fs/jffs2/current/include/linux/jffs2.h 22 Jan 2005 17:12:54 -0000 @@ -3,12 +3,12 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $ + * $Id: jffs2.h,v 1.34 2004/11/16 20:36:14 dwmw2 Exp $ * */ Index: fs/jffs2/current/include/linux/jffs2_fs_sb.h =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h,v retrieving revision 1.4 diff -u -r1.4 jffs2_fs_sb.h --- fs/jffs2/current/include/linux/jffs2_fs_sb.h 12 Nov 2004 16:50:33 -0000 1.4 +++ fs/jffs2/current/include/linux/jffs2_fs_sb.h 22 Jan 2005 17:12:54 -0000 @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.46 2004/11/03 12:57:39 jwboyer Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.48 2004/11/20 10:41:12 dwmw2 Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -11,6 +11,7 @@ #include #include #include +#include #define JFFS2_SB_FLAG_RO 1 #define JFFS2_SB_FLAG_MOUNTING 2 @@ -35,9 +36,7 @@ struct semaphore alloc_sem; /* Used to protect all the following fields, and also to protect against - out-of-order writing of nodes. - And GC. - */ + out-of-order writing of nodes. And GC. */ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER (i.e. zero for OOB CLEANMARKER */ @@ -103,6 +102,8 @@ uint32_t wbuf_pagesize; struct jffs2_inodirty *wbuf_inodes; + struct rw_semaphore wbuf_sem; /* Protects the write buffer */ + /* Information about out-of-band area usage... */ struct nand_oobinfo *oobinfo; uint32_t badblock_pos; Index: fs/jffs2/current/src/build.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/build.c,v retrieving revision 1.7 diff -u -r1.7 build.c --- fs/jffs2/current/src/build.c 12 Nov 2004 16:50:33 -0000 1.7 +++ fs/jffs2/current/src/build.c 22 Jan 2005 17:12:54 -0000 @@ -3,17 +3,19 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ + * $Id: build.c,v 1.69 2004/12/16 20:22:18 dmarlin Exp $ * */ #include #include #include +#include +#include #include "nodelist.h" static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); @@ -62,6 +64,7 @@ if (!child_ic) { printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", fd->name, fd->ino, ic->ino); + jffs2_mark_node_obsolete(c, fd->raw); continue; } @@ -88,6 +91,7 @@ int ret; int i; struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *fd; struct jffs2_full_dirent *dead_fds = NULL; /* First, scan the medium and build all the inode caches with @@ -95,13 +99,11 @@ c->flags |= JFFS2_SB_FLAG_MOUNTING; ret = jffs2_scan_medium(c); - c->flags &= ~JFFS2_SB_FLAG_MOUNTING; - if (ret) - return ret; + goto exit; D1(printk(KERN_DEBUG "Scanned flash completely\n")); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { @@ -114,6 +116,8 @@ cond_resched(); } } + c->flags &= ~JFFS2_SB_FLAG_MOUNTING; + D1(printk(KERN_DEBUG "Pass 1 complete\n")); /* Next, scan for inodes with nlink == 0 and remove them. If @@ -135,9 +139,7 @@ D1(printk(KERN_DEBUG "Pass 2a starting\n")); while (dead_fds) { - struct jffs2_inode_cache *ic; - struct jffs2_full_dirent *fd = dead_fds; - + fd = dead_fds; dead_fds = fd->next; ic = jffs2_get_ino_cache(c, fd->ino); @@ -152,7 +154,6 @@ /* Finally, we can scan again and free the dirent structs */ for_each_inode(i, c, ic) { - struct jffs2_full_dirent *fd; D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes)); while(ic->scan_dents) { @@ -164,11 +165,24 @@ cond_resched(); } D1(printk(KERN_DEBUG "Pass 3 complete\n")); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); /* Rotate the lists by some number to ensure wear levelling */ jffs2_rotate_lists(c); + ret = 0; + +exit: + if (ret) { + for_each_inode(i, c, ic) { + while(ic->scan_dents) { + fd = ic->scan_dents; + ic->scan_dents = fd->next; + jffs2_free_full_dirent(fd); + } + } + } + return ret; } @@ -179,9 +193,12 @@ D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); - for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { + raw = ic->nodes; + while (raw != (void *)ic) { + struct jffs2_raw_node_ref *next = raw->next_in_ino; D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw))); jffs2_mark_node_obsolete(c, raw); + raw = next; } if (ic->scan_dents) { @@ -297,7 +314,12 @@ c->free_size = c->flash_size; c->nr_blocks = c->flash_size / c->sector_size; - c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); +#ifndef __ECOS + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks); + else +#endif + c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); if (!c->blocks) return -ENOMEM; for (i=0; inr_blocks; i++) { @@ -310,6 +332,7 @@ c->blocks[i].used_size = 0; c->blocks[i].first_node = NULL; c->blocks[i].last_node = NULL; + c->blocks[i].bad_count = 0; } init_MUTEX(&c->alloc_sem); @@ -336,7 +359,13 @@ D1(printk(KERN_DEBUG "build_fs failed\n")); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); +#ifndef __ECOS + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + vfree(c->blocks); + else +#endif + kfree(c->blocks); + return -EIO; } Index: fs/jffs2/current/src/compr.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/compr.c,v retrieving revision 1.8 diff -u -r1.8 compr.c --- fs/jffs2/current/src/compr.c 12 Nov 2004 16:50:33 -0000 1.8 +++ fs/jffs2/current/src/compr.c 22 Jan 2005 17:12:55 -0000 @@ -9,13 +9,13 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.42 2004/08/07 21:56:08 dwmw2 Exp $ + * $Id: compr.c,v 1.43 2005/01/12 22:34:35 gleixner Exp $ * */ #include "compr.h" -static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(jffs2_compressor_list_lock); /* Available compressors are on this list */ static LIST_HEAD(jffs2_compressor_list); Index: fs/jffs2/current/src/compr_zlib.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/compr_zlib.c,v retrieving revision 1.7 diff -u -r1.7 compr_zlib.c --- fs/jffs2/current/src/compr_zlib.c 12 Aug 2004 21:35:28 -0000 1.7 +++ fs/jffs2/current/src/compr_zlib.c 22 Jan 2005 17:12:55 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $ * */ Index: fs/jffs2/current/src/erase.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/erase.c,v retrieving revision 1.8 diff -u -r1.8 erase.c --- fs/jffs2/current/src/erase.c 12 Nov 2004 16:50:33 -0000 1.8 +++ fs/jffs2/current/src/erase.c 22 Jan 2005 17:12:56 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.64 2004/11/12 15:25:14 jwboyer Exp $ + * $Id: erase.c,v 1.69 2004/12/06 10:17:48 dedekind Exp $ * */ @@ -47,6 +47,7 @@ #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)); 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"); @@ -387,7 +388,7 @@ jeb->dirty_size = 0; jeb->wasted_size = 0; } else { - struct iovec vecs[1]; + struct kvec vecs[1]; struct jffs2_unknown_node marker = { .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), Index: fs/jffs2/current/src/fs-ecos.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/fs-ecos.c,v retrieving revision 1.34 diff -u -r1.34 fs-ecos.c --- fs/jffs2/current/src/fs-ecos.c 22 Jan 2005 14:05:32 -0000 1.34 +++ fs/jffs2/current/src/fs-ecos.c 22 Jan 2005 17:12:58 -0000 @@ -8,7 +8,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs-ecos.c,v 1.39 2004/11/11 12:46:17 dwmw2 Exp $ + * $Id: fs-ecos.c,v 1.41 2005/01/22 16:01:12 lunn Exp $ * */ Index: fs/jffs2/current/src/gc.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/gc.c,v retrieving revision 1.8 diff -u -r1.8 gc.c --- fs/jffs2/current/src/gc.c 12 Aug 2004 21:35:28 -0000 1.8 +++ fs/jffs2/current/src/gc.c 22 Jan 2005 17:12:59 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.137 2004/07/20 13:44:55 dwmw2 Exp $ + * $Id: gc.c,v 1.144 2004/12/21 11:18:50 dwmw2 Exp $ * */ @@ -103,7 +103,7 @@ ret->wasted_size = 0; } - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); return ret; } @@ -134,7 +134,7 @@ 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); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); spin_unlock(&c->erase_completion_lock); BUG(); } @@ -602,7 +602,7 @@ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); jffs2_free_raw_node_ref(nraw); } - if (!retried && (nraw == jffs2_alloc_raw_node_ref())) { + if (!retried && (nraw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; @@ -628,6 +628,7 @@ jffs2_free_raw_node_ref(nraw); } + jffs2_free_raw_node_ref(nraw); if (!ret) ret = -EIO; goto out_node; @@ -637,10 +638,12 @@ /* Link into per-inode list. This is safe because of the ic state being INO_STATE_GC. Note that if we're doing this - for an inode which is in-code, the 'nraw' pointer is then + for an inode which is in-core, the 'nraw' pointer is then going to be fetched from ic->nodes by our caller. */ + spin_lock(&c->erase_completion_lock); nraw->next_in_ino = ic->nodes; ic->nodes = nraw; + spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, raw); D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw))); @@ -828,7 +831,7 @@ continue; } if (retlen != rawlen) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n", + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %u) reading header from obsolete node at %08x\n", retlen, rawlen, ref_offset(raw)); continue; } Index: fs/jffs2/current/src/nodelist.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/nodelist.c,v retrieving revision 1.5 diff -u -r1.5 nodelist.c --- fs/jffs2/current/src/nodelist.c 20 Nov 2003 16:52:36 -0000 1.5 +++ fs/jffs2/current/src/nodelist.c 22 Jan 2005 17:13:00 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $ + * $Id: nodelist.c,v 1.92 2005/01/19 19:22:00 tpoynor Exp $ * */ @@ -92,42 +92,53 @@ } } +/* 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; +} /* 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, ino_t ino, struct jffs2_inode_info *f, +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) { - struct jffs2_raw_node_ref *ref = f->inocache->nodes; + 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; *mctime_ver = 0; - - D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino)); - if (!f->inocache->nodes) { - printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino); - } + + D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino)); spin_lock(&c->erase_completion_lock); - for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) { - /* Work out whether it's a data node or a dirent node */ - if (ref_obsolete(ref)) { - /* FIXME: On NAND flash we may need to read these */ - D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); - continue; - } + 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 */ + 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(); @@ -183,7 +194,6 @@ err = -ENOMEM; goto free_out; } - memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1); fd->raw = ref; fd->version = je32_to_cpu(node.d.version); fd->ino = je32_to_cpu(node.d.ino); @@ -221,6 +231,7 @@ } 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 */ @@ -479,6 +490,8 @@ { 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); prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE]; Index: fs/jffs2/current/src/nodelist.h =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/nodelist.h,v retrieving revision 1.9 diff -u -r1.9 nodelist.h --- fs/jffs2/current/src/nodelist.h 12 Nov 2004 16:50:34 -0000 1.9 +++ fs/jffs2/current/src/nodelist.h 22 Jan 2005 17:13:01 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.120 2004/10/07 15:11:54 havasi Exp $ + * $Id: nodelist.h,v 1.126 2004/11/19 15:06:29 dedekind Exp $ * */ @@ -107,16 +107,6 @@ #define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) #define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) -/* - Used for keeping track of deletion nodes &c, which can only be marked - as obsolete when the node which they mark as deleted has actually been - removed from the flash. -*/ -struct jffs2_raw_node_ref_list { - struct jffs2_raw_node_ref *rew; - struct jffs2_raw_node_ref_list *next; -}; - /* For each inode in the filesystem, we need to keep a record of nlink, because it would be a PITA to scan the whole directory tree at read_inode() time to calculate it, and to keep sufficient information @@ -148,13 +138,6 @@ #define INOCACHE_HASHSIZE 128 -struct jffs2_scan_info { - struct jffs2_full_dirent *dents; - struct jffs2_tmp_dnode_info *tmpnodes; - /* Latest i_size info */ - uint32_t version; - uint32_t isize; -}; /* Larger representation of a raw node, kept in-core only when the struct inode for this particular ino is instantiated. @@ -163,12 +146,11 @@ struct jffs2_full_dnode { struct jffs2_raw_node_ref *raw; - uint32_t ofs; /* Don't really need this, but optimisation */ + uint32_t ofs; /* The offset to which the data of this node belongs */ uint32_t size; uint32_t frags; /* Number of fragments which currently refer to this node. When this reaches zero, - the node is obsolete. - */ + the node is obsolete. */ }; /* @@ -193,6 +175,7 @@ unsigned char type; unsigned char name[0]; }; + /* Fragments - used to build a map of which raw node to obtain data from for each part of the ino @@ -202,7 +185,7 @@ struct rb_node rb; struct jffs2_full_dnode *node; /* NULL for holes */ uint32_t size; - uint32_t ofs; /* Don't really need this, but optimisation */ + uint32_t ofs; /* The offset to which this fragment belongs */ }; struct jffs2_eraseblock @@ -221,14 +204,6 @@ struct jffs2_raw_node_ref *last_node; struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */ - - /* For deletia. When a dirent node in this eraseblock is - deleted by a node elsewhere, that other node can only - be marked as obsolete when this block is actually erased. - So we keep a list of the nodes to mark as obsolete when - the erase is completed. - */ - // MAYBE struct jffs2_raw_node_ref_list *deletia; }; #define ACCT_SANITY_CHECK(c, jeb) do { \ @@ -396,9 +371,9 @@ #define frag_erase(frag, list) rb_erase(&frag->rb, list); /* nodelist.c */ -D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)); +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, ino_t ino, struct jffs2_inode_info *f, +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); Index: fs/jffs2/current/src/nodemgmt.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/nodemgmt.c,v retrieving revision 1.7 diff -u -r1.7 nodemgmt.c --- fs/jffs2/current/src/nodemgmt.c 12 Nov 2004 16:50:34 -0000 1.7 +++ fs/jffs2/current/src/nodemgmt.c 22 Jan 2005 17:13:02 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.109 2004/10/07 15:08:47 havasi Exp $ + * $Id: nodemgmt.c,v 1.115 2004/11/22 11:07:21 dwmw2 Exp $ * */ @@ -399,6 +399,17 @@ } jeb = &c->blocks[blocknr]; + if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) && + !(c->flags & JFFS2_SB_FLAG_MOUNTING)) { + /* 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 + any jffs2_raw_node_refs. So we don't need to stop erases from + happening, or protect against people holding an obsolete + jffs2_raw_node_ref without the erase_completion_lock. */ + down(&c->erase_free_sem); + } + spin_lock(&c->erase_completion_lock); if (ref_flags(ref) == REF_UNCHECKED) { @@ -463,6 +474,7 @@ marked obsolete on the flash at the time they _became_ obsolete, there was probably a reason for that. */ spin_unlock(&c->erase_completion_lock); + /* We didn't lock the erase_free_sem */ return; } @@ -515,61 +527,87 @@ spin_unlock(&c->erase_completion_lock); - if (!jffs2_can_mark_obsolete(c)) - return; - if (jffs2_is_readonly(c)) + if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c)) { + /* We didn't lock the erase_free_sem */ return; + } + + /* The erase_free_sem is locked, and has been since before we marked the node obsolete + and potentially put its eraseblock onto the erase_pending_list. Thus, we know that + the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet + by jffs2_free_all_node_refs() in erase.c. Which is nice. */ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); - return; + goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); - return; + goto out_erase_sem; } if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); - return; + goto out_erase_sem; } if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); - return; + goto out_erase_sem; } /* XXX FIXME: This is ugly now */ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE); ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); - return; + goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); - return; + goto out_erase_sem; } /* Nodes which have been marked obsolete no longer need to be - associated with any inode. Remove them from the per-inode list */ + associated with any inode. Remove them from the per-inode list. + + Note we can't do this for NAND at the moment because we need + obsolete dirent nodes to stay on the lists, because of the + horridness in jffs2_garbage_collect_deletion_dirent(). Also + because we delete the inocache, and on NAND we need that to + stay around until all the nodes are actually erased, in order + to stop us from giving the same inode number to another newly + created inode. */ if (ref->next_in_ino) { struct jffs2_inode_cache *ic; struct jffs2_raw_node_ref **p; + spin_lock(&c->erase_completion_lock); + ic = jffs2_raw_ref_to_ic(ref); for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino)) ; *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)); + jffs2_del_ino_cache(c, ic); + jffs2_free_inode_cache(ic); + } + + spin_unlock(&c->erase_completion_lock); } /* Merge with the next node in the physical list, if there is one - and if it's also obsolete. */ - if (ref->next_phys && ref_obsolete(ref->next_phys) ) { + and if it's also obsolete and if it doesn't belong to any inode */ + if (ref->next_phys && ref_obsolete(ref->next_phys) && + !ref->next_phys->next_in_ino) { struct jffs2_raw_node_ref *n = ref->next_phys; + spin_lock(&c->erase_completion_lock); + ref->__totlen += n->__totlen; ref->next_phys = n->next_phys; if (jeb->last_node == n) jeb->last_node = ref; @@ -577,7 +615,8 @@ /* gc will be happy continuing gc on this node */ jeb->gc_node=ref; } - BUG_ON(n->next_in_ino); + spin_unlock(&c->erase_completion_lock); + jffs2_free_raw_node_ref(n); } @@ -585,11 +624,13 @@ and that one is obsolete */ if (ref != jeb->first_node ) { struct jffs2_raw_node_ref *p = jeb->first_node; - + + spin_lock(&c->erase_completion_lock); + while (p->next_phys != ref) p = p->next_phys; - if (ref_obsolete(p) ) { + if (ref_obsolete(p) && !ref->next_in_ino) { p->__totlen += ref->__totlen; if (jeb->last_node == ref) { jeb->last_node = p; @@ -601,10 +642,13 @@ p->next_phys = ref->next_phys; jffs2_free_raw_node_ref(ref); } + spin_unlock(&c->erase_completion_lock); } + out_erase_sem: + up(&c->erase_free_sem); } -#if CONFIG_JFFS2_FS_DEBUG > 0 +#if CONFIG_JFFS2_FS_DEBUG >= 2 void jffs2_dump_block_lists(struct jffs2_sb_info *c) { Index: fs/jffs2/current/src/pushpull.h =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/pushpull.h,v retrieving revision 1.4 diff -u -r1.4 pushpull.h --- fs/jffs2/current/src/pushpull.h 20 Nov 2003 16:52:36 -0000 1.4 +++ fs/jffs2/current/src/pushpull.h 22 Jan 2005 17:13:02 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $ * */ Index: fs/jffs2/current/src/read.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/read.c,v retrieving revision 1.7 diff -u -r1.7 read.c --- fs/jffs2/current/src/read.c 12 Aug 2004 21:35:28 -0000 1.7 +++ fs/jffs2/current/src/read.c 22 Jan 2005 17:13:02 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $ + * $Id: read.c,v 1.38 2004/11/16 20:36:12 dwmw2 Exp $ * */ @@ -174,7 +174,7 @@ 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); - D1(jffs2_print_frag_list(f)); + 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); Index: fs/jffs2/current/src/readinode.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/readinode.c,v retrieving revision 1.5 diff -u -r1.5 readinode.c --- fs/jffs2/current/src/readinode.c 20 Nov 2003 16:52:36 -0000 1.5 +++ fs/jffs2/current/src/readinode.c 22 Jan 2005 17:13:03 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $ + * $Id: readinode.c,v 1.117 2004/11/20 18:06:54 dwmw2 Exp $ * */ @@ -22,7 +22,7 @@ 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 >= 1 +#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); @@ -56,7 +56,9 @@ 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; @@ -225,7 +227,7 @@ If so, both 'this' and the new node get marked REF_NORMAL so the GC can take a look. */ - if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { + 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); @@ -510,7 +512,7 @@ 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->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); + 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); Index: fs/jffs2/current/src/scan.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/scan.c,v retrieving revision 1.8 diff -u -r1.8 scan.c --- fs/jffs2/current/src/scan.c 12 Nov 2004 16:50:34 -0000 1.8 +++ fs/jffs2/current/src/scan.c 22 Jan 2005 17:13:04 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.113 2004/11/03 12:57:39 jwboyer Exp $ + * $Id: scan.c,v 1.115 2004/11/17 12:59:08 dedekind Exp $ * */ #include @@ -160,11 +160,8 @@ case BLK_STATE_PARTDIRTY: /* Some data, but not full. Dirty list. */ - /* Except that we want to remember the block with most free space, - and stick it in the 'nextblock' position to start writing to it. - Later when we do snapshots, this must be the most recent block, - not the one with most free space. - */ + /* We want to remember the block with most free space + and stick it in the 'nextblock' position to start writing to it. */ if (jeb->free_size > min_free(c) && (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ Index: fs/jffs2/current/src/write.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/src/write.c,v retrieving revision 1.8 diff -u -r1.8 write.c --- fs/jffs2/current/src/write.c 12 Aug 2004 21:35:28 -0000 1.8 +++ fs/jffs2/current/src/write.c 22 Jan 2005 17:13:05 -0000 @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.85 2004/07/13 08:58:25 dwmw2 Exp $ + * $Id: write.c,v 1.87 2004/11/16 20:36:12 dwmw2 Exp $ * */ @@ -213,8 +213,10 @@ jffs2_add_physical_node_ref(c, raw); /* Link into per-inode list */ + spin_lock(&c->erase_completion_lock); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; + spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), @@ -333,8 +335,10 @@ raw->flash_offset |= REF_PRISTINE; jffs2_add_physical_node_ref(c, raw); + spin_lock(&c->erase_completion_lock); raw->next_in_ino = f->inocache->nodes; f->inocache->nodes = raw; + spin_unlock(&c->erase_completion_lock); if (retried) { ACCT_SANITY_CHECK(c,NULL); Index: fs/jffs2/current/tests/jffs2_1.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/tests/jffs2_1.c,v retrieving revision 1.2 diff -u -r1.2 jffs2_1.c --- fs/jffs2/current/tests/jffs2_1.c 22 Jan 2005 14:05:32 -0000 1.2 +++ fs/jffs2/current/tests/jffs2_1.c 22 Jan 2005 17:13:06 -0000 @@ -654,15 +654,11 @@ err = chdir( "/" ); if( err < 0 ) SHOW_RESULT( chdir, err ); checkcwd( "/" ); -#ifdef CYGOPT_FS_JFFS2_GCTHREAD - diag_printf(": Letting garbage collect thread run\n"); - cyg_thread_delay(2*100); -#endif diag_printf(": umount /jffs2\n"); err = umount( "/jffs2" ); if( err < 0 ) SHOW_RESULT( umount, err ); - + diag_printf(": umount /\n"); err = umount( "/" ); if( err < 0 ) SHOW_RESULT( umount, err ); Index: fs/jffs2/current/tests/jffs2_2.c =================================================================== RCS file: /cvs/ecos/ecos/packages/fs/jffs2/current/tests/jffs2_2.c,v retrieving revision 1.2 diff -u -r1.2 jffs2_2.c --- fs/jffs2/current/tests/jffs2_2.c 22 Jan 2005 14:05:32 -0000 1.2 +++ fs/jffs2/current/tests/jffs2_2.c 22 Jan 2005 17:13:06 -0000 @@ -45,7 +45,7 @@ // Contributors: asl // Date: 2004-03-29 // Purpose: Test fseek on a filesystem -// Description: This test uses the jffs2 to check out the fseek +// Description: This test uses the ramfs to check out the fseek // operation on a filesystem. // //