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) &&
* 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);
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;
* 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)) {
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;
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.
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);
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,
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;
};
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 &
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 */
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,
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;
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,
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;
* 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;
}
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,
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);
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;
}
}
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,