From: Zygo Blaxell Date: Sat, 12 Sep 2026 05:45:00 +0000 (-0400) Subject: wip: cand46 = cand45 + hold the probe margin until the metadata reservation (A5;... X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e6c8f513d330cd260882dd45f220805e16ff134;p=linux wip: cand46 = cand45 + hold the probe margin until the metadata reservation (A5; lane's NO_FLUSH defrag site included) --- diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index fd7cefc2e6d72..ee8d3ede27ba0 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -6221,7 +6221,7 @@ again: cache_size *= fs_info->sectorsize; ret = btrfs_check_data_free_space(BTRFS_I(inode), &data_reserved, 0, - cache_size, false); + cache_size, false, NULL); if (ret) goto out_put; diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index 575e0b9020f96..dc8e7fed99d42 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -1159,6 +1159,7 @@ static int defrag_one_locked_target(struct btrfs_inode *inode, struct extent_changeset *data_reserved = NULL; const u64 start = target->start; const u64 len = target->len; + u64 held = 0; int ret = 0; /* @@ -1173,10 +1174,11 @@ static int defrag_one_locked_target(struct btrfs_inode *inode, * stopping early is always safe. */ ret = btrfs_check_data_free_space(inode, &data_reserved, start, len, - true); + true, &held); if (ret < 0) return ret; ret = btrfs_delalloc_reserve_metadata(inode, len, len, true); + btrfs_release_data_margin_probe(inode, held); if (ret < 0) { btrfs_free_reserved_data_space(inode, data_reserved, start, len); @@ -1310,6 +1312,7 @@ static int carry_one_range(struct btrfs_inode *inode, u64 start, u64 len, const unsigned int nr_pages = ((start + len - 1) >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; u64 cur; + u64 held = 0; int ret = 0; ASSERT(IS_ALIGNED(start, fs_info->sectorsize) && @@ -1363,10 +1366,11 @@ static int carry_one_range(struct btrfs_inode *inode, u64 start, u64 len, * stays in its old stripe (see btrfs_carry_log_tail). */ ret = btrfs_check_data_free_space(inode, &data_reserved, start, len, - true); + true, &held); if (ret < 0) goto unlock_extent; ret = btrfs_delalloc_reserve_metadata(inode, len, len, true); + btrfs_release_data_margin_probe(inode, held); if (ret < 0) { btrfs_free_reserved_data_space(inode, data_reserved, start, len); diff --git a/fs/btrfs/delalloc-space.c b/fs/btrfs/delalloc-space.c index c36e1d531323e..54cd0a97f1edb 100644 --- a/fs/btrfs/delalloc-space.c +++ b/fs/btrfs/delalloc-space.c @@ -144,7 +144,7 @@ int btrfs_alloc_data_chunk_ondemand(const struct btrfs_inode *inode, u64 bytes) int btrfs_check_data_free_space(struct btrfs_inode *inode, struct extent_changeset **reserved, u64 start, - u64 len, bool noflush) + u64 len, bool noflush, u64 *stripe_held) { struct btrfs_fs_info *fs_info = inode->root->fs_info; enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_DATA; @@ -167,9 +167,15 @@ int btrfs_check_data_free_space(struct btrfs_inode *inode, * produce enough claimable whole stripes to cover this write's * worst case, and a writer that cannot be covered gets an honest * ENOSPC here rather than a dropped writeback later. The margin is - * released again immediately: the moment the range becomes delalloc, - * btrfs_mod_outstanding_extents() re-charges the same worst case - * into bytes_stripe_margin, which it then carries until writeback. + * then re-charged into bytes_stripe_margin by the metadata reservation + * (btrfs_mod_outstanding_extents()), which carries it until writeback. + * A caller that passes @stripe_held keeps the probe's margin in + * bytes_may_use until then and releases it with + * btrfs_release_data_margin_probe() after its metadata reservation: + * released here, the gap between the two let concurrent writers be + * admitted into each other's margin at the fill edge, and their + * writebacks then found no stripe. Without @stripe_held (callers + * that take no metadata reservation) it is released at once. */ if (flush == BTRFS_RESERVE_FLUSH_DATA && btrfs_test_opt(fs_info, STRIPE_ALLOC)) { @@ -183,13 +189,19 @@ int btrfs_check_data_free_space(struct btrfs_inode *inode, len + stripe_margin, flush); if (ret < 0) return ret; - if (stripe_margin) + if (stripe_margin && !stripe_held) btrfs_space_info_free_bytes_may_use(data_sinfo_for_inode(inode), stripe_margin); + if (stripe_held) + *stripe_held = stripe_margin; /* Use new btrfs_qgroup_reserve_data to reserve precious data space. */ ret = btrfs_qgroup_reserve_data(inode, reserved, start, len); if (ret < 0) { + if (stripe_held) { + btrfs_release_data_margin_probe(inode, stripe_margin); + *stripe_held = 0; + } btrfs_free_reserved_data_space_noquota(inode, len); extent_changeset_free(*reserved); *reserved = NULL; @@ -199,6 +211,13 @@ int btrfs_check_data_free_space(struct btrfs_inode *inode, return ret; } +/* Release the probe margin btrfs_check_data_free_space() handed back in @stripe_held. */ +void btrfs_release_data_margin_probe(struct btrfs_inode *inode, u64 held) +{ + if (held) + btrfs_space_info_free_bytes_may_use(data_sinfo_for_inode(inode), held); +} + /* * Called if we need to clear a data reservation for this inode * Normally in a error case. @@ -527,12 +546,14 @@ void btrfs_delalloc_shrink_extents(struct btrfs_inode *inode, u64 reserved_len, int btrfs_delalloc_reserve_space(struct btrfs_inode *inode, struct extent_changeset **reserved, u64 start, u64 len) { + u64 held = 0; int ret; - ret = btrfs_check_data_free_space(inode, reserved, start, len, false); + ret = btrfs_check_data_free_space(inode, reserved, start, len, false, &held); if (ret < 0) return ret; ret = btrfs_delalloc_reserve_metadata(inode, len, len, false); + btrfs_release_data_margin_probe(inode, held); if (ret < 0) { btrfs_free_reserved_data_space(inode, *reserved, start, len); extent_changeset_free(*reserved); diff --git a/fs/btrfs/delalloc-space.h b/fs/btrfs/delalloc-space.h index 6119c0d3f883c..c464cf0a88d9b 100644 --- a/fs/btrfs/delalloc-space.h +++ b/fs/btrfs/delalloc-space.h @@ -12,7 +12,8 @@ struct btrfs_fs_info; int btrfs_alloc_data_chunk_ondemand(const struct btrfs_inode *inode, u64 bytes); int btrfs_check_data_free_space(struct btrfs_inode *inode, struct extent_changeset **reserved, u64 start, u64 len, - bool noflush); + bool noflush, u64 *stripe_held); +void btrfs_release_data_margin_probe(struct btrfs_inode *inode, u64 held); void btrfs_free_reserved_data_space(struct btrfs_inode *inode, struct extent_changeset *reserved, u64 start, u64 len); void btrfs_delalloc_release_space(struct btrfs_inode *inode, diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index ae5d38f448160..2d8ce1a9ab195 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -19,6 +19,8 @@ struct btrfs_dio_data { struct extent_changeset *data_reserved; struct btrfs_ordered_extent *ordered; bool data_space_reserved; + /* stripe_alloc probe margin held in bytes_may_use until the metadata reservation */ + u64 stripe_held; bool nocow_done; bool updated_isize; }; @@ -447,7 +449,8 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start, if (write && !(flags & IOMAP_NOWAIT)) { ret = btrfs_check_data_free_space(BTRFS_I(inode), &dio_data->data_reserved, - start, data_alloc_len, false); + start, data_alloc_len, false, + &dio_data->stripe_held); if (!ret) dio_data->data_space_reserved = true; else if (!(BTRFS_I(inode)->flags & @@ -534,6 +537,8 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start, if (write) { ret = btrfs_get_blocks_direct_write(&em, inode, dio_data, start, &len, flags); + btrfs_release_data_margin_probe(BTRFS_I(inode), dio_data->stripe_held); + dio_data->stripe_held = 0; if (ret < 0) goto unlock_err; /* Recalc len in case the new em is smaller than requested */ @@ -604,6 +609,8 @@ unlock_err: btrfs_clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend, EXTENT_LOCKED | EXTENT_DIO_LOCKED, &cached_state); err: + btrfs_release_data_margin_probe(BTRFS_I(inode), dio_data->stripe_held); + dio_data->stripe_held = 0; if (dio_data->data_space_reserved) { btrfs_free_reserved_data_space(BTRFS_I(inode), dio_data->data_reserved, diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 3c0b572786bdd..fd18356dff0db 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1130,9 +1130,11 @@ static ssize_t reserve_space(struct btrfs_inode *inode, const struct btrfs_fs_info *fs_info = inode->root->fs_info; const unsigned int block_offset = (start & (fs_info->sectorsize - 1)); size_t reserve_bytes; + u64 held = 0; int ret; - ret = btrfs_check_data_free_space(inode, data_reserved, start, *len, nowait); + ret = btrfs_check_data_free_space(inode, data_reserved, start, *len, nowait, + &held); if (ret < 0) { int can_nocow; @@ -1157,6 +1159,7 @@ static ssize_t reserve_space(struct btrfs_inode *inode, WARN_ON(reserve_bytes == 0); ret = btrfs_delalloc_reserve_metadata(inode, reserve_bytes, reserve_bytes, nowait); + btrfs_release_data_margin_probe(inode, held); if (ret) { if (!*only_release_metadata) btrfs_free_reserved_data_space(inode, *data_reserved, @@ -1909,6 +1912,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) struct btrfs_fs_info *fs_info = inode->root->fs_info; struct extent_io_tree *io_tree = &inode->io_tree; struct btrfs_ordered_extent *ordered; + u64 held = 0; struct extent_state *cached_state = NULL; struct extent_changeset *data_reserved = NULL; unsigned long zero_start; @@ -1937,7 +1941,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) * being processed by btrfs_page_mkwrite() function. */ ret = btrfs_check_data_free_space(inode, &data_reserved, page_start, - reserved_space, false); + reserved_space, false, &held); if (ret < 0) { size_t write_bytes = reserved_space; @@ -1957,6 +1961,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) } ret = btrfs_delalloc_reserve_metadata(inode, reserved_space, reserved_space, false); + btrfs_release_data_margin_probe(inode, held); if (ret < 0) { if (!only_release_metadata) btrfs_free_reserved_data_space(inode, data_reserved, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index b2f5d514dde2e..7eebf10009a30 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5089,6 +5089,7 @@ int btrfs_truncate_block(struct btrfs_inode *inode, u64 offset, u64 start, u64 e struct extent_changeset *data_reserved = NULL; bool only_release_metadata = false; u32 blocksize = fs_info->sectorsize; + u64 held = 0; pgoff_t index = (offset >> PAGE_SHIFT); struct folio *folio; gfp_t mask = btrfs_alloc_write_mask(mapping); @@ -5141,7 +5142,7 @@ int btrfs_truncate_block(struct btrfs_inode *inode, u64 offset, u64 start, u64 e block_end = block_start + blocksize - 1; ret = btrfs_check_data_free_space(inode, &data_reserved, block_start, - blocksize, false); + blocksize, false, &held); if (ret < 0) { size_t write_bytes = blocksize; @@ -5155,6 +5156,7 @@ int btrfs_truncate_block(struct btrfs_inode *inode, u64 offset, u64 start, u64 e } } ret = btrfs_delalloc_reserve_metadata(inode, blocksize, blocksize, false); + btrfs_release_data_margin_probe(inode, held); if (ret < 0) { if (!only_release_metadata) btrfs_free_reserved_data_space(inode, data_reserved,