btrfs: zoned: use dedicated lock for data relocation
[ Upstream commit5f0addf7b8
] Currently, we use btrfs_inode_{lock,unlock}() to grant an exclusive writeback of the relocation data inode in btrfs_zoned_data_reloc_{lock,unlock}(). However, that can cause a deadlock in the following path. Thread A takes btrfs_inode_lock() and waits for metadata reservation by e.g, waiting for writeback: prealloc_file_extent_cluster() - btrfs_inode_lock(&inode->vfs_inode, 0); - btrfs_prealloc_file_range() ... - btrfs_replace_file_extents() - btrfs_start_transaction ... - btrfs_reserve_metadata_bytes() Thread B (e.g, doing a writeback work) needs to wait for the inode lock to continue writeback process: do_writepages - btrfs_writepages - extent_writpages - btrfs_zoned_data_reloc_lock(BTRFS_I(inode)); - btrfs_inode_lock() The deadlock is caused by relying on the vfs_inode's lock. By using it, we introduced unnecessary exclusion of writeback and btrfs_prealloc_file_range(). Also, the lock at this point is useless as we don't have any dirty pages in the inode yet. Introduce fs_info->zoned_data_reloc_io_lock and use it for the exclusive writeback. Fixes:35156d8527
("btrfs: zoned: only allow one process to add pages to a relocation inode") CC: stable@vger.kernel.org # 5.16.x: 869f4cdc73f9: btrfs: zoned: encapsulate inode locking for zoned relocation CC: stable@vger.kernel.org # 5.16.x CC: stable@vger.kernel.org # 5.17 Cc: Johannes Thumshirn <johannes.thumshirn@wdc.com> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Родитель
1519e6e284
Коммит
70e2e87ea8
|
@ -1027,6 +1027,7 @@ struct btrfs_fs_info {
|
|||
*/
|
||||
spinlock_t relocation_bg_lock;
|
||||
u64 data_reloc_bg;
|
||||
struct mutex zoned_data_reloc_io_lock;
|
||||
|
||||
#ifdef CONFIG_BTRFS_FS_REF_VERIFY
|
||||
spinlock_t ref_verify_lock;
|
||||
|
|
|
@ -2914,6 +2914,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info)
|
|||
mutex_init(&fs_info->reloc_mutex);
|
||||
mutex_init(&fs_info->delalloc_root_mutex);
|
||||
mutex_init(&fs_info->zoned_meta_io_lock);
|
||||
mutex_init(&fs_info->zoned_data_reloc_io_lock);
|
||||
seqlock_init(&fs_info->profiles_lock);
|
||||
|
||||
INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
|
||||
|
|
|
@ -330,7 +330,7 @@ static inline void btrfs_zoned_data_reloc_lock(struct btrfs_inode *inode)
|
|||
struct btrfs_root *root = inode->root;
|
||||
|
||||
if (btrfs_is_data_reloc_root(root) && btrfs_is_zoned(root->fs_info))
|
||||
btrfs_inode_lock(&inode->vfs_inode, 0);
|
||||
mutex_lock(&root->fs_info->zoned_data_reloc_io_lock);
|
||||
}
|
||||
|
||||
static inline void btrfs_zoned_data_reloc_unlock(struct btrfs_inode *inode)
|
||||
|
@ -338,7 +338,7 @@ static inline void btrfs_zoned_data_reloc_unlock(struct btrfs_inode *inode)
|
|||
struct btrfs_root *root = inode->root;
|
||||
|
||||
if (btrfs_is_data_reloc_root(root) && btrfs_is_zoned(root->fs_info))
|
||||
btrfs_inode_unlock(&inode->vfs_inode, 0);
|
||||
mutex_unlock(&root->fs_info->zoned_data_reloc_io_lock);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче