return 0;
if (!set && (state->state & bits) == 0)
return 0;
+
changeset->bytes_changed += state->end - state->start + 1;
+ if (!extent_changeset_tracks_ranges(changeset))
+ return 0;
ret = ulist_add(&changeset->range_changed, state->start, state->end, GFP_ATOMIC);
if (ret < 0)
ulist_init(&changeset->range_changed);
}
+/*
+ * Sentinel value for range_changed.prealloc indicating that the changeset
+ * only tracks bytes_changed and does not record individual ranges. This
+ * avoids GFP_ATOMIC allocations inside add_extent_changeset() when the
+ * caller doesn't need to iterate the changed ranges afterwards.
+ */
+#define EXTENT_CHANGESET_BYTES_ONLY ((struct ulist_node *)1)
+
+static inline void extent_changeset_init_bytes_only(struct extent_changeset *changeset)
+{
+ changeset->bytes_changed = 0;
+ changeset->range_changed.prealloc = EXTENT_CHANGESET_BYTES_ONLY;
+}
+
+static inline bool extent_changeset_tracks_ranges(const struct extent_changeset *changeset)
+{
+ return changeset->range_changed.prealloc != EXTENT_CHANGESET_BYTES_ONLY;
+}
+
static inline struct extent_changeset *extent_changeset_alloc(void)
{
struct extent_changeset *ret;
static inline void extent_changeset_prealloc(struct extent_changeset *changeset, gfp_t gfp_mask)
{
+ ASSERT(extent_changeset_tracks_ranges(changeset));
ulist_prealloc(&changeset->range_changed, gfp_mask);
}
if (!changeset)
return;
changeset->bytes_changed = 0;
- ulist_release(&changeset->range_changed);
+ if (extent_changeset_tracks_ranges(changeset))
+ ulist_release(&changeset->range_changed);
}
static inline void extent_changeset_free(struct extent_changeset *changeset)
u64 freed = 0;
int ret;
- extent_changeset_init(&changeset);
+ extent_changeset_init_bytes_only(&changeset);
len = round_up(start + len, root->fs_info->sectorsize);
start = round_down(start, root->fs_info->sectorsize);
WARN_ON(!free && reserved);
if (free && reserved)
return qgroup_free_reserved_data(inode, reserved, start, len, released);
- extent_changeset_init(&changeset);
+ extent_changeset_init_bytes_only(&changeset);
ret = btrfs_clear_record_extent_bits(&inode->io_tree, start, start + len - 1,
EXTENT_QGROUP_RESERVED, &changeset);
if (ret < 0)
WARN_ON(ret < 0);
if (WARN_ON(changeset.bytes_changed)) {
+ ASSERT(extent_changeset_tracks_ranges(&changeset));
ULIST_ITER_INIT(&iter);
while ((unode = ulist_next(&changeset.range_changed, &iter))) {
btrfs_warn(inode->root->fs_info,