summaryrefslogtreecommitdiffstats
path: root/fs/nilfs2/super.c
diff options
context:
space:
mode:
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2009-06-08 01:39:32 +0900
committerAl Viro <viro@zeniv.linux.org.uk>2009-06-11 21:36:18 -0400
commite59399d0102c1813cec48db5cebe1750313f88a0 (patch)
treecd4fde6b5c442ede5d00b9640d3a545f847aeb8c /fs/nilfs2/super.c
parent6dd4740662405a68bb229ac2b9e0aeaaf2188bf2 (diff)
downloadlinux-stable-e59399d0102c1813cec48db5cebe1750313f88a0.tar.gz
linux-stable-e59399d0102c1813cec48db5cebe1750313f88a0.tar.bz2
linux-stable-e59399d0102c1813cec48db5cebe1750313f88a0.zip
nilfs2: correct exclusion control in nilfs_remount function
nilfs_remount() changes mount state of a superblock instance. Even though nilfs accesses other superblock instances during mount or remount, the mount state was not properly protected in nilfs_remount(). Moreover, nilfs_remount() has a lock order reversal problem; nilfs_get_sb() holds: 1. bdev->bd_mount_sem 2. sb->s_umount (sget acquires) and nilfs_remount() holds: 1. sb->s_umount (locked by the caller in vfs) 2. bdev->bd_mount_sem To avoid these problems, this patch divides a semaphore protecting super block instances from nilfs->ns_sem, and applies it to the mount state protection in nilfs_remount(). With this change, bd_mount_sem use is removed from nilfs_remount() and the lock order reversal will be resolved. And the new rw-semaphore, nilfs->ns_super_sem will properly protect the mount state except the modification from nilfs_error function. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/nilfs2/super.c')
-rw-r--r--fs/nilfs2/super.c44
1 files changed, 20 insertions, 24 deletions
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 1d1b6e125159..f02762fa8ea0 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -327,10 +327,10 @@ static void nilfs_put_super(struct super_block *sb)
nilfs_commit_super(sbi, 1);
up_write(&nilfs->ns_sem);
}
- down_write(&nilfs->ns_sem);
+ down_write(&nilfs->ns_super_sem);
if (nilfs->ns_current == sbi)
nilfs->ns_current = NULL;
- up_write(&nilfs->ns_sem);
+ up_write(&nilfs->ns_super_sem);
nilfs_detach_checkpoint(sbi);
put_nilfs(sbi->s_nilfs);
@@ -408,9 +408,9 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
struct buffer_head *bh_cp;
int err;
- down_write(&nilfs->ns_sem);
+ down_write(&nilfs->ns_super_sem);
list_add(&sbi->s_list, &nilfs->ns_supers);
- up_write(&nilfs->ns_sem);
+ up_write(&nilfs->ns_super_sem);
sbi->s_ifile = nilfs_mdt_new(
nilfs, sbi->s_super, NILFS_IFILE_INO, NILFS_IFILE_GFP);
@@ -448,9 +448,9 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
nilfs_mdt_destroy(sbi->s_ifile);
sbi->s_ifile = NULL;
- down_write(&nilfs->ns_sem);
+ down_write(&nilfs->ns_super_sem);
list_del_init(&sbi->s_list);
- up_write(&nilfs->ns_sem);
+ up_write(&nilfs->ns_super_sem);
return err;
}
@@ -462,9 +462,9 @@ void nilfs_detach_checkpoint(struct nilfs_sb_info *sbi)
nilfs_mdt_clear(sbi->s_ifile);
nilfs_mdt_destroy(sbi->s_ifile);
sbi->s_ifile = NULL;
- down_write(&nilfs->ns_sem);
+ down_write(&nilfs->ns_super_sem);
list_del_init(&sbi->s_list);
- up_write(&nilfs->ns_sem);
+ up_write(&nilfs->ns_super_sem);
}
static int nilfs_mark_recovery_complete(struct nilfs_sb_info *sbi)
@@ -883,10 +883,10 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
goto failed_root;
}
- down_write(&nilfs->ns_sem);
+ down_write(&nilfs->ns_super_sem);
if (!nilfs_test_opt(sbi, SNAPSHOT))
nilfs->ns_current = sbi;
- up_write(&nilfs->ns_sem);
+ up_write(&nilfs->ns_super_sem);
return 0;
@@ -918,6 +918,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
lock_kernel();
+ down_write(&nilfs->ns_super_sem);
old_sb_flags = sb->s_flags;
old_opts.mount_opt = sbi->s_mount_opt;
old_opts.snapshot_cno = sbi->s_snapshot_cno;
@@ -965,24 +966,20 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
* store the current valid flag. (It may have been changed
* by fsck since we originally mounted the partition.)
*/
- down(&sb->s_bdev->bd_mount_sem);
- down_read(&nilfs->ns_sem);
if (nilfs->ns_current && nilfs->ns_current != sbi) {
printk(KERN_WARNING "NILFS (device %s): couldn't "
"remount because an RW-mount exists.\n",
sb->s_id);
- up_read(&nilfs->ns_sem);
err = -EBUSY;
- goto rw_remount_failed;
+ goto restore_opts;
}
- up_read(&nilfs->ns_sem);
if (sbi->s_snapshot_cno != nilfs_last_cno(nilfs)) {
printk(KERN_WARNING "NILFS (device %s): couldn't "
"remount because the current RO-mount is not "
"the latest one.\n",
sb->s_id);
err = -EINVAL;
- goto rw_remount_failed;
+ goto restore_opts;
}
sb->s_flags &= ~MS_RDONLY;
nilfs_clear_opt(sbi, SNAPSHOT);
@@ -990,25 +987,24 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
err = nilfs_attach_segment_constructor(sbi);
if (err)
- goto rw_remount_failed;
+ goto restore_opts;
down_write(&nilfs->ns_sem);
nilfs_setup_super(sbi);
- nilfs->ns_current = sbi;
up_write(&nilfs->ns_sem);
- up(&sb->s_bdev->bd_mount_sem);
+ nilfs->ns_current = sbi;
}
out:
+ up_write(&nilfs->ns_super_sem);
unlock_kernel();
return 0;
- rw_remount_failed:
- up(&sb->s_bdev->bd_mount_sem);
restore_opts:
sb->s_flags = old_sb_flags;
sbi->s_mount_opt = old_opts.mount_opt;
sbi->s_snapshot_cno = old_opts.snapshot_cno;
+ up_write(&nilfs->ns_super_sem);
unlock_kernel();
return err;
}
@@ -1118,15 +1114,15 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
* (i.e. rw-mount or ro-mount), whereas rw-mount and
* ro-mount are mutually exclusive.
*/
- down_read(&nilfs->ns_sem);
+ down_read(&nilfs->ns_super_sem);
if (nilfs->ns_current &&
((nilfs->ns_current->s_super->s_flags ^ flags)
& MS_RDONLY)) {
- up_read(&nilfs->ns_sem);
+ up_read(&nilfs->ns_super_sem);
err = -EBUSY;
goto failed_unlock;
}
- up_read(&nilfs->ns_sem);
+ up_read(&nilfs->ns_super_sem);
}
/*