aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorEric Sandeen <sandeen@redhat.com>2009-03-16 23:25:40 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-03-16 23:25:40 -0400
commitd33a1976fbee1ee321d6f014333d8f03a39d526c (patch)
tree9cc0de162cd0cba81d08ea1d0e505be6b2d54361 /fs
parent8d03c7a0c550e7ab24cadcef5e66656bfadec8b9 (diff)
ext4: fix bb_prealloc_list corruption due to wrong group locking
This is for Red Hat bug 490026: EXT4 panic, list corruption in ext4_mb_new_inode_pa ext4_lock_group(sb, group) is supposed to protect this list for each group, and a common code flow to remove an album is like this: ext4_get_group_no_and_offset(sb, pa->pa_pstart, &grp, NULL); ext4_lock_group(sb, grp); list_del(&pa->pa_group_list); ext4_unlock_group(sb, grp); so it's critical that we get the right group number back for this prealloc context, to lock the right group (the one associated with this pa) and prevent concurrent list manipulation. however, ext4_mb_put_pa() passes in (pa->pa_pstart - 1) with a comment, "-1 is to protect from crossing allocation group". This makes sense for the group_pa, where pa_pstart is advanced by the length which has been used (in ext4_mb_release_context()), and when the entire length has been used, pa_pstart has been advanced to the first block of the next group. However, for inode_pa, pa_pstart is never advanced; it's just set once to the first block in the group and not moved after that. So in this case, if we subtract one in ext4_mb_put_pa(), we are actually locking the *previous* group, and opening the race with the other threads which do not subtract off the extra block. Signed-off-by: Eric Sandeen <sandeen@redhat.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/mballoc.c9
1 files changed, 7 insertions, 2 deletions
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 41f4348b62f5..9f61e62f435f 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -3589,6 +3589,7 @@ static void ext4_mb_put_pa(struct ext4_allocation_context *ac,
3589 struct super_block *sb, struct ext4_prealloc_space *pa) 3589 struct super_block *sb, struct ext4_prealloc_space *pa)
3590{ 3590{
3591 ext4_group_t grp; 3591 ext4_group_t grp;
3592 ext4_fsblk_t grp_blk;
3592 3593
3593 if (!atomic_dec_and_test(&pa->pa_count) || pa->pa_free != 0) 3594 if (!atomic_dec_and_test(&pa->pa_count) || pa->pa_free != 0)
3594 return; 3595 return;
@@ -3603,8 +3604,12 @@ static void ext4_mb_put_pa(struct ext4_allocation_context *ac,
3603 pa->pa_deleted = 1; 3604 pa->pa_deleted = 1;
3604 spin_unlock(&pa->pa_lock); 3605 spin_unlock(&pa->pa_lock);
3605 3606
3606 /* -1 is to protect from crossing allocation group */ 3607 grp_blk = pa->pa_pstart;
3607 ext4_get_group_no_and_offset(sb, pa->pa_pstart - 1, &grp, NULL); 3608 /* If linear, pa_pstart may be in the next group when pa is used up */
3609 if (pa->pa_linear)
3610 grp_blk--;
3611
3612 ext4_get_group_no_and_offset(sb, grp_blk, &grp, NULL);
3608 3613
3609 /* 3614 /*
3610 * possible race: 3615 * possible race: