summaryrefslogtreecommitdiffstats
path: root/fs/jffs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-10-05 01:18:40 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-10-05 01:18:40 +0000
commit012bb3e61e5eced6c610f9e036372bf0c8def2d1 (patch)
tree87efc733f9b164e8c85c0336f92c8fb7eff6d183 /fs/jffs
parent625a1589d3d6464b5d90b8a0918789e3afffd220 (diff)
Merge with Linux 2.4.0-test9. Please check DECstation, I had a number
of rejects to fixup while integrating Linus patches. I also found that this kernel will only boot SMP on Origin; the UP kernel freeze soon after bootup with SCSI timeout messages. I commit this anyway since I found that the last CVS versions had the same problem.
Diffstat (limited to 'fs/jffs')
-rw-r--r--fs/jffs/inode-v23.c514
-rw-r--r--fs/jffs/intrep.c503
-rw-r--r--fs/jffs/intrep.h16
-rw-r--r--fs/jffs/jffs_fm.c28
-rw-r--r--fs/jffs/jffs_fm.h8
5 files changed, 660 insertions, 409 deletions
diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c
index 266e7bf64..3ac27df92 100644
--- a/fs/jffs/inode-v23.c
+++ b/fs/jffs/inode-v23.c
@@ -10,7 +10,7 @@
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
- * $Id: inode-v23.c,v 1.34 2000/08/10 08:58:00 dwmw2 Exp $
+ * $Id: inode-v23.c,v 1.43 2000/08/22 08:00:22 dwmw2 Exp $
*
*
* Ported to Linux 2.3.x and MTD:
@@ -151,10 +151,9 @@ jffs_put_super(struct super_block *sb)
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);
+ D1(printk (KERN_NOTICE "jffs_put_super(): Telling gc thread to die.\n"));
+ send_sig(SIGKILL, c->gc_task, 1);
}
down (&c->gc_thread_sem);
@@ -179,54 +178,56 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr)
struct jffs_fmcontrol *fmc;
struct jffs_file *f;
struct jffs_node *new_node;
- char *name = 0;
int update_all;
int res;
+ int recoverable = 0;
+
+ if ((res = inode_change_ok(inode, iattr)))
+ return res;
+
+ c = (struct jffs_control *)inode->i_sb->u.generic_sbp;
+ fmc = c->fmc;
+
+ D3(printk (KERN_NOTICE "notify_change(): down biglock\n"));
+ down(&fmc->biglock);
+
+ f = jffs_find_file(c, inode->i_ino);
- f = (struct jffs_file *)inode->u.generic_ip;
ASSERT(if (!f) {
printk("jffs_setattr(): Invalid inode number: %lu\n",
inode->i_ino);
- return -1;
+ D3(printk (KERN_NOTICE "notify_change(): up biglock\n"));
+ up(&fmc->biglock);
+ return -EINVAL;
});
D1(printk("***jffs_setattr(): file: \"%s\", ino: %u\n",
f->name, f->ino));
- c = f->c;
- fmc = c->fmc;
update_all = iattr->ia_valid & ATTR_FORCE;
- 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
- shrink the size of a file as an exception.
- Accept it. */
- /* TODO: Might just shrink it a bit?
- check f->size - ia_size */
- } else {
- D1(printk("jffs_setattr(): Free size = %u\n",
- jffs_free_size1(fmc)
- + jffs_free_size2(fmc)));
- D(printk(KERN_NOTICE "JFFS: No space left on "
- "device\n"));
- return -ENOSPC;
- }
- }
+ if ( (update_all || iattr->ia_valid & ATTR_SIZE)
+ && (iattr->ia_size + 128 < f->size) ) {
+ /* We're shrinking the file by more than 128 bytes.
+ We'll be able to GC and recover this space, so
+ allow it to go into the reserved space. */
+ recoverable = 1;
+ }
if (!(new_node = (struct jffs_node *)
kmalloc(sizeof(struct jffs_node), GFP_KERNEL))) {
D(printk("jffs_setattr(): Allocation failed!\n"));
+ D3(printk (KERN_NOTICE "notify_change(): up biglock\n"));
+ up(&fmc->biglock);
return -ENOMEM;
}
+
DJM(no_jffs_node++);
new_node->data_offset = 0;
new_node->removed_size = 0;
raw_inode.magic = JFFS_MAGIC_BITMASK;
raw_inode.ino = f->ino;
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;
@@ -237,7 +238,7 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr)
raw_inode.offset = 0;
raw_inode.rsize = 0;
raw_inode.dsize = 0;
- raw_inode.nsize = 0;
+ raw_inode.nsize = f->nsize;
raw_inode.nlink = f->nlink;
raw_inode.spare = 0;
raw_inode.rename = 0;
@@ -278,12 +279,8 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr)
new_node->data_offset = iattr->ia_size;
new_node->removed_size = len;
inode->i_size = iattr->ia_size;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
- /* If we truncate a file we want to add the name. If we
- always do that, we could perhaps free more space on
- the flash (and besides it doesn't hurt). */
- name = f->name;
- raw_inode.nsize = f->nsize;
if (len) {
invalidate_inode_pages(inode);
}
@@ -304,17 +301,20 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr)
}
/* Write this node to the flash. */
- if ((res = jffs_write_node(c, new_node, &raw_inode, name, 0)) < 0) {
+ if ((res = jffs_write_node(c, new_node, &raw_inode, f->name, 0, recoverable, f)) < 0) {
D(printk("jffs_notify_change(): The write failed!\n"));
kfree(new_node);
DJM(no_jffs_node--);
+ D3(printk (KERN_NOTICE "n_c(): up biglock\n"));
+ up(&c->fmc->biglock);
return res;
}
jffs_insert_node(c, f, &raw_inode, 0, new_node);
mark_inode_dirty(inode);
-
+ D3(printk (KERN_NOTICE "n_c(): up biglock\n"));
+ up(&c->fmc->biglock);
return 0;
} /* jffs_notify_change() */
@@ -403,7 +403,7 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry,
__u32 rename_data = 0;
D2(printk("***jffs_rename()\n"));
-
+
D(printk("jffs_rename(): old_dir: 0x%p, old name: 0x%p, "
"new_dir: 0x%p, new name: 0x%p\n",
old_dir, old_dentry->d_name.name,
@@ -413,17 +413,9 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry,
ASSERT(if (!c) {
printk(KERN_ERR "jffs_rename(): The old_dir inode "
"didn't have a reference to a jffs_file struct\n");
- return -1;
+ return -EIO;
});
- 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 old directory. */
result = -ENOTDIR;
if (!(old_dir_f = (struct jffs_file *)old_dir->u.generic_ip)) {
D(printk("jffs_rename(): Old dir invalid.\n"));
@@ -443,7 +435,8 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry,
D(printk("jffs_rename(): New dir invalid.\n"));
goto jffs_rename_end;
}
-
+ D3(printk (KERN_NOTICE "rename(): down biglock\n"));
+ down(&c->fmc->biglock);
/* Create a node and initialize as much as needed. */
result = -ENOMEM;
if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node),
@@ -459,7 +452,7 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry,
raw_inode.magic = JFFS_MAGIC_BITMASK;
raw_inode.ino = f->ino;
raw_inode.pino = new_dir_f->ino;
- raw_inode.version = f->highest_version + 1;
+/* raw_inode.version = f->highest_version + 1; */
raw_inode.mode = f->mode;
raw_inode.uid = current->fsuid;
raw_inode.gid = current->fsgid;
@@ -491,7 +484,7 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry,
/* Write the new node to the flash memory. */
if ((result = jffs_write_node(c, node, &raw_inode,
new_dentry->d_name.name,
- (unsigned char*)&rename_data)) < 0) {
+ (unsigned char*)&rename_data, 0, f)) < 0) {
D(printk("jffs_rename(): Failed to write node to flash.\n"));
kfree(node);
DJM(no_jffs_node--);
@@ -501,14 +494,14 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (raw_inode.rename) {
/* The file with the same name must be deleted. */
- down(&c->fmc->gclock);
+ //FIXME deadlock 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");
}
- up(&c->fmc->gclock);
+ // up(&c->fmc->gclock);
}
if (old_dir_f != new_dir_f) {
@@ -539,6 +532,8 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry,
}
jffs_rename_end:
+ D3(printk (KERN_NOTICE "rename(): up biglock\n"));
+ up(&c->fmc->biglock);
return result;
} /* jffs_rename() */
@@ -551,13 +546,18 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir)
struct jffs_file *f;
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
+ struct jffs_control *c = (struct jffs_control *)inode->i_sb->u.generic_sbp;
int j;
int ddino;
+ D3(printk (KERN_NOTICE "readdir(): down biglock\n"));
+ down(&c->fmc->biglock);
D2(printk("jffs_readdir(): inode: 0x%p, filp: 0x%p\n", inode, filp));
if (filp->f_pos == 0) {
D3(printk("jffs_readdir(): \".\" %lu\n", inode->i_ino));
if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) {
+ D3(printk (KERN_NOTICE "readdir(): up biglock\n"));
+ up(&c->fmc->biglock);
return 0;
}
filp->f_pos = 1;
@@ -571,8 +571,11 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir)
inode->u.generic_ip)->pino;
}
D3(printk("jffs_readdir(): \"..\" %u\n", ddino));
- if (filldir(dirent, "..", 2, filp->f_pos, ddino, DT_DIR) < 0)
+ if (filldir(dirent, "..", 2, filp->f_pos, ddino, DT_DIR) < 0) {
+ D3(printk (KERN_NOTICE "readdir(): up biglock\n"));
+ up(&c->fmc->biglock);
return 0;
+ }
filp->f_pos++;
}
f = ((struct jffs_file *)inode->u.generic_ip)->children;
@@ -583,11 +586,15 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir)
D3(printk("jffs_readdir(): \"%s\" ino: %u\n",
(f->name ? f->name : ""), f->ino));
if (filldir(dirent, f->name, f->nsize,
- filp->f_pos , f->ino, DT_UNKNOWN) < 0)
+ filp->f_pos , f->ino, DT_UNKNOWN) < 0) {
+ D3(printk (KERN_NOTICE "readdir(): up biglock\n"));
+ up(&c->fmc->biglock);
return 0;
+ }
filp->f_pos++;
}
-
+ D3(printk (KERN_NOTICE "readdir(): up biglock\n"));
+ up(&c->fmc->biglock);
return filp->f_pos;
} /* jffs_readdir() */
@@ -599,6 +606,7 @@ jffs_lookup(struct inode *dir, struct dentry *dentry)
{
struct jffs_file *d;
struct jffs_file *f;
+ struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp;
int len;
int r = 0;
const char *name;
@@ -615,6 +623,9 @@ jffs_lookup(struct inode *dir, struct dentry *dentry)
kfree(s);
});
+ D3(printk (KERN_NOTICE "lookup(): down biglock\n"));
+ down(&c->fmc->biglock);
+
r = -ENAMETOOLONG;
if (len > JFFS_MAX_NAME_LEN) {
goto jffs_lookup_end;
@@ -628,21 +639,39 @@ jffs_lookup(struct inode *dir, struct dentry *dentry)
}
/* Get the corresponding inode to the file. */
+
+ /* iget calls jffs_read_inode, so we need to drop the biglock
+ before calling iget. Unfortunately, the GC has a tendency
+ to sneak in here, because iget sometimes calls schedule ().
+ */
+
if ((len == 1) && (name[0] == '.')) {
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
if (!(inode = iget(dir->i_sb, d->ino))) {
D(printk("jffs_lookup(): . iget() ==> NULL\n"));
- goto jffs_lookup_end;
+ goto jffs_lookup_end_no_biglock;
}
+ D3(printk (KERN_NOTICE "lookup(): down biglock\n"));
+ down(&c->fmc->biglock);
} else if ((len == 2) && (name[0] == '.') && (name[1] == '.')) {
- if (!(inode = iget(dir->i_sb, d->pino))) {
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
+ if (!(inode = iget(dir->i_sb, d->pino))) {
D(printk("jffs_lookup(): .. iget() ==> NULL\n"));
- goto jffs_lookup_end;
+ goto jffs_lookup_end_no_biglock;
}
+ D3(printk (KERN_NOTICE "lookup(): down biglock\n"));
+ down(&c->fmc->biglock);
} else if ((f = jffs_find_child(d, name, len))) {
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
if (!(inode = iget(dir->i_sb, f->ino))) {
D(printk("jffs_lookup(): iget() ==> NULL\n"));
- goto jffs_lookup_end;
+ goto jffs_lookup_end_no_biglock;
}
+ D3(printk (KERN_NOTICE "lookup(): down biglock\n"));
+ down(&c->fmc->biglock);
} else {
D3(printk("jffs_lookup(): Couldn't find the file. "
"f = 0x%p, name = \"%s\", d = 0x%p, d->ino = %u\n",
@@ -651,9 +680,15 @@ jffs_lookup(struct inode *dir, struct dentry *dentry)
}
d_add(dentry, inode);
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
return NULL;
jffs_lookup_end:
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
+
+jffs_lookup_end_no_biglock:
return ERR_PTR(r);
} /* jffs_lookup() */
@@ -667,6 +702,7 @@ jffs_readpage(struct file *file, struct page *page)
int result = -EIO;
struct inode *inode = (struct inode*)page->mapping->host;
struct jffs_file *f = (struct jffs_file *)inode->u.generic_ip;
+ struct jffs_control *c = (struct jffs_control *)inode->i_sb->u.generic_sbp;
int r;
loff_t offset;
@@ -679,6 +715,9 @@ jffs_readpage(struct file *file, struct page *page)
ClearPageUptodate(page);
ClearPageError(page);
+ D3(printk (KERN_NOTICE "readpage(): down biglock\n"));
+ down(&c->fmc->biglock);
+
offset = page->index << PAGE_CACHE_SHIFT;
if (offset < inode->i_size) {
read_len = jffs_min(inode->i_size - offset, PAGE_SIZE);
@@ -697,6 +736,10 @@ jffs_readpage(struct file *file, struct page *page)
"read %d bytes.\n", read_len, r);
});
}
+
+ D3(printk (KERN_NOTICE "readpage(): up biglock\n"));
+ up(&c->fmc->biglock);
+
if (result) {
memset(buf, 0, PAGE_SIZE);
SetPageError(page);
@@ -737,22 +780,16 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
});
dir_f = (struct jffs_file *)dir->u.generic_ip;
+
ASSERT(if (!dir_f) {
printk(KERN_ERR "jffs_mkdir(): No reference to a "
"jffs_file struct in inode.\n");
- result = -1;
- goto jffs_mkdir_end;
+ return -EIO;
});
c = dir_f->c;
-
- 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"));
- result = -ENOSPC;
- goto jffs_mkdir_end;
- }
+ D3(printk (KERN_NOTICE "mkdir(): down biglock\n"));
+ down(&c->fmc->biglock);
dir_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX)
& ~current->fs->umask);
@@ -794,7 +831,7 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
/* Write the new node to the flash. */
if ((result = jffs_write_node(c, node, &raw_inode,
- dentry->d_name.name, 0)) < 0) {
+ dentry->d_name.name, 0, 0, NULL)) < 0) {
D(printk("jffs_mkdir(): jffs_write_node() failed.\n"));
kfree(node);
DJM(no_jffs_node--);
@@ -821,6 +858,8 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
result = 0;
jffs_mkdir_end:
+ D3(printk (KERN_NOTICE "mkdir(): up biglock\n"));
+ up(&c->fmc->biglock);
return result;
} /* jffs_mkdir() */
@@ -829,8 +868,15 @@ jffs_mkdir_end:
static int
jffs_rmdir(struct inode *dir, struct dentry *dentry)
{
+ struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp;
+ int ret;
D3(printk("***jffs_rmdir()\n"));
- return jffs_remove(dir, dentry, S_IFDIR);
+ D3(printk (KERN_NOTICE "rmdir(): down biglock\n"));
+ down(&c->fmc->biglock);
+ ret = jffs_remove(dir, dentry, S_IFDIR);
+ D3(printk (KERN_NOTICE "rmdir(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return ret;
}
@@ -838,8 +884,16 @@ jffs_rmdir(struct inode *dir, struct dentry *dentry)
static int
jffs_unlink(struct inode *dir, struct dentry *dentry)
{
+ struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp;
+ int ret;
+
D3(printk("***jffs_unlink()\n"));
- return jffs_remove(dir, dentry, 0);
+ D3(printk (KERN_NOTICE "unlink(): down biglock\n"));
+ down(&c->fmc->biglock);
+ ret = jffs_remove(dir, dentry, 0);
+ D3(printk (KERN_NOTICE "unlink(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return ret;
}
@@ -862,7 +916,7 @@ jffs_remove(struct inode *dir, struct dentry *dentry, int type)
char *_name = (char *) kmalloc(len + 1, GFP_KERNEL);
memcpy(_name, name, len);
_name[len] = '\0';
- printk("***jffs_remove(): file = \"%s\"\n", _name);
+ printk("***jffs_remove(): file = \"%s\", ino = %ld\n", _name, dentry->d_inode->i_ino);
kfree(_name);
});
@@ -916,7 +970,7 @@ jffs_remove(struct inode *dir, struct dentry *dentry, int type)
raw_inode.magic = JFFS_MAGIC_BITMASK;
raw_inode.ino = del_f->ino;
raw_inode.pino = del_f->pino;
- raw_inode.version = del_f->highest_version + 1;
+/* raw_inode.version = del_f->highest_version + 1; */
raw_inode.mode = del_f->mode;
raw_inode.uid = current->fsuid;
raw_inode.gid = current->fsgid;
@@ -933,7 +987,7 @@ jffs_remove(struct inode *dir, struct dentry *dentry, int type)
raw_inode.deleted = 1;
/* Write the new node to the flash memory. */
- if (jffs_write_node(c, del_node, &raw_inode, 0, 0) < 0) {
+ if (jffs_write_node(c, del_node, &raw_inode, 0, 0, 1, del_f) < 0) {
kfree(del_node);
DJM(no_jffs_node--);
result = -EIO;
@@ -979,13 +1033,8 @@ 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)) {
- 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"));
- result = -ENOSPC;
- goto jffs_mknod_end;
- }
+ D3(printk (KERN_NOTICE "mknod(): down biglock\n"));
+ down(&c->fmc->biglock);
/* Create and initialize a new node. */
if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node),
@@ -1021,7 +1070,7 @@ jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
/* Write the new node to the flash. */
if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name,
- (unsigned char *)&dev)) < 0) {
+ (unsigned char *)&dev, 0, NULL)) < 0) {
D(printk("jffs_mknod(): jffs_write_node() failed.\n"));
result = err;
goto jffs_mknod_err;
@@ -1053,6 +1102,8 @@ jffs_mknod_err:
}
jffs_mknod_end:
+ D3(printk (KERN_NOTICE "mknod(): up biglock\n"));
+ up(&c->fmc->biglock);
return result;
} /* jffs_mknod() */
@@ -1088,24 +1139,20 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
ASSERT(if (!dir_f) {
printk(KERN_ERR "jffs_symlink(): No reference to a "
"jffs_file struct in inode.\n");
- return -1;
+ return -EIO;
});
c = dir_f->c;
- 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"));
- return -ENOSPC;
- }
-
/* 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"));
return -ENOMEM;
}
+ D3(printk (KERN_NOTICE "symlink(): down biglock\n"));
+ down(&c->fmc->biglock);
+
DJM(no_jffs_node++);
node->data_offset = 0;
node->removed_size = 0;
@@ -1132,30 +1179,32 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
/* Write the new node to the flash. */
if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name,
- (const unsigned char *)symname)) < 0) {
+ (const unsigned char *)symname, 0, NULL)) < 0) {
D(printk("jffs_symlink(): jffs_write_node() failed.\n"));
kfree(node);
DJM(no_jffs_node--);
- return err;
+ goto jffs_symlink_end;
}
/* Insert the new node into the file system. */
if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name,
node)) < 0) {
- return err;
+ goto jffs_symlink_end;
}
inode = jffs_new_inode(dir, &raw_inode, &err);
if (inode == NULL) {
- return err;
+ goto jffs_symlink_end;
}
-
+ err = 0;
inode->i_op = &page_symlink_inode_operations;
inode->i_mapping->a_ops = &jffs_address_operations;
d_instantiate(dentry, inode);
-
- return 0;
+ jffs_symlink_end:
+ D3(printk (KERN_NOTICE "symlink(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return err;
} /* jffs_symlink() */
@@ -1191,24 +1240,20 @@ jffs_create(struct inode *dir, struct dentry *dentry, int mode)
ASSERT(if (!dir_f) {
printk(KERN_ERR "jffs_create(): No reference to a "
"jffs_file struct in inode.\n");
- return -1;
+ return -EIO;
});
c = dir_f->c;
- 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"));
- return -ENOSPC;
- }
-
/* Create a node and initialize as much as needed. */
if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node),
GFP_KERNEL))) {
D(printk("jffs_create(): Allocation failed: node == 0\n"));
return -ENOMEM;
}
+ D3(printk (KERN_NOTICE "create(): down biglock\n"));
+ down(&c->fmc->biglock);
+
DJM(no_jffs_node++);
node->data_offset = 0;
node->removed_size = 0;
@@ -1235,33 +1280,35 @@ jffs_create(struct inode *dir, struct dentry *dentry, int mode)
/* Write the new node to the flash. */
if ((err = jffs_write_node(c, node, &raw_inode,
- dentry->d_name.name, 0)) < 0) {
+ dentry->d_name.name, 0, 0, NULL)) < 0) {
D(printk("jffs_create(): jffs_write_node() failed.\n"));
kfree(node);
DJM(no_jffs_node--);
- return err;
+ goto jffs_create_end;
}
/* Insert the new node into the file system. */
if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name,
node)) < 0) {
- return err;
+ goto jffs_create_end;
}
/* Initialize an inode. */
inode = jffs_new_inode(dir, &raw_inode, &err);
if (inode == NULL) {
- return err;
+ goto jffs_create_end;
}
-
+ err = 0;
inode->i_op = &jffs_file_inode_operations;
inode->i_fop = &jffs_file_operations;
inode->i_mapping->a_ops = &jffs_address_operations;
inode->i_mapping->nrpages = 0;
d_instantiate(dentry, inode);
-
- return 0;
+ jffs_create_end:
+ D3(printk (KERN_NOTICE "create(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return err;
} /* jffs_create() */
@@ -1277,7 +1324,9 @@ jffs_file_write(struct file *filp, const char *buf, size_t count,
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
unsigned char *vbuf;
- int written = 0;
+ int recoverable = 0;
+ size_t written = 0;
+ __u32 thiscount = count;
loff_t pos;
int err;
@@ -1287,147 +1336,176 @@ jffs_file_write(struct file *filp, const char *buf, size_t count,
"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)
- goto out;
-
err = filp->f_error;
if (err) {
filp->f_error = 0;
- goto out;
+ return err;
}
+ down(&inode->i_sem);
+
if (inode->i_sb->s_flags & MS_RDONLY) {
D(printk("jffs_file_write(): MS_RDONLY\n"));
- err = -ENOSPC;
- goto out;
+ err = -EROFS;
+ goto out_isem;
}
+ err = -EINVAL;
+
if (!S_ISREG(inode->i_mode)) {
D(printk("jffs_file_write(): inode->i_mode == 0x%08x\n",
inode->i_mode));
- err = -EINVAL;
- goto out;
+ goto out_isem;
}
if (!(f = (struct jffs_file *)inode->u.generic_ip)) {
D(printk("jffs_file_write(): inode->u.generic_ip = 0x%p\n",
inode->u.generic_ip));
- err = -EINVAL;
- goto out;
+ goto out_isem;
}
c = f->c;
- 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"));
- err = -ENOSPC;
- goto out;
- }
-
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
+ else
+ pos = *ppos;
+
+ if (pos < 0) {
+ goto out_isem;
+ }
+
+ thiscount = jffs_min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count);
-
- if (!(vbuf = kmalloc(count, GFP_KERNEL))) {
+ if (!(vbuf = kmalloc(thiscount, GFP_KERNEL))) {
D(printk("jffs_file_write(): failed to allocate bounce buffer. Fix me to use page cache\n"));
err = -ENOMEM;
- goto out;
+ goto out_isem;
}
- /* 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 = pos;
- node->removed_size = 0;
+ D3(printk (KERN_NOTICE "file_write(): down biglock\n"));
+ down(&c->fmc->biglock);
- /* Initialize the raw inode. */
- raw_inode.magic = JFFS_MAGIC_BITMASK;
- raw_inode.ino = f->ino;
- raw_inode.pino = f->pino;
- raw_inode.version = f->highest_version + 1;
- raw_inode.mode = f->mode;
+ /* Urgh. POSIX says we can do short writes if we feel like it.
+ * In practice, we can't. Nothing will cope. So we loop until
+ * we're done.
+ *
+ * <_Anarchy_> posix and reality are not interconnected on this issue
+ */
+ while (count) {
+
+ /* 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, thiscount)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ /* 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;
+ goto out;
+ }
+ DJM(no_jffs_node++);
+
+ node->data_offset = pos;
+ node->removed_size = 0;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = f->ino;
+ raw_inode.pino = f->pino;
- raw_inode.uid = f->uid;
- raw_inode.gid = f->gid;
- /*
- raw_inode.uid = current->fsuid;
- raw_inode.gid = current->fsgid;
- */
- raw_inode.atime = CURRENT_TIME;
- raw_inode.mtime = raw_inode.atime;
- raw_inode.ctime = f->ctime;
- raw_inode.offset = pos;
- raw_inode.dsize = count;
- raw_inode.rsize = 0;
- raw_inode.nsize = 0;
- raw_inode.nlink = f->nlink;
- raw_inode.spare = 0;
- raw_inode.rename = 0;
- raw_inode.deleted = 0;
+ raw_inode.mode = f->mode;
+
+ raw_inode.uid = f->uid;
+ raw_inode.gid = f->gid;
+ raw_inode.atime = CURRENT_TIME;
+ raw_inode.mtime = raw_inode.atime;
+ raw_inode.ctime = f->ctime;
+ raw_inode.offset = pos;
+ raw_inode.dsize = thiscount;
+ raw_inode.rsize = 0;
+ raw_inode.nsize = f->nsize;
+ raw_inode.nlink = f->nlink;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 0;
+
+ if (pos < f->size) {
+ node->removed_size = raw_inode.rsize = jffs_min(thiscount, f->size - pos);
+
+ /* If this node is going entirely over the top of old data,
+ we can allow it to go into the reserved space, because
+ we can that GC can reclaim the space later.
+ */
+ if (pos + thiscount < f->size) {
+ /* If all the data we're overwriting are _real_,
+ not just holes, then:
+ recoverable = 1;
+ */
+ }
+ }
+
+ /* Write the new node to the flash. */
+ /* NOTE: We would be quite happy if jffs_write_node() wrote a
+ smaller node than we were expecting. There's no need for it
+ to waste the space at the end of the flash just because it's
+ a little smaller than what we asked for. But that's a whole
+ new can of worms which I'm not going to open this week. dwmw2.
+ */
+ if ((err = jffs_write_node(c, node, &raw_inode, f->name,
+ (const unsigned char *)vbuf,
+ recoverable, f)) < 0) {
+ D(printk("jffs_file_write(): jffs_write_node() failed.\n"));
+ kfree(node);
+ DJM(no_jffs_node--);
+ goto out;
+ }
- if (pos < f->size) {
- node->removed_size = raw_inode.rsize = jffs_min(count, f->size - pos);
- }
+ written += err;
+ buf += err;
+ count -= err;
+ pos += err;
- /* Write the new node to the flash. */
- if ((written = jffs_write_node(c, node, &raw_inode, 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;
- }
+ /* Insert the new node into the file system. */
+ if ((err = jffs_insert_node(c, f, &raw_inode, 0, node)) < 0) {
+ goto out;
+ }
- kfree(vbuf);
+ D3(printk("jffs_file_write(): new f_pos %ld.\n", (long)pos));
- /* Insert the new node into the file system. */
- if ((err = jffs_insert_node(c, f, &raw_inode, 0, node)) < 0) {
- goto out;
+ thiscount = jffs_min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count);
}
-
- pos += written;
+ out:
+ D3(printk (KERN_NOTICE "file_write(): up biglock\n"));
+ up(&c->fmc->biglock);
*ppos = pos;
-
- D3(printk("jffs_file_write(): new f_pos %ld.\n", (long)pos));
+ kfree(vbuf);
/* Fix things in the real inode. */
if (pos > inode->i_size) {
inode->i_size = pos;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
}
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
mark_inode_dirty(inode);
invalidate_inode_pages(inode);
- err = written;
-out:
+ out_isem:
up(&inode->i_sem);
- return err;
+
+ /* What if there was an error, _and_ we've written some data. */
+ if (written)
+ return written;
+ else
+ return err;
} /* jffs_file_write() */
@@ -1437,6 +1515,7 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct jffs_control *c;
+ int ret = 0;
D2(printk("***jffs_ioctl(): cmd = 0x%08x, arg = 0x%08lx\n",
cmd, arg));
@@ -1446,6 +1525,8 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
"(cmd = 0x%08x)\n", cmd);
return -EIO;
}
+ D3(printk (KERN_NOTICE "ioctl(): down biglock\n"));
+ down(&c->fmc->biglock);
switch (cmd) {
case JFFS_PRINT_HASH:
@@ -1464,7 +1545,8 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
sizeof(struct jffs_flash_status))) {
D(printk("jffs_ioctl(): Bad arg in "
"JFFS_GET_STATUS ioctl!\n"));
- return -EFAULT;
+ ret = -EFAULT;
+ break;
}
fst.size = fmc->flash_size;
fst.used = fmc->used_size;
@@ -1478,15 +1560,16 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
if (copy_to_user((struct jffs_flash_status *)arg,
&fst,
sizeof(struct jffs_flash_status))) {
- return -EFAULT;
+ ret = -EFAULT;
}
}
break;
default:
- return -ENOTTY;
+ ret = -ENOTTY;
}
-
- return 0;
+ D3(printk (KERN_NOTICE "ioctl(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return ret;
} /* jffs_ioctl() */
@@ -1555,9 +1638,13 @@ jffs_read_inode(struct inode *inode)
return;
}
c = (struct jffs_control *)inode->i_sb->u.generic_sbp;
+ D3(printk (KERN_NOTICE "read_inode(): down biglock\n"));
+ down(&c->fmc->biglock);
if (!(f = jffs_find_file(c, inode->i_ino))) {
D(printk("jffs_read_inode(): No such inode (%lu).\n",
inode->i_ino));
+ D3(printk (KERN_NOTICE "read_inode(): up biglock\n"));
+ up(&c->fmc->biglock);
return;
}
inode->u.generic_ip = (void *)f;
@@ -1592,6 +1679,8 @@ jffs_read_inode(struct inode *inode)
jffs_read_data(f, (char *)&rdev, 0, sizeof(kdev_t));
init_special_inode(inode, inode->i_mode, kdev_t_to_nr(rdev));
}
+ D3(printk (KERN_NOTICE "read_inode(): up biglock\n"));
+ up(&c->fmc->biglock);
}
@@ -1603,6 +1692,7 @@ jffs_delete_inode(struct inode *inode)
lock_kernel();
inode->i_size = 0;
+ inode->i_blocks = 0;
clear_inode(inode);
unlock_kernel();
}
diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c
index adb2e0c46..5cf82f468 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.39 2000/08/09 13:23:36 dwmw2 Exp $
+ * $Id: intrep.c,v 1.69 2000/08/24 09:35:47 dwmw2 Exp $
*
* Ported to Linux 2.3.x and MTD:
* Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
@@ -191,12 +191,13 @@ flash_safe_read(struct mtd_info *mtd, loff_t from,
u_char *buf, size_t count)
{
size_t retlen;
+ int res;
- MTD_READ(mtd, from, count, &retlen, buf);
+ res = MTD_READ(mtd, from, count, &retlen, buf);
if (retlen != count) {
- printk("Didn't read all bytes in flash_safe_read()\n");
+ printk("Didn't read all bytes in flash_safe_read(). Returned %d\n", res);
}
- return retlen;
+ return res?res:retlen;
}
@@ -205,10 +206,11 @@ flash_read_u32(struct mtd_info *mtd, loff_t from)
{
size_t retlen;
__u32 ret;
+ int res;
- MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret);
+ res = MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret);
if (retlen != 4) {
- printk("Didn't read all bytes in flash_read_u32()\n");
+ printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res);
return 0;
}
@@ -221,10 +223,11 @@ flash_read_u8(struct mtd_info *mtd, loff_t from)
{
size_t retlen;
__u8 ret;
+ int res;
- MTD_READ(mtd, from, 1, &retlen, &ret);
+ res = MTD_READ(mtd, from, 1, &retlen, &ret);
if (retlen != 1) {
- printk("Didn't read a byte in flash_read_u8()\n");
+ printk("Didn't read a byte in flash_read_u8(). Returned %d\n", res);
return 0;
}
@@ -237,12 +240,13 @@ flash_safe_write(struct mtd_info *mtd, loff_t to,
const u_char *buf, size_t count)
{
size_t retlen;
+ int res;
- MTD_WRITE(mtd, to, count, &retlen, buf);
+ res = MTD_WRITE(mtd, to, count, &retlen, buf);
if (retlen != count) {
- printk("Didn't write all bytes in flash_safe_write()\n");
+ printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res);
}
- return retlen;
+ return res?res:retlen;
}
@@ -306,7 +310,8 @@ flash_erase_region(struct mtd_info *mtd, loff_t start,
erase->len = size;
erase->priv = (u_long)&wait_q;
- set_current_state(TASK_INTERRUPTIBLE);
+ /* FIXME: Use TASK_INTERRUPTIBLE and deal with being interrupted */
+ set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&wait_q, &wait);
if (MTD_ERASE(mtd, erase) < 0) {
@@ -321,7 +326,6 @@ flash_erase_region(struct mtd_info *mtd, loff_t start,
}
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);
@@ -369,15 +373,14 @@ jffs_checksum_flash(struct mtd_info *mtd, loff_t start, int size)
D3(printk("checksum result: 0x%08x\n", sum));
return sum;
}
-
static __inline__ void jffs_fm_write_lock(struct jffs_fmcontrol *fmc)
{
- down(&fmc->wlock);
+ // down(&fmc->wlock);
}
static __inline__ void jffs_fm_write_unlock(struct jffs_fmcontrol *fmc)
{
- up(&fmc->wlock);
+ // up(&fmc->wlock);
}
@@ -421,6 +424,7 @@ jffs_create_control(kdev_t dev)
}
DJM(no_jffs_control++);
c->root = 0;
+ c->gc_task = 0;
c->hash_len = JFFS_HASH_SIZE;
s = sizeof(struct list_head) * c->hash_len;
if (!(c->hash = (struct list_head *)kmalloc(s, GFP_KERNEL))) {
@@ -640,6 +644,16 @@ jffs_scan_flash(struct jffs_control *c)
a single kernel thread will fix the original problem.
*/
if ((__u32) pos % fmc->sector_size) {
+ /* If there was free space in previous
+ sectors, don't mark that dirty too -
+ only from the beginning of this sector
+ (or from start)
+ */
+ if (start < (pos & ~(fmc->sector_size-1))) {
+ D1(printk("Reducing start to 0x%x from 0x%x\n", pos & ~(fmc->sector_size-1), start));
+ start = pos & ~(fmc->sector_size-1);
+ }
+ D1(printk("Dirty space: 0x%x for 0x%x bytes\n", start, (pos - start)));
jffs_fmalloced(fmc, (__u32) start,
(__u32) (pos - start), 0);
}
@@ -672,6 +686,7 @@ jffs_scan_flash(struct jffs_control *c)
"hexdump(pos = 0x%lx, len = 128):\n",
(long)pos));
D1(jffs_hexdump(fmc->mtd, pos, 128));
+ cont_dirty:
for (pos += 4; pos < end; pos += 4) {
switch (flash_read_u32(fmc->mtd, pos)) {
case JFFS_MAGIC_BITMASK:
@@ -679,6 +694,46 @@ jffs_scan_flash(struct jffs_control *c)
(__u32) (pos - start),
0);
goto cont_scan;
+ case JFFS_EMPTY_BITMASK:
+ /* First, mark as dirty the region
+ which really does contain crap. */
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start),
+ 0);
+
+ /* Then, scan the region which looks free.
+ Depending on how large it is, we may
+ mark it dirty too.
+ */
+ start = pos;
+ for (; pos < end ; pos += 4) {
+ switch (flash_read_u32(fmc->mtd, pos)) {
+ case JFFS_MAGIC_BITMASK:
+ if (pos - start < fmc->max_chunk_size) {
+ /* Not much free space. Mark it dirty. */
+ jffs_fmalloced(fmc, (__u32)start,
+ (__u32)pos-start, 0);
+ }
+ goto cont_scan;
+
+ case JFFS_EMPTY_BITMASK:
+ /* More empty space */
+ continue;
+
+ default:
+ /* i.e. more dirt */
+ if (pos - start < fmc->max_chunk_size) {
+ /* There wasn't much before the dirt
+ started again. Just mark it all dirty
+ */
+ goto cont_dirty;
+ }
+ /* There was quite a lot of free space. Leave it
+ free.
+ */
+ goto cont_scan;
+ }
+ }
default:
break;
}
@@ -734,6 +789,8 @@ jffs_scan_flash(struct jffs_control *c)
/* Check the raw inode read so far. Start with the
maximum length of the filename. */
if (raw_inode.nsize > JFFS_MAX_NAME_LEN) {
+ printk(KERN_WARNING "jffs_scan_flash: Found a "
+ "JFFS node with name too large\n");
goto bad_inode;
}
@@ -748,6 +805,9 @@ jffs_scan_flash(struct jffs_control *c)
/* The node's data segment should not exceed a
certain length. */
if (raw_inode.dsize > fmc->max_chunk_size) {
+ printk(KERN_WARNING "jffs_scan_flash: Found a "
+ "JFFS node with dsize (0x%x) > max_chunk_size (0x%x)\n",
+ raw_inode.dsize, fmc->max_chunk_size);
goto bad_inode;
}
@@ -895,9 +955,9 @@ jffs_insert_node(struct jffs_control *c, struct jffs_file *f,
int insert_into_tree = 0;
D2(printk("jffs_insert_node(): ino = %u, version = %u, "
- "name = \"%s\"\n",
+ "name = \"%s\", deleted = %d\n",
raw_inode->ino, raw_inode->version,
- ((name && *name) ? name : "")));
+ ((name && *name) ? name : ""), raw_inode->deleted));
/* If there doesn't exist an associated jffs_file, then
create, initialize and insert one into the file system. */
@@ -908,7 +968,6 @@ jffs_insert_node(struct jffs_control *c, struct jffs_file *f,
jffs_insert_file_into_hash(f);
insert_into_tree = 1;
}
-
node->ino = raw_inode->ino;
node->version = raw_inode->version;
node->data_size = raw_inode->dsize;
@@ -1345,18 +1404,30 @@ jffs_write_dummy_node(struct jffs_control *c, struct jffs_fm *dirty_fm)
int
jffs_write_node(struct jffs_control *c, struct jffs_node *node,
struct jffs_raw_inode *raw_inode,
- const char *name, const unsigned char *data)
+ const char *name, const unsigned char *data,
+ int recoverable,
+ struct jffs_file *f)
{
struct jffs_fmcontrol *fmc = c->fmc;
struct jffs_fm *fm = NULL;
__u32 pos;
int err;
+ __u32 slack = 0;
+
__u32 total_name_size = raw_inode->nsize
+ JFFS_GET_PAD_BYTES(raw_inode->nsize);
__u32 total_data_size = raw_inode->dsize
+ JFFS_GET_PAD_BYTES(raw_inode->dsize);
__u32 total_size = sizeof(struct jffs_raw_inode)
+ total_name_size + total_data_size;
+
+ /* If this node isn't something that will eventually let
+ GC free even more space, then don't allow it unless
+ there's at least max_chunk_size space still available
+ */
+ if (!recoverable)
+ slack = fmc->max_chunk_size;
+
/* Fire the retrorockets and shoot the fruiton torpedoes, sir! */
@@ -1371,14 +1442,22 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node,
});
D1(printk("jffs_write_node(): filename = \"%s\", ino = %u, "
- "version = %u, total_size = %u\n",
+ "total_size = %u\n",
(name ? name : ""), raw_inode->ino,
- raw_inode->version, total_size));
+ total_size));
jffs_fm_write_lock(fmc);
while (!fm) {
+ /* Deadlocks suck. */
+ while(fmc->free_size < fmc->min_free_size + total_size + slack) {
+ jffs_fm_write_unlock(fmc);
+ if (!JFFS_ENOUGH_SPACE(c, total_size + slack))
+ return -ENOSPC;
+ jffs_fm_write_lock(fmc);
+ }
+
/* First try to allocate some flash memory. */
err = jffs_fmalloc(fmc, total_size, node, &fm);
@@ -1431,6 +1510,15 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node,
pos = node->fm->offset;
+ /* Increment the version number here. We can't let the caller
+ set it beforehand, because we might have had to do GC on a node
+ of this file - and we'd end up reusing version numbers.
+ */
+ if (f) {
+ raw_inode->version = f->highest_version + 1;
+ D1(printk (KERN_NOTICE "jffs_write_node(): setting version of %s to %d\n", f->name, raw_inode->version));
+ }
+
/* Compute the checksum for the data and name chunks. */
raw_inode->dchksum = jffs_checksum(data, raw_inode->dsize);
raw_inode->nchksum = jffs_checksum(name, raw_inode->nsize);
@@ -1496,8 +1584,9 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node,
shouldn't be read again. 'max_size' is how much space there is in
the buffer. */
static int
-jffs_get_node_data(struct jffs_file *f, struct jffs_node *node, char *buf,
- __u32 node_offset, __u32 max_size, kdev_t dev)
+jffs_get_node_data(struct jffs_file *f, struct jffs_node *node,
+ unsigned char *buf,__u32 node_offset, __u32 max_size,
+ kdev_t dev)
{
struct jffs_fmcontrol *fmc = f->c->fmc;
__u32 pos = node->fm->offset + node->fm_offset + node_offset;
@@ -1521,14 +1610,15 @@ jffs_get_node_data(struct jffs_file *f, struct jffs_node *node, char *buf,
/* Read data from the file's nodes. Write the data to the buffer
'buf'. 'read_offset' tells how much data we should skip. */
int
-jffs_read_data(struct jffs_file *f, char *buf, __u32 read_offset, __u32 size)
+jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset,
+ __u32 size)
{
struct jffs_node *node;
__u32 read_data = 0; /* Total amount of read data. */
__u32 node_offset = 0;
__u32 pos = 0; /* Number of bytes traversed. */
- D1(printk("jffs_read_data(): file = \"%s\", read_offset = %d, "
+ D2(printk("jffs_read_data(): file = \"%s\", read_offset = %d, "
"size = %u\n",
(f->name ? f->name : ""), read_offset, size));
@@ -1842,7 +1932,21 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node)
n = n->range_next;
}
- f->size -= node->removed_size;
+ if (node->removed_size > (f->size - node->data_offset)) {
+ /* It's possible that the removed_size is in fact
+ * greater than the amount of data we actually thought
+ * were present in the first place - some of the nodes
+ * which this node originally obsoleted may already have
+ * been deleted from the flash by subsequent garbage
+ * collection.
+ *
+ * If this is the case, don't let f->size go negative.
+ * Bad things would happen :)
+ */
+ f->size = node->data_offset;
+ } else {
+ f->size -= node->removed_size;
+ }
D3(printk("jffs_delete_data(): f->size = %d\n", f->size));
return 0;
} /* jffs_delete_data() */
@@ -1881,7 +1985,7 @@ jffs_insert_data(struct jffs_file *f, struct jffs_node *node)
/* Find the correct place for the insertion and then insert
the node. */
for (n = f->range_head; n; n = n->range_next) {
- D1(printk("Cool stuff's happening!\n"));
+ D2(printk("Cool stuff's happening!\n"));
if (n->data_offset == node->data_offset) {
node->range_prev = n->range_prev;
@@ -2223,7 +2327,7 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size)
int err;
D1(printk("***jffs_rewrite_data(): node: %u, name: \"%s\", size: %u\n",
- f->ino, (f->name ? f->name : ""), size));
+ f->ino, (f->name ? f->name : "(null)"), size));
/* Create and initialize the new node. */
if (!(new_node = (struct jffs_node *)
@@ -2235,8 +2339,8 @@ 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->removed_size = size;
- total_name_size = f->nsize + JFFS_GET_PAD_BYTES(f->nsize);
- total_data_size = size + JFFS_GET_PAD_BYTES(size);
+ total_name_size = JFFS_PAD(f->nsize);
+ total_data_size = JFFS_PAD(size);
total_size = sizeof(struct jffs_raw_inode)
+ total_name_size + total_data_size;
new_node->fm_offset = sizeof(struct jffs_raw_inode)
@@ -2252,31 +2356,24 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size)
return err;
}
else if (!fm->nodes) {
- /* The jffs_fm struct that we got is not good enough. */
+ /* The jffs_fm struct that we got is not big enough. */
+ /* This should never happen, because we deal with this case
+ in jffs_garbage_collect_next().*/
+ printk(KERN_WARNING "jffs_rewrite_data(): Allocated node is too small (%d bytes of %d)\n", fm->size, total_size);
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);
- 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));
- return err;
+ } else {
+ err = -ENOSPC;
}
+ DJM(no_jffs_fm--);
+ jffs_fm_write_unlock(fmc);
+ kfree(fm);
+
+ return err;
}
new_node->fm = fm;
- ASSERT(if (new_node->fm->nodes == 0) {
- printk(KERN_ERR "jffs_rewrite_data(): "
- "new_node->fm->nodes == 0\n");
- });
-
/* Initialize the raw inode. */
raw_inode.magic = JFFS_MAGIC_BITMASK;
raw_inode.ino = f->ino;
@@ -2418,10 +2515,11 @@ jffs_garbage_collect_next(struct jffs_control *c)
struct jffs_fmcontrol *fmc = c->fmc;
struct jffs_node *node;
struct jffs_file *f;
- int size;
+ int size, err = 0;
int data_size;
int total_name_size;
- int free_size = fmc->flash_size - (fmc->used_size + fmc->dirty_size);
+ __u32 extra_available;
+ __u32 space_needed;
__u32 free_chunk_size1 = jffs_free_size1(fmc);
D2(__u32 free_chunk_size2 = jffs_free_size2(fmc));
@@ -2430,47 +2528,64 @@ jffs_garbage_collect_next(struct jffs_control *c)
ASSERT(if (!node) {
printk(KERN_ERR "JFFS: jffs_garbage_collect_next: "
"No oldest node found!\n");
- return -1;
+ err = -1;
+ goto jffs_garbage_collect_next_end;
+
+
});
/* 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);
- return -1;
- });
+
+ if (!f) {
+ printk (KERN_ERR "JFFS: jffs_garbage_collect_next: "
+ "No file to garbage collect! "
+ "(ino = 0x%08x)\n", node->ino);
+ /* FIXME: Free the offending node and recover. */
+ err = -1;
+ goto jffs_garbage_collect_next_end;
+ }
+
+ /* We always write out the name. Theoretically, we don't need
+ to, but for now it's easier - because otherwise we'd have
+ to keep track of how many times the current name exists on
+ the flash and make sure it never reaches zero.
+
+ The current approach means that would be possible to cause
+ the GC to end up eating its tail by writing lots of nodes
+ with no name for it to garbage-collect. Hence the change in
+ inode.c to write names with _every_ node.
+
+ It sucks, but it _should_ work.
+ */
+ total_name_size = JFFS_PAD(f->nsize);
D1(printk("jffs_garbage_collect_next(): \"%s\", "
- "ino: %u, version: %u\n",
- (f->name ? f->name : ""), node->ino, node->version));
+ "ino: %u, version: %u, location 0x%x, dsize %u\n",
+ (f->name ? f->name : ""), node->ino, node->version,
+ node->fm->offset, node->data_size));
- /* Compute how much we want to rewrite at the moment. */
+ /* Compute how many data it's possible to rewrite at the moment. */
data_size = f->size - node->data_offset;
- total_name_size = f->nsize + JFFS_GET_PAD_BYTES(f->nsize);
+
+ /* And from that, the total size of the chunk we want to write */
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(" 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 that's more than max_chunk_size, reduce it accordingly */
if (size > fmc->max_chunk_size) {
size = fmc->max_chunk_size;
data_size = size - sizeof(struct jffs_raw_inode)
- total_name_size;
}
+
+ /* If we're asking to take up more space than free_chunk_size1
+ but we _could_ fit in it, shrink accordingly.
+ */
if (size > free_chunk_size1) {
if (free_chunk_size1 <
- (sizeof(struct jffs_raw_inode) + f->nsize + BLOCK_SIZE)) {
+ (sizeof(struct jffs_raw_inode) + total_name_size + BLOCK_SIZE)){
/* The space left is too small to be of any
use really. */
struct jffs_fm *dirty_fm
@@ -2482,32 +2597,81 @@ jffs_garbage_collect_next(struct jffs_control *c)
"jffs_garbage_collect_next: "
"Failed to allocate `dirty' "
"flash memory!\n");
- return -1;
+ err = -1;
+ goto jffs_garbage_collect_next_end;
}
+ D1(printk("Dirtying end of flash - too small\n"));
jffs_write_dummy_node(c, dirty_fm);
+ err = 0;
goto jffs_garbage_collect_next_end;
}
+ D1(printk("Reducing size of new node from %d to %d to avoid "
+ " exceeding free_chunk_size1\n",
+ size, free_chunk_size1));
size = free_chunk_size1;
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));
- }
- else {
- size -= (sizeof(struct jffs_raw_inode) + f->nsize);
- jffs_rewrite_data(f, node, data_size);
- }
+ /* Calculate the amount of space needed to hold the nodes
+ which are remaining in the tail */
+ space_needed = fmc->min_free_size - (node->fm->offset % fmc->sector_size);
+
+ /* From that, calculate how much 'extra' space we can use to
+ increase the size of the node we're writing from the size
+ of the node we're obsoleting
+ */
+ if (space_needed > fmc->free_size) {
+ /* If we've gone below min_free_size for some reason,
+ don't fuck up. This is why we have
+ min_free_size > sector_size. Whinge about it though,
+ just so I can convince myself my maths is right.
+ */
+ D1(printk(KERN_WARNING "jffs_garbage_collect_next(): "
+ "space_needed %d exceeded free_size %d\n",
+ space_needed, fmc->free_size));
+ extra_available = 0;
+ } else {
+ extra_available = fmc->free_size - space_needed;
+ }
+
+ /* Check that we don't use up any more 'extra' space than
+ what's available */
+ if (size > JFFS_PAD(node->data_size) + total_name_size +
+ sizeof(struct jffs_raw_inode) + extra_available) {
+ D1(printk("Reducing size of new node from %d to %ld to avoid "
+ "catching our tail\n", size,
+ JFFS_PAD(node->data_size) + JFFS_PAD(node->name_size) +
+ sizeof(struct jffs_raw_inode) + extra_available));
+ D1(printk("space_needed = %d, extra_available = %d\n",
+ space_needed, extra_available));
+
+ size = JFFS_PAD(node->data_size) + total_name_size +
+ sizeof(struct jffs_raw_inode) + extra_available;
+ data_size = size - sizeof(struct jffs_raw_inode)
+ - total_name_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 ((err = jffs_rewrite_data(f, node, data_size))) {
+ printk(KERN_WARNING "jffs_rewrite_data() failed: %d\n", err);
+ return err;
+ }
+
jffs_garbage_collect_next_end:
D3(printk("jffs_garbage_collect_next: Leaving...\n"));
- return 0;
+ return err;
} /* jffs_garbage_collect_next */
@@ -2683,75 +2847,75 @@ jffs_try_to_erase(struct jffs_control *c)
for exemple). Of course there is a limit on how intelligent this garbage
collection can be. */
+
int
jffs_garbage_collect_now(struct jffs_control *c)
{
struct jffs_fmcontrol *fmc = c->fmc;
- long erased_total = 0;
- long erased;
+ long erased = 0;
int result = 0;
D1(int i = 1);
- D2(printk("***jffs_garbage_collect_now(): fmc->dirty_size = %u\n",
- fmc->dirty_size));
+ D2(printk("***jffs_garbage_collect_now(): fmc->dirty_size = %u, fmc->free_size = 0x%x\n, fcs1=0x%x, fcs2=0x%x",
+ fmc->dirty_size, fmc->free_size, jffs_free_size1(fmc), jffs_free_size2(fmc)));
D2(jffs_print_fmcontrol(fmc));
- down(&fmc->gclock);
+ // down(&fmc->gclock);
/* If it is possible to garbage collect, do so. */
-
- if (fmc->dirty_size >= fmc->sector_size) {
-
+
+ while (erased == 0) {
D1(printk("***jffs_garbage_collect_now(): round #%u, "
- "fmc->dirty_size = %u\n", i++, fmc->dirty_size));
+ "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_now(): "
- "erased: %ld, free_size: %u\n",
- erased, free_size));
- result = -1;
- goto gc_end;
- }
+ if (erased)
+ break;
+
+ if (fmc->free_size == 0) {
+ /* Argh */
+ printk(KERN_ERR "jffs_garbage_collect_now(): free_size == 0. This is BAD.\n");
+ result = -ENOSPC;
+ break;
+ }
+
+ if (fmc->dirty_size < fmc->sector_size) {
+ /* Actually, we _may_ have been able to free some,
+ * if there are many overlapping nodes which aren't
+ * actually marked dirty because they still have
+ * some valid data in each.
+ */
+ result = -ENOSPC;
+ break;
+ }
+
+ /* 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;
}
D1(printk(" jffs_garbage_collect_now(): erased: %ld\n", erased));
- erased_total += erased;
DJM(jffs_print_memory_allocation_statistics());
}
-
+
gc_end:
- up(&fmc->gclock);
+ // up(&fmc->gclock);
D3(printk(" jffs_garbage_collect_now(): Leaving...\n"));
- D1(if (erased_total) {
- printk("erased_total = %ld\n", erased_total);
+ D1(if (erased) {
+ printk("jffs_g_c_now(): erased = %ld\n", erased);
jffs_print_fmcontrol(fmc);
});
- if (!erased_total && !result)
+ if (!erased && !result)
return -ENOSPC;
return result;
@@ -2766,17 +2930,15 @@ gc_end:
*/
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));
+ c->fmc->free_size, 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)
+ if (c->fmc->dirty_size < c->gc_minfree_threshold)
return 1;
/* If there are more dirty bytes than the threshold, GC */
@@ -2807,7 +2969,6 @@ 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);
@@ -2821,7 +2982,7 @@ jffs_garbage_collect_thread(void *ptr)
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));
+ siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
recalc_sigpending(current);
spin_unlock_irq(&current->sigmask_lock);
strcpy(current->comm, "jffs_gcd");
@@ -2829,6 +2990,7 @@ jffs_garbage_collect_thread(void *ptr)
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
@@ -2836,7 +2998,7 @@ jffs_garbage_collect_thread(void *ptr)
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);
@@ -2844,7 +3006,7 @@ jffs_garbage_collect_thread(void *ptr)
on immediately - we're a low priority
background task. */
- /* Put_super will send a SIGQUIT and then wait on the sem.
+ /* Put_super will send a SIGKILL and then wait on the sem.
*/
while (signal_pending(current)) {
siginfo_t info;
@@ -2861,8 +3023,8 @@ jffs_garbage_collect_thread(void *ptr)
schedule();
break;
- case SIGQUIT:
- D1(printk("jffs_garbage_collect_thread(): SIGQUIT received.\n"));
+ case SIGKILL:
+ D1(printk("jffs_garbage_collect_thread(): SIGKILL received.\n"));
c->gc_task = NULL;
up(&c->gc_thread_sem);
unlock_kernel();
@@ -2872,69 +3034,44 @@ jffs_garbage_collect_thread(void *ptr)
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);
+ D1(printk(KERN_WARNING "jffs_garbage_collect_thread with insufficient dirty space (0x%x)\n", fmc->dirty_size));
continue;
}
- down(&c->fmc->gclock);
-
+ D3(printk (KERN_NOTICE "g_c_thread(): down biglock\n"));
+ down(&fmc->biglock);
+
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;
+ "garbage collector: %ld.\n", erased);
+ }
+
+ if (erased)
+ goto gc_end;
+
+ if (fmc->free_size == 0) {
+ /* Argh. Might as well commit suicide. */
+ printk(KERN_ERR "jffs_garbage_collect_thread(): free_size == 0. This is BAD.\n");
+ send_sig(SIGQUIT, c->gc_task, 1);
+ // panic()
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;
- }
+
+ /* 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: %d\n", result);
}
-
- 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);
- });
-
+ D3(printk (KERN_NOTICE "g_c_thread(): up biglock\n"));
+ up(&fmc->biglock);
} /* for (;;) */
} /* jffs_garbage_collect_thread() */
-
diff --git a/fs/jffs/intrep.h b/fs/jffs/intrep.h
index d4638af20..f10a994b4 100644
--- a/fs/jffs/intrep.h
+++ b/fs/jffs/intrep.h
@@ -10,7 +10,7 @@
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
- * $Id: intrep.h,v 1.6 2000/08/04 14:29:17 dwmw2 Exp $
+ * $Id: intrep.h,v 1.11 2000/08/17 22:46:46 bmatthews Exp $
*
*/
@@ -46,8 +46,9 @@ int jffs_file_count(struct jffs_file *f);
int jffs_write_node(struct jffs_control *c, struct jffs_node *node,
struct jffs_raw_inode *raw_inode,
- const char *name, const unsigned char *buf);
-int jffs_read_data(struct jffs_file *f, char *buf, __u32 read_offset, __u32 size);
+ const char *name, const unsigned char *buf,
+ int recoverable, struct jffs_file *f);
+int jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset, __u32 size);
/* Garbage collection stuff. */
int jffs_garbage_collect_thread(void *c);
@@ -55,19 +56,22 @@ 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)
+static inline int JFFS_ENOUGH_SPACE(struct jffs_control *c, __u32 space)
{
struct jffs_fmcontrol *fmc = c->fmc;
while (1) {
if ((fmc->flash_size - (fmc->used_size + fmc->dirty_size))
- >= fmc->min_free_size) {
+ >= fmc->min_free_size + space) {
return 1;
}
if (fmc->dirty_size < fmc->sector_size)
return 0;
- jffs_garbage_collect_now(c);
+ if (jffs_garbage_collect_now(c)) {
+ D1(printk("JFFS_ENOUGH_SPACE: jffs_garbage_collect_now() failed.\n"));
+ return 0;
+ }
}
}
diff --git a/fs/jffs/jffs_fm.c b/fs/jffs/jffs_fm.c
index 3140b8a53..b9bbeb4a9 100644
--- a/fs/jffs/jffs_fm.c
+++ b/fs/jffs/jffs_fm.c
@@ -10,7 +10,7 @@
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
- * $Id: jffs_fm.c,v 1.14 2000/08/09 14:26:35 dwmw2 Exp $
+ * $Id: jffs_fm.c,v 1.18 2000/08/21 10:41:45 dwmw2 Exp $
*
* Ported to Linux 2.3.x and MTD:
* Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
@@ -57,17 +57,26 @@ jffs_build_begin(struct jffs_control *c, kdev_t dev)
fmc->used_size = 0;
fmc->dirty_size = 0;
+ fmc->free_size = mtd->size;
fmc->sector_size = mtd->erasesize;
fmc->max_chunk_size = fmc->sector_size >> 1;
- fmc->min_free_size = (fmc->sector_size << 1) - fmc->max_chunk_size;
+ /* min_free_size:
+ 1 sector, obviously.
+ + 1 x max_chunk_size, for when a nodes overlaps the end of a sector
+ + 1 x max_chunk_size again, which ought to be enough to handle
+ the case where a rename causes a name to grow, and GC has
+ to write out larger nodes than the ones it's obsoleting.
+ We should fix it so it doesn't have to write the name
+ _every_ time. Later.
+ */
+ fmc->min_free_size = fmc->sector_size << 1;
fmc->mtd = mtd;
- 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);
+ init_MUTEX(&fmc->biglock);
return fmc;
}
@@ -203,6 +212,11 @@ jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, struct jffs_node *node,
free_chunk_size1 = jffs_free_size1(fmc);
free_chunk_size2 = jffs_free_size2(fmc);
+ if (free_chunk_size1 + free_chunk_size2 != fmc->free_size) {
+ printk(KERN_WARNING "Free size accounting screwed\n");
+ printk(KERN_WARNING "free_chunk_size1 == 0x%x, free_chunk_size2 == 0x%x, fmc->free_size == 0x%x\n", free_chunk_size1, free_chunk_size2, fmc->free_size);
+ }
+
D3(printk("jffs_fmalloc(): free_chunk_size1 = %u, "
"free_chunk_size2 = %u\n",
free_chunk_size1, free_chunk_size2));
@@ -240,6 +254,7 @@ jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, struct jffs_node *node,
fm->offset = fmc->flash_start;
}
fm->size = size;
+ fmc->free_size -= size;
fmc->used_size += size;
}
else if (size > free_chunk_size2) {
@@ -253,6 +268,7 @@ jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, struct jffs_node *node,
fm->offset = fmc->tail->offset + fmc->tail->size;
fm->size = free_chunk_size1;
fm->nodes = 0;
+ fmc->free_size -= fm->size;
fmc->dirty_size += fm->size; /* Changed by simonk. This seemingly fixes a
bug that caused infinite garbage collection.
It previously set fmc->dirty_size to size (which is the
@@ -380,10 +396,12 @@ jffs_fmalloced(struct jffs_fmcontrol *fmc, __u32 offset, __u32 size,
fm->nodes->node = node;
fm->nodes->next = 0;
fmc->used_size += size;
+ fmc->free_size -= size;
}
else {
/* If there is no node, then this is just a chunk of dirt. */
fmc->dirty_size += size;
+ fmc->free_size -= size;
}
if (fmc->head_extra) {
@@ -505,6 +523,7 @@ jffs_sync_erase(struct jffs_fmcontrol *fmc, int erased_size)
});
fmc->dirty_size -= erased_size;
+ fmc->free_size += erased_size;
for (fm = fmc->head; fm && (erased_size > 0);) {
if (erased_size >= fm->size) {
@@ -701,6 +720,7 @@ jffs_print_fmcontrol(struct jffs_fmcontrol *fmc)
D(printk(" %u, /* flash_size */\n", fmc->flash_size));
D(printk(" %u, /* used_size */\n", fmc->used_size));
D(printk(" %u, /* dirty_size */\n", fmc->dirty_size));
+ D(printk(" %u, /* free_size */\n", fmc->free_size));
D(printk(" %u, /* sector_size */\n", fmc->sector_size));
D(printk(" %u, /* min_free_size */\n", fmc->min_free_size));
D(printk(" %u, /* max_chunk_size */\n", fmc->max_chunk_size));
diff --git a/fs/jffs/jffs_fm.h b/fs/jffs/jffs_fm.h
index 82077d9d1..2bac0cb01 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.7 2000/08/08 09:10:39 dwmw2 Exp $
+ * $Id: jffs_fm.h,v 1.10 2000/08/17 15:42:44 dwmw2 Exp $
*
* Ported to Linux 2.3.x and MTD:
* Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
@@ -62,7 +62,7 @@
#define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \
- ((__u32)(size) % JFFS_ALIGN_SIZE)) \
% JFFS_ALIGN_SIZE)
-
+#define JFFS_PAD(size) ( (size + (JFFS_ALIGN_SIZE-1)) & ~(JFFS_ALIGN_SIZE-1) )
struct jffs_node_ref
{
struct jffs_node *node;
@@ -86,18 +86,18 @@ struct jffs_fmcontrol
__u32 flash_size;
__u32 used_size;
__u32 dirty_size;
+ __u32 free_size;
__u32 sector_size;
__u32 min_free_size; /* The minimum free space needed to be able
to perform garbage collections. */
__u32 max_chunk_size; /* The maximum size of a chunk of data. */
struct mtd_info *mtd;
- 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;
+ struct semaphore biglock;
};
/* Notice the two members head_extra and tail_extra in the jffs_control