diff options
author | Theodore Ts'o <tytso@mit.edu> | 2009-04-25 22:53:39 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2009-04-25 22:53:39 -0400 |
commit | 32ed5058ce90024efcd811254b4b1de0468099df (patch) | |
tree | 0054cc5c385b4ba02abd95a761ccb01b453ba05f | |
parent | 3b9d4ed26680771295d904a6b83e88e620780893 (diff) |
ext4: Replace lock/unlock_super() with an explicit lock for resizing
Use a separate lock to protect s_groups_count and the other block
group descriptors which get changed via an on-line resize operation,
so we can stop overloading the use of lock_super().
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r-- | fs/ext4/ext4_sb.h | 1 | ||||
-rw-r--r-- | fs/ext4/resize.c | 35 | ||||
-rw-r--r-- | fs/ext4/super.c | 1 |
3 files changed, 20 insertions, 17 deletions
diff --git a/fs/ext4/ext4_sb.h b/fs/ext4/ext4_sb.h index 4bda2f75d426..2d36223d5f57 100644 --- a/fs/ext4/ext4_sb.h +++ b/fs/ext4/ext4_sb.h | |||
@@ -72,6 +72,7 @@ struct ext4_sb_info { | |||
72 | struct journal_s *s_journal; | 72 | struct journal_s *s_journal; |
73 | struct list_head s_orphan; | 73 | struct list_head s_orphan; |
74 | struct mutex s_orphan_lock; | 74 | struct mutex s_orphan_lock; |
75 | struct mutex s_resize_lock; | ||
75 | unsigned long s_commit_interval; | 76 | unsigned long s_commit_interval; |
76 | u32 s_max_batch_time; | 77 | u32 s_max_batch_time; |
77 | u32 s_min_batch_time; | 78 | u32 s_min_batch_time; |
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 546c7dd869e1..e8ded13b5cb1 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c | |||
@@ -193,7 +193,7 @@ static int setup_new_group_blocks(struct super_block *sb, | |||
193 | if (IS_ERR(handle)) | 193 | if (IS_ERR(handle)) |
194 | return PTR_ERR(handle); | 194 | return PTR_ERR(handle); |
195 | 195 | ||
196 | lock_super(sb); | 196 | mutex_lock(&sbi->s_resize_lock); |
197 | if (input->group != sbi->s_groups_count) { | 197 | if (input->group != sbi->s_groups_count) { |
198 | err = -EBUSY; | 198 | err = -EBUSY; |
199 | goto exit_journal; | 199 | goto exit_journal; |
@@ -302,7 +302,7 @@ exit_bh: | |||
302 | brelse(bh); | 302 | brelse(bh); |
303 | 303 | ||
304 | exit_journal: | 304 | exit_journal: |
305 | unlock_super(sb); | 305 | mutex_unlock(&sbi->s_resize_lock); |
306 | if ((err2 = ext4_journal_stop(handle)) && !err) | 306 | if ((err2 = ext4_journal_stop(handle)) && !err) |
307 | err = err2; | 307 | err = err2; |
308 | 308 | ||
@@ -643,11 +643,12 @@ exit_free: | |||
643 | * important part is that the new block and inode counts are in the backup | 643 | * important part is that the new block and inode counts are in the backup |
644 | * superblocks, and the location of the new group metadata in the GDT backups. | 644 | * superblocks, and the location of the new group metadata in the GDT backups. |
645 | * | 645 | * |
646 | * We do not need lock_super() for this, because these blocks are not | 646 | * We do not need take the s_resize_lock for this, because these |
647 | * otherwise touched by the filesystem code when it is mounted. We don't | 647 | * blocks are not otherwise touched by the filesystem code when it is |
648 | * need to worry about last changing from sbi->s_groups_count, because the | 648 | * mounted. We don't need to worry about last changing from |
649 | * worst that can happen is that we do not copy the full number of backups | 649 | * sbi->s_groups_count, because the worst that can happen is that we |
650 | * at this time. The resize which changed s_groups_count will backup again. | 650 | * do not copy the full number of backups at this time. The resize |
651 | * which changed s_groups_count will backup again. | ||
651 | */ | 652 | */ |
652 | static void update_backups(struct super_block *sb, | 653 | static void update_backups(struct super_block *sb, |
653 | int blk_off, char *data, int size) | 654 | int blk_off, char *data, int size) |
@@ -809,7 +810,7 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) | |||
809 | goto exit_put; | 810 | goto exit_put; |
810 | } | 811 | } |
811 | 812 | ||
812 | lock_super(sb); | 813 | mutex_lock(&sbi->s_resize_lock); |
813 | if (input->group != sbi->s_groups_count) { | 814 | if (input->group != sbi->s_groups_count) { |
814 | ext4_warning(sb, __func__, | 815 | ext4_warning(sb, __func__, |
815 | "multiple resizers run on filesystem!"); | 816 | "multiple resizers run on filesystem!"); |
@@ -840,7 +841,7 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) | |||
840 | /* | 841 | /* |
841 | * OK, now we've set up the new group. Time to make it active. | 842 | * OK, now we've set up the new group. Time to make it active. |
842 | * | 843 | * |
843 | * Current kernels don't lock all allocations via lock_super(), | 844 | * We do not lock all allocations via s_resize_lock |
844 | * so we have to be safe wrt. concurrent accesses the group | 845 | * so we have to be safe wrt. concurrent accesses the group |
845 | * data. So we need to be careful to set all of the relevant | 846 | * data. So we need to be careful to set all of the relevant |
846 | * group descriptor data etc. *before* we enable the group. | 847 | * group descriptor data etc. *before* we enable the group. |
@@ -900,12 +901,12 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) | |||
900 | * | 901 | * |
901 | * The precise rules we use are: | 902 | * The precise rules we use are: |
902 | * | 903 | * |
903 | * * Writers of s_groups_count *must* hold lock_super | 904 | * * Writers of s_groups_count *must* hold s_resize_lock |
904 | * AND | 905 | * AND |
905 | * * Writers must perform a smp_wmb() after updating all dependent | 906 | * * Writers must perform a smp_wmb() after updating all dependent |
906 | * data and before modifying the groups count | 907 | * data and before modifying the groups count |
907 | * | 908 | * |
908 | * * Readers must hold lock_super() over the access | 909 | * * Readers must hold s_resize_lock over the access |
909 | * OR | 910 | * OR |
910 | * * Readers must perform an smp_rmb() after reading the groups count | 911 | * * Readers must perform an smp_rmb() after reading the groups count |
911 | * and before reading any dependent data. | 912 | * and before reading any dependent data. |
@@ -948,7 +949,7 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) | |||
948 | sb->s_dirt = 1; | 949 | sb->s_dirt = 1; |
949 | 950 | ||
950 | exit_journal: | 951 | exit_journal: |
951 | unlock_super(sb); | 952 | mutex_unlock(&sbi->s_resize_lock); |
952 | if ((err2 = ext4_journal_stop(handle)) && !err) | 953 | if ((err2 = ext4_journal_stop(handle)) && !err) |
953 | err = err2; | 954 | err = err2; |
954 | if (!err) { | 955 | if (!err) { |
@@ -986,7 +987,7 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, | |||
986 | 987 | ||
987 | /* We don't need to worry about locking wrt other resizers just | 988 | /* We don't need to worry about locking wrt other resizers just |
988 | * yet: we're going to revalidate es->s_blocks_count after | 989 | * yet: we're going to revalidate es->s_blocks_count after |
989 | * taking lock_super() below. */ | 990 | * taking the s_resize_lock below. */ |
990 | o_blocks_count = ext4_blocks_count(es); | 991 | o_blocks_count = ext4_blocks_count(es); |
991 | o_groups_count = EXT4_SB(sb)->s_groups_count; | 992 | o_groups_count = EXT4_SB(sb)->s_groups_count; |
992 | 993 | ||
@@ -1056,11 +1057,11 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, | |||
1056 | goto exit_put; | 1057 | goto exit_put; |
1057 | } | 1058 | } |
1058 | 1059 | ||
1059 | lock_super(sb); | 1060 | mutex_lock(&EXT4_SB(sb)->s_resize_lock); |
1060 | if (o_blocks_count != ext4_blocks_count(es)) { | 1061 | if (o_blocks_count != ext4_blocks_count(es)) { |
1061 | ext4_warning(sb, __func__, | 1062 | ext4_warning(sb, __func__, |
1062 | "multiple resizers run on filesystem!"); | 1063 | "multiple resizers run on filesystem!"); |
1063 | unlock_super(sb); | 1064 | mutex_unlock(&EXT4_SB(sb)->s_resize_lock); |
1064 | ext4_journal_stop(handle); | 1065 | ext4_journal_stop(handle); |
1065 | err = -EBUSY; | 1066 | err = -EBUSY; |
1066 | goto exit_put; | 1067 | goto exit_put; |
@@ -1070,14 +1071,14 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, | |||
1070 | EXT4_SB(sb)->s_sbh))) { | 1071 | EXT4_SB(sb)->s_sbh))) { |
1071 | ext4_warning(sb, __func__, | 1072 | ext4_warning(sb, __func__, |
1072 | "error %d on journal write access", err); | 1073 | "error %d on journal write access", err); |
1073 | unlock_super(sb); | 1074 | mutex_unlock(&EXT4_SB(sb)->s_resize_lock); |
1074 | ext4_journal_stop(handle); | 1075 | ext4_journal_stop(handle); |
1075 | goto exit_put; | 1076 | goto exit_put; |
1076 | } | 1077 | } |
1077 | ext4_blocks_count_set(es, o_blocks_count + add); | 1078 | ext4_blocks_count_set(es, o_blocks_count + add); |
1078 | ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); | 1079 | ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); |
1079 | sb->s_dirt = 1; | 1080 | sb->s_dirt = 1; |
1080 | unlock_super(sb); | 1081 | mutex_unlock(&EXT4_SB(sb)->s_resize_lock); |
1081 | ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count, | 1082 | ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count, |
1082 | o_blocks_count + add); | 1083 | o_blocks_count + add); |
1083 | /* We add the blocks to the bitmap and set the group need init bit */ | 1084 | /* We add the blocks to the bitmap and set the group need init bit */ |
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 7f43fde9554b..1fbf0906ae2e 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
@@ -2646,6 +2646,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) | |||
2646 | #endif | 2646 | #endif |
2647 | INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */ | 2647 | INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */ |
2648 | mutex_init(&sbi->s_orphan_lock); | 2648 | mutex_init(&sbi->s_orphan_lock); |
2649 | mutex_init(&sbi->s_resize_lock); | ||
2649 | 2650 | ||
2650 | sb->s_root = NULL; | 2651 | sb->s_root = NULL; |
2651 | 2652 | ||