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/ext4 | |
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/ext4')
-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 | ||