aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/inode.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/inode.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/inode.c')
-rw-r--r--fs/ext4/inode.c7
1 files changed, 4 insertions, 3 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 96f3366f59f6..4e7f363e3030 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4965,7 +4965,8 @@ static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
4965 */ 4965 */
4966int ext4_meta_trans_blocks(struct inode *inode, int nrblocks, int chunk) 4966int ext4_meta_trans_blocks(struct inode *inode, int nrblocks, int chunk)
4967{ 4967{
4968 int groups, gdpblocks; 4968 ext4_group_t groups, ngroups = ext4_get_groups_count(inode->i_sb);
4969 int gdpblocks;
4969 int idxblocks; 4970 int idxblocks;
4970 int ret = 0; 4971 int ret = 0;
4971 4972
@@ -4992,8 +4993,8 @@ int ext4_meta_trans_blocks(struct inode *inode, int nrblocks, int chunk)
4992 groups += nrblocks; 4993 groups += nrblocks;
4993 4994
4994 gdpblocks = groups; 4995 gdpblocks = groups;
4995 if (groups > EXT4_SB(inode->i_sb)->s_groups_count) 4996 if (groups > ngroups)
4996 groups = EXT4_SB(inode->i_sb)->s_groups_count; 4997 groups = ngroups;
4997 if (groups > EXT4_SB(inode->i_sb)->s_gdb_count) 4998 if (groups > EXT4_SB(inode->i_sb)->s_gdb_count)
4998 gdpblocks = EXT4_SB(inode->i_sb)->s_gdb_count; 4999 gdpblocks = EXT4_SB(inode->i_sb)->s_gdb_count;
4999 5000