diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-08-25 06:33:44 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-08-25 06:33:44 +0000 |
commit | 6bd6dbbd3ae53a268a510270bebaab24fff382ca (patch) | |
tree | 41d0361e6b48ce74584c9a6fcb475d5054ca4141 /fs/jffs | |
parent | ee355114ec6062d00c1376b184b886a39e74fd4e (diff) |
Merge with Linux 2.4.0-test6-pre10.
Diffstat (limited to 'fs/jffs')
-rw-r--r-- | fs/jffs/Makefile | 5 | ||||
-rw-r--r-- | fs/jffs/inode-v23.c | 347 | ||||
-rw-r--r-- | fs/jffs/intrep.c | 971 | ||||
-rw-r--r-- | fs/jffs/intrep.h | 26 | ||||
-rw-r--r-- | fs/jffs/jffs_fm.c | 34 | ||||
-rw-r--r-- | fs/jffs/jffs_fm.h | 37 |
6 files changed, 962 insertions, 458 deletions
diff --git a/fs/jffs/Makefile b/fs/jffs/Makefile index 63d25594a..541816171 100644 --- a/fs/jffs/Makefile +++ b/fs/jffs/Makefile @@ -1,18 +1,21 @@ # # Makefile for the linux Journalling Flash FileSystem (JFFS) routines. # +# $Id: Makefile,v 1.7 2000/08/04 12:46:34 dwmw2 Exp $ +# # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here # unless it's something special (ie not a .c file). # # Note 2! The CFLAGS definitions are now in the main makefile... -ifndef CONFIG_MTD +ifndef CONFIG_JFFS_FS # We're being invoked outside a normal kernel build. Fake it EXTRA_CFLAGS= -I$(shell pwd)/../../include # You need to change this to build for 2.2, dunno how to check for it. + #INODE_O := inode-v22.o INODE_O := inode-v23.o diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 06ac42709..9ea2223d3 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -1,19 +1,21 @@ /* * JFFS -- Journalling Flash File System, Linux implementation. * - * Copyright (C) 1999, 2000 Finn Hakansson, Axis Communications, Inc. + * Copyright (C) 1999, 2000 Axis Communications AB. + * + * Created by Finn Hakansson <finn@axis.com>. * * This 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 of the License, or * (at your option) any later version. * - * $Id: inode-v23.c,v 1.17 2000/07/06 20:35:19 prumpf Exp $ + * $Id: inode-v23.c,v 1.33 2000/08/09 15:59:06 dwmw2 Exp $ * * * Ported to Linux 2.3.x and MTD: * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB - * + * */ /* inode.c -- Contains the code that is called from the VFS. */ @@ -24,7 +26,15 @@ * maybe other stuff do to. */ -#include <linux/config.h> +/* Argh. Some architectures have kernel_thread in asm/processor.h + Some have it in unistd.h and you need to define __KERNEL_SYSCALLS__ + Pass me a baseball bat and the person responsible. + dwmw2 +*/ +#define __KERNEL_SYSCALLS__ +#include <linux/sched.h> +#include <linux/unistd.h> + #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> @@ -45,16 +55,6 @@ #include "jffs_fm.h" #include "intrep.h" -#if defined(CONFIG_JFFS_FS_VERBOSE) && CONFIG_JFFS_FS_VERBOSE -#define D(x) x -#else -#define D(x) -#endif -#define D1(x) D(x) -#define D2(x) -#define D3(x) -#define ASSERT(x) x - static int jffs_remove(struct inode *dir, struct dentry *dentry, int type); static struct super_operations jffs_ops; @@ -64,22 +64,24 @@ static struct file_operations jffs_dir_operations; static struct inode_operations jffs_dir_inode_operations; static struct address_space_operations jffs_address_operations; + /* Called by the VFS at mount time to initialize the whole file system. */ static struct super_block * jffs_read_super(struct super_block *sb, void *data, int silent) { kdev_t dev = sb->s_dev; struct inode *root_inode; + struct jffs_control *c; - printk(KERN_NOTICE "JFFS: Trying to mount device %s.\n", - kdevname(dev)); + D1(printk(KERN_NOTICE "JFFS: Trying to mount device %s.\n", + kdevname(dev))); - if (MAJOR(dev)!=MTD_BLOCK_MAJOR) { - printk(KERN_WARNING "JFFS: Trying to mount non-mtd device.\n"); - return 0; + if (MAJOR(dev) != MTD_BLOCK_MAJOR) { + printk(KERN_WARNING "JFFS: Trying to mount a " + "non-mtd device.\n"); + return 0; } - set_blocksize(dev, PAGE_CACHE_SIZE); sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->u.generic_sbp = (void *) 0; @@ -98,19 +100,36 @@ jffs_read_super(struct super_block *sb, void *data, int silent) root_inode = iget(sb, JFFS_MIN_INO); if (!root_inode) goto jffs_sb_err2; - + /* Get the root directory of this file system. */ if (!(sb->s_root = d_alloc_root(root_inode))) { goto jffs_sb_err3; } -#ifdef USE_GC - /* Do a garbage collect every time we mount. */ - jffs_garbage_collect((struct jffs_control *)sb->u.generic_sbp); -#endif + c = (struct jffs_control *) sb->u.generic_sbp; - printk(KERN_NOTICE "JFFS: Successfully mounted device %s.\n", - kdevname(dev)); + /* Set the Garbage Collection thresholds */ + + /* GC if free space goes below 5% of the total size */ + c->gc_minfree_threshold = c->fmc->flash_size / 20; + + if (c->gc_minfree_threshold < c->fmc->sector_size) + c->gc_minfree_threshold = c->fmc->sector_size; + + /* GC if dirty space exceeds 33% of the total size. */ + c->gc_maxdirty_threshold = c->fmc->flash_size / 3; + + if (c->gc_maxdirty_threshold < c->fmc->sector_size) + c->gc_maxdirty_threshold = c->fmc->sector_size; + + + c->thread_pid = kernel_thread (jffs_garbage_collect_thread, + (void *) c, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + D1(printk(KERN_NOTICE "JFFS: GC thread pid=%d.\n", (int) c->thread_pid)); + + D1(printk(KERN_NOTICE "JFFS: Successfully mounted device %s.\n", + kdevname(dev))); return sb; jffs_sb_err3: @@ -118,7 +137,6 @@ jffs_sb_err3: jffs_sb_err2: jffs_cleanup_control((struct jffs_control *)sb->u.generic_sbp); jffs_sb_err1: - printk(KERN_WARNING "JFFS: Failed to mount device %s.\n", kdevname(dev)); return 0; @@ -129,14 +147,27 @@ jffs_sb_err1: static void jffs_put_super(struct super_block *sb) { - kdev_t dev = sb->s_dev; + struct jffs_control *c = (struct jffs_control *) sb->u.generic_sbp; + D1(kdev_t dev = sb->s_dev); + D2(printk("jffs_put_super()\n")); + + D1(printk (KERN_NOTICE "jffs_put_super(): Telling gc thread to die.\n")); + if (c->gc_task) { + send_sig(SIGQUIT, c->gc_task, 1); + send_sig(SIGCONT, c->gc_task, 1); + } + down (&c->gc_thread_sem); + + D1(printk (KERN_NOTICE "jffs_put_super(): Successfully waited on thread.\n")); + sb->s_dev = 0; jffs_cleanup_control((struct jffs_control *)sb->u.generic_sbp); - printk(KERN_NOTICE "JFFS: Successfully unmounted device %s.\n", - kdevname(dev)); + D1(printk(KERN_NOTICE "JFFS: Successfully unmounted device %s.\n", + kdevname(dev))); } + /* This function is called when user commands like chmod, chgrp and chown are executed. System calls like trunc() results in a call to this function. */ @@ -165,9 +196,9 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr) c = f->c; fmc = c->fmc; - update_all = iattr->ia_valid & ATTR_FORCE; + update_all = iattr->ia_valid & ATTR_FORCE; - if (!JFFS_ENOUGH_SPACE(fmc)) { + if (!JFFS_ENOUGH_SPACE(c)) { if ( (update_all || iattr->ia_valid & ATTR_SIZE) && (iattr->ia_size < f->size) ) { /* See this case where someone is trying to @@ -212,7 +243,7 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr) raw_inode.spare = 0; raw_inode.rename = 0; raw_inode.deleted = 0; - + if (update_all || iattr->ia_valid & ATTR_MODE) { raw_inode.mode = iattr->ia_mode; inode->i_mode = iattr->ia_mode; @@ -282,13 +313,16 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr) } jffs_insert_node(c, f, &raw_inode, 0, new_node); - + mark_inode_dirty(inode); - + return 0; } /* jffs_notify_change() */ -struct inode * jffs_new_inode(const struct inode * dir, struct jffs_raw_inode *raw_inode, int * err) + +struct inode * +jffs_new_inode(const struct inode * dir, struct jffs_raw_inode *raw_inode, + int * err) { struct super_block * sb; struct inode * inode; @@ -299,7 +333,7 @@ struct inode * jffs_new_inode(const struct inode * dir, struct jffs_raw_inode *r *err = -ENOMEM; return NULL; } - + sb = dir->i_sb; c = (struct jffs_control *)sb->u.generic_sbp; @@ -315,12 +349,12 @@ struct inode * jffs_new_inode(const struct inode * dir, struct jffs_raw_inode *r inode->i_atime = raw_inode->atime; inode->i_mtime = raw_inode->mtime; inode->i_ctime = raw_inode->ctime; - inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ - inode->i_blocks = 0; + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = (raw_inode->dsize + PAGE_SIZE - 1) >> PAGE_SHIFT; inode->i_version = 0; inode->i_flags = sb->s_flags; inode->u.generic_ip = (void *)jffs_find_file(c, raw_inode->ino); - + insert_inode_hash(inode); return inode; @@ -343,7 +377,7 @@ jffs_statfs(struct super_block *sb, struct statfs *buf) + jffs_free_size2(fmc) / PAGE_CACHE_SIZE) - (fmc->min_free_size / PAGE_CACHE_SIZE); buf->f_bavail = buf->f_bfree; - + /* Find out how many files there are in the filesystem. */ buf->f_files = jffs_foreach_file(c, jffs_file_count); buf->f_ffree = buf->f_bfree; @@ -352,10 +386,11 @@ jffs_statfs(struct super_block *sb, struct statfs *buf) return 0; } + /* Rename a file. */ int jffs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) + struct inode *new_dir, struct dentry *new_dentry) { struct jffs_raw_inode raw_inode; struct jffs_control *c; @@ -382,14 +417,14 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, return -1; }); - if (!JFFS_ENOUGH_SPACE(c->fmc)) { + if (!JFFS_ENOUGH_SPACE(c)) { D1(printk("jffs_rename(): Free size = %u\n", jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); D(printk(KERN_NOTICE "JFFS: No space left on device\n")); return -ENOSPC; } - - /* Find the the old directory. */ + + /* Find the old directory. */ result = -ENOTDIR; if (!(old_dir_f = (struct jffs_file *)old_dir->u.generic_ip)) { D(printk("jffs_rename(): Old dir invalid.\n")); @@ -402,8 +437,8 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, old_dentry->d_name.len))) { goto jffs_rename_end; } - - /* Try to find the new directory's node. */ + + /* Find the new directory. */ result = -ENOTDIR; if (!(new_dir_f = (struct jffs_file *)new_dir->u.generic_ip)) { D(printk("jffs_rename(): New dir invalid.\n")); @@ -450,27 +485,31 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, if ((del_f = jffs_find_child(new_dir_f, new_dentry->d_name.name, new_dentry->d_name.len))) { raw_inode.rename = 1; - /*raw_inode.mode = del_f->ino;*/ + raw_inode.dsize = sizeof(__u32); + rename_data = del_f->ino; } /* Write the new node to the flash memory. */ - if ((result = jffs_write_node(c, node, &raw_inode, new_dentry->d_name.name, + if ((result = jffs_write_node(c, node, &raw_inode, + new_dentry->d_name.name, (unsigned char*)&rename_data)) < 0) { D(printk("jffs_rename(): Failed to write node to flash.\n")); kfree(node); DJM(no_jffs_node--); goto jffs_rename_end; } + raw_inode.dsize = 0; if (raw_inode.rename) { /* The file with the same name must be deleted. */ - c->fmc->no_call_gc = 1; /* TODO: What kind of locking is this? */ - if ((result = jffs_remove(new_dir, new_dentry, del_f->mode)) < 0) { + down(&c->fmc->gclock); + if ((result = jffs_remove(new_dir, new_dentry, + del_f->mode)) < 0) { /* This is really bad. */ printk(KERN_ERR "JFFS: An error occurred in " "rename().\n"); } - c->fmc->no_call_gc = 0; + up(&c->fmc->gclock); } if (old_dir_f != new_dir_f) { @@ -501,7 +540,6 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, } jffs_rename_end: - return result; } /* jffs_rename() */ @@ -524,13 +562,14 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) return 0; } filp->f_pos = 1; - } + } if (filp->f_pos == 1) { if (inode->i_ino == JFFS_MIN_INO) { ddino = JFFS_MIN_INO; } else { - ddino = ((struct jffs_file *)inode->u.generic_ip)->pino; + ddino = ((struct jffs_file *) + inode->u.generic_ip)->pino; } D3(printk("jffs_readdir(): \"..\" %u\n", ddino)); if (filldir(dirent, "..", 2, filp->f_pos, ddino) < 0) @@ -549,7 +588,7 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) return 0; filp->f_pos++; } - + return filp->f_pos; } /* jffs_readdir() */ @@ -565,10 +604,10 @@ jffs_lookup(struct inode *dir, struct dentry *dentry) int r = 0; const char *name; struct inode *inode = NULL; - + len = dentry->d_name.len; name = dentry->d_name.name; - + D3({ char *s = (char *)kmalloc(len + 1, GFP_KERNEL); memcpy(s, name, len); @@ -576,7 +615,7 @@ jffs_lookup(struct inode *dir, struct dentry *dentry) printk("jffs_lookup(): dir: 0x%p, name: \"%s\"\n", dir, s); kfree(s); }); - + r = -ENAMETOOLONG; if (len > JFFS_MAX_NAME_LEN) { goto jffs_lookup_end; @@ -584,7 +623,8 @@ jffs_lookup(struct inode *dir, struct dentry *dentry) r = -EACCES; if (!(d = (struct jffs_file *)dir->u.generic_ip)) { - D(printk("jffs_lookup(): No such inode! (%lu)\n", dir->i_ino)); + D(printk("jffs_lookup(): No such inode! (%lu)\n", + dir->i_ino)); goto jffs_lookup_end; } @@ -610,10 +650,10 @@ jffs_lookup(struct inode *dir, struct dentry *dentry) f, name, d, d->ino)); inode = NULL; } - + d_add(dentry, inode); return NULL; - + jffs_lookup_end: return ERR_PTR(r); } /* jffs_lookup() */ @@ -665,9 +705,9 @@ jffs_readpage(struct file *file, struct page *page) flush_dcache_page(page); UnlockPage(page); - + put_page(page); - + D3(printk("jffs_readpage(): Leaving...\n")); return result; @@ -686,7 +726,7 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) int dir_mode; int result = 0; int err; - + D1({ int len = dentry->d_name.len; char *_name = (char *) kmalloc(len + 1, GFP_KERNEL); @@ -707,7 +747,7 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) c = dir_f->c; - if (!JFFS_ENOUGH_SPACE(c->fmc)) { + if (!JFFS_ENOUGH_SPACE(c)) { D1(printk("jffs_mkdir(): Free size = %u\n", jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); D(printk(KERN_NOTICE "JFFS: No space left on device\n")); @@ -718,7 +758,7 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) dir_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); if (dir->i_mode & S_ISGID) { - dir_mode |= S_ISGID; + dir_mode |= S_ISGID; } /* Create a node and initialize it as much as needed. */ @@ -754,7 +794,8 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) raw_inode.deleted = 0; /* Write the new node to the flash. */ - if ((result = jffs_write_node(c, node, &raw_inode, dentry->d_name.name, 0)) < 0) { + if ((result = jffs_write_node(c, node, &raw_inode, + dentry->d_name.name, 0)) < 0) { D(printk("jffs_mkdir(): jffs_write_node() failed.\n")); kfree(node); DJM(no_jffs_node--); @@ -762,15 +803,17 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) } /* Insert the new node into the file system. */ - if ((result = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, node))<0) - goto jffs_mkdir_end; - + if ((result = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, + node)) < 0) { + goto jffs_mkdir_end; + } + inode = jffs_new_inode(dir, &raw_inode, &err); if (inode == NULL) { result = err; goto jffs_mkdir_end; } - + inode->i_op = &jffs_dir_inode_operations; inode->i_fop = &jffs_dir_operations; @@ -839,7 +882,8 @@ jffs_remove(struct inode *dir, struct dentry *dentry, int type) result = -ENOTEMPTY; goto jffs_remove_end; } - } else if (S_ISDIR(del_f->mode)) { + } + else if (S_ISDIR(del_f->mode)) { D(printk("jffs_remove(): node is a directory " "but it shouldn't be.\n")); result = -EPERM; @@ -847,7 +891,7 @@ jffs_remove(struct inode *dir, struct dentry *dentry, int type) } inode = dentry->d_inode; - + result = -EIO; if (del_f->ino != inode->i_ino) goto jffs_remove_end; @@ -936,7 +980,7 @@ jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) dir_f = (struct jffs_file *)dir->u.generic_ip; c = dir_f->c; - if (!JFFS_ENOUGH_SPACE(c->fmc)) { + if (!JFFS_ENOUGH_SPACE(c)) { D1(printk("jffs_mknod(): Free size = %u\n", jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); D(printk(KERN_NOTICE "JFFS: No space left on device\n")); @@ -985,7 +1029,8 @@ jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) } /* Insert the new node into the file system. */ - if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, node)) < 0) { + if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, + node)) < 0) { result = err; goto jffs_mknod_end; } @@ -995,11 +1040,11 @@ jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) result = err; goto jffs_mknod_end; } - + init_special_inode(inode, mode, rdev); d_instantiate(dentry, inode); - + goto jffs_mknod_end; jffs_mknod_err: @@ -1021,7 +1066,7 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) struct jffs_file *dir_f; struct jffs_node *node; struct inode *inode; - + int symname_len = strlen(symname); int err; @@ -1033,7 +1078,8 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) _name[len] = '\0'; memcpy(_symname, symname, symname_len); _symname[symname_len] = '\0'; - printk("***jffs_symlink(): dir = 0x%p, dentry->dname.name = \"%s\", " + printk("***jffs_symlink(): dir = 0x%p, " + "dentry->dname.name = \"%s\", " "symname = \"%s\"\n", dir, _name, _symname); kfree(_name); kfree(_symname); @@ -1048,7 +1094,7 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) c = dir_f->c; - if (!JFFS_ENOUGH_SPACE(c->fmc)) { + if (!JFFS_ENOUGH_SPACE(c)) { D1(printk("jffs_symlink(): Free size = %u\n", jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); D(printk(KERN_NOTICE "JFFS: No space left on device\n")); @@ -1058,7 +1104,7 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) /* Create a node and initialize it as much as needed. */ if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), GFP_KERNEL))) { - D(printk("jffs_symlink(): Allocation failed: node == NULL\n")); + D(printk("jffs_symlink(): Allocation failed: node = NULL\n")); return -ENOMEM; } DJM(no_jffs_node++); @@ -1095,7 +1141,8 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) } /* Insert the new node into the file system. */ - if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, node)) < 0) { + if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, + node)) < 0) { return err; } @@ -1103,7 +1150,7 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) if (inode == NULL) { return err; } - + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &jffs_address_operations; @@ -1112,14 +1159,15 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) return 0; } /* jffs_symlink() */ -/* Create an inode inside a JFFS directory (dir) and return it. + +/* Create an inode inside a JFFS directory (dir) and return it. * * By the time this is called, we already have created * the directory cache entry for the new file, but it * is so far negative - it has no inode. * * If the create succeeds, we fill in the inode information - * with d_instantiate(). + * with d_instantiate(). */ static int jffs_create(struct inode *dir, struct dentry *dentry, int mode) @@ -1149,7 +1197,7 @@ jffs_create(struct inode *dir, struct dentry *dentry, int mode) c = dir_f->c; - if (!JFFS_ENOUGH_SPACE(c->fmc)) { + if (!JFFS_ENOUGH_SPACE(c)) { D1(printk("jffs_create(): Free size = %u\n", jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); D(printk(KERN_NOTICE "JFFS: No space left on device\n")); @@ -1187,7 +1235,8 @@ jffs_create(struct inode *dir, struct dentry *dentry, int mode) raw_inode.deleted = 0; /* Write the new node to the flash. */ - if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name, 0)) < 0) { + if ((err = jffs_write_node(c, node, &raw_inode, + dentry->d_name.name, 0)) < 0) { D(printk("jffs_create(): jffs_write_node() failed.\n")); kfree(node); DJM(no_jffs_node--); @@ -1195,7 +1244,8 @@ jffs_create(struct inode *dir, struct dentry *dentry, int mode) } /* Insert the new node into the file system. */ - if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, node)) < 0) { + if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, + node)) < 0) { return err; } @@ -1211,33 +1261,35 @@ jffs_create(struct inode *dir, struct dentry *dentry, int mode) inode->i_mapping->nrpages = 0; d_instantiate(dentry, inode); - + return 0; } /* jffs_create() */ /* Write, append or rewrite data to an existing file. */ static ssize_t -jffs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) +jffs_file_write(struct file *filp, const char *buf, size_t count, + loff_t *ppos) { struct jffs_raw_inode raw_inode; struct jffs_control *c; struct jffs_file *f; struct jffs_node *node; - struct dentry *dentry = filp->f_dentry; - struct inode *inode = dentry->d_inode; + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned char *vbuf; int written = 0; loff_t pos; int err; inode = filp->f_dentry->d_inode; - + D2(printk("***jffs_file_write(): inode: 0x%p (ino: %lu), " "filp: 0x%p, buf: 0x%p, count: %d\n", inode, inode->i_ino, filp, buf, count)); down(&inode->i_sem); - + pos = *ppos; err = -EINVAL; if (pos < 0) @@ -1254,7 +1306,7 @@ jffs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) err = -ENOSPC; goto out; } - + if (!S_ISREG(inode->i_mode)) { D(printk("jffs_file_write(): inode->i_mode == 0x%08x\n", inode->i_mode)); @@ -1268,10 +1320,10 @@ jffs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) err = -EINVAL; goto out; } - + c = f->c; - if (!JFFS_ENOUGH_SPACE(c->fmc)) { + if (!JFFS_ENOUGH_SPACE(c)) { D1(printk("jffs_file_write(): Free size = %u\n", jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); D(printk(KERN_NOTICE "JFFS: No space left on device\n")); @@ -1282,16 +1334,35 @@ jffs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) if (filp->f_flags & O_APPEND) pos = inode->i_size; + + if (!(vbuf = kmalloc(count, GFP_KERNEL))) { + D(printk("jffs_file_write(): failed to allocate bounce buffer. Fix me to use page cache\n")); + err = -ENOMEM; + goto out; + } + + /* FIXME: This is entirely gratuitous use of bounce buffers. + Get a clue and use the page cache. + /me wanders off to get a crash course on Linux VFS + dwmw2 + */ + if (copy_from_user(vbuf, buf, count)) { + kfree(vbuf); + return -EFAULT; + } + + /* Things are going to be written so we could allocate and initialize the necessary data structures now. */ if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), GFP_KERNEL))) { D(printk("jffs_file_write(): node == 0\n")); err = -ENOMEM; + kfree(vbuf); goto out; } DJM(no_jffs_node++); - node->data_offset = f->size; + node->data_offset = pos; node->removed_size = 0; /* Initialize the raw inode. */ @@ -1300,7 +1371,7 @@ jffs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) raw_inode.pino = f->pino; raw_inode.version = f->highest_version + 1; raw_inode.mode = f->mode; - + raw_inode.uid = f->uid; raw_inode.gid = f->gid; /* @@ -1310,7 +1381,7 @@ jffs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) raw_inode.atime = CURRENT_TIME; raw_inode.mtime = raw_inode.atime; raw_inode.ctime = f->ctime; - raw_inode.offset = f->size; + raw_inode.offset = pos; raw_inode.dsize = count; raw_inode.rsize = 0; raw_inode.nsize = 0; @@ -1319,25 +1390,28 @@ jffs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) raw_inode.rename = 0; raw_inode.deleted = 0; + if (pos < f->size) { + node->removed_size = raw_inode.rsize = jffs_min(count, f->size - pos); + } - /* TODO: BAAAAAAAAD! buf is a userspace-pointer, and should be - treated as such, with copy_from_user etc... - */ /* Write the new node to the flash. */ if ((written = jffs_write_node(c, node, &raw_inode, 0, - (const unsigned char *)buf)) < 0) { + (const unsigned char *)vbuf)) < 0) { D(printk("jffs_file_write(): jffs_write_node() failed.\n")); kfree(node); + kfree(vbuf); DJM(no_jffs_node--); err = written; goto out; } + kfree(vbuf); + /* Insert the new node into the file system. */ if ((err = jffs_insert_node(c, f, &raw_inode, 0, node)) < 0) { goto out; } - + pos += written; *ppos = pos; @@ -1365,7 +1439,8 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, { struct jffs_control *c; - D2(printk("***jffs_ioctl(): cmd = 0x%08x, arg = 0x%08lx\n", cmd, arg)); + D2(printk("***jffs_ioctl(): cmd = 0x%08x, arg = 0x%08lx\n", + cmd, arg)); if (!(c = (struct jffs_control *)inode->i_sb->u.generic_sbp)) { printk(KERN_ERR "JFFS: Bad inode in ioctl() call. " @@ -1402,10 +1477,10 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, fst.size, fst.used, fst.dirty, fst.begin, fst.end); if (copy_to_user((struct jffs_flash_status *)arg, - &fst, sizeof(struct jffs_flash_status))) { + &fst, + sizeof(struct jffs_flash_status))) { return -EFAULT; } - } break; default: @@ -1417,9 +1492,17 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, static struct address_space_operations jffs_address_operations = { - readpage: jffs_readpage, + readpage: jffs_readpage, }; +static int jffs_fsync(struct file *f, struct dentry *d, int datasync) +{ + /* We currently have O_SYNC operations at all times. + Do nothing + */ + return 0; +} + static struct file_operations jffs_file_operations = { @@ -1427,19 +1510,23 @@ static struct file_operations jffs_file_operations = write: jffs_file_write, /* write */ ioctl: jffs_ioctl, /* ioctl */ mmap: generic_file_mmap, /* mmap */ + fsync: jffs_fsync, }; + static struct inode_operations jffs_file_inode_operations = { lookup: jffs_lookup, /* lookup */ setattr: jffs_setattr, }; + static struct file_operations jffs_dir_operations = { readdir: jffs_readdir, }; + static struct inode_operations jffs_dir_inode_operations = { create: jffs_create, @@ -1453,6 +1540,7 @@ static struct inode_operations jffs_dir_inode_operations = setattr: jffs_setattr, }; + /* Initialize an inode for the VFS. */ static void jffs_read_inode(struct inode *inode) @@ -1483,7 +1571,7 @@ jffs_read_inode(struct inode *inode) inode->i_mtime = f->mtime; inode->i_ctime = f->ctime; inode->i_blksize = PAGE_SIZE; - inode->i_blocks = 0; + inode->i_blocks = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; if (S_ISREG(inode->i_mode)) { inode->i_op = &jffs_file_inode_operations; inode->i_fop = &jffs_file_operations; @@ -1494,44 +1582,42 @@ jffs_read_inode(struct inode *inode) inode->i_fop = &jffs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &jffs_address_operations; - } else { - /* If the node is a device of some sort, then the number of the - device should be read from the flash memory and then added - to the inode's i_rdev member. */ + } + else { + /* If the node is a device of some sort, then the number of + the device should be read from the flash memory and then + added to the inode's i_rdev member. */ kdev_t rdev; jffs_read_data(f, (char *)&rdev, 0, sizeof(kdev_t)); init_special_inode(inode, inode->i_mode, kdev_t_to_nr(rdev)); } } + void jffs_delete_inode(struct inode *inode) { + D3(printk("jffs_delete_inode(): inode->i_ino == %lu\n", + inode->i_ino)); - D3(printk("jffs_delete_inode(): inode->i_ino == %lu\n", inode->i_ino)); - - lock_kernel(); - + lock_kernel(); inode->i_size = 0; - clear_inode(inode); - unlock_kernel(); } + void jffs_write_super(struct super_block *sb) { -#ifdef USE_GC struct jffs_control *c = (struct jffs_control *)sb->u.generic_sbp; - - if(!c->fmc->no_call_gc) - jffs_garbage_collect(c); -#endif + + jffs_garbage_collect_trigger(c); } + static struct super_operations jffs_ops = { read_inode: jffs_read_inode, @@ -1541,12 +1627,15 @@ static struct super_operations jffs_ops = statfs: jffs_statfs, }; + static DECLARE_FSTYPE_DEV(jffs_fs_type, "jffs", jffs_read_super); static int __init init_jffs_fs(void) { - printk("JFFS version " JFFS_VERSION_STRING ", (C) 1999, 2000 Axis Communications AB\n"); + printk("JFFS version " + JFFS_VERSION_STRING + ", (C) 1999, 2000 Axis Communications AB\n"); return register_filesystem(&jffs_fs_type); } diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c index 039aaa04e..adb2e0c46 100644 --- a/fs/jffs/intrep.c +++ b/fs/jffs/intrep.c @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: intrep.c,v 1.15 2000/06/27 15:33:43 dwmw2 Exp $ + * $Id: intrep.c,v 1.39 2000/08/09 13:23:36 dwmw2 Exp $ * * Ported to Linux 2.3.x and MTD: * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB @@ -23,7 +23,7 @@ /* * Todo list: * - * memcpy_to_flash() and memcpy_from_flash()-functions. + * memcpy_to_flash() and memcpy_from_flash() functions. * * Implementation of hard links. * @@ -43,9 +43,6 @@ * Implement more meaning of the nlink member in various data structures. * nlink could be used in conjunction with hard links for instance. * - * Fix the rename stuff. (I.e. if we have two files `a' and `b' and we - * do a `mv b a'.) Half of this is already implemented. - * * Better memory management. Allocate data structures in larger chunks * if possible. * @@ -57,8 +54,8 @@ * information to be able to debug (or to supervise) JFFS during run-time. * */ + #define __NO_VERSION__ -#include <linux/config.h> #include <linux/types.h> #include <linux/malloc.h> #include <linux/jffs.h> @@ -69,6 +66,9 @@ #include <asm/semaphore.h> #include <asm/byteorder.h> #include <linux/version.h> +#include <linux/smp_lock.h> +#include <linux/sched.h> + #include "intrep.h" #include "jffs_fm.h" @@ -77,16 +77,6 @@ #define set_current_state(x) do{current->state = x;} while (0) #endif -#if defined(CONFIG_JFFS_FS_VERBOSE) && CONFIG_JFFS_FS_VERBOSE -#define D(x) x -#else -#define D(x) -#endif -#define D1(x) D(x) -#define D2(x) -#define D3(x) -#define ASSERT(x) x - #if defined(JFFS_MEMORY_DEBUG) && JFFS_MEMORY_DEBUG long no_jffs_file = 0; long no_jffs_node = 0; @@ -192,50 +182,53 @@ jffs_hexdump(struct mtd_info *mtd, loff_t pos, int size) } #endif -#define flash_safe_acquire(arg) -#define flash_safe_release(arg) +#define flash_safe_acquire(arg) +#define flash_safe_release(arg) + static int flash_safe_read(struct mtd_info *mtd, loff_t from, u_char *buf, size_t count) { - size_t retlen; - - MTD_READ(mtd, from, count, &retlen, buf); - if (retlen != count) { - printk("Didn't read all bytes in flash_safe_read()\n"); - } - return retlen; + size_t retlen; + + MTD_READ(mtd, from, count, &retlen, buf); + if (retlen != count) { + printk("Didn't read all bytes in flash_safe_read()\n"); + } + return retlen; } + static __u32 flash_read_u32(struct mtd_info *mtd, loff_t from) { - size_t retlen; - __u32 ret; - - MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret); - if (retlen != 4) { - printk("Didn't read all bytes in flash_read_u32()\n"); - return 0; - } + size_t retlen; + __u32 ret; + + MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret); + if (retlen != 4) { + printk("Didn't read all bytes in flash_read_u32()\n"); + return 0; + } - return ret; + return ret; } + static __u8 flash_read_u8(struct mtd_info *mtd, loff_t from) { - size_t retlen; - __u8 ret; - - MTD_READ(mtd, from, 1, &retlen, &ret); - if (retlen != 1) { - printk("Didn't read all bytes in flash_read_u32()\n"); - return 0; - } + size_t retlen; + __u8 ret; - return ret; + MTD_READ(mtd, from, 1, &retlen, &ret); + if (retlen != 1) { + printk("Didn't read a byte in flash_read_u8()\n"); + return 0; + } + + return ret; } @@ -243,95 +236,100 @@ static int flash_safe_write(struct mtd_info *mtd, loff_t to, const u_char *buf, size_t count) { - size_t retlen; + size_t retlen; - MTD_WRITE(mtd, to, count, &retlen, buf); - if (retlen != count) { - printk("Didn't write all bytes in flash_safe_write()\n"); - } - return retlen; + MTD_WRITE(mtd, to, count, &retlen, buf); + if (retlen != count) { + printk("Didn't write all bytes in flash_safe_write()\n"); + } + return retlen; } + static int flash_memset(struct mtd_info *mtd, loff_t to, const u_char c, size_t size) { - static unsigned char pattern[16]; - int i; + static unsigned char pattern[16]; + int i; - /* fill up pattern */ - - for(i = 0; i < 16; i++) - pattern[i] = c; + /* fill up pattern */ - /* write as many 16-byte chunks as we can */ - - while(size >= 16) { - flash_safe_write(mtd, to, pattern, 16); - size -= 16; - to += 16; - } + for(i = 0; i < 16; i++) + pattern[i] = c; - /* and the rest */ - - if(size) - flash_safe_write(mtd, to, pattern, size); + /* write as many 16-byte chunks as we can */ - return size; + while (size >= 16) { + flash_safe_write(mtd, to, pattern, 16); + size -= 16; + to += 16; + } + + /* and the rest */ + + if(size) + flash_safe_write(mtd, to, pattern, size); + + return size; } -static void intrep_erase_callback(struct erase_info *done) + +static void +intrep_erase_callback(struct erase_info *done) { - wait_queue_head_t *wait_q; + wait_queue_head_t *wait_q; - wait_q = (wait_queue_head_t *)done->priv; + wait_q = (wait_queue_head_t *)done->priv; - wake_up(wait_q); + wake_up(wait_q); } + static int flash_erase_region(struct mtd_info *mtd, loff_t start, size_t size) { - struct erase_info *erase; - DECLARE_WAITQUEUE(wait, current); - wait_queue_head_t wait_q; + struct erase_info *erase; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; - erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL); - if (!erase) - return -ENOMEM; + erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL); + if (!erase) + return -ENOMEM; - init_waitqueue_head(&wait_q); + init_waitqueue_head(&wait_q); - erase->mtd = mtd; - erase->callback = intrep_erase_callback; - erase->addr = start; - erase->len = size; - erase->priv = (u_long)&wait_q; + erase->mtd = mtd; + erase->callback = intrep_erase_callback; + erase->addr = start; + erase->len = size; + erase->priv = (u_long)&wait_q; - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&wait_q, &wait); - - if (MTD_ERASE(mtd, erase) < 0) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&wait_q, &wait); - kfree(erase); - - printk(KERN_WARNING "flash: erase of region [0x%ld, 0x%ld] totally failed\n", - (long)start, (long)start + size); - - return -1; - } - - schedule(); /* Wait for flash to finish. */ - /* FIXME: We could have been interrupted here. We don't deal with it */ - remove_wait_queue(&wait_q, &wait); - - kfree(erase); - - return 0; + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + if (MTD_ERASE(mtd, erase) < 0) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + kfree(erase); + + printk(KERN_WARNING "flash: erase of region [0x%lx, 0x%lx] " + "totally failed\n", (long)start, (long)start + size); + + return -1; + } + + schedule(); /* Wait for flash to finish. */ + /* FIXME: We could have been interrupted here. We don't deal with it */ + remove_wait_queue(&wait_q, &wait); + + kfree(erase); + + return 0; } + inline int jffs_min(int a, int b) { @@ -359,6 +357,7 @@ jffs_checksum(const void *data, int size) return sum; } + __u32 jffs_checksum_flash(struct mtd_info *mtd, loff_t start, int size) { @@ -371,6 +370,17 @@ jffs_checksum_flash(struct mtd_info *mtd, loff_t start, int size) return sum; } +static __inline__ void jffs_fm_write_lock(struct jffs_fmcontrol *fmc) +{ + down(&fmc->wlock); +} + +static __inline__ void jffs_fm_write_unlock(struct jffs_fmcontrol *fmc) +{ + up(&fmc->wlock); +} + + /* Create and initialize a new struct jffs_file. */ static struct jffs_file * jffs_create_file(struct jffs_control *c, @@ -417,12 +427,13 @@ jffs_create_control(kdev_t dev) goto fail_hash; } DJM(no_hash++); - for (i=0;i<c->hash_len;i++) + for (i = 0; i < c->hash_len; i++) INIT_LIST_HEAD(&c->hash[i]); if (!(c->fmc = jffs_build_begin(c, dev))) { goto fail_fminit; } c->next_ino = JFFS_MIN_INO + 1; + c->delete_list = (struct jffs_delete_list *) 0; return c; fail_fminit: @@ -449,9 +460,17 @@ jffs_cleanup_control(struct jffs_control *c) return; } + while (c->delete_list) { + struct jffs_delete_list *delete_list_element; + delete_list_element = c->delete_list; + c->delete_list = c->delete_list->next; + kfree(delete_list_element); + } + /* Free all files and nodes. */ if (c->hash) { jffs_foreach_file(c, jffs_free_node_list); + jffs_foreach_file(c, jffs_free_file); kfree(c->hash); DJM(no_hash--); } @@ -525,6 +544,18 @@ jffs_build_fs(struct super_block *sb) } } + while (c->delete_list) { + struct jffs_file *f; + struct jffs_delete_list *delete_list_element; + + if ((f = jffs_find_file(c, c->delete_list->ino))) { + f->deleted = 1; + } + delete_list_element = c->delete_list; + c->delete_list = c->delete_list->next; + kfree(delete_list_element); + } + /* Remove deleted nodes. */ if ((err = jffs_foreach_file(c, jffs_possibly_delete_file)) < 0) { printk(KERN_ERR "JFFS: Failed to remove deleted nodes.\n"); @@ -570,46 +601,60 @@ jffs_scan_flash(struct jffs_control *c) __u32 checksum; __u8 tmp_accurate; __u16 tmp_chksum; + __u32 deleted_file; loff_t pos = fmc->flash_start; loff_t start; loff_t end = fmc->flash_start + fmc->flash_size; - D1(printk("jffs_scan_flash(): start pos = 0x%ld, end = 0x%ld\n", + D1(printk("jffs_scan_flash(): start pos = 0x%lx, end = 0x%lx\n", (long)pos, (long)end)); flash_safe_acquire(fmc->mtd); /* Start the scan. */ while (pos < end) { + deleted_file = 0; /* Remember the position from where we started this scan. */ start = pos; switch (flash_read_u32(fmc->mtd, pos)) { case JFFS_EMPTY_BITMASK: - /* We have found 0xff on this block. We have to - scan the rest of the block to be sure it is - filled with 0xff. */ - D1(printk("jffs_scan_flash(): 0xff at pos 0x%ld.\n", + /* We have found 0xff at this position. We have to + scan the rest of the flash till the end or till + something else than 0xff is found. */ + D1(printk("jffs_scan_flash(): 0xff at pos 0x%lx.\n", (long)pos)); for (; pos < end && JFFS_EMPTY_BITMASK == flash_read_u32(fmc->mtd, pos); pos += 4); D1(printk("jffs_scan_flash(): 0xff ended at " - "pos 0x%ld.\n", (long)pos)); + "pos 0x%lx.\n", (long)pos)); + + /* If some free space ends in the middle of a sector, + treat it as dirty rather than clean. + This is to handle the case where one thread + allocated space for a node, but didn't get to + actually _write_ it before power was lost, leaving + a gap in the log. Shifting all node writes into + a single kernel thread will fix the original problem. + */ + if ((__u32) pos % fmc->sector_size) { + jffs_fmalloced(fmc, (__u32) start, + (__u32) (pos - start), 0); + } continue; case JFFS_DIRTY_BITMASK: - /* We have found 0x00 on this block. We have to - scan as far as possible to find out how much - is dirty. */ - D1(printk("jffs_scan_flash(): 0x00 at pos 0x%ld.\n", + /* We have found 0x00 at this position. Scan as far + as possible to find out how much is dirty. */ + D1(printk("jffs_scan_flash(): 0x00 at pos 0x%lx.\n", (long)pos)); for (; pos < end && JFFS_DIRTY_BITMASK == flash_read_u32(fmc->mtd, pos); pos += 4); D1(printk("jffs_scan_flash(): 0x00 ended at " - "pos 0x%ld.\n", (long)pos)); + "pos 0x%lx.\n", (long)pos)); jffs_fmalloced(fmc, (__u32) start, (__u32) (pos - start), 0); continue; @@ -622,8 +667,9 @@ jffs_scan_flash(struct jffs_control *c) bad_inode: /* We're f*cked. This is not solved yet. We have to scan for the magic pattern. */ - D1(printk("*************** Dirty flash memory or bad inode: " - "hexdump(pos = 0x%ld, len = 128):\n", + D1(printk("*************** Dirty flash memory or " + "bad inode: " + "hexdump(pos = 0x%lx, len = 128):\n", (long)pos)); D1(jffs_hexdump(fmc->mtd, pos, 128)); for (pos += 4; pos < end; pos += 4) { @@ -642,7 +688,7 @@ jffs_scan_flash(struct jffs_control *c) } /* We have found the beginning of an inode. Create a - node for it. */ + node for it unless there already is one available. */ if (!node) { if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), @@ -655,7 +701,8 @@ jffs_scan_flash(struct jffs_control *c) /* Read the next raw inode. */ - flash_safe_read(fmc->mtd, pos, (u_char *) &raw_inode, sizeof(struct jffs_raw_inode)); + flash_safe_read(fmc->mtd, pos, (u_char *) &raw_inode, + sizeof(struct jffs_raw_inode)); /* When we compute the checksum for the inode, we never count the 'accurate' or the 'checksum' fields. */ @@ -668,7 +715,7 @@ jffs_scan_flash(struct jffs_control *c) raw_inode.accurate = tmp_accurate; raw_inode.chksum = tmp_chksum; - D3(printk("*** We have found this raw inode at pos 0x%ld " + D3(printk("*** We have found this raw inode at pos 0x%lx " "on the flash:\n", (long)pos)); D3(jffs_print_raw_inode(&raw_inode)); @@ -689,6 +736,15 @@ jffs_scan_flash(struct jffs_control *c) if (raw_inode.nsize > JFFS_MAX_NAME_LEN) { goto bad_inode; } + + if (raw_inode.rename && raw_inode.dsize != sizeof(__u32)) { + printk(KERN_WARNING "jffs_scan_flash: Found a " + "rename node with dsize %u.\n", + raw_inode.dsize); + jffs_print_raw_inode(&raw_inode); + goto bad_inode; + } + /* The node's data segment should not exceed a certain length. */ if (raw_inode.dsize > fmc->max_chunk_size) { @@ -728,20 +784,26 @@ jffs_scan_flash(struct jffs_control *c) } } - /* Read the data in order to be sure it matches the - checksum. */ - checksum = jffs_checksum_flash(fmc->mtd, pos, raw_inode.dsize); - pos += raw_inode.dsize + JFFS_GET_PAD_BYTES(raw_inode.dsize); + /* Read the data, if it exists, in order to be sure it + matches the checksum. */ + if (raw_inode.dsize) { + if (raw_inode.rename) { + deleted_file = flash_read_u32(fmc->mtd, pos); + } + checksum = jffs_checksum_flash(fmc->mtd, pos, raw_inode.dsize); + pos += raw_inode.dsize + + JFFS_GET_PAD_BYTES(raw_inode.dsize); - if (checksum != raw_inode.dchksum) { - D1(printk("jffs_scan_flash(): Bad checksum: " - "checksum = %u, " - "raw_inode.dchksum = %u\n", - checksum, raw_inode.dchksum)); - jffs_fmalloced(fmc, (__u32) start, - (__u32) (pos - start), 0); - /* Reuse this unused struct jffs_node. */ - continue; + if (checksum != raw_inode.dchksum) { + D1(printk("jffs_scan_flash(): Bad checksum: " + "checksum = %u, " + "raw_inode.dchksum = %u\n", + checksum, raw_inode.dchksum)); + jffs_fmalloced(fmc, (__u32) start, + (__u32) (pos - start), 0); + /* Reuse this unused struct jffs_node. */ + continue; + } } check_node: @@ -780,6 +842,23 @@ jffs_scan_flash(struct jffs_control *c) "(err = %d)\n", err); break; } + if (raw_inode.rename) { + struct jffs_delete_list *dl + = (struct jffs_delete_list *) + kmalloc(sizeof(struct jffs_delete_list), + GFP_KERNEL); + if (!dl) { + D(printk("jffs_scan_flash: !dl\n")); + kfree(node); + DJM(no_jffs_node--); + flash_safe_release(fmc->flash_part); + return -ENOMEM; + } + dl->ino = deleted_file; + dl->next = c->delete_list; + c->delete_list = dl; + node->data_size = 0; + } D3(jffs_print_node(node)); node = 0; /* Don't free the node! */ } @@ -815,7 +894,8 @@ jffs_insert_node(struct jffs_control *c, struct jffs_file *f, int update_name = 0; int insert_into_tree = 0; - D2(printk("jffs_insert_node(): ino = %u, version = %u, name = \"%s\"\n", + D2(printk("jffs_insert_node(): ino = %u, version = %u, " + "name = \"%s\"\n", raw_inode->ino, raw_inode->version, ((name && *name) ? name : ""))); @@ -942,11 +1022,9 @@ jffs_insert_node(struct jffs_control *c, struct jffs_file *f, } jffs_remove_redundant_nodes(f); } -#ifdef USE_GC - if (!c->fmc->no_call_gc) { - jffs_garbage_collect(c); - } -#endif + + jffs_garbage_collect_trigger(c); + D3(printk("jffs_insert_node(): ---------------------------" "------------------------------------------- 2\n")); } @@ -1247,8 +1325,8 @@ jffs_write_dummy_node(struct jffs_control *c, struct jffs_fm *dirty_fm) (u_char *)&raw_inode, sizeof(struct jffs_raw_inode))) < 0) { - printk(KERN_ERR "JFFS: jffs_write_dummy_node: " - "flash_safe_write failed!\n"); + printk(KERN_ERR "JFFS: jffs_write_dummy_node: " + "flash_safe_write failed!\n"); return err; } } @@ -1262,6 +1340,7 @@ jffs_write_dummy_node(struct jffs_control *c, struct jffs_fm *dirty_fm) return 0; } + /* Write a raw inode, possibly its name and possibly some data. */ int jffs_write_node(struct jffs_control *c, struct jffs_node *node, @@ -1269,7 +1348,7 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, const char *name, const unsigned char *data) { struct jffs_fmcontrol *fmc = c->fmc; - struct jffs_fm *fm; + struct jffs_fm *fm = NULL; __u32 pos; int err; __u32 total_name_size = raw_inode->nsize @@ -1296,30 +1375,54 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, (name ? name : ""), raw_inode->ino, raw_inode->version, total_size)); - /* First try to allocate some flash memory. */ - if ((err = jffs_fmalloc(fmc, total_size, node, &fm)) < 0) { - D(printk("jffs_write_node(): jffs_fmalloc(0x%p, %u) " - "failed!\n", fmc, total_size)); - return err; - } - else if (!fm->nodes) { - /* The jffs_fm struct that we got is not good enough. - Make that space dirty. */ - if ((err = jffs_write_dummy_node(c, fm)) < 0) { - D(printk("jffs_write_node(): " - "jffs_write_dummy_node(): Failed!\n")); - kfree(fm); - DJM(no_jffs_fm--); + jffs_fm_write_lock(fmc); + + while (!fm) { + + /* First try to allocate some flash memory. */ + err = jffs_fmalloc(fmc, total_size, node, &fm); + + if (err == -ENOSPC) { + /* Just out of space. GC and try again */ + if (fmc->dirty_size < fmc->sector_size) { + D(printk("jffs_write_node(): jffs_fmalloc(0x%p, %u) " + "failed, no dirty space to GC\n", fmc, + total_size)); + return err; + } + + D1(printk(KERN_INFO "jffs_write_node(): Calling jffs_garbage_collect_now()\n")); + jffs_fm_write_unlock(fmc); + if ((err = jffs_garbage_collect_now(c))) { + D(printk("jffs_write_node(): jffs_garbage_collect_now() failed\n")); + return err; + } + jffs_fm_write_lock(fmc); + continue; + } + + if (err < 0) { + jffs_fm_write_unlock(fmc); + + D(printk("jffs_write_node(): jffs_fmalloc(0x%p, %u) " + "failed!\n", fmc, total_size)); return err; } - /* Get a new one. */ - if ((err = jffs_fmalloc(fmc, total_size, node, &fm)) < 0) { - D(printk("jffs_write_node(): Second " - "jffs_fmalloc(0x%p, %u) failed!\n", - fmc, total_size)); - return err; + + if (!fm->nodes) { + /* The jffs_fm struct that we got is not good enough. + Make that space dirty and try again */ + if ((err = jffs_write_dummy_node(c, fm)) < 0) { + kfree(fm); + DJM(no_jffs_fm--); + jffs_fm_write_unlock(fmc); + D(printk("jffs_write_node(): " + "jffs_write_dummy_node(): Failed!\n")); + return err; + } + fm = NULL; } - } + } /* while(!fm) */ node->fm = fm; ASSERT(if (fm->nodes == 0) { @@ -1341,7 +1444,7 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, raw_inode->accurate = 0xff; D3(printk("jffs_write_node(): About to write this raw inode to the " - "flash at pos 0x%ld:\n", (long)pos)); + "flash at pos 0x%lx:\n", (long)pos)); D3(jffs_print_raw_inode(raw_inode)); /* Step 1: Write the raw jffs inode to the flash. */ @@ -1350,8 +1453,9 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, sizeof(struct jffs_raw_inode))) < 0) { jffs_fmfree_partly(fmc, fm, total_name_size + total_data_size); - printk(KERN_ERR "JFFS: jffs_write_node: Failed to write " - "raw_inode.\n"); + jffs_fm_write_unlock(fmc); + printk(KERN_ERR "JFFS: jffs_write_node: Failed to write " + "raw_inode.\n"); return err; } pos += sizeof(struct jffs_raw_inode); @@ -1359,9 +1463,10 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, /* Step 2: Write the name, if there is any. */ if (raw_inode->nsize) { if ((err = flash_safe_write(fmc->mtd, pos, - (u_char *)name, + (u_char *)name, raw_inode->nsize)) < 0) { jffs_fmfree_partly(fmc, fm, total_data_size); + jffs_fm_write_unlock(fmc); printk(KERN_ERR "JFFS: jffs_write_node: Failed to " "write the name.\n"); return err; @@ -1374,12 +1479,13 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, if ((err = flash_safe_write(fmc->mtd, pos, data, raw_inode->dsize)) < 0) { jffs_fmfree_partly(fmc, fm, 0); - printk(KERN_ERR "JFFS: jffs_write_node: Failed to " + jffs_fm_write_unlock(fmc); + printk(KERN_ERR "JFFS: jffs_write_node: Failed to " "write the data.\n"); return err; } } - + jffs_fm_write_unlock(fmc); D3(printk("jffs_write_node(): Leaving...\n")); return raw_inode->dsize; } /* jffs_write_node() */ @@ -1498,7 +1604,7 @@ jffs_foreach_file(struct jffs_control *c, int (*func)(struct jffs_file *)) } -/* Free all memory associated with a file. */ +/* Free all nodes associated with a file. */ int jffs_free_node_list(struct jffs_file *f) { @@ -1518,6 +1624,23 @@ jffs_free_node_list(struct jffs_file *f) } +/* Free a file and its name. */ +int +jffs_free_file(struct jffs_file *f) +{ + D3(printk("jffs_free_file: f #%u, \"%s\"\n", + f->ino, (f->name ? f->name : ""))); + + if (f->name) { + kfree(f->name); + DJM(no_name--); + } + kfree(f); + DJM(no_jffs_file--); + return 0; +} + + /* See if a file is deleted. If so, mark that file's nodes as obsolete. */ int jffs_possibly_delete_file(struct jffs_file *f) @@ -1533,7 +1656,8 @@ jffs_possibly_delete_file(struct jffs_file *f) }); if (f->deleted) { - /* First try to remove all older versions. */ + /* First try to remove all older versions. Commence with + the oldest node. */ for (n = f->version_head; n; n = n->version_next) { if (!n->fm) { continue; @@ -1543,15 +1667,12 @@ jffs_possibly_delete_file(struct jffs_file *f) } } /* Unlink the file from the filesystem. */ - jffs_unlink_file_from_tree(f); + if (!f->c->building_fs) { + jffs_unlink_file_from_tree(f); + } jffs_unlink_file_from_hash(f); jffs_free_node_list(f); - if (f->name) { - kfree(f->name); - DJM(no_name--); - } - kfree(f); - DJM(no_jffs_file--); + jffs_free_file(f); } return 0; } @@ -1589,7 +1710,7 @@ jffs_build_file(struct jffs_file *f) Starting offset of area to be removed is node->data_offset, and the length of the area is in node->removed_size. */ -static void +static int jffs_delete_data(struct jffs_file *f, struct jffs_node *node) { struct jffs_node *n; @@ -1604,7 +1725,7 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node) && f->range_tail->data_offset + f->range_tail->data_size == offset) { /* A simple append; nothing to remove or no node to split. */ - return; + return 0; } /* Find the node where we should begin the removal. */ @@ -1616,7 +1737,7 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node) if (!n) { /* If there's no data in the file there's no data to remove either. */ - return; + return 0; } if (n->data_offset > offset) { @@ -1626,7 +1747,7 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node) } else if (n->data_offset < offset) { /* See if the node has to be split into two parts. */ - if (n->data_offset + n->data_size < offset + remove_size) { + if (n->data_offset + n->data_size > offset + remove_size) { /* Do the split. */ struct jffs_node *new_node; D3(printk("jffs_delete_data(): Split node with " @@ -1636,18 +1757,15 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node) kmalloc(sizeof(struct jffs_node), GFP_KERNEL))) { D(printk("jffs_delete_data(): -ENOMEM\n")); - return; + return -ENOMEM; } DJM(no_jffs_node++); new_node->ino = n->ino; new_node->version = n->version; new_node->data_offset = offset; - new_node->data_size = n->data_size - - (remove_size - + (offset - n->data_offset)); - new_node->fm_offset = n->fm_offset + n->data_size - + remove_size; + new_node->data_size = n->data_size - (remove_size + (offset - n->data_offset)); + new_node->fm_offset = n->fm_offset + (remove_size + (offset - n->data_offset)); new_node->name_size = n->name_size; new_node->fm = n->fm; new_node->version_prev = n; @@ -1671,7 +1789,12 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node) /* A very interesting can of worms. */ n->range_next = new_node; n->data_size = offset - n->data_offset; - jffs_add_node(new_node); + if (new_node->fm) + jffs_add_node(new_node); + else { + D1(printk(KERN_WARNING "jffs_delete_data(): Splitting an empty node (file hold).\n!")); + D1(printk(KERN_WARNING "FIXME: Did dwmw2 do the right thing here?\n")); + } n = new_node->range_next; remove_size = 0; } @@ -1693,8 +1816,9 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node) remove_size -= n->data_size; n = n->range_next; D3(printk("jffs_delete_data(): Removing node: " - "ino: %u, version: %u\n", - p->ino, p->version)); + "ino: %u, version: %u%s\n", + p->ino, p->version, + (p->fm ? "" : " (virtual)"))); if (p->fm) { jffs_fmfree(f->c->fmc, p->fm, p); } @@ -1720,12 +1844,13 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node) f->size -= node->removed_size; D3(printk("jffs_delete_data(): f->size = %d\n", f->size)); + return 0; } /* jffs_delete_data() */ /* Insert some data into a file. Prior to the call to this function, - jffs_delete_data() should be called. */ -static void + jffs_delete_data should be called. */ +static int jffs_insert_data(struct jffs_file *f, struct jffs_node *node) { D3(printk("jffs_insert_data(): node->data_offset = %u, " @@ -1733,7 +1858,7 @@ jffs_insert_data(struct jffs_file *f, struct jffs_node *node) node->data_offset, node->data_size, f->size)); /* Find the position where we should insert data. */ - + retry: if (node->data_offset == f->size) { /* A simple append. This is the most common operation. */ node->range_next = 0; @@ -1775,7 +1900,7 @@ jffs_insert_data(struct jffs_file *f, struct jffs_node *node) printk(KERN_ERR "jffs_insert_data(): " "Couldn't find a place to insert " "the data!\n"); - return; + return -1; }); } @@ -1788,38 +1913,84 @@ jffs_insert_data(struct jffs_file *f, struct jffs_node *node) f->size += node->data_size; } else if (node->data_offset > f->size) { - /* Not implemented yet. */ -#if 0 - /* Below is some example code for future use if we decide - to implement it. */ - /* This is code that isn't supported by VFS. So there aren't - really any reasons to implement it yet. */ - if (!f->range_head) { - if (node->data_offset > f->size) { - if (!(nn = jffs_alloc_node())) { - D(printk("jffs_insert_data(): " - "Allocation failed.\n")); - return; + /* Okay. This is tricky. This means that we want to insert + data at a place that is beyond the limits of the file as + it is constructed right now. This is actually a common + event that for instance could occur during the mounting + of the file system if a large file have been truncated, + rewritten and then only partially garbage collected. */ + + struct jffs_node *n; + + /* We need a place holder for the data that is missing in + front of this insertion. This "virtual node" will not + be associated with any space on the flash device. */ + struct jffs_node *virtual_node; + if (!(virtual_node = (struct jffs_node *) + kmalloc(sizeof(struct jffs_node), + GFP_KERNEL))) { + return -ENOMEM; + } + + D(printk("jffs_insert_data: Inserting a virtual node.\n")); + D(printk(" node->data_offset = %u\n", node->data_offset)); + D(printk(" f->size = %u\n", f->size)); + + virtual_node->ino = node->ino; + virtual_node->version = node->version; + virtual_node->removed_size = 0; + virtual_node->fm_offset = 0; + virtual_node->name_size = 0; + virtual_node->fm = 0; /* This is a virtual data holder. */ + virtual_node->version_prev = 0; + virtual_node->version_next = 0; + virtual_node->range_next = 0; + + /* Are there any data at all in the file yet? */ + if (f->range_head) { + virtual_node->data_offset + = f->range_tail->data_offset + + f->range_tail->data_size; + virtual_node->data_size + = node->data_offset - virtual_node->data_offset; + virtual_node->range_prev = f->range_tail; + f->range_tail->range_next = virtual_node; + } + else { + virtual_node->data_offset = 0; + virtual_node->data_size = node->data_offset; + virtual_node->range_prev = 0; + f->range_head = virtual_node; + } + + f->range_tail = virtual_node; + f->size += virtual_node->data_size; + + /* Insert this virtual node in the version list as well. */ + for (n = f->version_head; n ; n = n->version_next) { + if (n->version == virtual_node->version) { + virtual_node->version_prev = n->version_prev; + n->version_prev = virtual_node; + if (virtual_node->version_prev) { + virtual_node->version_prev + ->version_next = virtual_node; + } + else { + f->version_head = virtual_node; } - nn->version = JFFS_MAGIC_BITMASK; - nn->data_offset = 0; - nn->data_size = node->data_offset; - nn->removed_size = 0; - nn->fm_offset = 0; - nn->name_size = 0; - nn->fm = 0; /* This is a virtual data holder. */ - nn->version_prev = 0; - nn->version_next = 0; - nn->range_prev = 0; - nn->range_next = 0; - nh->range_head = nn; - nh->range_tail = nn; + virtual_node->version_next = n; + break; } } -#endif + + D(jffs_print_node(virtual_node)); + + /* Make a new try to insert the node. */ + goto retry; } D3(printk("jffs_insert_data(): f->size = %d\n", f->size)); + return 0; } @@ -1828,6 +1999,8 @@ jffs_insert_data(struct jffs_file *f, struct jffs_node *node) static int jffs_update_file(struct jffs_file *f, struct jffs_node *node) { + int err; + D3(printk("jffs_update_file(): ino: %u, version: %u\n", f->ino, node->version)); @@ -1841,15 +2014,21 @@ jffs_update_file(struct jffs_file *f, struct jffs_node *node) /* data_offset == X */ /* data_size == 0 */ /* remove_size != 0 */ - jffs_delete_data(f, node); + if ((err = jffs_delete_data(f, node)) < 0) { + return err; + } } } else { /* data_offset == X */ /* data_size != 0 */ /* remove_size == Y */ - jffs_delete_data(f, node); - jffs_insert_data(f, node); + if ((err = jffs_delete_data(f, node)) < 0) { + return err; + } + if ((err = jffs_insert_data(f, node)) < 0) { + return err; + } } return 0; } @@ -1868,7 +2047,7 @@ jffs_print_node(struct jffs_node *n) D(printk(" 0x%08x, /* fm_offset */\n", n->fm_offset)); D(printk(" 0x%02x, /* name_size */\n", n->name_size)); D(printk(" 0x%p, /* fm, fm->offset: %u */\n", - n->fm, n->fm->offset)); + n->fm, (n->fm ? n->fm->offset : 0))); D(printk(" 0x%p, /* version_prev */\n", n->version_prev)); D(printk(" 0x%p, /* version_next */\n", n->version_next)); D(printk(" 0x%p, /* range_prev */\n", n->range_prev)); @@ -1979,6 +2158,7 @@ jffs_print_tree(struct jffs_file *first_file, int indent) { struct jffs_file *f; char *space; + int dir; if (!first_file) { return; @@ -1993,10 +2173,11 @@ jffs_print_tree(struct jffs_file *first_file, int indent) space[indent] = '\0'; for (f = first_file; f; f = f->sibling_next) { - printk("%s%s (ino: %u, highest_version: %u, size: %u)\n", - space, (f->name ? f->name : "/"), + dir = S_ISDIR(f->mode); + printk("%s%s%s (ino: %u, highest_version: %u, size: %u)\n", + space, (f->name ? f->name : ""), (dir ? "/" : ""), f->ino, f->highest_version, f->size); - if (S_ISDIR(f->mode)) { + if (dir) { jffs_print_tree(f->children, indent + 2); } } @@ -2053,7 +2234,6 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) } DJM(no_jffs_node++); new_node->data_offset = node->data_offset; - new_node->data_size = size; new_node->removed_size = size; total_name_size = f->nsize + JFFS_GET_PAD_BYTES(f->nsize); total_data_size = size + JFFS_GET_PAD_BYTES(size); @@ -2062,23 +2242,28 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) new_node->fm_offset = sizeof(struct jffs_raw_inode) + total_name_size; + jffs_fm_write_lock(fmc); + if ((err = jffs_fmalloc(fmc, total_size, new_node, &fm)) < 0) { + DJM(no_jffs_node--); + jffs_fm_write_unlock(fmc); D(printk("jffs_rewrite_data(): Failed to allocate fm.\n")); kfree(new_node); - DJM(no_jffs_node--); return err; } else if (!fm->nodes) { /* The jffs_fm struct that we got is not good enough. */ if ((err = jffs_write_dummy_node(c, fm)) < 0) { + DJM(no_jffs_fm--); + jffs_fm_write_unlock(fmc); D(printk("jffs_rewrite_data(): " "jffs_write_dummy_node() Failed!\n")); kfree(fm); - DJM(no_jffs_fm--); return err; } /* Get a new one. */ if ((err = jffs_fmalloc(fmc, total_size, node, &fm)) < 0) { + jffs_fm_write_unlock(fmc); D(printk("jffs_rewrite_data(): Second " "jffs_fmalloc(0x%p, %u) failed!\n", fmc, total_size)); @@ -2127,10 +2312,11 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) sizeof(struct jffs_raw_inode) - sizeof(__u32) - sizeof(__u16) - sizeof(__u16))) < 0) { - printk(KERN_ERR "JFFS: jffs_rewrite_data: Write error during " - "rewrite. (raw inode)\n"); jffs_fmfree_partly(fmc, fm, total_name_size + total_data_size); + jffs_fm_write_unlock(fmc); + printk(KERN_ERR "JFFS: jffs_rewrite_data: Write error during " + "rewrite. (raw inode)\n"); return err; } pos += sizeof(struct jffs_raw_inode); @@ -2142,9 +2328,10 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) if ((err = flash_safe_write(fmc->mtd, pos, (u_char *)f->name, f->nsize)) < 0) { - printk(KERN_ERR "JFFS: jffs_rewrite_data: Write " - "error during rewrite. (name)\n"); jffs_fmfree_partly(fmc, fm, total_data_size); + jffs_fm_write_unlock(fmc); + printk(KERN_ERR "JFFS: jffs_rewrite_data: Write " + "error during rewrite. (name)\n"); return err; } pos += total_name_size; @@ -2166,19 +2353,22 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) __u32 s = jffs_min(size, PAGE_SIZE); if ((r = jffs_read_data(f, (char *)page, offset, s)) < s) { - printk(KERN_ERR "JFFS: jffs_rewrite_data: " + free_page((unsigned long)page); + jffs_fmfree_partly(fmc, fm, 0); + jffs_fm_write_unlock(fmc); + printk(KERN_ERR "JFFS: jffs_rewrite_data: " "jffs_read_data() " "failed! (r = %d)\n", r); - jffs_fmfree_partly(fmc, fm, 0); return -1; } if ((err = flash_safe_write(fmc->mtd, pos, page, r)) < 0) { - printk(KERN_ERR "JFFS: jffs_rewrite_data: " - "Write error during rewrite. " - "(data)\n"); free_page((unsigned long)page); jffs_fmfree_partly(fmc, fm, 0); + jffs_fm_write_unlock(fmc); + printk(KERN_ERR "JFFS: jffs_rewrite_data: " + "Write error during rewrite. " + "(data)\n"); return err; } pos += r; @@ -2202,14 +2392,16 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) &raw_inode)[JFFS_RAW_INODE_DCHKSUM_OFFSET], sizeof(__u32) + sizeof(__u16) + sizeof(__u16))) < 0) { - printk(KERN_ERR "JFFS: jffs_rewrite_data: Write error during " - "rewrite. (checksum)\n"); jffs_fmfree_partly(fmc, fm, 0); + jffs_fm_write_unlock(fmc); + printk(KERN_ERR "JFFS: jffs_rewrite_data: Write error during " + "rewrite. (checksum)\n"); return err; } /* Now make the file system aware of the newly written node. */ jffs_insert_node(c, f, &raw_inode, f->name, new_node); + jffs_fm_write_unlock(fmc); D3(printk("jffs_rewrite_data(): Leaving...\n")); return 0; @@ -2219,6 +2411,7 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) /* jffs_garbage_collect_next implements one step in the garbage collect process and is often called multiple times at each occasion of a garbage collect. */ + int jffs_garbage_collect_next(struct jffs_control *c) { @@ -2226,8 +2419,8 @@ jffs_garbage_collect_next(struct jffs_control *c) struct jffs_node *node; struct jffs_file *f; int size; - int data_size; - int total_name_size; + int data_size; + int total_name_size; int free_size = fmc->flash_size - (fmc->used_size + fmc->dirty_size); __u32 free_chunk_size1 = jffs_free_size1(fmc); D2(__u32 free_chunk_size2 = jffs_free_size2(fmc)); @@ -2235,17 +2428,17 @@ jffs_garbage_collect_next(struct jffs_control *c) /* Get the oldest node in the flash. */ node = jffs_get_oldest_node(fmc); ASSERT(if (!node) { - printk(KERN_ERR "JFFS: jffs_garbage_collect_next: " - "No oldest node found!\n"); + printk(KERN_ERR "JFFS: jffs_garbage_collect_next: " + "No oldest node found!\n"); return -1; }); /* Find its corresponding file too. */ f = jffs_find_file(c, node->ino); ASSERT(if (!f) { - printk(KERN_ERR "JFFS: jffs_garbage_collect_next: " - "No file to garbage collect! " - "(ino = 0x%08x)\n", node->ino); + printk(KERN_ERR "JFFS: jffs_garbage_collect_next: " + "No file to garbage collect! " + "(ino = 0x%08x)\n", node->ino); return -1; }); @@ -2254,23 +2447,25 @@ jffs_garbage_collect_next(struct jffs_control *c) (f->name ? f->name : ""), node->ino, node->version)); /* Compute how much we want to rewrite at the moment. */ - data_size = f->size - node->data_offset; - total_name_size = f->nsize + JFFS_GET_PAD_BYTES(f->nsize); - size = sizeof(struct jffs_raw_inode) + total_name_size - + data_size + JFFS_GET_PAD_BYTES(data_size); + data_size = f->size - node->data_offset; + total_name_size = f->nsize + JFFS_GET_PAD_BYTES(f->nsize); + size = sizeof(struct jffs_raw_inode) + total_name_size + + data_size + JFFS_GET_PAD_BYTES(data_size); - D2(printk(" total_name_size: %u\n", total_name_size)); - D2(printk(" data_size: %u\n", data_size)); + D2(printk(" total_name_size: %u\n", total_name_size)); + D2(printk(" data_size: %u\n", data_size)); D2(printk(" size: %u\n", size)); D2(printk(" f->nsize: %u\n", f->nsize)); D2(printk(" f->size: %u\n", f->size)); + D2(printk(" node->data_offset: %u\n", node->data_offset)); D2(printk(" free_chunk_size1: %u\n", free_chunk_size1)); D2(printk(" free_chunk_size2: %u\n", free_chunk_size2)); + D2(printk(" node->fm->offset: 0x%08x\n", node->fm->offset)); if (size > fmc->max_chunk_size) { size = fmc->max_chunk_size; - data_size = size - sizeof(struct jffs_raw_inode) - - total_name_size; + data_size = size - sizeof(struct jffs_raw_inode) + - total_name_size; } if (size > free_chunk_size1) { @@ -2282,11 +2477,11 @@ jffs_garbage_collect_next(struct jffs_control *c) = jffs_fmalloced(fmc, fmc->tail->offset + fmc->tail->size, free_chunk_size1, NULL); - if (!dirty_fm) { - printk(KERN_ERR "JFFS: " - "jffs_garbage_collect_next: " - "Failed to allocate `dirty' " - "flash memory!\n"); + if (!dirty_fm) { + printk(KERN_ERR "JFFS: " + "jffs_garbage_collect_next: " + "Failed to allocate `dirty' " + "flash memory!\n"); return -1; } jffs_write_dummy_node(c, dirty_fm); @@ -2294,24 +2489,24 @@ jffs_garbage_collect_next(struct jffs_control *c) } size = free_chunk_size1; - data_size = size - sizeof(struct jffs_raw_inode) - - total_name_size; + data_size = size - sizeof(struct jffs_raw_inode) + - total_name_size; } D2(printk(" size: %u (again)\n", size)); if (free_size - size < fmc->sector_size) { /* Just rewrite that node (or even less). */ - jffs_rewrite_data(f, node, - jffs_min(node->data_size, data_size)); + jffs_rewrite_data(f, node, + jffs_min(node->data_size, data_size)); } else { size -= (sizeof(struct jffs_raw_inode) + f->nsize); - jffs_rewrite_data(f, node, data_size); + jffs_rewrite_data(f, node, data_size); } jffs_garbage_collect_next_end: - D3(printk("jffs_garbage_collect_next: Leaving...\n")); + D3(printk("jffs_garbage_collect_next: Leaving...\n")); return 0; } /* jffs_garbage_collect_next */ @@ -2319,7 +2514,9 @@ jffs_garbage_collect_next_end: /* If an obsolete node is partly going to be erased due to garbage collection, the part that isn't going to be erased must be filled with zeroes so that the scan of the flash will work smoothly next - time. + time. (The data in the file could for instance be a JFFS image + which could cause enormous confusion during a scan of the flash + device if we didn't do this.) There are two phases in this procedure: First, the clearing of the name and data parts of the node. Second, possibly also clearing a part of the raw inode as well. If the box is power cycled during @@ -2400,19 +2597,19 @@ jffs_try_to_erase(struct jffs_control *c) D2(printk("jffs_try_to_erase(): erase_size = %ld\n", erase_size)); - if (erase_size == 0) { - return 0; - } - else if (erase_size < 0) { - printk(KERN_ERR "JFFS: jffs_try_to_erase: " - "jffs_erasable_size returned %ld.\n", erase_size); + if (erase_size == 0) { + return 0; + } + else if (erase_size < 0) { + printk(KERN_ERR "JFFS: jffs_try_to_erase: " + "jffs_erasable_size returned %ld.\n", erase_size); return erase_size; } - if ((err = jffs_clear_end_of_node(c, erase_size)) < 0) { - printk(KERN_ERR "JFFS: jffs_try_to_erase: " - "Clearing of node failed.\n"); - return err; + if ((err = jffs_clear_end_of_node(c, erase_size)) < 0) { + printk(KERN_ERR "JFFS: jffs_try_to_erase: " + "Clearing of node failed.\n"); + return err; } offset = fmc->head->offset - fmc->flash_start; @@ -2444,7 +2641,7 @@ jffs_try_to_erase(struct jffs_control *c) for (; pos < end; pos += 4) { if (*(__u32 *)pos != JFFS_EMPTY_BITMASK) { - printk("JFFS: Erase failed! pos = 0x%ld\n", + printk("JFFS: Erase failed! pos = 0x%lx\n", (long)pos); jffs_hexdump(fmc->mtd, pos, jffs_min(256, end - pos)); @@ -2475,7 +2672,7 @@ jffs_try_to_erase(struct jffs_control *c) /* There are different criteria that should trigger a garbage collect: - + 1. There is too much dirt in the memory. 2. The free space is becoming small. 3. There are many versions of a node. @@ -2485,27 +2682,26 @@ jffs_try_to_erase(struct jffs_control *c) should not be too large (span more than one sector in the flash memory for exemple). Of course there is a limit on how intelligent this garbage collection can be. */ + int -jffs_garbage_collect(struct jffs_control *c) +jffs_garbage_collect_now(struct jffs_control *c) { struct jffs_fmcontrol *fmc = c->fmc; long erased_total = 0; long erased; int result = 0; D1(int i = 1); - - D2(printk("***jffs_garbage_collect(): fmc->dirty_size = %u\n", + D2(printk("***jffs_garbage_collect_now(): fmc->dirty_size = %u\n", fmc->dirty_size)); D2(jffs_print_fmcontrol(fmc)); - c->fmc->no_call_gc = 1; + down(&fmc->gclock); - /* While there is too much dirt left and it is possible - to garbage collect, do so. */ + /* If it is possible to garbage collect, do so. */ - while (fmc->dirty_size >= fmc->sector_size) { + if (fmc->dirty_size >= fmc->sector_size) { - D1(printk("***jffs_garbage_collect(): round #%u, " + D1(printk("***jffs_garbage_collect_now(): round #%u, " "fmc->dirty_size = %u\n", i++, fmc->dirty_size)); D2(jffs_print_fmcontrol(fmc)); @@ -2533,7 +2729,7 @@ jffs_garbage_collect(struct jffs_control *c) } else { /* What should we do here? */ - D(printk(" jffs_garbage_collect(): " + D(printk(" jffs_garbage_collect_now(): " "erased: %ld, free_size: %u\n", erased, free_size)); result = -1; @@ -2541,19 +2737,204 @@ jffs_garbage_collect(struct jffs_control *c) } } - D1(printk(" jffs_garbage_collect(): erased: %ld\n", erased)); + D1(printk(" jffs_garbage_collect_now(): erased: %ld\n", erased)); erased_total += erased; DJM(jffs_print_memory_allocation_statistics()); } - gc_end: - c->fmc->no_call_gc = 0; + up(&fmc->gclock); - D3(printk(" jffs_garbage_collect(): Leaving...\n")); + D3(printk(" jffs_garbage_collect_now(): Leaving...\n")); D1(if (erased_total) { printk("erased_total = %ld\n", erased_total); jffs_print_fmcontrol(fmc); }); + + if (!erased_total && !result) + return -ENOSPC; + return result; +} /* jffs_garbage_collect_now() */ + + +/* Determine if it is reasonable to start garbage collection. + We start a gc pass if either: + - The number of free bytes < MIN_FREE_BYTES && at least one + block is dirty, OR + - The number of dirty bytes > MAX_DIRTY_BYTES +*/ +static inline int thread_should_wake (struct jffs_control *c) +{ + __u32 nfree = c->fmc->flash_size - c->fmc->used_size - c->fmc->dirty_size; + + D1(printk (KERN_NOTICE "thread_should_wake(): free=%d, dirty=%d, blocksize=%d.\n", + nfree, c->fmc->dirty_size, c->fmc->sector_size)); + + /* If there's not enough dirty space to free a block, there's no point. */ + if (c->fmc->dirty_size < c->fmc->sector_size) + return 0; + + /* If there are fewer free bytes than the threshold, GC */ + if (nfree < c->gc_minfree_threshold) + return 1; + + /* If there are more dirty bytes than the threshold, GC */ + if (c->fmc->dirty_size > c->gc_maxdirty_threshold) + return 1; + + /* FIXME: What about the "There are many versions of a node" condition? */ + + return 0; } + + +void jffs_garbage_collect_trigger(struct jffs_control *c) +{ + /* NOTE: We rely on the fact that we have the BKL here. + * Otherwise, the gc_task could go away between the check + * and the wake_up_process() + */ + if (c->gc_task && thread_should_wake(c)) + send_sig(SIGHUP, c->gc_task, 1); +} + + +/* Kernel threads take (void *) as arguments. Thus we pass + the jffs_control data as a (void *) and then cast it. */ +int +jffs_garbage_collect_thread(void *ptr) +{ + struct jffs_control *c = (struct jffs_control *) ptr; + struct jffs_fmcontrol *fmc = c->fmc; + long erased_total = 0; + long erased; + int result = 0; + D1(int i = 1); + + c->gc_task = current; + + lock_kernel(); + exit_mm(c->gc_task); + + current->session = 1; + current->pgrp = 1; + init_MUTEX_LOCKED(&c->gc_thread_sem); /* barrier */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGQUIT) | sigmask(SIGSTOP) | sigmask(SIGCONT)); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + strcpy(current->comm, "jffs_gcd"); + + D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): Starting infinite loop.\n")); + + for (;;) { + /* See if we need to start gc. If we don't, go to sleep. + + Current implementation is a BAD THING(tm). If we try + to unmount the FS, the unmount operation will sleep waiting + for this thread to exit. We need to arrange to send it a + sig before the umount process sleeps. + */ + + if (!thread_should_wake(c)) + set_current_state (TASK_INTERRUPTIBLE); + + schedule(); /* Yes, we do this even if we want to go + on immediately - we're a low priority + background task. */ + + /* Put_super will send a SIGQUIT and then wait on the sem. + */ + while (signal_pending(current)) { + siginfo_t info; + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + switch(signr) { + case SIGSTOP: + D1(printk("jffs_garbage_collect_thread(): SIGSTOP received.\n")); + set_current_state(TASK_STOPPED); + schedule(); + break; + + case SIGQUIT: + D1(printk("jffs_garbage_collect_thread(): SIGQUIT received.\n")); + c->gc_task = NULL; + up(&c->gc_thread_sem); + unlock_kernel(); + return(0); + } + } + + + D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): collecting.\n")); +// printk (KERN_NOTICE "free=%d, dirty=%d, blocksize=%ld.\n", count_free_bytes(c), count_dirty_bytes(c), c->sb->s_blocksize); + + D2(printk("***jffs_garbage_collect_thread(): fmc->dirty_size = %u\n", + fmc->dirty_size)); + D2(jffs_print_fmcontrol(fmc)); + + if (fmc->dirty_size < fmc->sector_size) { + printk(KERN_WARNING "jffs_garbage_collect_thread with insufficient dirty space (0x%x)\n", fmc->dirty_size); + continue; + } + + down(&c->fmc->gclock); + + D1(printk("***jffs_garbage_collect_thread(): round #%u, " + "fmc->dirty_size = %u\n", i++, fmc->dirty_size)); + D2(jffs_print_fmcontrol(fmc)); + + /* At least one sector should be able to free now. */ + if ((erased = jffs_try_to_erase(c)) < 0) { + printk(KERN_WARNING "JFFS: Error in " + "garbage collector.\n"); + result = erased; + goto gc_end; + } + else if (erased == 0) { + __u32 free_size = fmc->flash_size + - (fmc->used_size + + fmc->dirty_size); + + if (free_size > 0) { + /* Let's dare to make a garbage collect. */ + if ((result = jffs_garbage_collect_next(c)) + < 0) { + printk(KERN_ERR "JFFS: Something " + "has gone seriously wrong " + "with a garbage collect.\n"); + goto gc_end; + } + } + else { + /* What should we do here? */ + D(printk(" jffs_garbage_collect(): " + "erased: %ld, free_size: %u\n", + erased, free_size)); + result = -1; + goto gc_end; + } + } + + D1(printk(" jffs_garbage_collect(): erased: %ld\n", erased)); + erased_total += erased; + DJM(jffs_print_memory_allocation_statistics()); + + + gc_end: + up(&c->fmc->gclock); + + D3(printk(" jffs_garbage_collect(): Leaving...\n")); + D1(if (erased_total) { + printk("erased_total = %ld\n", erased_total); + jffs_print_fmcontrol(fmc); + }); + + } /* for (;;) */ +} /* jffs_garbage_collect_thread() */ + diff --git a/fs/jffs/intrep.h b/fs/jffs/intrep.h index 3336c69e6..d4638af20 100644 --- a/fs/jffs/intrep.h +++ b/fs/jffs/intrep.h @@ -10,13 +10,13 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: intrep.h,v 1.2 2000/05/24 13:13:56 alex Exp $ + * $Id: intrep.h,v 1.6 2000/08/04 14:29:17 dwmw2 Exp $ * */ #ifndef __LINUX_JFFS_INTREP_H__ #define __LINUX_JFFS_INTREP_H__ - +#include "jffs_fm.h" inline int jffs_min(int a, int b); inline int jffs_max(int a, int b); __u32 jffs_checksum(const void *data, int size); @@ -34,6 +34,7 @@ void jffs_free_node(struct jffs_node *node); int jffs_foreach_file(struct jffs_control *c, int (*func)(struct jffs_file *)); int jffs_free_node_list(struct jffs_file *f); +int jffs_free_file(struct jffs_file *f); int jffs_possibly_delete_file(struct jffs_file *f); int jffs_build_file(struct jffs_file *f); int jffs_insert_file_into_hash(struct jffs_file *f); @@ -49,7 +50,26 @@ int jffs_write_node(struct jffs_control *c, struct jffs_node *node, int jffs_read_data(struct jffs_file *f, char *buf, __u32 read_offset, __u32 size); /* Garbage collection stuff. */ -int jffs_garbage_collect(struct jffs_control *c); +int jffs_garbage_collect_thread(void *c); +void jffs_garbage_collect_trigger(struct jffs_control *c); +int jffs_garbage_collect_now(struct jffs_control *c); + +/* Is there enough space on the flash? */ +static inline int JFFS_ENOUGH_SPACE(struct jffs_control *c) +{ + struct jffs_fmcontrol *fmc = c->fmc; + + while (1) { + if ((fmc->flash_size - (fmc->used_size + fmc->dirty_size)) + >= fmc->min_free_size) { + return 1; + } + if (fmc->dirty_size < fmc->sector_size) + return 0; + + jffs_garbage_collect_now(c); + } +} /* For debugging purposes. */ void jffs_print_node(struct jffs_node *n); diff --git a/fs/jffs/jffs_fm.c b/fs/jffs/jffs_fm.c index abdb759a5..3140b8a53 100644 --- a/fs/jffs/jffs_fm.c +++ b/fs/jffs/jffs_fm.c @@ -10,29 +10,18 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: jffs_fm.c,v 1.8 2000/07/13 13:15:33 scote1 Exp $ + * $Id: jffs_fm.c,v 1.14 2000/08/09 14:26:35 dwmw2 Exp $ * * Ported to Linux 2.3.x and MTD: * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB * */ #define __NO_VERSION__ -#include <linux/config.h> #include <linux/malloc.h> #include <linux/blkdev.h> #include <linux/jffs.h> #include "jffs_fm.h" -#if defined(CONFIG_JFFS_FS_VERBOSE) && CONFIG_JFFS_FS_VERBOSE -#define D(x) x -#else -#define D(x) -#endif -#define D1(x) D(x) -#define D2(x) -#define D3(x) -#define ASSERT(x) x - #if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE static int jffs_mark_obsolete(struct jffs_fmcontrol *fmc, __u32 fm_offset); #endif @@ -72,12 +61,13 @@ jffs_build_begin(struct jffs_control *c, kdev_t dev) fmc->max_chunk_size = fmc->sector_size >> 1; fmc->min_free_size = (fmc->sector_size << 1) - fmc->max_chunk_size; fmc->mtd = mtd; - fmc->no_call_gc = 0; + init_MUTEX(&fmc->gclock); fmc->c = c; fmc->head = 0; fmc->tail = 0; fmc->head_extra = 0; fmc->tail_extra = 0; + init_MUTEX(&fmc->wlock); return fmc; } @@ -425,18 +415,18 @@ int jffs_add_node(struct jffs_node *node) { struct jffs_node_ref *ref; - struct jffs_fm *fm = node->fm; - int s = sizeof(struct jffs_node_ref); D3(printk("jffs_add_node(): ino = %u\n", node->ino)); - if (!(ref = (struct jffs_node_ref *)kmalloc(s, GFP_KERNEL))) { + ref = (struct jffs_node_ref *)kmalloc(sizeof(struct jffs_node_ref), + GFP_KERNEL); + if (!ref) return -ENOMEM; - } + DJM(no_jffs_node_ref++); ref->node = node; - ref->next = fm->nodes; - fm->nodes = ref; + ref->next = node->fm->nodes; + node->fm->nodes = ref; return 0; } @@ -614,16 +604,16 @@ jffs_flash_erasable_size(struct mtd_info *mtd, __u32 offset, __u32 size) ssize = mtd->erasesize; if (offset % ssize) { - printk(KERN_WARNING "jffs_flash_erasable_size() given non-aligned offset %lx (erasesize %lx)\n", offset, ssize); + printk(KERN_WARNING "jffs_flash_erasable_size() given non-aligned offset %x (erasesize %lx)\n", offset, ssize); /* The offset is not sector size aligned. */ return -1; } else if (offset > mtd->size) { - printk(KERN_WARNING "jffs_flash_erasable_size given offset off the end of device (%lx > %lx)\n", offset, mtd->size); + printk(KERN_WARNING "jffs_flash_erasable_size given offset off the end of device (%x > %lx)\n", offset, mtd->size); return -2; } else if (offset + size > mtd->size) { - printk(KERN_WARNING "jffs_flash_erasable_size() given length which runs off the end of device (ofs %lx + len %lx = %lx, > %lx)\n", offset,size, offset+size, mtd->size); + printk(KERN_WARNING "jffs_flash_erasable_size() given length which runs off the end of device (ofs %x + len %x = %x, > %lx)\n", offset,size, offset+size, mtd->size); return -3; } diff --git a/fs/jffs/jffs_fm.h b/fs/jffs/jffs_fm.h index 1461d788a..82077d9d1 100644 --- a/fs/jffs/jffs_fm.h +++ b/fs/jffs/jffs_fm.h @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: jffs_fm.h,v 1.3 2000/07/04 16:15:42 dwmw2 Exp $ + * $Id: jffs_fm.h,v 1.7 2000/08/08 09:10:39 dwmw2 Exp $ * * Ported to Linux 2.3.x and MTD: * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB @@ -23,6 +23,7 @@ #include <linux/types.h> #include <linux/jffs.h> #include <linux/mtd/mtd.h> +#include <linux/config.h> /* The alignment between two nodes in the flash memory. */ #define JFFS_ALIGN_SIZE 4 @@ -30,7 +31,31 @@ /* Mark the on-flash space as obsolete when appropriate. */ #define JFFS_MARK_OBSOLETE 0 -#define CONFIG_JFFS_FS_VERBOSE 0 +#ifndef CONFIG_JFFS_FS_VERBOSE +#define CONFIG_JFFS_FS_VERBOSE 1 +#endif + +#if CONFIG_JFFS_FS_VERBOSE > 0 +#define D(x) x +#define D1(x) D(x) +#else +#define D(x) +#define D1(x) +#endif + +#if CONFIG_JFFS_FS_VERBOSE > 1 +#define D2(x) D(x) +#else +#define D2(x) +#endif + +#if CONFIG_JFFS_FS_VERBOSE > 2 +#define D3(x) D(x) +#else +#define D3(x) +#endif + +#define ASSERT(x) x /* How many padding bytes should be inserted between two chunks of data on the flash? */ @@ -38,11 +63,6 @@ - ((__u32)(size) % JFFS_ALIGN_SIZE)) \ % JFFS_ALIGN_SIZE) -/* Is there enough space on the flash? */ -#define JFFS_ENOUGH_SPACE(fmc) (((fmc)->flash_size - (fmc)->used_size \ - - (fmc)->dirty_size) >= (fmc)->min_free_size) - - struct jffs_node_ref { struct jffs_node *node; @@ -71,12 +91,13 @@ struct jffs_fmcontrol to perform garbage collections. */ __u32 max_chunk_size; /* The maximum size of a chunk of data. */ struct mtd_info *mtd; - __u32 no_call_gc; + struct semaphore gclock; struct jffs_control *c; struct jffs_fm *head; struct jffs_fm *tail; struct jffs_fm *head_extra; struct jffs_fm *tail_extra; + struct semaphore wlock; }; /* Notice the two members head_extra and tail_extra in the jffs_control |