summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-08-25 06:33:44 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-08-25 06:33:44 +0000
commit6bd6dbbd3ae53a268a510270bebaab24fff382ca (patch)
tree41d0361e6b48ce74584c9a6fcb475d5054ca4141 /fs
parentee355114ec6062d00c1376b184b886a39e74fd4e (diff)
Merge with Linux 2.4.0-test6-pre10.
Diffstat (limited to 'fs')
-rw-r--r--fs/Config.in3
-rw-r--r--fs/jffs/Makefile5
-rw-r--r--fs/jffs/inode-v23.c347
-rw-r--r--fs/jffs/intrep.c971
-rw-r--r--fs/jffs/intrep.h26
-rw-r--r--fs/jffs/jffs_fm.c34
-rw-r--r--fs/jffs/jffs_fm.h37
-rw-r--r--fs/partitions/acorn.c170
-rw-r--r--fs/partitions/acorn.h39
9 files changed, 1078 insertions, 554 deletions
diff --git a/fs/Config.in b/fs/Config.in
index 851c54726..e48f75496 100644
--- a/fs/Config.in
+++ b/fs/Config.in
@@ -25,6 +25,9 @@ dep_tristate ' UMSDOS: Unix-like file system on top of standard MSDOS fs' CON
dep_tristate ' VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
dep_tristate 'EFS file system support (read only) (EXPERIMENTAL)' CONFIG_EFS_FS $CONFIG_EXPERIMENTAL
dep_tristate 'Journalling Flash File System (JFFS) support (EXPERIMENTAL)' CONFIG_JFFS_FS $CONFIG_EXPERIMENTAL $CONFIG_MTD
+if [ "$CONFIG_JFFS_FS" != "n" ] ; then
+ int 'JFFS debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_JFFS_FS_VERBOSE 0
+fi
tristate 'Compressed ROM file system support' CONFIG_CRAMFS
tristate 'Simple RAM-based file system support' CONFIG_RAMFS
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(&current->sigmask_lock);
+ siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGQUIT) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->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(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->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
diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c
index 0e1ba2d77..fc13e8698 100644
--- a/fs/partitions/acorn.c
+++ b/fs/partitions/acorn.c
@@ -1,11 +1,10 @@
/*
* linux/arch/arm/drivers/block/adfspart.c
*
- * Copyright (c) 1996,1997 Russell King.
+ * Copyright (c) 1996-2000 Russell King.
*
* Scan ADFS partitions on hard disk drives.
*/
-
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/kdev_t.h>
@@ -30,17 +29,9 @@ adfspart_setgeometry(kdev_t dev, unsigned int secspertrack, unsigned int heads,
#endif
}
-#define LINUX_NATIVE_MAGIC 0xdeafa1de
-#define LINUX_SWAP_MAGIC 0xdeafab1e
-
-struct linux_part {
- unsigned long magic;
- unsigned long start_sect;
- unsigned long nr_sects;
-};
-
-static struct adfs_discrecord *adfs_partition(struct gendisk *hd, char *name, char *data,
- unsigned long first_sector, unsigned int minor)
+static struct adfs_discrecord *
+adfs_partition(struct gendisk *hd, char *name, char *data,
+ unsigned long first_sector, int minor)
{
struct adfs_discrecord *dr;
unsigned int nr_sects;
@@ -63,8 +54,9 @@ static struct adfs_discrecord *adfs_partition(struct gendisk *hd, char *name, ch
}
#ifdef CONFIG_ACORN_PARTITION_RISCIX
-static int riscix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sect,
- unsigned int minor, unsigned long nr_sects)
+static int
+riscix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sect,
+ int minor, unsigned long nr_sects)
{
struct buffer_head *bh;
struct riscix_record *rr;
@@ -76,7 +68,8 @@ static int riscix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_
printk(" [RISCiX]");
add_gd_partition(hd, riscix_minor = minor++, first_sect, nr_sects);
- hd->sizes[riscix_minor] = hd->part[riscix_minor].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ hd->sizes[riscix_minor] = hd->part[riscix_minor].nr_sects >>
+ (BLOCK_SIZE_BITS - 9);
dev = MKDEV(hd->major, riscix_minor);
if (!(bh = bread(dev, 0, 1024)))
@@ -109,8 +102,9 @@ static int riscix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_
}
#endif
-static int linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sect,
- unsigned int minor, unsigned long nr_sects)
+static int
+linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sect,
+ int minor, unsigned long nr_sects)
{
struct buffer_head *bh;
struct linux_part *linuxp;
@@ -122,7 +116,8 @@ static int linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_s
printk(" [Linux]");
add_gd_partition(hd, linux_minor = minor++, first_sect, nr_sects);
- hd->sizes[linux_minor] = hd->part[linux_minor].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ hd->sizes[linux_minor] = hd->part[linux_minor].nr_sects >>
+ (BLOCK_SIZE_BITS - 9);
dev = MKDEV(hd->major, linux_minor);
if (!(bh = bread(dev, 0, 1024)))
@@ -151,8 +146,9 @@ static int linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_s
}
#ifdef CONFIG_ACORN_PARTITION_CUMANA
-static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long first_sector,
- unsigned int minor)
+static int
+adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev,
+ unsigned long first_sector, int minor)
{
unsigned int start_blk = 0, mask = (1 << hd->minor_shift) - 1;
struct buffer_head *bh = NULL;
@@ -163,16 +159,17 @@ static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long f
return 0;
/*
- * Try Cumana style partitions - sector 3 contains ADFS boot block with pointer
- * to next 'drive'.
+ * Try Cumana style partitions - sector 3 contains ADFS boot block
+ * with pointer to next 'drive'.
*
- * There are unknowns in this code - is the 'cylinder number' of the next
- * partition relative to the start of this one - I'm assuming it is.
+ * There are unknowns in this code - is the 'cylinder number' of the
+ * next partition relative to the start of this one - I'm assuming
+ * it is.
*
* Also, which ID did Cumana use?
*
- * This is totally unfinished, and will require more work to get it going.
- * Hence it is totally untested.
+ * This is totally unfinished, and will require more work to get it
+ * going. Hence it is totally untested.
*/
do {
struct adfs_discrecord *dr;
@@ -184,7 +181,8 @@ static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long f
if (!(bh = bread(dev, start_blk + 3, 1024)))
return -1;
- dr = adfs_partition(hd, name, bh->b_data, first_sector, minor++);
+ dr = adfs_partition(hd, name, bh->b_data,
+ first_sector, minor++);
if (!dr)
break;
name = NULL;
@@ -206,13 +204,16 @@ static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long f
break;
#ifdef CONFIG_ACORN_PARTITION_RISCIX
- case PARTITION_RISCIX_SCSI: /* RiscIX - we don't know how to find the next one. */
- minor = riscix_partition(hd, dev, first_sector, minor, nr_sects);
+ case PARTITION_RISCIX_SCSI:
+ /* RISCiX - we don't know how to find the next one. */
+ minor = riscix_partition(hd, dev, first_sector,
+ minor, nr_sects);
break;
#endif
case PARTITION_LINUX:
- minor = linux_partition(hd, dev, first_sector, minor, nr_sects);
+ minor = linux_partition(hd, dev, first_sector,
+ minor, nr_sects);
break;
}
brelse(bh);
@@ -221,7 +222,7 @@ static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long f
return minor;
} while (1);
if (bh)
- brelse(bh);
+ bforget(bh);
return first ? 0 : 1;
}
#endif
@@ -241,8 +242,9 @@ static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long f
* hda1 = ADFS partition on first drive.
* hda2 = non-ADFS partition.
*/
-static int adfspart_check_ADFS(struct gendisk *hd, kdev_t dev, unsigned long first_sector,
- unsigned int minor)
+static int
+adfspart_check_ADFS(struct gendisk *hd, kdev_t dev,
+ unsigned long first_sector, int minor)
{
unsigned long start_sect, nr_sects, sectscyl, heads;
struct buffer_head *bh;
@@ -256,12 +258,13 @@ static int adfspart_check_ADFS(struct gendisk *hd, kdev_t dev, unsigned long fir
dr = adfs_partition(hd, "ADFS", bh->b_data, first_sector, minor++);
if (!dr) {
- brelse(bh);
+ bforget(bh);
return 0;
}
heads = dr->heads + ((dr->lowsector >> 6) & 1);
- adfspart_setgeometry(dev, dr->secspertrack, heads, hd->part[MINOR(dev)].nr_sects);
+ adfspart_setgeometry(dev, dr->secspertrack, heads,
+ hd->part[MINOR(dev)].nr_sects);
sectscyl = dr->secspertrack * heads;
/*
@@ -272,21 +275,19 @@ static int adfspart_check_ADFS(struct gendisk *hd, kdev_t dev, unsigned long fir
if (start_sect) {
first_sector += start_sect;
- /*
- * we now have a problem - how to set the origional disk size if the
- * disk doesn't report it, since there is no standard way of getting
- * that info.
- */
+
switch (bh->b_data[0x1fc] & 15) {
#ifdef CONFIG_ACORN_PARTITION_RISCIX
case PARTITION_RISCIX_SCSI:
case PARTITION_RISCIX_MFM:
- minor = riscix_partition(hd, dev, first_sector, minor, nr_sects);
+ minor = riscix_partition(hd, dev, first_sector,
+ minor, nr_sects);
break;
#endif
case PARTITION_LINUX:
- minor = linux_partition(hd, dev, first_sector, minor, nr_sects);
+ minor = linux_partition(hd, dev, first_sector,
+ minor, nr_sects);
break;
}
}
@@ -326,13 +327,14 @@ static int adfspart_check_ICSLinux(kdev_t dev, unsigned long block)
* hda2 = ADFS partition 1 on first drive.
* ..etc..
*/
-static int adfspart_check_ICS(struct gendisk *hd, kdev_t dev, unsigned long first_sector,
- unsigned int minor)
+static int
+adfspart_check_ICS(struct gendisk *hd, kdev_t dev,
+ unsigned long first_sector, int minor)
{
struct buffer_head *bh;
unsigned long sum;
unsigned int i, mask = (1 << hd->minor_shift) - 1;
- struct ics_part { unsigned long start; signed long size; } *p;
+ struct ics_part *p;
if(get_ptable_blocksize(dev)!=1024)
return 0;
@@ -349,9 +351,9 @@ static int adfspart_check_ICS(struct gendisk *hd, kdev_t dev, unsigned long firs
for (i = 0, sum = 0x50617274; i < 508; i++)
sum += bh->b_data[i];
- sum -= le32_to_cpu(*(unsigned long *)(&bh->b_data[508]));
+ sum -= le32_to_cpu(*(__u32 *)(&bh->b_data[508]));
if (sum) {
- brelse(bh);
+ bforget(bh);
return 0; /* not ICS partition table */
}
@@ -392,15 +394,6 @@ static int adfspart_check_ICS(struct gendisk *hd, kdev_t dev, unsigned long firs
#endif
#ifdef CONFIG_ACORN_PARTITION_POWERTEC
-struct ptec_partition {
- u32 unused1;
- u32 unused2;
- u32 start;
- u32 size;
- u32 unused5;
- char type[8];
-};
-
/*
* Purpose: allocate ICS partitions.
* Params : hd - pointer to gendisk structure to store partition info.
@@ -413,8 +406,9 @@ struct ptec_partition {
* hda2 = ADFS partition 1 on first drive.
* ..etc..
*/
-static int adfspart_check_POWERTEC(struct gendisk *hd, kdev_t dev, unsigned long first_sector,
- unsigned int minor)
+static int
+adfspart_check_POWERTEC(struct gendisk *hd, kdev_t dev,
+ unsigned long first_sector, int minor)
{
struct buffer_head *bh;
struct ptec_partition *p;
@@ -428,7 +422,7 @@ static int adfspart_check_POWERTEC(struct gendisk *hd, kdev_t dev, unsigned long
checksum += bh->b_data[i];
if (checksum != bh->b_data[511]) {
- brelse(bh);
+ bforget(bh);
return 0;
}
@@ -452,44 +446,44 @@ static int adfspart_check_POWERTEC(struct gendisk *hd, kdev_t dev, unsigned long
}
#endif
+static int (*partfn[])(struct gendisk *, kdev_t, unsigned long, int) = {
+#ifdef CONFIG_ACORN_PARTITION_ICS
+ adfspart_check_ICS,
+#endif
+#ifdef CONFIG_ACORN_PARTITION_CUMANA
+ adfspart_check_CUMANA,
+#endif
+#ifdef CONFIG_ACORN_PARTITION_ADFS
+ adfspart_check_ADFS,
+#endif
+#ifdef CONFIG_ACORN_PARTITION_POWERTEC
+ adfspart_check_POWERTEC,
+#endif
+ NULL
+};
/*
* Purpose: initialise all the partitions on an ADFS drive.
- * These may be other ADFS partitions or a Linux/RiscBSD/RiscIX
+ * These may be other ADFS partitions or a Linux/RiscBSD/RISCiX
* partition.
*
- * Params : hd - pointer to gendisk structure to store devices partitions.
- * dev - device number to access
- * first_sector - first available sector on the disk.
- * minor - first available minor on this device.
+ * Params : hd - pointer to gendisk structure
+ * dev - device number to access
+ * first_sect - first available sector on the disk.
+ * first_minor - first available minor on this device.
*
* Returns: -1 on error, 0 if not ADFS format, 1 if ok.
*/
int acorn_partition(struct gendisk *hd, kdev_t dev,
- unsigned long first_sector, int first_part_minor)
+ unsigned long first_sect, int first_minor)
{
- int r = 0;
+ int r = 0, i;
-#ifdef CONFIG_ACORN_PARTITION_ICS
- if (r == 0)
- r = adfspart_check_ICS(hd, dev, first_sector, first_part_minor);
-#endif
-#ifdef CONFIG_ACORN_PARTITION_CUMANA
- if (r == 0)
- r = adfspart_check_CUMANA(hd, dev, first_sector, first_part_minor);
-#endif
-#ifdef CONFIG_ACORN_PARTITION_ADFS
- if (r == 0)
- r = adfspart_check_ADFS(hd, dev, first_sector, first_part_minor);
-#endif
-#ifdef CONFIG_ACORN_PARTITION_POWERTEC
- if (r == 0)
- r = adfspart_check_POWERTEC(hd, dev, first_sector, first_part_minor);
-#endif
- if (r < 0) {
- if (warn_no_part)
- printk(" unable to read boot sectors / partition sectors\n");
- } else if (r) {
+ for (i = 0; partfn[i] && r == 0; i++)
+ r = partfn[i](hd, dev, first_sect, first_minor);
+
+ if (r < 0 && warn_no_part)
+ printk(" unable to read boot sectors / partition sectors\n");
+ if (r > 0)
printk("\n");
- }
return r;
}
diff --git a/fs/partitions/acorn.h b/fs/partitions/acorn.h
index 748c7c83f..e1d0a3830 100644
--- a/fs/partitions/acorn.h
+++ b/fs/partitions/acorn.h
@@ -13,20 +13,43 @@
#define PARTITION_LINUX 9
struct riscix_part {
- unsigned long start;
- unsigned long length;
- unsigned long one;
+ __u32 start;
+ __u32 length;
+ __u32 one;
char name[16];
};
struct riscix_record {
- unsigned long magic;
+ __u32 magic;
#define RISCIX_MAGIC (0x4a657320)
- unsigned long date;
+ __u32 date;
struct riscix_part part[8];
};
-int
-acorn_partition(struct gendisk *hd, kdev_t dev,
- unsigned long first_sector, int first_part_minor);
+#define LINUX_NATIVE_MAGIC 0xdeafa1de
+#define LINUX_SWAP_MAGIC 0xdeafab1e
+
+struct linux_part {
+ __u32 magic;
+ __u32 start_sect;
+ __u32 nr_sects;
+};
+
+struct ics_part {
+ __u32 start;
+ __s32 size;
+};
+
+struct ptec_partition {
+ __u32 unused1;
+ __u32 unused2;
+ __u32 start;
+ __u32 size;
+ __u32 unused5;
+ char type[8];
+};
+
+
+int acorn_partition(struct gendisk *hd, kdev_t dev,
+ unsigned long first_sect, int first_minor);