summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@infradead.org>2003-05-28 18:03:33 +0100
committerDavid Woodhouse <dwmw2@infradead.org>2003-05-28 18:03:33 +0100
commit5af017c0de780cb463b784ef909406ce397332ee (patch)
treeecfae73f9c51082ebe4cbe6d228adcba21ca524d /fs
parent015498d534572f8a9c3bf5f1dfc02bd02bfb2c9d (diff)
MTD and JFFS2 update.
- JFFS2 bugfixes and performance improvements - Support for 64-bit flash arrangements - Optimise for linear mappings of flash, without out-of-line access functions - New map drivers - Updated NAND flash support, new board drivers - Support for DiskOnChip Millennium Plus and INFTL translation layer - Clean up all translation layers with a single blkdev helper library. - Fix races in MTD device registration/deregistration - Add support for new flash chips - Clean up partition parsing code More detailed comments in per-file changelogs.
Diffstat (limited to 'fs')
-rw-r--r--fs/jffs2/background.c30
-rw-r--r--fs/jffs2/build.c28
-rw-r--r--fs/jffs2/compr.c5
-rw-r--r--fs/jffs2/compr_rtime.c6
-rw-r--r--fs/jffs2/compr_zlib.c129
-rw-r--r--fs/jffs2/dir.c31
-rw-r--r--fs/jffs2/erase.c74
-rw-r--r--fs/jffs2/file.c36
-rw-r--r--fs/jffs2/fs.c58
-rw-r--r--fs/jffs2/gc.c436
-rw-r--r--fs/jffs2/malloc.c41
-rw-r--r--fs/jffs2/nodelist.c135
-rw-r--r--fs/jffs2/nodelist.h31
-rw-r--r--fs/jffs2/nodemgmt.c114
-rw-r--r--fs/jffs2/os-linux.h35
-rw-r--r--fs/jffs2/read.c9
-rw-r--r--fs/jffs2/readinode.c261
-rw-r--r--fs/jffs2/scan.c41
-rw-r--r--fs/jffs2/super.c23
-rw-r--r--fs/jffs2/wbuf.c178
-rw-r--r--fs/jffs2/write.c42
21 files changed, 1211 insertions, 532 deletions
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c
index f72b1107c766..3cc44044a70a 100644
--- a/fs/jffs2/background.c
+++ b/fs/jffs2/background.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: background.c,v 1.33 2002/11/12 09:44:30 dwmw2 Exp $
+ * $Id: background.c,v 1.38 2003/05/26 09:50:38 dwmw2 Exp $
*
*/
@@ -16,9 +16,8 @@
#include <linux/kernel.h>
#include <linux/jffs2.h>
#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
#include <linux/completion.h>
-#include <linux/mtd/compatmac.h> /* recalc_sigpending() */
+#include <linux/sched.h>
#include <linux/unistd.h>
#include "nodelist.h"
@@ -28,10 +27,10 @@ static int thread_should_wake(struct jffs2_sb_info *c);
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
{
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
if (c->gc_task && thread_should_wake(c))
send_sig(SIGHUP, c->gc_task, 1);
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
}
/* This must only ever be called when no GC thread is currently running */
@@ -69,12 +68,12 @@ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
flush_scheduled_work();
}
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
if (c->gc_task) {
D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
send_sig(SIGKILL, c->gc_task, 1);
}
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
wait_for_completion(&c->gc_thread_exit);
}
@@ -126,9 +125,9 @@ static int jffs2_garbage_collect_thread(void *_c)
case SIGKILL:
D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
c->gc_task = NULL;
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
complete_and_exit(&c->gc_thread_exit, 0);
case SIGHUP:
@@ -152,6 +151,7 @@ static int jffs2_garbage_collect_thread(void *_c)
static int thread_should_wake(struct jffs2_sb_info *c)
{
int ret = 0;
+ uint32_t dirty;
if (c->unchecked_size) {
D1(printk(KERN_DEBUG "thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
@@ -159,8 +159,18 @@ static int thread_should_wake(struct jffs2_sb_info *c)
return 1;
}
+ /* dirty_size contains blocks on erase_pending_list
+ * those blocks are counted in c->nr_erasing_blocks.
+ * If one block is actually erased, it is not longer counted as dirty_space
+ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
+ * with c->nr_erasing_blocks * c->sector_size again.
+ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
+ * This helps us to force gc and pick eventually a clean block to spread the load.
+ */
+ 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 &&
- (c->dirty_size > c->sector_size))
+ (dirty > c->sector_size))
ret = 1;
D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
index 2049d0d1e4d2..01a575d6bfae 100644
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -7,19 +7,42 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: build.c,v 1.42 2002/09/09 16:29:08 dwmw2 Exp $
+ * $Id: build.c,v 1.46 2003/04/29 17:12:26 gleixner Exp $
*
*/
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include "nodelist.h"
int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+static inline struct jffs2_inode_cache *
+first_inode_chain(int *i, struct jffs2_sb_info *c)
+{
+ for (; *i < INOCACHE_HASHSIZE; (*i)++) {
+ if (c->inocache_list[*i])
+ return c->inocache_list[*i];
+ }
+ return NULL;
+}
+
+static inline struct jffs2_inode_cache *
+next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
+{
+ /* More in this chain? */
+ if (ic->next)
+ return ic->next;
+ (*i)++;
+ return first_inode_chain(i, c);
+}
-#define for_each_inode(i, c, ic) for (i=0; i<INOCACHE_HASHSIZE; i++) for (ic=c->inocache_list[i]; ic; ic=ic->next)
+#define for_each_inode(i, c, ic) \
+ for (i = 0, ic = first_inode_chain(&i, (c)); \
+ ic; \
+ ic = next_inode(&i, ic, (c)))
/* Scan plan:
- Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
@@ -229,6 +252,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
init_MUTEX(&c->alloc_sem);
init_MUTEX(&c->erase_free_sem);
init_waitqueue_head(&c->erase_wait);
+ init_waitqueue_head(&c->inocache_wq);
spin_lock_init(&c->erase_completion_lock);
spin_lock_init(&c->inocache_lock);
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
index 5750fc2aac6e..a2479e837acd 100644
--- a/fs/jffs2/compr.c
+++ b/fs/jffs2/compr.c
@@ -7,14 +7,15 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: compr.c,v 1.24 2002/05/20 14:56:37 dwmw2 Exp $
+ * $Id: compr.c,v 1.26 2003/01/12 13:21:28 dwmw2 Exp $
*
*/
-#ifdef __KERNEL__
+#if defined(__KERNEL__) || defined (__ECOS)
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
+#include <linux/types.h>
#else
#define KERN_DEBUG
#define KERN_NOTICE
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c
index 83dfe53fe91b..9dec50194a2c 100644
--- a/fs/jffs2/compr_rtime.c
+++ b/fs/jffs2/compr_rtime.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: compr_rtime.c,v 1.9 2002/05/20 14:56:37 dwmw2 Exp $
+ * $Id: compr_rtime.c,v 1.10 2003/05/11 10:47:13 dwmw2 Exp $
*
*
* Very simple lz77-ish encoder.
@@ -30,7 +30,7 @@
int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
- int positions[256];
+ short positions[256];
int outpos = 0;
int pos=0;
@@ -70,7 +70,7 @@ int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
{
- int positions[256];
+ short positions[256];
int outpos = 0;
int pos=0;
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c
index e1dbbbf169ec..76ad2570a301 100644
--- a/fs/jffs2/compr_zlib.c
+++ b/fs/jffs2/compr_zlib.c
@@ -7,20 +7,22 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: compr_zlib.c,v 1.18 2002/05/20 14:56:37 dwmw2 Exp $
+ * $Id: compr_zlib.c,v 1.23 2003/05/26 09:15:19 dwmw2 Exp $
*
*/
-#ifndef __KERNEL__
+#if !defined(__KERNEL__) && !defined(__ECOS)
#error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
#endif
#include <linux/config.h>
#include <linux/kernel.h>
-#include <linux/mtd/compatmac.h> /* for min() */
+#include <linux/vmalloc.h>
+#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/jffs2.h>
#include <linux/zlib.h>
+#include <linux/zutil.h>
+#include <asm/semaphore.h>
#include "nodelist.h"
/* Plan: call deflate() with avail_in == *sourcelen,
@@ -34,21 +36,21 @@
static DECLARE_MUTEX(deflate_sem);
static DECLARE_MUTEX(inflate_sem);
-static void *deflate_workspace;
-static void *inflate_workspace;
+static z_stream inf_strm, def_strm;
+#ifdef __KERNEL__ /* Linux-only */
int __init jffs2_zlib_init(void)
{
- deflate_workspace = vmalloc(zlib_deflate_workspacesize());
- if (!deflate_workspace) {
+ def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
+ if (!def_strm.workspace) {
printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
return -ENOMEM;
}
D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
- inflate_workspace = vmalloc(zlib_inflate_workspacesize());
- if (!inflate_workspace) {
+ inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
+ if (!inf_strm.workspace) {
printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
- vfree(deflate_workspace);
+ vfree(def_strm.workspace);
return -ENOMEM;
}
D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
@@ -57,97 +59,120 @@ int __init jffs2_zlib_init(void)
void jffs2_zlib_exit(void)
{
- vfree(deflate_workspace);
- vfree(inflate_workspace);
+ vfree(def_strm.workspace);
+ vfree(inf_strm.workspace);
}
+#endif /* __KERNEL__ */
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen)
{
- z_stream strm;
int ret;
if (*dstlen <= STREAM_END_SPACE)
return -1;
down(&deflate_sem);
- strm.workspace = deflate_workspace;
- if (Z_OK != zlib_deflateInit(&strm, 3)) {
+ if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
printk(KERN_WARNING "deflateInit failed\n");
up(&deflate_sem);
return -1;
}
- strm.next_in = data_in;
- strm.total_in = 0;
+ def_strm.next_in = data_in;
+ def_strm.total_in = 0;
- strm.next_out = cpage_out;
- strm.total_out = 0;
+ def_strm.next_out = cpage_out;
+ def_strm.total_out = 0;
- while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
- strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
- strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
+ while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
+ def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
+ def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
- strm.avail_in, strm.avail_out));
- ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH);
+ def_strm.avail_in, def_strm.avail_out));
+ ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
- strm.avail_in, strm.avail_out, strm.total_in, strm.total_out));
+ def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
if (ret != Z_OK) {
D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
- zlib_deflateEnd(&strm);
+ zlib_deflateEnd(&def_strm);
up(&deflate_sem);
return -1;
}
}
- strm.avail_out += STREAM_END_SPACE;
- strm.avail_in = 0;
- ret = zlib_deflate(&strm, Z_FINISH);
- zlib_deflateEnd(&strm);
- up(&deflate_sem);
+ def_strm.avail_out += STREAM_END_SPACE;
+ def_strm.avail_in = 0;
+ ret = zlib_deflate(&def_strm, Z_FINISH);
+ zlib_deflateEnd(&def_strm);
+
if (ret != Z_STREAM_END) {
D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
- return -1;
+ ret = -1;
+ goto out;
}
- D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
- strm.total_in, strm.total_out));
+ if (def_strm.total_out >= def_strm.total_in) {
+ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
+ def_strm.total_in, def_strm.total_out));
+ ret = -1;
+ goto out;
+ }
- if (strm.total_out >= strm.total_in)
- return -1;
+ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
+ def_strm.total_in, def_strm.total_out));
- *dstlen = strm.total_out;
- *sourcelen = strm.total_in;
- return 0;
+ *dstlen = def_strm.total_out;
+ *sourcelen = def_strm.total_in;
+ ret = 0;
+ out:
+ up(&deflate_sem);
+ return ret;
}
void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen)
{
- z_stream strm;
int ret;
+ int wbits = MAX_WBITS;
down(&inflate_sem);
- strm.workspace = inflate_workspace;
- if (Z_OK != zlib_inflateInit(&strm)) {
+ inf_strm.next_in = data_in;
+ inf_strm.avail_in = srclen;
+ inf_strm.total_in = 0;
+
+ inf_strm.next_out = cpage_out;
+ inf_strm.avail_out = destlen;
+ inf_strm.total_out = 0;
+
+ /* If it's deflate, and it's got no preset dictionary, then
+ we can tell zlib to skip the adler32 check. */
+ if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
+ ((data_in[0] & 0x0f) == Z_DEFLATED) &&
+ !(((data_in[0]<<8) + data_in[1]) % 31)) {
+
+ D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
+ wbits = -((data_in[0] >> 4) + 8);
+ inf_strm.next_in += 2;
+ inf_strm.avail_in -= 2;
+ } else {
+ /* Let this remain D1 for now -- it should never happen */
+ D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
+ }
+
+
+ if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
printk(KERN_WARNING "inflateInit failed\n");
up(&inflate_sem);
return;
}
- strm.next_in = data_in;
- strm.avail_in = srclen;
- strm.total_in = 0;
-
- strm.next_out = cpage_out;
- strm.avail_out = destlen;
- strm.total_out = 0;
- while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK)
+ while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
;
if (ret != Z_STREAM_END) {
printk(KERN_NOTICE "inflate returned %d\n", ret);
}
- zlib_inflateEnd(&strm);
+ zlib_inflateEnd(&inf_strm);
up(&inflate_sem);
}
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 9c60f565699a..dceb9cbce3b9 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: dir.c,v 1.73 2002/08/26 15:00:51 dwmw2 Exp $
+ * $Id: dir.c,v 1.76 2003/05/26 09:50:38 dwmw2 Exp $
*
*/
@@ -16,13 +16,20 @@
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/crc32.h>
-#include <linux/mtd/compatmac.h> /* For completion */
#include <linux/jffs2.h>
#include <linux/jffs2_fs_i.h>
#include <linux/jffs2_fs_sb.h>
#include <linux/time.h>
#include "nodelist.h"
+/* Urgh. Please tell me there's a nicer way of doing this. */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
+typedef int mknod_arg_t;
+#else
+typedef dev_t mknod_arg_t;
+#endif
+
static int jffs2_readdir (struct file *, void *, filldir_t);
static int jffs2_create (struct inode *,struct dentry *,int);
@@ -32,7 +39,7 @@ static int jffs2_unlink (struct inode *,struct dentry *);
static int jffs2_symlink (struct inode *,struct dentry *,const char *);
static int jffs2_mkdir (struct inode *,struct dentry *,int);
static int jffs2_rmdir (struct inode *,struct dentry *);
-static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t);
+static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t);
static int jffs2_rename (struct inode *, struct dentry *,
struct inode *, struct dentry *);
@@ -211,8 +218,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode)
return ret;
}
- dir_i->i_mtime.tv_sec = dir_i->i_ctime.tv_sec = je32_to_cpu(ri->ctime);
- dir_i->i_mtime.tv_nsec = dir_i->i_ctime.tv_nsec = 0;
+ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
jffs2_free_raw_inode(ri);
d_instantiate(dentry, inode);
@@ -402,8 +408,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
return PTR_ERR(fd);
}
- dir_i->i_mtime.tv_sec = dir_i->i_ctime.tv_sec = je32_to_cpu(rd->mctime);
- dir_i->i_mtime.tv_nsec = dir_i->i_ctime.tv_nsec = 0;
+ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
jffs2_free_raw_dirent(rd);
@@ -540,8 +545,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
return PTR_ERR(fd);
}
- dir_i->i_mtime.tv_sec = dir_i->i_ctime.tv_sec = je32_to_cpu(rd->mctime);
- dir_i->i_mtime.tv_nsec = dir_i->i_ctime.tv_nsec = 0;
+ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
dir_i->i_nlink++;
jffs2_free_raw_dirent(rd);
@@ -573,7 +577,7 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)
return ret;
}
-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev)
+static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
{
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;
@@ -583,7 +587,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
int namelen;
- unsigned short dev;
+ jint16_t dev;
int devlen = 0;
uint32_t alloclen, phys_ofs;
uint32_t writtenlen;
@@ -596,7 +600,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
c = JFFS2_SB_INFO(dir_i->i_sb);
if (S_ISBLK(mode) || S_ISCHR(mode)) {
- dev = (MAJOR(rdev) << 8) | MINOR(rdev);
+ dev = cpu_to_je16((MAJOR(rdev) << 8) | MINOR(rdev));
devlen = sizeof(dev);
}
@@ -704,8 +708,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
return PTR_ERR(fd);
}
- dir_i->i_mtime.tv_sec = dir_i->i_ctime.tv_sec = je32_to_cpu(rd->mctime);
- dir_i->i_mtime.tv_nsec = dir_i->i_ctime.tv_nsec = 0;
+ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
jffs2_free_raw_dirent(rd);
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index ada5721b730f..6e4327692e8d 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -7,16 +7,17 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: erase.c,v 1.45 2002/10/09 08:27:08 dwmw2 Exp $
+ * $Id: erase.c,v 1.51 2003/05/11 22:47:36 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
#include <linux/compiler.h>
#include <linux/crc32.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
#include "nodelist.h"
struct erase_priv_struct {
@@ -24,7 +25,10 @@ struct erase_priv_struct {
struct jffs2_sb_info *c;
};
+#ifndef __ECOS
static void jffs2_erase_callback(struct erase_info *);
+#endif
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
@@ -32,16 +36,25 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
int ret;
+#ifdef __ECOS
+ ret = jffs2_flash_erase(c, jeb);
+ if (!ret) {
+ jffs2_erase_succeeded(c, jeb);
+ return;
+ }
+#else /* Linux */
struct erase_info *instr;
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
- spin_unlock_bh(&c->erase_completion_lock);
+ c->dirty_size += c->sector_size;
+ jeb->dirty_size = c->sector_size;
+ spin_unlock(&c->erase_completion_lock);
return;
}
@@ -65,15 +78,18 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
return;
kfree(instr);
+#endif /* __ECOS */
if (ret == -ENOMEM || ret == -EAGAIN) {
/* Erase failed immediately. Refile it on the list */
D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
list_del(&jeb->list);
list_add(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
- spin_unlock_bh(&c->erase_completion_lock);
+ c->dirty_size += c->sector_size;
+ jeb->dirty_size = c->sector_size;
+ spin_unlock(&c->erase_completion_lock);
return;
}
@@ -82,20 +98,7 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
else
printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
- /* Note: This is almost identical to jffs2_erase_failed() except
- for the fact that we used spin_lock_bh() not spin_lock(). If
- we could use spin_lock_bh() from a BH, we could merge them.
- Or if we abandon the idea that MTD drivers may call the erase
- callback from a BH, I suppose :)
- */
- spin_lock_bh(&c->erase_completion_lock);
- c->erasing_size -= c->sector_size;
- c->bad_size += c->sector_size;
- list_del(&jeb->list);
- list_add(&jeb->list, &c->bad_list);
- c->nr_erasing_blocks--;
- spin_unlock_bh(&c->erase_completion_lock);
- wake_up(&c->erase_wait);
+ jffs2_erase_failed(c, jeb);
}
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
@@ -104,7 +107,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
down(&c->erase_free_sem);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
while (!list_empty(&c->erase_complete_list) ||
!list_empty(&c->erase_pending_list)) {
@@ -112,7 +115,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
if (!list_empty(&c->erase_complete_list)) {
jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
list_del(&jeb->list);
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
jffs2_mark_erased_block(c, jeb);
} else if (!list_empty(&c->erase_pending_list)) {
@@ -126,7 +129,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
jffs2_free_all_node_refs(c, jeb);
list_add(&jeb->list, &c->erasing_list);
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
jffs2_erase_block(c, jeb);
@@ -136,10 +139,10 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
/* Be nice */
cond_resched();
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
}
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
up(&c->erase_free_sem);
@@ -156,8 +159,7 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo
jffs2_erase_pending_trigger(c);
}
-
-static inline void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
@@ -169,6 +171,7 @@ static inline void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eras
wake_up(&c->erase_wait);
}
+#ifndef __ECOS
static void jffs2_erase_callback(struct erase_info *instr)
{
struct erase_priv_struct *priv = (void *)instr->priv;
@@ -181,6 +184,7 @@ static void jffs2_erase_callback(struct erase_info *instr)
}
kfree(instr);
}
+#endif /* !__ECOS */
/* Hmmm. Maybe we should accept the extra space it takes and make
this a standard doubly-linked list? */
@@ -290,9 +294,9 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
/* Stick it back on the list from whence it came and come back later */
jffs2_erase_pending_trigger(c);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
list_add(&jeb->list, &c->erase_complete_list);
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
return;
}
}
@@ -313,7 +317,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
goto bad;
}
if (retlen != readlen) {
- printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen);
+ printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
goto bad;
}
for (i=0; i<readlen; i += sizeof(unsigned long)) {
@@ -328,13 +332,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
jffs2_write_nand_badblock( c ,jeb );
kfree(ebuf);
bad2:
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
list_add_tail(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
return;
}
@@ -374,7 +378,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
goto bad2;
}
if (retlen != je32_to_cpu(marker.totlen)) {
- printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n",
+ printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
jeb->offset, je32_to_cpu(marker.totlen), retlen);
goto bad2;
}
@@ -392,7 +396,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
jeb->wasted_size = 0;
}
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->free_size += jeb->free_size;
c->used_size += jeb->used_size;
@@ -403,7 +407,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
list_add_tail(&jeb->list, &c->free_list);
c->nr_erasing_blocks--;
c->nr_free_blocks++;
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
wake_up(&c->erase_wait);
}
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index f7fadfa10640..4d241373dc15 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -7,12 +7,11 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: file.c,v 1.81 2002/11/12 09:46:22 dwmw2 Exp $
+ * $Id: file.c,v 1.85 2003/05/26 09:50:38 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
-#include <linux/mtd/compatmac.h> /* for min() */
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/time.h>
@@ -153,17 +152,20 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
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_je32(iattr->ia_mode & ~S_ISGID);
+ ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID);
else
- ri->mode = cpu_to_je32(iattr->ia_mode);
+ ri->mode = cpu_to_jemode(iattr->ia_mode);
else
- ri->mode = cpu_to_je32(inode->i_mode);
+ 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((ivalid & ATTR_ATIME)?iattr->ia_atime.tv_sec:inode->i_atime.tv_sec);
- ri->mtime = cpu_to_je32((ivalid & ATTR_MTIME)?iattr->ia_mtime.tv_sec:inode->i_mtime.tv_sec);
- ri->ctime = cpu_to_je32((ivalid & ATTR_CTIME)?iattr->ia_ctime.tv_sec:inode->i_ctime.tv_sec);
+ 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 */
@@ -188,13 +190,10 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
return PTR_ERR(new_metadata);
}
/* It worked. Update the inode */
- inode->i_atime.tv_sec = je32_to_cpu(ri->atime);
- inode->i_ctime.tv_sec = je32_to_cpu(ri->ctime);
- inode->i_mtime.tv_sec = je32_to_cpu(ri->mtime);
- inode->i_atime.tv_nsec =
- inode->i_ctime.tv_nsec =
- inode->i_mtime.tv_nsec = 0;
- inode->i_mode = je32_to_cpu(ri->mode);
+ 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);
@@ -309,7 +308,7 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
- ri.mode = cpu_to_je32(inode->i_mode);
+ ri.mode = cpu_to_jemode(inode->i_mode);
ri.uid = cpu_to_je16(inode->i_uid);
ri.gid = cpu_to_je16(inode->i_gid);
ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
@@ -390,7 +389,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi
/* Set the fields that the generic jffs2_write_inode_range() code can't find */
ri->ino = cpu_to_je32(inode->i_ino);
- ri->mode = cpu_to_je32(inode->i_mode);
+ ri->mode = cpu_to_jemode(inode->i_mode);
ri->uid = cpu_to_je16(inode->i_uid);
ri->gid = cpu_to_je16(inode->i_gid);
ri->isize = cpu_to_je32((uint32_t)inode->i_size);
@@ -416,8 +415,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi
inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
inode->i_blocks = (inode->i_size + 511) >> 9;
- inode->i_ctime.tv_sec = inode->i_mtime.tv_sec = je32_to_cpu(ri->ctime);
- inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0;
+ inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
}
}
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index b785d9a36055..a027504cd82d 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: fs.c,v 1.19 2002/11/12 09:53:40 dwmw2 Exp $
+ * $Id: fs.c,v 1.24 2003/04/29 09:52:58 dwmw2 Exp $
*
*/
@@ -16,7 +16,6 @@
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/list.h>
-#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
@@ -35,7 +34,7 @@ int jffs2_statfs(struct super_block *sb, struct statfs *buf)
buf->f_ffree = 0;
buf->f_namelen = JFFS2_MAX_NAME_LEN;
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
avail = c->dirty_size + c->free_size;
if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
@@ -47,7 +46,7 @@ int jffs2_statfs(struct super_block *sb, struct statfs *buf)
D1(jffs2_dump_block_lists(c));
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
return 0;
}
@@ -87,16 +86,13 @@ void jffs2_read_inode (struct inode *inode)
up(&f->sem);
return;
}
- inode->i_mode = je32_to_cpu(latest_node.mode);
+ inode->i_mode = jemode_to_cpu(latest_node.mode);
inode->i_uid = je16_to_cpu(latest_node.uid);
inode->i_gid = je16_to_cpu(latest_node.gid);
inode->i_size = je32_to_cpu(latest_node.isize);
- inode->i_atime.tv_sec = je32_to_cpu(latest_node.atime);
- inode->i_mtime.tv_sec = je32_to_cpu(latest_node.mtime);
- inode->i_ctime.tv_sec = je32_to_cpu(latest_node.ctime);
- inode->i_atime.tv_nsec =
- inode->i_mtime.tv_nsec =
- inode->i_ctime.tv_nsec = 0;
+ inode->i_atime = ITIME(je32_to_cpu(latest_node.atime));
+ inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
+ inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
inode->i_nlink = f->inocache->nlink;
@@ -104,7 +100,7 @@ void jffs2_read_inode (struct inode *inode)
inode->i_blocks = (inode->i_size + 511) >> 9;
switch (inode->i_mode & S_IFMT) {
- unsigned short rdev;
+ jint16_t rdev;
case S_IFLNK:
inode->i_op = &jffs2_symlink_inode_operations;
@@ -151,7 +147,7 @@ void jffs2_read_inode (struct inode *inode)
case S_IFSOCK:
case S_IFIFO:
inode->i_op = &jffs2_file_inode_operations;
- init_special_inode(inode, inode->i_mode, kdev_t_to_nr(mk_kdev(rdev>>8, rdev&0xff)));
+ init_special_inode(inode, inode->i_mode, kdev_t_to_nr(mk_kdev(je16_to_cpu(rdev)>>8, je16_to_cpu(rdev)&0xff)));
break;
default:
@@ -232,7 +228,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i
} else {
ri->gid = cpu_to_je16(current->fsgid);
}
- ri->mode = cpu_to_je32(mode);
+ ri->mode = cpu_to_jemode(mode);
ret = jffs2_do_new_inode (c, f, mode, ri);
if (ret) {
make_bad_inode(inode);
@@ -241,12 +237,11 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i
}
inode->i_nlink = 1;
inode->i_ino = je32_to_cpu(ri->ino);
- inode->i_mode = je32_to_cpu(ri->mode);
+ inode->i_mode = jemode_to_cpu(ri->mode);
inode->i_gid = je16_to_cpu(ri->gid);
inode->i_uid = je16_to_cpu(ri->uid);
- inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0;
- inode->i_atime.tv_sec = inode->i_ctime.tv_sec = inode->i_mtime.tv_sec = get_seconds();
- ri->atime = ri->mtime = ri->ctime = cpu_to_je32(inode->i_mtime.tv_sec);
+ inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime));
inode->i_blksize = PAGE_SIZE;
inode->i_blocks = 0;
@@ -263,27 +258,32 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
struct jffs2_sb_info *c;
struct inode *root_i;
int ret;
+ size_t blocks;
c = JFFS2_SB_INFO(sb);
- c->sector_size = c->mtd->erasesize;
c->flash_size = c->mtd->size;
-#if 0
- if (c->sector_size < 0x10000) {
- printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using 64KiB instead\n",
- c->sector_size / 1024);
- c->sector_size = 0x10000;
- }
-#endif
+ /*
+ * Check, if we have to concatenate physical blocks to larger virtual blocks
+ * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
+ */
+ blocks = c->flash_size / c->mtd->erasesize;
+ while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024))
+ blocks >>= 1;
+
+ c->sector_size = c->flash_size / blocks;
+ if (c->sector_size != c->mtd->erasesize)
+ printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n",
+ c->mtd->erasesize / 1024, c->sector_size / 1024);
+
if (c->flash_size < 5*c->sector_size) {
- printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n",
- c->flash_size / c->sector_size);
+ printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size);
return -EINVAL;
}
c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
- /* Jörn -- stick alignment for weird 8-byte-page flash here */
+ /* Joern -- stick alignment for weird 8-byte-page flash here */
if (jffs2_cleanmarker_oob(c)) {
/* Cleanmarker is out-of-band, so inline size zero */
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 6a36bbbd0e5a..a715475bdec7 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -7,19 +7,22 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: gc.c,v 1.88 2002/10/08 16:56:08 dwmw2 Exp $
+ * $Id: gc.c,v 1.103 2003/05/22 18:01:02 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/slab.h>
-#include <linux/interrupt.h>
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include <linux/compiler.h>
+#include <linux/stat.h>
#include "nodelist.h"
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
+ struct jffs2_inode_cache *ic,
+ struct jffs2_raw_node_ref *raw);
static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
@@ -32,6 +35,8 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end);
+static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic);
/* Called with erase_completion_lock held */
static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
@@ -107,61 +112,87 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
*/
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
{
+ struct jffs2_inode_cache *ic;
struct jffs2_eraseblock *jeb;
- struct jffs2_inode_info *f;
struct jffs2_raw_node_ref *raw;
- struct jffs2_node_frag *frag;
- struct jffs2_full_dnode *fn = NULL;
- struct jffs2_full_dirent *fd;
- uint32_t start = 0, end = 0, nrfrags = 0;
uint32_t inum;
- struct inode *inode;
int ret = 0;
if (down_interruptible(&c->alloc_sem))
return -EINTR;
- spin_lock_bh(&c->erase_completion_lock);
+ for (;;) {
+ spin_lock(&c->erase_completion_lock);
+ if (!c->unchecked_size)
+ break;
- while (c->unchecked_size) {
/* We can't start doing GC yet. We haven't finished checking
- the node CRCs etc. Do it now and wait for it. */
- struct jffs2_inode_cache *ic;
-
+ the node CRCs etc. Do it now. */
+
+ /* checked_ino is protected by the alloc_sem */
if (c->checked_ino > c->highest_ino) {
printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
c->unchecked_size);
D1(jffs2_dump_block_lists(c));
+ spin_unlock(&c->erase_completion_lock);
BUG();
}
+
+ spin_unlock(&c->erase_completion_lock);
+
+ spin_lock(&c->inocache_lock);
+
ic = jffs2_get_ino_cache(c, c->checked_ino++);
- if (!ic)
+
+ if (!ic) {
+ spin_unlock(&c->inocache_lock);
continue;
+ }
+
if (!ic->nlink) {
D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
ic->ino));
+ spin_unlock(&c->inocache_lock);
continue;
}
- if (ic->state != INO_STATE_UNCHECKED) {
- D1(printk(KERN_DEBUG "Skipping check of ino #%d already in state %d\n",
- ic->ino, ic->state));
+ switch(ic->state) {
+ case INO_STATE_CHECKEDABSENT:
+ case INO_STATE_PRESENT:
+ D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino));
+ spin_unlock(&c->inocache_lock);
continue;
- }
- spin_unlock_bh(&c->erase_completion_lock);
+ case INO_STATE_GC:
+ case INO_STATE_CHECKING:
+ printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state);
+ spin_unlock(&c->inocache_lock);
+ BUG();
- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino));
-
- {
- /* XXX: This wants doing more sensibly -- split the core of jffs2_do_read_inode up */
- struct inode *i = iget(OFNI_BS_2SFFJ(c), ic->ino);
- if (is_bad_inode(i)) {
- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
- ret = -EIO;
- }
- iput(i);
+ case INO_STATE_READING:
+ /* We need to wait for it to finish, lest we move on
+ and trigger the BUG() above while we haven't yet
+ finished checking all its nodes */
+ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
+ up(&c->alloc_sem);
+ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+ return 0;
+
+ default:
+ BUG();
+
+ case INO_STATE_UNCHECKED:
+ ;
}
+ ic->state = INO_STATE_CHECKING;
+ spin_unlock(&c->inocache_lock);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino));
+
+ ret = jffs2_do_crccheck_inode(c, ic);
+ if (ret)
+ printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino);
+
+ jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
up(&c->alloc_sem);
return ret;
}
@@ -174,7 +205,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
if (!jeb) {
printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
return -EIO;
}
@@ -197,7 +228,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
BUG();
}
@@ -207,7 +238,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
/* Inode-less node. Clean marker, snapshot or something like that */
/* FIXME: If it's something that needs to be copied, including something
we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
jffs2_mark_node_obsolete(c, raw);
up(&c->alloc_sem);
goto eraseit_lock;
@@ -216,13 +247,136 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
inum = jffs2_raw_ref_to_inum(raw);
D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
+
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum));
+
+ /* Three possibilities:
+ 1. Inode is already in-core. We must iget it and do proper
+ updating to its fragtree, etc.
+ 2. Inode is not in-core, node is REF_PRISTINE. We lock the
+ inocache to prevent a read_inode(), copy the node intact.
+ 3. Inode is not in-core, node is not pristine. We must iget()
+ and take the slow path.
+ */
+ spin_lock(&c->inocache_lock);
+ ic = jffs2_get_ino_cache(c, inum);
+
+ /* This should never fail unless I'm particularly stupid.
+ So we don't check before dereferencing it */
+
+ switch(ic->state) {
+ case INO_STATE_CHECKEDABSENT:
+ /* It's been checked, but it's not currently in-core.
+ We can just copy any pristine nodes, but have
+ to prevent anyone else from doing read_inode() while
+ we're at it, so we set the state accordingly */
+ if (ref_flags(raw) == REF_PRISTINE)
+ ic->state = INO_STATE_GC;
+ else {
+ D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
+ inum));
+ }
+ break;
+
+ case INO_STATE_PRESENT:
+ case INO_STATE_UNCHECKED:
+ /* It's in-core or hasn't been checked. GC must iget() it. */
+ break;
+
+ case INO_STATE_CHECKING:
+ /* Should never happen. We should have finished checking
+ by the time we actually start doing any GC. */
+ BUG();
+
+
+ case INO_STATE_GC:
+ /* Should never happen. We are holding the alloc_sem,
+ no other garbage collection can happen. Note that we
+ do depend on this later when deciding to do a simple
+ node copy */
+ BUG();
+
+ case INO_STATE_READING:
+ /* Someone's currently trying to read it. We must wait for
+ them to finish and then go through the full iget() route
+ to do the GC. However, sometimes read_inode() needs to get
+ the alloc_sem() (for marking nodes invalid) so we must
+ drop the alloc_sem before sleeping. */
+
+ up(&c->alloc_sem);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
+ inum, ic->state));
+ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+ /* And because we dropped the alloc_sem we must start again from the
+ beginning. Ponder chance of livelock here -- we're returning success
+ without actually making any progress.
+
+ Q: What are the chances that the inode is back in INO_STATE_READING
+ again by the time we next enter this function? And that this happens
+ enough times to cause a real delay?
+
+ A: Small enough that I don't care :)
+ */
+ return 0;
+
+ }
+
+ spin_unlock(&c->inocache_lock);
+
+ /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
+ node intact, and we don't have to muck about with the fragtree etc.
+ because we know it's not in-core. If it _was_ in-core, we go through
+ all the iget() crap anyway */
+
+ if (ic->state == INO_STATE_GC) {
+ ret = jffs2_garbage_collect_pristine(c, ic, raw);
+ jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
+
+ if (ret != -EBADFD)
+ goto release_sem;
+
+ /* Fall through if it wanted us to */
+ }
+
+ ret = jffs2_garbage_collect_live(c, jeb, raw, ic);
+
+ release_sem:
+ up(&c->alloc_sem);
+
+ eraseit_lock:
+ /* If we've finished this block, start it erasing */
+ spin_lock(&c->erase_completion_lock);
+
+ eraseit:
+ if (c->gcblock && !c->gcblock->used_size) {
+ D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
+ /* We're GC'ing an empty block? */
+ list_add_tail(&c->gcblock->list, &c->erase_pending_list);
+ c->gcblock = NULL;
+ c->nr_erasing_blocks++;
+ jffs2_erase_pending_trigger(c);
+ }
+ spin_unlock(&c->erase_completion_lock);
+
+ return ret;
+}
- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, ref_offset(raw), inum));
- inode = iget(OFNI_BS_2SFFJ(c), inum);
+static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_inode_info *f;
+ struct jffs2_node_frag *frag;
+ struct jffs2_full_dnode *fn = NULL;
+ struct jffs2_full_dirent *fd;
+ uint32_t start = 0, end = 0, nrfrags = 0;
+ struct inode *inode;
+ int ret = 0;
+
+ inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
if (is_bad_inode(inode)) {
- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum);
+ printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
/* NB. This will happen again. We need to do something appropriate here. */
up(&c->alloc_sem);
iput(inode);
@@ -254,16 +408,26 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
end = frag->ofs + frag->size;
#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */
if (!nrfrags && ref_flags(fn->raw) == REF_PRISTINE) {
- if (fn->frags > 1)
+ if (fn->frags > 1) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(raw), fn->frags);
-
- if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->node)
+ mark_ref_normal(raw);
+ }
+ /* A hole node which isn't multi-page should be garbage-collected
+ and merged anyway, so we just check for the frag size here,
+ rather than mucking around with actually reading the node
+ and checking the compression type, which is the real way
+ to tell a hole node. */
+ if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
ref_offset(raw));
+ mark_ref_normal(raw);
+ }
- if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->node)
+ if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) {
printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
ref_offset(raw), frag->ofs, frag->ofs+frag->size);
+ mark_ref_normal(raw);
+ }
}
#endif
if (!nrfrags++)
@@ -273,6 +437,15 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
}
}
if (fn) {
+ if (ref_flags(raw) == REF_PRISTINE) {
+ ret = jffs2_garbage_collect_pristine(c, ic, raw);
+ if (!ret) {
+ /* Urgh. Return it sensibly. */
+ frag->node->raw = ic->nodes;
+ }
+ if (ret != -EBADFD)
+ goto upnout;
+ }
/* We found a datanode. Do the GC */
if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
/* It crosses a page boundary. Therefore, it must be a hole. */
@@ -305,25 +478,145 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
}
upnout:
up(&f->sem);
- up(&c->alloc_sem);
iput(inode);
- eraseit_lock:
- /* If we've finished this block, start it erasing */
- spin_lock_bh(&c->erase_completion_lock);
+ return ret;
+}
- eraseit:
- if (c->gcblock && !c->gcblock->used_size) {
- D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
- /* We're GC'ing an empty block? */
- list_add_tail(&c->gcblock->list, &c->erase_pending_list);
- c->gcblock = NULL;
- c->nr_erasing_blocks++;
- jffs2_erase_pending_trigger(c);
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
+ struct jffs2_inode_cache *ic,
+ struct jffs2_raw_node_ref *raw)
+{
+ union jffs2_node_union *node;
+ struct jffs2_raw_node_ref *nraw;
+ size_t retlen;
+ int ret;
+ uint32_t phys_ofs, alloclen;
+ uint32_t crc;
+
+ D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
+
+ /* Ask for a small amount of space (or the totlen if smaller) because we
+ don't want to force wastage of the end of a block if splitting would
+ work. */
+ ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen),
+ &phys_ofs, &alloclen);
+ if (ret)
+ return ret;
+
+ if (alloclen < raw->totlen) {
+ /* Doesn't fit untouched. We'll go the old route and split it */
+ return -EBADFD;
+ }
+
+ node = kmalloc(raw->totlen, GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+
+ ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node);
+ if (!ret && retlen != raw->totlen)
+ ret = -EIO;
+ if (ret)
+ goto out_node;
+
+ crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
+ if (je32_to_cpu(node->u.hdr_crc) != crc) {
+ printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
+ goto bail;
+ }
+
+ switch(je16_to_cpu(node->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+ crc = crc32(0, node, sizeof(node->i)-8);
+ if (je32_to_cpu(node->i.node_crc) != crc) {
+ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ ref_offset(raw), je32_to_cpu(node->i.node_crc), crc);
+ goto bail;
+ }
+
+ if (je32_to_cpu(node->i.dsize)) {
+ crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize));
+ if (je32_to_cpu(node->i.data_crc) != crc) {
+ printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ ref_offset(raw), je32_to_cpu(node->i.data_crc), crc);
+ goto bail;
+ }
+ }
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ crc = crc32(0, node, sizeof(node->d)-8);
+ if (je32_to_cpu(node->d.node_crc) != crc) {
+ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
+ goto bail;
+ }
+
+ if (node->d.nsize) {
+ crc = crc32(0, node->d.name, node->d.nsize);
+ if (je32_to_cpu(node->d.name_crc) != crc) {
+ printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
+ goto bail;
+ }
+ }
+ break;
+ default:
+ printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
+ ref_offset(raw), je16_to_cpu(node->u.nodetype));
+ goto bail;
}
- spin_unlock_bh(&c->erase_completion_lock);
+ nraw = jffs2_alloc_raw_node_ref();
+ if (!nraw) {
+ ret = -ENOMEM;
+ goto out_node;
+ }
+ nraw->flash_offset = phys_ofs;
+ nraw->totlen = raw->totlen;
+ 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);
+ if (ret || (retlen != raw->totlen)) {
+ printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
+ raw->totlen, phys_ofs, ret, retlen);
+ if (retlen) {
+ /* Doesn't belong to any inode */
+ nraw->next_in_ino = NULL;
+
+ nraw->flash_offset |= REF_OBSOLETE;
+ jffs2_add_physical_node_ref(c, nraw);
+ jffs2_mark_node_obsolete(c, nraw);
+ } else {
+ 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);
+ }
+ if (!ret)
+ ret = -EIO;
+ goto out_node;
+ }
+ nraw->flash_offset |= REF_PRISTINE;
+ jffs2_add_physical_node_ref(c, nraw);
+
+ /* Link into per-inode list. This is safe because of the ic
+ state being INO_STATE_GC. Note that if we're doing this
+ for an inode which is in-code, the 'nraw' pointer is then
+ going to be fetched from ic->nodes by our caller. */
+ nraw->next_in_ino = ic->nodes;
+ ic->nodes = nraw;
+
+ jffs2_mark_node_obsolete(c, raw);
+ D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
+
+ out_node:
+ kfree(node);
return ret;
+ bail:
+ ret = -EBADFD;
+ goto out_node;
}
static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
@@ -331,7 +624,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
{
struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri;
- unsigned short dev;
+ jint16_t dev;
char *mdata = NULL, mdatalen = 0;
uint32_t alloclen, phys_ofs;
int ret;
@@ -340,8 +633,8 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
S_ISCHR(JFFS2_F_I_MODE(f)) ) {
/* For these, we don't actually need to read the old node */
/* FIXME: for minor or major > 255. */
- dev = ((JFFS2_F_I_RDEV_MAJ(f) << 8) |
- JFFS2_F_I_RDEV_MIN(f));
+ dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) |
+ JFFS2_F_I_RDEV_MIN(f)));
mdata = (char *)&dev;
mdatalen = sizeof(dev);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
@@ -364,7 +657,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
if (ret) {
- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n",
+ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
sizeof(ri)+ mdatalen, ret);
goto out;
}
@@ -377,7 +670,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
- ri.mode = cpu_to_je32(JFFS2_F_I_MODE(f));
+ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
@@ -431,7 +724,7 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
if (ret) {
- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n",
+ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
sizeof(rd)+rd.nsize, ret);
return ret;
}
@@ -489,7 +782,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
continue;
}
if (retlen != sizeof(struct jffs2_unknown_node)) {
- printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading header from obsolete node at %08x\n",
+ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw));
continue;
}
@@ -506,7 +799,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
continue;
}
if (retlen != sizeof(rd)) {
- printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading from obsolete node at %08x\n",
+ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n",
retlen, sizeof(rd), ref_offset(raw));
continue;
}
@@ -534,7 +827,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
continue;
}
if (retlen != name_len+1) {
- printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading name from obsolete node at %08x\n",
+ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n",
retlen, name_len+1, ref_offset(raw));
continue;
}
@@ -597,7 +890,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
write it out again with the _same_ version as before */
ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
if (readlen != sizeof(ri) || ret) {
- printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hole node\n", ret, readlen);
+ printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
goto fill;
}
if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
@@ -607,7 +900,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
return -EIO;
}
if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n",
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
ref_offset(fn->raw),
je32_to_cpu(ri.totlen), sizeof(ri));
return -EIO;
@@ -642,7 +935,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
ri.csize = cpu_to_je32(0);
ri.compr = JFFS2_COMPR_ZERO;
}
- ri.mode = cpu_to_je32(JFFS2_F_I_MODE(f));
+ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
@@ -654,7 +947,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
if (ret) {
- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n",
+ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
sizeof(ri), ret);
return ret;
}
@@ -762,8 +1055,11 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
* page OK. We'll actually write it out again in commit_write, which is a little
* suboptimal, but at least we're correct.
*/
+#ifdef __ECOS
+ pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
+#else
pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
-
+#endif
if (IS_ERR(pg)) {
printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
return PTR_ERR(pg);
@@ -780,11 +1076,11 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
if (ret) {
- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n",
+ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
break;
}
- cdatalen = min(alloclen - sizeof(ri), end - offset);
+ cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
datalen = end - offset;
writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
@@ -804,7 +1100,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
ri.ino = cpu_to_je32(f->inocache->ino);
ri.version = cpu_to_je32(++f->highest_version);
- ri.mode = cpu_to_je32(JFFS2_F_I_MODE(f));
+ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c
index f8b7ba5a46d8..4346f9874a2f 100644
--- a/fs/jffs2/malloc.c
+++ b/fs/jffs2/malloc.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: malloc.c,v 1.22 2002/05/20 14:56:38 dwmw2 Exp $
+ * $Id: malloc.c,v 1.24 2003/03/11 17:30:29 gleixner Exp $
*
*/
@@ -23,6 +23,9 @@
#define JFFS2_SLAB_POISON 0
#endif
+// replace this by #define D3 (x) x for cache debugging
+#define D3(x)
+
/* These are initialised to NULL in the kernel startup code.
If you're porting to other operating systems, beware */
static kmem_cache_t *full_dnode_slab;
@@ -73,8 +76,7 @@ int __init jffs2_create_slab_caches(void)
inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
sizeof(struct jffs2_inode_cache),
- 0, JFFS2_SLAB_POISON|SLAB_RECLAIM_ACCOUNT,
- NULL, NULL);
+ 0, JFFS2_SLAB_POISON, NULL, NULL);
if (inode_cache_slab)
return 0;
err:
@@ -112,75 +114,92 @@ void jffs2_free_full_dirent(struct jffs2_full_dirent *x)
struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
{
- void *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
+ struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
+ D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret));
return ret;
}
void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
{
+ D3 (printk (KERN_DEBUG "free full_dnode at %p\n", x));
kmem_cache_free(full_dnode_slab, x);
}
struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
{
- return kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
+ struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
+ D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret));
+ return ret;
}
void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
{
+ D3 (printk (KERN_DEBUG "free_raw_dirent at %p\n", x));
kmem_cache_free(raw_dirent_slab, x);
}
struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
{
- return kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
+ struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
+ D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret));
+ return ret;
}
void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
{
+ D3 (printk (KERN_DEBUG "free_raw_inode at %p\n", x));
kmem_cache_free(raw_inode_slab, x);
}
struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
{
- return kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
+ struct jffs2_tmp_dnode_info *ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
+ D3 (printk (KERN_DEBUG "alloc_tmp_dnode_info at %p\n", ret));
+ return ret;
}
void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
{
+ D3 (printk (KERN_DEBUG "free_tmp_dnode_info at %p\n", x));
kmem_cache_free(tmp_dnode_info_slab, x);
}
struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
{
- return kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
+ struct jffs2_raw_node_ref *ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
+ D3 (printk (KERN_DEBUG "alloc_raw_node_ref at %p\n", ret));
+ return ret;
}
void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
{
+ D3 (printk (KERN_DEBUG "free_raw_node_ref at %p\n", x));
kmem_cache_free(raw_node_ref_slab, x);
}
struct jffs2_node_frag *jffs2_alloc_node_frag(void)
{
- return kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
+ struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
+ D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret));
+ return ret;
}
void jffs2_free_node_frag(struct jffs2_node_frag *x)
{
+ D3 (printk (KERN_DEBUG "free_node_frag at %p\n", x));
kmem_cache_free(node_frag_slab, x);
}
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
{
struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
- D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
+ D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
return ret;
}
void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
{
- D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x));
+ D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x));
kmem_cache_free(inode_cache_slab, x);
}
diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c
index 4bd3c3d65960..294854d372f7 100644
--- a/fs/jffs2/nodelist.c
+++ b/fs/jffs2/nodelist.c
@@ -7,17 +7,18 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodelist.c,v 1.65 2002/11/12 09:50:13 dwmw2 Exp $
+ * $Id: nodelist.c,v 1.79 2003/04/08 08:20:01 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
#include <linux/rbtree.h>
#include <linux/crc32.h>
#include <linux/slab.h>
+#include <linux/pagemap.h>
#include "nodelist.h"
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
@@ -112,10 +113,10 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino));
if (!f->inocache->nodes) {
- printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino);
+ printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino);
}
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
/* Work out whether it's a data node or a dirent node */
@@ -127,12 +128,12 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
/* We can hold a pointer to a non-obsolete node without the spinlock,
but _obsolete_ nodes may disappear at any time, if the block
they're in gets erased */
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
cond_resched();
/* FIXME: point() */
- err = jffs2_flash_read(c, (ref_offset(ref)), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
+ err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node);
if (err) {
printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
goto free_out;
@@ -140,7 +141,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
/* Check we've managed to read at least the common node header */
- if (retlen < min(ref->totlen, sizeof(node.u))) {
+ if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) {
printk(KERN_WARNING "short read in get_inode_nodes()\n");
err = -EIO;
goto free_out;
@@ -158,6 +159,14 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
err = -EIO;
goto free_out;
}
+ /* sanity check */
+ if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) {
+ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n",
+ ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen));
+ jffs2_mark_node_obsolete(c, ref);
+ spin_lock(&c->erase_completion_lock);
+ continue;
+ }
if (je32_to_cpu(node.d.version) > *highest_version)
*highest_version = je32_to_cpu(node.d.version);
if (ref_obsolete(ref)) {
@@ -166,6 +175,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
ref_offset(ref));
BUG();
}
+
fd = jffs2_alloc_full_dirent(node.d.nsize+1);
if (!fd) {
err = -ENOMEM;
@@ -187,7 +197,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
dirent we've already read from the flash
*/
if (retlen > sizeof(struct jffs2_raw_dirent))
- memcpy(&fd->name[0], &node.d.name[0], min((uint32_t)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
+ memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
/* Do we need to copy any more of the name directly
from the flash?
@@ -244,46 +254,95 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
jffs2_mark_node_obsolete(c, ref);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
+ continue;
+ }
+
+ /* sanity checks */
+ if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) ||
+ PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) {
+ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino %d, version %d, isize %d, csize %d, dsize %d \n",
+ ref_offset(ref), je32_to_cpu(node.i.totlen), je32_to_cpu(node.i.ino),
+ je32_to_cpu(node.i.version), je32_to_cpu(node.i.isize),
+ je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize));
+ jffs2_mark_node_obsolete(c, ref);
+ spin_lock(&c->erase_completion_lock);
continue;
}
if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
- /* FIXME: point() */
- char *buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
- &retlen, buf);
- if (!err && retlen != je32_to_cpu(node.i.csize))
- err = -EIO;
- if (err) {
- kfree(buf);
- return err;
+ unsigned char *buf=NULL;
+ uint32_t pointed = 0;
+#ifndef __ECOS
+ if (c->mtd->point) {
+ err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
+ &retlen, &buf);
+ if (!err && retlen < je32_to_cpu(node.i.csize)) {
+ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
+ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
+ } else if (err){
+ D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
+ } else
+ pointed = 1; /* succefully pointed to device */
+ }
+#endif
+ if(!pointed){
+ buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
+ &retlen, buf);
+ if (!err && retlen != je32_to_cpu(node.i.csize))
+ err = -EIO;
+ if (err) {
+ kfree(buf);
+ return err;
+ }
}
-
crc = crc32(0, buf, je32_to_cpu(node.i.csize));
- kfree(buf);
+ if(!pointed)
+ kfree(buf);
+#ifndef __ECOS
+ else
+ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
+#endif
if (crc != je32_to_cpu(node.i.data_crc)) {
printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
jffs2_mark_node_obsolete(c, ref);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
continue;
}
}
/* Mark the node as having been checked and fix the accounting accordingly */
+ spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[ref->flash_offset / c->sector_size];
jeb->used_size += ref->totlen;
jeb->unchecked_size -= ref->totlen;
c->used_size += ref->totlen;
c->unchecked_size -= ref->totlen;
- mark_ref_normal(ref);
+ /* If node covers at least a whole page, or if it starts at the
+ beginning of a page and runs to the end of the file, or if
+ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
+
+ If it's actually overlapped, it'll get made NORMAL (or OBSOLETE)
+ when the overlapping node(s) get added to the tree anyway.
+ */
+ if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
+ ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
+ (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) {
+ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
+ ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
+ } else {
+ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
+ ref->flash_offset = ref_offset(ref) | REF_NORMAL;
+ }
+ spin_unlock(&c->erase_completion_lock);
}
tn = jffs2_alloc_tmp_dnode_info();
@@ -323,13 +382,15 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
je16_to_cpu(node.u.nodetype), ref_offset(ref));
/* Mark the node as having been checked and fix the accounting accordingly */
+ spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[ref->flash_offset / c->sector_size];
jeb->used_size += ref->totlen;
jeb->unchecked_size -= ref->totlen;
c->used_size += ref->totlen;
c->unchecked_size -= ref->totlen;
-
+
mark_ref_normal(ref);
+ spin_unlock(&c->erase_completion_lock);
}
node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
@@ -361,10 +422,10 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
}
}
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
}
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
*tnp = ret_tn;
*fdp = ret_fd;
@@ -376,12 +437,24 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
return err;
}
-struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino)
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
+{
+ spin_lock(&c->inocache_lock);
+ ic->state = state;
+ wake_up(&c->inocache_wq);
+ spin_unlock(&c->inocache_lock);
+}
+
+/* During mount, this needs no locking. During normal operation, its
+ callers want to do other stuff while still holding the inocache_lock.
+ Rather than introducing special case get_ino_cache functions or
+ callbacks, we just let the caller do the locking itself. */
+
+struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
{
struct jffs2_inode_cache *ret;
D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
- spin_lock (&c->inocache_lock);
ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
while (ret && ret->ino < ino) {
@@ -391,8 +464,6 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino)
if (ret && ret->ino != ino)
ret = NULL;
- spin_unlock(&c->inocache_lock);
-
D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino));
return ret;
}
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index 5ead344453d1..0cfad439f844 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodelist.h,v 1.87 2002/11/12 13:36:18 dwmw2 Exp $
+ * $Id: nodelist.h,v 1.93 2003/02/24 21:47:28 dwmw2 Exp $
*
*/
@@ -16,12 +16,17 @@
#include <linux/config.h>
#include <linux/fs.h>
-
-#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
+#include <linux/types.h>
#include <linux/jffs2.h>
#include <linux/jffs2_fs_sb.h>
#include <linux/jffs2_fs_i.h>
+
+#ifdef __ECOS
+#include "os-ecos.h"
+#else
+#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
#include "os-linux.h"
+#endif
#ifndef CONFIG_JFFS2_FS_DEBUG
#define CONFIG_JFFS2_FS_DEBUG 2
@@ -98,13 +103,18 @@ struct jffs2_inode_cache {
uint32_t ino;
int nlink;
int state;
-#define INO_STATE_UNCHECKED 0
-#define INO_STATE_CHECKING 1
-#define INO_STATE_CHECKEDABSENT 2
-#define INO_STATE_READINGINODE 3
-#define INO_STATE_PRESENT 5
};
+/* Inode states for 'state' above. We need the 'GC' state to prevent
+ someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
+ node without going through all the iget() nonsense */
+#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */
+#define INO_STATE_CHECKING 1 /* CRC checks in progress */
+#define INO_STATE_PRESENT 2 /* In core */
+#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
+#define INO_STATE_GC 4 /* GCing a 'pristine' node */
+#define INO_STATE_READING 5 /* In read_inode() */
+
#define INOCACHE_HASHSIZE 128
struct jffs2_scan_info {
@@ -281,7 +291,8 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver);
-struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino);
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
+struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
void jffs2_free_ino_caches(struct jffs2_sb_info *c);
@@ -315,10 +326,10 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint
/* readinode.c */
void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_full_dnode *fn);
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint32_t ino, struct jffs2_raw_inode *latest_node);
+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
/* malloc.c */
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index 8baac2d30906..62ab0469ac03 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -7,14 +7,15 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodemgmt.c,v 1.84 2002/11/12 11:17:29 dwmw2 Exp $
+ * $Id: nodemgmt.c,v 1.94 2003/02/19 17:50:26 gleixner Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
+#include <linux/compiler.h>
+#include <linux/sched.h> /* For cond_resched() */
#include "nodelist.h"
/**
@@ -54,26 +55,57 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
- /* this needs a little more thought */
+ /* this needs a little more thought (true <tglx> :)) */
while(ret == -EAGAIN) {
while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
int ret;
+ uint32_t dirty, avail;
up(&c->alloc_sem);
- if (c->dirty_size + c->unchecked_size < c->sector_size) {
+ /* calculate real dirty size
+ * dirty_size contains blocks on erase_pending_list
+ * those blocks are counted in c->nr_erasing_blocks.
+ * If one block is actually erased, it is not longer counted as dirty_space
+ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
+ * with c->nr_erasing_blocks * c->sector_size again.
+ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
+ * This helps us to force gc and pick eventually a clean block to spread the load.
+ * We add unchecked_size here, as we hopefully will find some space to use.
+ * This will affect the sum only once, as gc first finishes checking
+ * of nodes.
+ */
+ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
+ if (dirty < c->sector_size) {
D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < sector size 0x%08x, returning -ENOSPC\n",
- c->dirty_size, c->unchecked_size, c->sector_size));
- spin_unlock_bh(&c->erase_completion_lock);
+ dirty, c->unchecked_size, c->sector_size));
+ spin_unlock(&c->erase_completion_lock);
+ return -ENOSPC;
+ }
+
+ /* Calc possibly available space. Possibly available means that we
+ * don't know, if unchecked size contains obsoleted nodes, which could give us some
+ * more usable space. This will affect the sum only once, as gc first finishes checking
+ * of nodes.
+ + Return -ENOSPC, if the maximum possibly available space is less or equal than
+ * blocksneeded * sector_size.
+ * This blocks endless gc looping on a filesystem, which is nearly full, even if
+ * the check above passes.
+ */
+ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
+ if ( (avail / c->sector_size) <= blocksneeded) {
+ D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
+ avail, blocksneeded * c->sector_size));
+ spin_unlock(&c->erase_completion_lock);
return -ENOSPC;
}
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->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
ret = jffs2_garbage_collect_pass(c);
if (ret)
@@ -85,7 +117,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
return -EINTR;
down(&c->alloc_sem);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
}
ret = jffs2_do_reserve_space(c, minsize, ofs, len);
@@ -93,7 +125,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
}
}
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
if (ret)
up(&c->alloc_sem);
return ret;
@@ -106,14 +138,14 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
while(ret == -EAGAIN) {
ret = jffs2_do_reserve_space(c, minsize, ofs, len);
if (ret) {
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
}
}
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
return ret;
}
@@ -127,10 +159,10 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
/* 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 (c->wbuf_len) {
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
jffs2_flush_wbuf(c, 1);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
/* We know nobody's going to have changed nextblock. Just continue */
}
c->wasted_size += jeb->free_size;
@@ -186,9 +218,9 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
!list_empty(&c->erasable_pending_wbuf_list)) {
D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
/* c->nextblock is NULL, no update to c->nextblock allowed */
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
jffs2_flush_wbuf(c, 1);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
/* Have another go. It'll be on the erasable_list now */
return -EAGAIN;
}
@@ -203,6 +235,11 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
}
/* Make sure this can't deadlock. Someone has to start the erases
of erase_pending blocks */
+#ifdef __ECOS
+ /* In eCos, we don't have a handy kernel thread doing the erases for
+ us. We do them ourselves right now. */
+ jffs2_erase_pending_blocks(c);
+#else
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&c->erase_wait, &wait);
D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
@@ -212,13 +249,14 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
D1(printk(KERN_DEBUG "Triggering pending erases\n"));
jffs2_erase_pending_trigger(c);
}
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
schedule();
remove_wait_queue(&c->erase_wait, &wait);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
if (signal_pending(current)) {
return -EINTR;
}
+#endif
/* An erase may have failed, decreasing the
amount of free space available. So we must
restart from the beginning */
@@ -248,9 +286,9 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui
already set c->nextblock so that jffs2_mark_node_obsolete()
won't try to refile it to the dirty_list.
*/
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
jffs2_mark_node_obsolete(c, jeb->first_node);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
}
D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
@@ -276,7 +314,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
uint32_t len = new->totlen;
jeb = &c->blocks[new->flash_offset / c->sector_size];
- D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", ref_offset(new), len));
+ D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
#if 1
if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
printk(KERN_WARNING "argh. node added in wrong place\n");
@@ -284,7 +322,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
return -EINVAL;
}
#endif
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
if (!jeb->first_node)
jeb->first_node = new;
@@ -308,9 +346,9 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
if (c->wbuf_len) {
/* Flush the last write in the block if it's outstanding */
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
jffs2_flush_wbuf(c, 1);
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
}
list_add_tail(&jeb->list, &c->clean_list);
@@ -319,7 +357,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
return 0;
}
@@ -337,7 +375,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
struct jffs2_eraseblock *jeb;
int blocknr;
struct jffs2_unknown_node n;
- int ret;
+ int ret, addedsize;
size_t retlen;
if(!ref) {
@@ -355,7 +393,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
}
jeb = &c->blocks[blocknr];
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
if (ref_flags(ref) == REF_UNCHECKED) {
D1(if (unlikely(jeb->unchecked_size < ref->totlen)) {
@@ -377,14 +415,17 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
c->used_size -= ref->totlen;
}
+ // Take care, that wasted size is taken into concern
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) {
D1(printk("Dirtying\n"));
- jeb->dirty_size += ref->totlen + jeb->wasted_size;
- c->dirty_size += ref->totlen + jeb->wasted_size;
+ addedsize = ref->totlen + jeb->wasted_size;
+ jeb->dirty_size += addedsize;
+ c->dirty_size += addedsize;
c->wasted_size -= jeb->wasted_size;
jeb->wasted_size = 0;
} else {
D1(printk("Wasting\n"));
+ addedsize = 0;
jeb->wasted_size += ref->totlen;
c->wasted_size += ref->totlen;
}
@@ -400,7 +441,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
obliterate nodes that look obsolete. If they weren't
marked obsolete on the flash at the time they _became_
obsolete, there was probably a reason for that. */
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
return;
}
@@ -417,6 +458,10 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
if (c->wbuf_len) {
D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
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
+ after dropping the alloc_sem, and it did that just fine. But it also caused us to
+ lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise
+ have needed to. So I suspect it's outlived its usefulness. Thomas? */
/* We've changed the rules slightly. After
writing a node you now mustn't drop the
@@ -436,6 +481,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n");
BUG();
}
+#endif
} else {
if (jiffies & 127) {
/* Most of the time, we just erase it immediately. Otherwise we
@@ -454,7 +500,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
D1(printk(KERN_DEBUG "Done OK\n"));
} else if (jeb == c->gcblock) {
D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
- } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - ref->totlen)) {
+ } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) {
D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
list_del(&jeb->list);
D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
@@ -470,7 +516,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
}
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
if (!jffs2_can_mark_obsolete(c))
return;
@@ -484,7 +530,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
return;
}
if (retlen != sizeof(n)) {
- printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref_offset(ref), retlen);
+ printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
}
if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) {
@@ -503,7 +549,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
return;
}
if (retlen != sizeof(n)) {
- printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), retlen);
+ printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
}
}
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index a5c35fdb51c8..d08ca2e3a152 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: os-linux.h,v 1.21 2002/11/12 09:44:30 dwmw2 Exp $
+ * $Id: os-linux.h,v 1.26 2003/05/16 18:45:25 dwmw2 Exp $
*
*/
@@ -15,6 +15,11 @@
#define __JFFS2_OS_LINUX_H__
#include <linux/version.h>
+/* JFFS2 uses Linux mode bits natively -- no need for conversion */
+#define os_to_jffs2_mode(x) (x)
+#define jffs2_to_os_mode(x) (x)
+
+
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
@@ -37,9 +42,6 @@
#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
-#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
-#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
-#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
#define JFFS2_F_I_RDEV_MIN(f) (minor(OFNI_EDONI_2SFFJ(f)->i_rdev))
@@ -49,6 +51,21 @@
#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
#endif
+/* Urgh. The things we do to keep the 2.4 build working */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)
+#define ITIME(sec) ((struct timespec){sec, 0})
+#define I_SEC(tv) ((tv).tv_sec)
+#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
+#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
+#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
+#else
+#define ITIME(x) (x)
+#define I_SEC(x) (x)
+#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
+#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
+#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
+#endif
+
/* Hmmm. P'raps generic code should only ever see versions of signal
functions which do the locking automatically? */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40)
@@ -57,6 +74,16 @@
#define current_sig_lock current->sighand->siglock
#endif
+#define sleep_on_spinunlock(wq, s) \
+ do { \
+ DECLARE_WAITQUEUE(__wait, current); \
+ add_wait_queue((wq), &__wait); \
+ set_current_state(TASK_UNINTERRUPTIBLE); \
+ spin_unlock(s); \
+ schedule(); \
+ remove_wait_queue((wq), &__wait); \
+ } while(0)
+
static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
{
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c
index 4e8b3a1c624c..7216eb97cd46 100644
--- a/fs/jffs2/read.c
+++ b/fs/jffs2/read.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: read.c,v 1.29 2002/11/12 09:51:22 dwmw2 Exp $
+ * $Id: read.c,v 1.31 2003/01/14 14:06:22 dwmw2 Exp $
*
*/
@@ -39,7 +39,7 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig
}
if (readlen != sizeof(*ri)) {
jffs2_free_raw_inode(ri);
- printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n",
+ printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n",
ref_offset(fd->raw), sizeof(*ri), readlen);
return -EIO;
}
@@ -197,8 +197,9 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
fragofs = offset - frag->ofs;
readlen = min(frag->size - fragofs, end - offset);
- D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+fragofs, frag->ofs+fragofs+readlen,
- ref_offset(frag->node->raw)));
+ D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
+ frag->ofs+fragofs, frag->ofs+fragofs+readlen,
+ ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
D2(printk(KERN_DEBUG "node read done\n"));
if (ret) {
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index 92ba6f553d2d..4f935bfa2fd0 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: readinode.c,v 1.95 2002/11/12 11:17:29 dwmw2 Exp $
+ * $Id: readinode.c,v 1.106 2003/05/14 06:53:26 dwmw2 Exp $
*
*/
@@ -17,11 +17,13 @@
#include <linux/crc32.h>
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
+#include <linux/compiler.h>
#include "nodelist.h"
+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
-D1(static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
+#if CONFIG_JFFS2_FS_DEBUG >= 1
+static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
{
struct jffs2_node_frag *this = frag_first(list);
uint32_t lastofs = 0;
@@ -44,31 +46,17 @@ D1(static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
printk(KERN_CRIT "Frag tree got a hole in it\n");
BUG();
}
-})
+}
-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
+void jffs2_print_frag_list(struct jffs2_inode_info *f)
{
jffs2_print_fragtree(&f->fragtree, 0);
if (f->metadata) {
printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
}
-})
-
-
-/* Given an inode, probably with existing list of fragments, add the new node
- * to the fragment list.
- */
-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
-{
- int ret;
- D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
-
- ret = jffs2_add_full_dnode_to_fraglist(c, &f->fragtree, fn);
-
- D2(jffs2_print_frag_list(f));
- return ret;
}
+#endif /* D1 */
static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
{
@@ -91,26 +79,24 @@ static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_
jffs2_free_node_frag(this);
}
-/* Doesn't set inode->i_size */
-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_full_dnode *fn)
+/* Given an inode, probably with existing list of fragments, add the new node
+ * to the fragment list.
+ */
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
{
- struct jffs2_node_frag *this;
+ int ret;
struct jffs2_node_frag *newfrag;
- uint32_t lastend;
+
+ D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
newfrag = jffs2_alloc_node_frag();
- if (!newfrag) {
+ if (unlikely(!newfrag))
return -ENOMEM;
- }
-
- if (!fn->raw) {
- printk(KERN_WARNING "dwmw2 is stupid. j_a_f_d_t_f should never happen with ->raw == NULL\n");
- BUG();
- }
- D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
+ D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
+ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
- if (!fn->size) {
+ if (unlikely(!fn->size)) {
jffs2_free_node_frag(newfrag);
return 0;
}
@@ -120,8 +106,42 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *li
newfrag->node = fn;
newfrag->node->frags = 1;
+ ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
+ if (ret)
+ return ret;
+
+ /* If we now share a page with other nodes, mark either previous
+ or next node REF_NORMAL, as appropriate. */
+ if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
+ struct jffs2_node_frag *prev = frag_prev(newfrag);
+
+ mark_ref_normal(fn->raw);
+ /* If we don't start at zero there's _always_ a previous */
+ if (prev->node)
+ mark_ref_normal(prev->node->raw);
+ }
+
+ if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
+ struct jffs2_node_frag *next = frag_next(newfrag);
+
+ if (next) {
+ mark_ref_normal(fn->raw);
+ if (next->node)
+ mark_ref_normal(next->node->raw);
+ }
+ }
+ D2(jffs2_print_frag_list(f));
+ return 0;
+}
+
+/* Doesn't set inode->i_size */
+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
+{
+ struct jffs2_node_frag *this;
+ uint32_t lastend;
+
/* Skip all the nodes which are completed before this one starts */
- this = jffs2_lookup_node_frag(list, fn->ofs);
+ this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
if (this) {
D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
@@ -143,16 +163,18 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *li
if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
if (this->node)
mark_ref_normal(this->node->raw);
- mark_ref_normal(fn->raw);
+ mark_ref_normal(newfrag->node->raw);
}
- if (lastend < fn->ofs) {
+ if (lastend < newfrag->node->ofs) {
/* ... and we need to put a hole in before the new node */
struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
- if (!holefrag)
+ if (!holefrag) {
+ jffs2_free_node_frag(newfrag);
return -ENOMEM;
+ }
holefrag->ofs = lastend;
- holefrag->size = fn->ofs - lastend;
+ holefrag->size = newfrag->node->ofs - lastend;
holefrag->node = NULL;
if (this) {
/* By definition, the 'this' node has no right-hand child,
@@ -190,9 +212,9 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *li
if (newfrag->ofs > this->ofs) {
/* This node isn't completely obsoleted. The start of it remains valid */
- /* Mark the new node and the partially covered node REF_NORMAL -- let
+ /* Mark the new node and the partially covered node REF_NORMAL -- let
the GC take a look at them */
- mark_ref_normal(fn->raw);
+ mark_ref_normal(newfrag->node->raw);
if (this->node)
mark_ref_normal(this->node->raw);
@@ -283,7 +305,7 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *li
/* And mark them REF_NORMAL so the GC takes a look at them */
if (this->node)
mark_ref_normal(this->node->raw);
- mark_ref_normal(fn->raw);
+ mark_ref_normal(newfrag->node->raw);
return 0;
}
@@ -314,24 +336,54 @@ void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uin
/* Scan the list of all nodes present for this ino, build map of versions, etc. */
+static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f,
+ struct jffs2_raw_inode *latest_node);
+
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint32_t ino, struct jffs2_raw_inode *latest_node)
{
- struct jffs2_tmp_dnode_info *tn_list, *tn;
- struct jffs2_full_dirent *fd_list;
- struct jffs2_full_dnode *fn = NULL;
- uint32_t crc;
- uint32_t latest_mctime, mctime_ver;
- uint32_t mdata_ver = 0;
- size_t retlen;
- int ret;
-
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
+ retry_inocache:
+ spin_lock(&c->inocache_lock);
f->inocache = jffs2_get_ino_cache(c, ino);
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
+ if (f->inocache) {
+ /* Check its state. We may need to wait before we can use it */
+ switch(f->inocache->state) {
+ case INO_STATE_UNCHECKED:
+ case INO_STATE_CHECKEDABSENT:
+ f->inocache->state = INO_STATE_READING;
+ break;
+
+ case INO_STATE_CHECKING:
+ case INO_STATE_GC:
+ /* If it's in either of these states, we need
+ to wait for whoever's got it to finish and
+ put it back. */
+ D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
+ ino, f->inocache->state));
+ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+ goto retry_inocache;
+
+ case INO_STATE_READING:
+ case INO_STATE_PRESENT:
+ /* Eep. This should never happen. It can
+ happen if Linux calls read_inode() again
+ before clear_inode() has finished though. */
+ printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
+ /* Fail. That's probably better than allowing it to succeed */
+ f->inocache = NULL;
+ break;
+
+ default:
+ BUG();
+ }
+ }
+ spin_unlock(&c->inocache_lock);
if (!f->inocache && ino == 1) {
/* Special case - no root inode on medium */
f->inocache = jffs2_alloc_inode_cache();
@@ -343,19 +395,61 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
f->inocache->ino = f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+ f->inocache->state = INO_STATE_READING;
jffs2_add_ino_cache(c, f->inocache);
}
if (!f->inocache) {
printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
return -ENOENT;
}
- D1(printk(KERN_DEBUG "jffs2_do_read_inode(): ino #%u nlink is %d\n", ino, f->inocache->nlink));
+
+ return jffs2_do_read_inode_internal(c, f, latest_node);
+}
+
+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_raw_inode n;
+ struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
+ int ret;
+
+ if (!f)
+ return -ENOMEM;
+
+ memset(f, 0, sizeof(*f));
+ init_MUTEX_LOCKED(&f->sem);
+ f->inocache = ic;
+
+ ret = jffs2_do_read_inode_internal(c, f, &n);
+ if (!ret) {
+ up(&f->sem);
+ jffs2_do_clear_inode(c, f);
+ }
+ kfree (f);
+ return ret;
+}
+
+static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f,
+ struct jffs2_raw_inode *latest_node)
+{
+ struct jffs2_tmp_dnode_info *tn_list, *tn;
+ struct jffs2_full_dirent *fd_list;
+ struct jffs2_full_dnode *fn = NULL;
+ uint32_t crc;
+ uint32_t latest_mctime, mctime_ver;
+ uint32_t mdata_ver = 0;
+ size_t retlen;
+ int ret;
+
+ D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
/* Grab all nodes relevant to this ino */
- ret = jffs2_get_inode_nodes(c, ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
+ ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
if (ret) {
- printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", ino, ret);
+ printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
+ if (f->inocache->state == INO_STATE_READING)
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
return ret;
}
f->dents = fd_list;
@@ -365,13 +459,21 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
fn = tn->fn;
- if (f->metadata && tn->version > mdata_ver) {
- D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
- jffs2_mark_node_obsolete(c, f->metadata->raw);
- jffs2_free_full_dnode(f->metadata);
- f->metadata = NULL;
-
- mdata_ver = 0;
+ if (f->metadata) {
+ if (tn->version > mdata_ver) {
+ D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ f->metadata = NULL;
+
+ mdata_ver = 0;
+ } else {
+ D1(printk(KERN_DEBUG "Er. New metadata at 0x%08x with ver %d is actually older than previous %d\n",
+ ref_offset(f->metadata->raw), tn->version, mdata_ver));
+ jffs2_mark_node_obsolete(c, fn->raw);
+ jffs2_free_full_dnode(fn);
+ goto next_tn;
+ }
}
if (fn->size) {
@@ -382,31 +484,36 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
f->metadata = fn;
mdata_ver = tn->version;
}
+ next_tn:
tn_list = tn->next;
jffs2_free_tmp_dnode_info(tn);
}
if (!fn) {
/* No data nodes for this inode. */
- if (ino != 1) {
- printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", ino);
+ if (f->inocache->ino != 1) {
+ printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
if (!fd_list) {
+ if (f->inocache->state == INO_STATE_READING)
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
return -EIO;
}
printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
}
- latest_node->mode = cpu_to_je32(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
+ latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
latest_node->version = cpu_to_je32(0);
latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
latest_node->isize = cpu_to_je32(0);
latest_node->gid = cpu_to_je16(0);
latest_node->uid = cpu_to_je16(0);
+ if (f->inocache->state == INO_STATE_READING)
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
return 0;
}
ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
if (ret || retlen != sizeof(*latest_node)) {
- printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %ld of %d bytes read\n",
- ret, (long)retlen, sizeof(*latest_node));
+ printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
+ ret, retlen, sizeof(*latest_node));
/* FIXME: If this fails, there seems to be a memory leak. Find it. */
up(&f->sem);
jffs2_do_clear_inode(c, f);
@@ -415,13 +522,13 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
crc = crc32(0, latest_node, sizeof(*latest_node)-8);
if (crc != je32_to_cpu(latest_node->node_crc)) {
- printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", ino, ref_offset(fn->raw));
+ printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -EIO;
}
- switch(je32_to_cpu(latest_node->mode) & S_IFMT) {
+ switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
case S_IFDIR:
if (mctime_ver > je32_to_cpu(latest_node->version)) {
/* The times in the latest_node are actually older than
@@ -447,23 +554,26 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
case S_IFBLK:
case S_IFCHR:
- /* Xertain inode types should have only one data node, and it's
+ /* Certain inode types should have only one data node, and it's
kept as the metadata node */
if (f->metadata) {
- printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", ino, je32_to_cpu(latest_node->mode));
+ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
+ f->inocache->ino, jemode_to_cpu(latest_node->mode));
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -EIO;
}
if (!frag_first(&f->fragtree)) {
- printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", ino, je32_to_cpu(latest_node->mode));
+ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
+ f->inocache->ino, jemode_to_cpu(latest_node->mode));
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -EIO;
}
/* ASSERT: f->fraglist != NULL */
if (frag_next(frag_first(&f->fragtree))) {
- printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had more than one node\n", ino, je32_to_cpu(latest_node->mode));
+ printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
+ f->inocache->ino, jemode_to_cpu(latest_node->mode));
/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
up(&f->sem);
jffs2_do_clear_inode(c, f);
@@ -475,7 +585,8 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
f->fragtree = RB_ROOT;
break;
}
- f->inocache->state = INO_STATE_PRESENT;
+ if (f->inocache->state == INO_STATE_READING)
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
return 0;
}
@@ -494,7 +605,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
the nodes are marked obsolete, and jffs2_g_c_pass() won't
call iget() for the inode in question.
- We also do this to keep the (maybe temporary) BUG() in
+ We also used to do this to keep the temporary BUG() in
jffs2_mark_node_obsolete() from triggering.
*/
if(deleted)
@@ -518,8 +629,8 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
jffs2_free_full_dirent(fd);
}
- if (f->inocache)
- f->inocache->state = INO_STATE_CHECKEDABSENT;
+ if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
up(&f->sem);
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 889f055133b4..8ed8212bb5eb 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -7,10 +7,11 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: scan.c,v 1.92 2002/09/09 16:29:08 dwmw2 Exp $
+ * $Id: scan.c,v 1.99 2003/04/28 10:17:17 dwmw2 Exp $
*
*/
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/pagemap.h>
@@ -70,23 +71,21 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
uint32_t empty_blocks = 0, bad_blocks = 0;
unsigned char *flashbuf = NULL;
uint32_t buf_size = 0;
+#ifndef __ECOS
size_t pointlen;
- if (!c->blocks) {
- printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
- return -EINVAL;
- }
if (c->mtd->point) {
ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
if (!ret && pointlen < c->mtd->size) {
/* Don't muck about if it won't let us point to the whole flash */
- D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%x\n", pointlen));
- c->mtd->unpoint(c->mtd, flashbuf);
+ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
+ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
flashbuf = NULL;
}
if (ret)
D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
}
+#endif
if (!flashbuf) {
/* For NAND it's quicker to read a whole eraseblock at a time,
apparently */
@@ -237,9 +236,10 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
}
if (buf_size)
kfree(flashbuf);
+#ifndef __ECOS
else
- c->mtd->unpoint(c->mtd, flashbuf);
-
+ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
+#endif
return 0;
}
@@ -255,7 +255,7 @@ static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
return ret;
}
if (retlen < len) {
- D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen));
+ D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
return -EIO;
}
D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs));
@@ -366,7 +366,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
prevofs = ofs;
if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
- D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. (%x+%x<%x+%x) Not reading\n", sizeof(struct jffs2_unknown_node),
+ D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
jeb->offset, c->sector_size, ofs, sizeof(*node)));
DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
break;
@@ -374,7 +374,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
if (buf_ofs + buf_len < ofs + sizeof(*node)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
- D1(printk(KERN_DEBUG "Fewer than %d bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
+ D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
sizeof(struct jffs2_unknown_node), buf_len, ofs));
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
@@ -410,8 +410,8 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
/* Ran off end. */
D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs));
- if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
- !jeb->first_node->next_in_ino && !jeb->dirty_size)
+ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
+ c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size)
return BLK_STATE_CLEANMARKER;
wasempty = 1;
continue;
@@ -430,7 +430,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
continue;
}
if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
- D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs));
+ D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
DIRTY_SPACE(4);
ofs += 4;
continue;
@@ -492,7 +492,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
case JFFS2_NODETYPE_INODE:
if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
- D1(printk(KERN_DEBUG "Fewer than %d bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
+ D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
sizeof(struct jffs2_raw_inode), buf_len, ofs));
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
@@ -585,8 +585,8 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
}
- D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset,
- jeb->free_size, jeb->dirty_size, jeb->used_size));
+ D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset,
+ jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
/* mark_node_obsolete can add to wasted !! */
if (jeb->wasted_size) {
@@ -596,9 +596,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
jeb->wasted_size = 0;
}
- if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) &&
- !jeb->first_node->next_in_ino && !jeb->dirty_size)
+ if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
+ && (!jeb->first_node || jeb->first_node->next_in_ino) )
return BLK_STATE_CLEANMARKER;
+
/* move blocks with max 4 byte dirty space to cleanlist */
else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
c->dirty_size -= jeb->dirty_size;
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index e87cdaececb6..8bc53c400875 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: super.c,v 1.74 2002/11/12 09:37:39 dwmw2 Exp $
+ * $Id: super.c,v 1.79 2003/05/27 22:35:42 dwmw2 Exp $
*
*/
@@ -23,7 +23,6 @@
#include <linux/jffs2.h>
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
#include <linux/ctype.h>
#include <linux/namei.h>
#include "nodelist.h"
@@ -53,7 +52,7 @@ static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long f
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
- init_MUTEX(&ei->sem);
+ init_MUTEX_LOCKED(&ei->sem);
inode_init_once(&ei->vfs_inode);
}
}
@@ -101,9 +100,9 @@ static int jffs2_sb_set(struct super_block *sb, void *data)
return 0;
}
-static struct super_block *
-jffs2_get_sb_mtd(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data, struct mtd_info *mtd)
+static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data, struct mtd_info *mtd)
{
struct super_block *sb;
struct jffs2_sb_info *c;
@@ -153,9 +152,9 @@ jffs2_get_sb_mtd(struct file_system_type *fs_type, int flags,
return sb;
}
-static struct super_block *
-jffs2_get_sb_mtdnr(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data, int mtdnr)
+static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data, int mtdnr)
{
struct mtd_info *mtd;
@@ -168,9 +167,9 @@ jffs2_get_sb_mtdnr(struct file_system_type *fs_type, int flags,
return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
}
-static struct super_block *
-jffs2_get_sb(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data)
+static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data)
{
int err;
struct nameidata nd;
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index b63b05425e55..2ba43b76297a 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -7,39 +7,40 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: wbuf.c,v 1.20 2002/11/12 11:33:02 dwmw2 Exp $
- * + some of the dependencies on later MTD NAND code temporarily reverted.
+ * $Id: wbuf.c,v 1.30 2003/02/19 17:48:49 gleixner Exp $
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
#include <linux/crc32.h>
#include <linux/mtd/nand.h>
#include "nodelist.h"
-/* FIXME duplicated defines in wbuf.c and nand.c
- * Constants for out of band layout
- */
-#ifndef NAND_BADBLOCK_POS
-#define NAND_BADBLOCK_POS 5
-#endif
-#ifndef NAND_JFFS2_OOB_BADBPOS
-#define NAND_JFFS2_OOB_BADBPOS 5
-#define NAND_JFFS2_OOB8_FSDAPOS 6
-#define NAND_JFFS2_OOB16_FSDAPOS 8
-#define NAND_JFFS2_OOB8_FSDALEN 2
-#define NAND_JFFS2_OOB16_FSDALEN 8
-#endif
-
/* max. erase failures before we mark a block bad */
#define MAX_ERASE_FAILURES 5
/* two seconds timeout for timed wbuf-flushing */
#define WBUF_FLUSH_TIMEOUT 2 * HZ
+#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
+
+struct nand_oobinfo jffs2_oobinfo = {
+ useecc: 1,
+ eccpos: {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2, JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5}
+};
+
static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
{
struct list_head *this, *next;
@@ -178,13 +179,14 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
/* else jffs2_flash_writev has actually filled in the rest of the
buffer for us, and will deal with the node refs etc. later. */
- ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
+ ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, &jffs2_oobinfo);
if (ret || retlen != c->wbuf_pagesize) {
if (ret)
printk(KERN_CRIT "jffs2_flush_wbuf(): Write failed with %d\n",ret);
else
- printk(KERN_CRIT "jffs2_flush_wbuf(): Write was short %d instead of %d\n",retlen,c->wbuf_pagesize);
+ printk(KERN_CRIT "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
+ retlen, c->wbuf_pagesize);
ret = -EIO;
/* CHECKME NAND
@@ -205,7 +207,7 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
/* Adjusting free size of next block only, if it's called from fsync ! */
if (pad == 2) {
D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n"));
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
if (!c->nextblock)
BUG();
/* wbuf_pagesize - wbuf_len is the amount of space that's to be
@@ -222,13 +224,13 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
}
/* Stick any now-obsoleted blocks on the erase_pending_list */
- spin_lock_bh(&c->erase_completion_lock);
+ spin_lock(&c->erase_completion_lock);
jffs2_refile_wbuf_blocks(c);
- spin_unlock_bh(&c->erase_completion_lock);
+ spin_unlock(&c->erase_completion_lock);
memset(c->wbuf,0xff,c->wbuf_pagesize);
/* adjust write buffer offset, else we get a non contigous write bug */
@@ -394,7 +396,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi
outvecs[splitvec].iov_len = split_ofs;
/* We did cross a page boundary, so we write some now */
- ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
+ ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, &jffs2_oobinfo);
if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
/* At this point we have no problem,
c->wbuf is empty.
@@ -442,11 +444,19 @@ alldone:
}
/*
- This is the entry for NOR-Flash. We use it also for NAND to flush wbuf
+ * This is the entry for flash write.
+ * Check, if we work on NAND FLASH, if so build an iovec and write it via vritev
*/
int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
{
- return c->mtd->write(c->mtd, ofs, len, retlen, buf);
+ struct iovec vecs[1];
+
+ if (jffs2_can_mark_obsolete(c))
+ return c->mtd->write(c->mtd, ofs, len, retlen, buf);
+
+ vecs[0].iov_base = (unsigned char *) buf;
+ vecs[0].iov_len = len;
+ return jffs2_flash_writev(c, vecs, 1, ofs, retlen);
}
/*
@@ -459,10 +469,11 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
/* Read flash */
if (!jffs2_can_mark_obsolete(c)) {
- ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
+ ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, &jffs2_oobinfo);
if ( (ret == -EIO) && (*retlen == len) ) {
- printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%llx) returned ECC error\n", len, ofs);
+ printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
+ len, ofs);
/*
* We have the raw data without ECC correction in the buffer, maybe
* we are lucky and all data or parts are correct. We check the node.
@@ -549,7 +560,7 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
if (retlen < len) {
D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read "
- "(%d bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
+ "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
ret = -EIO;
goto out;
}
@@ -593,69 +604,83 @@ out:
return ret;
}
-int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+/*
+* Scan for a valid cleanmarker and for bad blocks
+* For virtual blocks (concatenated physical blocks) check the cleanmarker
+* only in the first page of the first physical block, but scan for bad blocks in all
+* physical blocks
+*/
+int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_unknown_node n;
unsigned char buf[32];
unsigned char *p;
- int ret,i;
- size_t retlen;
- int fsdata_pos,fsdata_len, oob_size, badblock_pos;
+ int ret, i, cnt, retval = 0;
+ size_t retlen, offset;
+ int fsdata_pos, fsdata_len, oob_size, badblock_pos;
+ offset = jeb->offset;
oob_size = c->mtd->oobsize;
- switch(c->mtd->ecctype) {
- case MTD_ECC_SW:
+ 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"));
+ D1 (printk (KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n"));
return -EINVAL;
- }
-
- /*
- * We read oob data from page 0 and 1 of the block.
- * page 0 contains cleanmarker and badblock info
- * page 2 contains failure count of this block
- */
- ret = c->mtd->read_oob(c->mtd, jeb->offset, oob_size << 1 , &retlen, buf);
-
- if (ret) {
- D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
- return ret;
- }
- if (retlen < (oob_size << 1) ) {
- D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%d bytes not %d) for block at %08x\n", retlen, oob_size << 1 , jeb->offset));
- return -EIO;
}
- /* Check for bad block marker */
- if (buf[badblock_pos] != 0xff) {
- D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n",jeb->offset));
- return 2;
- }
- /* Check for failure counter in the second page */
- if (buf[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]));
- return 3;
- }
+ /* Loop through the physical blocks */
+ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
+ /*
+ * We read oob data from page 0 and 1 of the block.
+ * page 0 contains cleanmarker and badblock info
+ * page 1 contains failure count of this block
+ */
+ ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf);
- n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
- n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
- n.totlen = cpu_to_je32(8);
- p = (unsigned char *) &n;
-
- for (i = 0; i < fsdata_len; i++) {
- if (buf[fsdata_pos+i] != p[i]) {
- D2(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset));
- return 1;
+ if (ret) {
+ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
+ return ret;
}
+ if (retlen < (oob_size << 1)) {
+ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset));
+ return -EIO;
+ }
+
+ /* Check for bad block marker */
+ if (buf[badblock_pos] != 0xff) {
+ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
+ return 2;
+ }
+
+ /* Check for failure counter in the second page */
+ if (buf[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]));
+ return 3;
+ }
+
+ /* Check cleanmarker only on the first physical block */
+ if (!cnt) {
+ n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
+ n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
+ n.totlen = cpu_to_je32 (8);
+ p = (unsigned char *) &n;
+
+ for (i = 0; i < fsdata_len; i++) {
+ if (buf[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;
+ }
+ }
+ }
+ offset += c->mtd->erasesize;
}
-
- return 0;
+ return retval;
}
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
@@ -686,7 +711,7 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc
return ret;
}
if (retlen != fsdata_len) {
- D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %d 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, fsdata_len));
return ret;
}
return 0;
@@ -721,7 +746,7 @@ int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
}
if (retlen < oob_size) {
- D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%d bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset));
+ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset));
return -EIO;
}
@@ -767,9 +792,8 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *
return ret;
}
if (retlen != 1) {
- D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %d not 1\n", jeb->offset, retlen));
+ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen));
return ret;
}
return 0;
}
-
diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c
index 8a4a8d9890f1..cc50ffbaea3d 100644
--- a/fs/jffs2/write.c
+++ b/fs/jffs2/write.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: write.c,v 1.60 2002/09/09 16:29:08 dwmw2 Exp $
+ * $Id: write.c,v 1.65 2003/01/21 18:11:29 dwmw2 Exp $
*
*/
@@ -47,7 +47,7 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri->totlen = cpu_to_je32(PAD(sizeof(*ri)));
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
- ri->mode = cpu_to_je32(mode);
+ ri->mode = cpu_to_jemode(mode);
f->highest_version = 1;
ri->version = cpu_to_je32(f->highest_version);
@@ -55,6 +55,7 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
return 0;
}
+#if CONFIG_JFFS2_FS_DEBUG > 0
static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
{
unsigned char buf[16];
@@ -63,7 +64,7 @@ static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
if (ret || (retlen != 16)) {
- D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen));
+ D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen));
return;
}
ret = 0;
@@ -79,7 +80,7 @@ static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
}
}
-
+#endif
/* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it,
@@ -105,10 +106,10 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
vecs[1].iov_base = (unsigned char *)data;
vecs[1].iov_len = datalen;
- writecheck(c, flash_ofs);
+ D1(writecheck(c, flash_ofs));
if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
- printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
+ printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
}
raw = jffs2_alloc_raw_node_ref();
if (!raw)
@@ -135,7 +136,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen);
if (ret || (retlen != sizeof(*ri) + datalen)) {
- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
+ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
sizeof(*ri)+datalen, flash_ofs, ret, retlen);
/* Mark the space as dirtied */
if (retlen) {
@@ -162,20 +163,27 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
return ERR_PTR(ret?ret:-EIO);
}
/* Mark the space used */
- if (datalen == PAGE_CACHE_SIZE)
+ /* If node covers at least a whole page, or if it starts at the
+ beginning of a page and runs to the end of the file, or if
+ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
+ */
+ if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
+ ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
+ (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) {
raw->flash_offset |= REF_PRISTINE;
- else
+ } else {
raw->flash_offset |= REF_NORMAL;
+ }
jffs2_add_physical_node_ref(c, raw);
/* Link into per-inode list */
raw->next_in_ino = f->inocache->nodes;
f->inocache->nodes = raw;
- D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
- flash_ofs, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
- je32_to_cpu(ri->node_crc), je32_to_cpu(ri->data_crc),
- je32_to_cpu(ri->totlen)));
+ D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
+ 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->data_crc), je32_to_cpu(ri->totlen)));
if (writelen)
*writelen = retlen;
@@ -194,7 +202,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n",
je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
je32_to_cpu(rd->name_crc)));
- writecheck(c, flash_ofs);
+ D1(writecheck(c, flash_ofs));
D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
@@ -233,7 +241,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen);
if (ret || (retlen != sizeof(*rd) + namelen)) {
- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
+ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
sizeof(*rd)+namelen, flash_ofs, ret, retlen);
/* Mark the space as dirtied */
if (retlen) {
@@ -290,7 +298,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
}
down(&f->sem);
datalen = writelen;
- cdatalen = min(alloclen - sizeof(*ri), writelen);
+ cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen);
comprbuf = kmalloc(cdatalen, GFP_KERNEL);
if (comprbuf) {
@@ -392,7 +400,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
- je32_to_cpu(ri->mode)));
+ jemode_to_cpu(ri->mode)));
if (IS_ERR(fn)) {
D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));