Commit 4a98f95f authored by David Woodhouse's avatar David Woodhouse

JFFS2 update; completed support for NAND flash.

 - Implement write-buffer flushing by garbage collection instead of padding.
 - Implement selective write-buffer flushing on fsync(). 
 - Implement error recovery on write-buffer flush.
 - Fix remove_suid(). Writing to a suid file didn't previously mark the file non-suid.
 - Fix handling of full file systems, to avoid unlink() returning -ENOSPC.
 - Fix assorted memory leaks.
 - Improve garbage collection efficiency by merging fewer pages.
parent 606e1044
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: background.c,v 1.38 2003/05/26 09:50:38 dwmw2 Exp $ * $Id: background.c,v 1.44 2003/10/08 13:29:55 dwmw2 Exp $
* *
*/ */
...@@ -61,13 +61,6 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) ...@@ -61,13 +61,6 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
void jffs2_stop_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_work();
}
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
if (c->gc_task) { if (c->gc_task) {
D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
...@@ -125,6 +118,7 @@ static int jffs2_garbage_collect_thread(void *_c) ...@@ -125,6 +118,7 @@ static int jffs2_garbage_collect_thread(void *_c)
case SIGKILL: case SIGKILL:
D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
die:
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
c->gc_task = NULL; c->gc_task = NULL;
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
...@@ -144,7 +138,10 @@ static int jffs2_garbage_collect_thread(void *_c) ...@@ -144,7 +138,10 @@ static int jffs2_garbage_collect_thread(void *_c)
spin_unlock_irq(&current_sig_lock); spin_unlock_irq(&current_sig_lock);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
jffs2_garbage_collect_pass(c); if (jffs2_garbage_collect_pass(c) == -ENOSPC) {
printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n");
goto die;
}
} }
} }
...@@ -169,8 +166,8 @@ static int thread_should_wake(struct jffs2_sb_info *c) ...@@ -169,8 +166,8 @@ static int thread_should_wake(struct jffs2_sb_info *c)
*/ */
dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER && if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
(dirty > c->sector_size)) (dirty > c->nospc_dirty_size))
ret = 1; ret = 1;
D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: build.c,v 1.46 2003/04/29 17:12:26 gleixner Exp $ * $Id: build.c,v 1.52 2003/10/09 00:38:38 dwmw2 Exp $
* *
*/ */
...@@ -228,6 +228,58 @@ int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inod ...@@ -228,6 +228,58 @@ int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inod
return ret; return ret;
} }
static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
{
uint32_t size;
/* Deletion should almost _always_ be allowed. We're fairly
buggered once we stop allowing people to delete stuff
because there's not enough free space... */
c->resv_blocks_deletion = 2;
/* Be conservative about how much space we need before we allow writes.
On top of that which is required for deletia, require an extra 2%
of the medium to be available, for overhead caused by nodes being
split across blocks, etc. */
size = c->flash_size / 50; /* 2% of flash size */
size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */
size += c->sector_size - 1; /* ... and round up */
c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size);
/* When do we let the GC thread run in the background */
c->resv_blocks_gctrigger = c->resv_blocks_write + 1;
/* When do we allow garbage collection to merge nodes to make
long-term progress at the expense of short-term space exhaustion? */
c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1;
/* When do we allow garbage collection to eat from bad blocks rather
than actually making progress? */
c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
/* If there's less than this amount of dirty space, don't bother
trying to GC to make more space. It'll be a fruitless task */
c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks));
D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n",
c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024));
D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n",
c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024));
D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n",
c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024));
D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n",
c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024));
D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n",
c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024));
D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n",
c->nospc_dirty_size));
}
int jffs2_do_mount_fs(struct jffs2_sb_info *c) int jffs2_do_mount_fs(struct jffs2_sb_info *c)
{ {
int i; int i;
...@@ -276,5 +328,8 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) ...@@ -276,5 +328,8 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
kfree(c->blocks); kfree(c->blocks);
return -EIO; return -EIO;
} }
jffs2_calc_trigger_levels(c);
return 0; return 0;
} }
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by Arjan van de Ven <arjanv@redhat.com> * Created by Arjan van de Ven <arjanv@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: compr.c,v 1.26 2003/01/12 13:21:28 dwmw2 Exp $ * $Id: compr.c,v 1.27 2003/10/04 08:33:06 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by Arjan van de Ven <arjanv@redhat.com> * Created by Arjan van de Ven <arjanv@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: compr_rtime.c,v 1.10 2003/05/11 10:47:13 dwmw2 Exp $ * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $
* *
* *
* Very simple lz77-ish encoder. * Very simple lz77-ish encoder.
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: compr_zlib.c,v 1.23 2003/05/26 09:15:19 dwmw2 Exp $ * $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: dir.c,v 1.77 2003/06/05 14:42:24 dwmw2 Exp $ * $Id: dir.c,v 1.82 2003/10/11 11:47:23 dwmw2 Exp $
* *
*/ */
...@@ -22,18 +22,22 @@ ...@@ -22,18 +22,22 @@
#include <linux/time.h> #include <linux/time.h>
#include "nodelist.h" #include "nodelist.h"
/* Urgh. Please tell me there's a nicer way of doing this. */ /* Urgh. Please tell me there's a nicer way of doing these. */
#include <linux/version.h> #include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
typedef int mknod_arg_t; typedef int mknod_arg_t;
#define NAMEI_COMPAT(x) ((void *)x)
#else #else
typedef dev_t mknod_arg_t; typedef dev_t mknod_arg_t;
#define NAMEI_COMPAT(x) (x)
#endif #endif
static int jffs2_readdir (struct file *, void *, filldir_t); static int jffs2_readdir (struct file *, void *, filldir_t);
static int jffs2_create (struct inode *,struct dentry *,int, struct nameidata *); static int jffs2_create (struct inode *,struct dentry *,int,
static struct dentry *jffs2_lookup (struct inode *,struct dentry *, struct nameidata *); struct nameidata *);
static struct dentry *jffs2_lookup (struct inode *,struct dentry *,
struct nameidata *);
static int jffs2_link (struct dentry *,struct inode *,struct dentry *); static int jffs2_link (struct dentry *,struct inode *,struct dentry *);
static int jffs2_unlink (struct inode *,struct dentry *); static int jffs2_unlink (struct inode *,struct dentry *);
static int jffs2_symlink (struct inode *,struct dentry *,const char *); static int jffs2_symlink (struct inode *,struct dentry *,const char *);
...@@ -54,8 +58,8 @@ struct file_operations jffs2_dir_operations = ...@@ -54,8 +58,8 @@ struct file_operations jffs2_dir_operations =
struct inode_operations jffs2_dir_inode_operations = struct inode_operations jffs2_dir_inode_operations =
{ {
.create = jffs2_create, .create = NAMEI_COMPAT(jffs2_create),
.lookup = jffs2_lookup, .lookup = NAMEI_COMPAT(jffs2_lookup),
.link = jffs2_link, .link = jffs2_link,
.unlink = jffs2_unlink, .unlink = jffs2_unlink,
.symlink = jffs2_symlink, .symlink = jffs2_symlink,
...@@ -73,7 +77,8 @@ struct inode_operations jffs2_dir_inode_operations = ...@@ -73,7 +77,8 @@ struct inode_operations jffs2_dir_inode_operations =
and we use the same hash function as the dentries. Makes this and we use the same hash function as the dentries. Makes this
nice and simple nice and simple
*/ */
static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, struct nameidata *nd) static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
struct nameidata *nd)
{ {
struct jffs2_inode_info *dir_f; struct jffs2_inode_info *dir_f;
struct jffs2_sb_info *c; struct jffs2_sb_info *c;
...@@ -176,7 +181,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -176,7 +181,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
struct nameidata *nd) struct nameidata *nd)
{ {
struct jffs2_raw_inode *ri; struct jffs2_raw_inode *ri;
struct jffs2_inode_info *f, *dir_f; struct jffs2_inode_info *f, *dir_f;
...@@ -292,7 +297,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char ...@@ -292,7 +297,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
struct jffs2_full_dirent *fd; struct jffs2_full_dirent *fd;
int namelen; int namelen;
uint32_t alloclen, phys_ofs; uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret; int ret;
/* FIXME: If you care. We'd need to use frags for the target /* FIXME: If you care. We'd need to use frags for the target
...@@ -339,7 +343,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char ...@@ -339,7 +343,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target))); ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, &writtenlen); fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
jffs2_free_raw_inode(ri); jffs2_free_raw_inode(ri);
...@@ -356,20 +360,12 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char ...@@ -356,20 +360,12 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
f->metadata = fn; f->metadata = fn;
up(&f->sem); up(&f->sem);
/* Work out where to put the dirent node now. */ jffs2_complete_reservation(c);
writtenlen = PAD(writtenlen); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
phys_ofs += writtenlen; if (ret) {
alloclen -= writtenlen; /* Eep. */
jffs2_clear_inode(inode);
if (alloclen < sizeof(*rd)+namelen) { return ret;
/* Not enough space left in this chunk. Get some more */
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
/* Eep. */
jffs2_clear_inode(inode);
return ret;
}
} }
rd = jffs2_alloc_raw_dirent(); rd = jffs2_alloc_raw_dirent();
...@@ -397,7 +393,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char ...@@ -397,7 +393,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
if (IS_ERR(fd)) { if (IS_ERR(fd)) {
/* dirent failed to write. Delete the inode normally /* dirent failed to write. Delete the inode normally
...@@ -436,7 +432,6 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) ...@@ -436,7 +432,6 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
struct jffs2_full_dirent *fd; struct jffs2_full_dirent *fd;
int namelen; int namelen;
uint32_t alloclen, phys_ofs; uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret; int ret;
mode |= S_IFDIR; mode |= S_IFDIR;
...@@ -476,7 +471,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) ...@@ -476,7 +471,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
ri->data_crc = cpu_to_je32(0); ri->data_crc = cpu_to_je32(0);
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen); fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
jffs2_free_raw_inode(ri); jffs2_free_raw_inode(ri);
...@@ -493,20 +488,12 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) ...@@ -493,20 +488,12 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
f->metadata = fn; f->metadata = fn;
up(&f->sem); up(&f->sem);
/* Work out where to put the dirent node now. */ jffs2_complete_reservation(c);
writtenlen = PAD(writtenlen); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
phys_ofs += writtenlen; if (ret) {
alloclen -= writtenlen; /* Eep. */
jffs2_clear_inode(inode);
if (alloclen < sizeof(*rd)+namelen) { return ret;
/* Not enough space left in this chunk. Get some more */
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
/* Eep. */
jffs2_clear_inode(inode);
return ret;
}
} }
rd = jffs2_alloc_raw_dirent(); rd = jffs2_alloc_raw_dirent();
...@@ -534,7 +521,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) ...@@ -534,7 +521,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
if (IS_ERR(fd)) { if (IS_ERR(fd)) {
/* dirent failed to write. Delete the inode normally /* dirent failed to write. Delete the inode normally
...@@ -591,7 +578,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mk ...@@ -591,7 +578,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mk
jint16_t dev; jint16_t dev;
int devlen = 0; int devlen = 0;
uint32_t alloclen, phys_ofs; uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret; int ret;
if (!old_valid_dev(rdev)) if (!old_valid_dev(rdev))
...@@ -639,7 +625,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mk ...@@ -639,7 +625,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mk
ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen)); ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, &writtenlen); fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL);
jffs2_free_raw_inode(ri); jffs2_free_raw_inode(ri);
...@@ -656,20 +642,12 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mk ...@@ -656,20 +642,12 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mk
f->metadata = fn; f->metadata = fn;
up(&f->sem); up(&f->sem);
/* Work out where to put the dirent node now. */ jffs2_complete_reservation(c);
writtenlen = PAD(writtenlen); ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
phys_ofs += writtenlen; if (ret) {
alloclen -= writtenlen; /* Eep. */
jffs2_clear_inode(inode);
if (alloclen < sizeof(*rd)+namelen) { return ret;
/* Not enough space left in this chunk. Get some more */
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
/* Eep. */
jffs2_clear_inode(inode);
return ret;
}
} }
rd = jffs2_alloc_raw_dirent(); rd = jffs2_alloc_raw_dirent();
...@@ -700,7 +678,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mk ...@@ -700,7 +678,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mk
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
if (IS_ERR(fd)) { if (IS_ERR(fd)) {
/* dirent failed to write. Delete the inode normally /* dirent failed to write. Delete the inode normally
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: erase.c,v 1.51 2003/05/11 22:47:36 dwmw2 Exp $ * $Id: erase.c,v 1.53 2003/10/08 17:22:54 dwmw2 Exp $
* *
*/ */
...@@ -123,10 +123,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) ...@@ -123,10 +123,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
list_del(&jeb->list); list_del(&jeb->list);
c->erasing_size += c->sector_size; c->erasing_size += c->sector_size;
c->wasted_size -= jeb->wasted_size;
c->free_size -= jeb->free_size; c->free_size -= jeb->free_size;
c->used_size -= jeb->used_size; c->used_size -= jeb->used_size;
c->dirty_size -= jeb->dirty_size; c->dirty_size -= jeb->dirty_size;
jeb->used_size = jeb->dirty_size = jeb->free_size = 0; jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
jffs2_free_all_node_refs(c, jeb); jffs2_free_all_node_refs(c, jeb);
list_add(&jeb->list, &c->erasing_list); list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: file.c,v 1.85 2003/05/26 09:50:38 dwmw2 Exp $ * $Id: file.c,v 1.96 2003/10/11 11:47:23 dwmw2 Exp $
* *
*/ */
...@@ -29,24 +29,10 @@ extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) ...@@ -29,24 +29,10 @@ extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
if (!c->wbuf || !c->wbuf_len)
return 0;
/* flush write buffer and update c->nextblock */ /* Trigger GC to flush any pending writes for this inode */
jffs2_flush_wbuf_gc(c, inode->i_ino);
/* FIXME NAND */
/* At the moment we flush the buffer, to make sure
* that every thing is on the flash.
* 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; return 0;
} }
...@@ -79,151 +65,6 @@ struct address_space_operations jffs2_file_address_operations = ...@@ -79,151 +65,6 @@ struct address_space_operations jffs2_file_address_operations =
.commit_write = jffs2_commit_write .commit_write = jffs2_commit_write
}; };
int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
{
struct jffs2_full_dnode *old_metadata, *new_metadata;
struct inode *inode = dentry->d_inode;
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;
unsigned short dev;
unsigned char *mdata = NULL;
int mdatalen = 0;
unsigned int ivalid;
uint32_t phys_ofs, alloclen;
int ret;
D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
ret = inode_change_ok(inode, iattr);
if (ret)
return ret;
/* Special cases - we don't want more than one data node
for these types on the medium at any time. So setattr
must read the original data associated with the node
(i.e. the device numbers or the target name) and write
it out again with the appropriate data attached */
if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
/* For these, we don't actually need to read the old node */
dev = old_encode_dev(dentry->d_inode->i_rdev);
mdata = (char *)&dev;
mdatalen = sizeof(dev);
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
} else if (S_ISLNK(inode->i_mode)) {
mdatalen = f->metadata->size;
mdata = kmalloc(f->metadata->size, GFP_USER);
if (!mdata)
return -ENOMEM;
ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
if (ret) {
kfree(mdata);
return ret;
}
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
}
ri = jffs2_alloc_raw_inode();
if (!ri) {
if (S_ISLNK(inode->i_mode))
kfree(mdata);
return -ENOMEM;
}
ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
jffs2_free_raw_inode(ri);
if (S_ISLNK(inode->i_mode & S_IFMT))
kfree(mdata);
return ret;
}
down(&f->sem);
ivalid = iattr->ia_valid;
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen);
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
ri->ino = cpu_to_je32(inode->i_ino);
ri->version = cpu_to_je32(++f->highest_version);
ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid);
ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid);
if (ivalid & ATTR_MODE)
if (iattr->ia_mode & S_ISGID &&
!in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID))
ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID);
else
ri->mode = cpu_to_jemode(iattr->ia_mode);
else
ri->mode = cpu_to_jemode(inode->i_mode);
ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size);
ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime));
ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
ri->offset = cpu_to_je32(0);
ri->csize = ri->dsize = cpu_to_je32(mdatalen);
ri->compr = JFFS2_COMPR_NONE;
if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
/* It's an extension. Make it a hole node */
ri->compr = JFFS2_COMPR_ZERO;
ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size);
ri->offset = cpu_to_je32(inode->i_size);
}
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
if (mdatalen)
ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
else
ri->data_crc = cpu_to_je32(0);
new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, NULL);
if (S_ISLNK(inode->i_mode))
kfree(mdata);
if (IS_ERR(new_metadata)) {
jffs2_complete_reservation(c);
jffs2_free_raw_inode(ri);
up(&f->sem);
return PTR_ERR(new_metadata);
}
/* It worked. Update the inode */
inode->i_atime = ITIME(je32_to_cpu(ri->atime));
inode->i_ctime = ITIME(je32_to_cpu(ri->ctime));
inode->i_mtime = ITIME(je32_to_cpu(ri->mtime));
inode->i_mode = jemode_to_cpu(ri->mode);
inode->i_uid = je16_to_cpu(ri->uid);
inode->i_gid = je16_to_cpu(ri->gid);
old_metadata = f->metadata;
if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) {
vmtruncate(inode, iattr->ia_size);
jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
}
if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
jffs2_add_full_dnode_to_inode(c, f, new_metadata);
inode->i_size = iattr->ia_size;
f->metadata = NULL;
} else {
f->metadata = new_metadata;
}
if (old_metadata) {
jffs2_mark_node_obsolete(c, old_metadata->raw);
jffs2_free_full_dnode(old_metadata);
}
jffs2_free_raw_inode(ri);
up(&f->sem);
jffs2_complete_reservation(c);
return 0;
}
int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
{ {
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
...@@ -320,7 +161,7 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns ...@@ -320,7 +161,7 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
ri.data_crc = cpu_to_je32(0); ri.data_crc = cpu_to_je32(0);
fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL); fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
if (IS_ERR(fn)) { if (IS_ERR(fn)) {
ret = PTR_ERR(fn); ret = PTR_ERR(fn);
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: fs.c,v 1.24 2003/04/29 09:52:58 dwmw2 Exp $ * $Id: fs.c,v 1.32 2003/10/11 11:47:23 dwmw2 Exp $
* *
*/ */
...@@ -21,8 +21,159 @@ ...@@ -21,8 +21,159 @@
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <linux/crc32.h>
#include "nodelist.h" #include "nodelist.h"
static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
{
struct jffs2_full_dnode *old_metadata, *new_metadata;
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;
unsigned short dev;
unsigned char *mdata = NULL;
int mdatalen = 0;
unsigned int ivalid;
uint32_t phys_ofs, alloclen;
int ret;
D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
ret = inode_change_ok(inode, iattr);
if (ret)
return ret;
/* Special cases - we don't want more than one data node
for these types on the medium at any time. So setattr
must read the original data associated with the node
(i.e. the device numbers or the target name) and write
it out again with the appropriate data attached */
if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
/* For these, we don't actually need to read the old node */
dev = old_encode_dev(inode->i_rdev);
mdata = (char *)&dev;
mdatalen = sizeof(dev);
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
} else if (S_ISLNK(inode->i_mode)) {
mdatalen = f->metadata->size;
mdata = kmalloc(f->metadata->size, GFP_USER);
if (!mdata)
return -ENOMEM;
ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
if (ret) {
kfree(mdata);
return ret;
}
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
}
ri = jffs2_alloc_raw_inode();
if (!ri) {
if (S_ISLNK(inode->i_mode))
kfree(mdata);
return -ENOMEM;
}
ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
jffs2_free_raw_inode(ri);
if (S_ISLNK(inode->i_mode & S_IFMT))
kfree(mdata);
return ret;
}
down(&f->sem);
ivalid = iattr->ia_valid;
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen);
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
ri->ino = cpu_to_je32(inode->i_ino);
ri->version = cpu_to_je32(++f->highest_version);
ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid);
ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid);
if (ivalid & ATTR_MODE)
if (iattr->ia_mode & S_ISGID &&
!in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID))
ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID);
else
ri->mode = cpu_to_jemode(iattr->ia_mode);
else
ri->mode = cpu_to_jemode(inode->i_mode);
ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size);
ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime));
ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
ri->offset = cpu_to_je32(0);
ri->csize = ri->dsize = cpu_to_je32(mdatalen);
ri->compr = JFFS2_COMPR_NONE;
if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
/* It's an extension. Make it a hole node */
ri->compr = JFFS2_COMPR_ZERO;
ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size);
ri->offset = cpu_to_je32(inode->i_size);
}
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
if (mdatalen)
ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
else
ri->data_crc = cpu_to_je32(0);
new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL);
if (S_ISLNK(inode->i_mode))
kfree(mdata);
if (IS_ERR(new_metadata)) {
jffs2_complete_reservation(c);
jffs2_free_raw_inode(ri);
up(&f->sem);
return PTR_ERR(new_metadata);
}
/* It worked. Update the inode */
inode->i_atime = ITIME(je32_to_cpu(ri->atime));
inode->i_ctime = ITIME(je32_to_cpu(ri->ctime));
inode->i_mtime = ITIME(je32_to_cpu(ri->mtime));
inode->i_mode = jemode_to_cpu(ri->mode);
inode->i_uid = je16_to_cpu(ri->uid);
inode->i_gid = je16_to_cpu(ri->gid);
old_metadata = f->metadata;
if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) {
vmtruncate(inode, iattr->ia_size);
jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
}
if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
jffs2_add_full_dnode_to_inode(c, f, new_metadata);
inode->i_size = iattr->ia_size;
f->metadata = NULL;
} else {
f->metadata = new_metadata;
}
if (old_metadata) {
jffs2_mark_node_obsolete(c, old_metadata->raw);
jffs2_free_full_dnode(old_metadata);
}
jffs2_free_raw_inode(ri);
up(&f->sem);
jffs2_complete_reservation(c);
return 0;
}
int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
{
return jffs2_do_setattr(dentry->d_inode, iattr);
}
int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
{ {
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
...@@ -38,8 +189,8 @@ int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) ...@@ -38,8 +189,8 @@ int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
avail = c->dirty_size + c->free_size; avail = c->dirty_size + c->free_size;
if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE) if (avail > c->sector_size * c->resv_blocks_write)
avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE; avail -= c->sector_size * c->resv_blocks_write;
else else
avail = 0; avail = 0;
...@@ -149,7 +300,7 @@ void jffs2_read_inode (struct inode *inode) ...@@ -149,7 +300,7 @@ void jffs2_read_inode (struct inode *inode)
case S_IFIFO: case S_IFIFO:
inode->i_op = &jffs2_file_inode_operations; inode->i_op = &jffs2_file_inode_operations;
init_special_inode(inode, inode->i_mode, init_special_inode(inode, inode->i_mode,
old_decode_dev(je16_to_cpu(rdev))); old_decode_dev((je16_to_cpu(rdev))));
break; break;
default: default:
...@@ -161,6 +312,27 @@ void jffs2_read_inode (struct inode *inode) ...@@ -161,6 +312,27 @@ void jffs2_read_inode (struct inode *inode)
D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
} }
void jffs2_dirty_inode(struct inode *inode)
{
struct iattr iattr;
if (!(inode->i_state & I_DIRTY_DATASYNC)) {
D1(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
return;
}
D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino));
iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME;
iattr.ia_mode = inode->i_mode;
iattr.ia_uid = inode->i_uid;
iattr.ia_gid = inode->i_gid;
iattr.ia_atime = inode->i_atime;
iattr.ia_mtime = inode->i_mtime;
iattr.ia_ctime = inode->i_ctime;
jffs2_do_setattr(inode, &iattr);
}
int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
{ {
...@@ -194,6 +366,7 @@ void jffs2_write_super (struct super_block *sb) ...@@ -194,6 +366,7 @@ void jffs2_write_super (struct super_block *sb)
D1(printk(KERN_DEBUG "jffs2_write_super()\n")); D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
jffs2_garbage_collect_trigger(c); jffs2_garbage_collect_trigger(c);
jffs2_erase_pending_blocks(c); jffs2_erase_pending_blocks(c);
jffs2_flush_wbuf_gc(c, 0);
} }
...@@ -288,25 +461,10 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) ...@@ -288,25 +461,10 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
/* Joern -- stick alignment for weird 8-byte-page flash here */ /* Joern -- stick alignment for weird 8-byte-page flash here */
if (jffs2_cleanmarker_oob(c)) { if (jffs2_cleanmarker_oob(c)) {
/* Cleanmarker is out-of-band, so inline size zero */ /* NAND (or other bizarre) flash... do setup accordingly */
c->cleanmarker_size = 0; ret = jffs2_nand_flash_setup(c);
} if (ret)
return ret;
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)
return -ENOMEM;
/* Initialise process for timed wbuf flush */
INIT_WORK(&c->wbuf_task,(void*) jffs2_wbuf_process, (void *)c);
/* Initialise timer for timed wbuf flush */
init_timer(&c->wbuf_timer);
c->wbuf_timer.function = jffs2_wbuf_timeout;
c->wbuf_timer.data = (unsigned long) c;
} }
c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
...@@ -352,7 +510,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) ...@@ -352,7 +510,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
out_inohash: out_inohash:
kfree(c->inocache_list); kfree(c->inocache_list);
out_wbuf: out_wbuf:
if (c->wbuf) jffs2_nand_flash_cleanup(c);
kfree(c->wbuf);
return ret; return ret;
} }
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: gc.c,v 1.103 2003/05/22 18:01:02 dwmw2 Exp $ * $Id: gc.c,v 1.114 2003/10/09 13:53:35 dwmw2 Exp $
* *
*/ */
...@@ -49,7 +49,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) ...@@ -49,7 +49,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
put the clever wear-levelling algorithms. Eventually. */ put the clever wear-levelling algorithms. Eventually. */
/* We possibly want to favour the dirtier blocks more when the /* We possibly want to favour the dirtier blocks more when the
number of free blocks is low. */ number of free blocks is low. */
if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) { if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
nextlist = &c->bad_used_list; nextlist = &c->bad_used_list;
} else if (n < 50 && !list_empty(&c->erasable_list)) { } else if (n < 50 && !list_empty(&c->erasable_list)) {
...@@ -274,7 +274,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) ...@@ -274,7 +274,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
if (ref_flags(raw) == REF_PRISTINE) if (ref_flags(raw) == REF_PRISTINE)
ic->state = INO_STATE_GC; ic->state = INO_STATE_GC;
else { else {
D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n", D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
inum)); inum));
} }
break; break;
...@@ -493,6 +493,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ...@@ -493,6 +493,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
int ret; int ret;
uint32_t phys_ofs, alloclen; uint32_t phys_ofs, alloclen;
uint32_t crc; uint32_t crc;
int retried = 0;
D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
...@@ -573,13 +574,15 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ...@@ -573,13 +574,15 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
ret = -ENOMEM; ret = -ENOMEM;
goto out_node; goto out_node;
} }
/* OK, all the CRCs are good; this node can just be copied as-is. */
retry:
nraw->flash_offset = phys_ofs; nraw->flash_offset = phys_ofs;
nraw->totlen = raw->totlen; nraw->totlen = raw->totlen;
nraw->next_phys = NULL; nraw->next_phys = NULL;
/* OK, all the CRCs are good; this node can just be copied as-is. */
ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node); ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node);
if (ret || (retlen != raw->totlen)) { if (ret || (retlen != raw->totlen)) {
printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
raw->totlen, phys_ofs, ret, retlen); raw->totlen, phys_ofs, ret, retlen);
...@@ -592,8 +595,34 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ...@@ -592,8 +595,34 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
jffs2_mark_node_obsolete(c, nraw); jffs2_mark_node_obsolete(c, nraw);
} else { } else {
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
jffs2_free_raw_node_ref(raw); jffs2_free_raw_node_ref(nraw);
} }
if (!retried && (nraw == jffs2_alloc_raw_node_ref())) {
/* Try to reallocate space and retry */
uint32_t dummy;
struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
retried = 1;
D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n"));
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
ret = jffs2_reserve_space_gc(c, raw->totlen, &phys_ofs, &dummy);
if (!ret) {
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
goto retry;
}
D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
jffs2_free_raw_node_ref(nraw);
}
if (!ret) if (!ret)
ret = -EIO; ret = -EIO;
goto out_node; goto out_node;
...@@ -684,7 +713,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ ...@@ -684,7 +713,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, NULL); new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC);
if (IS_ERR(new_fn)) { if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
...@@ -728,7 +757,7 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er ...@@ -728,7 +757,7 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
sizeof(rd)+rd.nsize, ret); sizeof(rd)+rd.nsize, ret);
return ret; return ret;
} }
new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, NULL); new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC);
if (IS_ERR(new_fd)) { if (IS_ERR(new_fd)) {
printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd)); printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
...@@ -951,7 +980,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras ...@@ -951,7 +980,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
sizeof(ri), ret); sizeof(ri), ret);
return ret; return ret;
} }
new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL); new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
if (IS_ERR(new_fn)) { if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn)); printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
...@@ -1010,7 +1039,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era ...@@ -1010,7 +1039,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
{ {
struct jffs2_full_dnode *new_fn; struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri; struct jffs2_raw_inode ri;
uint32_t alloclen, phys_ofs, offset, orig_end; uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;
int ret = 0; int ret = 0;
unsigned char *comprbuf = NULL, *writebuf; unsigned char *comprbuf = NULL, *writebuf;
struct page *pg; struct page *pg;
...@@ -1023,29 +1052,129 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era ...@@ -1023,29 +1052,129 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
f->inocache->ino, start, end)); f->inocache->ino, start, end));
orig_end = end; orig_end = end;
orig_start = start;
/* If we're looking at the last node in the block we're if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) {
garbage-collecting, we allow ourselves to merge as if the /* Attempt to do some merging. But only expand to cover logically
block was already erasing. We're likely to be GC'ing a adjacent frags if the block containing them is already considered
partial page, and the next block we GC is likely to have to be dirty. Otherwise we end up with GC just going round in
the other half of this page right at the beginning, which circles dirtying the nodes it already wrote out, especially
means we'd expand it _then_, as nr_erasing_blocks would have on NAND where we have small eraseblocks and hence a much higher
increased since we checked, and in doing so would obsolete chance of nodes having to be split to cross boundaries. */
the partial node which we'd have written here. Meaning that
the GC would churn and churn, and just leave dirty blocks in struct jffs2_node_frag *frag;
it's wake. uint32_t min, max;
*/
if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) { min = start & ~(PAGE_CACHE_SIZE-1);
/* Shitloads of space */ max = min + PAGE_CACHE_SIZE;
/* FIXME: Integrate this properly with GC calculations */
start &= ~(PAGE_CACHE_SIZE-1); frag = jffs2_lookup_node_frag(&f->fragtree, start);
end = min_t(uint32_t, start + PAGE_CACHE_SIZE, JFFS2_F_I_SIZE(f));
D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n", /* BUG_ON(!frag) but that'll happen anyway... */
start, end));
if (end < orig_end) { BUG_ON(frag->ofs != start);
printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
end = orig_end; /* First grow down... */
while((frag = frag_prev(frag)) && frag->ofs >= min) {
/* If the previous frag doesn't even reach the beginning, there's
excessive fragmentation. Just merge. */
if (frag->ofs > min) {
D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
frag->ofs, frag->ofs+frag->size));
start = frag->ofs;
continue;
}
/* OK. This frag holds the first byte of the page. */
if (!frag->node || !frag->node->raw) {
D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
frag->ofs, frag->ofs+frag->size));
break;
} else {
/* OK, it's a frag which extends to the beginning of the page. Does it live
in a block which is still considered clean? If so, don't obsolete it.
If not, cover it anyway. */
struct jffs2_raw_node_ref *raw = frag->node->raw;
struct jffs2_eraseblock *jeb;
jeb = &c->blocks[raw->flash_offset / c->sector_size];
if (jeb == c->gcblock) {
D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
start = frag->ofs;
break;
}
if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
break;
}
D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
start = frag->ofs;
break;
}
}
/* ... then up */
/* Find last frag which is actually part of the node we're to GC. */
frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
/* If the previous frag doesn't even reach the beginning, there's lots
of fragmentation. Just merge. */
if (frag->ofs+frag->size < max) {
D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n",
frag->ofs, frag->ofs+frag->size));
end = frag->ofs + frag->size;
continue;
}
if (!frag->node || !frag->node->raw) {
D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
frag->ofs, frag->ofs+frag->size));
break;
} else {
/* OK, it's a frag which extends to the beginning of the page. Does it live
in a block which is still considered clean? If so, don't obsolete it.
If not, cover it anyway. */
struct jffs2_raw_node_ref *raw = frag->node->raw;
struct jffs2_eraseblock *jeb;
jeb = &c->blocks[raw->flash_offset / c->sector_size];
if (jeb == c->gcblock) {
D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
end = frag->ofs + frag->size;
break;
}
if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
break;
}
D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
frag->ofs, frag->ofs+frag->size, jeb->offset));
end = frag->ofs + frag->size;
break;
}
} }
D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
orig_start, orig_end, start, end));
BUG_ON(end > JFFS2_F_I_SIZE(f));
BUG_ON(end < orig_end);
BUG_ON(start > orig_start);
} }
/* First, use readpage() to read the appropriate page into the page cache */ /* First, use readpage() to read the appropriate page into the page cache */
...@@ -1114,7 +1243,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era ...@@ -1114,7 +1243,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen)); ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen));
new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, NULL); new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, ALLOC_GC);
if (IS_ERR(new_fn)) { if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
......
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
* *
* Copyright (C) 2001 Red Hat, Inc. * Copyright (C) 2001 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: ioctl.c,v 1.6 2002/05/20 14:56:38 dwmw2 Exp $ * $Id: ioctl.c,v 1.7 2003/10/04 08:33:06 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: malloc.c,v 1.24 2003/03/11 17:30:29 gleixner Exp $ * $Id: malloc.c,v 1.25 2003/10/04 08:33:06 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: nodelist.c,v 1.79 2003/04/08 08:20:01 dwmw2 Exp $ * $Id: nodelist.c,v 1.80 2003/10/04 08:33:06 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: nodelist.h,v 1.93 2003/02/24 21:47:28 dwmw2 Exp $ * $Id: nodelist.h,v 1.104 2003/10/08 11:45:11 dwmw2 Exp $
* *
*/ */
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#endif #endif
#ifndef CONFIG_JFFS2_FS_DEBUG #ifndef CONFIG_JFFS2_FS_DEBUG
#define CONFIG_JFFS2_FS_DEBUG 2 #define CONFIG_JFFS2_FS_DEBUG 1
#endif #endif
#if CONFIG_JFFS2_FS_DEBUG > 0 #if CONFIG_JFFS2_FS_DEBUG > 0
...@@ -201,10 +201,11 @@ struct jffs2_eraseblock ...@@ -201,10 +201,11 @@ struct jffs2_eraseblock
}; };
#define ACCT_SANITY_CHECK(c, jeb) do { \ #define ACCT_SANITY_CHECK(c, jeb) do { \
if (jeb->used_size + jeb->dirty_size + jeb->free_size + jeb->wasted_size + jeb->unchecked_size != c->sector_size) { \ struct jffs2_eraseblock *___j = jeb; \
printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \ if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \
printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \
printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
jeb->free_size, jeb->dirty_size, jeb->used_size, jeb->wasted_size, jeb->unchecked_size, c->sector_size); \ ___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \
BUG(); \ BUG(); \
} \ } \
if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \ if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
...@@ -215,15 +216,46 @@ struct jffs2_eraseblock ...@@ -215,15 +216,46 @@ struct jffs2_eraseblock
} \ } \
} while(0) } while(0)
static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
{
struct jffs2_raw_node_ref *ref;
int i=0;
printk(KERN_NOTICE);
for (ref = jeb->first_node; ref; ref = ref->next_phys) {
printk("%08x->", ref_offset(ref));
if (++i == 8) {
i = 0;
printk("\n" KERN_NOTICE);
}
}
printk("\n");
}
#define ACCT_PARANOIA_CHECK(jeb) do { \ #define ACCT_PARANOIA_CHECK(jeb) do { \
uint32_t my_used_size = 0; \ uint32_t my_used_size = 0; \
uint32_t my_unchecked_size = 0; \ uint32_t my_unchecked_size = 0; \
struct jffs2_raw_node_ref *ref2 = jeb->first_node; \ struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
while (ref2) { \ while (ref2) { \
if (unlikely(ref2->flash_offset < jeb->offset || \
ref2->flash_offset > jeb->offset + c->sector_size)) { \
printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \
ref_offset(ref2), jeb->offset); \
paranoia_failed_dump(jeb); \
BUG(); \
} \
if (ref_flags(ref2) == REF_UNCHECKED) \ if (ref_flags(ref2) == REF_UNCHECKED) \
my_unchecked_size += ref2->totlen; \ my_unchecked_size += ref2->totlen; \
else if (!ref_obsolete(ref2)) \ else if (!ref_obsolete(ref2)) \
my_used_size += ref2->totlen; \ my_used_size += ref2->totlen; \
if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
jeb->last_node, ref_offset(jeb->last_node)); \
paranoia_failed_dump(jeb); \
BUG(); \
} \
ref2 = ref2->next_phys; \ ref2 = ref2->next_phys; \
} \ } \
if (my_used_size != jeb->used_size) { \ if (my_used_size != jeb->used_size) { \
...@@ -239,14 +271,7 @@ struct jffs2_eraseblock ...@@ -239,14 +271,7 @@ struct jffs2_eraseblock
#define ALLOC_NORMAL 0 /* Normal allocation */ #define ALLOC_NORMAL 0 /* Normal allocation */
#define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
#define ALLOC_GC 2 /* Space requested for GC. Give it or die */ #define ALLOC_GC 2 /* Space requested for GC. Give it or die */
#define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */
#define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */
#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */
#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE) /* ... allow a normal filesystem deletion */
#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */
#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */
#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 */ /* How much dirty space before it goes on the very_dirty_list */
#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) #define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
...@@ -314,8 +339,9 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c); ...@@ -314,8 +339,9 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c);
/* write.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); int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, uint32_t *writelen);
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, uint32_t *writelen); struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_raw_inode *ri, unsigned char *buf, struct jffs2_raw_inode *ri, unsigned char *buf,
uint32_t offset, uint32_t writelen, uint32_t *retlen); uint32_t offset, uint32_t writelen, uint32_t *retlen);
...@@ -383,7 +409,8 @@ void jffs2_erase_pending_trigger(struct jffs2_sb_info *c); ...@@ -383,7 +409,8 @@ void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
#ifdef CONFIG_JFFS2_FS_NAND #ifdef CONFIG_JFFS2_FS_NAND
/* wbuf.c */ /* wbuf.c */
int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad); int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
int jffs2_check_nand_cleanmarker(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_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: nodemgmt.c,v 1.94 2003/02/19 17:50:26 gleixner Exp $ * $Id: nodemgmt.c,v 1.102 2003/10/08 17:21:19 dwmw2 Exp $
* *
*/ */
...@@ -43,13 +43,10 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui ...@@ -43,13 +43,10 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio) int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
{ {
int ret = -EAGAIN; int ret = -EAGAIN;
int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE; int blocksneeded = c->resv_blocks_write;
/* align it */ /* align it */
minsize = PAD(minsize); minsize = PAD(minsize);
if (prio == ALLOC_DELETION)
blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION;
D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
down(&c->alloc_sem); down(&c->alloc_sem);
...@@ -63,8 +60,6 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs ...@@ -63,8 +60,6 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
int ret; int ret;
uint32_t dirty, avail; uint32_t dirty, avail;
up(&c->alloc_sem);
/* calculate real dirty size /* calculate real dirty size
* dirty_size contains blocks on erase_pending_list * dirty_size contains blocks on erase_pending_list
* those blocks are counted in c->nr_erasing_blocks. * those blocks are counted in c->nr_erasing_blocks.
...@@ -78,10 +73,16 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs ...@@ -78,10 +73,16 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
* of nodes. * of nodes.
*/ */
dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
if (dirty < c->sector_size) { if (dirty < c->nospc_dirty_size) {
D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < sector size 0x%08x, returning -ENOSPC\n", if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
break;
}
D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
dirty, c->unchecked_size, c->sector_size)); dirty, c->unchecked_size, c->sector_size));
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
return -ENOSPC; return -ENOSPC;
} }
...@@ -96,12 +97,20 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs ...@@ -96,12 +97,20 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
*/ */
avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
if ( (avail / c->sector_size) <= blocksneeded) { if ( (avail / c->sector_size) <= blocksneeded) {
if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
break;
}
D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
avail, blocksneeded * c->sector_size)); avail, blocksneeded * c->sector_size));
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
return -ENOSPC; return -ENOSPC;
} }
up(&c->alloc_sem);
D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
...@@ -158,12 +167,13 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui ...@@ -158,12 +167,13 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
if (jeb && minsize > jeb->free_size) { if (jeb && minsize > jeb->free_size) {
/* Skip the end of this block and file it as having some dirty space */ /* Skip the end of this block and file it as having some dirty space */
/* If there's a pending write to it, flush now */ /* If there's a pending write to it, flush now */
if (c->wbuf_len) { if (jffs2_wbuf_dirty(c)) {
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
jffs2_flush_wbuf(c, 1); jffs2_flush_wbuf_pad(c);
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
/* We know nobody's going to have changed nextblock. Just continue */ jeb = c->nextblock;
goto restart;
} }
c->wasted_size += jeb->free_size; c->wasted_size += jeb->free_size;
c->free_size -= jeb->free_size; c->free_size -= jeb->free_size;
...@@ -219,7 +229,7 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui ...@@ -219,7 +229,7 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
/* c->nextblock is NULL, no update to c->nextblock allowed */ /* c->nextblock is NULL, no update to c->nextblock allowed */
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
jffs2_flush_wbuf(c, 1); jffs2_flush_wbuf_pad(c);
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
/* Have another go. It'll be on the erasable_list now */ /* Have another go. It'll be on the erasable_list now */
return -EAGAIN; return -EAGAIN;
...@@ -344,10 +354,10 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r ...@@ -344,10 +354,10 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
/* If it lives on the dirty_list, jffs2_reserve_space will put it there */ /* If it lives on the dirty_list, jffs2_reserve_space will put it there */
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
if (c->wbuf_len) { if (jffs2_wbuf_dirty(c)) {
/* Flush the last write in the block if it's outstanding */ /* Flush the last write in the block if it's outstanding */
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
jffs2_flush_wbuf(c, 1); jffs2_flush_wbuf_pad(c);
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
} }
...@@ -370,6 +380,20 @@ void jffs2_complete_reservation(struct jffs2_sb_info *c) ...@@ -370,6 +380,20 @@ void jffs2_complete_reservation(struct jffs2_sb_info *c)
up(&c->alloc_sem); up(&c->alloc_sem);
} }
static inline int on_list(struct list_head *obj, struct list_head *head)
{
struct list_head *this;
list_for_each(this, head) {
if (this == obj) {
D1(printk("%p is on list at %p\n", obj, head));
return 1;
}
}
return 0;
}
void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref) void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
{ {
struct jffs2_eraseblock *jeb; struct jffs2_eraseblock *jeb;
...@@ -418,11 +442,26 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref ...@@ -418,11 +442,26 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
// Take care, that wasted size is taken into concern // Take care, that wasted size is taken into concern
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) { if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) {
D1(printk("Dirtying\n")); D1(printk("Dirtying\n"));
addedsize = ref->totlen + jeb->wasted_size; addedsize = ref->totlen;
jeb->dirty_size += addedsize; jeb->dirty_size += ref->totlen;
c->dirty_size += addedsize; c->dirty_size += ref->totlen;
c->wasted_size -= jeb->wasted_size;
jeb->wasted_size = 0; /* Convert wasted space to dirty, if not a bad block */
if (jeb->wasted_size) {
if (on_list(&jeb->list, &c->bad_used_list)) {
D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n",
jeb->offset));
addedsize = 0; /* To fool the refiling code later */
} else {
D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n",
jeb->wasted_size, jeb->offset));
addedsize += jeb->wasted_size;
jeb->dirty_size += jeb->wasted_size;
c->dirty_size += jeb->wasted_size;
c->wasted_size -= jeb->wasted_size;
jeb->wasted_size = 0;
}
}
} else { } else {
D1(printk("Wasting\n")); D1(printk("Wasting\n"));
addedsize = 0; addedsize = 0;
...@@ -455,7 +494,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref ...@@ -455,7 +494,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset)); D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
list_del(&jeb->list); list_del(&jeb->list);
} }
if (c->wbuf_len) { if (jffs2_wbuf_dirty(c)) {
D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
#if 0 /* This check was added to allow us to find places where we added nodes to the lists #if 0 /* This check was added to allow us to find places where we added nodes to the lists
...@@ -506,7 +545,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref ...@@ -506,7 +545,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
D1(printk(KERN_DEBUG "...and adding to dirty_list\n")); D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
list_add_tail(&jeb->list, &c->dirty_list); list_add_tail(&jeb->list, &c->dirty_list);
} else if (VERYDIRTY(c, jeb->dirty_size) && } else if (VERYDIRTY(c, jeb->dirty_size) &&
!VERYDIRTY(c, jeb->dirty_size - ref->totlen)) { !VERYDIRTY(c, jeb->dirty_size - addedsize)) {
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset)); D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
list_del(&jeb->list); list_del(&jeb->list);
D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n")); D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
...@@ -569,7 +608,7 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c) ...@@ -569,7 +608,7 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c)
printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
printk(KERN_DEBUG "sector_size: %08x\n", c->sector_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); printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write);
if (c->nextblock) { if (c->nextblock) {
printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
...@@ -672,7 +711,7 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c) ...@@ -672,7 +711,7 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c)
list_for_each(this, &c->erasable_pending_wbuf_list) { list_for_each(this, &c->erasable_pending_wbuf_list) {
struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
printk(KERN_DEBUG "erase_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
} }
} }
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2002 Red Hat, Inc. * Copyright (C) 2002-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: os-linux.h,v 1.26 2003/05/16 18:45:25 dwmw2 Exp $ * $Id: os-linux.h,v 1.37 2003/10/11 11:47:23 dwmw2 Exp $
* *
*/ */
...@@ -19,6 +19,9 @@ ...@@ -19,6 +19,9 @@
#define os_to_jffs2_mode(x) (x) #define os_to_jffs2_mode(x) (x)
#define jffs2_to_os_mode(x) (x) #define jffs2_to_os_mode(x) (x)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
#define kstatfs statfs
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) #define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
...@@ -68,7 +71,7 @@ ...@@ -68,7 +71,7 @@
/* Hmmm. P'raps generic code should only ever see versions of signal /* Hmmm. P'raps generic code should only ever see versions of signal
functions which do the locking automatically? */ functions which do the locking automatically? */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40) && !defined(__rh_config_h__)
#define current_sig_lock current->sigmask_lock #define current_sig_lock current->sigmask_lock
#else #else
#define current_sig_lock current->sighand->siglock #define current_sig_lock current->sighand->siglock
...@@ -108,10 +111,14 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) ...@@ -108,10 +111,14 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flush_wbuf(c, flag) do { ; } while(0) #define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0) #define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
#define jffs2_write_nand_badblock(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_nand_flash_setup(c) (0)
#define jffs2_nand_flash_cleanup(c) do {} while(0)
#define jffs2_wbuf_dirty(c) (0)
#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
#define jffs2_wbuf_timeout NULL #define jffs2_wbuf_timeout NULL
#define jffs2_wbuf_process NULL #define jffs2_wbuf_process NULL
...@@ -122,11 +129,11 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) ...@@ -122,11 +129,11 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
struct kstatfs; struct kstatfs;
/* wbuf.c */ /* wbuf.c */
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf); int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode); int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
...@@ -135,6 +142,8 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc ...@@ -135,6 +142,8 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc
int jffs2_write_nand_badblock(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_timeout(unsigned long data);
void jffs2_wbuf_process(void *data); void jffs2_wbuf_process(void *data);
int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
#endif /* NAND */ #endif /* NAND */
/* background.c */ /* background.c */
...@@ -151,7 +160,6 @@ extern struct file_operations jffs2_file_operations; ...@@ -151,7 +160,6 @@ extern struct file_operations jffs2_file_operations;
extern struct inode_operations jffs2_file_inode_operations; extern struct inode_operations jffs2_file_inode_operations;
extern struct address_space_operations jffs2_file_address_operations; extern struct address_space_operations jffs2_file_address_operations;
int jffs2_fsync(struct file *, struct dentry *, int); int jffs2_fsync(struct file *, struct dentry *, int);
int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg); int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
int jffs2_readpage (struct file *, struct page *); int jffs2_readpage (struct file *, struct page *);
...@@ -165,8 +173,10 @@ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); ...@@ -165,8 +173,10 @@ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
extern struct inode_operations jffs2_symlink_inode_operations; extern struct inode_operations jffs2_symlink_inode_operations;
/* fs.c */ /* fs.c */
int jffs2_setattr (struct dentry *, struct iattr *);
void jffs2_read_inode (struct inode *); void jffs2_read_inode (struct inode *);
void jffs2_clear_inode (struct inode *); void jffs2_clear_inode (struct inode *);
void jffs2_dirty_inode(struct inode *inode);
struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
struct jffs2_raw_inode *ri); struct jffs2_raw_inode *ri);
int jffs2_statfs (struct super_block *, struct kstatfs *); int jffs2_statfs (struct super_block *, struct kstatfs *);
......
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001, 2002 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: pushpull.h,v 1.8 2002/05/20 14:56:38 dwmw2 Exp $ * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: read.c,v 1.31 2003/01/14 14:06:22 dwmw2 Exp $ * $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $
* *
*/ */
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/compiler.h>
#include "nodelist.h" #include "nodelist.h"
int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
...@@ -165,7 +166,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ...@@ -165,7 +166,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
/* Now we're pointing at the first frag which overlaps our page */ /* Now we're pointing at the first frag which overlaps our page */
while(offset < end) { while(offset < end) {
D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end)); D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end));
if (!frag || frag->ofs > offset) { if (unlikely(!frag || frag->ofs > offset)) {
uint32_t holesize = end - offset; uint32_t holesize = end - offset;
if (frag) { if (frag) {
D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
...@@ -177,13 +178,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ...@@ -177,13 +178,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
buf += holesize; buf += holesize;
offset += holesize; offset += holesize;
continue; continue;
} else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) { } else if (unlikely(!frag->node)) {
D1(printk(KERN_NOTICE "Eep. Overlap in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
f->inocache->ino, frag->ofs, offset));
D1(jffs2_print_frag_list(f));
memset(buf, 0, end - offset);
return -EIO;
} else if (!frag->node) {
uint32_t holeend = min(end, frag->ofs + frag->size); uint32_t holeend = min(end, frag->ofs + frag->size);
D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size)); D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
memset(buf, 0, holeend - offset); memset(buf, 0, holeend - offset);
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: readinode.c,v 1.106 2003/05/14 06:53:26 dwmw2 Exp $ * $Id: readinode.c,v 1.107 2003/10/04 08:33:06 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: scan.c,v 1.100 2003/06/05 14:42:24 dwmw2 Exp $ * $Id: scan.c,v 1.103 2003/10/07 14:46:23 dwmw2 Exp $
* *
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -65,6 +65,16 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo ...@@ -65,6 +65,16 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
#define BLK_STATE_ALLDIRTY 4 #define BLK_STATE_ALLDIRTY 4
#define BLK_STATE_BADBLOCK 5 #define BLK_STATE_BADBLOCK 5
static inline int min_free(struct jffs2_sb_info *c)
{
uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
#ifdef CONFIG_JFFS2_FS_NAND
if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
return c->wbuf_pagesize;
#endif
return min;
}
int jffs2_scan_medium(struct jffs2_sb_info *c) int jffs2_scan_medium(struct jffs2_sb_info *c)
{ {
int i, ret; int i, ret;
...@@ -151,8 +161,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) ...@@ -151,8 +161,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
Later when we do snapshots, this must be the most recent block, Later when we do snapshots, this must be the most recent block,
not the one with most free space. not the one with most free space.
*/ */
if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && if (jeb->free_size > min_free(c) &&
(jffs2_can_mark_obsolete(c) || jeb->free_size > c->wbuf_pagesize) &&
(!c->nextblock || c->nextblock->free_size < jeb->free_size)) { (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
/* Better candidate for the next writes to go to */ /* Better candidate for the next writes to go to */
if (c->nextblock) { if (c->nextblock) {
...@@ -210,7 +219,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) ...@@ -210,7 +219,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
c->dirty_size -= c->nextblock->dirty_size; c->dirty_size -= c->nextblock->dirty_size;
c->nextblock->dirty_size = 0; c->nextblock->dirty_size = 0;
} }
#ifdef CONFIG_JFFS2_FS_NAND
if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { 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 /* If we're going to start writing into a block which already
contains data, and the end of the data isn't page-aligned, contains data, and the end of the data isn't page-aligned,
...@@ -226,6 +235,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) ...@@ -226,6 +235,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
c->nextblock->free_size -= skip; c->nextblock->free_size -= skip;
c->free_size -= skip; c->free_size -= skip;
} }
#endif
if (c->nr_erasing_blocks) { if (c->nr_erasing_blocks) {
if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_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"); printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
...@@ -672,6 +682,7 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc ...@@ -672,6 +682,7 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
ofs, je32_to_cpu(ri->node_crc), crc); ofs, je32_to_cpu(ri->node_crc), crc);
/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen))); DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
jffs2_free_raw_node_ref(raw);
return 0; return 0;
} }
ic = jffs2_scan_make_ino_cache(c, ino); ic = jffs2_scan_make_ino_cache(c, ino);
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: super.c,v 1.79 2003/05/27 22:35:42 dwmw2 Exp $ * $Id: super.c,v 1.90 2003/10/11 11:47:23 dwmw2 Exp $
* *
*/ */
...@@ -26,8 +26,7 @@ ...@@ -26,8 +26,7 @@
#include <linux/namei.h> #include <linux/namei.h>
#include "nodelist.h" #include "nodelist.h"
void jffs2_put_super (struct super_block *); static void jffs2_put_super(struct super_block *);
static kmem_cache_t *jffs2_inode_cachep; static kmem_cache_t *jffs2_inode_cachep;
...@@ -65,7 +64,8 @@ static struct super_operations jffs2_super_operations = ...@@ -65,7 +64,8 @@ static struct super_operations jffs2_super_operations =
.write_super = jffs2_write_super, .write_super = jffs2_write_super,
.statfs = jffs2_statfs, .statfs = jffs2_statfs,
.remount_fs = jffs2_remount_fs, .remount_fs = jffs2_remount_fs,
.clear_inode = jffs2_clear_inode .clear_inode = jffs2_clear_inode,
.dirty_inode = jffs2_dirty_inode,
}; };
static int jffs2_sb_compare(struct super_block *sb, void *data) static int jffs2_sb_compare(struct super_block *sb, void *data)
...@@ -136,8 +136,7 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, ...@@ -136,8 +136,7 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
/* Failure case... */ /* Failure case... */
up_write(&sb->s_umount); up_write(&sb->s_umount);
deactivate_super(sb); deactivate_super(sb);
sb = ERR_PTR(ret); return ERR_PTR(ret);
goto out_put1;
} }
sb->s_flags |= MS_ACTIVE; sb->s_flags |= MS_ACTIVE;
...@@ -145,7 +144,6 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, ...@@ -145,7 +144,6 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
out_put: out_put:
kfree(c); kfree(c);
out_put1:
put_mtd_device(mtd); put_mtd_device(mtd);
return sb; return sb;
...@@ -253,8 +251,7 @@ static struct super_block *jffs2_get_sb(struct file_system_type *fs_type, ...@@ -253,8 +251,7 @@ static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
return ERR_PTR(err); return ERR_PTR(err);
} }
static void jffs2_put_super (struct super_block *sb)
void jffs2_put_super (struct super_block *sb)
{ {
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
...@@ -263,13 +260,12 @@ void jffs2_put_super (struct super_block *sb) ...@@ -263,13 +260,12 @@ void jffs2_put_super (struct super_block *sb)
if (!(sb->s_flags & MS_RDONLY)) if (!(sb->s_flags & MS_RDONLY))
jffs2_stop_garbage_collect_thread(c); jffs2_stop_garbage_collect_thread(c);
down(&c->alloc_sem); down(&c->alloc_sem);
jffs2_flush_wbuf(c, 1); jffs2_flush_wbuf_pad(c);
up(&c->alloc_sem); up(&c->alloc_sem);
jffs2_free_ino_caches(c); jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c); jffs2_free_raw_node_refs(c);
kfree(c->blocks); kfree(c->blocks);
if (c->wbuf) jffs2_nand_flash_cleanup(c);
kfree(c->wbuf);
kfree(c->inocache_list); kfree(c->inocache_list);
if (c->mtd->sync) if (c->mtd->sync)
c->mtd->sync(c->mtd); c->mtd->sync(c->mtd);
...@@ -284,7 +280,7 @@ static void jffs2_kill_sb(struct super_block *sb) ...@@ -284,7 +280,7 @@ static void jffs2_kill_sb(struct super_block *sb)
put_mtd_device(c->mtd); put_mtd_device(c->mtd);
kfree(c); kfree(c);
} }
static struct file_system_type jffs2_fs_type = { static struct file_system_type jffs2_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "jffs2", .name = "jffs2",
...@@ -292,13 +288,15 @@ static struct file_system_type jffs2_fs_type = { ...@@ -292,13 +288,15 @@ static struct file_system_type jffs2_fs_type = {
.kill_sb = jffs2_kill_sb, .kill_sb = jffs2_kill_sb,
}; };
static int __init init_jffs2_fs(void) static int __init init_jffs2_fs(void)
{ {
int ret; int ret;
printk(KERN_INFO "JFFS2 version 2.1. (C) 2001, 2002 Red Hat, Inc.\n"); printk(KERN_INFO "JFFS2 version 2.2."
#ifdef CONFIG_FS_JFFS2_NAND
" (NAND)"
#endif
" (C) 2001-2003 Red Hat, Inc.\n");
jffs2_inode_cachep = kmem_cache_create("jffs2_i", jffs2_inode_cachep = kmem_cache_create("jffs2_i",
sizeof(struct jffs2_inode_info), sizeof(struct jffs2_inode_info),
......
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001, 2002 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: symlink.c,v 1.11 2002/07/23 17:00:45 dwmw2 Exp $ * $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: wbuf.c,v 1.30 2003/02/19 17:48:49 gleixner Exp $ * $Id: wbuf.c,v 1.53 2003/10/11 11:46:09 dwmw2 Exp $
* *
*/ */
...@@ -18,28 +18,86 @@ ...@@ -18,28 +18,86 @@
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
#include "nodelist.h" #include "nodelist.h"
/* For testing write failures */
#undef BREAKME
#undef BREAKMEHEADER
#ifdef BREAKME
static unsigned char *brokenbuf;
#endif
/* max. erase failures before we mark a block bad */ /* max. erase failures before we mark a block bad */
#define MAX_ERASE_FAILURES 5 #define MAX_ERASE_FAILURES 5
/* two seconds timeout for timed wbuf-flushing */ /* two seconds timeout for timed wbuf-flushing */
#define WBUF_FLUSH_TIMEOUT 2 * HZ #define WBUF_FLUSH_TIMEOUT 2 * HZ
#define JFFS2_OOB_ECCPOS0 0 struct jffs2_inodirty {
#define JFFS2_OOB_ECCPOS1 1 uint32_t ino;
#define JFFS2_OOB_ECCPOS2 2 struct jffs2_inodirty *next;
#define JFFS2_OOB_ECCPOS3 3 };
#define JFFS2_OOB_ECCPOS4 6
#define JFFS2_OOB_ECCPOS5 7
#define NAND_JFFS2_OOB8_FSDAPOS 6 static struct jffs2_inodirty inodirty_nomem;
#define NAND_JFFS2_OOB16_FSDAPOS 8
#define NAND_JFFS2_OOB8_FSDALEN 2
#define NAND_JFFS2_OOB16_FSDALEN 8
struct nand_oobinfo jffs2_oobinfo = { static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
.useecc = 1, {
.eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2, JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5} struct jffs2_inodirty *this = c->wbuf_inodes;
};
/* If a malloc failed, consider _everything_ dirty */
if (this == &inodirty_nomem)
return 1;
/* If ino == 0, _any_ non-GC writes mean 'yes' */
if (this && !ino)
return 1;
/* Look to see if the inode in question is pending in the wbuf */
while (this) {
if (this->ino == ino)
return 1;
this = this->next;
}
return 0;
}
static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
{
struct jffs2_inodirty *this;
this = c->wbuf_inodes;
if (this != &inodirty_nomem) {
while (this) {
struct jffs2_inodirty *next = this->next;
kfree(this);
this = next;
}
}
c->wbuf_inodes = NULL;
}
static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
{
struct jffs2_inodirty *new;
/* Mark the superblock dirty so that kupdated will flush... */
OFNI_BS_2SFFJ(c)->s_dirt = 1;
if (jffs2_wbuf_pending_for_ino(c, ino))
return;
new = kmalloc(sizeof(*new), GFP_KERNEL);
if (!new) {
D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n"));
jffs2_clear_wbuf_ino_list(c);
c->wbuf_inodes = &inodirty_nomem;
return;
}
new->ino = ino;
new->next = c->wbuf_inodes;
c->wbuf_inodes = new;
return;
}
static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
{ {
...@@ -70,74 +128,267 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) ...@@ -70,74 +128,267 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
} }
} }
/* /* Recover from failure to write wbuf. Recover the nodes up to the
* Timed flushing of wbuf. If we have no consecutive write to wbuf, within * wbuf, not the one which we were starting to try to write. */
* 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_work(&c->wbuf_task);
}
/* static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
* 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; struct jffs2_eraseblock *jeb, *new_jeb;
struct jffs2_raw_node_ref **first_raw, **raw;
D1(printk(KERN_DEBUG "jffs2_wbuf_process() entered\n")); size_t retlen;
int ret;
/* Check, if the timer is active again */ unsigned char *buf;
if (timer_pending (&c->wbuf_timer)) { uint32_t start, end, ofs, len;
D1(printk (KERN_DEBUG "Nothing to do, timer is active again\n"));
return; spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
D1(printk("About to refile bad block at %08x\n", jeb->offset));
D2(jffs2_dump_block_lists(c));
/* File the existing block on the bad_used_list.... */
if (c->nextblock == jeb)
c->nextblock = NULL;
else /* Not sure this should ever happen... need more coffee */
list_del(&jeb->list);
if (jeb->first_node) {
D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
list_add(&jeb->list, &c->bad_used_list);
} else {
BUG();
/* It has to have had some nodes or we couldn't be here */
D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
jffs2_erase_pending_trigger(c);
}
D2(jffs2_dump_block_lists(c));
/* Adjust its size counts accordingly */
c->wasted_size += jeb->free_size;
c->free_size -= jeb->free_size;
jeb->wasted_size += jeb->free_size;
jeb->free_size = 0;
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
/* Find the first node to be recovered, by skipping over every
node which ends before the wbuf starts, or which is obsolete. */
first_raw = &jeb->first_node;
while (*first_raw &&
(ref_obsolete(*first_raw) ||
(ref_offset(*first_raw) + (*first_raw)->totlen) < c->wbuf_ofs)) {
D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
ref_offset(*first_raw), ref_flags(*first_raw),
(ref_offset(*first_raw) + (*first_raw)->totlen),
c->wbuf_ofs));
first_raw = &(*first_raw)->next_phys;
} }
if (down_trylock(&c->alloc_sem)) { if (!*first_raw) {
/* If someone else has the alloc_sem, they're about to /* All nodes were obsolete. Nothing to recover. */
write anyway. So no need to waste space by D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
padding */ spin_unlock(&c->erase_completion_lock);
D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem already occupied\n"));
return; return;
} }
D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem got\n")); start = ref_offset(*first_raw);
end = ref_offset(*first_raw) + (*first_raw)->totlen;
if (!c->nextblock) { /* Find the last node to be recovered */
D1(printk(KERN_DEBUG "jffs2_wbuf_process(): nextblock NULL, nothing to do\n")); raw = first_raw;
if (c->wbuf_len) { while ((*raw)) {
printk(KERN_WARNING "jffs2_wbuf_process(): c->wbuf_len is 0x%03x but nextblock is NULL!\n", c->wbuf_len); if (!ref_obsolete(*raw))
up(&c->alloc_sem); end = ref_offset(*raw) + (*raw)->totlen;
BUG();
raw = &(*raw)->next_phys;
}
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
buf = NULL;
if (start < c->wbuf_ofs) {
/* First affected node was already partially written.
* Attempt to reread the old data into our buffer. */
buf = kmalloc(end - start, GFP_KERNEL);
if (!buf) {
printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
goto read_failed;
} }
/* Do the read... */
ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
if (ret == -EIO && retlen == c->wbuf_ofs - start) {
/* ECC recovered */
ret = 0;
}
if (ret || retlen != c->wbuf_ofs - start) {
printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
kfree(buf);
buf = NULL;
read_failed:
first_raw = &(*first_raw)->next_phys;
/* If this was the only node to be recovered, give up */
if (!(*first_raw))
return;
/* It wasn't. Go on and try to recover nodes complete in the wbuf */
start = ref_offset(*first_raw);
} else {
/* Read succeeded. Copy the remaining data from the wbuf */
memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
}
}
/* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
Either 'buf' contains the data, or we find it in the wbuf */
/* ... and get an allocation of space from a shiny new block instead */
ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
if (ret) {
printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
if (buf)
kfree(buf);
return; return;
} }
if (end-start >= c->wbuf_pagesize) {
/* Need to do another write immediately. This, btw,
/* if !c->nextblock then the tail will have got flushed from means that we'll be writing from 'buf' and not from
jffs2_do_reserve_space() anyway. */ the wbuf. Since if we're writing from the wbuf there
if(c->nextblock) won't be more than a wbuf full of data, now will
jffs2_flush_wbuf(c, 2); /* pad and adjust nextblock */ there? :) */
uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
towrite, ofs));
#ifdef BREAKMEHEADER
static int breakme;
if (breakme++ == 20) {
printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs);
breakme = 0;
c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
brokenbuf, NULL, c->oobinfo);
ret = -EIO;
} else
#endif
ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
buf, NULL, c->oobinfo);
if (ret || retlen != towrite) {
/* Argh. We tried. Really we did. */
printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
kfree(buf);
if (retlen) {
struct jffs2_raw_node_ref *raw2;
raw2 = jffs2_alloc_raw_node_ref();
if (!raw2)
return;
raw2->flash_offset = ofs | REF_OBSOLETE;
raw2->totlen = (*first_raw)->totlen;
raw2->next_phys = NULL;
raw2->next_in_ino = NULL;
jffs2_add_physical_node_ref(c, raw2);
}
return;
}
printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
up(&c->alloc_sem); c->wbuf_len = (end - start) - towrite;
} c->wbuf_ofs = ofs + towrite;
memcpy(c->wbuf, buf + towrite, c->wbuf_len);
/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
kfree(buf);
} else {
/* OK, now we're left with the dregs in whichever buffer we're using */
if (buf) {
memcpy(c->wbuf, buf, end-start);
kfree(buf);
} else {
memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
}
c->wbuf_ofs = ofs;
c->wbuf_len = end - start;
}
/* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
new_jeb = &c->blocks[ofs / c->sector_size];
spin_lock(&c->erase_completion_lock);
if (new_jeb->first_node) {
/* Odd, but possible with ST flash later maybe */
new_jeb->last_node->next_phys = *first_raw;
} else {
new_jeb->first_node = *first_raw;
}
raw = first_raw;
while (*raw) {
D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
(*raw)->totlen, ref_offset(*raw), ref_flags(*raw), ofs));
if (ref_obsolete(*raw)) {
/* Shouldn't really happen much */
new_jeb->dirty_size += (*raw)->totlen;
new_jeb->free_size -= (*raw)->totlen;
c->dirty_size += (*raw)->totlen;
} else {
new_jeb->used_size += (*raw)->totlen;
new_jeb->free_size -= (*raw)->totlen;
jeb->dirty_size += (*raw)->totlen;
jeb->used_size -= (*raw)->totlen;
c->dirty_size += (*raw)->totlen;
}
c->free_size -= (*raw)->totlen;
(*raw)->flash_offset = ofs | ref_flags(*raw);
ofs += (*raw)->totlen;
new_jeb->last_node = *raw;
raw = &(*raw)->next_phys;
}
/* Fix up the original jeb now it's on the bad_list */
*first_raw = NULL;
if (first_raw == &jeb->first_node) {
jeb->last_node = NULL;
D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
jffs2_erase_pending_trigger(c);
}
else
jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys);
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
ACCT_SANITY_CHECK(c,new_jeb);
D1(ACCT_PARANOIA_CHECK(new_jeb));
spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
}
/* Meaning of pad argument: /* Meaning of pad argument:
0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. 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 1: Pad, do not adjust nextblock free_size
2: Pad, adjust nextblock free_size 2: Pad, adjust nextblock free_size
*/ */
int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
{ {
int ret; int ret;
size_t retlen; size_t retlen;
...@@ -153,9 +404,6 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) ...@@ -153,9 +404,6 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
BUG(); BUG();
} }
/* delete a eventually started timed wbuf flush */
del_timer_sync(&c->wbuf_timer);
if(!c->wbuf || !c->wbuf_len) if(!c->wbuf || !c->wbuf_len)
return 0; return 0;
...@@ -179,28 +427,30 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) ...@@ -179,28 +427,30 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
/* else jffs2_flash_writev has actually filled in the rest of the /* else jffs2_flash_writev has actually filled in the rest of the
buffer for us, and will deal with the node refs etc. later. */ buffer for us, and will deal with the node refs etc. later. */
ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, &jffs2_oobinfo); #ifdef BREAKME
static int breakme;
if (breakme++ == 20) {
printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs);
breakme = 0;
c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize,
&retlen, brokenbuf, NULL, c->oobinfo);
ret = -EIO;
} else
#endif
ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
if (ret || retlen != c->wbuf_pagesize) { if (ret || retlen != c->wbuf_pagesize) {
if (ret) if (ret)
printk(KERN_CRIT "jffs2_flush_wbuf(): Write failed with %d\n",ret); printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
else else {
printk(KERN_CRIT "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
retlen, c->wbuf_pagesize); retlen, c->wbuf_pagesize);
ret = -EIO;
ret = -EIO; }
/* CHECKME NAND
So that the caller knows what happened. If jffs2_wbuf_recover(c);
we were called from jffs2_flash_writev(), it'll
know to return failure and _its_ caller will
try again. writev gives back to jffs2_write_xxx
in write.c. There are the real fixme's
*/
/* FIXME NAND
If we were called from GC or fsync, there's no repair kit yet
*/
return ret; return ret;
} }
...@@ -230,18 +480,71 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) ...@@ -230,18 +480,71 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
/* Stick any now-obsoleted blocks on the erase_pending_list */ /* Stick any now-obsoleted blocks on the erase_pending_list */
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
jffs2_refile_wbuf_blocks(c); jffs2_refile_wbuf_blocks(c);
jffs2_clear_wbuf_ino_list(c);
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
memset(c->wbuf,0xff,c->wbuf_pagesize); memset(c->wbuf,0xff,c->wbuf_pagesize);
/* adjust write buffer offset, else we get a non contigous write bug */ /* adjust write buffer offset, else we get a non contiguous write bug */
c->wbuf_ofs+= c->wbuf_pagesize; c->wbuf_ofs += c->wbuf_pagesize;
c->wbuf_len = 0; c->wbuf_len = 0;
return 0; return 0;
} }
/* Trigger garbage collection to flush the write-buffer.
If ino arg is zero, do it if _any_ real (i.e. not GC) writes are
outstanding. If ino arg non-zero, do it only if a write for the
given inode is outstanding. */
int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
{
uint32_t old_wbuf_ofs;
uint32_t old_wbuf_len;
int ret = 0;
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
down(&c->alloc_sem);
if (!jffs2_wbuf_pending_for_ino(c, ino)) {
D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
up(&c->alloc_sem);
return 0;
}
old_wbuf_ofs = c->wbuf_ofs;
old_wbuf_len = c->wbuf_len;
while (old_wbuf_len &&
old_wbuf_ofs == c->wbuf_ofs) {
up(&c->alloc_sem);
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
ret = jffs2_garbage_collect_pass(c);
if (ret) {
/* GC failed. Flush it with padding instead */
down(&c->alloc_sem);
ret = __jffs2_flush_wbuf(c, 2);
break;
}
down(&c->alloc_sem);
}
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
up(&c->alloc_sem);
return ret;
}
/* Pad write-buffer to end and write it, wasting space. */
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
{
return __jffs2_flush_wbuf(c, 1);
}
#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) ) #define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) ) #define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen) int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
{ {
struct iovec outvecs[3]; struct iovec outvecs[3];
uint32_t totlen = 0; uint32_t totlen = 0;
...@@ -275,7 +578,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi ...@@ -275,7 +578,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi
/* It's a write to a new block */ /* It's a write to a new block */
if (c->wbuf_len) { if (c->wbuf_len) {
D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
ret = jffs2_flush_wbuf(c, 1); ret = jffs2_flush_wbuf_pad(c);
if (ret) { if (ret) {
/* the underlying layer has to check wbuf_len to do the cleanup */ /* the underlying layer has to check wbuf_len to do the cleanup */
D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
...@@ -341,16 +644,18 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi ...@@ -341,16 +644,18 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi
} }
/* write buffer is full, flush buffer */ /* write buffer is full, flush buffer */
ret = jffs2_flush_wbuf(c, 0); ret = __jffs2_flush_wbuf(c, 0);
if (ret) { if (ret) {
/* the underlying layer has to check wbuf_len to do the cleanup */ /* the underlying layer has to check wbuf_len to do the cleanup */
D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
/* Retlen zero to make sure our caller doesn't mark the space dirty.
We've already done everything that's necessary */
*retlen = 0; *retlen = 0;
return ret; return ret;
} }
outvec_to += donelen; outvec_to += donelen;
c->wbuf_ofs = outvec_to; c->wbuf_ofs = outvec_to;
/* All invecs done ? */ /* All invecs done ? */
if (invec == count) if (invec == count)
goto alldone; goto alldone;
...@@ -396,7 +701,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi ...@@ -396,7 +701,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi
outvecs[splitvec].iov_len = split_ofs; outvecs[splitvec].iov_len = split_ofs;
/* We did cross a page boundary, so we write some now */ /* We did cross a page boundary, so we write some now */
ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, &jffs2_oobinfo); ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo);
if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
/* At this point we have no problem, /* At this point we have no problem,
c->wbuf is empty. c->wbuf is empty.
...@@ -433,13 +738,14 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi ...@@ -433,13 +738,14 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi
} }
c->wbuf_len = wbuf_ptr - c->wbuf; c->wbuf_len = wbuf_ptr - c->wbuf;
alldone: /* If there's a remainder in the wbuf and it's a non-GC write,
remember that the wbuf affects this ino */
alldone:
*retlen = donelen; *retlen = donelen;
/* Setup timed wbuf flush, if buffer len != 0 */
if (c->wbuf_len) { if (c->wbuf_len && ino)
D1(printk (KERN_DEBUG "jffs2_flash_writev: mod wbuf_timer\n")); jffs2_wbuf_dirties_inode(c, ino);
mod_timer(&c->wbuf_timer, jiffies + WBUF_FLUSH_TIMEOUT);
}
return 0; return 0;
} }
...@@ -456,7 +762,7 @@ int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *r ...@@ -456,7 +762,7 @@ int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *r
vecs[0].iov_base = (unsigned char *) buf; vecs[0].iov_base = (unsigned char *) buf;
vecs[0].iov_len = len; vecs[0].iov_len = len;
return jffs2_flash_writev(c, vecs, 1, ofs, retlen); return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0);
} }
/* /*
...@@ -469,7 +775,7 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re ...@@ -469,7 +775,7 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
/* Read flash */ /* Read flash */
if (!jffs2_can_mark_obsolete(c)) { if (!jffs2_can_mark_obsolete(c)) {
ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, &jffs2_oobinfo); ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
if ( (ret == -EIO) && (*retlen == len) ) { if ( (ret == -EIO) && (*retlen == len) ) {
printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
...@@ -525,22 +831,12 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb ...@@ -525,22 +831,12 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
{ {
unsigned char *buf; unsigned char *buf;
int ret = 0; int ret = 0;
int i,len,cnt,page; int i,len,page;
size_t retlen; size_t retlen;
int fsdata_pos,badblock_pos,oob_size; int oob_size;
oob_size = c->mtd->oobsize; oob_size = c->mtd->oobsize;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS;
badblock_pos = NAND_BADBLOCK_POS;
break;
default:
D1(printk(KERN_WARNING "jffs2_write_oob_empty(): Invalid ECC type\n"));
return -EINVAL;
}
/* allocate a buffer for all oob data in this sector */ /* allocate a buffer for all oob data in this sector */
len = 4 * oob_size; len = 4 * oob_size;
buf = kmalloc(len, GFP_KERNEL); buf = kmalloc(len, GFP_KERNEL);
...@@ -568,19 +864,22 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb ...@@ -568,19 +864,22 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
/* Special check for first two pages */ /* Special check for first two pages */
for (page = 0; page < 2 * oob_size; page += oob_size) { for (page = 0; page < 2 * oob_size; page += oob_size) {
/* Check for bad block marker */ /* Check for bad block marker */
if (buf[page+badblock_pos] != 0xff) { if (buf[page+c->badblock_pos] != 0xff) {
D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset)); D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset));
/* Return 2 for bad and 3 for failed block /* Return 2 for bad and 3 for failed block
bad goes to list_bad and failed to list_erase */ bad goes to list_bad and failed to list_erase */
ret = (!page) ? 2 : 3; ret = (!page) ? 2 : 3;
goto out; goto out;
} }
cnt = oob_size; for(i = 0; i < oob_size ; i++) {
if (mode) /* Yeah, we know about the cleanmarker. */
cnt -= fsdata_pos; if (mode && i >= c->fsdata_pos &&
for(i = 0; i < cnt ; i+=sizeof(unsigned short)) { i < c->fsdata_pos+c->fsdata_len)
unsigned short dat = *(unsigned short *)(&buf[page+i]); continue;
if(dat != 0xffff) {
if (buf[page+i] != 0xFF) {
D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
buf[page+i], page+i, jeb->offset));
ret = 1; ret = 1;
goto out; goto out;
} }
...@@ -617,23 +916,11 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo ...@@ -617,23 +916,11 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo
unsigned char *p; unsigned char *p;
int ret, i, cnt, retval = 0; int ret, i, cnt, retval = 0;
size_t retlen, offset; size_t retlen, offset;
int fsdata_pos, fsdata_len, oob_size, badblock_pos; int oob_size;
offset = jeb->offset; offset = jeb->offset;
oob_size = c->mtd->oobsize; oob_size = c->mtd->oobsize;
switch (c->mtd->ecctype) {
case MTD_ECC_SW:
fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS;
fsdata_len = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDALEN : NAND_JFFS2_OOB16_FSDALEN;
badblock_pos = NAND_BADBLOCK_POS;
break;
default:
D1 (printk (KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n"));
return -EINVAL;
}
/* Loop through the physical blocks */ /* Loop through the physical blocks */
for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
/* /*
...@@ -653,14 +940,15 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo ...@@ -653,14 +940,15 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo
} }
/* Check for bad block marker */ /* Check for bad block marker */
if (buf[badblock_pos] != 0xff) { if (buf[c->badblock_pos] != 0xff) {
D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n",
jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos));
return 2; return 2;
} }
/* Check for failure counter in the second page */ /* Check for failure counter in the second page */
if (buf[badblock_pos + oob_size] != 0xff) { if (buf[c->badblock_pos + oob_size] != 0xff) {
D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[badblock_pos + oob_size])); D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size]));
return 3; return 3;
} }
...@@ -671,12 +959,19 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo ...@@ -671,12 +959,19 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo
n.totlen = cpu_to_je32 (8); n.totlen = cpu_to_je32 (8);
p = (unsigned char *) &n; p = (unsigned char *) &n;
for (i = 0; i < fsdata_len; i++) { for (i = 0; i < c->fsdata_len; i++) {
if (buf[fsdata_pos + i] != p[i]) { if (buf[c->fsdata_pos + i] != p[i]) {
D2 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset));
retval = 1; retval = 1;
} }
} }
D1(if (retval == 1) {
printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
printk(KERN_WARNING "OOB at %08x was ", offset);
for (i=0; i < oob_size; i++) {
printk("%02x ", buf[i]);
}
printk("\n");
})
} }
offset += c->mtd->erasesize; offset += c->mtd->erasesize;
} }
...@@ -687,31 +982,20 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc ...@@ -687,31 +982,20 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc
{ {
struct jffs2_unknown_node n; struct jffs2_unknown_node n;
int ret; int ret;
int fsdata_pos,fsdata_len;
size_t retlen; size_t retlen;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS;
fsdata_len = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDALEN : NAND_JFFS2_OOB16_FSDALEN;
break;
default:
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n"));
return -EINVAL;
}
n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
n.totlen = cpu_to_je32(8); n.totlen = cpu_to_je32(8);
ret = jffs2_flash_write_oob(c, jeb->offset + fsdata_pos, fsdata_len, &retlen, (unsigned char *)&n); ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n);
if (ret) { if (ret) {
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
return ret; return ret;
} }
if (retlen != fsdata_len) { if (retlen != c->fsdata_len) {
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, fsdata_len)); D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
return ret; return ret;
} }
return 0; return 0;
...@@ -725,18 +1009,9 @@ int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *je ...@@ -725,18 +1009,9 @@ int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
unsigned char buf[16]; unsigned char buf[16];
int ret; int ret;
size_t retlen; size_t retlen;
int oob_size, badblock_pos; int oob_size;
oob_size = c->mtd->oobsize; oob_size = c->mtd->oobsize;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
badblock_pos = NAND_BADBLOCK_POS;
break;
default:
D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Invalid ECC type\n"));
return -EINVAL;
}
ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf); ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf);
...@@ -750,7 +1025,7 @@ int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *je ...@@ -750,7 +1025,7 @@ int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
return -EIO; return -EIO;
} }
jeb->bad_count = buf[badblock_pos]; jeb->bad_count = buf[c->badblock_pos];
return 0; return 0;
} }
...@@ -767,25 +1042,16 @@ int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *je ...@@ -767,25 +1042,16 @@ int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{ {
unsigned char buf = 0x0; unsigned char buf = 0x0;
int ret,pos; int ret;
size_t retlen; size_t retlen;
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
pos = NAND_BADBLOCK_POS;
break;
default:
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Invalid ECC type\n"));
return -EINVAL;
}
/* if the count is < max, we try to write the counter to the 2nd page oob area */ /* if the count is < max, we try to write the counter to the 2nd page oob area */
if( ++jeb->bad_count < MAX_ERASE_FAILURES) { if( ++jeb->bad_count < MAX_ERASE_FAILURES) {
buf = (unsigned char)jeb->bad_count; buf = (unsigned char)jeb->bad_count;
pos += c->mtd->oobblock; c->badblock_pos += c->mtd->oobblock;
} }
ret = jffs2_flash_write_oob(c, jeb->offset + pos, 1, &retlen, &buf); ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf);
if (ret) { if (ret) {
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
...@@ -797,3 +1063,88 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * ...@@ -797,3 +1063,88 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *
} }
return 0; return 0;
} }
#define JFFS2_OOB_ECCPOS0 0
#define JFFS2_OOB_ECCPOS1 1
#define JFFS2_OOB_ECCPOS2 2
#define JFFS2_OOB_ECCPOS3 3
#define JFFS2_OOB_ECCPOS4 6
#define JFFS2_OOB_ECCPOS5 7
#define NAND_JFFS2_OOB8_FSDAPOS 6
#define NAND_JFFS2_OOB16_FSDAPOS 8
#define NAND_JFFS2_OOB8_FSDALEN 2
#define NAND_JFFS2_OOB16_FSDALEN 8
static struct nand_oobinfo jffs2_oobinfo_swecc = {
.useecc = 1,
.eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2,
JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5}
};
static struct nand_oobinfo jffs2_oobinfo_docecc = {
.useecc = 1,
.eccpos = {0,1,2,3,4,5}
};
int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
{
/* Cleanmarker is out-of-band, so inline size zero */
c->cleanmarker_size = 0;
/* Initialise write buffer */
c->wbuf_pagesize = c->mtd->oobblock;
c->wbuf_ofs = 0xFFFFFFFF;
/* FIXME: If we had a generic way of describing the hardware's
use of OOB area, we could perhaps make this generic too. */
switch(c->mtd->ecctype) {
case MTD_ECC_SW:
D1(printk(KERN_DEBUG "JFFS2 using software ECC\n"));
c->oobinfo = &jffs2_oobinfo_swecc;
if (c->mtd->oobsize == 8) {
c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS;
c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN;
} else {
c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS;
c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
}
c->badblock_pos = NAND_BADBLOCK_POS;
break;
case MTD_ECC_RS_DiskOnChip:
D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n"));
c->oobinfo = &jffs2_oobinfo_docecc;
c->fsdata_pos = 6;
c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
c->badblock_pos = 15;
break;
default:
printk("JFFS2 doesn't yet know how to handle ECC type %d\n",
c->mtd->ecctype);
return -EINVAL;
}
c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf)
return -ENOMEM;
#ifdef BREAKME
if (!brokenbuf)
brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!brokenbuf) {
kfree(c->wbuf);
return -ENOMEM;
}
memset(brokenbuf, 0xdb, c->wbuf_pagesize);
#endif
return 0;
}
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
{
kfree(c->wbuf);
}
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: write.c,v 1.65 2003/01/21 18:11:29 dwmw2 Exp $ * $Id: write.c,v 1.75 2003/10/08 11:45:11 dwmw2 Exp $
* *
*/ */
...@@ -86,7 +86,7 @@ static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) ...@@ -86,7 +86,7 @@ static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
/* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it,
write it to the flash, link it into the existing inode/fragment list */ write it to the flash, link it into the existing inode/fragment list */
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, uint32_t *writelen) struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode)
{ {
struct jffs2_raw_node_ref *raw; struct jffs2_raw_node_ref *raw;
...@@ -94,6 +94,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 ...@@ -94,6 +94,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
size_t retlen; size_t retlen;
struct iovec vecs[2]; struct iovec vecs[2];
int ret; int ret;
int retried = 0;
unsigned long cnt = 2; unsigned long cnt = 2;
D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
...@@ -120,24 +121,28 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 ...@@ -120,24 +121,28 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
jffs2_free_raw_node_ref(raw); jffs2_free_raw_node_ref(raw);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
raw->flash_offset = flash_ofs;
raw->totlen = PAD(sizeof(*ri)+datalen);
raw->next_phys = NULL;
fn->ofs = je32_to_cpu(ri->offset); fn->ofs = je32_to_cpu(ri->offset);
fn->size = je32_to_cpu(ri->dsize); fn->size = je32_to_cpu(ri->dsize);
fn->frags = 0; fn->frags = 0;
fn->raw = raw;
/* check number of valid vecs */ /* check number of valid vecs */
if (!datalen || !data) if (!datalen || !data)
cnt = 1; cnt = 1;
retry:
fn->raw = raw;
raw->flash_offset = flash_ofs;
raw->totlen = PAD(sizeof(*ri)+datalen);
raw->next_phys = NULL;
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
(alloc_mode==ALLOC_GC)?0:f->inocache->ino);
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen);
if (ret || (retlen != sizeof(*ri) + datalen)) { if (ret || (retlen != sizeof(*ri) + datalen)) {
printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
sizeof(*ri)+datalen, flash_ofs, ret, retlen); sizeof(*ri)+datalen, flash_ofs, ret, retlen);
/* Mark the space as dirtied */ /* Mark the space as dirtied */
if (retlen) { if (retlen) {
/* Doesn't belong to any inode */ /* Doesn't belong to any inode */
...@@ -155,11 +160,42 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 ...@@ -155,11 +160,42 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
jffs2_free_raw_node_ref(raw); jffs2_free_raw_node_ref(raw);
} }
if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) {
/* Try to reallocate space and retry */
uint32_t dummy;
struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
retried = 1;
D1(printk(KERN_DEBUG "Retrying failed write.\n"));
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
if (alloc_mode == ALLOC_GC) {
ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy);
} else {
/* Locking pain */
up(&f->sem);
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode);
down(&f->sem);
}
if (!ret) {
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
goto retry;
}
D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
jffs2_free_raw_node_ref(raw);
}
/* Release the full_dnode which is now useless, and return */ /* Release the full_dnode which is now useless, and return */
jffs2_free_full_dnode(fn); jffs2_free_full_dnode(fn);
if (writelen)
*writelen = retlen;
return ERR_PTR(ret?ret:-EIO); return ERR_PTR(ret?ret:-EIO);
} }
/* Mark the space used */ /* Mark the space used */
...@@ -184,19 +220,21 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 ...@@ -184,19 +220,21 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize),
je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc), je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
if (writelen)
*writelen = retlen;
f->inocache->nodes = raw; if (retried) {
ACCT_SANITY_CHECK(c,NULL);
}
return fn; return fn;
} }
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, uint32_t *writelen) struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode)
{ {
struct jffs2_raw_node_ref *raw; struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *fd; struct jffs2_full_dirent *fd;
size_t retlen; size_t retlen;
struct iovec vecs[2]; struct iovec vecs[2];
int retried = 0;
int ret; int ret;
D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n",
...@@ -225,11 +263,6 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff ...@@ -225,11 +263,6 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
jffs2_free_raw_node_ref(raw); jffs2_free_raw_node_ref(raw);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
raw->flash_offset = flash_ofs;
raw->totlen = PAD(sizeof(*rd)+namelen);
raw->next_in_ino = f->inocache->nodes;
f->inocache->nodes = raw;
raw->next_phys = NULL;
fd->version = je32_to_cpu(rd->version); fd->version = je32_to_cpu(rd->version);
fd->ino = je32_to_cpu(rd->ino); fd->ino = je32_to_cpu(rd->ino);
...@@ -237,14 +270,22 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff ...@@ -237,14 +270,22 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
fd->type = rd->type; fd->type = rd->type;
memcpy(fd->name, name, namelen); memcpy(fd->name, name, namelen);
fd->name[namelen]=0; fd->name[namelen]=0;
retry:
fd->raw = raw; fd->raw = raw;
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen); raw->flash_offset = flash_ofs;
raw->totlen = PAD(sizeof(*rd)+namelen);
raw->next_phys = NULL;
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
(alloc_mode==ALLOC_GC)?0:fd->ino);
if (ret || (retlen != sizeof(*rd) + namelen)) { if (ret || (retlen != sizeof(*rd) + namelen)) {
printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
sizeof(*rd)+namelen, flash_ofs, ret, retlen); sizeof(*rd)+namelen, flash_ofs, ret, retlen);
/* Mark the space as dirtied */ /* Mark the space as dirtied */
if (retlen) { if (retlen) {
raw->next_in_ino = NULL;
raw->flash_offset |= REF_OBSOLETE; raw->flash_offset |= REF_OBSOLETE;
jffs2_add_physical_node_ref(c, raw); jffs2_add_physical_node_ref(c, raw);
jffs2_mark_node_obsolete(c, raw); jffs2_mark_node_obsolete(c, raw);
...@@ -252,20 +293,53 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff ...@@ -252,20 +293,53 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
jffs2_free_raw_node_ref(raw); jffs2_free_raw_node_ref(raw);
} }
if (!retried && (raw = jffs2_alloc_raw_node_ref())) {
/* Try to reallocate space and retry */
uint32_t dummy;
struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
retried = 1;
D1(printk(KERN_DEBUG "Retrying failed write.\n"));
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
if (alloc_mode == ALLOC_GC) {
ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy);
} else {
/* Locking pain */
up(&f->sem);
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode);
down(&f->sem);
}
if (!ret) {
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
goto retry;
}
D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
jffs2_free_raw_node_ref(raw);
}
/* Release the full_dnode which is now useless, and return */ /* Release the full_dnode which is now useless, and return */
jffs2_free_full_dirent(fd); jffs2_free_full_dirent(fd);
if (writelen)
*writelen = retlen;
return ERR_PTR(ret?ret:-EIO); return ERR_PTR(ret?ret:-EIO);
} }
/* Mark the space used */ /* Mark the space used */
raw->flash_offset |= REF_PRISTINE; raw->flash_offset |= REF_PRISTINE;
jffs2_add_physical_node_ref(c, raw); jffs2_add_physical_node_ref(c, raw);
if (writelen)
*writelen = retlen;
raw->next_in_ino = f->inocache->nodes;
f->inocache->nodes = raw; f->inocache->nodes = raw;
if (retried) {
ACCT_SANITY_CHECK(c,NULL);
}
return fd; return fd;
} }
...@@ -288,7 +362,9 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ...@@ -288,7 +362,9 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
unsigned char comprtype = JFFS2_COMPR_NONE; unsigned char comprtype = JFFS2_COMPR_NONE;
uint32_t phys_ofs, alloclen; uint32_t phys_ofs, alloclen;
uint32_t datalen, cdatalen; uint32_t datalen, cdatalen;
int retried = 0;
retry:
D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset)); D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL); ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
...@@ -331,7 +407,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ...@@ -331,7 +407,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, NULL); fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
if (comprtype != JFFS2_COMPR_NONE) if (comprtype != JFFS2_COMPR_NONE)
kfree(comprbuf); kfree(comprbuf);
...@@ -340,6 +416,12 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ...@@ -340,6 +416,12 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
ret = PTR_ERR(fn); ret = PTR_ERR(fn);
up(&f->sem); up(&f->sem);
jffs2_complete_reservation(c); jffs2_complete_reservation(c);
if (!retried) {
/* Write error to be retried */
retried = 1;
D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n"));
goto retry;
}
break; break;
} }
ret = jffs2_add_full_dnode_to_inode(c, f, fn); ret = jffs2_add_full_dnode_to_inode(c, f, fn);
...@@ -381,7 +463,6 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str ...@@ -381,7 +463,6 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
struct jffs2_full_dnode *fn; struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd; struct jffs2_full_dirent *fd;
uint32_t alloclen, phys_ofs; uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
int ret; int ret;
/* Try to reserve enough space for both node and dirent. /* Try to reserve enough space for both node and dirent.
...@@ -397,7 +478,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str ...@@ -397,7 +478,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
ri->data_crc = cpu_to_je32(0); ri->data_crc = cpu_to_je32(0);
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen); fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
jemode_to_cpu(ri->mode))); jemode_to_cpu(ri->mode)));
...@@ -414,22 +495,14 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str ...@@ -414,22 +495,14 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
*/ */
f->metadata = fn; f->metadata = fn;
/* Work out where to put the dirent node now. */
writtenlen = PAD(writtenlen);
phys_ofs += writtenlen;
alloclen -= writtenlen;
up(&f->sem); up(&f->sem);
jffs2_complete_reservation(c);
if (alloclen < sizeof(*rd)+namelen) { ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
/* Not enough space left in this chunk. Get some more */
jffs2_complete_reservation(c);
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) { if (ret) {
/* Eep. */ /* Eep. */
D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
return ret; return ret;
}
} }
rd = jffs2_alloc_raw_dirent(); rd = jffs2_alloc_raw_dirent();
...@@ -455,7 +528,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str ...@@ -455,7 +528,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, &writtenlen); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
jffs2_free_raw_dirent(rd); jffs2_free_raw_dirent(rd);
...@@ -513,7 +586,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, ...@@ -513,7 +586,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
jffs2_free_raw_dirent(rd); jffs2_free_raw_dirent(rd);
...@@ -598,7 +671,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint ...@@ -598,7 +671,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL); fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
jffs2_free_raw_dirent(rd); jffs2_free_raw_dirent(rd);
......
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001, 2002 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in this directory. * For licensing information, see the file 'LICENCE' in this directory.
* *
* $Id: writev.c,v 1.3 2002/08/08 08:35:21 dwmw2 Exp $ * $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $
* *
*/ */
......
/* /*
* JFFS2 -- Journalling Flash File System, Version 2. * JFFS2 -- Journalling Flash File System, Version 2.
* *
* Copyright (C) 2001, 2002 Red Hat, Inc. * Copyright (C) 2001-2003 Red Hat, Inc.
* *
* Created by David Woodhouse <dwmw2@cambridge.redhat.com> * Created by David Woodhouse <dwmw2@redhat.com>
* *
* For licensing information, see the file 'LICENCE' in the * For licensing information, see the file 'LICENCE' in the
* jffs2 directory. * jffs2 directory.
* *
* $Id: jffs2.h,v 1.30 2003/02/15 00:15:22 dwmw2 Exp $ * $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $
* *
*/ */
......
/* $Id: jffs2_fs_sb.h,v 1.37 2003/01/17 16:04:44 dwmw2 Exp $ */ /* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */
#ifndef _JFFS2_FS_SB #ifndef _JFFS2_FS_SB
#define _JFFS2_FS_SB #define _JFFS2_FS_SB
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#define JFFS2_SB_FLAG_RO 1 #define JFFS2_SB_FLAG_RO 1
#define JFFS2_SB_FLAG_MOUNTING 2 #define JFFS2_SB_FLAG_MOUNTING 2
struct jffs2_inodirty;
/* A struct for the overall file system control. Pointers to /* A struct for the overall file system control. Pointers to
jffs2_sb_info structs are named `c' in the source code. jffs2_sb_info structs are named `c' in the source code.
Nee jffs_control Nee jffs_control
...@@ -52,6 +54,15 @@ struct jffs2_sb_info { ...@@ -52,6 +54,15 @@ struct jffs2_sb_info {
uint32_t nr_free_blocks; uint32_t nr_free_blocks;
uint32_t nr_erasing_blocks; uint32_t nr_erasing_blocks;
/* Number of free blocks there must be before we... */
uint8_t resv_blocks_write; /* ... allow a normal filesystem write */
uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */
uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */
uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */
uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */
uint32_t nospc_dirty_size;
uint32_t nr_blocks; uint32_t nr_blocks;
struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks
* from the offset (blocks[ofs / sector_size]) */ * from the offset (blocks[ofs / sector_size]) */
...@@ -84,13 +95,20 @@ struct jffs2_sb_info { ...@@ -84,13 +95,20 @@ struct jffs2_sb_info {
to an obsoleted node. I don't like this. Alternatives welcomed. */ to an obsoleted node. I don't like this. Alternatives welcomed. */
struct semaphore erase_free_sem; struct semaphore erase_free_sem;
#ifdef CONFIG_JFFS2_FS_NAND
/* Write-behind buffer for NAND flash */ /* Write-behind buffer for NAND flash */
unsigned char *wbuf; unsigned char *wbuf;
uint32_t wbuf_ofs; uint32_t wbuf_ofs;
uint32_t wbuf_len; uint32_t wbuf_len;
uint32_t wbuf_pagesize; uint32_t wbuf_pagesize;
struct work_struct wbuf_task; /* task for timed wbuf flush */ struct jffs2_inodirty *wbuf_inodes;
struct timer_list wbuf_timer; /* timer for flushing wbuf */
/* Information about out-of-band area usage... */
struct nand_oobinfo *oobinfo;
uint32_t badblock_pos;
uint32_t fsdata_pos;
uint32_t fsdata_len;
#endif
/* OS-private pointer for getting back to master superblock info */ /* OS-private pointer for getting back to master superblock info */
void *os_priv; 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