* this block group can satisfy the allocation, or -ENOMEM.
*/
int btrfs_alloc_from_inode_stripe_run(struct btrfs_block_group *bg, u64 ino,
+ enum btrfs_stripe_run_class class,
u64 num_bytes, u64 *ret_offset,
u64 *available)
{
spin_lock_irqsave(&bg->stripe_run_lock, flags);
list_for_each_entry(run, &bg->open_stripe_runs, list) {
- if (run->class != BTRFS_STRIPE_RUN_LOG || run->owner != ino ||
- !run->open)
+ if (run->class != class || run->owner != ino || !run->open)
continue;
if (num_bytes <= run->end - run->offset) {
*ret_offset = run->offset;
goto out;
}
new_run->bg = bg;
- new_run->class = BTRFS_STRIPE_RUN_LOG;
+ new_run->class = class;
new_run->owner = ino;
new_run->start = start;
new_run->end = start + len;
* own stripes.
*/
BTRFS_STRIPE_RUN_LOG,
+ /*
+ * Private per-inode runs for nodatacow files' extents and
+ * preallocated extents. These extents are candidates for future
+ * write-in-place (which reintroduces the write hole for the stripes
+ * it touches), so they must never share a stripe with any other
+ * file's data: per-inode scoping confines a future in-place write's
+ * blast radius to the owning file alone.
+ */
+ BTRFS_STRIPE_RUN_NOCOW,
BTRFS_STRIPE_RUN_NR_CLASSES,
};
enum btrfs_stripe_run_class class,
u64 *ret_offset, u64 *available);
int btrfs_alloc_from_inode_stripe_run(struct btrfs_block_group *bg, u64 ino,
+ enum btrfs_stripe_run_class class,
u64 num_bytes, u64 *ret_offset,
u64 *available);
void btrfs_open_stripe_write_done(struct btrfs_block_group *bg, u64 start,
alloc_hint = btrfs_get_extent_allocation_hint(inode, start, len);
again:
ret = btrfs_reserve_extent(root, inode, len, len, fs_info->sectorsize,
- 0, alloc_hint, &ins, true, true);
+ 0, alloc_hint, &ins, true, true, false);
if (ret == -EAGAIN) {
ASSERT(btrfs_is_zoned(fs_info));
wait_on_bit_io(&inode->root->fs_info->flags, BTRFS_FS_NEED_ZONE_FINISH,
return 1;
/*
- * A log-active inode allocates from its own private LOG-class run so
- * its log commits settle only its own stripes. If this block group
- * has no fully-free stripes left for a private run, fall through to
- * the shared runs: the data is then placed exactly as before 3b.1
- * and the log-commit settling keeps it safe, just less cheaply.
+ * Steered allocations come from the inode's own private run of the
+ * requested class: a log-active inode's log commits then settle only
+ * its own stripes, and nocow/prealloc extents never share a stripe
+ * with another file's data. If this block group has no fully-free
+ * stripes left for a private run, fall through to the shared runs --
+ * safe in both cases: log-commit settling covers any placement, and
+ * a nocow/prealloc extent that lands in a shared stripe simply stays
+ * force-COWed.
*/
- if (ffe_ctl->for_log_inode) {
- struct btrfs_inode *log_inode = ffe_ctl->for_log_inode;
+ if (ffe_ctl->steer_inode) {
+ struct btrfs_inode *steer_inode = ffe_ctl->steer_inode;
ret = btrfs_alloc_from_inode_stripe_run(block_group,
- btrfs_ino(log_inode), ffe_ctl->num_bytes,
- &offset, &available);
+ btrfs_ino(steer_inode), ffe_ctl->steer_class,
+ ffe_ctl->num_bytes, &offset, &available);
if (ret == -ENOMEM)
return ret;
if (!ret) {
- WRITE_ONCE(log_inode->log_run_hint, offset);
+ WRITE_ONCE(steer_inode->log_run_hint, offset);
ffe_ctl->found_offset = offset;
ffe_ctl->search_start = offset;
return 0;
int btrfs_reserve_extent(struct btrfs_root *root, struct btrfs_inode *inode,
u64 ram_bytes, u64 num_bytes, u64 min_alloc_size,
u64 empty_size, u64 hint_byte,
- struct btrfs_key *ins, bool is_data, bool delalloc)
+ struct btrfs_key *ins, bool is_data, bool delalloc,
+ bool for_prealloc)
{
struct btrfs_fs_info *fs_info = root->fs_info;
struct find_free_extent_ctl ffe_ctl = {};
int ret;
bool for_treelog = (btrfs_root_id(root) == BTRFS_TREE_LOG_OBJECTID);
bool for_data_reloc = (btrfs_is_data_reloc_root(root) && is_data);
- struct btrfs_inode *for_log_inode = NULL;
+ struct btrfs_inode *steer_inode = NULL;
+ enum btrfs_stripe_run_class steer_class = BTRFS_STRIPE_RUN_COW;
/*
- * Steer a log-active inode's datacow data into its private LOG-class
- * stripe run, seeded with the run's location as the search hint; see
- * BTRFS_INODE_LOG_ALLOC. Purely placement: any allocation that
+ * Steer this inode's data into a private per-inode stripe run,
+ * seeded with the run's location as the search hint. NOCOW class
+ * for preallocated extents and nodatacow files' extents (candidates
+ * for future write-in-place, which must never share a stripe with
+ * any other file's data); LOG class for log-active inodes (see
+ * BTRFS_INODE_LOG_ALLOC). Purely placement: any allocation that
* cannot be served from a private run falls back to the shared runs.
*/
if (inode && is_data && !for_data_reloc &&
- btrfs_test_opt(fs_info, STRIPE_ALLOC) &&
- test_bit(BTRFS_INODE_LOG_ALLOC, &inode->runtime_flags)) {
- u64 log_hint = READ_ONCE(inode->log_run_hint);
+ btrfs_test_opt(fs_info, STRIPE_ALLOC)) {
+ if (for_prealloc || (inode->flags & BTRFS_INODE_NODATACOW)) {
+ steer_inode = inode;
+ steer_class = BTRFS_STRIPE_RUN_NOCOW;
+ } else if (test_bit(BTRFS_INODE_LOG_ALLOC,
+ &inode->runtime_flags)) {
+ steer_inode = inode;
+ steer_class = BTRFS_STRIPE_RUN_LOG;
+ }
+ if (steer_inode) {
+ u64 run_hint = READ_ONCE(inode->log_run_hint);
- for_log_inode = inode;
- if (log_hint)
- hint_byte = log_hint;
+ if (run_hint)
+ hint_byte = run_hint;
+ }
}
flags = get_alloc_profile_by_root(root, is_data);
ffe_ctl.hint_byte = hint_byte;
ffe_ctl.for_treelog = for_treelog;
ffe_ctl.for_data_reloc = for_data_reloc;
- ffe_ctl.for_log_inode = for_log_inode;
+ ffe_ctl.steer_inode = steer_inode;
+ ffe_ctl.steer_class = steer_class;
ret = find_free_extent(root, ins, &ffe_ctl);
if (!ret && !is_data) {
return ERR_CAST(block_rsv);
ret = btrfs_reserve_extent(root, NULL, blocksize, blocksize, blocksize,
- empty_size, hint, &ins, false, false);
+ empty_size, hint, &ins, false, false, false);
if (ret)
goto out_unuse;
bool for_data_reloc;
/*
- * Data allocation for a log-active inode under stripe-exclusive
- * allocation: steer it into the inode's private LOG-class stripe run.
+ * Data allocation to steer into a private per-inode stripe run under
+ * stripe-exclusive allocation (LOG class for log-active inodes,
+ * NOCOW class for nodatacow files' and preallocated extents), with
+ * the class to use.
*/
- struct btrfs_inode *for_log_inode;
+ struct btrfs_inode *steer_inode;
+ enum btrfs_stripe_run_class steer_class;
/*
* Set to true if we're retrying the allocation on this block group
int btrfs_reserve_extent(struct btrfs_root *root, struct btrfs_inode *inode,
u64 ram_bytes, u64 num_bytes,
u64 min_alloc_size, u64 empty_size, u64 hint_byte,
- struct btrfs_key *ins, bool is_data, bool delalloc);
+ struct btrfs_key *ins, bool is_data, bool delalloc,
+ bool for_prealloc);
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf, bool full_backref);
int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
compressed_size = async_extent->cb->bbio.bio.bi_iter.bi_size;
ret = btrfs_reserve_extent(root, inode, async_extent->ram_size,
compressed_size, compressed_size,
- 0, *alloc_hint, &ins, true, true);
+ 0, *alloc_hint, &ins, true, true, false);
if (ret) {
/*
* We can't reserve contiguous space for the compressed size.
ret = btrfs_reserve_extent(root, inode, num_bytes, num_bytes,
min_alloc_size, 0, alloc_hint, ins, true,
- true);
+ true, false);
if (ret < 0) {
*ret_alloc_size = cur_len;
return ret;
* sized chunks.
*/
cur_bytes = min(cur_bytes, last_alloc);
- ret = btrfs_reserve_extent(root, NULL, cur_bytes, cur_bytes,
- min_size, 0, *alloc_hint, &ins, true, false);
+ ret = btrfs_reserve_extent(root, BTRFS_I(inode), cur_bytes,
+ cur_bytes, min_size, 0, *alloc_hint, &ins,
+ true, false, true);
if (ret)
break;
/*
}
ret = btrfs_reserve_extent(root, inode, disk_num_bytes, disk_num_bytes,
- disk_num_bytes, 0, 0, &ins, true, true);
+ disk_num_bytes, 0, 0, &ins, true, true,
+ false);
if (ret)
goto out_delalloc_release;
extent_reserved = true;
min_size = fs_info->nodesize;
ret = btrfs_reserve_extent(fs_info->fs_root, NULL, length, length,
- min_size, 0, 0, &ins, is_data, false);
+ min_size, 0, 0, &ins, is_data, false, false);
if (unlikely(ret)) {
spin_lock(&sinfo->lock);
btrfs_space_info_update_bytes_may_use(sinfo, -length);
* rest of it the next time round.
*/
ret = btrfs_reserve_extent(fs_info->fs_root, NULL, remap_length,
- remap_length, min_size, 0, 0, &ins, is_data, false);
+ remap_length, min_size, 0, 0, &ins, is_data,
+ false, false);
if (ret) {
spin_lock(&sinfo->lock);
btrfs_space_info_update_bytes_may_use(sinfo, -remap_length);