diff options
author | Liu Bo <liubo2009@cn.fujitsu.com> | 2012-03-29 09:57:44 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2012-03-29 09:57:44 -0400 |
commit | 15d1ff8111aad85d8b40ee396758990d17a2caac (patch) | |
tree | 5258905b649150abae0a8aa010bbaa7ef1077e97 /fs/btrfs/extent-tree.c | |
parent | 2bcc0328c3a043880796a602c75fbeb1537aa1e1 (diff) |
Btrfs: fix deadlock during allocating chunks
This deadlock comes from xfstests 251.
We'll hold the chunk_mutex throughout the whole of a chunk allocation.
But if we find that we've used up system chunk space, we need to allocate a
new system chunk, but this will lead to a recursion of chunk allocation and end
up with a deadlock on chunk_mutex.
So instead we need to allocate the system chunk first if we find we're in ENOSPC.
Signed-off-by: Liu Bo <liubo2009@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r-- | fs/btrfs/extent-tree.c | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 7c233407beee..a84420491c11 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -3445,6 +3445,50 @@ static int should_alloc_chunk(struct btrfs_root *root, | |||
3445 | return 1; | 3445 | return 1; |
3446 | } | 3446 | } |
3447 | 3447 | ||
3448 | static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type) | ||
3449 | { | ||
3450 | u64 num_dev; | ||
3451 | |||
3452 | if (type & BTRFS_BLOCK_GROUP_RAID10 || | ||
3453 | type & BTRFS_BLOCK_GROUP_RAID0) | ||
3454 | num_dev = root->fs_info->fs_devices->rw_devices; | ||
3455 | else if (type & BTRFS_BLOCK_GROUP_RAID1) | ||
3456 | num_dev = 2; | ||
3457 | else | ||
3458 | num_dev = 1; /* DUP or single */ | ||
3459 | |||
3460 | /* metadata for updaing devices and chunk tree */ | ||
3461 | return btrfs_calc_trans_metadata_size(root, num_dev + 1); | ||
3462 | } | ||
3463 | |||
3464 | static void check_system_chunk(struct btrfs_trans_handle *trans, | ||
3465 | struct btrfs_root *root, u64 type) | ||
3466 | { | ||
3467 | struct btrfs_space_info *info; | ||
3468 | u64 left; | ||
3469 | u64 thresh; | ||
3470 | |||
3471 | info = __find_space_info(root->fs_info, BTRFS_BLOCK_GROUP_SYSTEM); | ||
3472 | spin_lock(&info->lock); | ||
3473 | left = info->total_bytes - info->bytes_used - info->bytes_pinned - | ||
3474 | info->bytes_reserved - info->bytes_readonly; | ||
3475 | spin_unlock(&info->lock); | ||
3476 | |||
3477 | thresh = get_system_chunk_thresh(root, type); | ||
3478 | if (left < thresh && btrfs_test_opt(root, ENOSPC_DEBUG)) { | ||
3479 | printk(KERN_INFO "left=%llu, need=%llu, flags=%llu\n", | ||
3480 | left, thresh, type); | ||
3481 | dump_space_info(info, 0, 0); | ||
3482 | } | ||
3483 | |||
3484 | if (left < thresh) { | ||
3485 | u64 flags; | ||
3486 | |||
3487 | flags = btrfs_get_alloc_profile(root->fs_info->chunk_root, 0); | ||
3488 | btrfs_alloc_chunk(trans, root, flags); | ||
3489 | } | ||
3490 | } | ||
3491 | |||
3448 | static int do_chunk_alloc(struct btrfs_trans_handle *trans, | 3492 | static int do_chunk_alloc(struct btrfs_trans_handle *trans, |
3449 | struct btrfs_root *extent_root, u64 alloc_bytes, | 3493 | struct btrfs_root *extent_root, u64 alloc_bytes, |
3450 | u64 flags, int force) | 3494 | u64 flags, int force) |
@@ -3515,6 +3559,12 @@ again: | |||
3515 | force_metadata_allocation(fs_info); | 3559 | force_metadata_allocation(fs_info); |
3516 | } | 3560 | } |
3517 | 3561 | ||
3562 | /* | ||
3563 | * Check if we have enough space in SYSTEM chunk because we may need | ||
3564 | * to update devices. | ||
3565 | */ | ||
3566 | check_system_chunk(trans, extent_root, flags); | ||
3567 | |||
3518 | ret = btrfs_alloc_chunk(trans, extent_root, flags); | 3568 | ret = btrfs_alloc_chunk(trans, extent_root, flags); |
3519 | if (ret < 0 && ret != -ENOSPC) | 3569 | if (ret < 0 && ret != -ENOSPC) |
3520 | goto out; | 3570 | goto out; |