aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/balloc.c
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2009-05-01 08:50:38 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-05-01 08:50:38 -0400
commit8df9675f8b498d0bfa1f0b5b06f56bf1ff366dd5 (patch)
tree38fd56a82049f50b4d774af47b9d39f116071755 /fs/ext4/balloc.c
parent9ca92389c5312a51e819c15c762f0abdc7f3129b (diff)
ext4: Avoid races caused by on-line resizing and SMP memory reordering
Ext4's on-line resizing adds a new block group and then, only at the last step adjusts s_groups_count. However, it's possible on SMP systems that another CPU could see the updated the s_group_count and not see the newly initialized data structures for the just-added block group. For this reason, it's important to insert a SMP read barrier after reading s_groups_count and before reading any (for example) the new block group descriptors allowed by the increased value of s_groups_count. Unfortunately, we rather blatently violate this locking protocol documented in fs/ext4/resize.c. Fortunately, (1) on-line resizes happen relatively rarely, and (2) it seems rare that the filesystem code will immediately try to use just-added block group before any memory ordering issues resolve themselves. So apparently problems here are relatively hard to hit, since ext3 has been vulnerable to the same issue for years with no one apparently complaining. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/balloc.c')
-rw-r--r--fs/ext4/balloc.c15
1 files changed, 7 insertions, 8 deletions
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 53c72ad85877..a5ba039850c5 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -88,6 +88,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
88 ext4_group_t block_group, struct ext4_group_desc *gdp) 88 ext4_group_t block_group, struct ext4_group_desc *gdp)
89{ 89{
90 int bit, bit_max; 90 int bit, bit_max;
91 ext4_group_t ngroups = ext4_get_groups_count(sb);
91 unsigned free_blocks, group_blocks; 92 unsigned free_blocks, group_blocks;
92 struct ext4_sb_info *sbi = EXT4_SB(sb); 93 struct ext4_sb_info *sbi = EXT4_SB(sb);
93 94
@@ -123,7 +124,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
123 bit_max += ext4_bg_num_gdb(sb, block_group); 124 bit_max += ext4_bg_num_gdb(sb, block_group);
124 } 125 }
125 126
126 if (block_group == sbi->s_groups_count - 1) { 127 if (block_group == ngroups - 1) {
127 /* 128 /*
128 * Even though mke2fs always initialize first and last group 129 * Even though mke2fs always initialize first and last group
129 * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need 130 * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need
@@ -131,7 +132,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
131 */ 132 */
132 group_blocks = ext4_blocks_count(sbi->s_es) - 133 group_blocks = ext4_blocks_count(sbi->s_es) -
133 le32_to_cpu(sbi->s_es->s_first_data_block) - 134 le32_to_cpu(sbi->s_es->s_first_data_block) -
134 (EXT4_BLOCKS_PER_GROUP(sb) * (sbi->s_groups_count - 1)); 135 (EXT4_BLOCKS_PER_GROUP(sb) * (ngroups - 1));
135 } else { 136 } else {
136 group_blocks = EXT4_BLOCKS_PER_GROUP(sb); 137 group_blocks = EXT4_BLOCKS_PER_GROUP(sb);
137 } 138 }
@@ -205,18 +206,18 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
205{ 206{
206 unsigned int group_desc; 207 unsigned int group_desc;
207 unsigned int offset; 208 unsigned int offset;
209 ext4_group_t ngroups = ext4_get_groups_count(sb);
208 struct ext4_group_desc *desc; 210 struct ext4_group_desc *desc;
209 struct ext4_sb_info *sbi = EXT4_SB(sb); 211 struct ext4_sb_info *sbi = EXT4_SB(sb);
210 212
211 if (block_group >= sbi->s_groups_count) { 213 if (block_group >= ngroups) {
212 ext4_error(sb, "ext4_get_group_desc", 214 ext4_error(sb, "ext4_get_group_desc",
213 "block_group >= groups_count - " 215 "block_group >= groups_count - "
214 "block_group = %u, groups_count = %u", 216 "block_group = %u, groups_count = %u",
215 block_group, sbi->s_groups_count); 217 block_group, ngroups);
216 218
217 return NULL; 219 return NULL;
218 } 220 }
219 smp_rmb();
220 221
221 group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb); 222 group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb);
222 offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1); 223 offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1);
@@ -665,7 +666,7 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb)
665 ext4_fsblk_t desc_count; 666 ext4_fsblk_t desc_count;
666 struct ext4_group_desc *gdp; 667 struct ext4_group_desc *gdp;
667 ext4_group_t i; 668 ext4_group_t i;
668 ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count; 669 ext4_group_t ngroups = ext4_get_groups_count(sb);
669#ifdef EXT4FS_DEBUG 670#ifdef EXT4FS_DEBUG
670 struct ext4_super_block *es; 671 struct ext4_super_block *es;
671 ext4_fsblk_t bitmap_count; 672 ext4_fsblk_t bitmap_count;
@@ -677,7 +678,6 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb)
677 bitmap_count = 0; 678 bitmap_count = 0;
678 gdp = NULL; 679 gdp = NULL;
679 680
680 smp_rmb();
681 for (i = 0; i < ngroups; i++) { 681 for (i = 0; i < ngroups; i++) {
682 gdp = ext4_get_group_desc(sb, i, NULL); 682 gdp = ext4_get_group_desc(sb, i, NULL);
683 if (!gdp) 683 if (!gdp)
@@ -700,7 +700,6 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb)
700 return bitmap_count; 700 return bitmap_count;
701#else 701#else
702 desc_count = 0; 702 desc_count = 0;
703 smp_rmb();
704 for (i = 0; i < ngroups; i++) { 703 for (i = 0; i < ngroups; i++) {
705 gdp = ext4_get_group_desc(sb, i, NULL); 704 gdp = ext4_get_group_desc(sb, i, NULL);
706 if (!gdp) 705 if (!gdp)