From 4af8e944c22d8af92a7548354a9567250cc1a782 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 15 Jul 2004 20:19:18 +0100 Subject: [PATCH] JFFS2 file system update - Improved support for NAND flash - More generic compression support, allowing for extra compressors - Fix potential deadlock with kupdated --- fs/Kconfig | 82 +++++- fs/jffs2/Makefile | 11 +- fs/jffs2/background.c | 42 +-- fs/jffs2/build.c | 157 ++++++------ fs/jffs2/compr.c | 508 ++++++++++++++++++++++++++++++------- fs/jffs2/compr.h | 122 +++++++++ fs/jffs2/compr_rtime.c | 34 ++- fs/jffs2/compr_rubin.c | 66 ++++- fs/jffs2/compr_zlib.c | 58 ++++- fs/jffs2/erase.c | 103 +++++--- fs/jffs2/file.c | 19 +- fs/jffs2/fs.c | 186 ++++++++++++-- fs/jffs2/gc.c | 316 +++++++++++------------ fs/jffs2/ioctl.c | 6 +- fs/jffs2/malloc.c | 2 +- fs/jffs2/nodelist.c | 35 ++- fs/jffs2/nodelist.h | 125 +++++++-- fs/jffs2/nodemgmt.c | 127 +++++----- fs/jffs2/os-linux.h | 43 ++-- fs/jffs2/proc.c | 217 ++++++++++++++++ fs/jffs2/read.c | 13 +- fs/jffs2/readinode.c | 100 ++++++-- fs/jffs2/scan.c | 78 +++--- fs/jffs2/super.c | 32 ++- fs/jffs2/symlink.c | 2 +- fs/jffs2/wbuf.c | 295 +++++++++------------ fs/jffs2/write.c | 129 +++++----- fs/jffs2/writev.c | 2 +- include/linux/jffs2.h | 37 +-- include/linux/jffs2_fs_i.h | 4 +- 30 files changed, 2021 insertions(+), 930 deletions(-) create mode 100644 fs/jffs2/compr.h create mode 100644 fs/jffs2/proc.c diff --git a/fs/Kconfig b/fs/Kconfig index a4a93a02ade3a..2c35a14ccfbb2 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1097,17 +1097,15 @@ config JFFS_FS_VERBOSE config JFFS_PROC_FS bool "JFFS stats available in /proc filesystem" - depends on JFFS_FS && PROC + depends on JFFS_FS && PROC_FS help Enabling this option will cause statistics from mounted JFFS file systems to be made available to the user in the /proc/fs/jffs/ directory. config JFFS2_FS tristate "Journalling Flash File System v2 (JFFS2) support" - depends on MTD select CRC32 - select ZLIB_INFLATE - select ZLIB_DEFLATE + depends on MTD help JFFS2 is the second generation of the Journalling Flash File System for use on diskless embedded devices. It provides improved wear @@ -1151,6 +1149,82 @@ config JFFS2_FS_NAND Say 'N' unless you have NAND flash and you are willing to test and develop JFFS2 support for it. +config JFFS2_COMPRESSION_OPTIONS + bool "Advanced compression options for JFFS2" + default n + help + Enabling this option allows you to explicitly choose which + compression modules, if any, are enabled in JFFS2. Removing + compressors and mean you cannot read existing file systems, + and enabling experimental compressors can mean that you + write a file system which cannot be read by a standard kernel. + + If unsure, you should _definitely_ say 'N'. + +config JFFS2_ZLIB + bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS + select ZLIB_INFLATE + select ZLIB_DEFLATE + depends on JFFS2_FS + default y + help + Zlib is designed to be a free, general-purpose, legally unencumbered, + lossless data-compression library for use on virtually any computer + hardware and operating system. See http://www.gzip.org/zlib/ for + further information. + + Say 'Y' if unsure. + +config JFFS2_RTIME + bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default y + help + Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. + +config JFFS2_RUBIN + bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default n + help + RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. + +choice + prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS + default JFFS2_CMODE_PRIORITY + depends on JFFS2_FS + help + You can set here the default compression mode of JFFS2 from + the avaiable compression modes. Don't touch if unsure. + +config JFFS2_CMODE_NONE + bool "no compression" + help + Uses no compression. + +config JFFS2_CMODE_PRIORITY + bool "priority" + help + Tries the compressors in a predefinied order and chooses the first + successful one. + +config JFFS2_CMODE_SIZE + bool "size (EXPERIMENTAL)" + help + Tries all compressors and chooses the one which has the smallest + result. + +endchoice + +config JFFS2_PROC + bool "JFFS2 proc interface support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS && PROC_FS + default n + help + You can read some statistics and set the compression mode and + compressor priorities with this interface. + + config CRAMFS tristate "Compressed ROM file system support" select ZLIB_INFLATE diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile index 1a47874c7c355..73ee11016976d 100644 --- a/fs/jffs2/Makefile +++ b/fs/jffs2/Makefile @@ -1,15 +1,18 @@ # -# Makefile for the linux Journalling Flash FileSystem (JFFS) routines. +# Makefile for the Linux Journalling Flash File System v2 (JFFS2) # -# $Id: Makefile,v 1.34 2002/03/08 11:27:59 dwmw2 Exp $ +# $Id: Makefile.common,v 1.5 2004/07/15 16:06:41 dwmw2 Exp $ # obj-$(CONFIG_JFFS2_FS) += jffs2.o -jffs2-y := compr.o compr_rubin.o compr_rtime.o compr_zlib.o -jffs2-y += dir.o file.o ioctl.o nodelist.o malloc.o +jffs2-y := compr.o dir.o file.o ioctl.o nodelist.o malloc.o jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o jffs2-y += super.o jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o +jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o +jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o +jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o +jffs2-$(CONFIG_JFFS2_PROC) += proc.o diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 621231064418f..ac59ee35da656 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: background.c,v 1.44 2003/10/08 13:29:55 dwmw2 Exp $ + * $Id: background.c,v 1.49 2004/07/13 08:56:40 dwmw2 Exp $ * */ @@ -20,12 +20,11 @@ static int jffs2_garbage_collect_thread(void *); -static int thread_should_wake(struct jffs2_sb_info *c); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { spin_lock(&c->erase_completion_lock); - if (c->gc_task && thread_should_wake(c)) + if (c->gc_task && jffs2_thread_should_wake(c)) send_sig(SIGHUP, c->gc_task, 1); spin_unlock(&c->erase_completion_lock); } @@ -84,11 +83,11 @@ static int jffs2_garbage_collect_thread(void *_c) for (;;) { allow_signal(SIGHUP); - if (!thread_should_wake(c)) { + if (!jffs2_thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); - /* Yes, there's a race here; we checked thread_should_wake() before - setting current->state to TASK_INTERRUPTIBLE. But it doesn't + /* Yes, there's a race here; we checked jffs2_thread_should_wake() + before setting current->state to TASK_INTERRUPTIBLE. But it doesn't matter - We don't care if we miss a wakeup, because the GC thread is only an optimisation anyway. */ schedule(); @@ -144,34 +143,3 @@ static int jffs2_garbage_collect_thread(void *_c) spin_unlock(&c->erase_completion_lock); complete_and_exit(&c->gc_thread_exit, 0); } - -static int thread_should_wake(struct jffs2_sb_info *c) -{ - int ret = 0; - uint32_t dirty; - - if (c->unchecked_size) { - D1(printk(KERN_DEBUG "thread_should_wake(): unchecked_size %d, checked_ino #%d\n", - c->unchecked_size, c->checked_ino)); - return 1; - } - - /* dirty_size contains blocks on erase_pending_list - * those blocks are counted in c->nr_erasing_blocks. - * If one block is actually erased, it is not longer counted as dirty_space - * but it is counted in c->nr_erasing_blocks, so we add it and subtract it - * with c->nr_erasing_blocks * c->sector_size again. - * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks - * This helps us to force gc and pick eventually a clean block to spread the load. - */ - dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; - - if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && - (dirty > c->nospc_dirty_size)) - ret = 1; - - D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", - c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); - - return ret; -} diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index c05f64b5a2a1b..9b6e1d8e486a6 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.52 2003/10/09 00:38:38 dwmw2 Exp $ + * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ * */ @@ -16,8 +16,7 @@ #include #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_cache *ic, struct jffs2_sb_info *c) 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 jffs2_sb_info *c) 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 jffs2_sb_info *c) /* 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(); + } - 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)); + D1(printk(KERN_DEBUG "Pass 2a starting\n")); - cond_resched(); - - } while(ret == -EAGAIN); + while (dead_fds) { + struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *fd = dead_fds; + + dead_fds = fd->next; + + 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 jffs2_sb_info *c) 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,18 +214,29 @@ int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inod 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) diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index 471ec55e6a156..9e5138a640151 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c @@ -2,137 +2,473 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. - * * Created by Arjan van de Ven * + * Copyright (C) 2004 Ferenc Havasi , + * University of Szeged, Hungary + * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.27 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $ * */ -#if defined(__KERNEL__) || defined (__ECOS) -#include -#include -#include -#include -#else -#define KERN_DEBUG -#define KERN_NOTICE -#define KERN_WARNING -#define printk printf -#include -#include -#include -#endif +#include "compr.h" + +static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED; + +/* Available compressors are on this list */ +static LIST_HEAD(jffs2_compressor_list); + +/* Actual compression mode */ +static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; -#include +void jffs2_set_compression_mode(int mode) +{ + jffs2_compression_mode = mode; +} -int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); -void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); -int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); -void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); -int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); -void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); -int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); -void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); +int jffs2_get_compression_mode(void) +{ + return jffs2_compression_mode; +} +/* Statistics for blocks stored without compression */ +static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; /* 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 * data. On exit, expected to hold the actual size of the compressed * data. * - * Returns: Byte to be stored with data indicating compression type used. + * Returns: Lower byte to be stored with data indicating compression type used. * Zero is used to show that the data could not be compressed - the * compressed version was actually larger than the original. + * Upper byte will be used later. (soon) * * If the cdata buffer isn't large enough to hold all the uncompressed data, * 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, - uint32_t *datalen, uint32_t *cdatalen) +uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen) { - int ret; + int ret = JFFS2_COMPR_NONE; + int compr_ret; + struct jffs2_compressor *this, *best=NULL; + unsigned char *output_buf = NULL, *tmp_buf; + uint32_t orig_slen, orig_dlen; + uint32_t best_slen=0, best_dlen=0; - 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 */ - 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 */ - ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen); - if (!ret) { - return JFFS2_COMPR_RUBINMIPS; - } -#endif - /* 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 - return JFFS2_COMPR_NONE; /* We failed to compress */ + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + break; + case JFFS2_COMPR_MODE_PRIORITY: + output_buf = kmalloc(*cdatalen,GFP_KERNEL); + if (!output_buf) { + printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); + goto out; + } + orig_slen = *datalen; + orig_dlen = *cdatalen; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + this->usecount--; + if (!compr_ret) { + ret = this->compr; + this->stat_compr_blocks++; + this->stat_compr_orig_size += *datalen; + this->stat_compr_new_size += *cdatalen; + break; + } + } + spin_unlock(&jffs2_compressor_list_lock); + if (ret == JFFS2_COMPR_NONE) kfree(output_buf); + break; + case JFFS2_COMPR_MODE_SIZE: + orig_slen = *datalen; + orig_dlen = *cdatalen; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + /* Allocating memory for output buffer if necessary */ + if ((this->compr_buf_sizecompr_buf)) { + spin_unlock(&jffs2_compressor_list_lock); + kfree(this->compr_buf); + spin_lock(&jffs2_compressor_list_lock); + this->compr_buf_size=0; + this->compr_buf=NULL; + } + if (!this->compr_buf) { + spin_unlock(&jffs2_compressor_list_lock); + tmp_buf = kmalloc(orig_dlen,GFP_KERNEL); + spin_lock(&jffs2_compressor_list_lock); + if (!tmp_buf) { + printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); + continue; + } + else { + this->compr_buf = tmp_buf; + this->compr_buf_size = orig_dlen; + } + } + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + this->usecount--; + if (!compr_ret) { + if ((!best_dlen)||(best_dlen>*cdatalen)) { + best_dlen = *cdatalen; + best_slen = *datalen; + best = this; + } + } + } + if (best_dlen) { + *cdatalen = best_dlen; + *datalen = best_slen; + output_buf = best->compr_buf; + best->compr_buf = NULL; + best->compr_buf_size = 0; + best->stat_compr_blocks++; + best->stat_compr_orig_size += best_slen; + best->stat_compr_new_size += best_dlen; + ret = best->compr; + } + spin_unlock(&jffs2_compressor_list_lock); + break; + default: + printk(KERN_ERR "JFFS2: unknow compression mode.\n"); + } + out: + if (ret == JFFS2_COMPR_NONE) { + *cpage_out = data_in; + *datalen = *cdatalen; + none_stat_compr_blocks++; + none_stat_compr_size += *datalen; + } + else { + *cpage_out = output_buf; + } + return ret; } - -int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, +int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + uint16_t comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) { - switch (comprtype) { + struct jffs2_compressor *this; + int ret; + + switch (comprtype & 0xff) { case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ memcpy(data_out, cdata_in, datalen); + none_stat_decompr_blocks++; break; - case JFFS2_COMPR_ZERO: memset(data_out, 0, datalen); break; + default: + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (comprtype == this->compr) { + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + if (ret) { + printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); + } + else { + this->stat_decompr_blocks++; + } + this->usecount--; + spin_unlock(&jffs2_compressor_list_lock); + return ret; + } + } + printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype); + spin_unlock(&jffs2_compressor_list_lock); + return -EIO; + } + return 0; +} - case JFFS2_COMPR_ZLIB: - jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen); - break; +int jffs2_register_compressor(struct jffs2_compressor *comp) +{ + struct jffs2_compressor *this; - case JFFS2_COMPR_RTIME: - jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen); - break; + if (!comp->name) { + printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); + return -1; + } + comp->compr_buf_size=0; + comp->compr_buf=NULL; + comp->usecount=0; + comp->stat_compr_orig_size=0; + comp->stat_compr_new_size=0; + comp->stat_compr_blocks=0; + comp->stat_decompr_blocks=0; + D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); + + spin_lock(&jffs2_compressor_list_lock); + + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + goto out; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); +out: + D2(list_for_each_entry(this, &jffs2_compressor_list, list) { + printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); + }) + + spin_unlock(&jffs2_compressor_list_lock); + + return 0; +} + +int jffs2_unregister_compressor(struct jffs2_compressor *comp) +{ + D2(struct jffs2_compressor *this;) + + D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); + + spin_lock(&jffs2_compressor_list_lock); + + if (comp->usecount) { + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); + return -1; + } + list_del(&comp->list); + + D2(list_for_each_entry(this, &jffs2_compressor_list, list) { + printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); + }) + spin_unlock(&jffs2_compressor_list_lock); + return 0; +} + +#ifdef CONFIG_JFFS2_PROC + +#define JFFS2_STAT_BUF_SIZE 16000 + +char *jffs2_list_compressors(void) +{ + struct jffs2_compressor *this; + char *buf, *act_buf; + + act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); + list_for_each_entry(this, &jffs2_compressor_list, list) { + act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); + if ((this->disabled)||(!this->compress)) + act_buf += sprintf(act_buf,"disabled"); + else + act_buf += sprintf(act_buf,"enabled"); + act_buf += sprintf(act_buf,"\n"); + } + return buf; +} + +char *jffs2_stats(void) +{ + struct jffs2_compressor *this; + char *buf, *act_buf; + + act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); + + act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n"); + act_buf += sprintf(act_buf,"%10s ","none"); + act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, + none_stat_compr_size, none_stat_decompr_blocks); + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + act_buf += sprintf(act_buf,"%10s ",this->name); + if ((this->disabled)||(!this->compress)) + act_buf += sprintf(act_buf,"- "); + else + act_buf += sprintf(act_buf,"+ "); + act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, + this->stat_compr_new_size, this->stat_compr_orig_size, + this->stat_decompr_blocks); + act_buf += sprintf(act_buf,"\n"); + } + spin_unlock(&jffs2_compressor_list_lock); + + return buf; +} + +char *jffs2_get_compression_mode_name(void) +{ + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + return "none"; + case JFFS2_COMPR_MODE_PRIORITY: + return "priority"; + case JFFS2_COMPR_MODE_SIZE: + return "size"; + } + return "unkown"; +} + +int jffs2_set_compression_mode_name(const char *name) +{ + if (!strcmp("none",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; + return 0; + } + if (!strcmp("priority",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; + return 0; + } + if (!strcmp("size",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; + return 0; + } + return 1; +} + +static int jffs2_compressor_Xable(const char *name, int disabled) +{ + struct jffs2_compressor *this; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (!strcmp(this->name, name)) { + this->disabled = disabled; + spin_unlock(&jffs2_compressor_list_lock); + return 0; + } + } + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); + return 1; +} + +int jffs2_enable_compressor_name(const char *name) +{ + return jffs2_compressor_Xable(name, 0); +} + +int jffs2_disable_compressor_name(const char *name) +{ + return jffs2_compressor_Xable(name, 1); +} + +int jffs2_set_compressor_priority(const char *name, int priority) +{ + struct jffs2_compressor *this,*comp; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (!strcmp(this->name, name)) { + this->priority = priority; + comp = this; + goto reinsert; + } + } + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); + return 1; +reinsert: + /* list is sorted in the order of priority, so if + we change it we have to reinsert it into the + good place */ + list_del(&comp->list); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + spin_unlock(&jffs2_compressor_list_lock); + return 0; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); + spin_unlock(&jffs2_compressor_list_lock); + return 0; +} - 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; - case JFFS2_COMPR_DYNRUBIN: -#if 1 /* Phase this one out */ - jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen); + +void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) +{ + if (orig != comprbuf) + kfree(comprbuf); +} + +int jffs2_compressors_init(void) +{ +/* Registering compressors */ +#ifdef CONFIG_JFFS2_ZLIB + jffs2_zlib_init(); +#endif +#ifdef CONFIG_JFFS2_RTIME + jffs2_rtime_init(); +#endif +#ifdef CONFIG_JFFS2_RUBIN + jffs2_rubinmips_init(); + jffs2_dynrubin_init(); +#endif +#ifdef CONFIG_JFFS2_LZARI + jffs2_lzari_init(); +#endif +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_init(); +#endif +/* Setting default compression mode */ +#ifdef CONFIG_JFFS2_CMODE_NONE + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; + D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) #else - printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n"); +#ifdef CONFIG_JFFS2_CMODE_SIZE + jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; + D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) +#else + D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) #endif - break; +#endif + return 0; +} - default: - printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype); - return -EIO; - } - return 0; +int jffs2_compressors_exit(void) +{ +/* Unregistering compressors */ +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_exit(); +#endif +#ifdef CONFIG_JFFS2_LZARI + jffs2_lzari_exit(); +#endif +#ifdef CONFIG_JFFS2_RUBIN + jffs2_dynrubin_exit(); + jffs2_rubinmips_exit(); +#endif +#ifdef CONFIG_JFFS2_RTIME + jffs2_rtime_exit(); +#endif +#ifdef CONFIG_JFFS2_ZLIB + jffs2_zlib_exit(); +#endif + return 0; } diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h new file mode 100644 index 0000000000000..5e3445da3e5ed --- /dev/null +++ b/fs/jffs2/compr.h @@ -0,0 +1,122 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi , + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: compr.h,v 1.5 2004/06/23 16:34:39 havasi Exp $ + * + */ + +#ifndef __JFFS2_COMPR_H__ +#define __JFFS2_COMPR_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +#define JFFS2_RUBINMIPS_PRIORITY 10 +#define JFFS2_DYNRUBIN_PRIORITY 20 +#define JFFS2_LZARI_PRIORITY 30 +#define JFFS2_LZO_PRIORITY 40 +#define JFFS2_RTIME_PRIORITY 50 +#define JFFS2_ZLIB_PRIORITY 60 + +#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ +#define JFFS2_DYNRUBIN_DISABLED /* for decompression */ + +#define JFFS2_COMPR_MODE_NONE 0 +#define JFFS2_COMPR_MODE_PRIORITY 1 +#define JFFS2_COMPR_MODE_SIZE 2 + +void jffs2_set_compression_mode(int mode); +int jffs2_get_compression_mode(void); + +struct jffs2_compressor { + struct list_head list; + int priority; /* used by prirority comr. mode */ + char *name; + char compr; /* JFFS2_COMPR_XXX */ + int (*compress)(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *srclen, uint32_t *destlen, void *model); + int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, + uint32_t cdatalen, uint32_t datalen, void *model); + int usecount; + int disabled; /* if seted the compressor won't compress */ + unsigned char *compr_buf; /* used by size compr. mode */ + uint32_t compr_buf_size; /* used by size compr. mode */ + uint32_t stat_compr_orig_size; + uint32_t stat_compr_new_size; + uint32_t stat_compr_blocks; + uint32_t stat_decompr_blocks; +}; + +int jffs2_register_compressor(struct jffs2_compressor *comp); +int jffs2_unregister_compressor(struct jffs2_compressor *comp); + +int jffs2_compressors_init(void); +int jffs2_compressors_exit(void); + +uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen); + +int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + uint16_t comprtype, unsigned char *cdata_in, + unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); + +void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); + +#ifdef CONFIG_JFFS2_PROC +int jffs2_enable_compressor_name(const char *name); +int jffs2_disable_compressor_name(const char *name); +int jffs2_set_compression_mode_name(const char *mode_name); +char *jffs2_get_compression_mode_name(void); +int jffs2_set_compressor_priority(const char *mode_name, int priority); +char *jffs2_list_compressors(void); +char *jffs2_stats(void); +#endif + +/* Compressor modules */ +/* These functions will be called by jffs2_compressors_init/exit */ + +#ifdef CONFIG_JFFS2_RUBIN +int jffs2_rubinmips_init(void); +void jffs2_rubinmips_exit(void); +int jffs2_dynrubin_init(void); +void jffs2_dynrubin_exit(void); +#endif +#ifdef CONFIG_JFFS2_RTIME +int jffs2_rtime_init(void); +void jffs2_rtime_exit(void); +#endif +#ifdef CONFIG_JFFS2_ZLIB +int jffs2_zlib_init(void); +void jffs2_zlib_exit(void); +#endif +#ifdef CONFIG_JFFS2_LZARI +int jffs2_lzari_init(void); +void jffs2_lzari_exit(void); +#endif +#ifdef CONFIG_JFFS2_LZO +int jffs2_lzo_init(void); +void jffs2_lzo_exit(void); +#endif + +/* Prototypes from proc.c */ +int jffs2_proc_init(void); +int jffs2_proc_exit(void); + +#endif /* __JFFS2_COMPR_H__ */ diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index a72ce670c5d43..3bfe27773e42a 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $ * * * Very simple lz77-ish encoder. @@ -25,10 +25,12 @@ #include #include #include +#include +#include "compr.h" /* _compress returns the compressed size, -1 if bigger */ int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) + uint32_t *sourcelen, uint32_t *dstlen, void *model) { short positions[256]; int outpos = 0; @@ -67,8 +69,8 @@ int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, } -void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen) +int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, void *model) { short positions[256]; int outpos = 0; @@ -98,7 +100,29 @@ void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, outpos+=repeat; } } - } + } + return 0; } +static struct jffs2_compressor jffs2_rtime_comp = { + .priority = JFFS2_RTIME_PRIORITY, + .name = "rtime", + .compr = JFFS2_COMPR_RTIME, + .compress = &jffs2_rtime_compress, + .decompress = &jffs2_rtime_decompress, +#ifdef JFFS2_RTIME_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_rtime_init(void) +{ + return jffs2_register_compressor(&jffs2_rtime_comp); +} +void jffs2_rtime_exit(void) +{ + jffs2_unregister_compressor(&jffs2_rtime_comp); +} diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index be8e8d5016d49..450d6624181fa 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c @@ -7,17 +7,17 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $ + * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $ * */ #include #include +#include #include "compr_rubin.h" #include "histo_mips.h" - - +#include "compr.h" static void init_rubin(struct rubin_state *rs, int div, int *bits) { @@ -223,13 +223,13 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, #if 0 /* _compress returns the compressed size, -1 if bigger */ int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) + uint32_t *sourcelen, uint32_t *dstlen, void *model) { return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); } #endif int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) + uint32_t *sourcelen, uint32_t *dstlen, void *model) { int bits[8]; unsigned char histo[256]; @@ -306,14 +306,15 @@ static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata } -void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t sourcelen, uint32_t dstlen) +int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t sourcelen, uint32_t dstlen, void *model) { rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); + return 0; } -void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t sourcelen, uint32_t dstlen) +int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t sourcelen, uint32_t dstlen, void *model) { int bits[8]; int c; @@ -322,4 +323,51 @@ void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, bits[c] = data_in[c]; rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen); + return 0; +} + +static struct jffs2_compressor jffs2_rubinmips_comp = { + .priority = JFFS2_RUBINMIPS_PRIORITY, + .name = "rubinmips", + .compr = JFFS2_COMPR_DYNRUBIN, + .compress = NULL, /*&jffs2_rubinmips_compress,*/ + .decompress = &jffs2_rubinmips_decompress, +#ifdef JFFS2_RUBINMIPS_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_rubinmips_init(void) +{ + return jffs2_register_compressor(&jffs2_rubinmips_comp); +} + +void jffs2_rubinmips_exit(void) +{ + jffs2_unregister_compressor(&jffs2_rubinmips_comp); +} + +static struct jffs2_compressor jffs2_dynrubin_comp = { + .priority = JFFS2_DYNRUBIN_PRIORITY, + .name = "dynrubin", + .compr = JFFS2_COMPR_RUBINMIPS, + .compress = jffs2_dynrubin_compress, + .decompress = &jffs2_dynrubin_decompress, +#ifdef JFFS2_DYNRUBIN_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_dynrubin_init(void) +{ + return jffs2_register_compressor(&jffs2_dynrubin_comp); +} + +void jffs2_dynrubin_exit(void) +{ + jffs2_unregister_compressor(&jffs2_dynrubin_comp); } diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index c05e65f4c7248..f3b0bc97ee1cc 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $ * */ @@ -17,13 +17,12 @@ #include #include -#include -#include #include #include #include #include #include "nodelist.h" +#include "compr.h" /* Plan: call deflate() with avail_in == *sourcelen, avail_out = *dstlen - 12 and flush == Z_FINISH. @@ -39,7 +38,10 @@ static DECLARE_MUTEX(inflate_sem); static z_stream inf_strm, def_strm; #ifdef __KERNEL__ /* Linux-only */ -int __init jffs2_zlib_init(void) +#include +#include + +static int __init alloc_workspaces(void) { def_strm.workspace = vmalloc(zlib_deflate_workspacesize()); if (!def_strm.workspace) { @@ -57,15 +59,18 @@ int __init jffs2_zlib_init(void) return 0; } -void jffs2_zlib_exit(void) +static void free_workspaces(void) { vfree(def_strm.workspace); vfree(inf_strm.workspace); } +#else +#define alloc_workspaces() (0) +#define free_workspaces() do { } while(0) #endif /* __KERNEL__ */ int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) + uint32_t *sourcelen, uint32_t *dstlen, void *model) { int ret; @@ -130,8 +135,8 @@ int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, return ret; } -void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen) +int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, void *model) { int ret; int wbits = MAX_WBITS; @@ -165,7 +170,7 @@ void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) { printk(KERN_WARNING "inflateInit failed\n"); up(&inflate_sem); - return; + return 1; } while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK) @@ -175,4 +180,39 @@ void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, } zlib_inflateEnd(&inf_strm); up(&inflate_sem); + return 0; +} + +static struct jffs2_compressor jffs2_zlib_comp = { + .priority = JFFS2_ZLIB_PRIORITY, + .name = "zlib", + .compr = JFFS2_COMPR_ZLIB, + .compress = &jffs2_zlib_compress, + .decompress = &jffs2_zlib_decompress, +#ifdef JFFS2_ZLIB_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int __init jffs2_zlib_init(void) +{ + int ret; + + ret = alloc_workspaces(); + if (ret) + return ret; + + ret = jffs2_register_compressor(&jffs2_zlib_comp); + if (ret) + free_workspaces(); + + return ret; +} + +void jffs2_zlib_exit(void) +{ + jffs2_unregister_compressor(&jffs2_zlib_comp); + free_workspaces(); } diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index f7a940962e5ff..4856f369c3855 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.53 2003/10/08 17:22:54 dwmw2 Exp $ + * $Id: erase.c,v 1.60 2004/06/30 17:26:15 dbrown Exp $ * */ @@ -28,7 +28,7 @@ struct erase_priv_struct { #ifndef __ECOS static void jffs2_erase_callback(struct erase_info *); #endif -static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); @@ -36,6 +36,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; + uint32_t bad_offset; #ifdef __ECOS ret = jffs2_flash_erase(c, jeb); if (!ret) { @@ -65,18 +66,16 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) instr->len = c->sector_size; instr->callback = jffs2_erase_callback; instr->priv = (unsigned long)(&instr[1]); + instr->fail_addr = 0xffffffff; ((struct erase_priv_struct *)instr->priv)->jeb = jeb; ((struct erase_priv_struct *)instr->priv)->c = c; - /* NAND , read out the fail counter, if possible */ - if (!jffs2_can_mark_obsolete(c)) - jffs2_nand_read_failcnt(c,jeb); - ret = c->mtd->erase(c->mtd, instr); if (!ret) return; + bad_offset = instr->fail_addr; kfree(instr); #endif /* __ECOS */ @@ -98,10 +97,10 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) else printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret); - jffs2_erase_failed(c, jeb); + jffs2_erase_failed(c, jeb, bad_offset); } -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,6 +117,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) 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)); @@ -144,6 +148,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) } spin_unlock(&c->erase_completion_lock); + done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); up(&c->erase_free_sem); @@ -160,16 +165,34 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo jffs2_erase_pending_trigger(c); } -static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { - spin_lock(&c->erase_completion_lock); - c->erasing_size -= c->sector_size; - c->bad_size += c->sector_size; - list_del(&jeb->list); - list_add(&jeb->list, &c->bad_list); - c->nr_erasing_blocks--; - spin_unlock(&c->erase_completion_lock); - wake_up(&c->erase_wait); + /* For NAND, if the failure did not occur at the device level for a + specific physical page, don't bother updating the bad block table. */ + if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) { + /* We had a device-level failure to erase. Let's see if we've + failed too many times. */ + if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { + /* We'd like to give this block another try. */ + spin_lock(&c->erase_completion_lock); + list_del(&jeb->list); + list_add(&jeb->list, &c->erase_pending_list); + c->erasing_size -= c->sector_size; + c->dirty_size += c->sector_size; + jeb->dirty_size = c->sector_size; + spin_unlock(&c->erase_completion_lock); + return; + } + } + + spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; + c->bad_size += c->sector_size; + list_del(&jeb->list); + list_add(&jeb->list, &c->bad_list); + c->nr_erasing_blocks--; + spin_unlock(&c->erase_completion_lock); + wake_up(&c->erase_wait); } #ifndef __ECOS @@ -179,7 +202,7 @@ static void jffs2_erase_callback(struct erase_info *instr) if(instr->state != MTD_ERASE_DONE) { printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state); - jffs2_erase_failed(priv->c, priv->jeb); + jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr); } else { jffs2_erase_succeeded(priv->c, priv->jeb); } @@ -277,17 +300,13 @@ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_erase jeb->last_node = NULL; } -void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) -{ - OFNI_BS_2SFFJ(c)->s_dirt = 1; -} - static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *marker_ref = NULL; unsigned char *ebuf; size_t retlen; int ret; + uint32_t bad_offset; if (!jffs2_cleanmarker_oob(c)) { marker_ref = jffs2_alloc_raw_node_ref(); @@ -312,6 +331,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); int i; + bad_offset = ofs; + ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); @@ -325,22 +346,21 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb /* It's OK. We know it's properly aligned */ unsigned long datum = *(unsigned long *)(&ebuf[i]); if (datum + 1) { - printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i); + bad_offset += i; + printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset); bad: if (!jffs2_cleanmarker_oob(c)) jffs2_free_raw_node_ref(marker_ref); - else - jffs2_write_nand_badblock( c ,jeb ); kfree(ebuf); bad2: spin_lock(&c->erase_completion_lock); - c->erasing_size -= c->sector_size; - c->bad_size += c->sector_size; - - list_add_tail(&jeb->list, &c->bad_list); - c->nr_erasing_blocks--; + /* Stick it on a list (any list) so + erase_failed can take it right off + again. Silly, but shouldn't happen + often. */ + list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); - wake_up(&c->erase_wait); + jffs2_erase_failed(c, jeb, bad_offset); return; } } @@ -349,7 +369,9 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb } kfree(ebuf); } - + + bad_offset = jeb->offset; + /* Write the erase complete marker */ D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); if (jffs2_cleanmarker_oob(c)) { @@ -370,29 +392,30 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb .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; } diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 8b3f8e7e56404..4b141011f8031 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.96 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $ * */ @@ -72,7 +72,7 @@ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) unsigned char *pg_buf; int ret; - D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT)); + D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT)); if (!PageLocked(pg)) PAGE_BUG(pg); @@ -93,7 +93,7 @@ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) flush_dcache_page(pg); kunmap(pg); - D1(printk(KERN_DEBUG "readpage finished\n")); + D2(printk(KERN_DEBUG "readpage finished\n")); return 0; } @@ -207,6 +207,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; + unsigned aligned_start = start & ~3; int ret = 0; uint32_t writtenlen = 0; @@ -240,9 +241,9 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi hurt to do it again. The alternative is ifdefs, which are ugly. */ kmap(pg); - ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start, - (pg->index << PAGE_CACHE_SHIFT) + start, - end - start, &writtenlen); + ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start, + (pg->index << PAGE_CACHE_SHIFT) + aligned_start, + end - aligned_start, &writtenlen); kunmap(pg); @@ -250,6 +251,12 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi /* There was an error writing. */ SetPageError(pg); } + + /* Adjust writtenlen for the padding we did, so we don't confuse our caller */ + if (writtenlen < (start&3)) + writtenlen = 0; + else + writtenlen -= (start&3); if (writtenlen) { if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 1f9e5787f66f0..76d7279c8f66b 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs.c,v 1.32 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $ * */ @@ -58,7 +58,7 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) return -ENOMEM; - ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen); + ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { kfree(mdata); return ret; @@ -145,10 +145,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) old_metadata = f->metadata; - if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) { - vmtruncate(inode, iattr->ia_size); + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size); - } if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { jffs2_add_full_dnode_to_inode(c, f, new_metadata); @@ -166,6 +164,14 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) up(&f->sem); jffs2_complete_reservation(c); + /* We have to do the vmtruncate() without f->sem held, since + some pages may be locked and waiting for it in readpage(). + We are protected from a simultaneous write() extending i_size + back past iattr->ia_size, because do_truncate() holds the + generic inode semaphore. */ + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) + vmtruncate(inode, iattr->ia_size); + return 0; } @@ -287,7 +293,7 @@ void jffs2_read_inode (struct inode *inode) case S_IFCHR: /* Read the device numbers from the media */ D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); - if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { + if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { /* Eep */ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); up(&f->sem); @@ -317,7 +323,7 @@ void jffs2_dirty_inode(struct inode *inode) struct iattr iattr; if (!(inode->i_state & I_DIRTY_DATASYNC)) { - D1(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); + D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); return; } @@ -343,9 +349,14 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) /* We stop if it was running, then restart if it needs to. This also catches the case where it was stopped and this - is just a remount to restart it */ - if (!(sb->s_flags & MS_RDONLY)) + is just a remount to restart it. + Flush the writebuffer, if neccecary, else we loose it */ + if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); + down(&c->alloc_sem); + jffs2_flush_wbuf_pad(c); + up(&c->alloc_sem); + } if (!(*flags & MS_RDONLY)) jffs2_start_garbage_collect_thread(c); @@ -365,7 +376,7 @@ void jffs2_write_super (struct super_block *sb) D1(printk(KERN_DEBUG "jffs2_write_super()\n")); jffs2_garbage_collect_trigger(c); - jffs2_erase_pending_blocks(c); + jffs2_erase_pending_blocks(c, 0); jffs2_flush_wbuf_gc(c, 0); } @@ -437,17 +448,35 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) c = JFFS2_SB_INFO(sb); +#ifndef CONFIG_JFFS2_FS_NAND + if (c->mtd->type == MTD_NANDFLASH) { + printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n"); + return -EINVAL; + } +#endif + c->flash_size = c->mtd->size; /* * Check, if we have to concatenate physical blocks to larger virtual blocks * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation) */ - blocks = c->flash_size / c->mtd->erasesize; - while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) + c->sector_size = c->mtd->erasesize; + blocks = c->flash_size / c->sector_size; + while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) { blocks >>= 1; + c->sector_size <<= 1; + } - c->sector_size = c->flash_size / blocks; + /* + * Size alignment check + */ + if ((c->sector_size * blocks) != c->flash_size) { + c->flash_size = c->sector_size * blocks; + printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n", + c->flash_size / 1024); + } + if (c->sector_size != c->mtd->erasesize) printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n", c->mtd->erasesize / 1024, c->sector_size / 1024); @@ -460,12 +489,10 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) c->cleanmarker_size = sizeof(struct jffs2_unknown_node); /* Joern -- stick alignment for weird 8-byte-page flash here */ - if (jffs2_cleanmarker_oob(c)) { - /* NAND (or other bizarre) flash... do setup accordingly */ - ret = jffs2_nand_flash_setup(c); - if (ret) - return ret; - } + /* NAND (or other bizarre) flash... do setup accordingly */ + ret = jffs2_flash_setup(c); + if (ret) + return ret; c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); if (!c->inocache_list) { @@ -510,7 +537,126 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) out_inohash: kfree(c->inocache_list); out_wbuf: - jffs2_nand_flash_cleanup(c); + jffs2_flash_cleanup(c); + + return ret; +} + +void jffs2_gc_release_inode(struct jffs2_sb_info *c, + struct jffs2_inode_info *f) +{ + iput(OFNI_EDONI_2SFFJ(f)); +} + +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, + int inum, int nlink) +{ + struct inode *inode; + struct jffs2_inode_cache *ic; + if (!nlink) { + /* 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 NULL; + } + 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 NULL; + } + } else { + /* 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), inum); + if (!inode) + return ERR_PTR(-ENOMEM); + } + if (is_bad_inode(inode)) { + printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", + inum, nlink); + /* NB. This will happen again. We need to do something appropriate here. */ + iput(inode); + return ERR_PTR(-EIO); + } + + return JFFS2_INODE_INFO(inode); +} + +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + unsigned long offset, + unsigned long *priv) +{ + struct inode *inode = OFNI_EDONI_2SFFJ(f); + struct page *pg; + + pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, + (void *)jffs2_do_readpage_unlock, inode); + if (IS_ERR(pg)) + return (void *)pg; + + *priv = (unsigned long)pg; + return kmap(pg); +} + +void jffs2_gc_release_page(struct jffs2_sb_info *c, + unsigned char *ptr, + unsigned long *priv) +{ + struct page *pg = (void *)*priv; + + kunmap(pg); + page_cache_release(pg); +} + +int jffs2_flash_setup(struct jffs2_sb_info *c) { + int ret = 0; + + if (jffs2_cleanmarker_oob(c)) { + /* NAND flash... do setup accordingly */ + ret = jffs2_nand_flash_setup(c); + if (ret) + return ret; + } + /* add setups for other bizarre flashes here... */ return ret; } + +void jffs2_flash_cleanup(struct jffs2_sb_info *c) { + + if (jffs2_cleanmarker_oob(c)) { + jffs2_nand_flash_cleanup(c); + } + + /* add cleanups for other bizarre flashes here... */ +} diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 1cc6bfc8fc421..f4d16ad9c9ade 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.114 2003/10/09 13:53:35 dwmw2 Exp $ + * $Id: gc.c,v 1.136 2004/05/27 19:06:09 gleixner Exp $ * */ @@ -19,6 +19,7 @@ #include #include #include "nodelist.h" +#include "compr.h" static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, @@ -36,7 +37,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end); 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_info *f); /* Called with erase_completion_lock held */ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) @@ -80,7 +81,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) nextlist = &c->erasable_list; } else { /* Eep. All were empty */ - printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"); + D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n")); return NULL; } @@ -112,11 +113,11 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) { + struct jffs2_inode_info *f; struct jffs2_inode_cache *ic; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; - uint32_t inum; - int ret = 0; + int ret = 0, inum, nlink; if (down_interruptible(&c->alloc_sem)) return -EINTR; @@ -186,7 +187,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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) @@ -204,7 +205,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) jeb = jffs2_find_gc_block(c); if (!jeb) { - printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"); + D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -EIO; @@ -223,17 +224,21 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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 +248,17 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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. Either the erase_completion_lock or + the inocache_lock are sufficient; we trade down since the inocache_lock + causes less contention. */ + 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 +268,6 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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: @@ -275,28 +279,28 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) ic->state = INO_STATE_GC; else { D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", - inum)); + 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 +310,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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,27 +323,50 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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 */ } - ret = jffs2_garbage_collect_live(c, jeb, raw, ic); + /* 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. */ + + inum = ic->ino; + nlink = ic->nlink; + spin_unlock(&c->inocache_lock); + + f = jffs2_gc_fetch_inode(c, inum, nlink); + if (IS_ERR(f)) + return PTR_ERR(f); + if (!f) + return 0; + + ret = jffs2_garbage_collect_live(c, jeb, raw, f); + + jffs2_gc_release_inode(c, f); release_sem: up(&c->alloc_sem); @@ -362,38 +389,35 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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_info *f) { - struct jffs2_inode_info *f; struct jffs2_node_frag *frag; struct jffs2_full_dnode *fn = NULL; struct jffs2_full_dirent *fd; uint32_t start = 0, end = 0, nrfrags = 0; - struct inode *inode; int ret = 0; - inode = iget(OFNI_BS_2SFFJ(c), ic->ino); - if (is_bad_inode(inode)) { - printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino); - /* NB. This will happen again. We need to do something appropriate here. */ - up(&c->alloc_sem); - iput(inode); - return -EIO; - } - - f = JFFS2_INODE_INFO(inode); down(&f->sem); /* 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 +430,6 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era 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) @@ -438,10 +438,10 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era } if (fn) { if (ref_flags(raw) == REF_PRISTINE) { - ret = jffs2_garbage_collect_pristine(c, ic, raw); + ret = jffs2_garbage_collect_pristine(c, f->inocache, raw); if (!ret) { /* Urgh. Return it sensibly. */ - frag->node->raw = ic->nodes; + frag->node->raw = f->inocache->nodes; } if (ret != -EBADFD) goto upnout; @@ -478,7 +478,6 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era } upnout: up(&f->sem); - iput(inode); return ret; } @@ -492,30 +491,32 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 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; @@ -578,14 +579,14 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, /* OK, all the CRCs are good; this node can just be copied as-is. */ retry: nraw->flash_offset = phys_ofs; - nraw->totlen = raw->totlen; + nraw->__totlen = rawlen; nraw->next_phys = NULL; - ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node); + ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); - if (ret || (retlen != raw->totlen)) { + 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; @@ -609,7 +610,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ACCT_SANITY_CHECK(c,jeb); D1(ACCT_PARANOIA_CHECK(jeb)); - ret = jffs2_reserve_space_gc(c, raw->totlen, &phys_ofs, &dummy); + 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)); @@ -674,7 +675,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n"); return -ENOMEM; } - ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen); + ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen); if (ret) { printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret); kfree(mdata); @@ -779,13 +780,17 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct 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 @@ -793,91 +798,66 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct 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)); + retlen, rawlen, 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 */ + if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) 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)); - continue; - } /* If the name CRC doesn't match, skip */ - if (je32_to_cpu(rd.name_crc) != name_crc) + 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); + + 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 */ @@ -1008,6 +988,9 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras 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) @@ -1042,10 +1025,9 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; - struct page *pg; + unsigned long pg; unsigned char *pg_ptr; - /* FIXME: */ struct inode *inode = OFNI_EDONI_2SFFJ(f); - + memset(&ri, 0, sizeof(ri)); D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n", @@ -1184,23 +1166,18 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era * page OK. We'll actually write it out again in commit_write, which is a little * suboptimal, but at least we're correct. */ -#ifdef __ECOS - pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); -#else - pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); -#endif - if (IS_ERR(pg)) { - printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg)); - return PTR_ERR(pg); + pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); + + if (IS_ERR(pg_ptr)) { + printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); + return PTR_ERR(pg_ptr); } - pg_ptr = (char *)kmap(pg); - comprbuf = kmalloc(end - start, GFP_KERNEL); offset = start; while(offset < orig_end) { uint32_t datalen; uint32_t cdatalen; - char comprtype = JFFS2_COMPR_NONE; + uint16_t comprtype = JFFS2_COMPR_NONE; ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen); @@ -1214,14 +1191,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era 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(c, f, 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); @@ -1239,11 +1210,14 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era ri.offset = cpu_to_je32(offset); ri.csize = cpu_to_je32(cdatalen); ri.dsize = cpu_to_je32(datalen); - ri.compr = comprtype; + ri.compr = comprtype & 0xff; + ri.usercompr = (comprtype >> 8) & 0xff; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); - ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen)); + ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); - new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, ALLOC_GC); + 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)); @@ -1258,12 +1232,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era f->metadata = NULL; } } - if (comprbuf) kfree(comprbuf); - kunmap(pg); - /* XXX: Does the page get freed automatically? */ - /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */ - page_cache_release(pg); + jffs2_gc_release_page(c, pg_ptr, &pg); return ret; } diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index 1c0bebc6b60e2..1f8fb486cf9de 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: ioctl.c,v 1.7 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $ * */ @@ -18,6 +18,6 @@ int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, { /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which will include compression support etc. */ - return -EINVAL; + return -ENOTTY; } diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index d81680fe6aead..b0bbe8a01cc26 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: malloc.c,v 1.25 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $ * */ diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 6c94c36998f10..b359f8a14220d 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.80 2003/10/04 08:33:06 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_sb_info *c, struct jffs2_full_dirent *new /* 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_sb_info *c, ino_t ino, struct jffs2_inode 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_sb_info *c, ino_t ino, struct jffs2_inode /* 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_sb_info *c, ino_t ino, struct jffs2_inode /* 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_sb_info *c, ino_t ino, struct jffs2_inode /* 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_sb_info *c, ino_t ino, struct jffs2_inode 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_sb_info *c, ino_t ino, struct jffs2_inode /* 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 *root, struct jffs2_sb_info *c) jffs2_free_node_frag(frag); frag = parent; + + cond_resched(); } } diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index d36f29aa59919..cea0c13901849 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.104 2003/10/08 11:45:11 dwmw2 Exp $ + * $Id: nodelist.h,v 1.119 2004/05/26 12:28:12 gleixner Exp $ * */ @@ -44,6 +44,39 @@ #define D2(x) #endif +#define JFFS2_NATIVE_ENDIAN + +/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from + whatever OS we're actually running on here too. */ + +#if defined(JFFS2_NATIVE_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){x}) +#define cpu_to_je32(x) ((jint32_t){x}) +#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) + +#define je16_to_cpu(x) ((x).v16) +#define je32_to_cpu(x) ((x).v32) +#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) +#elif defined(JFFS2_BIG_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) +#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) +#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) + +#define je16_to_cpu(x) (be16_to_cpu(x.v16)) +#define je32_to_cpu(x) (be32_to_cpu(x.v32)) +#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) +#elif defined(JFFS2_LITTLE_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) +#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) +#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) + +#define je16_to_cpu(x) (le16_to_cpu(x.v16)) +#define je32_to_cpu(x) (le32_to_cpu(x.v32)) +#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) +#else +#error wibble +#endif + /* This is all we need to keep in-core for each raw node during normal operation. As and when we do read_inode on a particular inode, we can @@ -59,13 +92,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 +106,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 @@ -246,9 +277,9 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *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), \ @@ -268,6 +299,57 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) } \ } 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 */ @@ -281,13 +363,13 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) #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) @@ -311,7 +393,6 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root) /* 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, @@ -330,6 +411,7 @@ struct rb_node *rb_prev(struct rb_node *); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); /* nodemgmt.c */ +int jffs2_thread_should_wake(struct jffs2_sb_info *c); int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio); int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); @@ -383,18 +465,13 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *); int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); /* read.c */ -int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len); +int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_full_dnode *fd, unsigned char *buf, + int ofs, int len); int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 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, - uint32_t *datalen, uint32_t *cdatalen); -int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, - unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); - /* scan.c */ int jffs2_scan_medium(struct jffs2_sb_info *c); void jffs2_rotate_lists(struct jffs2_sb_info *c); @@ -404,8 +481,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -void jffs2_erase_pending_blocks(struct jffs2_sb_info *c); -void jffs2_erase_pending_trigger(struct jffs2_sb_info *c); +void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); #ifdef CONFIG_JFFS2_FS_NAND /* wbuf.c */ @@ -413,11 +489,6 @@ 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); #endif -/* compr_zlib.c */ -int jffs2_zlib_init(void); -void jffs2_zlib_exit(void); - #endif /* __JFFS2_NODELIST_H__ */ diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 980de2e22cd82..5043d1a6f3d16 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.102 2003/10/08 17:21:19 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $ * */ @@ -209,8 +209,6 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui if (list_empty(&c->free_list)) { - DECLARE_WAITQUEUE(wait, current); - if (!c->nr_erasing_blocks && !list_empty(&c->erasable_list)) { struct jffs2_eraseblock *ejeb; @@ -243,30 +241,12 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui 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 */ @@ -321,9 +301,11 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui 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)) { @@ -420,31 +402,31 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref 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->dirty_size += ref->totlen; - c->dirty_size += ref->totlen; + 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) { @@ -465,8 +447,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref } 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; @@ -497,30 +479,6 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref 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 @@ -572,12 +530,12 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref 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 */ @@ -750,3 +708,34 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c) } } #endif /* CONFIG_JFFS2_FS_DEBUG */ + +int jffs2_thread_should_wake(struct jffs2_sb_info *c) +{ + int ret = 0; + uint32_t dirty; + + if (c->unchecked_size) { + D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", + c->unchecked_size, c->checked_ino)); + return 1; + } + + /* dirty_size contains blocks on erase_pending_list + * those blocks are counted in c->nr_erasing_blocks. + * If one block is actually erased, it is not longer counted as dirty_space + * but it is counted in c->nr_erasing_blocks, so we add it and subtract it + * with c->nr_erasing_blocks * c->sector_size again. + * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks + * This helps us to force gc and pick eventually a clean block to spread the load. + */ + dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; + + if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && + (dirty > c->nospc_dirty_size)) + ret = 1; + + D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", + c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); + + return ret; +} diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 11f3a5a35dfe2..9a7e01ca265a1 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-linux.h,v 1.37 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $ * */ @@ -23,6 +23,9 @@ #define kstatfs statfs #endif +struct kstatfs; +struct kvec; + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) #define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) #define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode) @@ -69,14 +72,6 @@ #define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime) #endif -/* Hmmm. P'raps generic code should only ever see versions of signal - functions which do the locking automatically? */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40) && !defined(__rh_config_h__) -#define current_sig_lock current->sigmask_lock -#else -#define current_sig_lock current->sighand->siglock -#endif - #define sleep_on_spinunlock(wq, s) \ do { \ DECLARE_WAITQUEUE(__wait, current); \ @@ -113,8 +108,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf)) #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_write_nand_badblock(c,jeb,bad_offset) (1) #define jffs2_nand_flash_setup(c) (0) #define jffs2_nand_flash_cleanup(c) do {} while(0) #define jffs2_wbuf_dirty(c) (0) @@ -130,9 +124,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len) -struct kstatfs; -struct kvec; /* wbuf.c */ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino); int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); @@ -140,13 +132,19 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode); 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_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); void jffs2_wbuf_timeout(unsigned long data); void jffs2_wbuf_process(void *data); int jffs2_nand_flash_setup(struct jffs2_sb_info *c); void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); #endif /* NAND */ +/* erase.c */ +static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) +{ + OFNI_BS_2SFFJ(c)->s_dirt = 1; +} + /* background.c */ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); @@ -184,13 +182,26 @@ int jffs2_statfs (struct super_block *, struct kstatfs *); void jffs2_write_super (struct super_block *); int jffs2_remount_fs (struct super_block *, int *, char *); int jffs2_do_fill_super(struct super_block *sb, void *data, int silent); +void jffs2_gc_release_inode(struct jffs2_sb_info *c, + struct jffs2_inode_info *f); +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, + int inum, int nlink); + +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + unsigned long offset, + unsigned long *priv); +void jffs2_gc_release_page(struct jffs2_sb_info *c, + unsigned char *pg, + unsigned long *priv); +int jffs2_flash_setup(struct jffs2_sb_info *c); +void jffs2_flash_cleanup(struct jffs2_sb_info *c); + /* writev.c */ int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); -/* super.c */ - #endif /* __JFFS2_OS_LINUX_H__ */ diff --git a/fs/jffs2/proc.c b/fs/jffs2/proc.c new file mode 100644 index 0000000000000..36c0a0e78a4e7 --- /dev/null +++ b/fs/jffs2/proc.c @@ -0,0 +1,217 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi , + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: proc.c,v 1.3 2004/06/24 09:51:38 havasi Exp $ + * + * Files in /proc/fs/jffs2 directory: + * compr_list + * read: shows the list of the loaded compressors + * (name, priority, enadbled/disabled) + * write: compressors can be enabled/disabled and + * the priority of them can be changed, + * required formats: + * enable COMPRESSOR_NAME + * disble COMPRESSOR_NAME + * priority NEW_PRIORITY COMPRESSOR_NAME + * compr_mode + * read: shows the name of the actual compression mode + * write: sets the actual comperession mode + * compr_stat + * read: shows compression statistics + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "compr.h" + +extern struct proc_dir_entry *jffs_proc_root; + +/* Structure for top-level entry in '/proc/fs' directory */ +static struct proc_dir_entry *jffs2_proc_root; + +/* Structure for files in /proc/fs/jffs2 directory */ +static struct proc_dir_entry *jffs2_proc_compr_stat; +static struct proc_dir_entry *jffs2_proc_compr_mode; + +/* Read the JFFS2 'compr_stat' file */ + +static int jffs2_proc_stat_read (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0,i; + char *stat = jffs2_stats(); + + if (strlen(stat)=strlen(stat)) *eof = 1; + else *eof = 0; + kfree(stat); + return len; +} + + +/* Read the JFFS2 'compr_mode' file */ + +static int jffs2_proc_mode_read (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + if (strlen(jffs2_get_compression_mode_name())+1>count) { + /* it should not happen */ + *eof = 1; + return 0; + } + len += sprintf(page, "%s\n",jffs2_get_compression_mode_name()); + *eof = 1; + return len; +} + +/* Write the JFFS2 'compr_mode' file + * sets the actual compression mode + */ + +static int jffs2_proc_mode_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char *compr_name; + + /* collect the name of the compression mode and set it */ + compr_name = kmalloc(count+1,GFP_KERNEL); + if (sscanf(buffer,"%s",compr_name)>0) { + if (jffs2_set_compression_mode_name(compr_name)) { + printk(KERN_WARNING "JFFS2: error switching compression mode. Invalid parameter (%s)?\n",compr_name); + } + } + else { + printk(KERN_WARNING "JFFS2: error: parameter missing\n"); + } + kfree(compr_name); + return count; +} + +/* Read the JFFS2 'compr_list' file */ + +static int jffs2_proc_list_read (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + char *list = jffs2_list_compressors(); + if (strlen(list)+1>count) { + /* it should not happen */ + *eof = 1; + kfree(list); + return 0; + } + len += sprintf(page,"%s",list); + *eof = 1; + kfree(list); + return len; +} + +/* Write the JFFS2 'compr_list' file + * enable/disable a compressor or set the priority of it + */ + +static int jffs2_proc_list_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int prior; + char *compr_name,*compr_cmd; + + compr_name = kmalloc(count+1,GFP_KERNEL); + compr_cmd = kmalloc(count+1,GFP_KERNEL); + if (!compr_name) { + printk(KERN_WARNING "JFFS2: unable to allocate memory\n"); + goto list_write_end; + } + compr_name[0] = 0; + + if (sscanf(buffer,"priority %d %s",&prior,compr_name)>1) { + jffs2_set_compressor_priority(compr_name, prior); + goto list_write_end; + } + if (sscanf(buffer,"enable %s",compr_name)>0) { + jffs2_enable_compressor_name(compr_name); + goto list_write_end; + } + if (sscanf(buffer,"disable %s",compr_name)>0) { + jffs2_disable_compressor_name(compr_name); + goto list_write_end; + } + printk(KERN_WARNING "JFFS2: usage of /proc/fs/jffs2/compr_list:\n" + " echo \"enable COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n" + " echo \"disable COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n" + " echo \"priority NEW_PRIORITY COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n"); +list_write_end: + kfree(compr_cmd); + kfree(compr_name); + return count; +} + +/* Register a JFFS2 proc directory */ + +int jffs2_proc_init(void) +{ + jffs2_proc_root = proc_mkdir("jffs2", proc_root_fs); + + /* create entry for 'compr_stat' file */ + if ((jffs2_proc_compr_stat = create_proc_entry ("compr_stat", 0, jffs2_proc_root))) { + jffs2_proc_compr_stat->read_proc = jffs2_proc_stat_read; + } + else { + return -ENOMEM; + } + /* create entry for 'compr_mode' file */ + if ((jffs2_proc_compr_mode = create_proc_entry ("compr_mode", 0, jffs2_proc_root))) { + jffs2_proc_compr_mode->read_proc = jffs2_proc_mode_read; + jffs2_proc_compr_mode->write_proc = jffs2_proc_mode_write; + } + else { + return -ENOMEM; + } + /* create entry for 'compr_list' file */ + if ((jffs2_proc_compr_mode = create_proc_entry ("compr_list", 0, jffs2_proc_root))) { + jffs2_proc_compr_mode->read_proc = jffs2_proc_list_read; + jffs2_proc_compr_mode->write_proc = jffs2_proc_list_write; + } + else { + return -ENOMEM; + } + return 0; +} + + +/* Unregister a JFFS2 proc directory */ + +int jffs2_proc_exit(void) +{ +#if LINUX_VERSION_CODE < 0x020300 + remove_proc_entry ("compr_stat", &jffs2_proc_root); + remove_proc_entry ("compr_mode", &jffs2_proc_root); + remove_proc_entry ("compr_list", &jffs2_proc_root); + remove_proc_entry ("jffs2", &proc_root_fs); +#else + remove_proc_entry ("compr_stat", jffs2_proc_root); + remove_proc_entry ("compr_mode", jffs2_proc_root); + remove_proc_entry ("compr_list", jffs2_proc_root); + remove_proc_entry ("jffs2", proc_root_fs); +#endif + return 0; +} diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c index adeeeb19aa4ba..37375950f6925 100644 --- a/fs/jffs2/read.c +++ b/fs/jffs2/read.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $ * */ @@ -18,8 +18,11 @@ #include #include #include "nodelist.h" +#include "compr.h" -int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) +int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_full_dnode *fd, unsigned char *buf, + int ofs, int len) { struct jffs2_raw_inode *ri; size_t readlen; @@ -127,7 +130,7 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig if (ri->compr != JFFS2_COMPR_NONE) { D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); - ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); + ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); if (ret) { printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); goto out_decomprbuf; @@ -195,7 +198,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n", frag->ofs+fragofs, frag->ofs+fragofs+readlen, ref_offset(frag->node->raw), ref_flags(frag->node->raw))); - ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); + ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); D2(printk(KERN_DEBUG "node read done\n")); if (ret) { D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret)); @@ -231,7 +234,7 @@ char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f) } buf[f->metadata->size]=0; - ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size); + ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size); up(&f->sem); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index ea4bbf7f237f0..54a5d1ac3aa16 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.107 2003/10/04 08:33:06 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_inode_info *f) 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 jffs2_sb_info *c, struct jffs2_inode_in 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_info *c, struct jffs2_inode_info *f, } } 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(struct jffs2_sb_info *c, 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(struct jffs2_sb_info *c, 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(struct jffs2_sb_info *c, 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(struct jffs2_sb_info *c, 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_sb_info *c, struct jffs2_inode_info *f) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); up(&f->sem); - - if(deleted) - up(&c->alloc_sem); } diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 6e59cad2da9c1..67a1d1bce71d8 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.104 2003/10/11 14:52:48 dwmw2 Exp $ + * $Id: scan.c,v 1.110 2004/06/17 17:15:31 gleixner Exp $ * */ #include @@ -285,8 +285,6 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; - int wasempty = 0; - uint32_t empty_start = 0; #ifdef CONFIG_JFFS2_FS_NAND int cleanmarkerfound = 0; #endif @@ -339,8 +337,6 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo switch (ret) { case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; case 1: return BLK_STATE_ALLDIRTY; - case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */ - case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ default: return ret; } } @@ -359,6 +355,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo noise = 10; +scan_more: while(ofs < jeb->offset + c->sector_size) { D1(ACCT_PARANOIA_CHECK(jeb)); @@ -398,42 +395,52 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { - uint32_t inbuf_ofs = ofs - buf_ofs + 4; - uint32_t scanend; + uint32_t inbuf_ofs; + uint32_t empty_start; empty_start = ofs; ofs += 4; - /* If scanning empty space after only a cleanmarker, don't - bother scanning the whole block */ - if (unlikely(empty_start == jeb->offset + c->cleanmarker_size && - jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len)) - scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs; - else - scanend = buf_len; - D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); - while (inbuf_ofs < scanend) { - if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) - goto emptyends; + more_empty: + inbuf_ofs = ofs - buf_ofs; + while (inbuf_ofs < buf_len) { + if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) { + printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", + empty_start, ofs); + DIRTY_SPACE(ofs-empty_start); + goto scan_more; + } inbuf_ofs+=4; ofs += 4; } /* Ran off end. */ - D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs)); + D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs)); - if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && - c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size) + /* If we're only checking the beginning of a block with a cleanmarker, + bail now */ + if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && + c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) { + D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE)); return BLK_STATE_CLEANMARKER; - wasempty = 1; - continue; - } else if (wasempty) { - emptyends: - printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); - DIRTY_SPACE(ofs-empty_start); - wasempty = 0; - continue; + } + + /* See how much more there is to read in this eraseblock... */ + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + if (!buf_len) { + /* No more to read. Break out of main loop without marking + this range of empty space as dirty (because it's not) */ + D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n", + empty_start)); + break; + } + D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + goto more_empty; } if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { @@ -554,7 +561,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo 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)); @@ -610,7 +617,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo } if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size - && (!jeb->first_node || jeb->first_node->next_in_ino) ) + && (!jeb->first_node || !jeb->first_node->next_in_ino) ) return BLK_STATE_CLEANMARKER; /* move blocks with max 4 byte dirty space to cleanlist */ @@ -634,6 +641,9 @@ static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info 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"); @@ -645,7 +655,7 @@ static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info ic->nodes = (void *)ic; jffs2_add_ino_cache(c, ic); if (ino == 1) - ic->nlink=1; + ic->nlink = 1; return ic; } @@ -698,7 +708,7 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc /* 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; @@ -775,7 +785,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo 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; diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index b657c55d5f51f..23dad4785e8bb 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.90 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: super.c,v 1.96 2004/07/13 08:57:30 dwmw2 Exp $ * */ @@ -24,6 +24,7 @@ #include #include #include +#include "compr.h" #include "nodelist.h" static void jffs2_put_super(struct super_block *); @@ -266,7 +267,7 @@ static void jffs2_put_super (struct super_block *sb) jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); kfree(c->blocks); - jffs2_nand_flash_cleanup(c); + jffs2_flash_cleanup(c); kfree(c->inocache_list); if (c->mtd->sync) c->mtd->sync(c->mtd); @@ -294,7 +295,7 @@ static int __init init_jffs2_fs(void) int ret; printk(KERN_INFO "JFFS2 version 2.2." -#ifdef CONFIG_FS_JFFS2_NAND +#ifdef CONFIG_JFFS2_FS_NAND " (NAND)" #endif " (C) 2001-2003 Red Hat, Inc.\n"); @@ -307,15 +308,22 @@ static int __init init_jffs2_fs(void) printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); return -ENOMEM; } - ret = jffs2_zlib_init(); +#ifdef CONFIG_JFFS2_PROC + ret = jffs2_proc_init(); if (ret) { - printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); + printk(KERN_ERR "JFFS2 error: Failed to initialise proc interface\n"); + goto out; + } +#endif + ret = jffs2_compressors_init(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n"); goto out; } ret = jffs2_create_slab_caches(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); - goto out_zlib; + goto out_compressors; } ret = register_filesystem(&jffs2_fs_type); if (ret) { @@ -326,8 +334,11 @@ static int __init init_jffs2_fs(void) out_slab: jffs2_destroy_slab_caches(); - out_zlib: - jffs2_zlib_exit(); + out_compressors: + jffs2_compressors_exit(); +#ifdef CONFIG_JFFS2_PROC + jffs2_proc_exit(); +#endif out: return ret; } @@ -336,7 +347,10 @@ static void __exit exit_jffs2_fs(void) { unregister_filesystem(&jffs2_fs_type); jffs2_destroy_slab_caches(); - jffs2_zlib_exit(); + jffs2_compressors_exit(); +#ifdef CONFIG_JFFS2_PROC + jffs2_proc_exit(); +#endif kmem_cache_destroy(jffs2_inode_cachep); } diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index d0adaf6fd5df8..958cafb9068e7 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $ + * $Id: symlink.c,v 1.13 2004/07/13 08:59:04 dwmw2 Exp $ * */ diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 956f98d8dd359..2470eef95ae5a 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -2,12 +2,14 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright (C) 2004 Thomas Gleixner * * Created by David Woodhouse + * Modified debugged and enhanced by Thomas Gleixner * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.53 2003/10/11 11:46:09 dwmw2 Exp $ + * $Id: wbuf.c,v 1.70 2004/07/13 08:58:25 dwmw2 Exp $ * */ @@ -27,7 +29,7 @@ static unsigned char *brokenbuf; #endif /* max. erase failures before we mark a block bad */ -#define MAX_ERASE_FAILURES 5 +#define MAX_ERASE_FAILURES 2 /* two seconds timeout for timed wbuf-flushing */ #define WBUF_FLUSH_TIMEOUT 2 * HZ @@ -179,10 +181,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) first_raw = &jeb->first_node; while (*first_raw && (ref_obsolete(*first_raw) || - (ref_offset(*first_raw) + (*first_raw)->totlen) < c->wbuf_ofs)) { + (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) { D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", ref_offset(*first_raw), ref_flags(*first_raw), - (ref_offset(*first_raw) + (*first_raw)->totlen), + (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)), c->wbuf_ofs)); first_raw = &(*first_raw)->next_phys; } @@ -195,13 +197,13 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) } start = ref_offset(*first_raw); - end = ref_offset(*first_raw) + (*first_raw)->totlen; + end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw); /* Find the last node to be recovered */ raw = first_raw; while ((*raw)) { if (!ref_obsolete(*raw)) - end = ref_offset(*raw) + (*raw)->totlen; + end = ref_offset(*raw) + ref_totlen(c, jeb, *raw); raw = &(*raw)->next_phys; } @@ -295,7 +297,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) return; raw2->flash_offset = ofs | REF_OBSOLETE; - raw2->totlen = (*first_raw)->totlen; + raw2->__totlen = ref_totlen(c, jeb, *first_raw); raw2->next_phys = NULL; raw2->next_in_ino = NULL; @@ -336,24 +338,26 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) raw = first_raw; while (*raw) { + uint32_t rawlen = ref_totlen(c, jeb, *raw); + D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", - (*raw)->totlen, ref_offset(*raw), ref_flags(*raw), ofs)); + rawlen, ref_offset(*raw), ref_flags(*raw), ofs)); if (ref_obsolete(*raw)) { /* Shouldn't really happen much */ - new_jeb->dirty_size += (*raw)->totlen; - new_jeb->free_size -= (*raw)->totlen; - c->dirty_size += (*raw)->totlen; + new_jeb->dirty_size += rawlen; + new_jeb->free_size -= rawlen; + c->dirty_size += rawlen; } else { - new_jeb->used_size += (*raw)->totlen; - new_jeb->free_size -= (*raw)->totlen; - jeb->dirty_size += (*raw)->totlen; - jeb->used_size -= (*raw)->totlen; - c->dirty_size += (*raw)->totlen; + new_jeb->used_size += rawlen; + new_jeb->free_size -= rawlen; + jeb->dirty_size += rawlen; + jeb->used_size -= rawlen; + c->dirty_size += rawlen; } - c->free_size -= (*raw)->totlen; + c->free_size -= rawlen; (*raw)->flash_offset = ofs | ref_flags(*raw); - ofs += (*raw)->totlen; + ofs += rawlen; new_jeb->last_node = *raw; raw = &(*raw)->next_phys; @@ -422,6 +426,9 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); + } else { + /* Pad with JFFS2_DIRTY_BITMASK */ + memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); } } /* else jffs2_flash_writev has actually filled in the rest of the @@ -454,31 +461,34 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) return ret; } - /* Adjusting free size of next block only, if it's called from fsync ! */ - if (pad == 2) { - D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n")); - spin_lock(&c->erase_completion_lock); - if (!c->nextblock) - BUG(); + spin_lock(&c->erase_completion_lock); + + /* Adjust free size of the block if we padded. */ + if (pad) { + struct jffs2_eraseblock *jeb; + + jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + + D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n", + (jeb==c->nextblock)?"next":"", jeb->offset)); + /* wbuf_pagesize - wbuf_len is the amount of space that's to be padded. If there is less free space in the block than that, something screwed up */ - if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) { + if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) { printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len); printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", - c->nextblock->offset, c->nextblock->free_size); + jeb->offset, jeb->free_size); BUG(); } - c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len); + jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len); c->free_size -= (c->wbuf_pagesize - c->wbuf_len); - c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len); + jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len); c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); - spin_unlock(&c->erase_completion_lock); } /* Stick any now-obsoleted blocks on the erase_pending_list */ - spin_lock(&c->erase_completion_lock); jffs2_refile_wbuf_blocks(c); jffs2_clear_wbuf_ino_list(c); spin_unlock(&c->erase_completion_lock); @@ -512,8 +522,12 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) old_wbuf_ofs = c->wbuf_ofs; old_wbuf_len = c->wbuf_len; - while (old_wbuf_len && - old_wbuf_ofs == c->wbuf_ofs) { + if (c->unchecked_size) { + /* GC won't make any progress for a while */ + D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); + ret = __jffs2_flush_wbuf(c, 2); + } else while (old_wbuf_len && + old_wbuf_ofs == c->wbuf_ofs) { up(&c->alloc_sem); @@ -835,9 +849,8 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb size_t retlen; int oob_size; - oob_size = c->mtd->oobsize; - /* allocate a buffer for all oob data in this sector */ + oob_size = c->mtd->oobsize; len = 4 * oob_size; buf = kmalloc(len, GFP_KERNEL); if (!buf) { @@ -861,35 +874,23 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb goto out; } - /* Special check for first two pages */ - for (page = 0; page < 2 * oob_size; page += oob_size) { - /* Check for bad block marker */ - if (buf[page+c->badblock_pos] != 0xff) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset)); - /* Return 2 for bad and 3 for failed block - bad goes to list_bad and failed to list_erase */ - ret = (!page) ? 2 : 3; + /* Special check for first page */ + for(i = 0; i < oob_size ; i++) { + /* Yeah, we know about the cleanmarker. */ + if (mode && i >= c->fsdata_pos && + i < c->fsdata_pos + c->fsdata_len) + continue; + + if (buf[i] != 0xFF) { + D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", + buf[page+i], page+i, jeb->offset)); + ret = 1; goto out; } - for(i = 0; i < oob_size ; i++) { - /* Yeah, we know about the cleanmarker. */ - if (mode && i >= c->fsdata_pos && - i < c->fsdata_pos+c->fsdata_len) - continue; - - if (buf[page+i] != 0xFF) { - D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", - buf[page+i], page+i, jeb->offset)); - ret = 1; - goto out; - } - } - /* only the first page can contain a cleanmarker !*/ - mode = 0; - } + } /* we know, we are aligned :) */ - for (; page < len; page += sizeof(long)) { + for (page = oob_size; page < len; page += sizeof(long)) { unsigned long dat = *(unsigned long *)(&buf[page]); if(dat != -1) { ret = 1; @@ -912,7 +913,7 @@ out: int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; - unsigned char buf[32]; + unsigned char buf[2 * NAND_MAX_OOBSIZE]; unsigned char *p; int ret, i, cnt, retval = 0; size_t retlen, offset; @@ -923,6 +924,11 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo /* Loop through the physical blocks */ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { + /* Check first if the block is bad. */ + if (c->mtd->block_isbad (c->mtd, offset)) { + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); + return 2; + } /* * We read oob data from page 0 and 1 of the block. * page 0 contains cleanmarker and badblock info @@ -939,19 +945,6 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo return -EIO; } - /* Check for bad block marker */ - if (buf[c->badblock_pos] != 0xff) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n", - jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos)); - return 2; - } - - /* Check for failure counter in the second page */ - if (buf[c->badblock_pos + oob_size] != 0xff) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size])); - return 3; - } - /* Check cleanmarker only on the first physical block */ if (!cnt) { n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); @@ -1002,136 +995,100 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc } /* - * We try to get the failure count of this block. - */ -int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - - unsigned char buf[16]; - int ret; - size_t retlen; - int oob_size; - - oob_size = c->mtd->oobsize; - - ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf); - - if (ret) { - D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); - return ret; - } - - if (retlen < oob_size) { - D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset)); - return -EIO; - } - - jeb->bad_count = buf[c->badblock_pos]; - return 0; -} - -/* - * On NAND we try to mark this block bad. We try to write how often - * the block was erased and mark it finaly bad, if the count - * is > MAX_ERASE_FAILURES. We read this information on mount ! - * jeb->bad_count contains the count before this erase. + * On NAND we try to mark this block bad. If the block was erased more + * than MAX_ERASE_FAILURES we mark it finaly bad. * Don't care about failures. This block remains on the erase-pending * or badblock list as long as nobody manipulates the flash with * a bootloader or something like that. */ -int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { - unsigned char buf = 0x0; int ret; - size_t retlen; /* if the count is < max, we try to write the counter to the 2nd page oob area */ - if( ++jeb->bad_count < MAX_ERASE_FAILURES) { - buf = (unsigned char)jeb->bad_count; - c->badblock_pos += c->mtd->oobblock; - } - - ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf); + if( ++jeb->bad_count < MAX_ERASE_FAILURES) + return 0; + + if (!c->mtd->block_markbad) + return 1; // What else can we do? + + D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset)); + ret = c->mtd->block_markbad(c->mtd, bad_offset); if (ret) { D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); return ret; } - if (retlen != 1) { - D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen)); - return ret; - } - return 0; + return 1; } -#define JFFS2_OOB_ECCPOS0 0 -#define JFFS2_OOB_ECCPOS1 1 -#define JFFS2_OOB_ECCPOS2 2 -#define JFFS2_OOB_ECCPOS3 3 -#define JFFS2_OOB_ECCPOS4 6 -#define JFFS2_OOB_ECCPOS5 7 - -#define NAND_JFFS2_OOB8_FSDAPOS 6 -#define NAND_JFFS2_OOB16_FSDAPOS 8 -#define NAND_JFFS2_OOB8_FSDALEN 2 #define NAND_JFFS2_OOB16_FSDALEN 8 -static struct nand_oobinfo jffs2_oobinfo_swecc = { - .useecc = 1, - .eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2, - JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5} -}; - static struct nand_oobinfo jffs2_oobinfo_docecc = { - .useecc = 1, + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, .eccpos = {0,1,2,3,4,5} }; - -int jffs2_nand_flash_setup(struct jffs2_sb_info *c) +int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { + struct nand_oobinfo *oinfo = &c->mtd->oobinfo; + + /* Do this only, if we have an oob buffer */ + if (!c->mtd->oobsize) + return 0; + /* Cleanmarker is out-of-band, so inline size zero */ c->cleanmarker_size = 0; - /* Initialise write buffer */ - c->wbuf_pagesize = c->mtd->oobblock; - c->wbuf_ofs = 0xFFFFFFFF; - - /* FIXME: If we had a generic way of describing the hardware's - use of OOB area, we could perhaps make this generic too. */ - switch(c->mtd->ecctype) { - case MTD_ECC_SW: - D1(printk(KERN_DEBUG "JFFS2 using software ECC\n")); - c->oobinfo = &jffs2_oobinfo_swecc; - if (c->mtd->oobsize == 8) { - c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS; - c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN; - } else { - c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS; + /* Should we use autoplacement ? */ + if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { + D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); + /* Get the position of the free bytes */ + if (!oinfo->oobfree[0][0]) { + printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n"); + return -ENOSPC; + } + c->fsdata_pos = oinfo->oobfree[0][0]; + c->fsdata_len = oinfo->oobfree[0][1]; + if (c->fsdata_len > 8) + c->fsdata_len = 8; + } else { + /* This is just a legacy fallback and should go away soon */ + switch(c->mtd->ecctype) { + case MTD_ECC_RS_DiskOnChip: + printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n"); + c->oobinfo = &jffs2_oobinfo_docecc; + c->fsdata_pos = 6; c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; + c->badblock_pos = 15; + break; + + default: + D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); + return -EINVAL; } - c->badblock_pos = NAND_BADBLOCK_POS; - break; - - case MTD_ECC_RS_DiskOnChip: - D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n")); - c->oobinfo = &jffs2_oobinfo_docecc; - c->fsdata_pos = 6; - c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; - c->badblock_pos = 15; - break; - - default: - printk("JFFS2 doesn't yet know how to handle ECC type %d\n", - c->mtd->ecctype); - return -EINVAL; } + return 0; +} +int jffs2_nand_flash_setup(struct jffs2_sb_info *c) +{ + int res; + + /* Initialise write buffer */ + c->wbuf_pagesize = c->mtd->oobblock; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; + res = jffs2_nand_set_oobinfo(c); + #ifdef BREAKME if (!brokenbuf) brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); @@ -1141,7 +1098,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) } memset(brokenbuf, 0xdb, c->wbuf_pagesize); #endif - return 0; + return res; } void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 11c7610452158..1253c48dadebe 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.75 2003/10/08 11:45:11 dwmw2 Exp $ + * $Id: write.c,v 1.85 2004/07/13 08:58:25 dwmw2 Exp $ * */ @@ -18,6 +18,7 @@ #include #include #include "nodelist.h" +#include "compr.h" int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) @@ -31,7 +32,6 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint memset(ic, 0, sizeof(*ic)); - init_MUTEX_LOCKED(&f->sem); f->inocache = ic; f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; @@ -133,7 +133,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 fn->raw = raw; raw->flash_offset = flash_ofs; - raw->totlen = PAD(sizeof(*ri)+datalen); + raw->__totlen = PAD(sizeof(*ri)+datalen); raw->next_phys = NULL; ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, @@ -275,11 +275,11 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff fd->raw = raw; raw->flash_offset = flash_ofs; - raw->totlen = PAD(sizeof(*rd)+namelen); + 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); + (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); 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); @@ -359,7 +359,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, while(writelen) { struct jffs2_full_dnode *fn; unsigned char *comprbuf = NULL; - unsigned char comprtype = JFFS2_COMPR_NONE; + uint16_t comprtype = JFFS2_COMPR_NONE; uint32_t phys_ofs, alloclen; uint32_t datalen, cdatalen; int retried = 0; @@ -373,24 +373,10 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, break; } down(&f->sem); - datalen = writelen; - cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen); + datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); + cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); - 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(c, f, buf, &comprbuf, &datalen, &cdatalen); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); @@ -403,14 +389,14 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ri->offset = cpu_to_je32(offset); ri->csize = cpu_to_je32(cdatalen); ri->dsize = cpu_to_je32(datalen); - ri->compr = comprtype; + ri->compr = comprtype & 0xff; + ri->usercompr = (comprtype >> 8 ) & 0xff; 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, ALLOC_NORETRY); - if (comprtype != JFFS2_COMPR_NONE) - kfree(comprbuf); + jffs2_free_comprbuf(comprbuf, buf); if (IS_ERR(fn)) { ret = PTR_ERR(fn); @@ -559,48 +545,75 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t alloclen, phys_ofs; int ret; - rd = jffs2_alloc_raw_dirent(); - if (!rd) - return -ENOMEM; + if (1 /* alternative branch needs testing */ || + !jffs2_can_mark_obsolete(c)) { + /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */ - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION); - if (ret) { + rd = jffs2_alloc_raw_dirent(); + if (!rd) + return -ENOMEM; + + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION); + if (ret) { + jffs2_free_raw_dirent(rd); + return ret; + } + + down(&dir_f->sem); + + /* Build a deletion node */ + rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); + rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); + + rd->pino = cpu_to_je32(dir_f->inocache->ino); + rd->version = cpu_to_je32(++dir_f->highest_version); + rd->ino = cpu_to_je32(0); + rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + rd->type = DT_UNKNOWN; + 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, ALLOC_DELETION); + jffs2_free_raw_dirent(rd); - return ret; - } - down(&dir_f->sem); + if (IS_ERR(fd)) { + jffs2_complete_reservation(c); + up(&dir_f->sem); + return PTR_ERR(fd); + } - /* Build a deletion node */ - rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); - rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); - rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); - rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); + /* File it. This will mark the old one obsolete. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); + up(&dir_f->sem); + } else { + struct jffs2_full_dirent **prev = &dir_f->dents; + uint32_t nhash = full_name_hash(name, namelen); - rd->pino = cpu_to_je32(dir_f->inocache->ino); - rd->version = cpu_to_je32(++dir_f->highest_version); - rd->ino = cpu_to_je32(0); - rd->mctime = cpu_to_je32(get_seconds()); - rd->nsize = namelen; - rd->type = DT_UNKNOWN; - rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); - rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); + down(&dir_f->sem); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); - - jffs2_free_raw_dirent(rd); + while ((*prev) && (*prev)->nhash <= nhash) { + if ((*prev)->nhash == nhash && + !memcmp((*prev)->name, name, namelen) && + !(*prev)->name[namelen]) { + struct jffs2_full_dirent *this = *prev; - if (IS_ERR(fd)) { - jffs2_complete_reservation(c); + D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n", + this->ino, ref_offset(this->raw))); + + *prev = this->next; + jffs2_mark_node_obsolete(c, (this->raw)); + jffs2_free_full_dirent(this); + break; + } + prev = &((*prev)->next); + } up(&dir_f->sem); - return PTR_ERR(fd); } - /* File it. This will mark the old one obsolete. */ - jffs2_add_fd_to_list(c, fd, &dir_f->dents); - - up(&dir_f->sem); - /* dead_f is NULL if this was a rename not a real unlink */ /* Also catch the !f->inocache case, where there was a dirent pointing to an inode which didn't exist. */ diff --git a/fs/jffs2/writev.c b/fs/jffs2/writev.c index c88bf7336e509..7e46e952d9329 100644 --- a/fs/jffs2/writev.c +++ b/fs/jffs2/writev.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $ + * $Id: writev.c,v 1.5 2004/07/13 08:58:25 dwmw2 Exp $ * */ diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index e1a49dca4abb3..162f06ef56fef 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -8,7 +8,7 @@ * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $ + * $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $ * */ @@ -43,6 +43,8 @@ #define JFFS2_COMPR_COPY 0x04 #define JFFS2_COMPR_DYNRUBIN 0x05 #define JFFS2_COMPR_ZLIB 0x06 +#define JFFS2_COMPR_LZO 0x07 +#define JFFS2_COMPR_LZARI 0x08 /* Compatibility flags. */ #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ #define JFFS2_NODE_ACCURATE 0x2000 @@ -87,39 +89,6 @@ typedef struct { uint16_t v16; } __attribute__((packed)) jint16_t; -#define JFFS2_NATIVE_ENDIAN - -/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from - whatever OS we're actually running on here too. */ - -#if defined(JFFS2_NATIVE_ENDIAN) -#define cpu_to_je16(x) ((jint16_t){x}) -#define cpu_to_je32(x) ((jint32_t){x}) -#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) - -#define je16_to_cpu(x) ((x).v16) -#define je32_to_cpu(x) ((x).v32) -#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) -#elif defined(JFFS2_BIG_ENDIAN) -#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) -#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) -#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) - -#define je16_to_cpu(x) (be16_to_cpu(x.v16)) -#define je32_to_cpu(x) (be32_to_cpu(x.v32)) -#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) -#elif defined(JFFS2_LITTLE_ENDIAN) -#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) -#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) -#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) - -#define je16_to_cpu(x) (le16_to_cpu(x.v16)) -#define je32_to_cpu(x) (le32_to_cpu(x.v32)) -#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) -#else -#error wibble -#endif - struct jffs2_unknown_node { /* All start like this */ diff --git a/include/linux/jffs2_fs_i.h b/include/linux/jffs2_fs_i.h index db91aa6c0322f..14743de6cf593 100644 --- a/include/linux/jffs2_fs_i.h +++ b/include/linux/jffs2_fs_i.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_i.h,v 1.15 2002/11/12 09:42:49 dwmw2 Exp $ */ +/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */ #ifndef _JFFS2_FS_I #define _JFFS2_FS_I @@ -36,9 +36,11 @@ struct jffs2_inode_info { uint16_t flags; uint8_t usercompr; +#if !defined (__ECOS) #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) struct inode vfs_inode; #endif +#endif }; #endif /* _JFFS2_FS_I */ -- 2.53.0