diff options
| -rw-r--r-- | fs/jffs2/TODO | 26 | ||||
| -rw-r--r-- | fs/jffs2/background.c | 33 | ||||
| -rw-r--r-- | fs/jffs2/build.c | 6 | ||||
| -rw-r--r-- | fs/jffs2/compr_zlib.c | 12 | ||||
| -rw-r--r-- | fs/jffs2/dir.c | 40 | ||||
| -rw-r--r-- | fs/jffs2/erase.c | 27 | ||||
| -rw-r--r-- | fs/jffs2/file.c | 12 | ||||
| -rw-r--r-- | fs/jffs2/fs.c | 172 | ||||
| -rw-r--r-- | fs/jffs2/gc.c | 40 | ||||
| -rw-r--r-- | fs/jffs2/nodelist.c | 26 | ||||
| -rw-r--r-- | fs/jffs2/nodelist.h | 10 | ||||
| -rw-r--r-- | fs/jffs2/nodemgmt.c | 165 | ||||
| -rw-r--r-- | fs/jffs2/os-linux.h | 6 | ||||
| -rw-r--r-- | fs/jffs2/readinode.c | 7 | ||||
| -rw-r--r-- | fs/jffs2/scan.c | 142 | ||||
| -rw-r--r-- | fs/jffs2/super.c | 22 | ||||
| -rw-r--r-- | fs/jffs2/wbuf.c | 86 | ||||
| -rw-r--r-- | fs/jffs2/write.c | 16 | ||||
| -rw-r--r-- | include/linux/jffs2_fs_sb.h | 14 |
19 files changed, 623 insertions, 239 deletions
diff --git a/fs/jffs2/TODO b/fs/jffs2/TODO index a5dfc795626f..f78a5bf4569f 100644 --- a/fs/jffs2/TODO +++ b/fs/jffs2/TODO @@ -1,6 +1,5 @@ -$Id: TODO,v 1.7 2002/03/11 12:36:59 dwmw2 Exp $ +$Id: TODO,v 1.9 2002/07/11 10:39:04 dwmw2 Exp $ - - Locking audit. Even more so now 2.5 took away the BKL. - disable compression in commit_write()? - fine-tune the allocation / GC thresholds - chattr support - turning on/off and tuning compression per-inode @@ -9,7 +8,6 @@ $Id: TODO,v 1.7 2002/03/11 12:36:59 dwmw2 Exp $ mount doesn't have to read the flash twice for large files. Make this a per-inode option, changable with chattr, so you can decide which inodes should be in-core immediately after mount. - - stop it depending on a block device. - test, test, test - NAND flash support: @@ -22,3 +20,25 @@ $Id: TODO,v 1.7 2002/03/11 12:36:59 dwmw2 Exp $ - timed flush of old wbuf - fix magical second arg of jffs2_flush_wbuf(). Split into two or more functions instead. + + - Optimisations: + - Stop GC from decompressing and immediately recompressing nodes which could + just be copied intact. + - Furthermore, in the case where it could be copied intact we don't even need + to call iget() for it -- if we use (raw_node_raw->flash_offset & 2) as a flag + to show a node can be copied intact and it's _not_ in icache, we could just do + it, fix up the next_in_ino list and move on. We would need a way to find out + _whether_ it's in icache though -- if it's in icache we also need to do the + fragment lists, etc. P'raps a flag or pointer in the jffs2_inode_cache could + help. + - Stop keeping name in-core with struct jffs2_full_dirent. If we keep the hash in + the full dirent, we only need to go to the flash in lookup() when we think we've + got a match, and in readdir(). + - Doubly-linked next_in_ino list to allow us to free obsoleted raw_node_refs immediately? + - Remove totlen from jffs2_raw_node_ref? Need to have totlen passed into + jffs2_mark_node_obsolete(). Can all callers work it out? + - Don't check data CRC on node scan during mount. We don't really need to know + yet. This means we can't build up node fragment lists, and hence can't + build accurate clean/dirty information. But we don't _need_ that for reading, + only for writing. And in fact we don't even need it for writing until we + start to need GC. diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 33a1e840e314..6a672556dddb 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -7,19 +7,19 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: background.c,v 1.23 2002/03/06 12:37:08 dwmw2 Exp $ + * $Id: background.c,v 1.29 2002/06/07 10:04:28 dwmw2 Exp $ * */ #define __KERNEL_SYSCALLS__ #include <linux/kernel.h> -#include <linux/unistd.h> #include <linux/jffs2.h> #include <linux/mtd/mtd.h> #include <linux/interrupt.h> #include <linux/completion.h> #include <linux/mtd/compatmac.h> /* recalc_sigpending() */ +#include <linux/unistd.h> #include "nodelist.h" @@ -62,6 +62,13 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) { + if (c->mtd->type == MTD_NANDFLASH) { + /* stop a eventually scheduled wbuf flush timer */ + del_timer_sync(&c->wbuf_timer); + /* make sure, that a scheduled wbuf flush task is completed */ + flush_scheduled_tasks(); + } + spin_lock_bh(&c->erase_completion_lock); if (c->gc_task) { D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); @@ -147,11 +154,23 @@ static int jffs2_garbage_collect_thread(void *_c) static int thread_should_wake(struct jffs2_sb_info *c) { - D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n", - c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size)); + uint32_t gcnodeofs = 0; + int ret; + + /* Don't count any progress we've already made through the gcblock + as dirty space, for the purposes of this calculation */ + if (c->gcblock && c->gcblock->gc_node) + gcnodeofs = c->gcblock->gc_node->flash_offset & ~3 & (c->sector_size-1); + if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER && - c->dirty_size > c->sector_size) - return 1; + (c->dirty_size - gcnodeofs) > c->sector_size) + ret = 1; else - return 0; + ret = 0; + + D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x (mod 0x%x): %s\n", + c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, + c->dirty_size - gcnodeofs, ret?"yes":"no")); + + return ret; } diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 1e2f739a8d86..902f680a95f1 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.32 2002/03/08 15:11:24 dwmw2 Exp $ + * $Id: build.c,v 1.35 2002/05/20 14:56:37 dwmw2 Exp $ * */ @@ -100,6 +100,9 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) } D1(printk(KERN_DEBUG "Pass 3 complete\n")); + /* Rotate the lists by some number to ensure wear levelling */ + jffs2_rotate_lists(c); + return ret; } @@ -280,6 +283,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) spin_lock_init(&c->inocache_lock); INIT_LIST_HEAD(&c->clean_list); + INIT_LIST_HEAD(&c->very_dirty_list); INIT_LIST_HEAD(&c->dirty_list); INIT_LIST_HEAD(&c->erasable_list); INIT_LIST_HEAD(&c->erasing_list); diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index 8235df147c37..e1dbbbf169ec 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.15 2002/03/04 09:35:48 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.18 2002/05/20 14:56:37 dwmw2 Exp $ * */ @@ -41,21 +41,21 @@ int __init jffs2_zlib_init(void) { deflate_workspace = vmalloc(zlib_deflate_workspacesize()); if (!deflate_workspace) { - printk("Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize()); + printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize()); return -ENOMEM; } - D1(printk("Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize())); + D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize())); inflate_workspace = vmalloc(zlib_inflate_workspacesize()); if (!inflate_workspace) { - printk("Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize()); + printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize()); vfree(deflate_workspace); return -ENOMEM; } - D1(printk("Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize())); + D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize())); return 0; } -void __exit jffs2_zlib_exit(void) +void jffs2_zlib_exit(void) { vfree(deflate_workspace); vfree(inflate_workspace); diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 0dff91d28aee..136b85bf9d0a 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: dir.c,v 1.68 2002/03/11 12:36:59 dwmw2 Exp $ + * $Id: dir.c,v 1.70 2002/06/20 23:33:12 dwmw2 Exp $ * */ @@ -716,8 +716,30 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); + struct jffs2_inode_info *victim_f = NULL; uint8_t type; + /* The VFS will check for us and prevent trying to rename a + * file over a directory and vice versa, but if it's a directory, + * the VFS can't check whether the victim is empty. The filesystem + * needs to do that for itself. + */ + if (new_dentry->d_inode) { + victim_f = JFFS2_INODE_INFO(new_dentry->d_inode); + if (S_ISDIR(new_dentry->d_inode->i_mode)) { + struct jffs2_full_dirent *fd; + + down(&victim_f->sem); + for (fd = victim_f->dents; fd; fd = fd->next) { + if (fd->ino) { + up(&victim_f->sem); + return -ENOTEMPTY; + } + } + up(&victim_f->sem); + } + } + /* XXX: We probably ought to alloc enough space for both nodes at the same time. Writing the new link, then getting -ENOSPC, is quite bad :) @@ -736,7 +758,21 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, if (ret) return ret; - if (S_ISDIR(old_dentry->d_inode->i_mode)) + if (victim_f) { + /* There was a victim. Kill it off nicely */ + new_dentry->d_inode->i_nlink--; + /* Don't oops if the victim was a dirent pointing to an + inode which didn't exist. */ + if (victim_f->inocache) { + down(&victim_f->sem); + victim_f->inocache->nlink--; + up(&victim_f->sem); + } + } + + /* If it was a directory we moved, and there was no victim, + increase i_nlink on its new parent */ + if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f) new_dir_i->i_nlink++; /* Unlink the original */ diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index d8961858e128..ab340ec82161 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.35 2002/03/08 15:11:24 dwmw2 Exp $ + * $Id: erase.c,v 1.38 2002/07/02 22:48:24 dwmw2 Exp $ * */ @@ -262,20 +262,12 @@ void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) void jffs2_mark_erased_blocks(struct jffs2_sb_info *c) { - static struct jffs2_unknown_node marker = { - magic: JFFS2_MAGIC_BITMASK, - nodetype: JFFS2_NODETYPE_CLEANMARKER, - totlen: sizeof(struct jffs2_unknown_node) - }; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *marker_ref = NULL; unsigned char *ebuf; size_t retlen; int ret; - if (unlikely(!marker.hdr_crc)) - marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4); - spin_lock_bh(&c->erase_completion_lock); while (!list_empty(&c->erase_complete_list)) { jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); @@ -335,6 +327,7 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c) } } ofs += readlen; + cond_resched(); } kfree(ebuf); } @@ -352,22 +345,30 @@ void jffs2_mark_erased_blocks(struct jffs2_sb_info *c) jeb->used_size = 0; jeb->dirty_size = 0; } else { - ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); + struct jffs2_unknown_node marker = { + magic: JFFS2_MAGIC_BITMASK, + nodetype: JFFS2_NODETYPE_CLEANMARKER, + totlen: c->cleanmarker_size + }; + + marker.hdr_crc = crc32(0, &marker, marker.totlen - 4); + + ret = jffs2_flash_write(c, jeb->offset, marker.totlen, &retlen, (char *)&marker); if (ret) { printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", jeb->offset, ret); goto bad2; } - if (retlen != sizeof(marker)) { + if (retlen != marker.totlen) { printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n", - jeb->offset, sizeof(marker), retlen); + jeb->offset, marker.totlen, retlen); goto bad2; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = jeb->offset; - marker_ref->totlen = PAD(sizeof(marker)); + marker_ref->totlen = PAD(marker.totlen); jeb->first_node = jeb->last_node = marker_ref; diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index cc6909f1570c..db9a94f0adbe 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.70 2002/03/05 09:55:07 dwmw2 Exp $ + * $Id: file.c,v 1.74 2002/07/23 12:58:53 dwmw2 Exp $ * */ @@ -41,9 +41,11 @@ int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) * maybe we have to think about it to find a smarter * solution. */ + down(&c->alloc_sem); down(&f->sem); jffs2_flush_wbuf(c,2); up(&f->sem); + up(&c->alloc_sem); return 0; } @@ -256,18 +258,18 @@ int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg) int jffs2_readpage (struct file *filp, struct page *pg) { - struct jffs2_inode_info *f = JFFS2_INODE_INFO(filp->f_dentry->d_inode); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host); int ret; down(&f->sem); - ret = jffs2_do_readpage_unlock(filp->f_dentry->d_inode, pg); + ret = jffs2_do_readpage_unlock(pg->mapping->host, pg); up(&f->sem); return ret; } int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end) { - struct inode *inode = filp->f_dentry->d_inode; + struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT; int ret = 0; @@ -351,7 +353,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple */ - struct inode *inode = filp->f_dentry->d_inode; + struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index a6f1102b8292..c9db5fe4af7e 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs.c,v 1.4 2002/03/11 12:36:59 dwmw2 Exp $ + * $Id: fs.c,v 1.13 2002/07/02 22:48:24 dwmw2 Exp $ * */ @@ -44,122 +44,10 @@ int jffs2_statfs(struct super_block *sb, struct statfs *buf) buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; -#if CONFIG_JFFS2_FS_DEBUG > 0 - printk(KERN_DEBUG "STATFS:\n"); - printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); - printk(KERN_DEBUG "used_size: %08x\n", c->used_size); - printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); - printk(KERN_DEBUG "free_size: %08x\n", c->free_size); - printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); - printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); - printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); - printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE); - - if (c->nextblock) { - printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset); - } else { - printk(KERN_DEBUG "nextblock: NULL\n"); - } - if (c->gcblock) { - printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset); - } else { - printk(KERN_DEBUG "gcblock: NULL\n"); - } - if (list_empty(&c->clean_list)) { - printk(KERN_DEBUG "clean_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->clean_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset); - } - } - if (list_empty(&c->dirty_list)) { - printk(KERN_DEBUG "dirty_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->dirty_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset); - } - } - if (list_empty(&c->erasable_list)) { - printk(KERN_DEBUG "erasable_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasable_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erasable_list: %08x\n", jeb->offset); - } - } - if (list_empty(&c->erasing_list)) { - printk(KERN_DEBUG "erasing_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasing_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset); - } - } - if (list_empty(&c->erase_pending_list)) { - printk(KERN_DEBUG "erase_pending_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erase_pending_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset); - } - } - if (list_empty(&c->erasable_pending_wbuf_list)) { - printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->erasable_pending_wbuf_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "erase_pending_wbuf_list: %08x\n", jeb->offset); - } - } - if (list_empty(&c->free_list)) { - printk(KERN_DEBUG "free_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->free_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "free_list: %08x\n", jeb->offset); - } - } - if (list_empty(&c->bad_list)) { - printk(KERN_DEBUG "bad_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->bad_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset); - } - } - if (list_empty(&c->bad_used_list)) { - printk(KERN_DEBUG "bad_used_list: empty\n"); - } else { - struct list_head *this; - - list_for_each(this, &c->bad_used_list) { - struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); - printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset); - } - } -#endif /* CONFIG_JFFS2_FS_DEBUG */ + D1(jffs2_dump_block_lists(c)); spin_unlock_bh(&c->erase_completion_lock); - return 0; } @@ -250,9 +138,9 @@ void jffs2_read_inode (struct inode *inode) if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { /* Eep */ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); + up(&f->sem); jffs2_do_clear_inode(c, f); make_bad_inode(inode); - up(&f->sem); return; } @@ -301,11 +189,19 @@ void jffs2_write_super (struct super_block *sb) if (sb->s_flags & MS_RDONLY) return; - D1(printk("jffs2_write_super(): flush_wbuf before gc-trigger\n")); - jffs2_flush_wbuf(c, 2); + D1(printk(KERN_DEBUG "jffs2_write_super(): flush_wbuf before gc-trigger\n")); jffs2_garbage_collect_trigger(c); jffs2_erase_pending_blocks(c); jffs2_mark_erased_blocks(c); + /* Eep. If we lock this here, we deadlock with jffs2_reserve_space() when + * it locks the alloc_sem and jffs2_do_reserve_space() waits for erases + * to happen. I think the erases and/or the flush_wbuf want doing from + * + */ + if (!down_trylock(&c->alloc_sem)) { + jffs2_flush_wbuf(c, 2); + up(&c->alloc_sem); + } // else it stays dirty. FIXME. } @@ -370,33 +266,61 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) { struct jffs2_sb_info *c; struct inode *root_i; + int ret; c = JFFS2_SB_INFO(sb); c->sector_size = c->mtd->erasesize; c->flash_size = c->mtd->size; +#if 0 if (c->sector_size < 0x10000) { printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using 64KiB instead\n", c->sector_size / 1024); c->sector_size = 0x10000; } +#endif if (c->flash_size < 5*c->sector_size) { printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size); return -EINVAL; } + + c->cleanmarker_size = sizeof(struct jffs2_unknown_node); + /* Jörn -- stick alignment for weird 8-byte-page flash here */ + + if (jffs2_cleanmarker_oob(c)) { + /* Cleanmarker is out-of-band, so inline size zero */ + c->cleanmarker_size = 0; + } + if (c->mtd->type == MTD_NANDFLASH) { /* Initialise write buffer */ c->wbuf_pagesize = c->mtd->oobblock; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) - goto out_mtd; + return -ENOMEM; + + /* Initialize process for timed wbuf flush */ + INIT_TQUEUE(&c->wbuf_task,(void*) jffs2_wbuf_process, (void *)c); + /* Initialize timer for timed wbuf flush */ + init_timer(&c->wbuf_timer); + c->wbuf_timer.function = jffs2_wbuf_timeout; + c->wbuf_timer.data = (unsigned long) c; } - if (jffs2_do_mount_fs(c)) - goto out_mtd; + c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); + if (!c->inocache_list) { + ret = -ENOMEM; + goto out_wbuf; + } + memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); + + if ((ret = jffs2_do_mount_fs(c))) + goto out_inohash; + + ret = -EINVAL; D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); root_i = iget(sb, 1); @@ -426,6 +350,10 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); kfree(c->blocks); - out_mtd: - return -EINVAL; + out_inohash: + kfree(c->inocache_list); + out_wbuf: + if (c->wbuf) + kfree(c->wbuf); + return ret; } diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 000b42856094..cd5adf15244b 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.68 2002/03/08 15:11:24 dwmw2 Exp $ + * $Id: gc.c,v 1.74 2002/05/20 14:56:38 dwmw2 Exp $ * */ @@ -41,14 +41,21 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) /* Pick an eraseblock to garbage collect next. This is where we'll put the clever wear-levelling algorithms. Eventually. */ + /* We possibly want to favour the dirtier blocks more when the + number of free blocks is low. */ if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) { D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); nextlist = &c->bad_used_list; - } else if (n < 100 && !list_empty(&c->erasable_list)) { + } else if (n < 50 && !list_empty(&c->erasable_list)) { + /* Note that most of them will have gone directly to be erased. + So don't favour the erasable_list _too_ much. */ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n")); nextlist = &c->erasable_list; + } else if (n < 110 && !list_empty(&c->very_dirty_list)) { + /* Most of the time, pick one off the very_dirty list */ + D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n")); + nextlist = &c->very_dirty_list; } else if (n < 126 && !list_empty(&c->dirty_list)) { - /* Most of the time, pick one off the dirty list */ D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n")); nextlist = &c->dirty_list; } else if (!list_empty(&c->clean_list)) { @@ -58,12 +65,15 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n")); nextlist = &c->dirty_list; + } else if (!list_empty(&c->very_dirty_list)) { + D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n")); + nextlist = &c->very_dirty_list; } else if (!list_empty(&c->erasable_list)) { - D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and dirty_list were empty)\n")); + D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n")); nextlist = &c->erasable_list; } else { - /* Eep. Both were empty */ + /* Eep. All were empty */ printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"); return NULL; } @@ -76,6 +86,8 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); BUG(); } + + D1(jffs2_dump_block_lists(c)); return ret; } @@ -114,7 +126,9 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) return -EIO; } - D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset)); + D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); + D1(if (c->nextblock) + printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->free_size)); if (!jeb->used_size) goto eraseit; @@ -582,9 +596,21 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; - return 0; } + return 0; } + + /* + * We should only get here in the case where the node we are + * replacing had more than one frag, so we kept the same version + * number as before. (Except in case of error -- see 'goto fill;' + * above.) + */ + D1(if(unlikely(fn->frags <= 1)) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n", + fn->frags, ri.version, f->highest_version, ri.ino); + }); + for (frag = f->fraglist; frag; frag = frag->next) { if (frag->ofs > fn->size + fn->ofs) break; diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 21c669f5a202..f3a52ca40fed 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.42 2002/03/11 11:17:29 dwmw2 Exp $ + * $Id: nodelist.c,v 1.47 2002/06/26 01:25:30 dwmw2 Exp $ * */ @@ -45,7 +45,7 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new *prev = new; out: - D1(while(*list) { + D2(while(*list) { printk(KERN_DEBUG "Dirent \"%s\" (hash 0x%08x, ino #%u\n", (*list)->name, (*list)->nhash, (*list)->ino); list = &(*list)->next; }); @@ -287,17 +287,14 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino) D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino)); spin_lock (&c->inocache_lock); - if (c->inocache_last && c->inocache_last->ino == ino) { - ret = c->inocache_last; - } else { - ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; - while (ret && ret->ino < ino) { - ret = ret->next; - } - - if (ret && ret->ino != ino) - ret = NULL; + ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; + while (ret && ret->ino < ino) { + ret = ret->next; } + + if (ret && ret->ino != ino) + ret = NULL; + spin_unlock(&c->inocache_lock); D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino)); @@ -318,8 +315,6 @@ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new new->next = *prev; *prev = new; - c->inocache_last = new; - spin_unlock(&c->inocache_lock); } @@ -337,8 +332,6 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old) if ((*prev) == old) { *prev = old->next; } - if (c->inocache_last == old) - c->inocache_last = NULL; spin_unlock(&c->inocache_lock); } @@ -358,7 +351,6 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c) } c->inocache_list[i] = NULL; } - c->inocache_last = NULL; } void jffs2_free_raw_node_refs(struct jffs2_sb_info *c) diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index e39c6b1f6b0b..16cc9b9b3cf4 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.68 2002/03/08 11:27:19 dwmw2 Exp $ + * $Id: nodelist.h,v 1.74 2002/06/26 01:20:43 dwmw2 Exp $ * */ @@ -93,6 +93,8 @@ struct jffs2_inode_cache { int nlink; }; +#define INOCACHE_HASHSIZE 128 + struct jffs2_scan_info { struct jffs2_full_dirent *dents; struct jffs2_tmp_dnode_info *tmpnodes; @@ -148,7 +150,6 @@ struct jffs2_node_frag struct jffs2_full_dnode *node; /* NULL for holes */ uint32_t size; uint32_t ofs; /* Don't really need this, but optimisation */ - uint32_t node_ofs; /* offset within the physical node */ }; struct jffs2_eraseblock @@ -216,6 +217,9 @@ struct jffs2_eraseblock #define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */ +/* How much dirty space before it goes on the very_dirty_list */ +#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) + #define PAD(x) (((x)+3)&~3) static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw) @@ -247,6 +251,7 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t * int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, uint32_t len, int dirty); void jffs2_complete_reservation(struct jffs2_sb_info *c); void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); +void jffs2_dump_block_lists(struct jffs2_sb_info *c); /* write.c */ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri); @@ -307,6 +312,7 @@ int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, /* scan.c */ int jffs2_scan_medium(struct jffs2_sb_info *c); +void jffs2_rotate_lists(struct jffs2_sb_info *c); /* build.c */ int jffs2_do_mount_fs(struct jffs2_sb_info *c); diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 291cbe3c1120..15ceb8d480c7 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.63 2002/03/08 14:54:09 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.70 2002/07/02 22:48:24 dwmw2 Exp $ * */ @@ -134,9 +134,15 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui c->free_size -= jeb->free_size; jeb->dirty_size += jeb->free_size; jeb->free_size = 0; - D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + if (VERYDIRTY(c, jeb->dirty_size)) { + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); - list_add_tail(&jeb->list, &c->dirty_list); + list_add_tail(&jeb->list, &c->very_dirty_list); + } else { + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); + list_add_tail(&jeb->list, &c->dirty_list); + } c->nextblock = jeb = NULL; } @@ -156,6 +162,7 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui list_del(&ejeb->list); list_add_tail(&ejeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n", ejeb->offset)); } @@ -208,10 +215,7 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list); c->nr_free_blocks--; - /* On NAND free_size == sector_size, cleanmarker is in spare area !*/ - if (jeb->free_size != c->sector_size - - (jffs2_cleanmarker_oob(c)) ? 0 : sizeof(struct jffs2_unknown_node)) { - + if (jeb->free_size != c->sector_size - c->cleanmarker_size) { printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); goto restart; } @@ -396,8 +400,19 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref BUG(); } } else { - D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); - list_add_tail(&jeb->list, &c->erasable_list); + if (jiffies & 127) { + /* Most of the time, we just erase it immediately. Otherwise we + spend ages scanning it on mount, etc. */ + D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); + list_add_tail(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); + } else { + /* Sometimes, however, we leave it elsewhere so it doesn't get + immediately reused, and we spread the load a bit. */ + D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); + list_add_tail(&jeb->list, &c->erasable_list); + } } D1(printk(KERN_DEBUG "Done OK\n")); } else if (jeb == c->gcblock) { @@ -407,6 +422,12 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref list_del(&jeb->list); D1(printk(KERN_DEBUG "...and adding to dirty_list\n")); list_add_tail(&jeb->list, &c->dirty_list); + } else if (VERYDIRTY(c, jeb->dirty_size) && + !VERYDIRTY(c, jeb->dirty_size - ref->totlen)) { + D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset)); + list_del(&jeb->list); + D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n")); + list_add_tail(&jeb->list, &c->very_dirty_list); } spin_unlock_bh(&c->erase_completion_lock); @@ -445,3 +466,129 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref return; } } + +#if CONFIG_JFFS2_FS_DEBUG > 0 +void jffs2_dump_block_lists(struct jffs2_sb_info *c) +{ + printk(KERN_DEBUG "jffs2_dump_block_lists:\n"); + printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); + printk(KERN_DEBUG "used_size: %08x\n", c->used_size); + printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); + printk(KERN_DEBUG "free_size: %08x\n", c->free_size); + printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); + printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); + printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); + printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE); + + if (c->nextblock) { + printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, free %08x)\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->free_size); + } else { + printk(KERN_DEBUG "nextblock: NULL\n"); + } + if (c->gcblock) { + printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, free %08x)\n", c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->free_size); + } else { + printk(KERN_DEBUG "gcblock: NULL\n"); + } + if (list_empty(&c->clean_list)) { + printk(KERN_DEBUG "clean_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->clean_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->very_dirty_list)) { + printk(KERN_DEBUG "very_dirty_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->very_dirty_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->dirty_list)) { + printk(KERN_DEBUG "dirty_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->dirty_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->erasable_list)) { + printk(KERN_DEBUG "erasable_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasable_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->erasing_list)) { + printk(KERN_DEBUG "erasing_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasing_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->erase_pending_list)) { + printk(KERN_DEBUG "erase_pending_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erase_pending_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->erasable_pending_wbuf_list)) { + printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erasable_pending_wbuf_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "erase_pending_wbuf_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->free_list)) { + printk(KERN_DEBUG "free_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->free_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->bad_list)) { + printk(KERN_DEBUG "bad_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->bad_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } + if (list_empty(&c->bad_used_list)) { + printk(KERN_DEBUG "bad_used_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->bad_used_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size); + } + } +} +#endif /* CONFIG_JFFS2_FS_DEBUG */ diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index dbdca2bd1761..1c3e380c4608 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-linux.h,v 1.16 2002/03/17 10:18:42 dwmw2 Exp $ + * $Id: os-linux.h,v 1.19 2002/05/20 14:56:38 dwmw2 Exp $ * */ @@ -77,6 +77,8 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0) #define jffs2_write_nand_badblock(c,jeb) do { ; } while(0) #define jffs2_flash_writev jffs2_flash_direct_writev +#define jffs2_wbuf_timeout NULL +#define jffs2_wbuf_process NULL #else /* NAND support present */ @@ -95,6 +97,8 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +void jffs2_wbuf_timeout(unsigned long data); +void jffs2_wbuf_process(void *data); #endif /* NAND */ /* background.c */ diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 28f04a67af2d..afa8cfb4cdc3 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.71 2002/03/06 12:25:59 dwmw2 Exp $ + * $Id: readinode.c,v 1.73 2002/05/20 14:56:38 dwmw2 Exp $ * */ @@ -313,6 +313,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %ld of %d bytes read\n", ret, (long)retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ + up(&f->sem); jffs2_do_clear_inode(c, f); return ret?ret:-EIO; } @@ -320,6 +321,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, crc = crc32(0, latest_node, sizeof(*latest_node)-8); if (crc != latest_node->node_crc) { printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", ino, fn->raw->flash_offset & ~3); + up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -354,11 +356,13 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, kept as the metadata node */ if (f->metadata) { printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", ino, latest_node->mode); + up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } if (!f->fraglist) { printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", ino, latest_node->mode); + up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -366,6 +370,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, if (f->fraglist->next) { printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had more than one node\n", ino, latest_node->mode); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ + up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 0deb72326073..f3149189643c 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.69 2002/03/08 11:03:23 dwmw2 Exp $ + * $Id: scan.c,v 1.78 2002/07/02 22:48:24 dwmw2 Exp $ * */ #include <linux/kernel.h> @@ -38,7 +38,6 @@ } while(0) static uint32_t pseudo_random; -static void jffs2_rotate_lists(struct jffs2_sb_info *c); static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); @@ -117,13 +116,23 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) not the one with most free space. */ if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && - (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { + (jffs2_can_mark_obsolete(c) || jeb->free_size > c->wbuf_pagesize) && + (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ - if (c->nextblock) - list_add(&c->nextblock->list, &c->dirty_list); + if (c->nextblock) { + if (VERYDIRTY(c, c->nextblock->dirty_size)) { + list_add(&c->nextblock->list, &c->very_dirty_list); + } else { + list_add(&c->nextblock->list, &c->dirty_list); + } + } c->nextblock = jeb; } else { - list_add(&jeb->list, &c->dirty_list); + if (VERYDIRTY(c, jeb->dirty_size)) { + list_add(&jeb->list, &c->very_dirty_list); + } else { + list_add(&jeb->list, &c->dirty_list); + } } break; @@ -143,14 +152,26 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) bad_blocks++; break; default: - printk("jffs2_scan_medium(): unknown block state\n"); + printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n"); BUG(); } } - /* Rotate the lists by some number to ensure wear levelling */ - jffs2_rotate_lists(c); + if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { + /* If we're going to start writing into a block which already + contains data, and the end of the data isn't page-aligned, + skip a little and align it. */ + + uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1); + D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", + skip)); + c->nextblock->dirty_size += skip; + c->dirty_size += skip; + + c->nextblock->free_size -= skip; + c->free_size -= skip; + } if (c->nr_erasing_blocks) { if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); @@ -221,7 +242,9 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo while(ofs < jeb->offset + c->sector_size) { size_t retlen; ACCT_PARANOIA_CHECK(jeb); - + + cond_resched(); + if (ofs & 3) { printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); ofs = (ofs+3)&~3; @@ -319,9 +342,9 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo break; case JFFS2_NODETYPE_CLEANMARKER: - if (node.totlen != sizeof(struct jffs2_unknown_node)) { + if (node.totlen != c->cleanmarker_size) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", - ofs, node.totlen, sizeof(struct jffs2_unknown_node)); + ofs, node.totlen, c->cleanmarker_size); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); } else if (jeb->first_node) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); @@ -345,6 +368,11 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo ofs += PAD(sizeof(struct jffs2_unknown_node)); break; + case JFFS2_NODETYPE_PADDING: + DIRTY_SPACE(PAD(node.totlen)); + ofs += PAD(node.totlen); + break; + default: switch (node.nodetype & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_ROCOMPAT: @@ -354,7 +382,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo return -EROFS; DIRTY_SPACE(PAD(node.totlen)); ofs += PAD(node.totlen); - continue; + break; case JFFS2_FEATURE_INCOMPAT: printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); @@ -600,7 +628,7 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc if (!jeb->used_size) { D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n", jeb->offset, raw->flash_offset & ~3)); - ri.nodetype &= ~JFFS2_NODE_ACCURATE; + ri.nodetype &= ~JFFS2_NODE_ACCURATE; /* Perhaps we could also mark it as such on the medium. Maybe later */ } break; @@ -626,7 +654,11 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc tn->version = ri.version; USED_SPACE(PAD(ri.totlen)); - jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes); + + /* No need to scan from the beginning of the list again. + We can start from tn_list instead (Thanks Jocke) */ + jffs2_add_tn_to_list(tn, tn_list); + /* Make sure the one we just added is the _last_ in the list with this version number, so the older ones get obsoleted */ while (tn->next && tn->next->version == tn->version) { @@ -789,22 +821,84 @@ static void rotate_list(struct list_head *head, uint32_t count) list_add(head, n); } -static void jffs2_rotate_lists(struct jffs2_sb_info *c) +void jffs2_rotate_lists(struct jffs2_sb_info *c) { uint32_t x; + uint32_t rotateby; x = count_list(&c->clean_list); - if (x) - rotate_list((&c->clean_list), pseudo_random % x); + if (x) { + rotateby = pseudo_random % x; + D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby)); + + rotate_list((&c->clean_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n", + list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty clean_list\n")); + } + + x = count_list(&c->very_dirty_list); + if (x) { + rotateby = pseudo_random % x; + D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby)); + + rotate_list((&c->very_dirty_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n", + list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n")); + } x = count_list(&c->dirty_list); - if (x) - rotate_list((&c->dirty_list), pseudo_random % x); + if (x) { + rotateby = pseudo_random % x; + D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby)); - if (c->nr_erasing_blocks) - rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks); + rotate_list((&c->dirty_list), rotateby); - if (c->nr_free_blocks) /* Not that it should ever be zero */ - rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks); + D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n", + list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n")); + } + + x = count_list(&c->erasable_list); + if (x) { + rotateby = pseudo_random % x; + D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby)); + + rotate_list((&c->erasable_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n", + list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n")); + } + if (c->nr_erasing_blocks) { + rotateby = pseudo_random % c->nr_erasing_blocks; + D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby)); + + rotate_list((&c->erase_pending_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n", + list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n")); + } + + if (c->nr_free_blocks) { + rotateby = pseudo_random % c->nr_free_blocks; + D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby)); + + rotate_list((&c->free_list), rotateby); + + D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n", + list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset)); + } else { + D1(printk(KERN_DEBUG "Not rotating empty free_list\n")); + } } diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 90fe049f2fe4..95fa3bed8fa8 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.64 2002/03/17 10:18:42 dwmw2 Exp $ + * $Id: super.c,v 1.71 2002/07/23 12:57:38 dwmw2 Exp $ * */ @@ -19,12 +19,12 @@ #include <linux/init.h> #include <linux/list.h> #include <linux/fs.h> -#include <linux/namei.h> #include <linux/jffs2.h> #include <linux/pagemap.h> #include <linux/mtd/mtd.h> #include <linux/interrupt.h> #include <linux/ctype.h> +#include <linux/namei.h> #include "nodelist.h" void jffs2_put_super (struct super_block *); @@ -258,10 +258,15 @@ void jffs2_put_super (struct super_block *sb) if (!(sb->s_flags & MS_RDONLY)) jffs2_stop_garbage_collect_thread(c); + down(&c->alloc_sem); jffs2_flush_wbuf(c, 1); + up(&c->alloc_sem); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); kfree(c->blocks); + if (c->wbuf) + kfree(c->wbuf); + kfree(c->inocache_list); if (c->mtd->sync) c->mtd->sync(c->mtd); @@ -302,18 +307,25 @@ static int __init init_jffs2_fs(void) ret = jffs2_zlib_init(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); - return ret; + goto out; } ret = jffs2_create_slab_caches(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); - return ret; + goto out_zlib; } ret = register_filesystem(&jffs2_fs_type); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); - jffs2_destroy_slab_caches(); + goto out_slab; } + return 0; + + out_slab: + jffs2_destroy_slab_caches(); + out_zlib: + jffs2_zlib_exit(); + out: return ret; } diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index d42c878cc483..a242044c5763 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -7,8 +7,8 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.7 2002/03/08 11:27:59 dwmw2 Exp $ - * + * $Id: wbuf.c,v 1.12 2002/05/20 14:56:39 dwmw2 Exp $ + * -- with the NAND definitions added back pending MTD update for 2.5. */ #include <linux/kernel.h> @@ -27,25 +27,100 @@ #define NAND_JFFS2_OOB8_FSDALEN 2 #define NAND_JFFS2_OOB16_FSDALEN 8 + +/* max. erase failures before we mark a block bad */ #define MAX_ERASE_FAILURES 5 +/* two seconds timeout for timed wbuf-flushing */ +#define WBUF_FLUSH_TIMEOUT 2 * HZ + static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) { struct list_head *this, *next; + static int n; if (list_empty(&c->erasable_pending_wbuf_list)) return; list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset)); list_del(this); - list_add_tail(this, &c->erasable_list); + if ((jiffies + (n++)) & 127) { + /* Most of the time, we just erase it immediately. Otherwise we + spend ages scanning it on mount, etc. */ + D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); + list_add_tail(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); + } else { + /* Sometimes, however, we leave it elsewhere so it doesn't get + immediately reused, and we spread the load a bit. */ + D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); + list_add_tail(&jeb->list, &c->erasable_list); + } } } +/* +* Timed flushing of wbuf. If we have no consecutive write to wbuf, within +* the specified time, we flush the contents with padding ! +*/ +void jffs2_wbuf_timeout (unsigned long data) +{ + struct jffs2_sb_info *c = (struct jffs2_sb_info *) data; + /* + * Wake up the flush process, we need process context to have the right + * to sleep on flash write + */ + D1(printk(KERN_DEBUG "jffs2_wbuf_timeout(): timer expired\n")); + schedule_task(&c->wbuf_task); +} + +/* +* Process for timed wbuf flush +* +* FIXME What happens, if we have a write failure there ???? +*/ +void jffs2_wbuf_process (void *data) +{ + struct jffs2_sb_info *c = (struct jffs2_sb_info *) data; + + D1(printk(KERN_DEBUG "jffs2_wbuf_process() entered\n")); + + if (!down_trylock(&c->alloc_sem)) { + D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem got\n")); + + if(!c->nextblock || (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len))) + jffs2_flush_wbuf(c, 1); /* pad only */ + else + jffs2_flush_wbuf(c, 2); /* pad and adjust nextblock */ + up(&c->alloc_sem); + } else { + D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem already occupied\n")); + } +} + + +/* Meaning of pad argument: + 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. + 1: Pad, do not adjust nextblock free_size + 2: Pad, adjust nextblock free_size +*/ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) { int ret; size_t retlen; + + if (!down_trylock(&c->alloc_sem)) { + up(&c->alloc_sem); + printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); + BUG(); + } + + /* delete a eventually started timed wbuf flush */ + del_timer_sync(&c->wbuf_timer); if(!c->wbuf || !c->wbuf_len) return 0; @@ -315,6 +390,11 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi alldone: *retlen = donelen; + /* Setup timed wbuf flush, if buffer len != 0 */ + if (c->wbuf_len) { + D1(printk (KERN_DEBUG "jffs2_flash_writev: mod wbuf_timer\n")); + mod_timer(&c->wbuf_timer, jiffies + WBUF_FLUSH_TIMEOUT); + } return 0; } diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 45c3786b29be..f57e1e1d3fb8 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.52 2002/03/08 11:01:43 dwmw2 Exp $ + * $Id: write.c,v 1.56 2002/07/10 14:05:16 dwmw2 Exp $ * */ @@ -65,7 +65,7 @@ static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) ret = 1; } if (ret) { - printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs); + printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs); printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ofs, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], @@ -362,8 +362,10 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str */ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL); D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); - if (ret) + if (ret) { + up(&f->sem); return ret; + } ri->data_crc = 0; ri->node_crc = crc32(0, ri, sizeof(*ri)-8); @@ -448,7 +450,8 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str } -int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f) +int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, + const char *name, int namelen, struct jffs2_inode_info *dead_f) { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; @@ -498,7 +501,10 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, con jffs2_complete_reservation(c); up(&dir_f->sem); - if (dead_f) { /* Null if this was a rename not a real unlink */ + /* dead_f is NULL if this was a rename not a real unlink */ + /* Also catch the !f->inocache case, where there was a dirent + pointing to an inode which didn't exist. */ + if (dead_f && dead_f->inocache) { down(&dead_f->sem); diff --git a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h index 129a4c31dee1..a215d4e88098 100644 --- a/include/linux/jffs2_fs_sb.h +++ b/include/linux/jffs2_fs_sb.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.26 2002/03/17 10:18:42 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.31 2002/07/02 22:48:24 dwmw2 Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -9,8 +9,6 @@ #include <asm/semaphore.h> #include <linux/list.h> -#define INOCACHE_HASHSIZE 14 - #define JFFS2_SB_FLAG_RO 1 #define JFFS2_SB_FLAG_MOUNTING 2 @@ -33,6 +31,9 @@ struct jffs2_sb_info { out-of-order writing of nodes. And GC. */ + uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER + (i.e. zero for OOB CLEANMARKER */ + uint32_t flash_size; uint32_t used_size; uint32_t dirty_size; @@ -52,6 +53,7 @@ struct jffs2_sb_info { struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ struct list_head clean_list; /* Blocks 100% full of clean data */ + struct list_head very_dirty_list; /* Blocks with lots of dirty space */ struct list_head dirty_list; /* Blocks with some dirty space */ struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ @@ -66,10 +68,8 @@ struct jffs2_sb_info { against erase completion handler */ wait_queue_head_t erase_wait; /* For waiting for erases to complete */ - struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE]; + struct jffs2_inode_cache **inocache_list; spinlock_t inocache_lock; - /* This _really_ speeds up mounts. */ - struct jffs2_inode_cache *inocache_last; /* Sem to allow jffs2_garbage_collect_deletion_dirent to drop the erase_completion_lock while it's holding a pointer @@ -81,6 +81,8 @@ struct jffs2_sb_info { uint32_t wbuf_ofs; uint32_t wbuf_len; uint32_t wbuf_pagesize; + struct tq_struct wbuf_task; /* task for timed wbuf flush */ + struct timer_list wbuf_timer; /* timer for flushing wbuf */ /* OS-private pointer for getting back to master superblock info */ void *os_priv; |
