}
int btrfs_alloc_data_chunk_ondemand(const struct btrfs_inode *inode, u64 bytes)
+{
+ return btrfs_alloc_data_chunk_ondemand_held(inode, bytes, NULL);
+}
+
+/*
+ * With @stripe_held: a preallocation, which claims whole stripes under
+ * stripe_alloc; the reservation charges one stripe of collateral together
+ * with the bytes and returns it for btrfs_stripe_prealloc_release().
+ */
+int btrfs_alloc_data_chunk_ondemand_held(const struct btrfs_inode *inode,
+ u64 bytes, u64 *stripe_held)
{
struct btrfs_root *root = inode->root;
struct btrfs_fs_info *fs_info = root->fs_info;
if (btrfs_is_free_space_inode(inode))
flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE;
- return btrfs_reserve_data_bytes(data_sinfo_for_inode(inode), bytes, flush);
+ return btrfs_reserve_data_bytes_held(data_sinfo_for_inode(inode), bytes,
+ flush, stripe_held);
}
int btrfs_check_data_free_space(struct btrfs_inode *inode,
* 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)
+/*
+ * Charge one whole stripe of collateral for a preallocation, under
+ * space_info->lock and together with the bytes it is admitted for: the
+ * caller's allocations claim round_up(bytes, stripe) whole stripes, and the
+ * hold keeps that stripe out of the supply the gate shows to everyone else
+ * until the claims land (btrfs_stripe_prealloc_release()). Charged after
+ * the admission, with the lock dropped in between, two sub-stripe
+ * preallocations could both be admitted against one stripe of supply and a
+ * buffered write admitted alongside them found no stripe at writeback.
+ */
+static u64 stripe_hold_locked(struct btrfs_space_info *space_info)
{
- struct btrfs_space_info *sinfo = fs_info->data_sinfo;
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
const u64 unit = READ_ONCE(fs_info->stripe_margin_unit);
- if (!unit || !sinfo || !btrfs_test_opt(fs_info, STRIPE_ALLOC))
+ lockdep_assert_held(&space_info->lock);
+ if (!unit || !(space_info->flags & BTRFS_BLOCK_GROUP_DATA) ||
+ !btrfs_test_opt(fs_info, STRIPE_ALLOC))
return 0;
- spin_lock(&sinfo->lock);
- sinfo->bytes_stripe_margin += unit;
- spin_unlock(&sinfo->lock);
+ space_info->bytes_stripe_margin += unit;
return unit;
}
flush, ticket->reloc)) &&
stripe_claimable_admit(space_info, ticket->bytes, false)) {
btrfs_space_info_update_bytes_may_use(space_info, ticket->bytes);
+ if (ticket->hold_stripe)
+ ticket->held = stripe_hold_locked(space_info);
remove_ticket(space_info, ticket);
ticket->bytes = 0;
space_info->tickets_id++;
*/
static int __reserve_bytes(struct btrfs_fs_info *fs_info,
struct btrfs_space_info *space_info, u64 orig_bytes,
- enum btrfs_reserve_flush_enum flush)
+ enum btrfs_reserve_flush_enum flush, u64 *held)
{
struct work_struct *async_work;
struct reserve_ticket ticket;
stripe_claimable_admit(space_info, orig_bytes,
flush == BTRFS_RESERVE_FLUSH_DATA_RELOC)) {
btrfs_space_info_update_bytes_may_use(space_info, orig_bytes);
+ if (held)
+ *held = stripe_hold_locked(space_info);
ret = 0;
}
init_waitqueue_head(&ticket.wait);
ticket.steal = can_steal(flush);
ticket.reloc = reloc;
+ ticket.hold_stripe = held != NULL;
+ ticket.held = 0;
if (trace_btrfs_reserve_ticket_enabled())
start_ns = ktime_get_ns();
if (!ret || !can_ticket(flush))
return ret;
- return handle_reserve_ticket(fs_info, space_info, &ticket, start_ns,
- orig_bytes, flush);
+ ret = handle_reserve_ticket(fs_info, space_info, &ticket, start_ns,
+ orig_bytes, flush);
+ if (!ret && held)
+ *held = ticket.held;
+ return ret;
}
/*
{
int ret;
- ret = __reserve_bytes(fs_info, space_info, orig_bytes, flush);
+ ret = __reserve_bytes(fs_info, space_info, orig_bytes, flush, NULL);
if (ret == -ENOSPC) {
trace_btrfs_space_reservation(fs_info, "space_info:enospc",
space_info->flags, orig_bytes, 1);
*/
int btrfs_reserve_data_bytes(struct btrfs_space_info *space_info, u64 bytes,
enum btrfs_reserve_flush_enum flush)
+{
+ return btrfs_reserve_data_bytes_held(space_info, bytes, flush, NULL);
+}
+
+/*
+ * Like btrfs_reserve_data_bytes(), for a preallocation: with @held the
+ * reservation also charges one whole stripe of stripe_alloc collateral,
+ * atomically with the bytes, and returns it for
+ * btrfs_stripe_prealloc_release() once the caller's allocations are done.
+ */
+int btrfs_reserve_data_bytes_held(struct btrfs_space_info *space_info, u64 bytes,
+ enum btrfs_reserve_flush_enum flush, u64 *held)
{
struct btrfs_fs_info *fs_info = space_info->fs_info;
int ret;
(flush != BTRFS_RESERVE_FLUSH_DATA &&
flush != BTRFS_RESERVE_FLUSH_DATA_RELOC));
- ret = __reserve_bytes(fs_info, space_info, bytes, flush);
+ if (held)
+ *held = 0;
+ ret = __reserve_bytes(fs_info, space_info, bytes, flush, held);
if (ret == -ENOSPC) {
trace_btrfs_space_reservation(fs_info, "space_info:enospc",
space_info->flags, bytes, 1);