aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nilfs2
diff options
context:
space:
mode:
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2012-07-30 17:42:07 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-30 20:25:19 -0400
commit572d8b3945a31bee7c40d21556803e4807fd9141 (patch)
tree8988dde859a68d88280047f5214427b49ebcf5de /fs/nilfs2
parentfe0627e7b3d32a41c16fac6e0af091991545865e (diff)
nilfs2: fix deadlock issue between chcp and thaw ioctls
An fs-thaw ioctl causes deadlock with a chcp or mkcp -s command: chcp D ffff88013870f3d0 0 1325 1324 0x00000004 ... Call Trace: nilfs_transaction_begin+0x11c/0x1a0 [nilfs2] wake_up_bit+0x20/0x20 copy_from_user+0x18/0x30 [nilfs2] nilfs_ioctl_change_cpmode+0x7d/0xcf [nilfs2] nilfs_ioctl+0x252/0x61a [nilfs2] do_page_fault+0x311/0x34c get_unmapped_area+0x132/0x14e do_vfs_ioctl+0x44b/0x490 __set_task_blocked+0x5a/0x61 vm_mmap_pgoff+0x76/0x87 __set_current_blocked+0x30/0x4a sys_ioctl+0x4b/0x6f system_call_fastpath+0x16/0x1b thaw D ffff88013870d890 0 1352 1351 0x00000004 ... Call Trace: rwsem_down_failed_common+0xdb/0x10f call_rwsem_down_write_failed+0x13/0x20 down_write+0x25/0x27 thaw_super+0x13/0x9e do_vfs_ioctl+0x1f5/0x490 vm_mmap_pgoff+0x76/0x87 sys_ioctl+0x4b/0x6f filp_close+0x64/0x6c system_call_fastpath+0x16/0x1b where the thaw ioctl deadlocked at thaw_super() when called while chcp was waiting at nilfs_transaction_begin() called from nilfs_ioctl_change_cpmode(). This deadlock is 100% reproducible. This is because nilfs_ioctl_change_cpmode() first locks sb->s_umount in read mode and then waits for unfreezing in nilfs_transaction_begin(), whereas thaw_super() locks sb->s_umount in write mode. The locking of sb->s_umount here was intended to make snapshot mounts and the downgrade of snapshots to checkpoints exclusive. This fixes the deadlock issue by replacing the sb->s_umount usage in nilfs_ioctl_change_cpmode() with a dedicated mutex which protects snapshot mounts. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Cc: Fernando Luis Vazquez Cao <fernando@oss.ntt.co.jp> Tested-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/nilfs2')
-rw-r--r--fs/nilfs2/ioctl.c4
-rw-r--r--fs/nilfs2/super.c3
-rw-r--r--fs/nilfs2/the_nilfs.c1
-rw-r--r--fs/nilfs2/the_nilfs.h2
4 files changed, 8 insertions, 2 deletions
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 06658caa18bd..0b6387c67e6c 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -182,7 +182,7 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
182 if (copy_from_user(&cpmode, argp, sizeof(cpmode))) 182 if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
183 goto out; 183 goto out;
184 184
185 down_read(&inode->i_sb->s_umount); 185 mutex_lock(&nilfs->ns_snapshot_mount_mutex);
186 186
187 nilfs_transaction_begin(inode->i_sb, &ti, 0); 187 nilfs_transaction_begin(inode->i_sb, &ti, 0);
188 ret = nilfs_cpfile_change_cpmode( 188 ret = nilfs_cpfile_change_cpmode(
@@ -192,7 +192,7 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
192 else 192 else
193 nilfs_transaction_commit(inode->i_sb); /* never fails */ 193 nilfs_transaction_commit(inode->i_sb); /* never fails */
194 194
195 up_read(&inode->i_sb->s_umount); 195 mutex_unlock(&nilfs->ns_snapshot_mount_mutex);
196out: 196out:
197 mnt_drop_write_file(filp); 197 mnt_drop_write_file(filp);
198 return ret; 198 return ret;
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index a76d6ea51ffb..6522cac6057c 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -945,6 +945,8 @@ static int nilfs_attach_snapshot(struct super_block *s, __u64 cno,
945 struct nilfs_root *root; 945 struct nilfs_root *root;
946 int ret; 946 int ret;
947 947
948 mutex_lock(&nilfs->ns_snapshot_mount_mutex);
949
948 down_read(&nilfs->ns_segctor_sem); 950 down_read(&nilfs->ns_segctor_sem);
949 ret = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile, cno); 951 ret = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile, cno);
950 up_read(&nilfs->ns_segctor_sem); 952 up_read(&nilfs->ns_segctor_sem);
@@ -969,6 +971,7 @@ static int nilfs_attach_snapshot(struct super_block *s, __u64 cno,
969 ret = nilfs_get_root_dentry(s, root, root_dentry); 971 ret = nilfs_get_root_dentry(s, root, root_dentry);
970 nilfs_put_root(root); 972 nilfs_put_root(root);
971 out: 973 out:
974 mutex_unlock(&nilfs->ns_snapshot_mount_mutex);
972 return ret; 975 return ret;
973} 976}
974 977
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index 501b7f8b739f..41e6a04a561f 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -76,6 +76,7 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev)
76 nilfs->ns_bdev = bdev; 76 nilfs->ns_bdev = bdev;
77 atomic_set(&nilfs->ns_ndirtyblks, 0); 77 atomic_set(&nilfs->ns_ndirtyblks, 0);
78 init_rwsem(&nilfs->ns_sem); 78 init_rwsem(&nilfs->ns_sem);
79 mutex_init(&nilfs->ns_snapshot_mount_mutex);
79 INIT_LIST_HEAD(&nilfs->ns_dirty_files); 80 INIT_LIST_HEAD(&nilfs->ns_dirty_files);
80 INIT_LIST_HEAD(&nilfs->ns_gc_inodes); 81 INIT_LIST_HEAD(&nilfs->ns_gc_inodes);
81 spin_lock_init(&nilfs->ns_inode_lock); 82 spin_lock_init(&nilfs->ns_inode_lock);
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index ef40a510e2f3..2558f320b821 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -47,6 +47,7 @@ enum {
47 * @ns_flags: flags 47 * @ns_flags: flags
48 * @ns_bdev: block device 48 * @ns_bdev: block device
49 * @ns_sem: semaphore for shared states 49 * @ns_sem: semaphore for shared states
50 * @ns_snapshot_mount_mutex: mutex to protect snapshot mounts
50 * @ns_sbh: buffer heads of on-disk super blocks 51 * @ns_sbh: buffer heads of on-disk super blocks
51 * @ns_sbp: pointers to super block data 52 * @ns_sbp: pointers to super block data
52 * @ns_sbwtime: previous write time of super block 53 * @ns_sbwtime: previous write time of super block
@@ -100,6 +101,7 @@ struct the_nilfs {
100 101
101 struct block_device *ns_bdev; 102 struct block_device *ns_bdev;
102 struct rw_semaphore ns_sem; 103 struct rw_semaphore ns_sem;
104 struct mutex ns_snapshot_mount_mutex;
103 105
104 /* 106 /*
105 * used for 107 * used for