diff options
author | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2009-06-07 12:39:32 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-06-11 21:36:18 -0400 |
commit | e59399d0102c1813cec48db5cebe1750313f88a0 (patch) | |
tree | cd4fde6b5c442ede5d00b9640d3a545f847aeb8c /fs/nilfs2/super.c | |
parent | 6dd4740662405a68bb229ac2b9e0aeaaf2188bf2 (diff) |
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.c | 44 |
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) | |||
327 | nilfs_commit_super(sbi, 1); | 327 | nilfs_commit_super(sbi, 1); |
328 | up_write(&nilfs->ns_sem); | 328 | up_write(&nilfs->ns_sem); |
329 | } | 329 | } |
330 | down_write(&nilfs->ns_sem); | 330 | down_write(&nilfs->ns_super_sem); |
331 | if (nilfs->ns_current == sbi) | 331 | if (nilfs->ns_current == sbi) |
332 | nilfs->ns_current = NULL; | 332 | nilfs->ns_current = NULL; |
333 | up_write(&nilfs->ns_sem); | 333 | up_write(&nilfs->ns_super_sem); |
334 | 334 | ||
335 | nilfs_detach_checkpoint(sbi); | 335 | nilfs_detach_checkpoint(sbi); |
336 | put_nilfs(sbi->s_nilfs); | 336 | put_nilfs(sbi->s_nilfs); |
@@ -408,9 +408,9 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno) | |||
408 | struct buffer_head *bh_cp; | 408 | struct buffer_head *bh_cp; |
409 | int err; | 409 | int err; |
410 | 410 | ||
411 | down_write(&nilfs->ns_sem); | 411 | down_write(&nilfs->ns_super_sem); |
412 | list_add(&sbi->s_list, &nilfs->ns_supers); | 412 | list_add(&sbi->s_list, &nilfs->ns_supers); |
413 | up_write(&nilfs->ns_sem); | 413 | up_write(&nilfs->ns_super_sem); |
414 | 414 | ||
415 | sbi->s_ifile = nilfs_mdt_new( | 415 | sbi->s_ifile = nilfs_mdt_new( |
416 | nilfs, sbi->s_super, NILFS_IFILE_INO, NILFS_IFILE_GFP); | 416 | 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) | |||
448 | nilfs_mdt_destroy(sbi->s_ifile); | 448 | nilfs_mdt_destroy(sbi->s_ifile); |
449 | sbi->s_ifile = NULL; | 449 | sbi->s_ifile = NULL; |
450 | 450 | ||
451 | down_write(&nilfs->ns_sem); | 451 | down_write(&nilfs->ns_super_sem); |
452 | list_del_init(&sbi->s_list); | 452 | list_del_init(&sbi->s_list); |
453 | up_write(&nilfs->ns_sem); | 453 | up_write(&nilfs->ns_super_sem); |
454 | 454 | ||
455 | return err; | 455 | return err; |
456 | } | 456 | } |
@@ -462,9 +462,9 @@ void nilfs_detach_checkpoint(struct nilfs_sb_info *sbi) | |||
462 | nilfs_mdt_clear(sbi->s_ifile); | 462 | nilfs_mdt_clear(sbi->s_ifile); |
463 | nilfs_mdt_destroy(sbi->s_ifile); | 463 | nilfs_mdt_destroy(sbi->s_ifile); |
464 | sbi->s_ifile = NULL; | 464 | sbi->s_ifile = NULL; |
465 | down_write(&nilfs->ns_sem); | 465 | down_write(&nilfs->ns_super_sem); |
466 | list_del_init(&sbi->s_list); | 466 | list_del_init(&sbi->s_list); |
467 | up_write(&nilfs->ns_sem); | 467 | up_write(&nilfs->ns_super_sem); |
468 | } | 468 | } |
469 | 469 | ||
470 | static int nilfs_mark_recovery_complete(struct nilfs_sb_info *sbi) | 470 | 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, | |||
883 | goto failed_root; | 883 | goto failed_root; |
884 | } | 884 | } |
885 | 885 | ||
886 | down_write(&nilfs->ns_sem); | 886 | down_write(&nilfs->ns_super_sem); |
887 | if (!nilfs_test_opt(sbi, SNAPSHOT)) | 887 | if (!nilfs_test_opt(sbi, SNAPSHOT)) |
888 | nilfs->ns_current = sbi; | 888 | nilfs->ns_current = sbi; |
889 | up_write(&nilfs->ns_sem); | 889 | up_write(&nilfs->ns_super_sem); |
890 | 890 | ||
891 | return 0; | 891 | return 0; |
892 | 892 | ||
@@ -918,6 +918,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) | |||
918 | 918 | ||
919 | lock_kernel(); | 919 | lock_kernel(); |
920 | 920 | ||
921 | down_write(&nilfs->ns_super_sem); | ||
921 | old_sb_flags = sb->s_flags; | 922 | old_sb_flags = sb->s_flags; |
922 | old_opts.mount_opt = sbi->s_mount_opt; | 923 | old_opts.mount_opt = sbi->s_mount_opt; |
923 | old_opts.snapshot_cno = sbi->s_snapshot_cno; | 924 | old_opts.snapshot_cno = sbi->s_snapshot_cno; |
@@ -965,24 +966,20 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) | |||
965 | * store the current valid flag. (It may have been changed | 966 | * store the current valid flag. (It may have been changed |
966 | * by fsck since we originally mounted the partition.) | 967 | * by fsck since we originally mounted the partition.) |
967 | */ | 968 | */ |
968 | down(&sb->s_bdev->bd_mount_sem); | ||
969 | down_read(&nilfs->ns_sem); | ||
970 | if (nilfs->ns_current && nilfs->ns_current != sbi) { | 969 | if (nilfs->ns_current && nilfs->ns_current != sbi) { |
971 | printk(KERN_WARNING "NILFS (device %s): couldn't " | 970 | printk(KERN_WARNING "NILFS (device %s): couldn't " |
972 | "remount because an RW-mount exists.\n", | 971 | "remount because an RW-mount exists.\n", |
973 | sb->s_id); | 972 | sb->s_id); |
974 | up_read(&nilfs->ns_sem); | ||
975 | err = -EBUSY; | 973 | err = -EBUSY; |
976 | goto rw_remount_failed; | 974 | goto restore_opts; |
977 | } | 975 | } |
978 | up_read(&nilfs->ns_sem); | ||
979 | if (sbi->s_snapshot_cno != nilfs_last_cno(nilfs)) { | 976 | if (sbi->s_snapshot_cno != nilfs_last_cno(nilfs)) { |
980 | printk(KERN_WARNING "NILFS (device %s): couldn't " | 977 | printk(KERN_WARNING "NILFS (device %s): couldn't " |
981 | "remount because the current RO-mount is not " | 978 | "remount because the current RO-mount is not " |
982 | "the latest one.\n", | 979 | "the latest one.\n", |
983 | sb->s_id); | 980 | sb->s_id); |
984 | err = -EINVAL; | 981 | err = -EINVAL; |
985 | goto rw_remount_failed; | 982 | goto restore_opts; |
986 | } | 983 | } |
987 | sb->s_flags &= ~MS_RDONLY; | 984 | sb->s_flags &= ~MS_RDONLY; |
988 | nilfs_clear_opt(sbi, SNAPSHOT); | 985 | nilfs_clear_opt(sbi, SNAPSHOT); |
@@ -990,25 +987,24 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) | |||
990 | 987 | ||
991 | err = nilfs_attach_segment_constructor(sbi); | 988 | err = nilfs_attach_segment_constructor(sbi); |
992 | if (err) | 989 | if (err) |
993 | goto rw_remount_failed; | 990 | goto restore_opts; |
994 | 991 | ||
995 | down_write(&nilfs->ns_sem); | 992 | down_write(&nilfs->ns_sem); |
996 | nilfs_setup_super(sbi); | 993 | nilfs_setup_super(sbi); |
997 | nilfs->ns_current = sbi; | ||
998 | up_write(&nilfs->ns_sem); | 994 | up_write(&nilfs->ns_sem); |
999 | 995 | ||
1000 | up(&sb->s_bdev->bd_mount_sem); | 996 | nilfs->ns_current = sbi; |
1001 | } | 997 | } |
1002 | out: | 998 | out: |
999 | up_write(&nilfs->ns_super_sem); | ||
1003 | unlock_kernel(); | 1000 | unlock_kernel(); |
1004 | return 0; | 1001 | return 0; |
1005 | 1002 | ||
1006 | rw_remount_failed: | ||
1007 | up(&sb->s_bdev->bd_mount_sem); | ||
1008 | restore_opts: | 1003 | restore_opts: |
1009 | sb->s_flags = old_sb_flags; | 1004 | sb->s_flags = old_sb_flags; |
1010 | sbi->s_mount_opt = old_opts.mount_opt; | 1005 | sbi->s_mount_opt = old_opts.mount_opt; |
1011 | sbi->s_snapshot_cno = old_opts.snapshot_cno; | 1006 | sbi->s_snapshot_cno = old_opts.snapshot_cno; |
1007 | up_write(&nilfs->ns_super_sem); | ||
1012 | unlock_kernel(); | 1008 | unlock_kernel(); |
1013 | return err; | 1009 | return err; |
1014 | } | 1010 | } |
@@ -1118,15 +1114,15 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, | |||
1118 | * (i.e. rw-mount or ro-mount), whereas rw-mount and | 1114 | * (i.e. rw-mount or ro-mount), whereas rw-mount and |
1119 | * ro-mount are mutually exclusive. | 1115 | * ro-mount are mutually exclusive. |
1120 | */ | 1116 | */ |
1121 | down_read(&nilfs->ns_sem); | 1117 | down_read(&nilfs->ns_super_sem); |
1122 | if (nilfs->ns_current && | 1118 | if (nilfs->ns_current && |
1123 | ((nilfs->ns_current->s_super->s_flags ^ flags) | 1119 | ((nilfs->ns_current->s_super->s_flags ^ flags) |
1124 | & MS_RDONLY)) { | 1120 | & MS_RDONLY)) { |
1125 | up_read(&nilfs->ns_sem); | 1121 | up_read(&nilfs->ns_super_sem); |
1126 | err = -EBUSY; | 1122 | err = -EBUSY; |
1127 | goto failed_unlock; | 1123 | goto failed_unlock; |
1128 | } | 1124 | } |
1129 | up_read(&nilfs->ns_sem); | 1125 | up_read(&nilfs->ns_super_sem); |
1130 | } | 1126 | } |
1131 | 1127 | ||
1132 | /* | 1128 | /* |