]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: add a separate bio_set for spliting GC writes
authorChristoph Hellwig <hch@lst.de>
Wed, 29 Jul 2026 13:02:48 +0000 (15:02 +0200)
committerCarlos Maiolino <cem@kernel.org>
Mon, 3 Aug 2026 08:24:02 +0000 (10:24 +0200)
Allocating the new bio for a split from the same pool as the original
one can deadlock under memory pressure as the origin bio could be the
last one from the mempool.

Add a separate pool for splitting GC write bios to avoid this.

Fixes: 080d01c41d44 ("xfs: implement zoned garbage collection")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
fs/xfs/xfs_zone_gc.c

index f76a091308528b3bf267195093672fed926f741e..e4f70e02463273bfc6ed819930f62ec8b8145200 100644 (file)
@@ -130,6 +130,9 @@ struct xfs_zone_gc_data {
        /* bioset used to allocate the gc_bios */
        struct bio_set                  bio_set;
 
+       /* bioset used when writes need to be split to hardware limits */
+       struct bio_set                  split_bio_set;
+
        /*
         * Scratchpad to buffer GC data, organized as a ring buffer over
         * discontiguous folios.  scratch_head is where the buffer is filled,
@@ -221,6 +224,9 @@ xfs_zone_gc_data_alloc(
        if (bioset_init(&data->bio_set, 16, offsetof(struct xfs_gc_bio, bio),
                        BIOSET_NEED_BVECS))
                goto out_free_recs;
+       if (bioset_init(&data->split_bio_set, 16,
+                       offsetof(struct xfs_gc_bio, bio), 0))
+               goto out_exit_bio_set;
        for (i = 0; i < XFS_GC_NR_BUFS; i++) {
                data->scratch_folios[i] =
                        folio_alloc(GFP_KERNEL, get_order(XFS_GC_BUF_SIZE));
@@ -238,6 +244,8 @@ xfs_zone_gc_data_alloc(
 out_free_scratch:
        while (--i >= 0)
                folio_put(data->scratch_folios[i]);
+       bioset_exit(&data->split_bio_set);
+out_exit_bio_set:
        bioset_exit(&data->bio_set);
 out_free_recs:
        kfree(data->iter.recs);
@@ -254,6 +262,7 @@ xfs_zone_gc_data_free(
 
        for (i = 0; i < XFS_GC_NR_BUFS; i++)
                folio_put(data->scratch_folios[i]);
+       bioset_exit(&data->split_bio_set);
        bioset_exit(&data->bio_set);
        kfree(data->iter.recs);
        kfree(data);
@@ -810,7 +819,8 @@ xfs_zone_gc_split_write(
                        data->mp->m_sb.sb_blocksize) >> SECTOR_SHIFT;
        split_len = split_sectors << SECTOR_SHIFT;
 
-       split = bio_split(&chunk->bio, split_sectors, GFP_NOFS, &data->bio_set);
+       split = bio_split(&chunk->bio, split_sectors, GFP_NOFS,
+                       &data->split_bio_set);
        split_chunk = container_of(split, struct xfs_gc_bio, bio);
        split_chunk->data = data;
        ihold(VFS_I(chunk->ip));