diff options
Diffstat (limited to 'fs/jffs2')
-rw-r--r-- | fs/jffs2/README.Locking | 22 | ||||
-rw-r--r-- | fs/jffs2/build.c | 1 | ||||
-rw-r--r-- | fs/jffs2/debug.c | 164 | ||||
-rw-r--r-- | fs/jffs2/debug.h | 6 | ||||
-rw-r--r-- | fs/jffs2/dir.c | 58 | ||||
-rw-r--r-- | fs/jffs2/erase.c | 80 | ||||
-rw-r--r-- | fs/jffs2/file.c | 16 | ||||
-rw-r--r-- | fs/jffs2/fs.c | 42 | ||||
-rw-r--r-- | fs/jffs2/gc.c | 42 | ||||
-rw-r--r-- | fs/jffs2/ioctl.c | 1 | ||||
-rw-r--r-- | fs/jffs2/jffs2_fs_i.h | 4 | ||||
-rw-r--r-- | fs/jffs2/jffs2_fs_sb.h | 7 | ||||
-rw-r--r-- | fs/jffs2/nodelist.h | 2 | ||||
-rw-r--r-- | fs/jffs2/nodemgmt.c | 24 | ||||
-rw-r--r-- | fs/jffs2/readinode.c | 38 | ||||
-rw-r--r-- | fs/jffs2/super.c | 14 | ||||
-rw-r--r-- | fs/jffs2/wbuf.c | 28 | ||||
-rw-r--r-- | fs/jffs2/write.c | 52 |
18 files changed, 399 insertions, 202 deletions
diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking index d14d5a4dc5a..3ea36554107 100644 --- a/fs/jffs2/README.Locking +++ b/fs/jffs2/README.Locking @@ -14,7 +14,7 @@ be fairly close. alloc_sem --------- -The alloc_sem is a per-filesystem semaphore, used primarily to ensure +The alloc_sem is a per-filesystem mutex, used primarily to ensure contiguous allocation of space on the medium. It is automatically obtained during space allocations (jffs2_reserve_space()) and freed upon write completion (jffs2_complete_reservation()). Note that @@ -41,10 +41,10 @@ if the wbuf is currently holding any data is permitted, though. Ordering constraints: See f->sem. - File Semaphore f->sem + File Mutex f->sem --------------------- -This is the JFFS2-internal equivalent of the inode semaphore i->i_sem. +This is the JFFS2-internal equivalent of the inode mutex i->i_sem. It protects the contents of the jffs2_inode_info private inode data, including the linked list of node fragments (but see the notes below on erase_completion_lock), etc. @@ -60,14 +60,14 @@ lead to deadlock, unless we played games with unlocking the i_sem before calling the space allocation functions. Instead of playing such games, we just have an extra internal -semaphore, which is obtained by the garbage collection code and also +mutex, which is obtained by the garbage collection code and also by the normal file system code _after_ allocation of space. Ordering constraints: 1. Never attempt to allocate space or lock alloc_sem with any f->sem held. - 2. Never attempt to lock two file semaphores in one thread. + 2. Never attempt to lock two file mutexes in one thread. No ordering rules have been made for doing so. @@ -86,8 +86,8 @@ a simple spin_lock() rather than spin_lock_bh(). Note that the per-inode list of physical nodes (f->nodes) is a special case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in -the list are protected by the file semaphore f->sem. But the erase -code may remove _obsolete_ nodes from the list while holding only the +the list are protected by the file mutex f->sem. But the erase code +may remove _obsolete_ nodes from the list while holding only the erase_completion_lock. So you can walk the list only while holding the erase_completion_lock, and can drop the lock temporarily mid-walk as long as the pointer you're holding is to a _valid_ node, not an @@ -124,10 +124,10 @@ Ordering constraints: erase_free_sem -------------- -This semaphore is only used by the erase code which frees obsolete -node references and the jffs2_garbage_collect_deletion_dirent() -function. The latter function on NAND flash must read _obsolete_ nodes -to determine whether the 'deletion dirent' under consideration can be +This mutex is only used by the erase code which frees obsolete node +references and the jffs2_garbage_collect_deletion_dirent() function. +The latter function on NAND flash must read _obsolete_ nodes to +determine whether the 'deletion dirent' under consideration can be discarded or whether it is still required to show that an inode has been unlinked. Because reading from the flash may sleep, the erase_completion_lock cannot be held, so an alternative, more diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 722a6b68295..d58f845ccb8 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -345,6 +345,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) INIT_LIST_HEAD(&c->dirty_list); INIT_LIST_HEAD(&c->erasable_list); INIT_LIST_HEAD(&c->erasing_list); + INIT_LIST_HEAD(&c->erase_checking_list); INIT_LIST_HEAD(&c->erase_pending_list); INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); INIT_LIST_HEAD(&c->erase_complete_list); diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index 3a32c64ed49..5544d31c066 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -62,9 +62,9 @@ __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, void __jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f) { - down(&f->sem); + mutex_lock(&f->sem); __jffs2_dbg_fragtree_paranoia_check_nolock(f); - up(&f->sem); + mutex_unlock(&f->sem); } void @@ -153,6 +153,139 @@ __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, kfree(buf); } +void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c) +{ + struct jffs2_eraseblock *jeb; + uint32_t free = 0, dirty = 0, used = 0, wasted = 0, + erasing = 0, bad = 0, unchecked = 0; + int nr_counted = 0; + int dump = 0; + + if (c->gcblock) { + nr_counted++; + free += c->gcblock->free_size; + dirty += c->gcblock->dirty_size; + used += c->gcblock->used_size; + wasted += c->gcblock->wasted_size; + unchecked += c->gcblock->unchecked_size; + } + if (c->nextblock) { + nr_counted++; + free += c->nextblock->free_size; + dirty += c->nextblock->dirty_size; + used += c->nextblock->used_size; + wasted += c->nextblock->wasted_size; + unchecked += c->nextblock->unchecked_size; + } + list_for_each_entry(jeb, &c->clean_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->very_dirty_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->dirty_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erasable_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erasable_pending_wbuf_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erase_pending_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->free_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->bad_used_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + + list_for_each_entry(jeb, &c->erasing_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->erase_checking_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->erase_complete_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->bad_list, list) { + nr_counted++; + bad += c->sector_size; + } + +#define check(sz) \ + if (sz != c->sz##_size) { \ + printk(KERN_WARNING #sz "_size mismatch counted 0x%x, c->" #sz "_size 0x%x\n", \ + sz, c->sz##_size); \ + dump = 1; \ + } + check(free); + check(dirty); + check(used); + check(wasted); + check(unchecked); + check(bad); + check(erasing); +#undef check + + if (nr_counted != c->nr_blocks) { + printk(KERN_WARNING "%s counted only 0x%x blocks of 0x%x. Where are the others?\n", + __func__, nr_counted, c->nr_blocks); + dump = 1; + } + + if (dump) { + __jffs2_dbg_dump_block_lists_nolock(c); + BUG(); + } +} + /* * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'. */ @@ -229,6 +362,9 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, } #endif + if (!(c->flags & (JFFS2_SB_FLAG_BUILDING|JFFS2_SB_FLAG_SCANNING))) + __jffs2_dbg_superblock_counts(c); + return; error: @@ -268,7 +404,10 @@ __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, printk(JFFS2_DBG); for (ref = jeb->first_node; ; ref = ref_next(ref)) { - printk("%#08x(%#x)", ref_offset(ref), ref->__totlen); + printk("%#08x", ref_offset(ref)); +#ifdef TEST_TOTLEN + printk("(%x)", ref->__totlen); +#endif if (ref_next(ref)) printk("->"); else @@ -447,6 +586,21 @@ __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) } } } + if (list_empty(&c->erase_checking_list)) { + printk(JFFS2_DBG "erase_checking_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erase_checking_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG "erase_checking_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); + } + } + } if (list_empty(&c->erase_pending_list)) { printk(JFFS2_DBG "erase_pending_list: empty\n"); @@ -532,9 +686,9 @@ __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) void __jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f) { - down(&f->sem); + mutex_lock(&f->sem); jffs2_dbg_dump_fragtree_nolock(f); - up(&f->sem); + mutex_unlock(&f->sem); } void diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h index 4130adabd76..9645275023e 100644 --- a/fs/jffs2/debug.h +++ b/fs/jffs2/debug.h @@ -38,6 +38,7 @@ #if CONFIG_JFFS2_FS_DEBUG > 1 #define JFFS2_DBG_FRAGTREE2_MESSAGES +#define JFFS2_DBG_READINODE2_MESSAGES #define JFFS2_DBG_MEMALLOC_MESSAGES #endif @@ -115,6 +116,11 @@ #else #define dbg_readinode(fmt, ...) #endif +#ifdef JFFS2_DBG_READINODE2_MESSAGES +#define dbg_readinode2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define dbg_readinode2(fmt, ...) +#endif /* Fragtree build debugging messages */ #ifdef JFFS2_DBG_FRAGTREE_MESSAGES diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index f948f7e6ec8..c63e7a96af0 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -86,7 +86,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, dir_f = JFFS2_INODE_INFO(dir_i); c = JFFS2_SB_INFO(dir_i->i_sb); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */ for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) { @@ -99,7 +99,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, } if (fd) ino = fd->ino; - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); if (ino) { inode = jffs2_iget(dir_i->i_sb, ino); if (IS_ERR(inode)) { @@ -146,7 +146,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) } curofs=1; - down(&f->sem); + mutex_lock(&f->sem); for (fd = f->dents; fd; fd = fd->next) { curofs++; @@ -166,7 +166,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) break; offset++; } - up(&f->sem); + mutex_unlock(&f->sem); out: filp->f_pos = offset; return 0; @@ -275,9 +275,9 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct de ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now); if (!ret) { - down(&f->sem); + mutex_lock(&f->sem); old_dentry->d_inode->i_nlink = ++f->inocache->nlink; - up(&f->sem); + mutex_unlock(&f->sem); d_instantiate(dentry, old_dentry->d_inode); dir_i->i_mtime = dir_i->i_ctime = ITIME(now); atomic_inc(&old_dentry->d_inode->i_count); @@ -351,7 +351,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -361,7 +361,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char f->target = kmalloc(targetlen + 1, GFP_KERNEL); if (!f->target) { printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; @@ -374,7 +374,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -406,7 +406,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -429,7 +429,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -442,7 +442,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -507,7 +507,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -516,7 +516,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -548,7 +548,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -571,7 +571,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -585,7 +585,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -673,7 +673,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -682,7 +682,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -714,7 +714,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -740,7 +740,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -753,7 +753,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -780,14 +780,14 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, if (S_ISDIR(new_dentry->d_inode->i_mode)) { struct jffs2_full_dirent *fd; - down(&victim_f->sem); + mutex_lock(&victim_f->sem); for (fd = victim_f->dents; fd; fd = fd->next) { if (fd->ino) { - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); return -ENOTEMPTY; } } - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); } } @@ -816,9 +816,9 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, /* Don't oops if the victim was a dirent pointing to an inode which didn't exist. */ if (victim_f->inocache) { - down(&victim_f->sem); + mutex_lock(&victim_f->sem); victim_f->inocache->nlink--; - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); } } @@ -836,11 +836,11 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); - down(&f->sem); + mutex_lock(&f->sem); inc_nlink(old_dentry->d_inode); if (f->inocache) f->inocache->nlink++; - up(&f->sem); + mutex_unlock(&f->sem); printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); /* Might as well let the VFS know */ diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index a1db9180633..25a640e566d 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -50,14 +50,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } @@ -84,14 +84,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, if (ret == -ENOMEM || ret == -EAGAIN) { /* Erase failed immediately. Refile it on the list */ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } @@ -107,7 +107,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); @@ -116,9 +116,9 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) if (!list_empty(&c->erase_complete_list)) { jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); - list_del(&jeb->list); + list_move(&jeb->list, &c->erase_checking_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_mark_erased_block(c, jeb); if (!--count) { @@ -139,7 +139,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) jffs2_free_jeb_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_erase_block(c, jeb); @@ -149,12 +149,12 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) /* Be nice */ yield(); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); } spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); } @@ -162,11 +162,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); } @@ -180,26 +180,26 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock failed too many times. */ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { /* We'd like to give this block another try. */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } } - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->bad_size += c->sector_size; list_move(&jeb->list, &c->bad_list); c->nr_erasing_blocks--; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); wake_up(&c->erase_wait); } @@ -350,9 +350,11 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl break; } while(--retlen); c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size); - if (retlen) + if (retlen) { printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n", *wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf)); + return -EIO; + } return 0; } do_flash_read: @@ -373,10 +375,12 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); + ret = -EIO; goto fail; } if (retlen != readlen) { printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + ret = -EIO; goto fail; } for (i=0; i<readlen; i += sizeof(unsigned long)) { @@ -385,6 +389,7 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl if (*datum + 1) { *bad_offset += i; printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", *datum, *bad_offset); + ret = -EIO; goto fail; } } @@ -419,9 +424,6 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb if (jffs2_write_nand_cleanmarker(c, jeb)) goto filebad; } - - /* Everything else got zeroed before the erase */ - jeb->free_size = c->sector_size; } else { struct kvec vecs[1]; @@ -449,48 +451,50 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb goto filebad; } - - /* Everything else got zeroed before the erase */ - jeb->free_size = c->sector_size; - /* FIXME Special case for cleanmarker in empty block */ - jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); } + /* Everything else got zeroed before the erase */ + jeb->free_size = c->sector_size; - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; - c->free_size += jeb->free_size; - c->used_size += jeb->used_size; + c->free_size += c->sector_size; - jffs2_dbg_acct_sanity_check_nolock(c,jeb); - jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + /* Account for cleanmarker now, if it's in-band */ + if (c->cleanmarker_size && !jffs2_cleanmarker_oob(c)) + jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); - list_add_tail(&jeb->list, &c->free_list); + list_move_tail(&jeb->list, &c->free_list); c->nr_erasing_blocks--; c->nr_free_blocks++; + + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); wake_up(&c->erase_wait); return; filebad: - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); /* Stick it on a list (any list) so erase_failed can take it right off again. Silly, but shouldn't happen often. */ - list_add(&jeb->list, &c->erasing_list); + list_move(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_erase_failed(c, jeb, bad_offset); return; refile: /* Stick it back on the list from whence it came and come back later */ jffs2_erase_pending_trigger(c); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); - list_add(&jeb->list, &c->erase_complete_list); + list_move(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index dcc2734e0b5..5e920343b2c 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -115,9 +115,9 @@ static int jffs2_readpage (struct file *filp, struct page *pg) struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host); int ret; - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_readpage_unlock(pg->mapping->host, pg); - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -154,7 +154,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, if (ret) goto out_page; - down(&f->sem); + mutex_lock(&f->sem); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -181,7 +181,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, if (IS_ERR(fn)) { ret = PTR_ERR(fn); jffs2_complete_reservation(c); - up(&f->sem); + mutex_unlock(&f->sem); goto out_page; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); @@ -195,12 +195,12 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); jffs2_complete_reservation(c); - up(&f->sem); + mutex_unlock(&f->sem); goto out_page; } jffs2_complete_reservation(c); inode->i_size = pageofs; - up(&f->sem); + mutex_unlock(&f->sem); } /* @@ -209,9 +209,9 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, * case of a short-copy. */ if (!PageUptodate(pg)) { - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_readpage_nolock(inode, pg); - up(&f->sem); + mutex_unlock(&f->sem); if (ret) goto out_page; } diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index e26ea78c789..3eb1c84b0a3 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -36,6 +36,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) unsigned int ivalid; uint32_t alloclen; int ret; + int alloc_type = ALLOC_NORMAL; D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); @@ -50,20 +51,20 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) mdata = (char *)&dev; D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(inode->i_mode)) { - down(&f->sem); + mutex_lock(&f->sem); mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) { - up(&f->sem); + mutex_unlock(&f->sem); return -ENOMEM; } ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); kfree(mdata); return ret; } - up(&f->sem); + mutex_unlock(&f->sem); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); } @@ -82,7 +83,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) kfree(mdata); return ret; } - down(&f->sem); + mutex_lock(&f->sem); ivalid = iattr->ia_valid; ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -115,6 +116,10 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) ri->compr = JFFS2_COMPR_ZERO; ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); ri->offset = cpu_to_je32(inode->i_size); + } else if (ivalid & ATTR_SIZE && !iattr->ia_size) { + /* For truncate-to-zero, treat it as deletion because + it'll always be obsoleting all previous nodes */ + alloc_type = ALLOC_DELETION; } ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); if (mdatalen) @@ -122,14 +127,14 @@ int jffs2_do |