summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.com>2026-07-22 15:24:45 +0200
committerDavid Sterba <dsterba@suse.com>2026-07-22 15:24:45 +0200
commit2f82ceba24a4d7182a0f8750fc024e7f840fff12 (patch)
tree34c1d7ff4bcca67e4d5ca72cd3f180c99540e3e7 /fs
parent1590cf0329716306e948a8fc29f1d3ee87d3989f (diff)
parent5e6fa0d7657227c59793295c2eee3a3cb74e98c7 (diff)
downloadlinux-next-2f82ceba24a4d7182a0f8750fc024e7f840fff12.tar.gz
linux-next-2f82ceba24a4d7182a0f8750fc024e7f840fff12.zip
Merge branch 'misc-7.2' into next-fixes
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/acl.c4
-rw-r--r--fs/btrfs/backref.c5
-rw-r--r--fs/btrfs/block-group.c34
-rw-r--r--fs/btrfs/block-rsv.c19
-rw-r--r--fs/btrfs/btrfs_inode.h2
-rw-r--r--fs/btrfs/disk-io.c11
-rw-r--r--fs/btrfs/extent_io.c4
-rw-r--r--fs/btrfs/extent_map.c4
-rw-r--r--fs/btrfs/fs.h9
-rw-r--r--fs/btrfs/inode.c3
-rw-r--r--fs/btrfs/ioctl.c3
-rw-r--r--fs/btrfs/raid-stripe-tree.c6
-rw-r--r--fs/btrfs/raid56.c15
-rw-r--r--fs/btrfs/relocation.c55
-rw-r--r--fs/btrfs/send.c2
-rw-r--r--fs/btrfs/subpage.c26
-rw-r--r--fs/btrfs/super.c8
-rw-r--r--fs/btrfs/zoned.c27
18 files changed, 176 insertions, 61 deletions
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index e55b686fe1ab..662cdd1cbdef 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -15,6 +15,7 @@
#include "xattr.h"
#include "acl.h"
#include "misc.h"
+#include "btrfs_inode.h"
struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu)
{
@@ -107,6 +108,9 @@ int btrfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
struct inode *inode = d_inode(dentry);
umode_t old_mode = inode->i_mode;
+ if (btrfs_root_readonly(BTRFS_I(inode)->root))
+ return -EROFS;
+
if (type == ACL_TYPE_ACCESS && acl) {
ret = posix_acl_update_mode(idmap, inode,
&inode->i_mode, &acl);
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 23c3eeb58dc1..1be632c742bd 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -3054,7 +3054,10 @@ void btrfs_backref_free_node(struct btrfs_backref_cache *cache,
if (node) {
ASSERT(list_empty(&node->list));
ASSERT(list_empty(&node->lower));
- ASSERT(node->eb == NULL, "node->eb->start=%llu", node->eb->start);
+ ASSERT(node->eb == NULL, "node->eb->start=%llu level=%d owner=%llu",
+ node->eb ? node->eb->start : 0,
+ node->eb ? btrfs_header_level(node->eb) : 0,
+ node->eb ? btrfs_header_owner(node->eb) : 0);
cache->nr_nodes--;
btrfs_put_root(node->root);
kfree(node);
diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index ab76a5173272..8def7abb728f 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -4532,25 +4532,29 @@ static void reserve_chunk_space(struct btrfs_trans_handle *trans,
if (IS_ERR(bg)) {
ret = PTR_ERR(bg);
} else {
+ int activate_ret;
+
/*
* We have a new chunk. We also need to activate it for
* zoned filesystem.
*/
- ret = btrfs_zoned_activate_one_bg(info, true);
- if (ret < 0)
- return;
-
- /*
- * If we fail to add the chunk item here, we end up
- * trying again at phase 2 of chunk allocation, at
- * btrfs_create_pending_block_groups(). So ignore
- * any error here. An ENOSPC here could happen, due to
- * the cases described at do_chunk_alloc() - the system
- * block group we just created was just turned into RO
- * mode by a scrub for example, or a running discard
- * temporarily removed its free space entries, etc.
- */
- btrfs_chunk_alloc_add_chunk_item(trans, bg);
+ activate_ret = btrfs_zoned_activate_one_bg(info, true);
+ if (activate_ret < 0) {
+ ret = activate_ret;
+ } else {
+ /*
+ * If we fail to add the chunk item here, we end
+ * up trying again at phase 2 of chunk allocation,
+ * at btrfs_create_pending_block_groups(). So
+ * ignore any error here. An ENOSPC here could
+ * happen, due to the cases described at
+ * do_chunk_alloc() - the system block group we
+ * just created was just turned into RO mode by a
+ * scrub for example, or a running discard
+ * temporarily removed its free space entries, etc.
+ */
+ btrfs_chunk_alloc_add_chunk_item(trans, bg);
+ }
}
}
diff --git a/fs/btrfs/block-rsv.c b/fs/btrfs/block-rsv.c
index 9efb3016ef11..c68a8f4b7d19 100644
--- a/fs/btrfs/block-rsv.c
+++ b/fs/btrfs/block-rsv.c
@@ -322,10 +322,25 @@ void btrfs_block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv,
void btrfs_update_global_block_rsv(struct btrfs_fs_info *fs_info)
{
struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
- struct btrfs_space_info *sinfo = block_rsv->space_info;
+ struct btrfs_space_info *sinfo;
struct btrfs_root *root, *tmp;
- u64 num_bytes = btrfs_root_used(&fs_info->tree_root->root_item);
unsigned int min_items = 1;
+ u64 num_bytes;
+
+ /*
+ * A full read-only mount (rescue options) cannot start transactions,
+ * so the global reserve is never consumed. Mark it as full and skip
+ * the accounting.
+ */
+ if (btrfs_is_full_ro(fs_info)) {
+ spin_lock(&block_rsv->lock);
+ block_rsv->full = true;
+ spin_unlock(&block_rsv->lock);
+ return;
+ }
+
+ sinfo = block_rsv->space_info;
+ num_bytes = btrfs_root_used(&fs_info->tree_root->root_item);
/*
* The global block rsv is based on the size of the extent tree, the
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index d5d81f9546c3..7fdc6c3fd066 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -476,6 +476,8 @@ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode)
if (inode->flags & BTRFS_INODE_NODATACOW ||
inode->flags & BTRFS_INODE_NODATASUM)
return false;
+ if (btrfs_is_data_reloc_root(inode->root))
+ return false;
return true;
}
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 274e2f0826b6..22c321719e4f 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -3288,15 +3288,6 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
return 0;
}
-static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info)
-{
- if (!sb_rdonly(fs_info->sb))
- return false;
- if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
- return true;
- return false;
-}
-
/*
* Try to wait for any metadata readahead, and invalidate all btree folios.
*
@@ -3462,7 +3453,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
WRITE_ONCE(fs_info->fs_error, -EUCLEAN);
/* If the fs has any rescue options, no transaction is allowed. */
- if (fs_is_full_ro(fs_info))
+ if (btrfs_is_full_ro(fs_info))
WRITE_ONCE(fs_info->fs_error, -EROFS);
/* Set up fs_info before parsing mount options */
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 7d604524e83c..de5785117a47 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2004,7 +2004,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e
btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
percpu_counter_add_batch(&fs_info->dirty_metadata_bytes,
- -eb->len,
+ -(s64)eb->len,
fs_info->dirty_metadata_batch);
ret = true;
} else {
@@ -3774,7 +3774,7 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,
return;
buffer_tree_clear_mark(eb, PAGECACHE_TAG_DIRTY);
- percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -eb->len,
+ percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -(s64)eb->len,
fs_info->dirty_metadata_batch);
for (int i = 0; i < num_extent_folios(eb); i++) {
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index fce9c5cc0122..6ad7b39ae358 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -866,13 +866,13 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
goto next;
}
- flags = em->flags;
/*
* In case we split the extent map, we want to preserve the
* EXTENT_FLAG_LOGGING flag on our extent map, but we don't want
* it on the new extent maps.
*/
- em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING);
+ flags = em->flags & ~EXTENT_FLAG_LOGGING;
+ em->flags &= ~EXTENT_FLAG_PINNED;
modified = !list_empty(&em->list);
/*
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 5f0cfb0b5466..7ee9ec2b0efb 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -1159,6 +1159,15 @@ void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag,
#define btrfs_test_opt(fs_info, opt) ((fs_info)->mount_opt & \
BTRFS_MOUNT_##opt)
+static inline bool btrfs_is_full_ro(const struct btrfs_fs_info *fs_info)
+{
+ if (!sb_rdonly(fs_info->sb))
+ return false;
+ if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
+ return true;
+ return false;
+}
+
static inline bool btrfs_fs_closing(const struct btrfs_fs_info *fs_info)
{
return unlikely(test_bit(BTRFS_FS_CLOSING_START, &fs_info->flags));
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 272598f6ae77..b446c3014b24 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8068,7 +8068,8 @@ static int btrfs_getattr(struct mnt_idmap *idmap,
stat->result_mask |= STATX_SUBVOL;
spin_lock(&BTRFS_I(inode)->lock);
- delalloc_bytes = BTRFS_I(inode)->new_delalloc_bytes;
+ delalloc_bytes = S_ISREG(inode->i_mode) ?
+ BTRFS_I(inode)->new_delalloc_bytes : 0;
inode_bytes = inode_get_bytes(inode);
spin_unlock(&BTRFS_I(inode)->lock);
stat->blocks = (ALIGN(inode_bytes, blocksize) +
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 81e87bc39828..baa645e98812 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2048,6 +2048,7 @@ static int _btrfs_ioctl_get_subvol_info(struct inode *inode,
ret = -ENOENT;
goto out;
}
+ ret = 0;
}
out:
@@ -5204,7 +5205,7 @@ static int btrfs_ioctl_get_csums(struct file *file, void __user *argp)
struct btrfs_inode *inode = BTRFS_I(vfs_inode);
struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct btrfs_root *root = inode->root;
- struct btrfs_ioctl_get_csums_args args;
+ struct btrfs_ioctl_get_csums_args args = { 0 };
BTRFS_PATH_AUTO_FREE(path);
const u64 ino = btrfs_ino(inode);
const u32 csum_size = fs_info->csum_size;
diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c
index 454a95bf542a..b210371ce91e 100644
--- a/fs/btrfs/raid-stripe-tree.c
+++ b/fs/btrfs/raid-stripe-tree.c
@@ -414,6 +414,12 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info,
int slot;
int ret;
+ if (unlikely(!stripe_root)) {
+ btrfs_err_rl(fs_info, "missing raid stripe tree root for logical %llu",
+ logical);
+ return -EUCLEAN;
+ }
+
stripe_key.objectid = logical;
stripe_key.type = BTRFS_RAID_STRIPE_KEY;
stripe_key.offset = 0;
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 00a01b97cc0c..ffb654d36391 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1679,8 +1679,10 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
continue;
/* No csum for this sector, skip to the next sector. */
- if (!test_bit(total_sector_nr, rbio->csum_bitmap))
+ if (!test_bit(total_sector_nr, rbio->csum_bitmap)) {
+ total_sector_nr++;
continue;
+ }
expected_csum = rbio->csum_buf + total_sector_nr * fs_info->csum_size;
btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf);
@@ -2909,13 +2911,12 @@ static int scrub_assemble_read_bios(struct btrfs_raid_bio *rbio)
continue;
/*
- * We want to find all the sectors missing from the rbio and
- * read them from the disk. If sector_paddr_in_rbio() finds a sector
- * in the bio list we don't need to read it off the stripe.
+ * A parity-scrub rbio carries no data in its bio list: the
+ * only bio there is the empty completion bio added by
+ * raid56_parity_alloc_scrub_rbio(). Every sector is read
+ * from the stripe, so only assert that invariant here.
*/
- paddrs = sector_paddrs_in_rbio(rbio, stripe, sectornr, 1);
- if (paddrs == NULL)
- continue;
+ ASSERT(!sector_paddrs_in_rbio(rbio, stripe, sectornr, 1));
paddrs = rbio_stripe_paddrs(rbio, stripe, sectornr);
/*
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 0d63d117db59..fc5c14b5adad 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -588,6 +588,7 @@ static int __add_reloc_root(struct btrfs_root *root, struct reloc_control *rc)
btrfs_err(fs_info,
"Duplicate root found for start=%llu while inserting into relocation tree",
node->bytenr);
+ kfree(node);
return -EEXIST;
}
@@ -890,6 +891,13 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr,
leaf = path->nodes[0];
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
+ if (unlikely(btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE)) {
+ btrfs_print_leaf(leaf);
+ btrfs_err(fs_info,
+ "unexpected inline file extent item for data reloc inode %llu key offset %llu",
+ btrfs_ino(BTRFS_I(reloc_inode)), bytenr);
+ return -EUCLEAN;
+ }
/*
* The cluster-boundary key searched above is always written by
@@ -1518,6 +1526,17 @@ static int insert_dirty_subvol(struct btrfs_trans_handle *trans,
return 0;
}
+static void clear_reloc_root(struct btrfs_root *root)
+{
+ root->reloc_root = NULL;
+ /*
+ * Need barrier to ensure clear_bit() only happens after
+ * root->reloc_root = NULL. Pairs with have_reloc_root().
+ */
+ smp_wmb();
+ clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
+}
+
static int clean_dirty_subvols(struct reloc_control *rc)
{
struct btrfs_root *root;
@@ -1532,13 +1551,7 @@ static int clean_dirty_subvols(struct reloc_control *rc)
struct btrfs_root *reloc_root = root->reloc_root;
list_del_init(&root->reloc_dirty_list);
- root->reloc_root = NULL;
- /*
- * Need barrier to ensure clear_bit() only happens after
- * root->reloc_root = NULL. Pairs with have_reloc_root.
- */
- smp_wmb();
- clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
+ clear_reloc_root(root);
if (reloc_root) {
/*
* btrfs_drop_snapshot drops our ref we hold for
@@ -1914,21 +1927,39 @@ again:
goto out;
}
ret = merge_reloc_root(rc, root);
- btrfs_put_root(root);
if (ret) {
- if (list_empty(&reloc_root->root_list))
+ /*
+ * Clear the reloc root since below we will call
+ * free_reloc_roots(), otherwise we leave
+ * root->reloc_root pointing to a freed reloc
+ * root and trigger a use-after-free during
+ * unmount or elsewhere.
+ */
+ clear_reloc_root(root);
+ btrfs_put_root(root);
+ /*
+ * We are adding the reloc_root to the local
+ * reloc_roots list, so we add a ref for this
+ * list which will be dropped below by the call
+ * to free_reloc_roots().
+ */
+ if (list_empty(&reloc_root->root_list)) {
list_add_tail(&reloc_root->root_list,
&reloc_roots);
+ btrfs_grab_root(reloc_root);
+ }
+ /* Now drop the ref for root->reloc_root. */
+ btrfs_put_root(reloc_root);
goto out;
}
+ btrfs_put_root(root);
} else {
if (!IS_ERR(root)) {
if (root->reloc_root == reloc_root) {
- root->reloc_root = NULL;
+ clear_reloc_root(root);
+ /* Drop the ref for root->reloc_root. */
btrfs_put_root(reloc_root);
}
- clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE,
- &root->state);
btrfs_put_root(root);
}
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 3ae480c7474b..297704edf1b4 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -8251,7 +8251,7 @@ out:
}
if (sort_clone_roots) {
- for (i = 0; i < sctx->clone_roots_cnt; i++) {
+ for (i = 0; sctx && i < sctx->clone_roots_cnt; i++) {
btrfs_root_dec_send_in_progress(
sctx->clone_roots[i].root);
btrfs_put_root(sctx->clone_roots[i].root);
diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c
index 56060acac2e9..2a9397be8116 100644
--- a/fs/btrfs/subpage.c
+++ b/fs/btrfs/subpage.c
@@ -359,6 +359,23 @@ void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info,
folio_mark_dirty(folio);
}
+static void folio_clear_tags(struct folio *folio)
+{
+ struct address_space *mapping = folio_mapping(folio);
+ XA_STATE(xas, &mapping->i_pages, folio->index);
+ unsigned long flags;
+
+ ASSERT(folio_test_locked(folio));
+ ASSERT(mapping);
+ ASSERT(mapping_use_writeback_tags(mapping));
+
+ xas_lock_irqsave(&xas, flags);
+ xas_load(&xas);
+ xas_clear_mark(&xas, PAGECACHE_TAG_DIRTY);
+ xas_clear_mark(&xas, PAGECACHE_TAG_TOWRITE);
+ xas_unlock_irqrestore(&xas, flags);
+}
+
/*
* Extra clear_and_test function for subpage dirty bitmap.
*
@@ -403,7 +420,6 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info,
unsigned int start_bit = subpage_calc_start_bit(fs_info, folio,
writeback, start, len);
unsigned long flags;
- bool keep_write;
spin_lock_irqsave(&bfs->lock, flags);
bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits);
@@ -413,10 +429,14 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info,
* folio. Doing so can cause WB_SYNC_ALL writepages() to overlook it,
* assume writeback is complete, and exit too early — violating sync
* ordering guarantees.
+ *
+ * Instead we manually clear the DIRTY and TOWRITE tags after the folio
+ * is no longer dirty.
*/
- keep_write = folio_test_dirty(folio);
if (!folio_test_writeback(folio))
- __folio_start_writeback(folio, keep_write);
+ __folio_start_writeback(folio, true);
+ if (!folio_test_dirty(folio))
+ folio_clear_tags(folio);
spin_unlock_irqrestore(&bfs->lock, flags);
}
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index a7d804219bec..4b2c07211c30 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1519,12 +1519,14 @@ static int btrfs_reconfigure(struct fs_context *fc)
sync_filesystem(sb);
set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
- if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags))
- return -EINVAL;
+ if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) {
+ ret = -EINVAL;
+ goto restore;
+ }
ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY));
if (ret < 0)
- return ret;
+ goto restore;
btrfs_ctx_to_info(fs_info, ctx);
btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags);
diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index 97f06dd01693..a016cb471beb 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -2138,6 +2138,16 @@ void btrfs_finish_ordered_zoned(struct btrfs_ordered_extent *ordered)
if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags))
return;
+ /*
+ * A fully truncated ordered extent wrote no data and so has
+ * no zone append result to record.
+ */
+ if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags) &&
+ ordered->truncated_len == 0) {
+ ASSERT(list_empty(&ordered->csum_list));
+ return;
+ }
+
ASSERT(!list_empty(&ordered->csum_list));
sum = list_first_entry(&ordered->csum_list, struct btrfs_ordered_sum, list);
logical = sum->logical;
@@ -2190,7 +2200,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx,
if (fs_info->treelog_bg == block_group->start) {
if (!btrfs_zone_activate(block_group)) {
- int ret_fin = btrfs_zone_finish_one_bg(fs_info);
+ int ret_fin;
+
+ btrfs_zoned_meta_io_unlock(fs_info);
+ ret_fin = btrfs_zone_finish_one_bg(fs_info);
+ btrfs_zoned_meta_io_lock(fs_info);
if (ret_fin != 1 || !btrfs_zone_activate(block_group))
return false;
@@ -3186,6 +3200,17 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num
bg->zone_unusable = bg->length - bg->zone_capacity;
bg->alloc_offset = 0;
/*
+ * The zone was just reset to empty, so alloc_offset went back to
+ * the start of the zone. For metadata/system block groups the
+ * write pointer must follow it back to the start of the zone;
+ * otherwise it stays stale at the previous (finished) zone end,
+ * and metadata written into the reused zone would sit behind the
+ * write pointer, could never be written out in sequential order,
+ * and would be stranded (pinning its folio) until unmount.
+ */
+ if (bg->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM))
+ bg->meta_write_pointer = bg->start;
+ /*
* This holds because we currently reset fully used then freed
* block group.
*/