From 4a98f95f1d9e412805040814f5dda55b77eaee65 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 11 Oct 2003 17:51:34 +0100 Subject: [PATCH] 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. --- fs/jffs2/background.c | 23 +- fs/jffs2/build.c | 61 +++- fs/jffs2/compr.c | 4 +- fs/jffs2/compr_rtime.c | 4 +- fs/jffs2/compr_zlib.c | 6 +- fs/jffs2/dir.c | 100 ++--- fs/jffs2/erase.c | 9 +- fs/jffs2/file.c | 171 +-------- fs/jffs2/fs.c | 212 +++++++++-- fs/jffs2/gc.c | 197 ++++++++-- fs/jffs2/ioctl.c | 4 +- fs/jffs2/malloc.c | 6 +- fs/jffs2/nodelist.c | 6 +- fs/jffs2/nodelist.h | 63 +++- fs/jffs2/nodemgmt.c | 93 +++-- fs/jffs2/os-linux.h | 28 +- fs/jffs2/pushpull.h | 4 +- fs/jffs2/read.c | 17 +- fs/jffs2/readinode.c | 6 +- fs/jffs2/scan.c | 23 +- fs/jffs2/super.c | 34 +- fs/jffs2/symlink.c | 4 +- fs/jffs2/wbuf.c | 709 +++++++++++++++++++++++++++--------- fs/jffs2/write.c | 165 ++++++--- fs/jffs2/writev.c | 4 +- include/linux/jffs2.h | 6 +- include/linux/jffs2_fs_sb.h | 24 +- 27 files changed, 1332 insertions(+), 651 deletions(-) diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 3cc44044a70ad..8d60e62800b0a 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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(¤t_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", diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 01a575d6bfae7..c05f64b5a2a1b 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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; } diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index a2479e837acde..471ec55e6a156 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c @@ -1,13 +1,13 @@ /* * 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 * * 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 $ * */ diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index 9dec50194a2c8..a72ce670c5d43 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -1,13 +1,13 @@ /* * 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 * * 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. diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index 76ad2570a3017..c05e65f4c7248 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 $ * */ diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 93742552a9e93..561e37f3d6440 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 #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 #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; @@ -176,7 +181,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, - struct nameidata *nd) + struct nameidata *nd) { struct jffs2_raw_inode *ri; struct jffs2_inode_info *f, *dir_f; @@ -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,20 +360,12 @@ 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) { - /* Eep. */ - jffs2_clear_inode(inode); - return ret; - } + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { + /* Eep. */ + jffs2_clear_inode(inode); + return ret; } rd = jffs2_alloc_raw_dirent(); @@ -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,20 +488,12 @@ 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) { - /* Eep. */ - jffs2_clear_inode(inode); - return ret; - } + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { + /* Eep. */ + jffs2_clear_inode(inode); + return ret; } rd = jffs2_alloc_raw_dirent(); @@ -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,20 +642,12 @@ 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) { - /* Eep. */ - jffs2_clear_inode(inode); - return ret; - } + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { + /* Eep. */ + jffs2_clear_inode(inode); + return ret; } rd = jffs2_alloc_raw_dirent(); @@ -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 diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 6e4327692e8d4..f7a940962e5ff 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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); diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index e462866532d8b..8b3f8e7e56404 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index d71073d27b772..bf1d109a035fd 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 #include #include +#include #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; } diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index a715475bdec7f..1cc6bfc8fc421 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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)); diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index 734608a978068..1c0bebc6b60e2 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * 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 $ * */ diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 4346f9874a2fb..d81680fe6aead 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 $ * */ diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 294854d372f77..6c94c36998f10 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 $ * */ diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 0cfad439f8448..d36f29aa59919 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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); diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 62ab0469ac035..980de2e22cd82 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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; - c->wasted_size -= jeb->wasted_size; - jeb->wasted_size = 0; + 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); } } diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index d06443ceec683..91401c1c9dca7 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2002 Red Hat, Inc. + * Copyright (C) 2002-2003 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * 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 *); diff --git a/fs/jffs2/pushpull.h b/fs/jffs2/pushpull.h index 412c467c540a3..953b653205584 100644 --- a/fs/jffs2/pushpull.h +++ b/fs/jffs2/pushpull.h @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * 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 $ * */ diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c index 7216eb97cd466..adeeeb19aa4ba 100644 --- a/fs/jffs2/read.c +++ b/fs/jffs2/read.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 #include #include +#include #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); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 4f935bfa2fd09..ea4bbf7f237f0 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 $ * */ diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 55c51478692c6..51da078d0fb36 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 @@ -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); diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index cc55f6176f9bd..0f662f5d8a810 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 #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 @@ out: 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); @@ -284,7 +280,7 @@ static void jffs2_kill_sb(struct super_block *sb) put_mtd_device(c->mtd); kfree(c); } - + static struct file_system_type jffs2_fs_type = { .owner = THIS_MODULE, .name = "jffs2", @@ -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), diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index 56de574be22b1..46d306cf0df4a 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * 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 $ * */ diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 82ae183777828..ecfb155cb82bd 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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 #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; - - D1(printk(KERN_DEBUG "jffs2_wbuf_process() entered\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")); - return; + 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 (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")); + 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; - } + } - D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem got\n")); + start = ref_offset(*first_raw); + end = ref_offset(*first_raw) + (*first_raw)->totlen; - if (!c->nextblock) { - 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(); + /* 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 "wbuf recover %08x-%08x\n", start, end)); + + buf = NULL; + if (start < c->wbuf_ofs) { + /* First affected node was already partially written. + * Attempt to reread the old data into our buffer. */ + + buf = kmalloc(end - start, GFP_KERNEL); + if (!buf) { + printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n"); + + goto read_failed; } + + /* Do the read... */ + ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); + if (ret == -EIO && retlen == c->wbuf_ofs - start) { + /* ECC recovered */ + ret = 0; + } + if (ret || retlen != c->wbuf_ofs - start) { + printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); + + kfree(buf); + buf = NULL; + read_failed: + first_raw = &(*first_raw)->next_phys; + /* If this was the only node to be recovered, give up */ + if (!(*first_raw)) + return; + + /* It wasn't. Go on and try to recover nodes complete in the wbuf */ + start = ref_offset(*first_raw); + } else { + /* Read succeeded. Copy the remaining data from the wbuf */ + memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); + } + } + /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. + Either 'buf' contains the data, or we find it in the wbuf */ + + + /* ... and get an allocation of space from a shiny new block instead */ + ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len); + if (ret) { + printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); + if (buf) + kfree(buf); return; } - - - /* 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 */ + 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); + + if (ret || retlen != towrite) { + /* Argh. We tried. Really we did. */ + printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); + kfree(buf); + + if (retlen) { + struct jffs2_raw_node_ref *raw2; + + raw2 = jffs2_alloc_raw_node_ref(); + if (!raw2) + return; + + raw2->flash_offset = ofs | REF_OBSOLETE; + raw2->totlen = (*first_raw)->totlen; + raw2->next_phys = NULL; + raw2->next_in_ino = NULL; + + jffs2_add_physical_node_ref(c, raw2); + } + return; + } + printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs); - up(&c->alloc_sem); -} + c->wbuf_len = (end - start) - towrite; + c->wbuf_ofs = ofs + towrite; + memcpy(c->wbuf, buf + towrite, c->wbuf_len); + /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ + kfree(buf); + } else { + /* OK, now we're left with the dregs in whichever buffer we're using */ + if (buf) { + memcpy(c->wbuf, buf, end-start); + kfree(buf); + } else { + memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); + } + c->wbuf_ofs = ofs; + c->wbuf_len = end - start; + } + + /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */ + new_jeb = &c->blocks[ofs / c->sector_size]; + + spin_lock(&c->erase_completion_lock); + if (new_jeb->first_node) { + /* Odd, but possible with ST flash later maybe */ + new_jeb->last_node->next_phys = *first_raw; + } else { + new_jeb->first_node = *first_raw; + } + + raw = first_raw; + while (*raw) { + D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", + (*raw)->totlen, ref_offset(*raw), ref_flags(*raw), ofs)); + + if (ref_obsolete(*raw)) { + /* Shouldn't really happen much */ + new_jeb->dirty_size += (*raw)->totlen; + new_jeb->free_size -= (*raw)->totlen; + c->dirty_size += (*raw)->totlen; + } else { + new_jeb->used_size += (*raw)->totlen; + new_jeb->free_size -= (*raw)->totlen; + jeb->dirty_size += (*raw)->totlen; + jeb->used_size -= (*raw)->totlen; + c->dirty_size += (*raw)->totlen; + } + c->free_size -= (*raw)->totlen; + (*raw)->flash_offset = ofs | ref_flags(*raw); + ofs += (*raw)->totlen; + new_jeb->last_node = *raw; + + raw = &(*raw)->next_phys; + } + + /* Fix up the original jeb now it's on the bad_list */ + *first_raw = NULL; + if (first_raw == &jeb->first_node) { + jeb->last_node = NULL; + D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset)); + list_del(&jeb->list); + list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); + } + else + jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys); + + ACCT_SANITY_CHECK(c,jeb); + D1(ACCT_PARANOIA_CHECK(jeb)); + + ACCT_SANITY_CHECK(c,new_jeb); + D1(ACCT_PARANOIA_CHECK(new_jeb)); + + spin_unlock(&c->erase_completion_lock); + + D1(printk(KERN_DEBUG "wbuf recovery completed OK\n")); +} /* Meaning of pad argument: 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,28 +427,30 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) /* else jffs2_flash_writev has actually filled in the rest of the 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 - */ + ret = -EIO; + } + + jffs2_wbuf_recover(c); - /* FIXME NAND - If we were called from GC or fsync, there's no repair kit yet - */ - 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,16 +644,18 @@ 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; } outvec_to += donelen; c->wbuf_ofs = outvec_to; - + /* All invecs done ? */ if (invec == count) goto alldone; @@ -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; -alldone: + /* If there's a remainder in the wbuf and it's a non-GC write, + remember that the wbuf affects this ino */ +alldone: *retlen = donelen; - /* 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,18 +1009,9 @@ 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); @@ -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); +} diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index cc50ffbaea3d1..f28d9d5bcd36a 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -1,13 +1,13 @@ /* * 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 + * Created by David Woodhouse * * 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; + + raw->flash_offset = flash_ofs; + raw->totlen = PAD(sizeof(*ri)+datalen); + raw->next_phys = NULL; + + ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, + (alloc_mode==ALLOC_GC)?0:f->inocache->ino); - ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen); - if (ret || (retlen != sizeof(*ri) + datalen)) { 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,22 +495,14 @@ 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); + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); - if (ret) { - /* Eep. */ - D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); - return ret; - } + if (ret) { + /* Eep. */ + D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); + return ret; } rd = jffs2_alloc_raw_dirent(); @@ -455,7 +528,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str 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); diff --git a/fs/jffs2/writev.c b/fs/jffs2/writev.c index 84a6c13e6ae5f..80cd3820373f7 100644 --- a/fs/jffs2/writev.c +++ b/fs/jffs2/writev.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse + * Created by David Woodhouse * * 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 $ * */ diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 2e9a2c8e4fddc..e1a49dca4abb3 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -1,14 +1,14 @@ /* * 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 + * Created by David Woodhouse * * 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 $ * */ diff --git a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h index 246ff237325cb..ee80d2520afbd 100644 --- a/include/linux/jffs2_fs_sb.h +++ b/include/linux/jffs2_fs_sb.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.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; -- 2.53.0