Commit 15d1ff8111aad85d8b40ee396758990d17a2caac
Committed by
Chris Mason
1 parent
2bcc0328c3
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
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>
Showing 1 changed file with 50 additions and 0 deletions Side-by-side Diff
fs/btrfs/extent-tree.c
... | ... | @@ -3445,6 +3445,50 @@ |
3445 | 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 | 3492 | static int do_chunk_alloc(struct btrfs_trans_handle *trans, |
3449 | 3493 | struct btrfs_root *extent_root, u64 alloc_bytes, |
3450 | 3494 | u64 flags, int force) |
... | ... | @@ -3514,6 +3558,12 @@ |
3514 | 3558 | fs_info->metadata_ratio)) |
3515 | 3559 | force_metadata_allocation(fs_info); |
3516 | 3560 | } |
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); | |
3517 | 3567 | |
3518 | 3568 | ret = btrfs_alloc_chunk(trans, extent_root, flags); |
3519 | 3569 | if (ret < 0 && ret != -ENOSPC) |