diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-10-05 01:18:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-10-05 01:18:40 +0000 |
commit | 012bb3e61e5eced6c610f9e036372bf0c8def2d1 (patch) | |
tree | 87efc733f9b164e8c85c0336f92c8fb7eff6d183 /fs/jffs | |
parent | 625a1589d3d6464b5d90b8a0918789e3afffd220 (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.c | 514 | ||||
-rw-r--r-- | fs/jffs/intrep.c | 503 | ||||
-rw-r--r-- | fs/jffs/intrep.h | 16 | ||||
-rw-r--r-- | fs/jffs/jffs_fm.c | 28 | ||||
-rw-r--r-- | fs/jffs/jffs_fm.h | 8 |
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(¤t->sigmask_lock); - siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGQUIT) | sigmask(SIGSTOP) | sigmask(SIGCONT)); + siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); recalc_sigpending(current); spin_unlock_irq(¤t->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 |