diff options
| author | Theodore Ts'o <tytso@mit.edu> | 2012-08-10 13:57:52 -0400 |
|---|---|---|
| committer | Theodore Ts'o <tytso@mit.edu> | 2012-08-17 09:06:06 -0400 |
| commit | 7a4c5de27efa4c2ecca87af0a3deea63446367e2 (patch) | |
| tree | 9f56c68e86e9a379621ee49e0b6d05dae8b01f9e /fs | |
| parent | 7e731bc9a12339f344cddf82166b82633d99dd86 (diff) | |
ext4: don't call ext4_error while block group is locked
While in ext4_validate_block_bitmap(), if an block allocation bitmap
is found to be invalid, we call ext4_error() while the block group is
still locked. This causes ext4_commit_super() to call a function
which might sleep while in an atomic context.
There's no need to keep the block group locked at this point, so hoist
the ext4_error() call up to ext4_validate_block_bitmap() and release
the block group spinlock before calling ext4_error().
The reported stack trace can be found at:
http://article.gmane.org/gmane.comp.file-systems.ext4/33731
Reported-by: Dave Jones <davej@redhat.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/ext4/balloc.c | 62 | ||||
| -rw-r--r-- | fs/ext4/bitmap.c | 1 |
2 files changed, 37 insertions, 26 deletions
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index d23b31ca9d7a..1b5089067d01 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c | |||
| @@ -280,14 +280,18 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb, | |||
| 280 | return desc; | 280 | return desc; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | static int ext4_valid_block_bitmap(struct super_block *sb, | 283 | /* |
| 284 | struct ext4_group_desc *desc, | 284 | * Return the block number which was discovered to be invalid, or 0 if |
| 285 | unsigned int block_group, | 285 | * the block bitmap is valid. |
| 286 | struct buffer_head *bh) | 286 | */ |
| 287 | static ext4_fsblk_t ext4_valid_block_bitmap(struct super_block *sb, | ||
| 288 | struct ext4_group_desc *desc, | ||
| 289 | unsigned int block_group, | ||
| 290 | struct buffer_head *bh) | ||
| 287 | { | 291 | { |
| 288 | ext4_grpblk_t offset; | 292 | ext4_grpblk_t offset; |
| 289 | ext4_grpblk_t next_zero_bit; | 293 | ext4_grpblk_t next_zero_bit; |
| 290 | ext4_fsblk_t bitmap_blk; | 294 | ext4_fsblk_t blk; |
| 291 | ext4_fsblk_t group_first_block; | 295 | ext4_fsblk_t group_first_block; |
| 292 | 296 | ||
| 293 | if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) { | 297 | if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) { |
| @@ -297,37 +301,33 @@ static int ext4_valid_block_bitmap(struct super_block *sb, | |||
| 297 | * or it has to also read the block group where the bitmaps | 301 | * or it has to also read the block group where the bitmaps |
| 298 | * are located to verify they are set. | 302 | * are located to verify they are set. |
| 299 | */ | 303 | */ |
| 300 | return 1; | 304 | return 0; |
| 301 | } | 305 | } |
| 302 | group_first_block = ext4_group_first_block_no(sb, block_group); | 306 | group_first_block = ext4_group_first_block_no(sb, block_group); |
| 303 | 307 | ||
| 304 | /* check whether block bitmap block number is set */ | 308 | /* check whether block bitmap block number is set */ |
| 305 | bitmap_blk = ext4_block_bitmap(sb, desc); | 309 | blk = ext4_block_bitmap(sb, desc); |
| 306 | offset = bitmap_blk - group_first_block; | 310 | offset = blk - group_first_block; |
| 307 | if (!ext4_test_bit(offset, bh->b_data)) | 311 | if (!ext4_test_bit(offset, bh->b_data)) |
| 308 | /* bad block bitmap */ | 312 | /* bad block bitmap */ |
| 309 | goto err_out; | 313 | return blk; |
| 310 | 314 | ||
| 311 | /* check whether the inode bitmap block number is set */ | 315 | /* check whether the inode bitmap block number is set */ |
| 312 | bitmap_blk = ext4_inode_bitmap(sb, desc); | 316 | blk = ext4_inode_bitmap(sb, desc); |
| 313 | offset = bitmap_blk - group_first_block; | 317 | offset = blk - group_first_block; |
| 314 | if (!ext4_test_bit(offset, bh->b_data)) | 318 | if (!ext4_test_bit(offset, bh->b_data)) |
| 315 | /* bad block bitmap */ | 319 | /* bad block bitmap */ |
| 316 | goto err_out; | 320 | return blk; |
| 317 | 321 | ||
| 318 | /* check whether the inode table block number is set */ | 322 | /* check whether the inode table block number is set */ |
| 319 | bitmap_blk = ext4_inode_table(sb, desc); | 323 | blk = ext4_inode_table(sb, desc); |
| 320 | offset = bitmap_blk - group_first_block; | 324 | offset = blk - group_first_block; |
| 321 | next_zero_bit = ext4_find_next_zero_bit(bh->b_data, | 325 | next_zero_bit = ext4_find_next_zero_bit(bh->b_data, |
| 322 | offset + EXT4_SB(sb)->s_itb_per_group, | 326 | offset + EXT4_SB(sb)->s_itb_per_group, |
| 323 | offset); | 327 | offset); |
| 324 | if (next_zero_bit >= offset + EXT4_SB(sb)->s_itb_per_group) | 328 | if (next_zero_bit < offset + EXT4_SB(sb)->s_itb_per_group) |
| 325 | /* good bitmap for inode tables */ | 329 | /* bad bitmap for inode tables */ |
| 326 | return 1; | 330 | return blk; |
| 327 | |||
| 328 | err_out: | ||
| 329 | ext4_error(sb, "Invalid block bitmap - block_group = %d, block = %llu", | ||
| 330 | block_group, bitmap_blk); | ||
| 331 | return 0; | 331 | return 0; |
| 332 | } | 332 | } |
| 333 | 333 | ||
| @@ -336,14 +336,26 @@ void ext4_validate_block_bitmap(struct super_block *sb, | |||
| 336 | unsigned int block_group, | 336 | unsigned int block_group, |
| 337 | struct buffer_head *bh) | 337 | struct buffer_head *bh) |
| 338 | { | 338 | { |
| 339 | ext4_fsblk_t blk; | ||
| 340 | |||
| 339 | if (buffer_verified(bh)) | 341 | if (buffer_verified(bh)) |
| 340 | return; | 342 | return; |
| 341 | 343 | ||
| 342 | ext4_lock_group(sb, block_group); | 344 | ext4_lock_group(sb, block_group); |
| 343 | if (ext4_valid_block_bitmap(sb, desc, block_group, bh) && | 345 | blk = ext4_valid_block_bitmap(sb, desc, block_group, bh); |
| 344 | ext4_block_bitmap_csum_verify(sb, block_group, desc, bh, | 346 | if (unlikely(blk != 0)) { |
| 345 | EXT4_BLOCKS_PER_GROUP(sb) / 8)) | 347 | ext4_unlock_group(sb, block_group); |
| 346 | set_buffer_verified(bh); | 348 | ext4_error(sb, "bg %u: block %llu: invalid block bitmap", |
| 349 | block_group, blk); | ||
| 350 | return; | ||
| 351 | } | ||
| 352 | if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group, | ||
| 353 | desc, bh, EXT4_BLOCKS_PER_GROUP(sb) / 8))) { | ||
| 354 | ext4_unlock_group(sb, block_group); | ||
| 355 | ext4_error(sb, "bg %u: bad block bitmap checksum", block_group); | ||
| 356 | return; | ||
| 357 | } | ||
| 358 | set_buffer_verified(bh); | ||
| 347 | ext4_unlock_group(sb, block_group); | 359 | ext4_unlock_group(sb, block_group); |
| 348 | } | 360 | } |
| 349 | 361 | ||
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c index f8716eab9995..5c2d1813ebe9 100644 --- a/fs/ext4/bitmap.c +++ b/fs/ext4/bitmap.c | |||
| @@ -79,7 +79,6 @@ int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group, | |||
| 79 | if (provided == calculated) | 79 | if (provided == calculated) |
| 80 | return 1; | 80 | return 1; |
| 81 | 81 | ||
| 82 | ext4_error(sb, "Bad block bitmap checksum: block_group = %u", group); | ||
| 83 | return 0; | 82 | return 0; |
| 84 | } | 83 | } |
| 85 | 84 | ||
