mirror of https://gitee.com/openkylin/linux.git
Btrfs: fix deadlock between alloc_mutex/chunk_mutex
This fixes a deadlock that happens between the alloc_mutex and chunk_mutex. Process A comes in, decides to do a do_chunk_alloc, which takes the chunk_mutex, and is holding the alloc_mutex because the only way you get to do_chunk_alloc is by holding the alloc_mutex. btrfs_alloc_chunk does its thing and goes to insert a new item, which results in a cow of the block. We get into del_pending_extents from there, where if we need to be rescheduled we drop the alloc_mutex and schedule. At this point process B comes in to do an allocation and gets the alloc_mutex, and because process A did not do the chunk allocation completely it thinks its a good time to do a chunk allocation as well, and hangs on the chunk_mutex. Process A wakes up and tries to take the alloc_mutex and cannot. The way to fix this is do a mutex_trylock() on chunk_mutex. If we return 0 we didn't get the lock, and if this is just a "hey it may be a good time to allocate a chunk" then we just exit. If we are trying to force an allocation then we reschedule and keep trying to acquire the chunk_mutex. If once we acquire it the space is already full then we can just exit, otherwise we can continue with the chunk allocation. Thank you, Signed-off-by: Josef Bacik <jbacik@redhat.com>
This commit is contained in:
parent
83afeac42c
commit
cf74982385
|
@ -1505,7 +1505,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
|
|||
u64 thresh;
|
||||
u64 start;
|
||||
u64 num_bytes;
|
||||
int ret = 0;
|
||||
int ret = 0, waited = 0;
|
||||
|
||||
flags = reduce_alloc_profile(extent_root, flags);
|
||||
|
||||
|
@ -1530,7 +1530,18 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
|
|||
space_info->bytes_reserved + alloc_bytes) < thresh)
|
||||
goto out;
|
||||
|
||||
mutex_lock(&extent_root->fs_info->chunk_mutex);
|
||||
while (!mutex_trylock(&extent_root->fs_info->chunk_mutex)) {
|
||||
if (!force)
|
||||
goto out;
|
||||
mutex_unlock(&extent_root->fs_info->alloc_mutex);
|
||||
cond_resched();
|
||||
mutex_lock(&extent_root->fs_info->alloc_mutex);
|
||||
waited = 1;
|
||||
}
|
||||
|
||||
if (waited && space_info->full)
|
||||
goto out_unlock;
|
||||
|
||||
ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes, flags);
|
||||
if (ret == -ENOSPC) {
|
||||
printk("space info full %Lu\n", flags);
|
||||
|
|
Loading…
Reference in New Issue