diff options
author | Miklos Szeredi | 2006-10-17 09:10:06 +0200 |
---|---|---|
committer | Linus Torvalds | 2006-10-17 17:18:45 +0200 |
commit | 9ffbb9162312fd8113037cb3d94f787f06bbfa9a (patch) | |
tree | a8194bb542dcda56271b44d7de23f2a72ecac900 /fs/fuse/inode.c | |
parent | [PATCH] sx: fix user-visible typo (devic) (diff) | |
download | kernel-qcow2-linux-9ffbb9162312fd8113037cb3d94f787f06bbfa9a.tar.gz kernel-qcow2-linux-9ffbb9162312fd8113037cb3d94f787f06bbfa9a.tar.xz kernel-qcow2-linux-9ffbb9162312fd8113037cb3d94f787f06bbfa9a.zip |
[PATCH] fuse: fix hang on SMP
Fuse didn't always call i_size_write() with i_mutex held which caused rare
hangs on SMP/32bit. This bug has been present since fuse-2.2, well before
being merged into mainline.
The simplest solution is to protect i_size_write() with the per-connection
spinlock. Using i_mutex for this purpose would require some restructuring of
the code and I'm not even sure it's always safe to acquire i_mutex in all
places i_size needs to be set.
Since most of vmtruncate is already duplicated for other reasons, duplicate
the remaining part as well, making all i_size_write() calls internal to fuse.
Using i_size_write() was unnecessary in fuse_init_inode(), since this function
is only called on a newly created locked inode.
Reported by a few people over the years, but special thanks to Dana Henriksen
who was persistent enough in helping me debug it.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/fuse/inode.c')
-rw-r--r-- | fs/fuse/inode.c | 5 |
1 files changed, 4 insertions, 1 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 7d0a9aee01f2..8e106163aaed 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -109,6 +109,7 @@ static int fuse_remount_fs(struct super_block *sb, int *flags, char *data) void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) { + struct fuse_conn *fc = get_fuse_conn(inode); if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) invalidate_inode_pages(inode->i_mapping); @@ -117,7 +118,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) inode->i_nlink = attr->nlink; inode->i_uid = attr->uid; inode->i_gid = attr->gid; + spin_lock(&fc->lock); i_size_write(inode, attr->size); + spin_unlock(&fc->lock); inode->i_blocks = attr->blocks; inode->i_atime.tv_sec = attr->atime; inode->i_atime.tv_nsec = attr->atimensec; @@ -130,7 +133,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) { inode->i_mode = attr->mode & S_IFMT; - i_size_write(inode, attr->size); + inode->i_size = attr->size; if (S_ISREG(inode->i_mode)) { fuse_init_common(inode); fuse_init_file_inode(inode); |