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 | |
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')
-rw-r--r-- | fs/nilfs2/super.c | 44 | ||||
-rw-r--r-- | fs/nilfs2/the_nilfs.c | 13 | ||||
-rw-r--r-- | fs/nilfs2/the_nilfs.h | 7 |
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 | ||
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 | /* |
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. |