* [PATCH] btrfs: tag all metadata I/O with REQ_META
@ 2026-11-04 10:38 Mark Korenberg via B4 Relay
2026-11-04 17:21 ` David Sterba
0 siblings, 1 reply; 3+ messages in thread
From: Mark Korenberg via B4 Relay @ 2026-11-04 10:38 UTC (permalink / raw)
To: David Sterba, Chris Mason
Cc: Qu Wenruo, Mark Harmstone, linux-btrfs, linux-kernel, Mark Korenberg
From: Mark Korenberg <socketpair@gmail.com>
Tree block reads and writes and super block writes carry REQ_META, but
several other paths do metadata I/O without it, so the block layer and
drivers (blktrace, BFQ, blk-cgroup, wbt, ublk) see it as data I/O:
- scrub reads super blocks with a bare REQ_OP_READ;
- btrfs_repair_bbio_failure() rewrites a bad tree block copy without it;
- RAID5/6 rbios build new per-device bios from a bare op, so all tree
block writes on RAID5/6, and recovery reads, lose the flag;
- scrub and dev-replace read, repair and copy metadata block groups
without it;
- remap relocation copies metadata block groups without it.
Tag these by the chunk type: metadata and system chunks get REQ_META,
mixed chunks do not, as they also hold data. All bios of a block group
then carry the same flag and stay mergeable (blk-cgroup does not merge
bios that differ in REQ_META).
This does not change the btrfs submission path: scrub and remap bbios
skip checksumming, and scrub_read_endio() and scrub_repair_read_endio()
do not wait for other I/O, so running them on endio_meta_workers is
fine. With blk-cgroup, metadata scrub I/O is now issued as root and
charged back, like other metadata I/O.
bdev_rw_virt() takes an enum req_op, but gfs2 and xfs already pass
REQ_META through it.
Signed-off-by: Mark Korenberg <socketpair@gmail.com>
---
I noticed this in the request log of a ublk zoned device: tree block
reads and writes arrive with UBLK_IO_F_META, while the super block
reads at mount do not. An audit of all bio submission sites in
fs/btrfs then found the paths listed in the commit message.
The super block reads at mount are the one case I left alone, along
with a few others:
- btrfs_read_disk_super() and sb_write_pointer() read through the
block device page cache, which cannot pass REQ_META. That is on
purpose since 8f32380d3f29 ("btrfs: use the page cache for super
block reading"), to stay coherent with mkfs and udev. The same goes
for btrfs_scratch_superblocks().
- Discards free space, and the empty preflush of write_dev_flush()
covers the whole device.
Testing: virtme-ng VM, memory backed null_blk, tracefs
block:block_bio_queue, every bio classified as super block or by the
chunk type of its dev extent:
- RAID5 metadata and data: copy /usr/include, scrub, then replace
devid 1 with a new device
- DUP metadata with one copy of a leaf corrupted, read the directory
until "read error corrected"
Bios of metadata chunks, with REQ_META / without:
before after
RAID5 writes (and RMW reads) 0 / 274 245 / 0
scrub 0 / 390 369 / 0
scrub, super blocks 0 / 6 6 / 0
dev-replace 0 / 258 243 / 0
repair write 0 / 1 1 / 0
No data bio carries REQ_META, before or after. No new warnings, W=1
build clean.
Not tested: the remap relocation part, btrfs-progs 7.1 here cannot
create a remap-tree filesystem. Mark, could you give it a look?
The audit, the patch and the test were drafted with the help of an AI
assistant (Claude Opus 5.5).
---
fs/btrfs/bio.c | 3 ++-
fs/btrfs/raid56.c | 3 ++-
fs/btrfs/relocation.c | 13 ++++++++-----
fs/btrfs/scrub.c | 5 +++--
fs/btrfs/volumes.h | 10 ++++++++++
5 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 2e12a6d7e16f..efbce4d549ff 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -951,7 +951,8 @@ int btrfs_repair_bbio_failure(struct btrfs_bio *bbio, const struct bvec_iter *or
}
repair_bio = bio_alloc(smap.dev->bdev, max(1, length >> PAGE_SHIFT),
- REQ_OP_WRITE | REQ_SYNC, GFP_NOFS);
+ REQ_OP_WRITE | REQ_SYNC |
+ (is_data_inode(inode) ? 0 : REQ_META), GFP_NOFS);
repair_bio->bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
while (cur < length) {
struct page *page = bio_iter_page(&bbio->bio, iter);
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index a8a8004bb14c..1b5cdcdeced3 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1205,7 +1205,8 @@ static int rbio_add_io_paddrs(struct btrfs_raid_bio *rbio, struct bio_list *bio_
/* put a new bio on the list */
bio = bio_alloc(stripe->dev->bdev,
max(rbio->bioc->stripe_len >> PAGE_SHIFT, 1),
- op, GFP_NOFS);
+ op | btrfs_chunk_req_meta(rbio->bioc->map_type),
+ GFP_NOFS);
bio->bi_iter.bi_sector = disk_start >> SECTOR_SHIFT;
bio->bi_private = rbio;
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index b2ac5b836b21..af17be38eea3 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -4146,8 +4146,9 @@ static int copy_remapped_data_io(struct btrfs_fs_info *fs_info,
}
static int copy_remapped_data(struct btrfs_fs_info *fs_info, u64 old_addr,
- u64 new_addr, u64 length)
+ u64 new_addr, u64 length, u64 type)
{
+ const blk_opf_t meta = btrfs_chunk_req_meta(type);
int ret;
const u64 copy_len = min_t(u64, length, SZ_1M);
struct page **pages;
@@ -4172,12 +4173,12 @@ static int copy_remapped_data(struct btrfs_fs_info *fs_info, u64 old_addr,
to_copy = min_t(u64, to_copy, BIO_MAX_VECS << PAGE_SHIFT);
ret = copy_remapped_data_io(fs_info, &priv, pages, old_addr,
- to_copy, REQ_OP_READ);
+ to_copy, REQ_OP_READ | meta);
if (ret)
goto end;
ret = copy_remapped_data_io(fs_info, &priv, pages, new_addr,
- to_copy, REQ_OP_WRITE);
+ to_copy, REQ_OP_WRITE | meta);
if (ret)
goto end;
@@ -4344,7 +4345,8 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info,
goto end;
}
- ret = copy_remapped_data(fs_info, new_addr, dest_addr, dest_length);
+ ret = copy_remapped_data(fs_info, new_addr, dest_addr, dest_length,
+ bg->flags);
if (unlikely(ret))
goto end;
@@ -5161,7 +5163,8 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info,
}
}
- ret = copy_remapped_data(fs_info, start, new_addr, length);
+ ret = copy_remapped_data(fs_info, start, new_addr, length,
+ src_bg->flags);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto fail;
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 98362c91bce4..269948b1caeb 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -934,7 +934,8 @@ static struct btrfs_bio *alloc_scrub_bbio(struct btrfs_fs_info *fs_info,
* enough to contain the full stripe.
*/
bbio = btrfs_bio_alloc(round_up(stripe->stripe_len, PAGE_SIZE) >>
- PAGE_SHIFT, opf,
+ PAGE_SHIFT,
+ opf | btrfs_chunk_req_meta(stripe->bg->flags),
BTRFS_I(fs_info->btree_inode),
logical, end_io, stripe);
bbio->is_scrub = true;
@@ -2957,7 +2958,7 @@ static int scrub_one_super(struct scrub_ctx *sctx, struct btrfs_device *dev,
int ret;
ret = bdev_rw_virt(dev->bdev, physical >> SECTOR_SHIFT, sb,
- BTRFS_SUPER_INFO_SIZE, REQ_OP_READ);
+ BTRFS_SUPER_INFO_SIZE, REQ_OP_READ | REQ_META);
if (ret < 0)
return ret;
ret = btrfs_check_super_csum(fs_info, sb);
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 891bc2432870..66796f09eae8 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -709,6 +709,16 @@ static inline enum btrfs_map_op btrfs_op(const struct bio *bio)
}
}
+/*
+ * REQ_META for I/O to a chunk of type @type that holds only tree blocks.
+ * A mixed chunk also holds data and gets no flag, otherwise its data
+ * would be tagged as metadata.
+ */
+static inline blk_opf_t btrfs_chunk_req_meta(u64 type)
+{
+ return (type & BTRFS_BLOCK_GROUP_DATA) ? 0 : REQ_META;
+}
+
static inline unsigned long btrfs_chunk_item_size(int num_stripes)
{
return offsetof(struct btrfs_chunk, stripe) +
---
base-commit: df5b88a7914e18488908ddf13e14e31ab39f954c
change-id: 20261104-btrfs-sb-read-req-meta-7be597aaaa51
Best regards,
--
Mark Korenberg <socketpair@gmail.com>
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] btrfs: tag all metadata I/O with REQ_META
2026-11-04 10:38 [PATCH] btrfs: tag all metadata I/O with REQ_META Mark Korenberg via B4 Relay
@ 2026-11-04 17:21 ` David Sterba
2026-11-04 19:03 ` Марк Коренберг
0 siblings, 1 reply; 3+ messages in thread
From: David Sterba @ 2026-11-04 17:21 UTC (permalink / raw)
To: socketpair
Cc: David Sterba, Chris Mason, Qu Wenruo, Mark Harmstone,
linux-btrfs, linux-kernel
On Wed, Nov 04, 2026 at 03:38:13PM +0500, Mark Korenberg via B4 Relay wrote:
> Not tested: the remap relocation part, btrfs-progs 7.1 here cannot
> create a remap-tree filesystem. Mark, could you give it a look?
Remap tree is experimental feature in btrfs-progs, so you'd have to do
custom build with './configure --enable-experimental', then
'mkfs.btrfs -O list-all' will contain 'remapt-ree'.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] btrfs: tag all metadata I/O with REQ_META
2026-11-04 17:21 ` David Sterba
@ 2026-11-04 19:03 ` Марк Коренберг
0 siblings, 0 replies; 3+ messages in thread
From: Марк Коренберг @ 2026-11-04 19:03 UTC (permalink / raw)
To: dsterba
Cc: David Sterba, Chris Mason, Qu Wenruo, Mark Harmstone,
linux-btrfs, linux-kernel
On Wed, Nov 04, 2026 at 06:21:40PM +0100, David Sterba wrote:
> Remap tree is experimental feature in btrfs-progs, so you'd have to do
> custom build with './configure --enable-experimental', then
> 'mkfs.btrfs -O list-all' will contain 'remapt-ree'.
Thanks, that worked. I tested the remap part now: btrfs-progs devel
(4f5956a) with --enable-experimental, kernel with
CONFIG_BTRFS_EXPERIMENTAL, a 2 GiB filesystem with remap-tree, DUP
metadata and single data, /usr/include copied in, then "balance -m"
twice and "balance -d" twice. kprobes on entry and return of
copy_remapped_data_io() mark the bios of each copy.
Both callers of copy_remapped_data() are covered: the first balance
goes through do_remap_reloc_trans(), the second one mostly through
move_existing_remap().
Copy bios, with REQ_META / without:
without the patch with the patch
run 1 run 2
metadata, reads 0 / 56 0 / 74 70 / 0
metadata, writes 0 / 112 0 / 148 140 / 0
data, reads 0 / 288 0 / 288 0 / 288
data, writes 0 / 288 0 / 288 0 / 288
The metadata counts vary from run to run on the same kernel: each
contiguous range of the block group is copied separately, and how
fragmented the metadata ends up after the copy depends on the timing
of the commits. Writes are twice the reads because of DUP.
btrfs check finds no errors afterwards, no warnings in the log.
One thing on the way: mkfs.btrfs from devel crashes with any
"-O remap-tree". create_metadata_block_groups() commits a
transaction before setup_remap_tree_root() has created the tree, and
write_ctree_super() then dereferences fs_info->remap_root->node, which
is still NULL. For the test I skipped the remap root fields in
write_ctree_super() while the node is not there yet.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-04 19:03 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-04 10:38 [PATCH] btrfs: tag all metadata I/O with REQ_META Mark Korenberg via B4 Relay
2026-11-04 17:21 ` David Sterba
2026-11-04 19:03 ` Марк Коренберг
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®