From: Johannes Thumshirn Date: Fri, 22 May 2026 09:02:47 +0000 (+0200) Subject: btrfs: zoned: fix deadlock waiting for ticket during data relocation X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=814c3b4ea357297c507158bceb07bcdc5fbe9808;p=thirdparty%2Flinux.git btrfs: zoned: fix deadlock waiting for ticket during data relocation When performing data relocation on a zoned filesystem, BTRFS can deadlock in handle_reserve_tickets(). The relocation process is waiting on a space reservation ticket that can never be fulfilled, because the relocation itself is the operation responsible for freeing up that space. Fix this by introducing a new flush state, BTRFS_RESERVE_FLUSH_ZONED_RELOCATION, specifically for data chunk allocation during zoned relocation. Like BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE, this state uses priority_reclaim_data_space() instead of the normal flushing path, which avoids re-entering the relocation code and breaking the deadlock cycle. In btrfs_alloc_data_chunk_ondemand(), select this new flush state when the inode belongs to a data relocation root on a zoned filesystem. Fixes: e2a7fd22378f ("btrfs: zoned: add zone reclaim flush state for DATA space_info") Reviewed-by: Boris Burkov Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba --- diff --git a/fs/btrfs/delalloc-space.c b/fs/btrfs/delalloc-space.c index 0970799d0aa44..4293a63834337 100644 --- a/fs/btrfs/delalloc-space.c +++ b/fs/btrfs/delalloc-space.c @@ -134,6 +134,8 @@ int btrfs_alloc_data_chunk_ondemand(const struct btrfs_inode *inode, u64 bytes) if (btrfs_is_free_space_inode(inode)) flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE; + else if (btrfs_is_zoned(fs_info) && btrfs_is_data_reloc_root(root)) + flush = BTRFS_RESERVE_FLUSH_ZONED_RELOCATION; return btrfs_reserve_data_bytes(data_sinfo_for_inode(inode), bytes, flush); } diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index 739984462677e..e6641597b321e 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -1705,6 +1705,7 @@ static int handle_reserve_ticket(struct btrfs_space_info *space_info, ARRAY_SIZE(evict_flush_states)); break; case BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE: + case BTRFS_RESERVE_FLUSH_ZONED_RELOCATION: priority_reclaim_data_space(space_info, ticket); break; default: @@ -1968,6 +1969,7 @@ int btrfs_reserve_data_bytes(struct btrfs_space_info *space_info, u64 bytes, ASSERT(flush == BTRFS_RESERVE_FLUSH_DATA || flush == BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE || + flush == BTRFS_RESERVE_FLUSH_ZONED_RELOCATION || flush == BTRFS_RESERVE_NO_FLUSH, "flush=%d", flush); ASSERT(!current->journal_info || flush != BTRFS_RESERVE_FLUSH_DATA, "current->journal_info=0x%lx flush=%d", diff --git a/fs/btrfs/space-info.h b/fs/btrfs/space-info.h index 24f45072ca4b1..aa836e8a9d4a6 100644 --- a/fs/btrfs/space-info.h +++ b/fs/btrfs/space-info.h @@ -77,6 +77,17 @@ enum btrfs_reserve_flush_enum { */ BTRFS_RESERVE_FLUSH_ALL_STEAL, + /* + * This is for relocation on zoned filesystems only. We need to use + * priority flushing for this, because otherwise we can deadlock on + * waiting for a ticket, that cannot be granted, because we cannot do + * any allocations. + * + * Apart from being specific to zoned relocation, it is equal to + * BTRFS_FLUSH_FREE_SPACE_INODE. + */ + BTRFS_RESERVE_FLUSH_ZONED_RELOCATION, + /* * This is for btrfs_use_block_rsv only. We have exhausted our block * rsv and our global block rsv. This can happen for things like