From b28ba9fa0154f78f3d36f5ae9a42f7bb01663cca Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 25 Feb 2008 15:20:50 +0000 Subject: [JFFS2] Set i_blocks when truncating files Addresses OLPC trac #6480 Signed-off-by: David Woodhouse --- fs/jffs2/fs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index e26ea78c789..3f49562dc50 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -149,6 +149,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) 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; + inode->i_blocks = (inode->i_size + 511) >> 9; f->metadata = NULL; } else { f->metadata = new_metadata; @@ -167,8 +168,10 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) We are protected from a simultaneous write() extending i_size back past iattr->ia_size, because do_truncate() holds the generic inode semaphore. */ - if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) - vmtruncate(inode, iattr->ia_size); + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) { + vmtruncate(inode, iattr->ia_size); + inode->i_blocks = (inode->i_size + 511) >> 9; + } return 0; } -- cgit v1.2.3 From dd919660aacdf4adfcd279556aa03e595f7f0fc2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 25 Feb 2008 15:25:25 +0000 Subject: [JFFS2] Use ALLOC_DELETION priority for truncation to zero length This is going to obsolete all previous nodes, so treat it as deletion. Signed-off-by: David Woodhouse --- fs/jffs2/fs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 3f49562dc50..9dafb53fb1d 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)); @@ -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,7 +127,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) else ri->data_crc = cpu_to_je32(0); - new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, ALLOC_NORMAL); + new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, alloc_type); if (S_ISLNK(inode->i_mode)) kfree(mdata); -- cgit v1.2.3 From f876a59dae09a353444913bdf73b125bc124a848 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Fri, 18 Apr 2008 13:44:12 -0700 Subject: [JFFS2] include function prototype for jffs2_ioctl fs/jffs2/ioctl.c:14:5: warning: symbol 'jffs2_ioctl' was not declared. Signed-off-by: Harvey Harrison Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/ioctl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index f4d525b0ea5..e2177210f62 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -10,6 +10,7 @@ */ #include +#include "nodelist.h" int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) -- cgit v1.2.3 From 8ca646abb4503f39a7d235b89b9f8015e3ab4631 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 22 Apr 2008 11:25:47 +0100 Subject: [JFFS2] Fix sparse warning in nodemgmt.c fs/jffs2/nodemgmt.c:60:8: warning: symbol 'ret' shadows an earlier one fs/jffs2/nodemgmt.c:45:6: originally declared here (reported by Harvey Harrison) Just remove the offending declaration of 'int ret' and use the earlier one. Signed-off-by: David Woodhouse --- fs/jffs2/nodemgmt.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index a0313fa8748..a3e67a4ed67 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -57,7 +57,6 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, /* this needs a little more thought (true :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { - int ret; uint32_t dirty, avail; /* calculate real dirty size -- cgit v1.2.3 From bf66737ca85c41442e99c9d380eb7807d88bac1f Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Fri, 18 Apr 2008 13:44:14 -0700 Subject: [JFFS2] fix sparse warning in write.c fs/jffs2/write.c:585:28: warning: symbol 'fd' shadows an earlier one fs/jffs2/write.c:536:27: originally declared here No need to redeclare fd, use the original one, after this point, fd is always reassigned before it used again. Signed-off-by: Harvey Harrison Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 776f13cbf2b..beade550909 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -582,9 +582,9 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); } else { - struct jffs2_full_dirent *fd = dir_f->dents; uint32_t nhash = full_name_hash(name, namelen); + fd = dir_f->dents; /* We don't actually want to reserve any space, but we do want to be holding the alloc_sem when we write to flash */ down(&c->alloc_sem); -- cgit v1.2.3 From 25dc30b4cd68df1de8932fe77ca574227d42a259 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 22 Apr 2008 12:12:25 +0100 Subject: [JFFS2] fix sparse warnings in gc.c fs/jffs2/gc.c:1147:29: warning: symbol 'jeb' shadows an earlier one fs/jffs2/gc.c:1084:89: originally declared here fs/jffs2/gc.c:1197:29: warning: symbol 'jeb' shadows an earlier one fs/jffs2/gc.c:1084:89: originally declared here Rename the unused 'jeb' argument to avoid this. We could potentially remove the argument, but GCC should be doing that anyway. Signed-off-by: David Woodhouse --- fs/jffs2/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 32ff0373aa0..59aeb982043 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -1081,7 +1081,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras return 0; } -static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *orig_jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { -- cgit v1.2.3 From cca15841710da39490abc17f56b55703e3140955 Mon Sep 17 00:00:00 2001 From: michael Date: Fri, 18 Apr 2008 13:44:17 -0700 Subject: [JFFS2] add write verify on dataflash. Add the write verification buffer to the dataflash. The mtd_dataflash has the CONFIG_DATAFLASH_WRITE_VERIFY so is better a change to Kconfig. Signed-off-by: Michael Trimarchi Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/wbuf.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'fs') diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index d1d4f27464b..ba49f19cff8 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1236,12 +1236,24 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) { if (!c->wbuf) return -ENOMEM; +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->oobbuf); + kfree(c->wbuf); + return -ENOMEM; + } +#endif + printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size); return 0; } void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif kfree(c->wbuf); } -- cgit v1.2.3 From ced22070363ef50e4a47aadd003a81ebeaa3f917 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 22 Apr 2008 15:13:40 +0100 Subject: [JFFS2] semaphore->mutex conversion Signed-off-by: David Woodhouse --- fs/jffs2/README.Locking | 22 +++++++++---------- fs/jffs2/debug.c | 8 +++---- fs/jffs2/dir.c | 58 ++++++++++++++++++++++++------------------------- fs/jffs2/erase.c | 42 +++++++++++++++++------------------ fs/jffs2/file.c | 16 +++++++------- fs/jffs2/fs.c | 28 ++++++++++++------------ fs/jffs2/gc.c | 32 +++++++++++++-------------- fs/jffs2/jffs2_fs_i.h | 2 +- fs/jffs2/jffs2_fs_sb.h | 6 ++--- fs/jffs2/nodemgmt.c | 18 +++++++-------- fs/jffs2/readinode.c | 23 ++++++++++---------- fs/jffs2/super.c | 14 ++++++------ fs/jffs2/wbuf.c | 16 +++++++------- fs/jffs2/write.c | 50 +++++++++++++++++++++--------------------- 14 files changed, 168 insertions(+), 167 deletions(-) (limited to 'fs') 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/debug.c b/fs/jffs2/debug.c index 3a32c64ed49..660793107e9 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 @@ -532,9 +532,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/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..96d9ad56e57 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); @@ -118,7 +118,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); list_del(&jeb->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); } @@ -456,7 +456,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); } - 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; @@ -469,28 +469,28 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb c->nr_erasing_blocks--; c->nr_free_blocks++; 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); 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); 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 9dafb53fb1d..3eb1c84b0a3 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -51,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)); } @@ -83,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); @@ -134,7 +134,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) if (IS_ERR(new_metadata)) { jffs2_complete_reservation(c); jffs2_free_raw_inode(ri); - up(&f->sem); + mutex_unlock(&f->sem); return PTR_ERR(new_metadata); } /* It worked. Update the inode */ @@ -165,7 +165,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) } jffs2_free_raw_inode(ri); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); /* We have to do the vmtruncate() without f->sem held, since @@ -256,12 +256,12 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) c = JFFS2_SB_INFO(inode->i_sb); jffs2_init_inode_info(f); - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); iget_failed(inode); return ERR_PTR(ret); } @@ -338,7 +338,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); } - up(&f->sem); + mutex_unlock(&f->sem); D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); unlock_new_inode(inode); @@ -347,7 +347,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) error_io: ret = -EIO; error: - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); iget_failed(inode); return ERR_PTR(ret); @@ -388,9 +388,9 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) Flush the writebuffer, if neccecary, else we loose it */ if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); } if (!(*flags & MS_RDONLY)) @@ -437,7 +437,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i f = JFFS2_INODE_INFO(inode); jffs2_init_inode_info(f); - down(&f->sem); + mutex_lock(&f->sem); memset(ri, 0, sizeof(*ri)); /* Set OS-specific defaults for new inodes */ diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 59aeb982043..26c7992c45c 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -126,7 +126,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) int ret = 0, inum, nlink; int xattr = 0; - if (down_interruptible(&c->alloc_sem)) + if (mutex_lock_interruptible(&c->alloc_sem)) return -EINTR; for (;;) { @@ -143,7 +143,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } @@ -190,7 +190,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) made no progress in this case, but that should be OK */ c->checked_ino--; - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); return 0; @@ -210,7 +210,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } @@ -223,7 +223,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) if (!jeb) { D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -EIO; } @@ -232,7 +232,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); if (!jeb->used_size) { - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); goto eraseit; } @@ -248,7 +248,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); jeb->gc_node = raw; spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); BUG(); } } @@ -266,7 +266,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) /* Just mark it obsolete */ jffs2_mark_node_obsolete(c, raw); } - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); goto eraseit_lock; } @@ -334,7 +334,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) */ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", ic->ino, ic->state); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); spin_unlock(&c->inocache_lock); BUG(); @@ -345,7 +345,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) the alloc_sem() (for marking nodes invalid) so we must drop the alloc_sem before sleeping. */ - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); @@ -416,7 +416,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) ret = -ENOSPC; } release_sem: - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); eraseit_lock: /* If we've finished this block, start it erasing */ @@ -445,7 +445,7 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era uint32_t start = 0, end = 0, nrfrags = 0; int ret = 0; - down(&f->sem); + mutex_lock(&f->sem); /* Now we have the lock for this inode. Check that it's still the one at the head of the list. */ @@ -525,7 +525,7 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era } } upnout: - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -846,7 +846,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct /* Prevent the erase code from nicking the obsolete node refs while we're looking at them. I really don't like this extra lock but can't see any alternative. Suggestions on a postcard to... */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { @@ -899,7 +899,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct /* OK. The name really does match. There really is still an older node on the flash which our deletion dirent obsoletes. So we have to write out a new deletion dirent to replace it */ - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); @@ -908,7 +908,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct return jffs2_garbage_collect_dirent(c, jeb, f, fd); } - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); kfree(rd); } diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index a841f4973a7..b5427b5c948 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -24,7 +24,7 @@ struct jffs2_inode_info { before letting GC proceed. Or we'd have to put ugliness into the GC code so it didn't attempt to obtain the i_mutex for the inode(s) which are already locked */ - struct semaphore sem; + struct mutex sem; /* The highest (datanode) version number used for this ino */ uint32_t highest_version; diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 18fca2b9e53..d9c4b27575e 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,7 +44,7 @@ struct jffs2_sb_info { struct completion gc_thread_start; /* GC thread start completion */ struct completion gc_thread_exit; /* GC thread exit completion port */ - struct semaphore alloc_sem; /* Used to protect all the following + struct mutex alloc_sem; /* Used to protect all the following fields, and also to protect against out-of-order writing of nodes. And GC. */ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER @@ -104,7 +104,7 @@ struct jffs2_sb_info { /* Sem to allow jffs2_garbage_collect_deletion_dirent to drop the erase_completion_lock while it's holding a pointer to an obsoleted node. I don't like this. Alternatives welcomed. */ - struct semaphore erase_free_sem; + struct mutex erase_free_sem; uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index a3e67a4ed67..747a73f0aa4 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -48,7 +48,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); @@ -81,7 +81,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, dirty, c->unchecked_size, c->sector_size)); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } @@ -104,11 +104,11 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, 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); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } - up(&c->alloc_sem); + mutex_unlock(&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, @@ -124,7 +124,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, if (signal_pending(current)) return -EINTR; - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); spin_lock(&c->erase_completion_lock); } @@ -137,7 +137,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, if (!ret) ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); if (ret) - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } @@ -462,7 +462,7 @@ void jffs2_complete_reservation(struct jffs2_sb_info *c) { D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n")); jffs2_garbage_collect_trigger(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); } static inline int on_list(struct list_head *obj, struct list_head *head) @@ -511,7 +511,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref any jffs2_raw_node_refs. So we don't need to stop erases from happening, or protect against people holding an obsolete jffs2_raw_node_ref without the erase_completion_lock. */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); } spin_lock(&c->erase_completion_lock); @@ -714,7 +714,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref } out_erase_sem: - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); } int jffs2_thread_should_wake(struct jffs2_sb_info *c) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index e512a93d624..8a7cf1e8d68 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1193,7 +1193,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", ret, retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return ret?ret:-EIO; } @@ -1202,7 +1202,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, if (crc != je32_to_cpu(latest_node->node_crc)) { JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(rii.latest_ref)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1242,7 +1242,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); if (!f->target) { JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -ENOMEM; } @@ -1255,7 +1255,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, ret = -EIO; kfree(f->target); f->target = NULL; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -ret; } @@ -1273,14 +1273,14 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, if (f->metadata) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } if (!frag_first(&f->fragtree)) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1289,7 +1289,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1379,12 +1379,13 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i if (!f) return -ENOMEM; - init_MUTEX_LOCKED(&f->sem); + mutex_init(&f->sem); + mutex_lock(&f->sem); f->inocache = ic; ret = jffs2_do_read_inode_internal(c, f, &n); if (!ret) { - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); } kfree (f); @@ -1398,7 +1399,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_clear_acl(f); jffs2_xattr_delete_inode(c, f->inocache); - down(&f->sem); + mutex_lock(&f->sem); deleted = f->inocache && !f->inocache->nlink; if (f->inocache && f->inocache->state != INO_STATE_CHECKING) @@ -1430,5 +1431,5 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_del_ino_cache(c, f->inocache); } - up(&f->sem); + mutex_unlock(&f->sem); } diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 4677355996c..f3353df178e 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -47,7 +47,7 @@ static void jffs2_i_init_once(struct kmem_cache *cachep, void *foo) { struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; - init_MUTEX(&ei->sem); + mutex_init(&ei->sem); inode_init_once(&ei->vfs_inode); } @@ -55,9 +55,9 @@ static int jffs2_sync_fs(struct super_block *sb, int wait) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return 0; } @@ -95,8 +95,8 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) /* Initialize JFFS2 superblock locks, the further initialization will * be done later */ - init_MUTEX(&c->alloc_sem); - init_MUTEX(&c->erase_free_sem); + mutex_init(&c->alloc_sem); + mutex_init(&c->erase_free_sem); init_waitqueue_head(&c->erase_wait); init_waitqueue_head(&c->inocache_wq); spin_lock_init(&c->erase_completion_lock); @@ -125,9 +125,9 @@ static void jffs2_put_super (struct super_block *sb) D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); jffs2_sum_exit(c); diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index ba49f19cff8..8de52b60767 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -578,8 +578,8 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) if (!jffs2_is_writebuffered(c)) return 0; - if (!down_trylock(&c->alloc_sem)) { - up(&c->alloc_sem); + if (mutex_trylock(&c->alloc_sem)) { + mutex_unlock(&c->alloc_sem); printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); BUG(); } @@ -702,10 +702,10 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) if (!c->wbuf) return 0; - down(&c->alloc_sem); + mutex_lock(&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); + mutex_unlock(&c->alloc_sem); return 0; } @@ -725,14 +725,14 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) } else while (old_wbuf_len && old_wbuf_ofs == c->wbuf_ofs) { - up(&c->alloc_sem); + mutex_unlock(&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); + mutex_lock(&c->alloc_sem); down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); /* retry flushing wbuf in case jffs2_wbuf_recover @@ -742,12 +742,12 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) up_write(&c->wbuf_sem); break; } - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); } D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n")); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index beade550909..665fce9797d 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -137,12 +137,12 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 JFFS2_SUMMARY_INODE_SIZE); } else { /* Locking pain */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE); - down(&f->sem); + mutex_lock(&f->sem); } if (!ret) { @@ -285,12 +285,12 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff JFFS2_SUMMARY_DIRENT_SIZE(namelen)); } else { /* Locking pain */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); - down(&f->sem); + mutex_lock(&f->sem); } if (!ret) { @@ -353,7 +353,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); break; } - down(&f->sem); + mutex_lock(&f->sem); datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); @@ -381,7 +381,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, if (IS_ERR(fn)) { ret = PTR_ERR(fn); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); if (!retried) { /* Write error to be retried */ @@ -403,11 +403,11 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); break; } - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); if (!datalen) { printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n"); @@ -439,7 +439,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str JFFS2_SUMMARY_INODE_SIZE); D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -454,7 +454,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str if (IS_ERR(fn)) { D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); return PTR_ERR(fn); } @@ -463,7 +463,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_init_security(&f->vfs_inode, &dir_f->vfs_inode); @@ -489,7 +489,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str return -ENOMEM; } - 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); @@ -513,7 +513,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } @@ -522,7 +522,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return 0; } @@ -551,7 +551,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, return ret; } - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -574,21 +574,21 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, if (IS_ERR(fd)) { jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); } else { uint32_t nhash = full_name_hash(name, namelen); fd = dir_f->dents; /* We don't actually want to reserve any space, but we do want to be holding the alloc_sem when we write to flash */ - down(&c->alloc_sem); - down(&dir_f->sem); + mutex_lock(&c->alloc_sem); + mutex_lock(&dir_f->sem); for (fd = dir_f->dents; fd; fd = fd->next) { if (fd->nhash == nhash && @@ -607,7 +607,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, break; } } - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); } /* dead_f is NULL if this was a rename not a real unlink */ @@ -615,7 +615,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, pointing to an inode which didn't exist. */ if (dead_f && dead_f->inocache) { - down(&dead_f->sem); + mutex_lock(&dead_f->sem); if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { while (dead_f->dents) { @@ -639,7 +639,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, dead_f->inocache->nlink--; /* NB: Caller must set inode nlink if appropriate */ - up(&dead_f->sem); + mutex_unlock(&dead_f->sem); } jffs2_complete_reservation(c); @@ -666,7 +666,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint return ret; } - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -691,7 +691,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint if (IS_ERR(fd)) { jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } @@ -699,7 +699,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return 0; } -- cgit v1.2.3 From cf9d1e428cc28ef5798aeda0822a6ce64849a439 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 22 Apr 2008 23:53:26 +0100 Subject: [JFFS2] Self-sufficient #includes in jffs2_fs_i.h: include ... instead of which we don't need any more anyway. Signed-off-by: David Woodhouse --- fs/jffs2/jffs2_fs_i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index b5427b5c948..31559f45fdd 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include struct jffs2_inode_info { /* We need an internal mutex similar to inode->i_mutex. -- cgit v1.2.3 From 014b164e1392a166fe96e003d2f0e7ad2e2a0bb7 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 22 Apr 2008 23:54:38 +0100 Subject: [JFFS2] Fix free space leak with in-band cleanmarkers We were accounting for the cleanmarker by calling jffs2_link_node_ref() (without locking!), which adjusted both superblock and per-eraseblock accounting, subtracting the size of the cleanmarker from {jeb,c}->free_size and adding it to {jeb,c}->used_size. But only _then_ were we adding the size of the newly-erased block back to the superblock counts, and we were adding each of jeb->{free,used}_size to the corresponding superblock counts. Thus, the size of the cleanmarker was effectively subtracted from the superblock's free_size _twice_. Fix this, by always adding a full eraseblock size to c->free_size when we've erased a block. And call jffs2_link_node_ref() under the proper lock, while we're at it. Thanks to Alexander Yurchenko and/or Damir Shayhutdinov for (almost) pinpointing the problem. Signed-off-by: David Woodhouse --- fs/jffs2/erase.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 96d9ad56e57..bdc6a7bec80 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -419,9 +419,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,18 +446,19 @@ 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; 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; + + /* 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); jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); -- cgit v1.2.3 From 85a62db6245a82f07a31b387915ee2180b9ea11a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 23 Apr 2008 01:17:51 +0100 Subject: [JFFS2] Add paranoia debugging for superblock counts The problem fixed in commit 014b164e1392a166fe96e003d2f0e7ad2e2a0bb7 (space leak with in-band cleanmarkers) would have been caught a lot quicker if our paranoid debugging mode had included adding up the size counts from all the eraseblocks and comparing the totals with the counts in the superblock. Add that. Make jffs2_mark_erased_block() file the newly-erased block on the free_list before calling the debug function, to make it happy. Signed-off-by: David Woodhouse --- fs/jffs2/debug.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/jffs2/erase.c | 7 +-- 2 files changed, 136 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index 660793107e9..590bdd6e014 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -153,6 +153,135 @@ __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_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 +358,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: diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index bdc6a7bec80..65d91943fc2 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -460,12 +460,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb if (c->cleanmarker_size && !jffs2_cleanmarker_oob(c)) jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); - jffs2_dbg_acct_sanity_check_nolock(c,jeb); - jffs2_dbg_acct_paranoia_check_nolock(c, jeb); - list_add_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); mutex_unlock(&c->erase_free_sem); wake_up(&c->erase_wait); -- cgit v1.2.3 From 27e6b8e388fffb332476ddab00bbe05cd5da5f32 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 23 Apr 2008 01:25:33 +0100 Subject: [JFFS2] Honour TEST_TOTLEN macro in debugging code. ref->__totlen is going! Signed-off-by: David Woodhouse --- fs/jffs2/debug.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index 590bdd6e014..e198468a8c6 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -400,7 +400,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 -- cgit v1.2.3 From 19e56ceae7cb1833ffd806038c19477b2c265f9f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 23 Apr 2008 01:26:12 +0100 Subject: [JFFS2] Finally remove redundant ref->__totlen field. Haven't had any complaints about it recently, despite having the test code enabled to verify that the calculated length is correct. Kill it off, just by #undef TEST_TOTLEN for now; removing it for real can come a little later. Signed-off-by: David Woodhouse --- fs/jffs2/nodelist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index ec1aae9e695..8219df6eb6d 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -87,7 +87,7 @@ struct jffs2_raw_node_ref xattr_ref or xattr_datum instead. The common part of those structures has NULL in the first word. See jffs2_raw_ref_to_ic() below */ uint32_t flash_offset; -#define TEST_TOTLEN +#undef TEST_TOTLEN #ifdef TEST_TOTLEN uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ #endif -- cgit v1.2.3 From 8a0f572397ca0673c65c1662946014bb73b5cdc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Grafstr=C3=B6m?= Date: Wed, 12 Mar 2008 20:29:23 +0100 Subject: [JFFS2] Return values of jffs2_block_check_erase error paths It looks the error paths in jffs2_block_check_erase() have wrong return values. A block that failed to be erased never gets marked as bad. Signed-off-by: David Woodhouse --- fs/jffs2/erase.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 65d91943fc2..5e2719cb693 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -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 Date: Wed, 23 Apr 2008 14:15:24 +0100 Subject: [JFFS2] Add erase_checking_list to hold blocks being marked. Just to keep the debug code happy when it's adding all the blocks up. Otherwise, they disappear for a while while the locks are dropped to check them and write the cleanmarker. Signed-off-by: David Woodhouse --- fs/jffs2/build.c | 1 + fs/jffs2/debug.c | 19 +++++++++++++++++++ fs/jffs2/erase.c | 8 ++++---- fs/jffs2/jffs2_fs_sb.h | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) (limited to 'fs') 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 e198468a8c6..5544d31c066 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -246,6 +246,10 @@ void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c) 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; @@ -582,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"); diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 5e2719cb693..25a640e566d 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -116,7 +116,7 @@ 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); mutex_unlock(&c->erase_free_sem); jffs2_mark_erased_block(c, jeb); @@ -465,7 +465,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb 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++; @@ -482,7 +482,7 @@ filebad: 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); mutex_unlock(&c->erase_free_sem); jffs2_erase_failed(c, jeb, bad_offset); @@ -493,7 +493,7 @@ refile: jffs2_erase_pending_trigger(c); 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); mutex_unlock(&c->erase_free_sem); return; diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index d9c4b27575e..85ef6dbb1be 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -87,6 +87,7 @@ struct jffs2_sb_info { struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ struct list_head erasing_list; /* Blocks which are currently erasing */ + struct list_head erase_checking_list; /* Blocks which are being checked and marked */ struct list_head erase_pending_list; /* Blocks which need erasing now */ struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ struct list_head free_list; /* Blocks which are free and ready to be used */ -- cgit v1.2.3 From 422b120238130307da64fa44c9fb722bfaf5f1af Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 23 Apr 2008 15:40:52 +0100 Subject: [JFFS2] Fix jffs2_reserve_space() when all blocks are pending erasure. When _all_ the blocks were on the erase_pending_list, we could't find a block to GC from but there was no _actually_ free space, and jffs2_reserve_space() would get a little unhappy. Handle this case by returning -EAGAIN from jffs2_garbage_collect_pass(). There are two callers of that function -- jffs2_flush_wbuf_gc(), which will interpret it as an error and flush the writebuffer by other means, and jffs2_reserve_space(), which we modify to respond to -EAGAIN with an immediate call to jffs2_erase_pending_blocks() and another run round the loop. Signed-off-by: David Woodhouse --- fs/jffs2/gc.c | 8 +++++++- fs/jffs2/nodemgmt.c | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 26c7992c45c..bad005664e3 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -221,7 +221,13 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) jeb = jffs2_find_gc_block(c); if (!jeb) { - D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); + /* Couldn't find a free block. But maybe we can just erase one and make 'progress'? */ + if (!list_empty(&c->erase_pending_list)) { + spin_unlock(&c->erase_completion_lock); + mutex_unlock(&c->alloc_sem); + return -EAGAIN; + } + D1(printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->alloc_sem); return -EIO; diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 747a73f0aa4..9df8f3ef20d 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -116,7 +116,10 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); - if (ret) + + if (ret == -EAGAIN) + jffs2_erase_pending_blocks(c, 1); + else if (ret) return ret; cond_resched(); -- cgit v1.2.3 From 2c61cb250cf7e8cdd3b83b79b76d2ea0b3da010a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 23 Apr 2008 16:43:15 +0100 Subject: [JFFS2] Introduce dbg_readinode2 log level, use it to shut read_dnode() up We haven't seen bugs in this for a while now, since the rewrite. No need to be _quite_ so verbose... Signed-off-by: David Woodhouse --- fs/jffs2/debug.h | 6 ++++++ fs/jffs2/readinode.c | 15 ++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'fs') 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/readinode.c b/fs/jffs2/readinode.c index 8a7cf1e8d68..4cb4d76de07 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -825,8 +825,9 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref else // normal case... tn->fn->size = je32_to_cpu(rd->dsize); - dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", - ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); + dbg_readinode2("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", + ref_offset(ref), je32_to_cpu(rd->version), + je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); ret = jffs2_add_tn_to_tree(c, rii, tn); @@ -836,13 +837,13 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref jffs2_free_tmp_dnode_info(tn); return ret; } -#ifdef JFFS2_DBG_READINODE_MESSAGES - dbg_readinode("After adding ver %d:\n", je32_to_cpu(rd->version)); +#ifdef JFFS2_DBG_READINODE2_MESSAGES + dbg_readinode2("After adding ver %d:\n", je32_to_cpu(rd->version)); tn = tn_first(&rii->tn_root); while (tn) { - dbg_readinode("%p: v %d r 0x%x-0x%x ov %d\n", - tn, tn->version, tn->fn->ofs, - tn->fn->ofs+tn->fn->size, tn->overlapped); + dbg_readinode2("%p: v %d r 0x%x-0x%x ov %d\n", + tn, tn->version, tn->fn->ofs, + tn->fn->ofs+tn->fn->size, tn->overlapped); tn = tn_next(tn); } #endif -- cgit v1.2.3 From 5fcc60c3a05bf417229fba715e7aec52bf6717fb Mon Sep 17 00:00:00 2001 From: "David M. Richter" Date: Wed, 23 Apr 2008 16:28:59 -0400 Subject: leases: fix a return-value mixup Fixes a return-value mixup from 85c59580b30c82aa771aa33b37217a6b6851bc14 "locks: Fix potential OOPS in generic_setlease()", in which -ENOMEM replaced what had been intended to stay -EAGAIN in the variable "error". Signed-off-by: David M. Richter Signed-off-by: J. Bruce Fields --- fs/locks.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/locks.c b/fs/locks.c index 592faadbcec..b9f3a0bed30 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1404,6 +1404,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) rdlease_count++; } + error = -EAGAIN; if ((arg == F_RDLCK && (wrlease_count > 0)) || (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) goto out; -- cgit v1.2.3 From 288b2fd8251cb0bcb14b8a93755ef9c78de70e0f Mon Sep 17 00:00:00 2001 From: "David M. Richter" Date: Wed, 23 Apr 2008 16:29:00 -0400 Subject: leases: when unlocking, skip locking-related steps In generic_setlease(), we don't need to allocate a new struct file_lock or check for readers or writers when called with F_UNLCK. Signed-off-by: David M. Richter Signed-off-by: J. Bruce Fields --- fs/locks.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/locks.c b/fs/locks.c index b9f3a0bed30..da1d0ddb4ab 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1367,18 +1367,20 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) lease = *flp; - error = -EAGAIN; - if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) - goto out; - if ((arg == F_WRLCK) - && ((atomic_read(&dentry->d_count) > 1) - || (atomic_read(&inode->i_count) > 1))) - goto out; + if (arg != F_UNLCK) { + error = -EAGAIN; + if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) + goto out; + if ((arg == F_WRLCK) + && ((atomic_read(&dentry->d_count) > 1) + || (atomic_read(&inode->i_count) > 1))) + goto out; - error = -ENOMEM; - new_fl = locks_alloc_lock(); - if (new_fl == NULL) - goto out; + error = -ENOMEM; + new_fl = locks_alloc_lock(); + if (new_fl == NULL) + goto out; + } /* * At this point, we know that if there is an exclusive -- cgit v1.2.3 From 190855576743a510219fc67886dace29b825d8cb Mon Sep 17 00:00:00 2001 From: "David M. Richter" Date: Wed, 23 Apr 2008 16:29:01 -0400 Subject: leases: move lock allocation earlier in generic_setlease() In generic_setlease(), the struct file_lock is allocated after tests for the presence of conflicting readers/writers is done, despite the fact that the allocation might block; this patch moves the allocation earlier. A subsequent set of patches will rely on this behavior to properly serialize between a modified __break_lease() and generic_setlease(). Signed-off-by: David M. Richter Signed-off-by: J. Bruce Fields --- fs/locks.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/locks.c b/fs/locks.c index da1d0ddb4ab..6a132cd4fa5 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1368,6 +1368,11 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) lease = *flp; if (arg != F_UNLCK) { + error = -ENOMEM; + new_fl = locks_alloc_lock(); + if (new_fl == NULL) + goto out; + error = -EAGAIN; if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) goto out; @@ -1375,11 +1380,6 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) && ((atomic_read(&dentry->d_count) > 1) || (atomic_read(&inode->i_count) > 1))) goto out; - - error = -ENOMEM; - new_fl = locks_alloc_lock(); - if (new_fl == NULL) - goto out; } /* -- cgit v1.2.3 From 9d91cdcc0cce3186742f38e7352459b2087fbb86 Mon Sep 17 00:00:00 2001 From: "David M. Richter" Date: Wed, 23 Apr 2008 16:29:02 -0400 Subject: leases: remove unneeded variable from fcntl_setlease(). fcntl_setlease() has a struct dentry* that is used only once; this patch removes it. Signed-off-by: David M. Richter Signed-off-by: J. Bruce Fields --- fs/locks.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/locks.c b/fs/locks.c index 6a132cd4fa5..2e0fa661e42 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1493,8 +1493,7 @@ EXPORT_SYMBOL_GPL(vfs_setlease); int fcntl_setlease(unsigned int fd, struct file *filp, long arg) { struct file_lock fl, *flp = &fl; - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = filp->f_path.dentry->d_inode; int error; locks_init_lock(&fl); -- cgit v1.2.3 From 4373ea84c84d8a96e99d3da99e813d3e36d1bd11 Mon Sep 17 00:00:00 2001 From: Wendy Cheng Date: Thu, 17 Jan 2008 11:10:12 -0500 Subject: lockd: unlock lockd locks associated with a given server ip For high-availability NFS service, we generally need to be able to drop file locks held on the exported filesystem before moving clients to a new server. Currently the only way to do that is by shutting down lockd entirely, which is often undesireable (for example, if you want to continue exporting other filesystems). This patch allows the administrator to release all locks held by clients accessing the client through a given server ip address, by echoing that address to a new file, /proc/fs/nfsd/unlock_ip, as in: shell> echo 10.1.1.2 > /proc/fs/nfsd/unlock_ip The expected sequence of events can be: 1. Tear down the IP address 2. Unexport the path 3. Write IP to /proc/fs/nfsd/unlock_ip to unlock files 4. Signal peer to begin take-over. For now we only support IPv4 addresses and NFSv2/v3 (NFSv4 locks are not affected). Also, if unmounting the filesystem is required, we assume at step 3 that clients using the given server ip are the only clients holding locks on the given filesystem; otherwise, an additional patch is required to allow revoking all locks held by lockd on a given filesystem. Signed-off-by: S. Wendy Cheng Cc: Lon Hohberger Cc: Christoph Hellwig Signed-off-by: J. Bruce Fields fs/lockd/svcsubs.c | 66 +++++++++++++++++++++++++++++++++++++++----- fs/nfsd/nfsctl.c | 65 +++++++++++++++++++++++++++++++++++++++++++ include/linux/lockd/lockd.h | 7 ++++ 3 files changed, 131 insertions(+), 7 deletions(-) --- fs/lockd/svcsubs.c | 36 +++++++++++++++++++++++++++++++----- fs/nfsd/nfsctl.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index dbbefbcd671..1adcf93cb6e 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #define NLMDBG_FACILITY NLMDBG_SVCSUBS @@ -230,7 +232,7 @@ nlm_file_inuse(struct nlm_file *file) * Loop over all files in the file table. */ static int -nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match) +nlm_traverse_files(void *data, nlm_host_match_fn_t match) { struct hlist_node *pos, *next; struct nlm_file *file; @@ -244,7 +246,7 @@ nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match) /* Traverse locks, blocks and shares of this file * and update file->f_locks count */ - if (nlm_inspect_file(host, file, match)) + if (nlm_inspect_file(data, file, match)) ret = 1; mutex_lock(&nlm_file_mutex); @@ -303,21 +305,27 @@ nlm_release_file(struct nlm_file *file) * Used by nlmsvc_invalidate_all */ static int -nlmsvc_mark_host(struct nlm_host *host, struct nlm_host *dummy) +nlmsvc_mark_host(void *data, struct nlm_host *dummy) { + struct nlm_host *host = data; + host->h_inuse = 1; return 0; } static int -nlmsvc_same_host(struct nlm_host *host, struct nlm_host *other) +nlmsvc_same_host(void *data, struct nlm_host *other) { + struct nlm_host *host = data; + return host == other; } static int -nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy) +nlmsvc_is_client(void *data, struct nlm_host *dummy) { + struct nlm_host *host = data; + if (host->h_server) { /* we are destroying locks even though the client * hasn't asked us too, so don't unmonitor the @@ -370,3 +378,21 @@ nlmsvc_invalidate_all(void) */ nlm_traverse_files(NULL, nlmsvc_is_client); } + +static int +nlmsvc_match_ip(void *datap, struct nlm_host *host) +{ + __be32 *server_addr = datap; + + return host->h_saddr.sin_addr.s_addr == *server_addr; +} + +int +nlmsvc_unlock_all_by_ip(__be32 server_addr) +{ + int ret; + ret = nlm_traverse_files(&server_addr, nlmsvc_match_ip); + return ret ? -EIO : 0; + +} +EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 613bcb8171a..7706f6c9ab8 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include @@ -53,6 +55,7 @@ enum { NFSD_Getfs, NFSD_List, NFSD_Fh, + NFSD_FO_UnlockIP, NFSD_Threads, NFSD_Pool_Threads, NFSD_Versions, @@ -89,6 +92,8 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size); static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); #endif +static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size); + static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Svc] = write_svc, [NFSD_Add] = write_add, @@ -98,6 +103,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Getfd] = write_getfd, [NFSD_Getfs] = write_getfs, [NFSD_Fh] = write_filehandle, + [NFSD_FO_UnlockIP] = failover_unlock_ip, [NFSD_Threads] = write_threads, [NFSD_Pool_Threads] = write_pool_threads, [NFSD_Versions] = write_versions, @@ -298,6 +304,31 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size) return err; } +static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) +{ + __be32 server_ip; + char *fo_path, c; + int b1, b2, b3, b4; + + /* sanity check */ + if (size == 0) + return -EINVAL; + + if (buf[size-1] != '\n') + return -EINVAL; + + fo_path = buf; + if (qword_get(&buf, fo_path, size) < 0) + return -EINVAL; + + /* get ipv4 address */ + if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4) + return -EINVAL; + server_ip = htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); + + return nlmsvc_unlock_all_by_ip(server_ip); +} + static ssize_t write_filehandle(struct file *file, char *buf, size_t size) { /* request is: @@ -700,6 +731,8 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, + [NFSD_FO_UnlockIP] = {"unlock_ip", + &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, -- cgit v1.2.3 From 17efa372cfe4d189705edf6cd4fbe283827a5dc7 Mon Sep 17 00:00:00 2001 From: Wendy Cheng Date: Thu, 17 Jan 2008 11:10:12 -0500 Subject: lockd: unlock lockd locks held for a certain filesystem Add /proc/fs/nfsd/unlock_filesystem, which allows e.g.: shell> echo /mnt/sfs1 > /proc/fs/nfsd/unlock_filesystem so that a filesystem can be unmounted before allowing a peer nfsd to take over nfs service for the filesystem. Signed-off-by: S. Wendy Cheng Cc: Lon Hohberger Cc: Christoph Hellwig Signed-off-by: J. Bruce Fields fs/lockd/svcsubs.c | 66 +++++++++++++++++++++++++++++++++++++++----- fs/nfsd/nfsctl.c | 65 +++++++++++++++++++++++++++++++++++++++++++ include/linux/lockd/lockd.h | 7 ++++ 3 files changed, 131 insertions(+), 7 deletions(-) --- fs/lockd/svcsubs.c | 37 ++++++++++++++++++++++++++++++++----- fs/nfsd/nfsctl.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 1adcf93cb6e..d1c48b539df 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -196,6 +196,12 @@ again: return 0; } +static int +nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2) +{ + return 1; +} + /* * Inspect a single file */ @@ -232,7 +238,8 @@ nlm_file_inuse(struct nlm_file *file) * Loop over all files in the file table. */ static int -nlm_traverse_files(void *data, nlm_host_match_fn_t match) +nlm_traverse_files(void *data, nlm_host_match_fn_t match, + int (*is_failover_file)(void *data, struct nlm_file *file)) { struct hlist_node *pos, *next; struct nlm_file *file; @@ -241,6 +248,8 @@ nlm_traverse_files(void *data, nlm_host_match_fn_t match) mutex_lock(&nlm_file_mutex); for (i = 0; i < FILE_NRHASH; i++) { hlist_for_each_entry_safe(file, pos, next, &nlm_files[i], f_list) { + if (is_failover_file && !is_failover_file(data, file)) + continue; file->f_count++; mutex_unlock(&nlm_file_mutex); @@ -345,7 +354,7 @@ void nlmsvc_mark_resources(void) { dprintk("lockd: nlmsvc_mark_resources\n"); - nlm_traverse_files(NULL, nlmsvc_mark_host); + nlm_traverse_files(NULL, nlmsvc_mark_host, NULL); } /* @@ -356,7 +365,7 @@ nlmsvc_free_host_resources(struct nlm_host *host) { dprintk("lockd: nlmsvc_free_host_resources\n"); - if (nlm_traverse_files(host, nlmsvc_same_host)) { + if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) { printk(KERN_WARNING "lockd: couldn't remove all locks held by %s\n", host->h_name); @@ -376,8 +385,26 @@ nlmsvc_invalidate_all(void) * turn, which is about as inefficient as it gets. * Now we just do it once in nlm_traverse_files. */ - nlm_traverse_files(NULL, nlmsvc_is_client); + nlm_traverse_files(NULL, nlmsvc_is_client, NULL); +} + +static int +nlmsvc_match_sb(void *datap, struct nlm_file *file) +{ + struct super_block *sb = datap; + + return sb == file->f_file->f_path.mnt->mnt_sb; +} + +int +nlmsvc_unlock_all_by_sb(struct super_block *sb) +{ + int ret; + + ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb); + return ret ? -EIO : 0; } +EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb); static int nlmsvc_match_ip(void *datap, struct nlm_host *host) @@ -391,7 +418,7 @@ int nlmsvc_unlock_all_by_ip(__be32 server_addr) { int ret; - ret = nlm_traverse_files(&server_addr, nlmsvc_match_ip); + ret = nlm_traverse_files(&server_addr, nlmsvc_match_ip, NULL); return ret ? -EIO : 0; } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 7706f6c9ab8..42f3820ee8f 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -56,6 +56,7 @@ enum { NFSD_List, NFSD_Fh, NFSD_FO_UnlockIP, + NFSD_FO_UnlockFS, NFSD_Threads, NFSD_Pool_Threads, NFSD_Versions, @@ -93,6 +94,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); #endif static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size); +static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size); static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Svc] = write_svc, @@ -104,6 +106,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Getfs] = write_getfs, [NFSD_Fh] = write_filehandle, [NFSD_FO_UnlockIP] = failover_unlock_ip, + [NFSD_FO_UnlockFS] = failover_unlock_fs, [NFSD_Threads] = write_threads, [NFSD_Pool_Threads] = write_pool_threads, [NFSD_Versions] = write_versions, @@ -329,6 +332,33 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) return nlmsvc_unlock_all_by_ip(server_ip); } +static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) +{ + struct nameidata nd; + char *fo_path; + int error; + + /* sanity check */ + if (size == 0) + return -EINVAL; + + if (buf[size-1] != '\n') + return -EINVAL; + + fo_path = buf; + if (qword_get(&buf, fo_path, size) < 0) + return -EINVAL; + + error = path_lookup(fo_path, 0, &nd); + if (error) + return error; + + error = nlmsvc_unlock_all_by_sb(nd.path.mnt->mnt_sb); + + path_put(&nd.path); + return error; +} + static ssize_t write_filehandle(struct file *file, char *buf, size_t size) { /* request is: @@ -733,6 +763,8 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, [NFSD_FO_UnlockIP] = {"unlock_ip", &transaction_ops, S_IWUSR|S_IRUSR}, + [NFSD_FO_UnlockFS] = {"unlock_filesystem", + &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, -- cgit v1.2.3 From 1a747ee0cc11a198f9e2435add821bd0dfedb7c1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 24 Apr 2008 10:08:22 -0400 Subject: locks: don't call ->copy_lock methods on return of conflicting locks The file_lock structure is used both as a heavy-weight representation of an active lock, with pointers to reference-counted structures, etc., and as a simple container for parameters that describe a file lock. The conflicting lock returned from __posix_lock_file is an example of the latter; so don't call the filesystem or lock manager callbacks when copying to it. This also saves the need for an unnecessary locks_init_lock in the nfsv4 server. Thanks to Trond for pointing out the error. Signed-off-by: J. Bruce Fields Cc: Trond Myklebust --- fs/lockd/svclock.c | 2 +- fs/locks.c | 4 ++-- fs/nfsd/nfs4state.c | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 1f122c1940a..4d81553d294 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -632,7 +632,7 @@ nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf, block->b_flags |= B_TIMED_OUT; if (conf) { if (block->b_fl) - locks_copy_lock(block->b_fl, conf); + __locks_copy_lock(block->b_fl, conf); } } diff --git a/fs/locks.c b/fs/locks.c index 2e0fa661e42..e1ea2fe0368 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -224,7 +224,7 @@ static void locks_copy_private(struct file_lock *new, struct file_lock *fl) /* * Initialize a new lock from an existing file_lock structure. */ -static void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl) +void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl) { new->fl_owner = fl->fl_owner; new->fl_pid = fl->fl_pid; @@ -833,7 +833,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str if (!posix_locks_conflict(request, fl)) continue; if (conflock) - locks_copy_lock(conflock, fl); + __locks_copy_lock(conflock, fl); error = -EAGAIN; if (!(request->fl_flags & FL_SLEEP)) goto out; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 55dfdd71f1b..8799b870818 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2712,9 +2712,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * Note: locks.c uses the BKL to protect the inode's lock list. */ - /* XXX?: Just to divert the locks_release_private at the start of - * locks_copy_lock: */ - locks_init_lock(&conflock); err = vfs_lock_file(filp, cmd, &file_lock, &conflock); switch (-err) { case 0: /* success! */ -- cgit v1.2.3 From e36cd4a2873c398ba188f16e4087cce7f00a1506 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 24 Apr 2008 16:59:30 -0400 Subject: nfsd: don't allow setting ctime over v4 Presumably this is left over from earlier drafts of v4, which listed TIME_METADATA as writeable. It's read-only in rfc 3530, and shouldn't be modifiable anyway. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 1ba7ad98193..c513bbdf2d3 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -376,20 +376,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia goto xdr_error; } } - if (bmval[1] & FATTR4_WORD1_TIME_METADATA) { - /* We require the high 32 bits of 'seconds' to be 0, and we ignore - all 32 bits of 'nseconds'. */ - READ_BUF(12); - len += 12; - READ32(dummy32); - if (dummy32) - return nfserr_inval; - READ32(iattr->ia_ctime.tv_sec); - READ32(iattr->ia_ctime.tv_nsec); - if (iattr->ia_ctime.tv_nsec >= (u32)1000000000) - return nfserr_inval; - iattr->ia_valid |= ATTR_CTIME; - } if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { READ_BUF(4); len += 4; -- cgit v1.2.3