Mercurial > ecos
changeset 1372:121121d3d18f
Merge changes from David Woodhouse (bring up to date with MTD project)
line wrap: on
line diff
--- 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.8 2003/07/15 20:45:32 gthomas Exp $ +# $Id: jffs2.cdl,v 1.6 2003/11/20 16:41:57 dwmw2 Exp $ # # ==================================================================== #####ECOSGPLCOPYRIGHTBEGIN#### @@ -73,7 +73,80 @@ 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 erase.c dir-ecos.c write.c gc.c read.c compr.c compr_zlib.c compr_rtime.c compr_rubin.c file-ecos.c + compile build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c erase.c dir-ecos.c write.c gc.c read.c compr.c file-ecos.c + + cdl_option CYGOPT_FS_JFFS2_NAND { + display "Support for NAND flash" + flavor bool + define CONFIG_JFFS2_FS_NAND + compile wbuf.c + requires 0 + description " + Enable support for JFFS2 on NAND flash." + } + + cdl_option CYGOPT_FS_JFFS2_DEBUG { + display "Debug level" + flavor data + legal_values 0 to 2 + define CONFIG_JFFS2_FS_DEBUG + description " + Debug verbosity of JFFS2 code. Zero is normal operation + without debugging. Level 1 adds extra sanity checks and + fairly verbose output. Level 2 is insanely loquacious." + } + + cdl_option CYGOPT_FS_JFFS2_COMPRESS { + display "Compress data" + flavor bool + define JFFS2_COMPRESSION + default_value 1 + description " + Compression and decompression are entirely handled by the file + system and are fully transparent to applications. However, + selecting this option increases the amount of RAM required and + slows down read and write operations considerably if you have a + slow CPU." + } + + cdl_option CYGOPT_FS_JFFS2_COMPRESS_ZLIB { + display "Compress data using zlib" + flavor bool + define JFFS2_USE_ZLIB + requires CYGPKG_COMPRESS_ZLIB + requires CYGOPT_FS_JFFS2_COMPRESS + compile compr_zlib.c + default_value 1 + description " + Use zlib for compression of data. This is the slowest of the + compression options available but the most effective." + } + + cdl_option CYGOPT_FS_JFFS2_COMPRESS_RTIME { + display "Compress data using rtime" + flavor bool + define JFFS2_USE_RTIME + requires CYGOPT_FS_JFFS2_COMPRESS + compile compr_rtime.c + default_value 1 + description " + Use the rtime algorithm for compression of data. This + simple algorithm often manages to squeeze and extra few + bytes from data already compressed with gzip." + } + + cdl_option CYGOPT_FS_JFFS2_COMPRESS_RUBIN { + display "Compress data using rubin" + flavor bool + define JFFS2_USE_RTIME + requires CYGOPT_FS_JFFS2_COMPRESS + compile compr_rubin.c + default_value !CYGOPT_FS_JFFS2_COMPRESS_ZLIB + description " + Use the rubin algorithm for compression of data. This + simple algorithm is faster than zlib but not quite as + effective." + } cdl_option CYGPKG_FS_JFFS2_CFLAGS_ADD { display "Additional compiler flags" @@ -81,7 +154,7 @@ cdl_package CYGPKG_FS_JFFS2 { no_define # We add -D__ECOS to trigger eCos-specific code in places. # We add -Werror because I find it useful. - default_value { "-D__ECOS -nostdinc -iwithprefix include " } + default_value { "-D__ECOS -nostdinc -iwithprefix include" } description " This option modifies the set of compiler flags for building the JFFS2 package. @@ -110,7 +183,7 @@ cdl_package CYGPKG_FS_JFFS2 { display "JFFS2 FS tests" flavor data no_define - calculated { "tests/fileio1.c tests/jffs2_exerciser.c" } + calculated { "tests/fileio1.c" } description " This option specifies the set of tests for the JFFS2 FS package." }
--- a/packages/fs/jffs2/current/include/linux/jffs2.h +++ b/packages/fs/jffs2/current/include/linux/jffs2.h @@ -1,14 +1,14 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.30 2003/02/15 00:15:22 dwmw2 Exp $ + * $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $ * */
--- 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.39 2003/06/30 14:36:47 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -15,6 +15,8 @@ #define JFFS2_SB_FLAG_RO 1 #define JFFS2_SB_FLAG_MOUNTING 2 +struct jffs2_inodirty; + /* A struct for the overall file system control. Pointers to jffs2_sb_info structs are named `c' in the source code. Nee jffs_control @@ -52,6 +54,15 @@ struct jffs2_sb_info { uint32_t nr_free_blocks; uint32_t nr_erasing_blocks; + /* Number of free blocks there must be before we... */ + uint8_t resv_blocks_write; /* ... allow a normal filesystem write */ + uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */ + uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ + uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ + uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ + + uint32_t nospc_dirty_size; + uint32_t nr_blocks; struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks * from the offset (blocks[ofs / sector_size]) */ @@ -90,8 +101,7 @@ struct jffs2_sb_info { uint32_t wbuf_ofs; uint32_t wbuf_len; uint32_t wbuf_pagesize; - struct work_struct wbuf_task; /* task for timed wbuf flush */ - struct timer_list wbuf_timer; /* timer for flushing wbuf */ + struct jffs2_inodirty *wbuf_inodes; /* Information about out-of-band area usage... */ struct nand_oobinfo *oobinfo;
--- a/packages/fs/jffs2/current/src/LICENCE +++ b/packages/fs/jffs2/current/src/LICENCE @@ -1,7 +1,7 @@ The files in this directory and elsewhere which refer to this LICENCE file are part of JFFS2, the Journalling Flash File System v2. - Copyright (C) 2001, 2002 Red Hat, Inc. + Copyright (C) 2001-2003 Red Hat, Inc. JFFS2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -32,4 +32,4 @@ For information on obtaining alternative http://sources.redhat.com/jffs2/jffs2-licence.html - $Id: LICENCE,v 1.1 2002/05/20 14:56:37 dwmw2 Exp $ + $Id: LICENCE,v 1.2 2003/10/04 08:33:05 dwmw2 Exp $
--- a/packages/fs/jffs2/current/src/build.c +++ b/packages/fs/jffs2/current/src/build.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.46 2003/04/29 17:12:26 gleixner Exp $ + * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ * */ @@ -16,8 +16,7 @@ #include <linux/slab.h> #include "nodelist.h" -int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *); -int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *); +static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); static inline struct jffs2_inode_cache * first_inode_chain(int *i, struct jffs2_sb_info *c) @@ -44,6 +43,41 @@ next_inode(int *i, struct jffs2_inode_ca ic; \ ic = next_inode(&i, ic, (c))) + +static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_full_dirent *fd; + + D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino)); + + /* For each child, increase nlink */ + for(fd = ic->scan_dents; fd; fd = fd->next) { + struct jffs2_inode_cache *child_ic; + if (!fd->ino) + continue; + + /* XXX: Can get high latency here with huge directories */ + + child_ic = jffs2_get_ino_cache(c, fd->ino); + 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); + continue; + } + + if (child_ic->nlink++ && fd->type == DT_DIR) { + printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); + if (fd->ino == 1 && ic->ino == 1) { + printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); + printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); + } + /* What do we do about it? */ + } + D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); + /* Can't free them. We might need them in pass 2 */ + } +} + /* Scan plan: - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go - Scan directory tree from top down, setting nlink in inocaches @@ -54,6 +88,7 @@ static int jffs2_build_filesystem(struct int ret; int i; struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *dead_fds = NULL; /* First, scan the medium and build all the inode caches with lists of physical nodes */ @@ -71,47 +106,51 @@ static int jffs2_build_filesystem(struct /* 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)); - ret = jffs2_build_inode_pass1(c, ic); - if (ret) { - D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret)); - return ret; + + D1(BUG_ON(ic->ino > c->highest_ino)); + + if (ic->scan_dents) { + jffs2_build_inode_pass1(c, ic); + cond_resched(); } - cond_resched(); } D1(printk(KERN_DEBUG "Pass 1 complete\n")); - D1(jffs2_dump_block_lists(c)); /* Next, scan for inodes with nlink == 0 and remove them. If they were directories, then decrement the nlink of their children too, and repeat the scan. As that's going to be a fairly uncommon occurrence, it's not so evil to do it this way. Recursion bad. */ - do { - D1(printk(KERN_DEBUG "Pass 2 (re)starting\n")); - ret = 0; - for_each_inode(i, c, ic) { - D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes)); - if (ic->nlink) - continue; + D1(printk(KERN_DEBUG "Pass 2 starting\n")); + + for_each_inode(i, c, ic) { + D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes)); + if (ic->nlink) + continue; - /* XXX: Can get high latency here. Move the cond_resched() from the end of the loop? */ + jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); + cond_resched(); + } + + D1(printk(KERN_DEBUG "Pass 2a starting\n")); - ret = jffs2_build_remove_unlinked_inode(c, ic); - if (ret) - break; - /* -EAGAIN means the inode's nlink was zero, so we deleted it, - and furthermore that it had children and their nlink has now - gone to zero too. So we have to restart the scan. */ - } - D1(jffs2_dump_block_lists(c)); + while (dead_fds) { + struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *fd = dead_fds; + + dead_fds = fd->next; - cond_resched(); - - } while(ret == -EAGAIN); + ic = jffs2_get_ino_cache(c, fd->ino); + D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic)); + + if (ic) + jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); + jffs2_free_full_dirent(fd); + } D1(printk(KERN_DEBUG "Pass 2 complete\n")); - /* Finally, we can scan again and free the dirent nodes and scan_info structs */ + /* 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)); @@ -133,49 +172,10 @@ static int jffs2_build_filesystem(struct return ret; } -int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) -{ - struct jffs2_full_dirent *fd; - - D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino)); - - if (ic->ino > c->highest_ino) - c->highest_ino = ic->ino; - - /* For each child, increase nlink */ - for(fd=ic->scan_dents; fd; fd = fd->next) { - struct jffs2_inode_cache *child_ic; - if (!fd->ino) - continue; - - /* XXX: Can get high latency here with huge directories */ - - child_ic = jffs2_get_ino_cache(c, fd->ino); - 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); - continue; - } - - if (child_ic->nlink++ && fd->type == DT_DIR) { - printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); - if (fd->ino == 1 && ic->ino == 1) { - printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); - printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); - } - /* What do we do about it? */ - } - D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); - /* Can't free them. We might need them in pass 2 */ - } - return 0; -} - -int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; - int ret = 0; D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); @@ -214,19 +214,82 @@ int jffs2_build_remove_unlinked_inode(st jffs2_free_full_dirent(fd); continue; } - jffs2_free_full_dirent(fd); + + /* Reduce nlink of the child. If it's now zero, stick it on the + dead_fds list to be cleaned up later. Else just free the fd */ + child_ic->nlink--; + + if (!child_ic->nlink) { + D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n", + fd->ino, fd->name)); + fd->next = *dead_fds; + *dead_fds = fd; + } else { + D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", + fd->ino, fd->name, child_ic->nlink)); + jffs2_free_full_dirent(fd); + } } - ret = -EAGAIN; } /* We don't delete the inocache from the hash list and free it yet. The erase code will do that, when all the nodes are completely gone. */ +} - return ret; -} +static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) +{ + uint32_t size; + + /* Deletion should almost _always_ be allowed. We're fairly + buggered once we stop allowing people to delete stuff + because there's not enough free space... */ + c->resv_blocks_deletion = 2; + + /* Be conservative about how much space we need before we allow writes. + On top of that which is required for deletia, require an extra 2% + of the medium to be available, for overhead caused by nodes being + split across blocks, etc. */ + + size = c->flash_size / 50; /* 2% of flash size */ + size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */ + size += c->sector_size - 1; /* ... and round up */ + + c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size); + + /* When do we let the GC thread run in the background */ + + c->resv_blocks_gctrigger = c->resv_blocks_write + 1; + + /* When do we allow garbage collection to merge nodes to make + long-term progress at the expense of short-term space exhaustion? */ + c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1; + + /* When do we allow garbage collection to eat from bad blocks rather + than actually making progress? */ + c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; + + /* If there's less than this amount of dirty space, don't bother + trying to GC to make more space. It'll be a fruitless task */ + c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); + + D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n", + c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks)); + D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n", + c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n", + c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n", + c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n", + c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n", + c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024)); + D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n", + c->nospc_dirty_size)); +} int jffs2_do_mount_fs(struct jffs2_sb_info *c) { @@ -276,5 +339,8 @@ int jffs2_do_mount_fs(struct jffs2_sb_in kfree(c->blocks); return -EIO; } + + jffs2_calc_trigger_levels(c); + return 0; }
--- a/packages/fs/jffs2/current/src/compr.c +++ b/packages/fs/jffs2/current/src/compr.c @@ -1,31 +1,21 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by Arjan van de Ven <arjanv@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.26 2003/01/12 13:21:28 dwmw2 Exp $ + * $Id: compr.c,v 1.29 2003/11/20 16:40:35 dwmw2 Exp $ * */ -#if defined(__KERNEL__) || defined (__ECOS) #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/types.h> -#else -#define KERN_DEBUG -#define KERN_NOTICE -#define KERN_WARNING -#define printk printf -#include <stdio.h> -#include <stdint.h> -#include <errno.h> -#endif - +#include <linux/slab.h> #include <linux/jffs2.h> int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); @@ -40,7 +30,7 @@ void jffs2_dynrubin_decompress(unsigned /* jffs2_compress: * @data: Pointer to uncompressed data - * @cdata: Pointer to buffer for compressed data + * @cdata: Pointer to returned pointer to buffer for compressed data * @datalen: On entry, holds the amount of data available for compression. * On exit, expected to hold the amount of data actually compressed. * @cdatalen: On entry, holds the amount of space available for compressed @@ -55,44 +45,55 @@ void jffs2_dynrubin_decompress(unsigned * jffs2_compress should compress as much as will fit, and should set * *datalen accordingly to show the amount of data which were compressed. */ -unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, +unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen) { +#ifdef JFFS2_COMPRESSION int ret; + *cpage_out = kmalloc(*cdatalen, GFP_KERNEL); + if (!*cpage_out) { + printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n"); + return JFFS2_COMPR_NONE; + } + +#ifdef JFFS2_USE_ZLIB ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen); if (!ret) { return JFFS2_COMPR_ZLIB; } -#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */ +#endif +#ifdef JFFS2_USE_DYNRUBIN ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen); if (!ret) { return JFFS2_COMPR_DYNRUBIN; } #endif -#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */ +#ifdef JFFS2_USE_RUBINMIPS ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen); if (!ret) { return JFFS2_COMPR_RUBINMIPS; } #endif +#ifdef JFFS2_USE_RTIME /* rtime does manage to recompress already-compressed data */ ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen); if (!ret) { return JFFS2_COMPR_RTIME; } -#if 0 - /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */ - /* If we get here, no compression is going to work */ - /* But we might want to use the fragmentation part -- Arjan */ - memcpy(cpage_out,data_in,min(*datalen,*cdatalen)); - if (*datalen > *cdatalen) - *datalen = *cdatalen; -#endif +#endif + kfree(*cpage_out); +#endif /* Compression */ + *cpage_out = data_in; + *datalen = *cdatalen; return JFFS2_COMPR_NONE; /* We failed to compress */ - } +void jffs2_free_comprbuf(unsigned char *orig, unsigned char *comprbuf) +{ + if (orig != comprbuf) + kfree(comprbuf); +} int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) @@ -106,30 +107,27 @@ int jffs2_decompress(unsigned char compr case JFFS2_COMPR_ZERO: memset(data_out, 0, datalen); break; - +#ifdef JFFS2_USE_ZLIB case JFFS2_COMPR_ZLIB: jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen); break; - +#endif +#ifdef JFFS2_USE_RTIME case JFFS2_COMPR_RTIME: jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen); break; - +#endif +#ifdef JFFS2_USE_RUBINMIPS case JFFS2_COMPR_RUBINMIPS: -#if 0 /* Disabled 23/9/1 */ jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen); -#else - printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n"); -#endif break; +#endif +#ifdef JFFS2_USE_DYNRUBIN case JFFS2_COMPR_DYNRUBIN: -#if 1 /* Phase this one out */ + jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen); -#else - printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n"); + break; #endif - break; - default: printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype); return -EIO;
--- a/packages/fs/jffs2/current/src/compr_rtime.c +++ b/packages/fs/jffs2/current/src/compr_rtime.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by Arjan van de Ven <arjanv@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.10 2003/05/11 10:47:13 dwmw2 Exp $ + * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $ * * * Very simple lz77-ish encoder.
--- a/packages/fs/jffs2/current/src/compr_zlib.c +++ b/packages/fs/jffs2/current/src/compr_zlib.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.23 2003/05/26 09:15:19 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $ * */
--- 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.3 2003/01/22 00:04:13 dwmw2 Exp $ + * $Id: dir-ecos.c,v 1.4 2003/11/20 16:41:58 dwmw2 Exp $ * */ @@ -162,7 +162,6 @@ int jffs2_mkdir (struct inode *dir_i, st struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen, phys_ofs; - uint32_t writtenlen; int ret; mode |= S_IFDIR; @@ -197,7 +196,7 @@ int jffs2_mkdir (struct inode *dir_i, st ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); - fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen); + fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_inode(ri); @@ -214,20 +213,12 @@ int jffs2_mkdir (struct inode *dir_i, st f->metadata = fn; up(&f->sem); - /* Work out where to put the dirent node now. */ - writtenlen = PAD(writtenlen); - phys_ofs += writtenlen; - alloclen -= writtenlen; - - if (alloclen < sizeof(*rd)+namelen) { - /* Not enough space left in this chunk. Get some more */ - jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); - if (ret) { - /* Eep. */ - jffs2_clear_inode(inode); - return ret; - } + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { + /* Eep. */ + jffs2_clear_inode(inode); + return ret; } rd = jffs2_alloc_raw_dirent(); @@ -255,7 +246,7 @@ int jffs2_mkdir (struct inode *dir_i, st rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, d_name->name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, &writtenlen); + fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, ALLOC_NORMAL); jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd);
--- a/packages/fs/jffs2/current/src/erase.c +++ b/packages/fs/jffs2/current/src/erase.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.51 2003/05/11 22:47:36 dwmw2 Exp $ + * $Id: erase.c,v 1.57 2003/11/04 14:46:13 dwmw2 Exp $ * */ @@ -101,7 +101,7 @@ void jffs2_erase_block(struct jffs2_sb_i jffs2_erase_failed(c, jeb); } -void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) +void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; @@ -118,15 +118,21 @@ void jffs2_erase_pending_blocks(struct j spin_unlock(&c->erase_completion_lock); jffs2_mark_erased_block(c, jeb); + if (!--count) { + D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); + goto done; + } + } else if (!list_empty(&c->erase_pending_list)) { jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); list_del(&jeb->list); c->erasing_size += c->sector_size; + c->wasted_size -= jeb->wasted_size; c->free_size -= jeb->free_size; c->used_size -= jeb->used_size; c->dirty_size -= jeb->dirty_size; - jeb->used_size = jeb->dirty_size = jeb->free_size = 0; + jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; jffs2_free_all_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); @@ -143,6 +149,7 @@ void jffs2_erase_pending_blocks(struct j } spin_unlock(&c->erase_completion_lock); + done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); up(&c->erase_free_sem); @@ -369,29 +376,30 @@ static void jffs2_mark_erased_block(stru .totlen = cpu_to_je32(c->cleanmarker_size) }; - marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4)); + marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); - ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker); + /* We only write the header; the rest was noise or padding anyway */ + ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); if (ret) { printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", jeb->offset, ret); goto bad2; } - if (retlen != je32_to_cpu(marker.totlen)) { + if (retlen != sizeof(marker)) { printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n", - jeb->offset, je32_to_cpu(marker.totlen), retlen); + jeb->offset, sizeof(marker), retlen); goto bad2; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = jeb->offset | REF_NORMAL; - marker_ref->totlen = PAD(je32_to_cpu(marker.totlen)); + marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; - jeb->free_size = c->sector_size - marker_ref->totlen; - jeb->used_size = marker_ref->totlen; + jeb->free_size = c->sector_size - c->cleanmarker_size; + jeb->used_size = c->cleanmarker_size; jeb->dirty_size = 0; jeb->wasted_size = 0; }
--- a/packages/fs/jffs2/current/src/file-ecos.c +++ b/packages/fs/jffs2/current/src/file-ecos.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file-ecos.c,v 1.5 2003/01/22 00:04:13 dwmw2 Exp $ + * $Id: file-ecos.c,v 1.6 2003/11/20 16:41:58 dwmw2 Exp $ * */ @@ -115,7 +115,7 @@ int jffs2_prepare_write (struct inode *d ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(0); - fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL); + fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL); jffs2_complete_reservation(c); if (IS_ERR(fn)) { ret = PTR_ERR(fn);
--- 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.9 2003/07/15 20:42:33 gthomas Exp $ + * $Id: fs-ecos.c,v 1.11 2003/11/20 16:41:58 dwmw2 Exp $ * */ @@ -567,7 +567,7 @@ static int jffs2_mount(cyg_fstab_entry * jffs2_sb->s_root->i_count = 1; // Ensures the root inode is always in ram until umount D2(printf("jffs2_mount erasing pending blocks\n")); - jffs2_erase_pending_blocks(c); + jffs2_erase_pending_blocks(c,0); } mte->data = (CYG_ADDRWORD) jffs2_sb; @@ -1705,6 +1705,21 @@ struct inode *new_inode(struct super_blo return inode; } +struct inode *ilookup(struct super_block *sb, cyg_uint32 ino) +{ + struct inode *inode = NULL; + + D2(printf("ilookup\n")); + // Check for this inode in the cache + for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) { + if (inode->i_ino == ino) { + inode->i_count++; + break; + } + } + + return inode; +} struct inode *iget(struct super_block *sb, cyg_uint32 ino) { @@ -1722,12 +1737,9 @@ struct inode *iget(struct super_block *s D2(printf("iget\n")); - // Check for this inode in the cache - for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) { - if (inode->i_ino == ino) - return inode; - } - inode = NULL; + inode = ilookup(sb, ino); + if (inode) + return inode; // Not cached, so malloc it inode = new_inode(sb); @@ -1736,7 +1748,7 @@ struct inode *iget(struct super_block *s inode->i_ino = ino; jffs2_read_inode(inode); - + inode->i_count = 1; return inode; } @@ -1851,7 +1863,7 @@ cyg_bool jffs2_flash_write(struct jffs2_ } int -jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, +jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t * retlen) { unsigned long i;
--- a/packages/fs/jffs2/current/src/gc.c +++ b/packages/fs/jffs2/current/src/gc.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.103 2003/05/22 18:01:02 dwmw2 Exp $ + * $Id: gc.c,v 1.129 2003/11/20 16:40:14 dwmw2 Exp $ * */ @@ -49,7 +49,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. */ - if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) { + 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; } else if (n < 50 && !list_empty(&c->erasable_list)) { @@ -115,7 +115,6 @@ int jffs2_garbage_collect_pass(struct jf struct jffs2_inode_cache *ic; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; - uint32_t inum; int ret = 0; if (down_interruptible(&c->alloc_sem)) @@ -186,7 +185,7 @@ int jffs2_garbage_collect_pass(struct jf ic->state = INO_STATE_CHECKING; spin_unlock(&c->inocache_lock); - D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino)); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino)); ret = jffs2_do_crccheck_inode(c, ic); if (ret) @@ -223,17 +222,21 @@ int jffs2_garbage_collect_pass(struct jf while(ref_obsolete(raw)) { D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); - jeb->gc_node = raw = raw->next_phys; - if (!raw) { + raw = raw->next_phys; + if (unlikely(!raw)) { printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); + jeb->gc_node = raw; spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); BUG(); } } + jeb->gc_node = raw; + D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw))); + if (!raw->next_in_ino) { /* Inode-less node. Clean marker, snapshot or something like that */ /* FIXME: If it's something that needs to be copied, including something @@ -243,13 +246,15 @@ int jffs2_garbage_collect_pass(struct jf up(&c->alloc_sem); goto eraseit_lock; } - - inum = jffs2_raw_ref_to_inum(raw); - D1(printk(KERN_DEBUG "Inode number is #%u\n", inum)); + + ic = jffs2_raw_ref_to_ic(raw); + + /* We need to hold the inocache */ + spin_lock(&c->inocache_lock); spin_unlock(&c->erase_completion_lock); - D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum)); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino)); /* Three possibilities: 1. Inode is already in-core. We must iget it and do proper @@ -259,11 +264,6 @@ int jffs2_garbage_collect_pass(struct jf 3. Inode is not in-core, node is not pristine. We must iget() and take the slow path. */ - spin_lock(&c->inocache_lock); - ic = jffs2_get_ino_cache(c, inum); - - /* This should never fail unless I'm particularly stupid. - So we don't check before dereferencing it */ switch(ic->state) { case INO_STATE_CHECKEDABSENT: @@ -274,29 +274,29 @@ int jffs2_garbage_collect_pass(struct jf if (ref_flags(raw) == REF_PRISTINE) ic->state = INO_STATE_GC; else { - D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n", - inum)); + D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", + ic->ino)); } break; case INO_STATE_PRESENT: - case INO_STATE_UNCHECKED: - /* It's in-core or hasn't been checked. GC must iget() it. */ + /* It's in-core. GC must iget() it. */ break; + case INO_STATE_UNCHECKED: case INO_STATE_CHECKING: + case INO_STATE_GC: /* Should never happen. We should have finished checking - by the time we actually start doing any GC. */ + by the time we actually start doing any GC, and since + we're holding the alloc_sem, no other garbage collection + can happen. + */ + printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", + ic->ino, ic->state); + up(&c->alloc_sem); + spin_unlock(&c->inocache_lock); BUG(); - - case INO_STATE_GC: - /* Should never happen. We are holding the alloc_sem, - no other garbage collection can happen. Note that we - do depend on this later when deciding to do a simple - node copy */ - BUG(); - case INO_STATE_READING: /* Someone's currently trying to read it. We must wait for them to finish and then go through the full iget() route @@ -306,7 +306,7 @@ int jffs2_garbage_collect_pass(struct jf up(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", - inum, ic->state)); + ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); /* And because we dropped the alloc_sem we must start again from the beginning. Ponder chance of livelock here -- we're returning success @@ -319,26 +319,37 @@ int jffs2_garbage_collect_pass(struct jf A: Small enough that I don't care :) */ return 0; - } - spin_unlock(&c->inocache_lock); - /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the node intact, and we don't have to muck about with the fragtree etc. because we know it's not in-core. If it _was_ in-core, we go through all the iget() crap anyway */ if (ic->state == INO_STATE_GC) { + spin_unlock(&c->inocache_lock); + ret = jffs2_garbage_collect_pristine(c, ic, raw); - jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); - if (ret != -EBADFD) + spin_lock(&c->inocache_lock); + ic->state = INO_STATE_CHECKEDABSENT; + wake_up(&c->inocache_wq); + + if (ret != -EBADFD) { + spin_unlock(&c->inocache_lock); goto release_sem; + } - /* Fall through if it wanted us to */ + /* Fall through if it wanted us to, with inocache_lock held */ } + /* FIXME: We still have the inocache_lock held. This is ugly. + It's done to prevent the fairly unlikely race where the + gcblock is entirely obsoleted by the final close of a file + which had the only valid nodes in the block, followed by + erasure, followed by freeing of the ic because the erased + block(s) held _all_ the nodes of that inode.... never been + seen but it's vaguely possible. */ ret = jffs2_garbage_collect_live(c, jeb, raw, ic); release_sem: @@ -362,9 +373,8 @@ int jffs2_garbage_collect_pass(struct jf return ret; } - static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic) + struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic) { struct jffs2_inode_info *f; struct jffs2_node_frag *frag; @@ -374,13 +384,65 @@ static int jffs2_garbage_collect_live(st struct inode *inode; int ret = 0; - inode = iget(OFNI_BS_2SFFJ(c), ic->ino); + if (!ic->nlink) { + int inum = ic->ino; + + spin_unlock(&c->inocache_lock); + + /* The inode has zero nlink but its nodes weren't yet marked + obsolete. This has to be because we're still waiting for + the final (close() and) iput() to happen. + + There's a possibility that the final iput() could have + happened while we were contemplating. In order to ensure + that we don't cause a new read_inode() (which would fail) + for the inode in question, we use ilookup() in this case + instead of iget(). + + The nlink can't _become_ zero at this point because we're + holding the alloc_sem, and jffs2_do_unlink() would also + need that while decrementing nlink on any inode. + */ + inode = ilookup(OFNI_BS_2SFFJ(c), inum); + if (!inode) { + D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", + inum)); + + spin_lock(&c->inocache_lock); + ic = jffs2_get_ino_cache(c, inum); + if (!ic) { + D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); + spin_unlock(&c->inocache_lock); + return 0; + } + if (ic->state != INO_STATE_CHECKEDABSENT) { + /* Wait for progress. Don't just loop */ + D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", + ic->ino, ic->state)); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + } else { + spin_unlock(&c->inocache_lock); + } + + return 0; + } + } else { + spin_unlock(&c->inocache_lock); + + /* Inode has links to it still; they're not going away because + jffs2_do_unlink() would need the alloc_sem and we have it. + Just iget() it, and if read_inode() is necessary that's OK. + */ + inode = iget(OFNI_BS_2SFFJ(c), ic->ino); + if (!inode) + return -ENOMEM; + } if (is_bad_inode(inode)) { - printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino); + printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d, state %d\n", + ic->ino, ic->nlink, ic->state); /* NB. This will happen again. We need to do something appropriate here. */ - up(&c->alloc_sem); - iput(inode); - return -EIO; + ret = -EIO; + goto put_out; } f = JFFS2_INODE_INFO(inode); @@ -389,11 +451,21 @@ static int jffs2_garbage_collect_live(st /* Now we have the lock for this inode. Check that it's still the one at the head of the list. */ + spin_lock(&c->erase_completion_lock); + + if (c->gcblock != jeb) { + spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n")); + goto upnout; + } if (ref_obsolete(raw)) { + spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n")); /* They'll call again */ goto upnout; } + spin_unlock(&c->erase_completion_lock); + /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */ if (f->metadata && f->metadata->raw == raw) { fn = f->metadata; @@ -406,30 +478,6 @@ static int jffs2_garbage_collect_live(st if (frag->node && frag->node->raw == raw) { fn = frag->node; end = frag->ofs + frag->size; -#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */ - if (!nrfrags && 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(raw), fn->frags); - mark_ref_normal(raw); - } - /* 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) { - printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n", - ref_offset(raw)); - mark_ref_normal(raw); - } - - if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) { - 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(raw), frag->ofs, frag->ofs+frag->size); - mark_ref_normal(raw); - } - } -#endif if (!nrfrags++) start = frag->ofs; if (nrfrags == frag->node->frags) @@ -478,6 +526,7 @@ static int jffs2_garbage_collect_live(st } upnout: up(&f->sem); + put_out: iput(inode); return ret; @@ -492,29 +541,32 @@ static int jffs2_garbage_collect_pristin size_t retlen; int ret; uint32_t phys_ofs, alloclen; - uint32_t crc; + uint32_t crc, rawlen; + int retried = 0; D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); + rawlen = ref_totlen(c, c->gcblock, raw); + /* Ask for a small amount of space (or the totlen if smaller) because we don't want to force wastage of the end of a block if splitting would work. */ - ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen), - &phys_ofs, &alloclen); + ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, + rawlen), &phys_ofs, &alloclen); if (ret) return ret; - if (alloclen < raw->totlen) { + if (alloclen < rawlen) { /* Doesn't fit untouched. We'll go the old route and split it */ return -EBADFD; } - node = kmalloc(raw->totlen, GFP_KERNEL); + node = kmalloc(rawlen, GFP_KERNEL); if (!node) return -ENOMEM; - ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node); - if (!ret && retlen != raw->totlen) + ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); + if (!ret && retlen != rawlen) ret = -EIO; if (ret) goto out_node; @@ -573,16 +625,18 @@ static int jffs2_garbage_collect_pristin ret = -ENOMEM; goto out_node; } - nraw->flash_offset = phys_ofs; - nraw->totlen = raw->totlen; - nraw->next_phys = NULL; /* OK, all the CRCs are good; this node can just be copied as-is. */ + retry: + nraw->flash_offset = phys_ofs; + nraw->__totlen = rawlen; + nraw->next_phys = NULL; - ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node); - if (ret || (retlen != raw->totlen)) { + ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); + + if (ret || (retlen != rawlen)) { printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", - raw->totlen, phys_ofs, ret, retlen); + rawlen, phys_ofs, ret, retlen); if (retlen) { /* Doesn't belong to any inode */ nraw->next_in_ino = NULL; @@ -592,8 +646,34 @@ static int jffs2_garbage_collect_pristin jffs2_mark_node_obsolete(c, nraw); } else { 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(raw); + jffs2_free_raw_node_ref(nraw); } + 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]; + + retried = 1; + + D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n")); + + ACCT_SANITY_CHECK(c,jeb); + D1(ACCT_PARANOIA_CHECK(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)); + + goto retry; + } + D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); + jffs2_free_raw_node_ref(nraw); + } + if (!ret) ret = -EIO; goto out_node; @@ -684,7 +764,7 @@ static int jffs2_garbage_collect_metadat ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); - new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, NULL); + new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); @@ -728,7 +808,7 @@ static int jffs2_garbage_collect_dirent( sizeof(rd)+rd.nsize, ret); return ret; } - new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, NULL); + new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC); if (IS_ERR(new_fd)) { printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd)); @@ -750,13 +830,17 @@ static int jffs2_garbage_collect_deletio delete a 'real' dirent with the same name that's still somewhere else on the flash. */ if (!jffs2_can_mark_obsolete(c)) { - struct jffs2_raw_dirent rd; + struct jffs2_raw_dirent *rd; struct jffs2_raw_node_ref *raw; int ret; size_t retlen; int name_len = strlen(fd->name); uint32_t name_crc = crc32(0, fd->name, name_len); - char *namebuf = NULL; + uint32_t rawlen = ref_totlen(c, jeb, fd->raw); + + rd = kmalloc(rawlen, GFP_KERNEL); + if (!rd) + return -ENOMEM; /* Prevent the erase code from nicking the obsolete node refs while we're looking at them. I really don't like this extra lock but @@ -764,91 +848,66 @@ static int jffs2_garbage_collect_deletio down(&c->erase_free_sem); for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { + /* We only care about obsolete ones */ if (!(ref_obsolete(raw))) continue; + /* Any dirent with the same name is going to have the same length... */ + if (ref_totlen(c, NULL, raw) != rawlen) + continue; + /* 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))) continue; - /* This is an obsolete node belonging to the same directory */ - ret = jffs2_flash_read(c, ref_offset(raw), sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd); + D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); + + /* This is an obsolete node belonging to the same directory, and it's of the right + length. We need to take a closer look...*/ + ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); if (ret) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, ref_offset(raw)); + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw)); /* If we can't read it, we don't need to continue to obsolete it. Continue */ continue; } - if (retlen != sizeof(struct jffs2_unknown_node)) { + if (retlen != rawlen) { printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n", - retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw)); - continue; - } - if (je16_to_cpu(rd.nodetype) != JFFS2_NODETYPE_DIRENT || - PAD(je32_to_cpu(rd.totlen)) != PAD(sizeof(rd) + name_len)) - continue; - - /* OK, it's a dirent node, it's the right length. We have to take a - closer look at it... */ - ret = jffs2_flash_read(c, ref_offset(raw), sizeof(rd), &retlen, (char *)&rd); - if (ret) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, ref_offset(raw)); - /* If we can't read it, we don't need to continune to obsolete it. Continue */ - continue; - } - if (retlen != sizeof(rd)) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n", - retlen, sizeof(rd), ref_offset(raw)); + retlen, rawlen, ref_offset(raw)); continue; } - /* If the name CRC doesn't match, skip */ - if (je32_to_cpu(rd.name_crc) != name_crc) + if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) continue; + + /* If the name CRC doesn't match, skip */ + if (je32_to_cpu(rd->name_crc) != name_crc) + continue; + /* If the name length doesn't match, or it's another deletion dirent, skip */ - if (rd.nsize != name_len || !je32_to_cpu(rd.ino)) + if (rd->nsize != name_len || !je32_to_cpu(rd->ino)) continue; /* OK, check the actual name now */ - if (!namebuf) { - namebuf = kmalloc(name_len + 1, GFP_KERNEL); - if (!namebuf) { - up(&c->erase_free_sem); - return -ENOMEM; - } - } - /* We read the extra byte before it so it's a word-aligned read */ - ret = jffs2_flash_read(c, (ref_offset(raw))+sizeof(rd)-1, name_len+1, &retlen, namebuf); - if (ret) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, ref_offset(raw)); - /* If we can't read it, we don't need to continune to obsolete it. Continue */ - continue; - } - if (retlen != name_len+1) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n", - retlen, name_len+1, ref_offset(raw)); - continue; - } - if (memcmp(namebuf+1, fd->name, name_len)) + if (memcmp(rd->name, fd->name, name_len)) continue; /* OK. The name really does match. There really is still an older node on the flash which our deletion dirent obsoletes. So we have to write out a new deletion dirent to replace it */ - - if (namebuf) - kfree(namebuf); + up(&c->erase_free_sem); - up(&c->erase_free_sem); + D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", + ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); + kfree(rd); + return jffs2_garbage_collect_dirent(c, jeb, f, fd); } up(&c->erase_free_sem); - - if (namebuf) - kfree(namebuf); + kfree(rd); } /* No need for it any more. Just mark it obsolete and remove it from the list */ @@ -951,7 +1010,7 @@ static int jffs2_garbage_collect_hole(st sizeof(ri), ret); return ret; } - new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL); + new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn)); @@ -979,6 +1038,9 @@ static int jffs2_garbage_collect_hole(st je32_to_cpu(ri.ino)); }); + /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */ + mark_ref_normal(new_fn->raw); + for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); frag; frag = frag_next(frag)) { if (frag->ofs > fn->size + fn->ofs) @@ -1010,7 +1072,7 @@ static int jffs2_garbage_collect_dnode(s { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; - uint32_t alloclen, phys_ofs, offset, orig_end; + uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; struct page *pg; @@ -1023,29 +1085,129 @@ static int jffs2_garbage_collect_dnode(s f->inocache->ino, start, end)); orig_end = end; + orig_start = start; - /* If we're looking at the last node in the block we're - garbage-collecting, we allow ourselves to merge as if the - block was already erasing. We're likely to be GC'ing a - partial page, and the next block we GC is likely to have - the other half of this page right at the beginning, which - means we'd expand it _then_, as nr_erasing_blocks would have - increased since we checked, and in doing so would obsolete - the partial node which we'd have written here. Meaning that - the GC would churn and churn, and just leave dirty blocks in - it's wake. - */ - if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) { - /* Shitloads of space */ - /* FIXME: Integrate this properly with GC calculations */ - start &= ~(PAGE_CACHE_SIZE-1); - end = min_t(uint32_t, start + PAGE_CACHE_SIZE, JFFS2_F_I_SIZE(f)); - D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n", - start, end)); - if (end < orig_end) { - printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end); - end = orig_end; + if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) { + /* Attempt to do some merging. But only expand to cover logically + adjacent frags if the block containing them is already considered + to be dirty. Otherwise we end up with GC just going round in + circles dirtying the nodes it already wrote out, especially + on NAND where we have small eraseblocks and hence a much higher + chance of nodes having to be split to cross boundaries. */ + + struct jffs2_node_frag *frag; + uint32_t min, max; + + min = start & ~(PAGE_CACHE_SIZE-1); + max = min + PAGE_CACHE_SIZE; + + frag = jffs2_lookup_node_frag(&f->fragtree, start); + + /* BUG_ON(!frag) but that'll happen anyway... */ + + BUG_ON(frag->ofs != start); + + /* First grow down... */ + while((frag = frag_prev(frag)) && frag->ofs >= min) { + + /* If the previous frag doesn't even reach the beginning, there's + excessive fragmentation. Just merge. */ + if (frag->ofs > min) { + D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n", + frag->ofs, frag->ofs+frag->size)); + start = frag->ofs; + continue; + } + /* OK. This frag holds the first byte of the page. */ + if (!frag->node || !frag->node->raw) { + D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n", + frag->ofs, frag->ofs+frag->size)); + break; + } else { + + /* OK, it's a frag which extends to the beginning of the page. Does it live + in a block which is still considered clean? If so, don't obsolete it. + If not, cover it anyway. */ + + struct jffs2_raw_node_ref *raw = frag->node->raw; + struct jffs2_eraseblock *jeb; + + jeb = &c->blocks[raw->flash_offset / c->sector_size]; + + if (jeb == c->gcblock) { + D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n", + frag->ofs, frag->ofs+frag->size, ref_offset(raw))); + start = frag->ofs; + break; + } + if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { + D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n", + frag->ofs, frag->ofs+frag->size, jeb->offset)); + break; + } + + D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n", + frag->ofs, frag->ofs+frag->size, jeb->offset)); + start = frag->ofs; + break; + } } + + /* ... then up */ + + /* Find last frag which is actually part of the node we're to GC. */ + frag = jffs2_lookup_node_frag(&f->fragtree, end-1); + + while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) { + + /* If the previous frag doesn't even reach the beginning, there's lots + of fragmentation. Just merge. */ + if (frag->ofs+frag->size < max) { + D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n", + frag->ofs, frag->ofs+frag->size)); + end = frag->ofs + frag->size; + continue; + } + + if (!frag->node || !frag->node->raw) { + D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n", + frag->ofs, frag->ofs+frag->size)); + break; + } else { + + /* OK, it's a frag which extends to the beginning of the page. Does it live + in a block which is still considered clean? If so, don't obsolete it. + If not, cover it anyway. */ + + struct jffs2_raw_node_ref *raw = frag->node->raw; + struct jffs2_eraseblock *jeb; + + jeb = &c->blocks[raw->flash_offset / c->sector_size]; + + if (jeb == c->gcblock) { + D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n", + frag->ofs, frag->ofs+frag->size, ref_offset(raw))); + end = frag->ofs + frag->size; + break; + } + if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { + D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n", + frag->ofs, frag->ofs+frag->size, jeb->offset)); + break; + } + + D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n", + frag->ofs, frag->ofs+frag->size, jeb->offset)); + end = frag->ofs + frag->size; + break; + } + } + 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)); + BUG_ON(end < orig_end); + BUG_ON(start > orig_start); } /* First, use readpage() to read the appropriate page into the page cache */ @@ -1065,7 +1227,6 @@ static int jffs2_garbage_collect_dnode(s return PTR_ERR(pg); } pg_ptr = (char *)kmap(pg); - comprbuf = kmalloc(end - start, GFP_KERNEL); offset = start; while(offset < orig_end) { @@ -1085,14 +1246,8 @@ static int jffs2_garbage_collect_dnode(s writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1)); - if (comprbuf) { - comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen); - } - if (comprtype) { - writebuf = comprbuf; - } else { - datalen = cdatalen; - } + comprtype = jffs2_compress(writebuf, &comprbuf, &datalen, &cdatalen); + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen); @@ -1114,7 +1269,9 @@ static int jffs2_garbage_collect_dnode(s ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen)); - new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, NULL); + new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); + + jffs2_free_comprbuf(comprbuf, writebuf); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); @@ -1129,7 +1286,6 @@ static int jffs2_garbage_collect_dnode(s f->metadata = NULL; } } - if (comprbuf) kfree(comprbuf); kunmap(pg); /* XXX: Does the page get freed automatically? */
--- a/packages/fs/jffs2/current/src/jffs2port.h +++ b/packages/fs/jffs2/current/src/jffs2port.h @@ -1,7 +1,7 @@ #ifndef __LINUX_JFFS2PORT_H__ #define __LINUX_JFFS2PORT_H__ -/* $Id: jffs2port.h,v 1.3 2003/01/21 18:13:22 dwmw2 Exp $ */ +/* $Id: jffs2port.h,v 1.4 2003/11/20 16:41:58 dwmw2 Exp $ */ #include <pkgconf/system.h> #include <pkgconf/hal.h> @@ -189,7 +189,7 @@ struct jffs2_eraseblock; 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); 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); -int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); +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);
--- a/packages/fs/jffs2/current/src/nodelist.c +++ b/packages/fs/jffs2/current/src/nodelist.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.79 2003/04/08 08:20:01 dwmw2 Exp $ + * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $ * */ @@ -58,7 +58,7 @@ void jffs2_add_fd_to_list(struct jffs2_s /* Put a new tmp_dnode_info into the list, keeping the list in order of increasing version */ -void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) +static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) { struct jffs2_tmp_dnode_info **prev = list; @@ -133,7 +133,9 @@ int jffs2_get_inode_nodes(struct jffs2_s cond_resched(); /* FIXME: point() */ - err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node); + 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; @@ -141,7 +143,7 @@ int jffs2_get_inode_nodes(struct jffs2_s /* Check we've managed to read at least the common node header */ - if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) { + 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; @@ -246,7 +248,7 @@ int jffs2_get_inode_nodes(struct jffs2_s /* If we've never checked the CRCs on this node, check them now. */ if (ref_flags(ref) == REF_UNCHECKED) { - uint32_t crc; + uint32_t crc, len; struct jffs2_eraseblock *jeb; crc = crc32(0, &node, sizeof(node.i)-8); @@ -321,10 +323,12 @@ int jffs2_get_inode_nodes(struct jffs2_s /* 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]; - jeb->used_size += ref->totlen; - jeb->unchecked_size -= ref->totlen; - c->used_size += ref->totlen; - c->unchecked_size -= ref->totlen; + 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 @@ -377,6 +381,7 @@ int jffs2_get_inode_nodes(struct jffs2_s default: if (ref_flags(ref) == REF_UNCHECKED) { struct jffs2_eraseblock *jeb; + uint32_t len; printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); @@ -384,10 +389,12 @@ int jffs2_get_inode_nodes(struct jffs2_s /* 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]; - jeb->used_size += ref->totlen; - jeb->unchecked_size -= ref->totlen; - c->used_size += ref->totlen; - c->unchecked_size -= ref->totlen; + len = ref_totlen(c, jeb, ref); + + jeb->used_size += len; + jeb->unchecked_size -= len; + c->used_size += len; + c->unchecked_size -= len; mark_ref_normal(ref); spin_unlock(&c->erase_completion_lock); @@ -631,6 +638,8 @@ void jffs2_kill_fragtree(struct rb_root jffs2_free_node_frag(frag); frag = parent; + + cond_resched(); } }
--- a/packages/fs/jffs2/current/src/nodelist.h +++ b/packages/fs/jffs2/current/src/nodelist.h @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.94 2003/07/03 10:35:45 dwmw2 Exp $ + * $Id: nodelist.h,v 1.113 2003/11/20 16:40:14 dwmw2 Exp $ * */ @@ -59,13 +59,12 @@ struct jffs2_raw_node_ref word so you know when you've got there :) */ struct jffs2_raw_node_ref *next_phys; uint32_t flash_offset; - uint32_t totlen; - + uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ +}; + /* flash_offset & 3 always has to be zero, because nodes are always aligned at 4 bytes. So we have a couple of extra bits - to play with. So we set the least significant bit to 1 to - signify that the node is obsoleted by later nodes. - */ + to play with, which indicate the node's status; see below: */ #define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ #define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */ #define REF_PRISTINE 2 /* Completely clean. GC without looking */ @@ -74,7 +73,6 @@ struct jffs2_raw_node_ref #define ref_offset(ref) ((ref)->flash_offset & ~3) #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 @@ -201,10 +199,11 @@ struct jffs2_eraseblock }; #define ACCT_SANITY_CHECK(c, jeb) do { \ - if (jeb->used_size + jeb->dirty_size + jeb->free_size + jeb->wasted_size + jeb->unchecked_size != c->sector_size) { \ - printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \ + 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", \ - jeb->free_size, jeb->dirty_size, jeb->used_size, jeb->wasted_size, jeb->unchecked_size, c->sector_size); \ + ___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) { \ @@ -215,15 +214,46 @@ struct jffs2_eraseblock } \ } 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 += ref2->totlen; \ + my_unchecked_size += ref_totlen(c, jeb, ref2); \ else if (!ref_obsolete(ref2)) \ - my_used_size += ref2->totlen; \ + my_used_size += ref_totlen(c, jeb, ref2); \ + if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \ + 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) { \ @@ -236,17 +266,61 @@ struct jffs2_eraseblock } \ } while(0) +/* Calculate totlen from surrounding nodes or eraseblock */ +static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref) +{ + uint32_t ref_end; + + if (ref->next_phys) + ref_end = ref_offset(ref->next_phys); + else { + if (!jeb) + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + + /* Last node in block. Use free_space */ + BUG_ON(ref != jeb->last_node); + ref_end = jeb->offset + c->sector_size - jeb->free_size; + } + return ref_end - ref_offset(ref); +} + +static inline uint32_t ref_totlen(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref) +{ + uint32_t ret; + + D1(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(); + }) + +#if 1 + ret = ref->__totlen; +#else + /* This doesn't actually work yet */ + ret = __ref_totlen(c, jeb, ref); + if (ret != ref->__totlen) { + printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", + ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, + ret, ref->__totlen); + if (!jeb) + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + paranoia_failed_dump(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 */ - -#define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */ -#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */ -#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE) /* ... allow a normal filesystem deletion */ -#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */ -#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */ -#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */ - +#define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */ /* How much dirty space before it goes on the very_dirty_list */ #define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) @@ -256,13 +330,13 @@ struct jffs2_eraseblock #define PAD(x) (((x)+3)&~3) -static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw) +static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw) { while(raw->next_in_ino) { raw = raw->next_in_ino; } - return ((struct jffs2_inode_cache *)raw)->ino; + return ((struct jffs2_inode_cache *)raw); } static inline struct jffs2_node_frag *frag_first(struct rb_root *root) @@ -286,7 +360,6 @@ static inline struct jffs2_node_frag *fr /* nodelist.c */ D1(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); -void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list); int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, uint32_t *highest_version, uint32_t *latest_mctime, @@ -314,8 +387,9 @@ void jffs2_dump_block_lists(struct jffs2 /* 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); -struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, uint32_t *writelen); -struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, uint32_t *writelen); + +struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode); +struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode); int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, unsigned char *buf, uint32_t offset, uint32_t writelen, uint32_t *retlen); @@ -362,10 +436,10 @@ int jffs2_read_inode_range(struct jffs2_ unsigned char *buf, uint32_t offset, uint32_t len); char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f); - /* compr.c */ -unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, +unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen); +void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); @@ -378,12 +452,13 @@ int jffs2_do_mount_fs(struct jffs2_sb_in /* 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); +void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); void jffs2_erase_pending_trigger(struct jffs2_sb_info *c); #ifdef CONFIG_JFFS2_FS_NAND /* wbuf.c */ -int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad); +int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); +int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
--- a/packages/fs/jffs2/current/src/nodemgmt.c +++ b/packages/fs/jffs2/current/src/nodemgmt.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.95 2003/06/30 10:58:57 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.106 2003/11/03 17:33:54 dwmw2 Exp $ * */ @@ -43,13 +43,10 @@ static int jffs2_do_reserve_space(struct int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio) { int ret = -EAGAIN; - int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE; + int blocksneeded = c->resv_blocks_write; /* align it */ minsize = PAD(minsize); - if (prio == ALLOC_DELETION) - blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION; - D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); down(&c->alloc_sem); @@ -63,8 +60,6 @@ int jffs2_reserve_space(struct jffs2_sb_ int ret; uint32_t dirty, avail; - up(&c->alloc_sem); - /* calculate real dirty size * dirty_size contains blocks on erase_pending_list * those blocks are counted in c->nr_erasing_blocks. @@ -78,10 +73,16 @@ int jffs2_reserve_space(struct jffs2_sb_ * of nodes. */ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; - if (dirty < c->sector_size) { - D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < sector size 0x%08x, returning -ENOSPC\n", + 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"); + break; + } + D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", dirty, c->unchecked_size, c->sector_size)); + spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); return -ENOSPC; } @@ -96,12 +97,20 @@ 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"); + break; + } + D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", avail, blocksneeded * c->sector_size)); spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); return -ENOSPC; } - + + up(&c->alloc_sem); + D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); @@ -161,9 +170,10 @@ static int jffs2_do_reserve_space(struct if (jffs2_wbuf_dirty(c)) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); - jffs2_flush_wbuf(c, 1); + jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); - /* We know nobody's going to have changed nextblock. Just continue */ + jeb = c->nextblock; + goto restart; } c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; @@ -199,8 +209,6 @@ static int jffs2_do_reserve_space(struct if (list_empty(&c->free_list)) { - DECLARE_WAITQUEUE(wait, current); - if (!c->nr_erasing_blocks && !list_empty(&c->erasable_list)) { struct jffs2_eraseblock *ejeb; @@ -219,7 +227,7 @@ static int jffs2_do_reserve_space(struct D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); /* c->nextblock is NULL, no update to c->nextblock allowed */ spin_unlock(&c->erase_completion_lock); - jffs2_flush_wbuf(c, 1); + jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); /* Have another go. It'll be on the erasable_list now */ return -EAGAIN; @@ -233,30 +241,12 @@ static int jffs2_do_reserve_space(struct list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); return -ENOSPC; } - /* Make sure this can't deadlock. Someone has to start the erases - of erase_pending blocks */ -#ifdef __ECOS - /* In eCos, we don't have a handy kernel thread doing the erases for - us. We do them ourselves right now. */ - jffs2_erase_pending_blocks(c); -#else - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&c->erase_wait, &wait); - D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", - c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no", - list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no")); - if (!list_empty(&c->erase_pending_list)) { - D1(printk(KERN_DEBUG "Triggering pending erases\n")); - jffs2_erase_pending_trigger(c); - } + spin_unlock(&c->erase_completion_lock); - schedule(); - remove_wait_queue(&c->erase_wait, &wait); + /* Don't wait for it; just erase one right now */ + jffs2_erase_pending_blocks(c, 1); spin_lock(&c->erase_completion_lock); - if (signal_pending(current)) { - return -EINTR; - } -#endif + /* An erase may have failed, decreasing the amount of free space available. So we must restart from the beginning */ @@ -311,9 +301,11 @@ static int jffs2_do_reserve_space(struct int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new) { struct jffs2_eraseblock *jeb; - uint32_t len = new->totlen; + uint32_t len; jeb = &c->blocks[new->flash_offset / c->sector_size]; + len = ref_totlen(c, jeb, new); + 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)) { @@ -347,7 +339,7 @@ int jffs2_add_physical_node_ref(struct j if (jffs2_wbuf_dirty(c)) { /* Flush the last write in the block if it's outstanding */ spin_unlock(&c->erase_completion_lock); - jffs2_flush_wbuf(c, 1); + jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); } @@ -370,6 +362,20 @@ void jffs2_complete_reservation(struct j up(&c->alloc_sem); } +static inline int on_list(struct list_head *obj, struct list_head *head) +{ + struct list_head *this; + + list_for_each(this, head) { + if (this == obj) { + D1(printk("%p is on list at %p\n", obj, head)); + return 1; + + } + } + return 0; +} + void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref) { struct jffs2_eraseblock *jeb; @@ -396,38 +402,53 @@ void jffs2_mark_node_obsolete(struct jff spin_lock(&c->erase_completion_lock); if (ref_flags(ref) == REF_UNCHECKED) { - D1(if (unlikely(jeb->unchecked_size < ref->totlen)) { + D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) { printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n", - ref->totlen, blocknr, ref->flash_offset, jeb->used_size); + ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); BUG(); }) - D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen)); - jeb->unchecked_size -= ref->totlen; - c->unchecked_size -= ref->totlen; + D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); + jeb->unchecked_size -= ref_totlen(c, jeb, ref); + c->unchecked_size -= ref_totlen(c, jeb, ref); } else { - D1(if (unlikely(jeb->used_size < ref->totlen)) { + D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) { printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", - ref->totlen, blocknr, ref->flash_offset, jeb->used_size); + 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)); - jeb->used_size -= ref->totlen; - c->used_size -= ref->totlen; + 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)) && jeb != c->nextblock) { + if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { D1(printk("Dirtying\n")); - addedsize = ref->totlen + jeb->wasted_size; - jeb->dirty_size += addedsize; - c->dirty_size += addedsize; - c->wasted_size -= jeb->wasted_size; - jeb->wasted_size = 0; + addedsize = ref_totlen(c, jeb, ref); + jeb->dirty_size += ref_totlen(c, jeb, ref); + c->dirty_size += ref_totlen(c, jeb, ref); + + /* Convert wasted space to dirty, if not a bad block */ + if (jeb->wasted_size) { + if (on_list(&jeb->list, &c->bad_used_list)) { + D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n", + jeb->offset)); + addedsize = 0; /* To fool the refiling code later */ + } else { + D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n", + jeb->wasted_size, jeb->offset)); + addedsize += jeb->wasted_size; + jeb->dirty_size += jeb->wasted_size; + c->dirty_size += jeb->wasted_size; + c->wasted_size -= jeb->wasted_size; + jeb->wasted_size = 0; + } + } } else { D1(printk("Wasting\n")); addedsize = 0; - jeb->wasted_size += ref->totlen; - c->wasted_size += ref->totlen; + jeb->wasted_size += ref_totlen(c, jeb, ref); + c->wasted_size += ref_totlen(c, jeb, ref); } ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; @@ -458,30 +479,6 @@ void jffs2_mark_node_obsolete(struct jff if (jffs2_wbuf_dirty(c)) { D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); -#if 0 /* This check was added to allow us to find places where we added nodes to the lists - after dropping the alloc_sem, and it did that just fine. But it also caused us to - lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise - have needed to. So I suspect it's outlived its usefulness. Thomas? */ - - /* We've changed the rules slightly. After - writing a node you now mustn't drop the - alloc_sem before you've finished all the - list management - this is so that when we - get here, we know that no other nodes have - been written, and the above check on wbuf - is valid - wbuf_len is nonzero IFF the node - which obsoletes this node is still in the - wbuf. - - So we BUG() if that new rule is broken, to - make sure we catch it and fix it. - */ - if (!down_trylock(&c->alloc_sem)) { - up(&c->alloc_sem); - printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n"); - BUG(); - } -#endif } else { if (jiffies & 127) { /* Most of the time, we just erase it immediately. Otherwise we @@ -506,7 +503,7 @@ void jffs2_mark_node_obsolete(struct jff D1(printk(KERN_DEBUG "...and adding to dirty_list\n")); list_add_tail(&jeb->list, &c->dirty_list); } else if (VERYDIRTY(c, jeb->dirty_size) && - !VERYDIRTY(c, jeb->dirty_size - ref->totlen)) { + !VERYDIRTY(c, jeb->dirty_size - addedsize)) { D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset)); list_del(&jeb->list); D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n")); @@ -533,12 +530,12 @@ void jffs2_mark_node_obsolete(struct jff printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); return; } - if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) { - printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen); + 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; } 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))); + 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; } /* XXX FIXME: This is ugly now */ @@ -569,7 +566,7 @@ void jffs2_dump_block_lists(struct jffs2 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 * JFFS2_RESERVED_BLOCKS_WRITE); + 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", @@ -672,7 +669,7 @@ void jffs2_dump_block_lists(struct jffs2 list_for_each(this, &c->erasable_pending_wbuf_list) { struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erase_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", + 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); } }
--- 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.5 2003/01/21 18:14:27 dwmw2 Exp $ + * $Id: os-ecos.h,v 1.8 2003/11/20 16:41:58 dwmw2 Exp $ * */ @@ -233,13 +233,14 @@ int jffs2_commit_write (struct inode *d_ #define jffs2_cleanmarker_oob(c) (0) #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) -#define jffs2_flush_wbuf(c, flag) do { ; } while(0) +#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; }) +#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) do { ; } while(0) #define jffs2_nand_flash_setup(c) (0) #define jffs2_nand_flash_cleanup(c) do {} while(0) #define jffs2_wbuf_dirty(c) (0) -#define jffs2_flash_writev jffs2_flash_writev +#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) #define jffs2_wbuf_timeout NULL #define jffs2_wbuf_process NULL #else @@ -255,4 +256,6 @@ static inline void jffs2_init_inode_info init_MUTEX_LOCKED(&f->sem); } +#define BUG_ON(x) do { if (unlikely(x)) BUG(); } while(0) + #endif /* __JFFS2_OS_ECOS_H__ */
--- a/packages/fs/jffs2/current/src/pushpull.h +++ b/packages/fs/jffs2/current/src/pushpull.h @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: pushpull.h,v 1.8 2002/05/20 14:56:38 dwmw2 Exp $ + * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $ * */
--- a/packages/fs/jffs2/current/src/read.c +++ b/packages/fs/jffs2/current/src/read.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.32 2003/07/15 10:11:37 dwmw2 Exp $ + * $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $ * */ @@ -16,6 +16,7 @@ #include <linux/crc32.h> #include <linux/pagemap.h> #include <linux/mtd/mtd.h> +#include <linux/compiler.h> #include "nodelist.h" int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
--- a/packages/fs/jffs2/current/src/readinode.c +++ b/packages/fs/jffs2/current/src/readinode.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.106 2003/05/14 06:53:26 dwmw2 Exp $ + * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $ * */ @@ -56,6 +56,66 @@ void jffs2_print_frag_list(struct jffs2_ printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); } } + +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) @@ -130,6 +190,11 @@ int jffs2_add_full_dnode_to_inode(struct 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; } @@ -384,6 +449,7 @@ int jffs2_do_read_inode(struct jffs2_sb_ } } spin_unlock(&c->inocache_lock); + if (!f->inocache && ino == 1) { /* Special case - no root inode on medium */ f->inocache = jffs2_alloc_inode_cache(); @@ -460,7 +526,7 @@ static int jffs2_do_read_inode_internal( fn = tn->fn; if (f->metadata) { - if (tn->version > mdata_ver) { + 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); @@ -468,10 +534,13 @@ static int jffs2_do_read_inode_internal( mdata_ver = 0; } else { - D1(printk(KERN_DEBUG "Er. New metadata at 0x%08x with ver %d is actually older than previous %d\n", - ref_offset(f->metadata->raw), tn->version, mdata_ver)); + /* 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; } } @@ -488,6 +557,8 @@ static int jffs2_do_read_inode_internal( 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) { @@ -594,24 +665,10 @@ static int jffs2_do_read_inode_internal( void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { struct jffs2_full_dirent *fd, *fds; - /* I don't think we care about the potential race due to reading this - without f->sem. It can never get undeleted. */ - int deleted = f->inocache && !f->inocache->nlink; - - /* If it's a deleted inode, grab the alloc_sem. This prevents - jffs2_garbage_collect_pass() from deciding that it wants to - garbage collect one of the nodes we're just about to mark - obsolete -- by the time we drop alloc_sem and return, all - the nodes are marked obsolete, and jffs2_g_c_pass() won't - call iget() for the inode in question. - - We also used to do this to keep the temporary BUG() in - jffs2_mark_node_obsolete() from triggering. - */ - if(deleted) - down(&c->alloc_sem); + int deleted; down(&f->sem); + deleted = f->inocache && !f->inocache->nlink; if (f->metadata) { if (deleted) @@ -633,7 +690,4 @@ void jffs2_do_clear_inode(struct jffs2_s jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); up(&f->sem); - - if(deleted) - up(&c->alloc_sem); }
--- a/packages/fs/jffs2/current/src/scan.c +++ b/packages/fs/jffs2/current/src/scan.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.101 2003/06/30 10:58:57 dwmw2 Exp $ + * $Id: scan.c,v 1.106 2003/10/28 17:01:13 dwmw2 Exp $ * */ #include <linux/kernel.h> @@ -116,7 +116,7 @@ int jffs2_scan_medium(struct jffs2_sb_in ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size); if (ret < 0) - return ret; + goto out; ACCT_PARANOIA_CHECK(jeb); @@ -240,17 +240,20 @@ int jffs2_scan_medium(struct jffs2_sb_in if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks); - return -EIO; + ret = -EIO; + goto out; } jffs2_erase_pending_trigger(c); } + ret = 0; + out: if (buf_size) kfree(flashbuf); #ifndef __ECOS else c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); #endif - return 0; + return ret; } static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf, @@ -551,7 +554,7 @@ static int jffs2_scan_eraseblock (struct marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = ofs | REF_NORMAL; - marker_ref->totlen = c->cleanmarker_size; + marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; USED_SPACE(PAD(c->cleanmarker_size)); @@ -631,6 +634,9 @@ static struct jffs2_inode_cache *jffs2_s if (ic) return ic; + if (ino > c->highest_ino) + c->highest_ino = ino; + ic = jffs2_alloc_inode_cache(); if (!ic) { printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n"); @@ -642,7 +648,7 @@ static struct jffs2_inode_cache *jffs2_s ic->nodes = (void *)ic; jffs2_add_ino_cache(c, ic); if (ino == 1) - ic->nlink=1; + ic->nlink = 1; return ic; } @@ -682,6 +688,7 @@ static int jffs2_scan_inode_node(struct ofs, je32_to_cpu(ri->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen))); + jffs2_free_raw_node_ref(raw); return 0; } ic = jffs2_scan_make_ino_cache(c, ino); @@ -694,7 +701,7 @@ static int jffs2_scan_inode_node(struct /* Wheee. It worked */ raw->flash_offset = ofs | REF_UNCHECKED; - raw->totlen = PAD(je32_to_cpu(ri->totlen)); + raw->__totlen = PAD(je32_to_cpu(ri->totlen)); raw->next_phys = NULL; raw->next_in_ino = ic->nodes; @@ -771,7 +778,7 @@ static int jffs2_scan_dirent_node(struct return -ENOMEM; } - raw->totlen = PAD(je32_to_cpu(rd->totlen)); + raw->__totlen = PAD(je32_to_cpu(rd->totlen)); raw->flash_offset = ofs | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes;
--- a/packages/fs/jffs2/current/src/write.c +++ b/packages/fs/jffs2/current/src/write.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001, 2002 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@cambridge.redhat.com> + * Created by David Woodhouse <dwmw2@redhat.com> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.65 2003/01/21 18:11:29 dwmw2 Exp $ + * $Id: write.c,v 1.77 2003/11/18 21:14:02 dwmw2 Exp $ * */ @@ -86,7 +86,7 @@ static void writecheck(struct jffs2_sb_i /* 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 */ -struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, uint32_t *writelen) +struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode) { struct jffs2_raw_node_ref *raw; @@ -94,6 +94,7 @@ struct jffs2_full_dnode *jffs2_write_dno size_t retlen; struct iovec vecs[2]; int ret; + int retried = 0; unsigned long cnt = 2; D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { @@ -120,24 +121,28 @@ struct jffs2_full_dnode *jffs2_write_dno jffs2_free_raw_node_ref(raw); return ERR_PTR(-ENOMEM); } - raw->flash_offset = flash_ofs; - raw->totlen = PAD(sizeof(*ri)+datalen); - raw->next_phys = NULL; fn->ofs = je32_to_cpu(ri->offset); fn->size = je32_to_cpu(ri->dsize); fn->frags = 0; - fn->raw = raw; /* check number of valid vecs */ if (!datalen || !data) cnt = 1; + retry: + fn->raw = raw; - ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen); - + raw->flash_offset = flash_ofs; + raw->__totlen = PAD(sizeof(*ri)+datalen); + raw->next_phys = NULL; + + ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, + (alloc_mode==ALLOC_GC)?0:f->inocache->ino); + if (ret || (retlen != sizeof(*ri) + datalen)) { printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*ri)+datalen, flash_ofs, ret, retlen); + /* Mark the space as dirtied */ if (retlen) { /* Doesn't belong to any inode */ @@ -155,11 +160,42 @@ struct jffs2_full_dnode *jffs2_write_dno printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); jffs2_free_raw_node_ref(raw); } + if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) { + /* Try to reallocate space and retry */ + uint32_t dummy; + struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; + retried = 1; + + D1(printk(KERN_DEBUG "Retrying failed write.\n")); + + ACCT_SANITY_CHECK(c,jeb); + D1(ACCT_PARANOIA_CHECK(jeb)); + + if (alloc_mode == ALLOC_GC) { + ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy); + } else { + /* Locking pain */ + up(&f->sem); + jffs2_complete_reservation(c); + + ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode); + down(&f->sem); + } + + 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)); + + goto retry; + } + D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); + jffs2_free_raw_node_ref(raw); + } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dnode(fn); - if (writelen) - *writelen = retlen; return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ @@ -184,19 +220,21 @@ struct jffs2_full_dnode *jffs2_write_dno flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc), je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); - if (writelen) - *writelen = retlen; - f->inocache->nodes = raw; + if (retried) { + ACCT_SANITY_CHECK(c,NULL); + } + return fn; } -struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, uint32_t *writelen) +struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; size_t retlen; struct iovec vecs[2]; + int retried = 0; int ret; D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", @@ -225,11 +263,6 @@ struct jffs2_full_dirent *jffs2_write_di jffs2_free_raw_node_ref(raw); return ERR_PTR(-ENOMEM); } - raw->flash_offset = flash_ofs; - raw->totlen = PAD(sizeof(*rd)+namelen); - raw->next_in_ino = f->inocache->nodes; - f->inocache->nodes = raw; - raw->next_phys = NULL; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); @@ -237,14 +270,22 @@ struct jffs2_full_dirent *jffs2_write_di fd->type = rd->type; memcpy(fd->name, name, namelen); fd->name[namelen]=0; + + retry: fd->raw = raw; - ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen); + raw->flash_offset = flash_ofs; + raw->__totlen = PAD(sizeof(*rd)+namelen); + raw->next_phys = NULL; + + ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, + (alloc_mode==ALLOC_GC)?0:fd->ino); if (ret || (retlen != sizeof(*rd) + namelen)) { printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*rd)+namelen, flash_ofs, ret, retlen); /* Mark the space as dirtied */ if (retlen) { + raw->next_in_ino = NULL; raw->flash_offset |= REF_OBSOLETE; jffs2_add_physical_node_ref(c, raw); jffs2_mark_node_obsolete(c, raw); @@ -252,20 +293,53 @@ struct jffs2_full_dirent *jffs2_write_di printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); jffs2_free_raw_node_ref(raw); } + if (!retried && (raw = jffs2_alloc_raw_node_ref())) { + /* Try to reallocate space and retry */ + uint32_t dummy; + struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; + retried = 1; + + D1(printk(KERN_DEBUG "Retrying failed write.\n")); + + ACCT_SANITY_CHECK(c,jeb); + D1(ACCT_PARANOIA_CHECK(jeb)); + + if (alloc_mode == ALLOC_GC) { + ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy); + } else { + /* Locking pain */ + up(&f->sem); + jffs2_complete_reservation(c); + + ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode); + down(&f->sem); + } + + 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)); + goto retry; + } + D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); + jffs2_free_raw_node_ref(raw); + } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dirent(fd); - if (writelen) - *writelen = retlen; return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ raw->flash_offset |= REF_PRISTINE; jffs2_add_physical_node_ref(c, raw); - if (writelen) - *writelen = retlen; + + raw->next_in_ino = f->inocache->nodes; + f->inocache->nodes = raw; - f->inocache->nodes = raw; + if (retried) { + ACCT_SANITY_CHECK(c,NULL); + } + return fd; } @@ -288,7 +362,9 @@ int jffs2_write_inode_range(struct jffs2 unsigned char comprtype = JFFS2_COMPR_NONE; uint32_t phys_ofs, alloclen; uint32_t datalen, cdatalen; + int retried = 0; + retry: D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset)); ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL); @@ -300,21 +376,7 @@ int jffs2_write_inode_range(struct jffs2 datalen = writelen; cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen); - comprbuf = kmalloc(cdatalen, GFP_KERNEL); - if (comprbuf) { - comprtype = jffs2_compress(buf, comprbuf, &datalen, &cdatalen); - } - if (comprtype == JFFS2_COMPR_NONE) { - /* Either compression failed, or the allocation of comprbuf failed */ - if (comprbuf) - kfree(comprbuf); - comprbuf = buf; - datalen = cdatalen; - } - /* Now comprbuf points to the data to be written, be it compressed or not. - comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means - that the comprbuf doesn't need to be kfree()d. - */ + comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); @@ -331,15 +393,20 @@ int jffs2_write_inode_range(struct jffs2 ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); - fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, NULL); + fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY); - if (comprtype != JFFS2_COMPR_NONE) - kfree(comprbuf); + jffs2_free_comprbuf(comprbuf, buf); if (IS_ERR(fn)) { ret = PTR_ERR(fn); up(&f->sem); jffs2_complete_reservation(c); + if (!retried) { + /* Write error to be retried */ + retried = 1; + D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n")); + goto retry; + } break; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); @@ -381,7 +448,6 @@ int jffs2_do_create(struct jffs2_sb_info struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; uint32_t alloclen, phys_ofs; - uint32_t writtenlen; int ret; /* Try to reserve enough space for both node and dirent. @@ -397,7 +463,7 @@ int jffs2_do_create(struct jffs2_sb_info ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); - fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen); + fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", jemode_to_cpu(ri->mode))); @@ -414,22 +480,14 @@ int jffs2_do_create(struct jffs2_sb_info */ f->metadata = fn; - /* Work out where to put the dirent node now. */ - writtenlen = PAD(writtenlen); - phys_ofs += writtenlen; - alloclen -= writtenlen; up(&f->sem); - - if (alloclen < sizeof(*rd)+namelen) { - /* Not enough space left in this chunk. Get some more */ - jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); - if (ret) { - /* Eep. */ - D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); - return ret; - } + if (ret) { + /* Eep. */ + D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); + return ret; } rd = jffs2_alloc_raw_dirent(); @@ -455,7 +513,7 @@ int jffs2_do_create(struct jffs2_sb_info rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, &writtenlen); + fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_dirent(rd); @@ -513,7 +571,7 @@ int jffs2_do_unlink(struct jffs2_sb_info rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL); + fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); jffs2_free_raw_dirent(rd); @@ -598,7 +656,7 @@ int jffs2_do_link (struct jffs2_sb_info rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL); + fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); jffs2_free_raw_dirent(rd);
