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

JFFS2 update; completed support for NAND flash.

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