aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2009-06-07 12:39:32 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2009-06-11 21:36:18 -0400
commite59399d0102c1813cec48db5cebe1750313f88a0 (patch)
treecd4fde6b5c442ede5d00b9640d3a545f847aeb8c
parent6dd4740662405a68bb229ac2b9e0aeaaf2188bf2 (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>
-rw-r--r--fs/nilfs2/super.c44
-rw-r--r--fs/nilfs2/the_nilfs.c13
-rw-r--r--fs/nilfs2/the_nilfs.h7
3 files changed, 33 insertions, 31 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
470static int nilfs_mark_recovery_complete(struct nilfs_sb_info *sbi) 470static 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 /*
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index 221953bfc859..06e8dfd538d6 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -72,6 +72,7 @@ static struct the_nilfs *alloc_nilfs(struct block_device *bdev)
72 atomic_set(&nilfs->ns_writer_refcount, -1); 72 atomic_set(&nilfs->ns_writer_refcount, -1);
73 atomic_set(&nilfs->ns_ndirtyblks, 0); 73 atomic_set(&nilfs->ns_ndirtyblks, 0);
74 init_rwsem(&nilfs->ns_sem); 74 init_rwsem(&nilfs->ns_sem);
75 init_rwsem(&nilfs->ns_super_sem);
75 mutex_init(&nilfs->ns_writer_mutex); 76 mutex_init(&nilfs->ns_writer_mutex);
76 INIT_LIST_HEAD(&nilfs->ns_list); 77 INIT_LIST_HEAD(&nilfs->ns_list);
77 INIT_LIST_HEAD(&nilfs->ns_supers); 78 INIT_LIST_HEAD(&nilfs->ns_supers);
@@ -681,10 +682,10 @@ struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *nilfs,
681{ 682{
682 struct nilfs_sb_info *sbi; 683 struct nilfs_sb_info *sbi;
683 684
684 down_read(&nilfs->ns_sem); 685 down_read(&nilfs->ns_super_sem);
685 /* 686 /*
686 * The SNAPSHOT flag and sb->s_flags are supposed to be 687 * The SNAPSHOT flag and sb->s_flags are supposed to be
687 * protected with nilfs->ns_sem. 688 * protected with nilfs->ns_super_sem.
688 */ 689 */
689 sbi = nilfs->ns_current; 690 sbi = nilfs->ns_current;
690 if (rw_mount) { 691 if (rw_mount) {
@@ -705,12 +706,12 @@ struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *nilfs,
705 goto found; /* snapshot mount */ 706 goto found; /* snapshot mount */
706 } 707 }
707 out: 708 out:
708 up_read(&nilfs->ns_sem); 709 up_read(&nilfs->ns_super_sem);
709 return NULL; 710 return NULL;
710 711
711 found: 712 found:
712 atomic_inc(&sbi->s_count); 713 atomic_inc(&sbi->s_count);
713 up_read(&nilfs->ns_sem); 714 up_read(&nilfs->ns_super_sem);
714 return sbi; 715 return sbi;
715} 716}
716 717
@@ -720,7 +721,7 @@ int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno,
720 struct nilfs_sb_info *sbi; 721 struct nilfs_sb_info *sbi;
721 int ret = 0; 722 int ret = 0;
722 723
723 down_read(&nilfs->ns_sem); 724 down_read(&nilfs->ns_super_sem);
724 if (cno == 0 || cno > nilfs->ns_cno) 725 if (cno == 0 || cno > nilfs->ns_cno)
725 goto out_unlock; 726 goto out_unlock;
726 727
@@ -737,6 +738,6 @@ int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno,
737 ret++; 738 ret++;
738 739
739 out_unlock: 740 out_unlock:
740 up_read(&nilfs->ns_sem); 741 up_read(&nilfs->ns_super_sem);
741 return ret; 742 return ret;
742} 743}
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index be4c040fd629..d0cf4fb7c9ce 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -48,6 +48,7 @@ enum {
48 * @ns_bdi: backing dev info 48 * @ns_bdi: backing dev info
49 * @ns_writer: back pointer to writable nilfs_sb_info 49 * @ns_writer: back pointer to writable nilfs_sb_info
50 * @ns_sem: semaphore for shared states 50 * @ns_sem: semaphore for shared states
51 * @ns_super_sem: semaphore for global operations across super block instances
51 * @ns_writer_mutex: mutex protecting ns_writer attach/detach 52 * @ns_writer_mutex: mutex protecting ns_writer attach/detach
52 * @ns_writer_refcount: number of referrers on ns_writer 53 * @ns_writer_refcount: number of referrers on ns_writer
53 * @ns_current: back pointer to current mount 54 * @ns_current: back pointer to current mount
@@ -96,10 +97,15 @@ struct the_nilfs {
96 struct backing_dev_info *ns_bdi; 97 struct backing_dev_info *ns_bdi;
97 struct nilfs_sb_info *ns_writer; 98 struct nilfs_sb_info *ns_writer;
98 struct rw_semaphore ns_sem; 99 struct rw_semaphore ns_sem;
100 struct rw_semaphore ns_super_sem;
99 struct mutex ns_writer_mutex; 101 struct mutex ns_writer_mutex;
100 atomic_t ns_writer_refcount; 102 atomic_t ns_writer_refcount;
101 103
104 /*
105 * components protected by ns_super_sem
106 */
102 struct nilfs_sb_info *ns_current; 107 struct nilfs_sb_info *ns_current;
108 struct list_head ns_supers;
103 109
104 /* 110 /*
105 * used for 111 * used for
@@ -113,7 +119,6 @@ struct the_nilfs {
113 time_t ns_sbwtime[2]; 119 time_t ns_sbwtime[2];
114 unsigned ns_sbsize; 120 unsigned ns_sbsize;
115 unsigned ns_mount_state; 121 unsigned ns_mount_state;
116 struct list_head ns_supers;
117 122
118 /* 123 /*
119 * Following fields are dedicated to a writable FS-instance. 124 * Following fields are dedicated to a writable FS-instance.