#include "file.h"
#include "super.h"
#include "print-tree.h"
+#include "space-info.h"
/*
* Unlock folio after btrfs_file_write() is done with it.
u64 alloc_start = round_down(offset, sectorsize);
u64 alloc_end = round_up(offset + len, sectorsize);
u64 bytes_to_reserve = 0;
+ u64 stripe_held = 0;
bool space_reserved = false;
em = btrfs_get_extent(BTRFS_I(inode), NULL, alloc_start,
if (ret < 0)
goto out;
space_reserved = true;
+ stripe_held = btrfs_stripe_prealloc_hold(fs_info);
btrfs_punch_hole_lock_range(inode, lockstart, lockend,
&cached_state);
ret = btrfs_qgroup_reserve_data(BTRFS_I(inode), &data_reserved,
alloc_end - alloc_start,
fs_info->sectorsize,
offset + len, &alloc_hint);
+ btrfs_stripe_prealloc_release(fs_info, stripe_held);
+ stripe_held = 0;
btrfs_unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend,
&cached_state);
/* btrfs_prealloc_file_range releases reserved space on error */
}
ret = btrfs_fallocate_update_isize(inode, offset + len, mode);
out:
+ btrfs_stripe_prealloc_release(fs_info, stripe_held);
if (ret && space_reserved)
btrfs_free_reserved_data_space(BTRFS_I(inode), data_reserved,
alloc_start, bytes_to_reserve);
u64 actual_end = 0;
u64 data_space_needed = 0;
u64 data_space_reserved = 0;
+ u64 stripe_held = 0;
u64 qgroup_reserved = 0;
struct extent_map *em;
int blocksize = BTRFS_I(inode)->root->fs_info->sectorsize;
*/
ret = btrfs_alloc_data_chunk_ondemand(BTRFS_I(inode),
data_space_needed);
- if (!ret)
+ if (!ret) {
data_space_reserved = data_space_needed;
+ stripe_held = btrfs_stripe_prealloc_hold(inode_to_fs_info(inode));
+ }
}
/*
list_del(&range->list);
kfree(range);
}
+ btrfs_stripe_prealloc_release(inode_to_fs_info(inode), stripe_held);
if (ret < 0)
goto out_unlock;
struct folio **folios;
struct btrfs_key ins;
bool extent_reserved = false;
+ u64 stripe_held = 0;
struct extent_map *em;
ssize_t ret;
ret = btrfs_alloc_data_chunk_ondemand(inode, disk_num_bytes);
if (ret)
goto out_unlock;
+ stripe_held = btrfs_stripe_prealloc_hold(fs_info);
ret = btrfs_qgroup_reserve_data(inode, &data_reserved, start, num_bytes);
if (ret)
goto out_free_data_space;
ret = btrfs_reserve_extent(root, inode, disk_num_bytes, disk_num_bytes,
disk_num_bytes, 0, 0, &ins, 1, 1, false);
+ btrfs_stripe_prealloc_release(fs_info, stripe_held);
+ stripe_held = 0;
if (ret)
goto out_delalloc_release;
extent_reserved = true;
if (ret < 0)
btrfs_qgroup_free_data(inode, data_reserved, start, num_bytes, NULL);
out_free_data_space:
+ btrfs_stripe_prealloc_release(fs_info, stripe_held);
/*
* If btrfs_reserve_extent() succeeded, then we already decremented
* bytes_may_use.
u64 prealloc_start = cluster->start - offset;
u64 prealloc_end = cluster->end - offset;
u64 cur_offset = prealloc_start;
+ u64 stripe_held;
/*
* For blocksize < folio size case (either bs < page size or large folios),
prealloc_end + 1 - prealloc_start);
if (ret)
return ret;
+ stripe_held = btrfs_stripe_prealloc_hold(inode->root->fs_info);
btrfs_inode_lock(inode, 0);
for (nr = 0; nr < cluster->nr; nr++) {
break;
}
btrfs_inode_unlock(inode, 0);
+ btrfs_stripe_prealloc_release(inode->root->fs_info, stripe_held);
if (cur_offset < prealloc_end)
btrfs_free_reserved_data_space_noquota(inode,
spin_unlock(&sinfo->lock);
}
+/*
+ * The preallocation twin of the delalloc margin. A preallocation has no
+ * outstanding extent to carry a margin, yet its allocation claims
+ * round_up(bytes, stripe) whole stripes: admission charges the roundup
+ * (stripe_claimable_admit()), but between the admission and the claim only
+ * the raw bytes sit in bytes_may_use, and several preallocations in flight
+ * together overran the supply behind writes admitted earlier. Hold one
+ * stripe in the margin from a successful data reservation until the
+ * caller's allocations are done. Returns the bytes held, 0 when the gate
+ * is not in effect; the caller hands it back to the release.
+ */
+u64 btrfs_stripe_prealloc_hold(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_space_info *sinfo = fs_info->data_sinfo;
+ const u64 unit = READ_ONCE(fs_info->stripe_margin_unit);
+
+ if (!unit || !sinfo || !btrfs_test_opt(fs_info, STRIPE_ALLOC))
+ return 0;
+ spin_lock(&sinfo->lock);
+ sinfo->bytes_stripe_margin += unit;
+ spin_unlock(&sinfo->lock);
+ return unit;
+}
+
+void btrfs_stripe_prealloc_release(struct btrfs_fs_info *fs_info, u64 held)
+{
+ struct btrfs_space_info *sinfo = fs_info->data_sinfo;
+
+ if (!held || !sinfo)
+ return;
+ spin_lock(&sinfo->lock);
+ sinfo->bytes_stripe_margin -= min(held, sinfo->bytes_stripe_margin);
+ btrfs_try_granting_tickets(sinfo);
+ spin_unlock(&sinfo->lock);
+}
+
struct btrfs_space_info *btrfs_find_space_info(struct btrfs_fs_info *info,
u64 flags)
int btrfs_init_space_info(struct btrfs_fs_info *fs_info);
void btrfs_stripe_margin_mod(struct btrfs_inode *inode, int mod);
+u64 btrfs_stripe_prealloc_hold(struct btrfs_fs_info *fs_info);
+void btrfs_stripe_prealloc_release(struct btrfs_fs_info *fs_info, u64 held);
void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info,
struct btrfs_block_group *block_group);
void btrfs_update_space_info_chunk_size(struct btrfs_space_info *space_info,