Commit e2aebe41 authored by David Woodhouse's avatar David Woodhouse

JFFS2 update.

Fix rename-over-existing-file behaviour to actually reduce nlink of the victim, and disallow rename over a non-empty directory.
Improved NAND support, some optimisations and other bug fixes.
parent 390fdff5
$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.
......@@ -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;
}
......@@ -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);
......
......@@ -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);
......
......@@ -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 */
......
......@@ -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;
......
......@@ -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;
......
......@@ -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);
/* Jrn -- 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;
}
......@@ -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;
......
......@@ -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)
......
......@@ -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);
......
......@@ -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 */
......@@ -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 */
......
......@@ -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;
}
......
......@@ -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"));
}
}
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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);
......
/* $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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment