diff -urN linux-2.4.32/fs/jffs2/GNUmakefile linux-2.4.32-uc0/fs/jffs2/GNUmakefile --- linux-2.4.32/fs/jffs2/GNUmakefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/GNUmakefile 2006-02-26 12:49:17.000000000 +0100 @@ -0,0 +1,49 @@ +# GNUmakefile for JFFS2 build outside kernel tree +# $Id: GNUmakefile,v 1.1 2002/09/12 00:23:22 davidm Exp $ + +LINUXDIR=/lib/modules/$(shell uname -r)/build + +ifndef VERSION +# Someone just typed 'make' in this directory. +# Invoke a kernel build process, with only this dir in SUBDIRS. + +modules: + $(MAKE) -C $(LINUXDIR) CONFIG_JFFS2_FS_NAND=y SUBDIRS=`pwd` modules + +nonand: + make -C $(LINUXDIR) SUBDIRS=`pwd` modules + +dep: + make -C $(LINUXDIR) SUBDIRS=`pwd` dep + +clean: + rm -f *.o + +else + +ifndef CONFIG_JFFS2_FS +# Invoked from a kernel build tree, but CONFIG_JFFS2_FS isn't set. +# That means we must have got here from the hack above - pretend +# CONFIG_JFFS2_FS _was_ set so that we actually build it, and also +# pull in the local MTD include files in case they're also newer than +# the ones in the kernel tree. And turn on debugging. + +CC += -I$(shell pwd)/../../include + +CONFIG_JFFS2_FS := m +EXTRA_CFLAGS += -DCONFIG_JFFS2_FS_DEBUG=1 -g -Werror + +ifeq ($(CONFIG_JFFS2_FS_NAND),y) +EXTRA_CFLAGS += -DCONFIG_JFFS2_FS_NAND=1 +endif +endif + +# Check whether we've put the JFFS2 stuff in the superblock and inode unions +OUT_OF_KERNEL_CFLAGS := $(shell grep -q jffs2 $(TOPDIR)/include/linux/fs.h || echo "-DJFFS2_OUT_OF_KERNEL") +EXTRA_CFLAGS += $(OUT_OF_KERNEL_CFLAGS) + + +include Makefile + + +endif diff -urN linux-2.4.32/fs/jffs2/LICENCE linux-2.4.32-uc0/fs/jffs2/LICENCE --- linux-2.4.32/fs/jffs2/LICENCE 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/LICENCE 2006-02-26 12:49:17.000000000 +0100 @@ -0,0 +1,35 @@ +The files in this directory and elsewhere which refer to this LICENCE +file are part of JFFS2, the Journalling Flash File System v2. + + Copyright (C) 2001, 2002 Red Hat, Inc. + +JFFS2 is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 or (at your option) any later +version. + +JFFS2 is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with JFFS2; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +As a special exception, if other files instantiate templates or use +macros or inline functions from these files, or you compile these +files and link them with other works to produce a work based on these +files, these files do not by themselves cause the resulting work to be +covered by the GNU General Public License. However the source code for +these files must still be made available in accordance with section (3) +of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based on +this file might be covered by the GNU General Public License. + +For information on obtaining alternative licences for JFFS2, see +http://sources.redhat.com/jffs2/jffs2-licence.html + + + $Id: LICENCE,v 1.1 2002/05/20 14:56:37 dwmw2 Exp $ diff -urN linux-2.4.32/fs/jffs2/README.Locking linux-2.4.32-uc0/fs/jffs2/README.Locking --- linux-2.4.32/fs/jffs2/README.Locking 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/README.Locking 2006-02-26 12:49:17.000000000 +0100 @@ -0,0 +1,116 @@ + $Id: README.Locking,v 1.4 2002/03/08 16:20:06 dwmw2 Exp $ + + JFFS2 LOCKING DOCUMENTATION + --------------------------- + +At least theoretically, JFFS2 does not require the Big Kernel Lock +(BKL), which was always helpfully obtained for it by Linux 2.4 VFS +code. It has its own locking, as described below. + +This document attempts to describe the existing locking rules for +JFFS2. It is not expected to remain perfectly up to date, but ought to +be fairly close. + + + alloc_sem + --------- + +The alloc_sem is a per-filesystem semaphore, used primarily to ensure +contiguous allocation of space on the medium. It is automatically +obtained during space allocations (jffs2_reserve_space()) and freed +upon write completion (jffs2_complete_reservation()). Note that +the garbage collector will obtain this right at the beginning of +jffs2_garbage_collect_pass() and release it at the end, thereby +preventing any other write activity on the file system during a +garbage collect pass. + +When writing new nodes, the alloc_sem must be held until the new nodes +have been properly linked into the data structures for the inode to +which they belong. This is for the benefit of NAND flash - adding new +nodes to an inode may obsolete old ones, and by holding the alloc_sem +until this happens we ensure that any data in the write-buffer at the +time this happens are part of the new node, not just something that +was written afterwards. Hence, we can ensure the newly-obsoleted nodes +don't actually get erased until the write-buffer has been flushed to +the medium. + +With the introduction of NAND flash support and the write-buffer, +the alloc_sem is also used to protect the wbuf-related members of the +jffs2_sb_info structure. Atomically reading the wbuf_len member to see +if the wbuf is currently holding any data is permitted, though. + +Ordering constraints: See f->sem. + + + File Semaphore f->sem + --------------------- + +This is the JFFS2-internal equivalent of the inode semaphore i->i_sem. +It protects the contents of the jffs2_inode_info private inode data, +including the linked list of node fragments (but see the notes below on +erase_completion_lock), etc. + +The reason that the i_sem itself isn't used for this purpose is to +avoid deadlocks with garbage collection -- the VFS will lock the i_sem +before calling a function which may need to allocate space. The +allocation may trigger garbage-collection, which may need to move a +node belonging to the inode which was locked in the first place by the +VFS. If the garbage collection code were to attempt to lock the i_sem +of the inode from which it's garbage-collecting a physical node, this +lead to deadlock, unless we played games with unlocking the i_sem +before calling the space allocation functions. + +Instead of playing such games, we just have an extra internal +semaphore, which is obtained by the garbage collection code and also +by the normal file system code _after_ allocation of space. + +Ordering constraints: + + 1. Never attempt to allocate space or lock alloc_sem with + any f->sem held. + 2. Never attempt to lock two file semaphores in one thread. + No ordering rules have been made for doing so. + + + erase_completion_lock spinlock + ------------------------------ + +This is used to serialise access to the eraseblock lists, to the +per-eraseblock lists of physical jffs2_raw_node_ref structures, and +(NB) the per-inode list of physical nodes. The latter is a special +case - see below. + +As the MTD API permits erase-completion callback functions to be +called from bottom-half (timer) context, and these functions access +the data structures protected by this lock, it must be locked with +spin_lock_bh(). + +Note that the per-inode list of physical nodes (f->nodes) is a special +case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in +the list are protected by the file semaphore f->sem. But the erase +code may remove _obsolete_ nodes from the list while holding only the +erase_completion_lock. So you can walk the list only while holding the +erase_completion_lock, and can drop the lock temporarily mid-walk as +long as the pointer you're holding is to a _valid_ node, not an +obsolete one. + +The erase_completion_lock is also used to protect the c->gc_task +pointer when the garbage collection thread exits. The code to kill the +GC thread locks it, sends the signal, then unlocks it - while the GC +thread itself locks it, zeroes c->gc_task, then unlocks on the exit path. + + node_free_sem + ------------- + +This semaphore is only used by the erase code which frees obsolete +node references and the jffs2_garbage_collect_deletion_dirent() +function. The latter function on NAND flash must read _obsolete_ nodes +to determine whether the 'deletion dirent' under consideration can be +discarded or whether it is still required to show that an inode has +been unlinked. Because reading from the flash may sleep, the +erase_completion_lock cannot be held, so an alternative, more +heavyweight lock was required to prevent the erase code from freeing +the jffs2_raw_node_ref structures in question while the garbage +collection code is looking at them. + +Suggestions for alternative solutions to this problem would be welcomed. diff -urN linux-2.4.32/fs/jffs2/dir.c linux-2.4.32-uc0/fs/jffs2/dir.c --- linux-2.4.32/fs/jffs2/dir.c 2003-11-28 19:26:21.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/dir.c 2006-02-26 12:49:17.000000000 +0100 @@ -972,6 +972,11 @@ then getting -ENOSPC, is quite bad :) */ + if ( new_dentry->d_inode ) + { + jffs2_unlink(new_dir_i, new_dentry); + } + /* Make a hard link */ ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1); if (ret) diff -urN linux-2.4.32/fs/jffs2/fs.c linux-2.4.32-uc0/fs/jffs2/fs.c --- linux-2.4.32/fs/jffs2/fs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/fs.c 2006-02-26 12:49:17.000000000 +0100 @@ -0,0 +1,351 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: fs.c,v 1.17 2002/08/21 10:42:12 dwmw2 Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +int jffs2_statfs(struct super_block *sb, struct statfs *buf) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + unsigned long avail; + + buf->f_type = JFFS2_SUPER_MAGIC; + buf->f_bsize = 1 << PAGE_SHIFT; + buf->f_blocks = c->flash_size >> PAGE_SHIFT; + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_namelen = JFFS2_MAX_NAME_LEN; + + spin_lock_bh(&c->erase_completion_lock); + + avail = c->dirty_size + c->free_size; + if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE) + avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE; + else + avail = 0; + + buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; + + D1(jffs2_dump_block_lists(c)); + + spin_unlock_bh(&c->erase_completion_lock); + + return 0; +} + + +void jffs2_clear_inode (struct inode *inode) +{ + /* We can forget about this inode for now - drop all + * the nodelists associated with it, etc. + */ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + + D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); + + jffs2_do_clear_inode(c, f); +} + +void jffs2_read_inode (struct inode *inode) +{ + struct jffs2_inode_info *f; + struct jffs2_sb_info *c; + struct jffs2_raw_inode latest_node; + int ret; + + D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); + + f = JFFS2_INODE_INFO(inode); + c = JFFS2_SB_INFO(inode->i_sb); + + jffs2_init_inode_info(f); + + ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); + + if (ret) { + make_bad_inode(inode); + up(&f->sem); + return; + } + inode->i_mode = je32_to_cpu(latest_node.mode); + inode->i_uid = je16_to_cpu(latest_node.uid); + inode->i_gid = je16_to_cpu(latest_node.gid); + inode->i_size = je32_to_cpu(latest_node.isize); + inode->i_atime = je32_to_cpu(latest_node.atime); + inode->i_mtime = je32_to_cpu(latest_node.mtime); + inode->i_ctime = je32_to_cpu(latest_node.ctime); + + inode->i_nlink = f->inocache->nlink; + + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = (inode->i_size + 511) >> 9; + + switch (inode->i_mode & S_IFMT) { + unsigned short rdev; + + case S_IFLNK: + inode->i_op = &jffs2_symlink_inode_operations; + break; + + case S_IFDIR: + { + struct jffs2_full_dirent *fd; + + for (fd=f->dents; fd; fd = fd->next) { + if (fd->type == DT_DIR && fd->ino) + inode->i_nlink++; + } + /* and '..' */ + inode->i_nlink++; + /* Root dir gets i_nlink 3 for some reason */ + if (inode->i_ino == 1) + inode->i_nlink++; + + inode->i_op = &jffs2_dir_inode_operations; + inode->i_fop = &jffs2_dir_operations; + break; + } + case S_IFREG: + inode->i_op = &jffs2_file_inode_operations; + inode->i_fop = &jffs2_file_operations; + inode->i_mapping->a_ops = &jffs2_file_address_operations; + inode->i_mapping->nrpages = 0; + break; + + case S_IFBLK: + 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) { + /* Eep */ + printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); + up(&f->sem); + jffs2_do_clear_inode(c, f); + make_bad_inode(inode); + return; + } + + case S_IFSOCK: + case S_IFIFO: + inode->i_op = &jffs2_file_inode_operations; + init_special_inode(inode, inode->i_mode, kdev_t_to_nr(mk_kdev(rdev>>8, rdev&0xff))); + break; + + default: + printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); + } + + up(&f->sem); + + D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); +} + + +int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + + if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) + return -EROFS; + + /* 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)) + jffs2_stop_garbage_collect_thread(c); + + if (!(*flags & MS_RDONLY)) + jffs2_start_garbage_collect_thread(c); + + sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY); + + return 0; +} + +void jffs2_write_super (struct super_block *sb) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + sb->s_dirt = 0; + + if (sb->s_flags & MS_RDONLY) + return; + + D1(printk(KERN_DEBUG "jffs2_write_super()\n")); + jffs2_garbage_collect_trigger(c); + jffs2_erase_pending_blocks(c); + jffs2_mark_erased_blocks(c); +} + + +/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, + fill in the raw_inode while you're at it. */ +struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) +{ + struct inode *inode; + struct super_block *sb = dir_i->i_sb; + struct jffs2_sb_info *c; + struct jffs2_inode_info *f; + int ret; + + D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); + + c = JFFS2_SB_INFO(sb); + + inode = new_inode(sb); + + if (!inode) + return ERR_PTR(-ENOMEM); + + f = JFFS2_INODE_INFO(inode); + jffs2_init_inode_info(f); + + memset(ri, 0, sizeof(*ri)); + /* Set OS-specific defaults for new inodes */ + ri->uid = cpu_to_je16(current->fsuid); + + if (dir_i->i_mode & S_ISGID) { + ri->gid = cpu_to_je16(dir_i->i_gid); + if (S_ISDIR(mode)) + mode |= S_ISGID; + } else { + ri->gid = cpu_to_je16(current->fsgid); + } + ri->mode = cpu_to_je32(mode); + ret = jffs2_do_new_inode (c, f, mode, ri); + if (ret) { + make_bad_inode(inode); + iput(inode); + return ERR_PTR(ret); + } + inode->i_nlink = 1; + inode->i_ino = je32_to_cpu(ri->ino); + inode->i_mode = je32_to_cpu(ri->mode); + inode->i_gid = je16_to_cpu(ri->gid); + inode->i_uid = je16_to_cpu(ri->uid); + inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; + ri->atime = ri->mtime = ri->ctime = cpu_to_je32(inode->i_mtime); + + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = 0; + inode->i_size = 0; + + insert_inode_hash(inode); + + return inode; +} + + +int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) +{ + struct jffs2_sb_info *c; + struct inode *root_i; + int ret; + + c = JFFS2_SB_INFO(sb); + + c->sector_size = c->mtd->erasesize; + c->flash_size = c->mtd->size; + +#if 0 + if (c->sector_size < 0x10000) { + printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using 64KiB instead\n", + c->sector_size / 1024); + c->sector_size = 0x10000; + } +#endif + if (c->flash_size < 5*c->sector_size) { + printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", + c->flash_size / c->sector_size); + return -EINVAL; + } + + c->cleanmarker_size = sizeof(struct jffs2_unknown_node); + /* Jörn -- stick alignment for weird 8-byte-page flash here */ + + if (jffs2_cleanmarker_oob(c)) { + /* Cleanmarker is out-of-band, so inline size zero */ + c->cleanmarker_size = 0; + } + + if (c->mtd->type == MTD_NANDFLASH) { + /* 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; + + /* Initialize process for timed wbuf flush */ + INIT_TQUEUE(&c->wbuf_task,(void*) jffs2_wbuf_process, (void *)c); + /* Initialize timer for timed wbuf flush */ + init_timer(&c->wbuf_timer); + c->wbuf_timer.function = jffs2_wbuf_timeout; + c->wbuf_timer.data = (unsigned long) c; + } + + c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); + if (!c->inocache_list) { + ret = -ENOMEM; + goto out_wbuf; + } + memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); + + if ((ret = jffs2_do_mount_fs(c))) + goto out_inohash; + + ret = -EINVAL; + + D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); + root_i = iget(sb, 1); + if (is_bad_inode(root_i)) { + D1(printk(KERN_WARNING "get root inode failed\n")); + goto out_nodes; + } + + D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n")); + sb->s_root = d_alloc_root(root_i); + if (!sb->s_root) + goto out_root_i; + +#if LINUX_VERSION_CODE >= 0x20403 + sb->s_maxbytes = 0xFFFFFFFF; +#endif + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = JFFS2_SUPER_MAGIC; + if (!(sb->s_flags & MS_RDONLY)) + jffs2_start_garbage_collect_thread(c); + return 0; + + out_root_i: + iput(root_i); + out_nodes: + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + kfree(c->blocks); + out_inohash: + kfree(c->inocache_list); + out_wbuf: + if (c->wbuf) + kfree(c->wbuf); + return ret; +} diff -urN linux-2.4.32/fs/jffs2/os-linux.h linux-2.4.32-uc0/fs/jffs2/os-linux.h --- linux-2.4.32/fs/jffs2/os-linux.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/os-linux.h 2006-02-26 12:49:17.000000000 +0100 @@ -0,0 +1,150 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2002 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: os-linux.h,v 1.19 2002/05/20 14:56:38 dwmw2 Exp $ + * + */ + +#ifndef __JFFS2_OS_LINUX_H__ +#define __JFFS2_OS_LINUX_H__ +#include + +#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) +#define JFFS2_SB_INFO(sb) (sb->u.generic_sbp) +#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv) +#elif defined(JFFS2_OUT_OF_KERNEL) +#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u) +#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) ) +#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u) +#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) ) +#else +#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i) +#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) ) +#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb) +#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) ) +#endif + + +#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size) +#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode) +#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid) +#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid) +#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime) +#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime) +#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime) + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1) +#define JFFS2_F_I_RDEV_MIN(f) (minor(OFNI_EDONI_2SFFJ(f)->i_rdev)) +#define JFFS2_F_I_RDEV_MAJ(f) (major(OFNI_EDONI_2SFFJ(f)->i_rdev)) +#else +#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev))) +#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev))) +#endif + +static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) + f->highest_version = 0; + f->fraglist = NULL; + f->metadata = NULL; + f->dents = NULL; + f->flags = 0; + f->usercompr = 0; +#else + memset(f, 0, sizeof(*f)); + init_MUTEX_LOCKED(&f->sem); +#endif +} + +#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) + +#ifndef CONFIG_JFFS2_FS_NAND +#define jffs2_can_mark_obsolete(c) (1) +#define jffs2_cleanmarker_oob(c) (0) +#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) + +#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf)) +#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf)) +#define jffs2_flush_wbuf(c, flag) do { ; } while(0) +#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0) +#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0) +#define jffs2_flash_writev jffs2_flash_direct_writev +#define jffs2_wbuf_timeout NULL +#define jffs2_wbuf_process NULL + +#else /* NAND support present */ + +#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM) +#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) + +#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)) + + +/* wbuf.c */ +int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); +int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); +int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf); +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); +void jffs2_wbuf_timeout(unsigned long data); +void jffs2_wbuf_process(void *data); +#endif /* NAND */ + +/* background.c */ +int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); +void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); +void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); + +/* dir.c */ +extern struct file_operations jffs2_dir_operations; +extern struct inode_operations jffs2_dir_inode_operations; + +/* file.c */ +extern struct file_operations jffs2_file_operations; +extern struct inode_operations jffs2_file_inode_operations; +extern struct address_space_operations jffs2_file_address_operations; +int jffs2_fsync(struct file *, struct dentry *, int); +int jffs2_setattr (struct dentry *dentry, struct iattr *iattr); +int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg); +int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); +int jffs2_readpage (struct file *, struct page *); +int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned); +int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned); + +/* ioctl.c */ +int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); + +/* symlink.c */ +extern struct inode_operations jffs2_symlink_inode_operations; + +/* fs.c */ +void jffs2_read_inode (struct inode *); +void jffs2_clear_inode (struct inode *); +struct inode *jffs2_new_inode (struct inode *dir_i, int mode, + struct jffs2_raw_inode *ri); +int jffs2_statfs (struct super_block *, struct statfs *); +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); + +/* writev.c */ +int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen); + +/* super.c */ + + +#endif /* __JFFS2_OS_LINUX_H__ */ + + diff -urN linux-2.4.32/fs/jffs2/super-v24.c linux-2.4.32-uc0/fs/jffs2/super-v24.c --- linux-2.4.32/fs/jffs2/super-v24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/super-v24.c 2006-02-26 12:49:17.000000000 +0100 @@ -0,0 +1,161 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: super-v24.c,v 1.63 2002/05/20 14:56:39 dwmw2 Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +#ifndef MTD_BLOCK_MAJOR +#define MTD_BLOCK_MAJOR 31 +#endif + +void jffs2_put_super (struct super_block *); + +static struct super_operations jffs2_super_operations = +{ + read_inode: jffs2_read_inode, + put_super: jffs2_put_super, + write_super: jffs2_write_super, + statfs: jffs2_statfs, + remount_fs: jffs2_remount_fs, + clear_inode: jffs2_clear_inode +}; + + +static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent) +{ + struct jffs2_sb_info *c; + int ret; + + D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev))); + + if (major(sb->s_dev) != MTD_BLOCK_MAJOR) { + if (!silent) + printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev)); + return NULL; + } + + c = JFFS2_SB_INFO(sb); + memset(c, 0, sizeof(*c)); + + sb->s_op = &jffs2_super_operations; + + c->mtd = get_mtd_device(NULL, minor(sb->s_dev)); + if (!c->mtd) { + D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev))); + return NULL; + } + + ret = jffs2_do_fill_super(sb, data, silent); + if (ret) { + put_mtd_device(c->mtd); + return NULL; + } + + return sb; +} + +void jffs2_put_super (struct super_block *sb) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + + D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); + + + if (!(sb->s_flags & MS_RDONLY)) + jffs2_stop_garbage_collect_thread(c); + down(&c->alloc_sem); + jffs2_flush_wbuf(c, 1); + up(&c->alloc_sem); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + kfree(c->blocks); + if (c->mtd->sync) + c->mtd->sync(c->mtd); + put_mtd_device(c->mtd); + + D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); +} + +static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super); + +static int __init init_jffs2_fs(void) +{ + int ret; + + printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001, 2002 Red Hat, Inc., designed by Axis Communications AB.\n"); + +#ifdef JFFS2_OUT_OF_KERNEL + /* sanity checks. Could we do these at compile time? */ + if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) { + printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", + sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u)); + return -EIO; + } + + if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) { + printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", + sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u)); + return -EIO; + } +#endif + ret = jffs2_zlib_init(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); + goto out; + } + ret = jffs2_create_slab_caches(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); + goto out_zlib; + } + ret = register_filesystem(&jffs2_fs_type); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); + goto out_slab; + } + return 0; + + out_slab: + jffs2_destroy_slab_caches(); + out_zlib: + jffs2_zlib_exit(); + out: + + return ret; +} + +static void __exit exit_jffs2_fs(void) +{ + jffs2_destroy_slab_caches(); + jffs2_zlib_exit(); + unregister_filesystem(&jffs2_fs_type); +} + +module_init(init_jffs2_fs); +module_exit(exit_jffs2_fs); + +MODULE_DESCRIPTION("The Journalling Flash File System, v2"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for + // the sake of this tag. It's Free Software. diff -urN linux-2.4.32/fs/jffs2/wbuf.c linux-2.4.32-uc0/fs/jffs2/wbuf.c --- linux-2.4.32/fs/jffs2/wbuf.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/wbuf.c 2006-02-26 12:49:17.000000000 +0100 @@ -0,0 +1,755 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: wbuf.c,v 1.18 2002/09/10 06:35:48 gleixner Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +/* max. erase failures before we mark a block bad */ +#define MAX_ERASE_FAILURES 5 + +/* two seconds timeout for timed wbuf-flushing */ +#define WBUF_FLUSH_TIMEOUT 2 * HZ + +static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) +{ + struct list_head *this, *next; + static int n; + + if (list_empty(&c->erasable_pending_wbuf_list)) + return; + + list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset)); + list_del(this); + if ((jiffies + (n++)) & 127) { + /* Most of the time, we just erase it immediately. Otherwise we + spend ages scanning it on mount, etc. */ + D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); + list_add_tail(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); + } else { + /* Sometimes, however, we leave it elsewhere so it doesn't get + immediately reused, and we spread the load a bit. */ + D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); + list_add_tail(&jeb->list, &c->erasable_list); + } + } +} + +/* +* Timed flushing of wbuf. If we have no consecutive write to wbuf, within +* the specified time, we flush the contents with padding ! +*/ +void jffs2_wbuf_timeout (unsigned long data) +{ + struct jffs2_sb_info *c = (struct jffs2_sb_info *) data; + /* + * Wake up the flush process, we need process context to have the right + * to sleep on flash write + */ + D1(printk(KERN_DEBUG "jffs2_wbuf_timeout(): timer expired\n")); + schedule_task(&c->wbuf_task); +} + +/* +* Process for timed wbuf flush +* +* FIXME What happens, if we have a write failure there ???? +*/ +void jffs2_wbuf_process (void *data) +{ + struct jffs2_sb_info *c = (struct jffs2_sb_info *) data; + + D1(printk(KERN_DEBUG "jffs2_wbuf_process() entered\n")); + + /* Check, if the timer is active again */ + if (timer_pending (&c->wbuf_timer)) { + D1(printk (KERN_DEBUG "Nothing to do, timer is active again\n")); + return; + } + + if (down_trylock(&c->alloc_sem)) { + /* If someone else has the alloc_sem, they're about to + write anyway. So no need to waste space by + padding */ + D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem already occupied\n")); + return; + } + + D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem got\n")); + + if (!c->nextblock) { + D1(printk(KERN_DEBUG "jffs2_wbuf_process(): nextblock NULL, nothing to do\n")); + if (c->wbuf_len) { + printk(KERN_WARNING "jffs2_wbuf_process(): c->wbuf_len is 0x%03x but nextblock is NULL!\n", c->wbuf_len); + up(&c->alloc_sem); + BUG(); + } + return; + } + + + /* if !c->nextblock then the tail will have got flushed from + jffs2_do_reserve_space() anyway. */ + if(c->nextblock) + jffs2_flush_wbuf(c, 2); /* pad and adjust nextblock */ + + up(&c->alloc_sem); +} + + +/* Meaning of pad argument: + 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. + 1: Pad, do not adjust nextblock free_size + 2: Pad, adjust nextblock free_size +*/ +int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) +{ + int ret; + size_t retlen; + + if (!down_trylock(&c->alloc_sem)) { + up(&c->alloc_sem); + printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); + BUG(); + } + + /* delete a eventually started timed wbuf flush */ + del_timer_sync(&c->wbuf_timer); + + if(!c->wbuf || !c->wbuf_len) + return 0; + + /* claim remaining space on the page + this happens, if we have a change to a new block, + or if fsync forces us to flush the writebuffer. + if we have a switch to next page, we will not have + enough remaining space for this. + */ + if (pad) { + c->wbuf_len = PAD(c->wbuf_len); + + if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { + struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); + padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + 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 jffs2_flash_writev has actually filled in the rest of the + buffer for us, and will deal with the node refs etc. later. */ + + ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, NAND_JFFS2_OOB); + + if (ret || retlen != c->wbuf_pagesize) { + if (ret) + printk(KERN_CRIT "jffs2_flush_wbuf(): Write failed with %d\n",ret); + else + printk(KERN_CRIT "jffs2_flush_wbuf(): Write was short %d instead of %d\n",retlen,c->wbuf_pagesize); + + ret = -EIO; + /* CHECKME NAND + So that the caller knows what happened. If + we were called from jffs2_flash_writev(), it'll + know to return failure and _its_ caller will + try again. writev gives back to jffs2_write_xxx + in write.c. There are the real fixme's + */ + + /* FIXME NAND + If we were called from GC or fsync, there's no repair kit yet + */ + + 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_bh(&c->erase_completion_lock); + if (!c->nextblock) + BUG(); + /* 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)) { + 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); + BUG(); + } + c->nextblock->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); + c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); + spin_unlock_bh(&c->erase_completion_lock); + } + + /* Stick any now-obsoleted blocks on the erase_pending_list */ + spin_lock_bh(&c->erase_completion_lock); + jffs2_refile_wbuf_blocks(c); + spin_unlock_bh(&c->erase_completion_lock); + + memset(c->wbuf,0xff,c->wbuf_pagesize); + /* adjust write buffer offset, else we get a non contigous write bug */ + c->wbuf_ofs+= c->wbuf_pagesize; + c->wbuf_len = 0; + return 0; +} + +#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) ) +#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) ) +int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen) +{ + struct iovec outvecs[3]; + uint32_t totlen = 0; + uint32_t split_ofs = 0; + uint32_t old_totlen; + int ret, splitvec = -1; + int invec, outvec; + size_t wbuf_retlen; + unsigned char *wbuf_ptr; + size_t donelen = 0; + uint32_t outvec_to = to; + + /* If not NAND flash, don't bother */ + if (!c->wbuf) + return jffs2_flash_direct_writev(c, invecs, count, to, retlen); + + /* If wbuf_ofs is not initialized, set it to target adress */ + if (c->wbuf_ofs == 0xFFFFFFFF) { + c->wbuf_ofs = PAGE_DIV(to); + c->wbuf_len = PAGE_MOD(to); + memset(c->wbuf,0xff,c->wbuf_pagesize); + } + + /* Sanity checks on target address. + It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), + and it's permitted to write at the beginning of a new + erase block. Anything else, and you die. + New block starts at xxx000c (0-b = block header) + */ + if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) { + /* It's a write to a new block */ + if (c->wbuf_len) { + D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); + ret = jffs2_flush_wbuf(c, 1); + if (ret) { + /* the underlying layer has to check wbuf_len to do the cleanup */ + D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); + *retlen = 0; + return ret; + } + } + /* set pointer to new block */ + c->wbuf_ofs = PAGE_DIV(to); + c->wbuf_len = PAGE_MOD(to); + } + + if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { + /* We're not writing immediately after the writebuffer. Bad. */ + printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to); + if (c->wbuf_len) + printk(KERN_CRIT "wbuf was previously %08x-%08x\n", + c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); + BUG(); + } + + /* Note outvecs[3] above. We know count is never greater than 2 */ + if (count > 2) { + printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count); + BUG(); + } + + invec = 0; + outvec = 0; + + + /* Fill writebuffer first, if already in use */ + if (c->wbuf_len) { + uint32_t invec_ofs = 0; + + /* adjust alignment offset */ + if (c->wbuf_len != PAGE_MOD(to)) { + c->wbuf_len = PAGE_MOD(to); + /* take care of alignment to next page */ + if (!c->wbuf_len) + c->wbuf_len = c->wbuf_pagesize; + } + + while(c->wbuf_len < c->wbuf_pagesize) { + uint32_t thislen; + + if (invec == count) + goto alldone; + + thislen = c->wbuf_pagesize - c->wbuf_len; + + if (thislen >= invecs[invec].iov_len) + thislen = invecs[invec].iov_len; + + invec_ofs = thislen; + + memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen); + c->wbuf_len += thislen; + donelen += thislen; + /* Get next invec, if actual did not fill the buffer */ + if (c->wbuf_len < c->wbuf_pagesize) + invec++; + } + + /* write buffer is full, flush buffer */ + ret = jffs2_flush_wbuf(c, 0); + if (ret) { + /* the underlying layer has to check wbuf_len to do the cleanup */ + D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); + *retlen = 0; + return ret; + } + outvec_to += donelen; + c->wbuf_ofs = outvec_to; + + /* All invecs done ? */ + if (invec == count) + goto alldone; + + /* Set up the first outvec, containing the remainder of the + invec we partially used */ + if (invecs[invec].iov_len > invec_ofs) { + outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs; + totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs; + if (totlen > c->wbuf_pagesize) { + splitvec = outvec; + split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen); + } + outvec++; + } + invec++; + } + + /* OK, now we've flushed the wbuf and the start of the bits + we have been asked to write, now to write the rest.... */ + + /* totlen holds the amount of data still to be written */ + old_totlen = totlen; + for ( ; invec < count; invec++,outvec++ ) { + outvecs[outvec].iov_base = invecs[invec].iov_base; + totlen += outvecs[outvec].iov_len = invecs[invec].iov_len; + if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) { + splitvec = outvec; + split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen); + old_totlen = totlen; + } + } + + /* Now the outvecs array holds all the remaining data to write */ + /* Up to splitvec,split_ofs is to be written immediately. The rest + goes into the (now-empty) wbuf */ + + if (splitvec != -1) { + uint32_t remainder; + int ret; + + remainder = outvecs[splitvec].iov_len - split_ofs; + outvecs[splitvec].iov_len = split_ofs; + + /* We did cross a page boundary, so we write some now */ + ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, NAND_JFFS2_OOB); + if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { + /* At this point we have no problem, + c->wbuf is empty. + */ + *retlen = donelen; + return ret; + } + + donelen += wbuf_retlen; + c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen); + + if (remainder) { + outvecs[splitvec].iov_base += split_ofs; + outvecs[splitvec].iov_len = remainder; + } else { + splitvec++; + } + + } else { + splitvec = 0; + } + + /* Now splitvec points to the start of the bits we have to copy + into the wbuf */ + wbuf_ptr = c->wbuf; + + for ( ; splitvec < outvec; splitvec++) { + /* Don't copy the wbuf into itself */ + if (outvecs[splitvec].iov_base == c->wbuf) + continue; + memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len); + wbuf_ptr += outvecs[splitvec].iov_len; + donelen += outvecs[splitvec].iov_len; + } + c->wbuf_len = wbuf_ptr - c->wbuf; + +alldone: + *retlen = donelen; + /* Setup timed wbuf flush, if buffer len != 0 */ + if (c->wbuf_len) { + D1(printk (KERN_DEBUG "jffs2_flash_writev: mod wbuf_timer\n")); + mod_timer(&c->wbuf_timer, jiffies + WBUF_FLUSH_TIMEOUT); + } + return 0; +} + +/* + This is the entry for NOR-Flash. We use it also for NAND to flush wbuf +*/ +int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) +{ + return c->mtd->write(c->mtd, ofs, len, retlen, buf); +} + +/* + Handle readback from writebuffer and ECC failure return +*/ +int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf) +{ + loff_t orbf = 0, owbf = 0, lwbf = 0; + int ret; + + /* Read flash */ + if (!jffs2_can_mark_obsolete(c)) { + ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, NAND_JFFS2_OOB); + + if ( (ret == -EIO) && (*retlen == len) ) { + printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%llx) returned ECC error\n", len, ofs); + /* + * We have the raw data without ECC correction in the buffer, maybe + * we are lucky and all data or parts are correct. We check the node. + * If data are corrupted node check will sort it out. + * We keep this block, it will fail on write or erase and the we + * mark it bad. Or should we do that now? But we should give him a chance. + * Maybe we had a system crash or power loss before the ecc write or + * a erase was completed. + * So we return success. :) + */ + ret = 0; + } + } else + return c->mtd->read(c->mtd, ofs, len, retlen, buf); + + /* if no writebuffer available or write buffer empty, return */ + if (!c->wbuf_pagesize || !c->wbuf_len) + return ret; + + /* if we read in a different block, return */ + if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) + return ret; + + if (ofs >= c->wbuf_ofs) { + owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ + if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ + return ret; + lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ + if (lwbf > len) + lwbf = len; + } else { + orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ + if (orbf > len) /* is write beyond write buffer ? */ + return ret; + lwbf = len - orbf; /* number of bytes to copy */ + if (lwbf > c->wbuf_len) + lwbf = c->wbuf_len; + } + if (lwbf > 0) + memcpy(buf+orbf,c->wbuf+owbf,lwbf); + + return ret; +} + +/* + * Check, if the out of band area is empty + */ +int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) +{ + unsigned char *buf; + int ret = 0; + int i,len,cnt,page; + size_t retlen; + int fsdata_pos,badblock_pos,oob_size; + + oob_size = c->mtd->oobsize; + + switch(c->mtd->ecctype) { + case MTD_ECC_SW: + fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS; + badblock_pos = NAND_BADBLOCK_POS; + break; + default: + D1(printk(KERN_WARNING "jffs2_write_oob_empty(): Invalid ECC type\n")); + return -EINVAL; + } + + /* allocate a buffer for all oob data in this sector */ + len = 4 * oob_size; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); + return -ENOMEM; + } + /* + * if mode = 0, we scan for a total empty oob area, else we have + * to take care of the cleanmarker in the first page of the block + */ + ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); + if (ret) { + D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); + goto out; + } + + if (retlen < len) { + D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " + "(%d bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); + ret = -EIO; + 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+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; + goto out; + } + cnt = oob_size; + if (mode) + cnt -= fsdata_pos; + for(i = 0; i < cnt ; i+=sizeof(unsigned short)) { + unsigned short dat = *(unsigned short *)(&buf[page+i]); + if(dat != 0xffff) { + 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)) { + unsigned long dat = *(unsigned long *)(&buf[page]); + if(dat != -1) { + ret = 1; + goto out; + } + } + +out: + kfree(buf); + + return ret; +} + +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 *p; + int ret,i; + size_t retlen; + int fsdata_pos,fsdata_len, oob_size, badblock_pos; + + oob_size = c->mtd->oobsize; + + switch(c->mtd->ecctype) { + case MTD_ECC_SW: + fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS; + fsdata_len = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDALEN : NAND_JFFS2_OOB16_FSDALEN; + badblock_pos = NAND_BADBLOCK_POS; + break; + default: + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n")); + return -EINVAL; + } + + /* + * We read oob data from page 0 and 1 of the block. + * page 0 contains cleanmarker and badblock info + * page 2 contains failure count of this block + */ + ret = c->mtd->read_oob(c->mtd, jeb->offset, oob_size << 1 , &retlen, buf); + + if (ret) { + D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); + return ret; + } + if (retlen < (oob_size << 1) ) { + D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%d bytes not %d) for block at %08x\n", retlen, oob_size << 1 , jeb->offset)); + return -EIO; + } + + /* Check for bad block marker */ + if (buf[badblock_pos] != 0xff) { + D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n",jeb->offset)); + return 2; + } + + /* Check for failure counter in the second page */ + if (buf[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[badblock_pos+oob_size])); + return 3; + } + + n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); + n.totlen = cpu_to_je32(8); + p = (unsigned char *) &n; + + for (i = 0; i < fsdata_len; i++) { + if (buf[fsdata_pos+i] != p[i]) { + D2(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset)); + return 1; + } + } + + return 0; +} + +int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + struct jffs2_unknown_node n; + int ret; + int fsdata_pos,fsdata_len; + size_t retlen; + + switch(c->mtd->ecctype) { + case MTD_ECC_SW: + fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS; + fsdata_len = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDALEN : NAND_JFFS2_OOB16_FSDALEN; + break; + default: + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n")); + return -EINVAL; + } + + n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); + n.totlen = cpu_to_je32(8); + + ret = jffs2_flash_write_oob(c, jeb->offset + fsdata_pos, fsdata_len, &retlen, (unsigned char *)&n); + + if (ret) { + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); + return ret; + } + if (retlen != fsdata_len) { + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %d not %d\n", jeb->offset, retlen, fsdata_len)); + return ret; + } + return 0; +} + +/* + * 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, badblock_pos; + + oob_size = c->mtd->oobsize; + + switch(c->mtd->ecctype) { + case MTD_ECC_SW: + badblock_pos = NAND_BADBLOCK_POS; + break; + default: + D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Invalid ECC type\n")); + return -EINVAL; + } + + 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 (%d bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset)); + return -EIO; + } + + jeb->bad_count = buf[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. + * 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) +{ + unsigned char buf = 0x0; + int ret,pos; + size_t retlen; + + switch(c->mtd->ecctype) { + case MTD_ECC_SW: + pos = NAND_BADBLOCK_POS; + break; + default: + D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Invalid ECC type\n")); + return -EINVAL; + } + + /* 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; + pos += c->mtd->oobblock; + } + + ret = jffs2_flash_write_oob(c, jeb->offset + pos, 1, &retlen, &buf); + + 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: %d not 1\n", jeb->offset, retlen)); + return ret; + } + return 0; +} + diff -urN linux-2.4.32/fs/jffs2/writev.c linux-2.4.32-uc0/fs/jffs2/writev.c --- linux-2.4.32/fs/jffs2/writev.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32-uc0/fs/jffs2/writev.c 2006-02-26 12:49:17.000000000 +0100 @@ -0,0 +1,50 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: writev.c,v 1.3 2002/08/08 08:35:21 dwmw2 Exp $ + * + */ + +#include +#include +#include "nodelist.h" + +/* This ought to be in core MTD code. All registered MTD devices + without writev should have this put in place. Bug the MTD + maintainer */ +static inline int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + unsigned long i; + size_t totlen = 0, thislen; + int ret = 0; + + for (i=0; iwrite(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); + totlen += thislen; + if (ret || thislen != vecs[i].iov_len) + break; + to += vecs[i].iov_len; + } + if (retlen) + *retlen = totlen; + return ret; +} + +int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + if (c->mtd->writev) + return c->mtd->writev(c->mtd, vecs, count, to, retlen); + else + return mtd_fake_writev(c->mtd, vecs, count, to, retlen); +} +