When both block device integrity checksums and fsverity are used, all read ioends end up in the ioend processing. This means that fsverity data and metadata ioends could both get on ioend queue ip->i_ioend_list. Then, when worker pick ups the work, it will process all ioends in the queue. Before, processing ioends are sorted based on file offset, meaning data ioend first, metadata second. Metadata ioend already holding folio lock, if data ioend needs this exact folio the worker will self deadlock. Fix this by prioritizing fsverity metadata while sorting read ioends. Signed-off-by: Andrey Albershteyn --- fs/iomap/ioend.c | 8 ++++---- fs/xfs/xfs_aops.c | 32 +++++++++++++++++++++++++++++++- include/linux/iomap.h | 5 ++++- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 0565328764c1..8e46b7f1aa6a 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -4,7 +4,6 @@ */ #include #include -#include #include #include #include @@ -425,7 +424,7 @@ void iomap_ioend_try_merge(struct iomap_ioend *ioend, } EXPORT_SYMBOL_GPL(iomap_ioend_try_merge); -static int iomap_ioend_compare(void *priv, const struct list_head *a, +int iomap_ioend_compare(void *priv, const struct list_head *a, const struct list_head *b) { struct iomap_ioend *ia = container_of(a, struct iomap_ioend, io_list); @@ -437,10 +436,11 @@ static int iomap_ioend_compare(void *priv, const struct list_head *a, return 1; return 0; } +EXPORT_SYMBOL_GPL(iomap_ioend_compare); -void iomap_sort_ioends(struct list_head *ioend_list) +void iomap_sort_ioends(struct list_head *ioend_list, list_cmp_func_t cmp) { - list_sort(NULL, ioend_list, iomap_ioend_compare); + list_sort(NULL, ioend_list, cmp); } EXPORT_SYMBOL_GPL(iomap_sort_ioends); diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 34d0ff7c7086..c7843a8776b0 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -182,6 +182,32 @@ xfs_end_ioend_write( memalloc_nofs_restore(nofs_flag); } +/* + * Prioritize fsverity metadata over file data. Sort based on i_size as fsverity + * metadata is always beyond EOF + */ +static int +xfs_read_ioend_compare( + void *priv, + const struct list_head *a, + const struct list_head *b) +{ + struct iomap_ioend *ia = container_of(a, struct iomap_ioend, + io_list); + struct iomap_ioend *ib = container_of(b, struct iomap_ioend, + io_list); + loff_t i_size = i_size_read(ia->io_inode); + + if (!IS_VERITY(ia->io_inode)) + return iomap_ioend_compare(priv, a, b); + + if (ia->io_offset > i_size && ib->io_offset < i_size) + return -1; + if (ia->io_offset < i_size && ib->io_offset > i_size) + return 1; + return 0; +} + /* * Finish all pending IO completions that require transactional modifications. * @@ -210,7 +236,11 @@ xfs_end_io( list_replace_init(&ip->i_ioend_list, &tmp); spin_unlock_irqrestore(&ip->i_ioend_lock, flags); - iomap_sort_ioends(&tmp); + ioend = list_first_entry_or_null(&tmp, struct iomap_ioend, io_list); + if (bio_op(&ioend->io_bio) == REQ_OP_READ) + iomap_sort_ioends(&tmp, xfs_read_ioend_compare); + else + iomap_sort_ioends(&tmp, iomap_ioend_compare); while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend, io_list))) { list_del_init(&ioend->io_list); diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 56b43d594e6e..36fc931382a8 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -10,6 +10,7 @@ #include #include #include +#include struct address_space; struct fiemap_extent_info; @@ -488,7 +489,9 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, void iomap_finish_ioends(struct iomap_ioend *ioend, int error); void iomap_ioend_try_merge(struct iomap_ioend *ioend, struct list_head *more_ioends); -void iomap_sort_ioends(struct list_head *ioend_list); +int iomap_ioend_compare(void *priv, const struct list_head *a, + const struct list_head *b); +void iomap_sort_ioends(struct list_head *ioend_list, list_cmp_func_t cmp); ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio, loff_t pos, loff_t end_pos, unsigned int dirty_len); int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error); -- 2.54.0