summaryrefslogtreecommitdiff
path: root/fs/btrfs
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-14 13:36:19 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-14 13:36:19 +0200
commitb94a3879cf84d98cee119045bee6538659bfc2ce (patch)
tree6ad8bdff7057bc0d27865fc80eecb8b597adc196 /fs/btrfs
parent70db9eace66c4932f17d42640fad17f561aa20da (diff)
parent8f3741e6feb045da5b406df0a80b42a1adfb289b (diff)
downloadlinux-rolling-lts.tar.gz
linux-rolling-lts.zip
Merge v6.18.52linux-rolling-lts
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/bio.c68
-rw-r--r--fs/btrfs/bio.h5
-rw-r--r--fs/btrfs/block-group.c3
-rw-r--r--fs/btrfs/ctree.c3
-rw-r--r--fs/btrfs/defrag.c50
-rw-r--r--fs/btrfs/delayed-inode.c4
-rw-r--r--fs/btrfs/disk-io.c53
-rw-r--r--fs/btrfs/extent-io-tree.c10
-rw-r--r--fs/btrfs/extent-tree.c10
-rw-r--r--fs/btrfs/extent_io.c31
-rw-r--r--fs/btrfs/extent_io.h23
-rw-r--r--fs/btrfs/fiemap.c4
-rw-r--r--fs/btrfs/file.c101
-rw-r--r--fs/btrfs/free-space-tree.c4
-rw-r--r--fs/btrfs/inode-item.c7
-rw-r--r--fs/btrfs/inode.c13
-rw-r--r--fs/btrfs/qgroup.c37
-rw-r--r--fs/btrfs/relocation.c5
-rw-r--r--fs/btrfs/verity.c16
-rw-r--r--fs/btrfs/volumes.c9
20 files changed, 260 insertions, 196 deletions
diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 7d8aaa019c8c..afe022a3b8fc 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -172,7 +172,21 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
struct btrfs_inode *inode = repair_bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct bio_vec *bv = bio_first_bvec_all(&repair_bbio->bio);
+ /*
+ * We can not move forward the saved_iter, as it will be later
+ * utilized by repair_bbio again.
+ */
+ struct bvec_iter saved_iter = repair_bbio->saved_iter;
+ const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
+ const u64 logical = repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT;
+ const u32 nr_steps = repair_bbio->saved_iter.bi_size / step;
int mirror = repair_bbio->mirror_num;
+ phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
+ phys_addr_t paddr;
+ unsigned int slot = 0;
+
+ /* Repair bbio should be eaxctly one block sized. */
+ ASSERT(repair_bbio->saved_iter.bi_size == fs_info->sectorsize);
if (repair_bbio->bio.bi_status ||
!btrfs_data_csum_ok(repair_bbio, dev, 0, bvec_phys(bv))) {
@@ -190,12 +204,17 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
return;
}
+ btrfs_bio_for_each_block(paddr, &repair_bbio->bio, &saved_iter, step) {
+ ASSERT(slot < nr_steps);
+ paddrs[slot] = paddr;
+ slot++;
+ }
+
do {
mirror = prev_repair_mirror(fbio, mirror);
btrfs_repair_io_failure(fs_info, btrfs_ino(inode),
repair_bbio->file_offset, fs_info->sectorsize,
- repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT,
- bvec_phys(bv), mirror);
+ logical, paddrs, step, mirror);
} while (mirror != fbio->bbio->mirror_num);
done:
@@ -865,18 +884,36 @@ void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num)
*
* The I/O is issued synchronously to block the repair read completion from
* freeing the bio.
+ *
+ * @ino: Offending inode number
+ * @fileoff: File offset inside the inode
+ * @length: Length of the repair write
+ * @logical: Logical address of the range
+ * @paddrs: Physical address array of the content
+ * @step: Length of for each paddrs
+ * @mirror_num: Mirror number to write to. Must not be zero
*/
-int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start,
- u64 length, u64 logical, phys_addr_t paddr, int mirror_num)
+int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
+ u32 length, u64 logical, const phys_addr_t paddrs[],
+ unsigned int step, int mirror_num)
{
+ const u32 nr_steps = DIV_ROUND_UP_POW2(length, step);
struct btrfs_io_stripe smap = { 0 };
- struct bio_vec bvec;
- struct bio bio;
+ struct bio *bio = NULL;
int ret = 0;
ASSERT(!(fs_info->sb->s_flags & SB_RDONLY));
BUG_ON(!mirror_num);
+ /* Basic alignment checks. */
+ ASSERT(IS_ALIGNED(logical, fs_info->sectorsize));
+ ASSERT(IS_ALIGNED(length, fs_info->sectorsize));
+ ASSERT(IS_ALIGNED(fileoff, fs_info->sectorsize));
+ /* Either it's a single data or metadata block. */
+ ASSERT(length <= BTRFS_MAX_BLOCKSIZE);
+ ASSERT(step <= length);
+ ASSERT(is_power_of_2(step));
+
if (btrfs_repair_one_zone(fs_info, logical))
return 0;
@@ -896,24 +933,27 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start,
goto out_counter_dec;
}
- bio_init(&bio, smap.dev->bdev, &bvec, 1, REQ_OP_WRITE | REQ_SYNC);
- bio.bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
- __bio_add_page(&bio, phys_to_page(paddr), length, offset_in_page(paddr));
- ret = submit_bio_wait(&bio);
+ bio = bio_alloc(smap.dev->bdev, nr_steps, REQ_OP_WRITE | REQ_SYNC, GFP_NOFS);
+ bio->bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
+ for (int i = 0; i < nr_steps; i++) {
+ ret = bio_add_page(bio, phys_to_page(paddrs[i]), step, offset_in_page(paddrs[i]));
+ /* We should have allocated enough slots to contain all the different pages. */
+ ASSERT(ret == step);
+ }
+ ret = submit_bio_wait(bio);
+ bio_put(bio);
if (ret) {
/* try to remap that extent elsewhere? */
btrfs_dev_stat_inc_and_print(smap.dev, BTRFS_DEV_STAT_WRITE_ERRS);
- goto out_bio_uninit;
+ goto out_counter_dec;
}
btrfs_info_rl(fs_info,
"read error corrected: ino %llu off %llu (dev %s sector %llu)",
- ino, start, btrfs_dev_name(smap.dev),
+ ino, fileoff, btrfs_dev_name(smap.dev),
smap.physical >> SECTOR_SHIFT);
ret = 0;
-out_bio_uninit:
- bio_uninit(&bio);
out_counter_dec:
btrfs_bio_counter_dec(fs_info);
return ret;
diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h
index 69fe54f564fc..fd09b6501c96 100644
--- a/fs/btrfs/bio.h
+++ b/fs/btrfs/bio.h
@@ -127,7 +127,8 @@ void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status);
void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num);
void btrfs_submit_repair_write(struct btrfs_bio *bbio, int mirror_num, bool dev_replace);
-int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start,
- u64 length, u64 logical, phys_addr_t paddr, int mirror_num);
+int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
+ u32 length, u64 logical, const phys_addr_t paddrs[],
+ unsigned int step, int mirror_num);
#endif
diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index 93e0063bb1fa..e9ee10bee2ec 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -1057,8 +1057,7 @@ static int remove_block_group_item(struct btrfs_trans_handle *trans,
if (ret < 0)
return ret;
- ret = btrfs_del_item(trans, root, path);
- return ret;
+ return btrfs_del_item(trans, root, path);
}
int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 0f2e9f33dd0c..c7c2e4405c47 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -3990,8 +3990,7 @@ int btrfs_split_item(struct btrfs_trans_handle *trans,
if (ret)
return ret;
- ret = split_item(trans, path, new_key, split_offset);
- return ret;
+ return split_item(trans, path, new_key, split_offset);
}
/*
diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c
index 2e3c011d410a..c59548a0b9c5 100644
--- a/fs/btrfs/defrag.c
+++ b/fs/btrfs/defrag.c
@@ -1150,20 +1150,15 @@ static_assert(PAGE_ALIGNED(CLUSTER_SIZE));
*
* - Extent bits are locked
*/
-static int defrag_one_locked_target(struct btrfs_inode *inode,
- struct defrag_target_range *target,
- struct folio **folios, int nr_pages,
- struct extent_state **cached_state)
+static void defrag_one_locked_target(struct btrfs_inode *inode,
+ struct defrag_target_range *target,
+ struct folio **folios, int nr_pages,
+ struct extent_state **cached_state)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- struct extent_changeset *data_reserved = NULL;
const u64 start = target->start;
const u64 len = target->len;
- int ret = 0;
- ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start, len);
- if (ret < 0)
- return ret;
btrfs_clear_extent_bit(&inode->io_tree, start, start + len - 1,
EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING |
EXTENT_DEFRAG, cached_state);
@@ -1184,10 +1179,6 @@ static int defrag_one_locked_target(struct btrfs_inode *inode,
btrfs_folio_clamp_clear_checked(fs_info, folio, start, len);
btrfs_folio_clamp_set_dirty(fs_info, folio, start, len);
}
- btrfs_delalloc_release_extents(inode, len);
- extent_changeset_free(data_reserved);
-
- return ret;
}
static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
@@ -1203,6 +1194,8 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
u64 cur = start;
const unsigned int nr_pages = ((start + len - 1) >> PAGE_SHIFT) -
(start >> PAGE_SHIFT) + 1;
+ struct extent_changeset *data_reserved = NULL;
+ u64 last_defrag_end = start;
int ret = 0;
ASSERT(nr_pages <= CLUSTER_SIZE / PAGE_SIZE);
@@ -1212,6 +1205,22 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
if (!folios)
return -ENOMEM;
+ /*
+ * Reserve delalloc space before locking the range and before locking
+ * and dirtying any folios - otherwise we could deadlock, for example
+ * after defrag of one range we dirty folios and keep them locked when
+ * we move to the next range, so reserving delalloc space right before
+ * each range could trigger flushing of delalloc and deadlock on the
+ * extent lock or trigger a transaction commit with flushoncommit, which
+ * can either deadlock on the lock of a folio made dirty in the previous
+ * range or the extent lock.
+ */
+ ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start, len);
+ if (ret < 0) {
+ kfree(folios);
+ return ret;
+ }
+
/* Prepare all pages */
for (int i = 0; cur < start + len && i < nr_pages; i++) {
folios[i] = defrag_prepare_one_folio(inode, cur >> PAGE_SHIFT);
@@ -1246,10 +1255,11 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
goto unlock_extent;
list_for_each_entry(entry, &target_list, list) {
- ret = defrag_one_locked_target(inode, entry, folios, nr_pages,
- &cached_state);
- if (ret < 0)
- break;
+ defrag_one_locked_target(inode, entry, folios, nr_pages, &cached_state);
+ if (entry->start > last_defrag_end)
+ btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end,
+ entry->start - last_defrag_end, true);
+ last_defrag_end = entry->start + entry->len;
}
list_for_each_entry_safe(entry, tmp, &target_list, list) {
@@ -1266,6 +1276,12 @@ free_folios:
folio_put(folios[i]);
}
kfree(folios);
+ btrfs_delalloc_release_extents(inode, len);
+ if (last_defrag_end < start + len)
+ btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end,
+ start + len - last_defrag_end, true);
+ extent_changeset_free(data_reserved);
+
return ret;
}
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index ea48706a3d81..e1d1f71153fc 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1140,8 +1140,8 @@ __btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
ret = btrfs_record_root_in_trans(trans, node->root);
if (ret)
return ret;
- ret = btrfs_update_delayed_inode(trans, node->root, path, node);
- return ret;
+
+ return btrfs_update_delayed_inode(trans, node->root, path, node);
}
/*
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 655eed981078..15b6a67d17dd 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -183,27 +183,32 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb,
int mirror_num)
{
struct btrfs_fs_info *fs_info = eb->fs_info;
- int ret = 0;
+ const u32 step = min(fs_info->nodesize, PAGE_SIZE);
+ const u32 nr_steps = eb->len / step;
+ phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
if (sb_rdonly(fs_info->sb))
return -EROFS;
- for (int i = 0; i < num_extent_folios(eb); i++) {
+ for (int i = 0; i < num_extent_pages(eb); i++) {
struct folio *folio = eb->folios[i];
- u64 start = max_t(u64, eb->start, folio_pos(folio));
- u64 end = min_t(u64, eb->start + eb->len,
- folio_pos(folio) + eb->folio_size);
- u32 len = end - start;
- phys_addr_t paddr = PFN_PHYS(folio_pfn(folio)) +
- offset_in_folio(folio, start);
-
- ret = btrfs_repair_io_failure(fs_info, 0, start, len, start,
- paddr, mirror_num);
- if (ret)
- break;
+
+ /* No large folio support yet. */
+ ASSERT(folio_order(folio) == 0);
+ ASSERT(i < nr_steps);
+
+ /*
+ * For nodesize < page size, there is just one paddr, with some
+ * offset inside the page.
+ *
+ * For nodesize >= page size, it's one or more paddrs, and eb->start
+ * must be aligned to page boundary.
+ */
+ paddrs[i] = page_to_phys(&folio->page) + offset_in_page(eb->start);
}
- return ret;
+ return btrfs_repair_io_failure(fs_info, 0, eb->start, eb->len,
+ eb->start, paddrs, step, mirror_num);
}
/*
@@ -273,14 +278,15 @@ int btree_csum_one_bio(struct btrfs_bio *bbio)
return -EIO;
/*
- * If an extent_buffer is marked as EXTENT_BUFFER_ZONED_ZEROOUT, don't
- * checksum it but zero-out its content. This is done to preserve
- * ordering of I/O without unnecessarily writing out data.
+ * An extent_buffer marked EXTENT_BUFFER_ZONED_ZEROOUT is written out as
+ * zeros to preserve ordering of I/O without persisting the now
+ * unnecessary block. The bio is fed from the shared zero page (see
+ * write_one_eb()), so there is nothing to checksum here. Crucially, the
+ * buffer's own content is left intact: it may still be referenced, e.g.
+ * btrfs_free_tree_block() reads its header to add a delayed reference.
*/
- if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) {
- memzero_extent_buffer(eb, 0, eb->len);
+ if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags))
return 0;
- }
if (WARN_ON_ONCE(found_start != eb->start))
return -EIO;
@@ -2183,11 +2189,10 @@ static int load_global_roots(struct btrfs_root *tree_root)
return ret;
if (!btrfs_fs_compat_ro(tree_root->fs_info, FREE_SPACE_TREE))
return ret;
- ret = load_global_roots_objectid(tree_root, path,
- BTRFS_FREE_SPACE_TREE_OBJECTID,
- "free space");
- return ret;
+ return load_global_roots_objectid(tree_root, path,
+ BTRFS_FREE_SPACE_TREE_OBJECTID,
+ "free space");
}
static int btrfs_read_roots(struct btrfs_fs_info *fs_info)
diff --git a/fs/btrfs/extent-io-tree.c b/fs/btrfs/extent-io-tree.c
index bb2ca1c9c7b0..2a2bce0f1f7c 100644
--- a/fs/btrfs/extent-io-tree.c
+++ b/fs/btrfs/extent-io-tree.c
@@ -187,18 +187,18 @@ static int add_extent_changeset(struct extent_state *state, u32 bits,
struct extent_changeset *changeset,
int set)
{
- int ret;
-
if (!changeset)
return 0;
if (set && (state->state & bits) == bits)
return 0;
if (!set && (state->state & bits) == 0)
return 0;
+
changeset->bytes_changed += state->end - state->start + 1;
- ret = ulist_add(&changeset->range_changed, state->start, state->end,
- GFP_ATOMIC);
- return ret;
+ if (!extent_changeset_tracks_ranges(changeset))
+ return 0;
+
+ return ulist_add(&changeset->range_changed, state->start, state->end, GFP_ATOMIC);
}
static inline struct extent_state *next_state(struct extent_state *state)
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index d2d96f366dac..7c199d59a546 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2557,7 +2557,6 @@ static u64 get_alloc_profile_by_root(struct btrfs_root *root, int data)
{
struct btrfs_fs_info *fs_info = root->fs_info;
u64 flags;
- u64 ret;
if (data)
flags = BTRFS_BLOCK_GROUP_DATA;
@@ -2566,8 +2565,7 @@ static u64 get_alloc_profile_by_root(struct btrfs_root *root, int data)
else
flags = BTRFS_BLOCK_GROUP_METADATA;
- ret = btrfs_get_alloc_profile(fs_info, flags);
- return ret;
+ return btrfs_get_alloc_profile(fs_info, flags);
}
static u64 first_logical_byte(struct btrfs_fs_info *fs_info)
@@ -4192,10 +4190,8 @@ static int find_free_extent_update_loop(struct btrfs_fs_info *fs_info,
else
trans = btrfs_join_transaction(root);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- return ret;
- }
+ if (IS_ERR(trans))
+ return PTR_ERR(trans);
ret = btrfs_chunk_alloc(trans, space_info, ffe_ctl->flags,
CHUNK_ALLOC_FORCE_FOR_EXTENT);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 80fc2d59bfeb..90a6b161d7c1 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2182,14 +2182,17 @@ static struct extent_buffer *find_extent_buffer_nolock(
static void end_bbio_meta_write(struct btrfs_bio *bbio)
{
struct extent_buffer *eb = bbio->private;
- struct folio_iter fi;
if (bbio->bio.bi_status != BLK_STS_OK)
set_btree_ioerr(eb);
- bio_for_each_folio_all(fi, &bbio->bio) {
- btrfs_meta_folio_clear_writeback(fi.folio, eb);
- }
+ /*
+ * Clear writeback on the buffer's own folios. The bio may carry the
+ * shared zero page instead (EXTENT_BUFFER_ZONED_ZEROOUT), so iterate
+ * the extent buffer folios rather than the bio folios.
+ */
+ for (int i = 0; i < num_extent_folios(eb); i++)
+ btrfs_meta_folio_clear_writeback(eb->folios[i], eb);
buffer_tree_clear_mark(eb, PAGECACHE_TAG_WRITEBACK);
clear_and_wake_up_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags);
@@ -2230,7 +2233,8 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb,
struct btrfs_fs_info *fs_info = eb->fs_info;
struct btrfs_bio *bbio;
- prepare_eb_write(eb);
+ if (!test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags))
+ prepare_eb_write(eb);
bbio = btrfs_bio_alloc(INLINE_EXTENT_BUFFER_PAGES,
REQ_OP_WRITE | REQ_META | wbc_to_write_flags(wbc),
@@ -2250,8 +2254,21 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb,
btrfs_meta_folio_set_writeback(folio, eb);
if (!folio_test_dirty(folio))
wbc->nr_to_write -= folio_nr_pages(folio);
- bio_add_folio_nofail(&bbio->bio, folio, range_len,
- offset_in_folio(folio, range_start));
+ if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) {
+ u32 off = 0;
+
+ while (off < range_len) {
+ u32 add = min_t(u32, PAGE_SIZE, range_len - off);
+
+ bio_add_folio_nofail(&bbio->bio,
+ page_folio(ZERO_PAGE(0)),
+ add, 0);
+ off += add;
+ }
+ } else {
+ bio_add_folio_nofail(&bbio->bio, folio, range_len,
+ offset_in_folio(folio, range_start));
+ }
wbc_account_cgroup_owner(wbc, folio, range_len);
folio_unlock(folio);
}
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 73571d5d3d5a..65d1ed3fcd5b 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -196,6 +196,25 @@ static inline void extent_changeset_init(struct extent_changeset *changeset)
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;
@@ -210,6 +229,7 @@ static inline struct extent_changeset *extent_changeset_alloc(void)
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);
}
@@ -218,7 +238,8 @@ static inline void extent_changeset_release(struct extent_changeset *changeset)
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)
diff --git a/fs/btrfs/fiemap.c b/fs/btrfs/fiemap.c
index f2eaaef8422b..30a0f66c9c9e 100644
--- a/fs/btrfs/fiemap.c
+++ b/fs/btrfs/fiemap.c
@@ -661,7 +661,7 @@ restart:
range_end = round_up(start + len, sectorsize);
prev_extent_end = range_start;
- btrfs_lock_extent(&inode->io_tree, range_start, range_end, &cached_state);
+ btrfs_lock_extent(&inode->io_tree, range_start, range_end - 1, &cached_state);
ret = fiemap_find_last_extent_offset(inode, path, &last_extent_end);
if (ret < 0)
@@ -841,7 +841,7 @@ check_eof_delalloc:
}
out_unlock:
- btrfs_unlock_extent(&inode->io_tree, range_start, range_end, &cached_state);
+ btrfs_unlock_extent(&inode->io_tree, range_start, range_end - 1, &cached_state);
if (ret == BTRFS_FIEMAP_FLUSH_CACHE) {
btrfs_release_path(path);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 66c76f425afb..a15062f976ba 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -881,62 +881,56 @@ again:
/*
* Locks the extent and properly waits for data=ordered extents to finish
- * before allowing the folios to be modified if need.
+ * before allowing the folios to be modified.
*
* Return:
- * 1 - the extent is locked
- * 0 - the extent is not locked, and everything is OK
+ * 0 - the extent is locked
* -EAGAIN - need to prepare the folios again
*/
static noinline int
-lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct folio *folio,
- loff_t pos, size_t write_bytes,
- u64 *lockstart, u64 *lockend, bool nowait,
- struct extent_state **cached_state)
+lock_and_cleanup_extent(struct btrfs_inode *inode, struct folio *folio,
+ loff_t pos, size_t write_bytes,
+ u64 *lockstart, u64 *lockend, bool nowait,
+ struct extent_state **cached_state)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ struct btrfs_ordered_extent *ordered;
u64 start_pos;
u64 last_pos;
- int ret = 0;
start_pos = round_down(pos, fs_info->sectorsize);
last_pos = round_up(pos + write_bytes, fs_info->sectorsize) - 1;
- if (start_pos < inode->vfs_inode.i_size) {
- struct btrfs_ordered_extent *ordered;
-
- if (nowait) {
- if (!btrfs_try_lock_extent(&inode->io_tree, start_pos,
- last_pos, cached_state)) {
- folio_unlock(folio);
- folio_put(folio);
- return -EAGAIN;
- }
- } else {
- btrfs_lock_extent(&inode->io_tree, start_pos, last_pos,
- cached_state);
- }
-
- ordered = btrfs_lookup_ordered_range(inode, start_pos,
- last_pos - start_pos + 1);
- if (ordered &&
- ordered->file_offset + ordered->num_bytes > start_pos &&
- ordered->file_offset <= last_pos) {
- btrfs_unlock_extent(&inode->io_tree, start_pos, last_pos,
- cached_state);
+ if (nowait) {
+ if (!btrfs_try_lock_extent(&inode->io_tree, start_pos,
+ last_pos, cached_state)) {
folio_unlock(folio);
folio_put(folio);
- btrfs_start_ordered_extent(ordered);
- btrfs_put_ordered_extent(ordered);
return -EAGAIN;
}
- if (ordered)
- btrfs_put_ordered_extent(ordered);
+ } else {
+ btrfs_lock_extent(&inode->io_tree, start_pos, last_pos,
+ cached_state);
+ }
- *lockstart = start_pos;
- *lockend = last_pos;
- ret = 1;
+ ordered = btrfs_lookup_ordered_range(inode, start_pos,
+ last_pos - start_pos + 1);
+ if (ordered &&
+ ordered->file_offset + ordered->num_bytes > start_pos &&
+ ordered->file_offset <= last_pos) {
+ btrfs_unlock_extent(&inode->io_tree, start_pos, last_pos,
+ cached_state);
+ folio_unlock(folio);
+ folio_put(folio);
+ btrfs_start_ordered_extent(ordered);
+ btrfs_put_ordered_extent(ordered);
+ return -EAGAIN;
}
+ if (ordered)
+ btrfs_put_ordered_extent(ordered);
+
+ *lockstart = start_pos;
+ *lockend = last_pos;
/*
* We should be called after prepare_one_folio() which should have locked
@@ -944,7 +938,7 @@ lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct folio *folio,
*/
WARN_ON(!folio_test_locked(folio));
- return ret;
+ return 0;
}
/*
@@ -1201,7 +1195,6 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter,
const u64 reserved_start = round_down(start, fs_info->sectorsize);
u64 reserved_len;
struct folio *folio = NULL;
- int extents_locked;
u64 lockstart;
u64 lockend;
bool only_release_metadata = false;
@@ -1256,18 +1249,15 @@ again:
reserved_len = last_block - reserved_start;
}
- extents_locked = lock_and_cleanup_extent_if_need(inode, folio, start,
- write_bytes, &lockstart,
- &lockend, nowait,
- &cached_state);
- if (extents_locked < 0) {
- if (!nowait && extents_locked == -EAGAIN)
+ ret = lock_and_cleanup_extent(inode, folio, start, write_bytes,
+ &lockstart, &lockend, nowait, &cached_state);
+ if (ret < 0) {
+ if (!nowait)
goto again;
btrfs_delalloc_release_extents(inode, reserved_len);
release_space(inode, *data_reserved, reserved_start, reserved_len,
only_release_metadata);
- ret = extents_locked;
return ret;
}
@@ -1292,11 +1282,8 @@ again:
/* No copied bytes, unlock, release reserved space and exit. */
if (copied == 0) {
- if (extents_locked)
- btrfs_unlock_extent(&inode->io_tree, lockstart, lockend,
- &cached_state);
- else
- btrfs_free_extent_state(cached_state);
+ btrfs_unlock_extent(&inode->io_tree, lockstart, lockend,
+ &cached_state);
btrfs_delalloc_release_extents(inode, reserved_len);
release_space(inode, *data_reserved, reserved_start, reserved_len,
only_release_metadata);
@@ -1315,17 +1302,7 @@ again:
ret = btrfs_dirty_folio(inode, folio, start, copied, &cached_state,
only_release_metadata);
- /*
- * If we have not locked the extent range, because the range's start
- * offset is >= i_size, we might still have a non-NULL cached extent
- * state, acquired while marking the extent range as delalloc through
- * btrfs_dirty_page(). Therefore free any possible cached extent state
- * to avoid a memory leak.
- */
- if (extents_locked)
- btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state);
- else
- btrfs_free_extent_state(cached_state);
+ btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state);
btrfs_delalloc_release_extents(inode, reserved_len);
if (ret) {
diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c
index f9ffb1c8988d..fa8fbb19ff65 100644
--- a/fs/btrfs/free-space-tree.c
+++ b/fs/btrfs/free-space-tree.c
@@ -1533,9 +1533,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans,
btrfs_release_path(path);
}
- ret = 0;
-
- return ret;
+ return 0;
}
static int load_free_space_bitmaps(struct btrfs_caching_control *caching_ctl,
diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c
index b73e1dd97208..a864f8c99729 100644
--- a/fs/btrfs/inode-item.c
+++ b/fs/btrfs/inode-item.c
@@ -371,14 +371,13 @@ int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans,
struct btrfs_path *path, u64 objectid)
{
struct btrfs_key key;
- int ret;
+
key.objectid = objectid;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
- ret = btrfs_insert_empty_item(trans, root, path, &key,
- sizeof(struct btrfs_inode_item));
- return ret;
+ return btrfs_insert_empty_item(trans, root, path, &key,
+ sizeof(struct btrfs_inode_item));
}
int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7af5ea0cf55f..cca9840bee89 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2348,7 +2348,6 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_fol
u64 start, u64 end, struct writeback_control *wbc)
{
const bool zoned = btrfs_is_zoned(inode->root->fs_info);
- int ret;
/*
* The range must cover part of the @locked_folio, or a return of 1
@@ -2356,10 +2355,8 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_fol
*/
ASSERT(!(end <= folio_pos(locked_folio) || start >= folio_end(locked_folio)));
- if (should_nocow(inode, start, end)) {
- ret = run_delalloc_nocow(inode, locked_folio, start, end);
- return ret;
- }
+ if (should_nocow(inode, start, end))
+ return run_delalloc_nocow(inode, locked_folio, start, end);
if (btrfs_inode_can_compress(inode) &&
inode_need_compress(inode, start, end) &&
@@ -2367,11 +2364,9 @@ int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_fol
return 1;
if (zoned)
- ret = run_delalloc_cow(inode, locked_folio, start, end, wbc,
- true);
+ return run_delalloc_cow(inode, locked_folio, start, end, wbc, true);
else
- ret = cow_file_range(inode, locked_folio, start, end, NULL, 0);
- return ret;
+ return cow_file_range(inode, locked_folio, start, end, NULL, 0);
}
void btrfs_split_delalloc_extent(struct btrfs_inode *inode,
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index b4c6cbbcd168..f654639334fc 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -694,7 +694,6 @@ void btrfs_free_qgroup_config(struct btrfs_fs_info *fs_info)
static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src,
u64 dst)
{
- int ret;
struct btrfs_root *quota_root = trans->fs_info->quota_root;
BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
@@ -707,8 +706,7 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src,
key.type = BTRFS_QGROUP_RELATION_KEY;
key.offset = dst;
- ret = btrfs_insert_empty_item(trans, quota_root, path, &key, 0);
- return ret;
+ return btrfs_insert_empty_item(trans, quota_root, path, &key, 0);
}
static int del_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src,
@@ -833,9 +831,7 @@ static int del_qgroup_item(struct btrfs_trans_handle *trans, u64 qgroupid)
if (ret > 0)
return -ENOENT;
- ret = btrfs_del_item(trans, quota_root, path);
-
- return ret;
+ return btrfs_del_item(trans, quota_root, path);
}
static int update_qgroup_limit_item(struct btrfs_trans_handle *trans,
@@ -2682,10 +2678,8 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
return ret;
}
- if (root_level == 0) {
- ret = btrfs_qgroup_trace_leaf_items(trans, root_eb);
- return ret;
- }
+ if (root_level == 0)
+ return btrfs_qgroup_trace_leaf_items(trans, root_eb);
path = btrfs_alloc_path();
if (!path)
@@ -4350,12 +4344,13 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode,
struct ulist_node *unode;
struct ulist_iterator uiter;
struct extent_changeset changeset;
+ const u32 sectorsize = root->fs_info->sectorsize;
+ const u64 aligned_start = round_down(start, sectorsize);
+ const u64 aligned_len = round_up(start + len, sectorsize) - aligned_start;
u64 freed = 0;
int ret;
- extent_changeset_init(&changeset);
- len = round_up(start + len, root->fs_info->sectorsize);
- start = round_down(start, root->fs_info->sectorsize);
+ extent_changeset_init_bytes_only(&changeset);
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(&reserved->range_changed, &uiter))) {
@@ -4367,12 +4362,15 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode,
extent_changeset_release(&changeset);
- /* Only free range in range [start, start + len) */
- if (range_start >= start + len ||
- range_start + range_len <= start)
+ /*
+ * Only free the range within
+ * [aligned_start, aligned_start + aligned_len).
+ */
+ if (range_start >= aligned_start + aligned_len ||
+ range_start + range_len <= aligned_start)
continue;
- free_start = max(range_start, start);
- free_len = min(start + len, range_start + range_len) -
+ free_start = max(range_start, aligned_start);
+ free_len = min(aligned_start + aligned_len, range_start + range_len) -
free_start;
/*
* TODO: To also modify reserved->ranges_reserved to reflect
@@ -4418,7 +4416,7 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
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)
@@ -4673,6 +4671,7 @@ void btrfs_qgroup_check_reserved_leak(struct btrfs_inode *inode)
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,
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 37f20f401b0e..b76cd0b1c355 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -3266,7 +3266,6 @@ static int delete_v1_space_cache(struct extent_buffer *leaf,
struct btrfs_key key;
bool found = false;
int i;
- int ret;
if (btrfs_header_owner(leaf) != BTRFS_ROOT_TREE_OBJECTID)
return 0;
@@ -3290,8 +3289,8 @@ static int delete_v1_space_cache(struct extent_buffer *leaf,
}
if (!found)
return -ENOENT;
- ret = delete_block_group_cache(block_group, NULL, space_cache_ino);
- return ret;
+
+ return delete_block_group_cache(block_group, NULL, space_cache_ino);
}
/*
diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index 46bd8ca58670..7fc30a58d8d9 100644
--- a/fs/btrfs/verity.c
+++ b/fs/btrfs/verity.c
@@ -731,14 +731,18 @@ again:
goto out;
folio_lock(folio);
- /* If it's not uptodate after we have the lock, we got a read error. */
- if (!folio_test_uptodate(folio)) {
+ /* Folio was truncated from mapping. */
+ if (!folio->mapping) {
folio_unlock(folio);
folio_put(folio);
- return ERR_PTR(-EIO);
+ goto again;
}
- folio_unlock(folio);
- goto out;
+ /* Another reader may have filled the folio while we waited. */
+ if (folio_test_uptodate(folio)) {
+ folio_unlock(folio);
+ goto out;
+ }
+ goto read_folio;
}
folio = filemap_alloc_folio(mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS),
@@ -755,6 +759,7 @@ again:
return ERR_PTR(ret);
}
+read_folio:
/*
* Merkle item keys are indexed from byte 0 in the merkle tree.
* They have the form:
@@ -764,6 +769,7 @@ again:
ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, off,
folio_address(folio), PAGE_SIZE, folio);
if (ret < 0) {
+ folio_unlock(folio);
folio_put(folio);
return ERR_PTR(ret);
}
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 863f69b9cf14..51c3eab54bde 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -2456,9 +2456,7 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info,
free_fs_devices(cur_devices);
}
- ret = btrfs_commit_transaction(trans);
-
- return ret;
+ return btrfs_commit_transaction(trans);
error_undo:
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) {
@@ -7347,7 +7345,6 @@ static int read_one_dev(struct extent_buffer *leaf,
struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
struct btrfs_device *device;
u64 devid;
- int ret;
u8 fs_uuid[BTRFS_FSID_SIZE];
u8 dev_uuid[BTRFS_UUID_SIZE];
@@ -7446,8 +7443,8 @@ static int read_one_dev(struct extent_buffer *leaf,
atomic64_add(device->total_bytes - device->bytes_used,
&fs_info->free_chunk_space);
}
- ret = 0;
- return ret;
+
+ return 0;
}
int btrfs_read_sys_array(struct btrfs_fs_info *fs_info)