<feed xmlns='http://www.w3.org/2005/Atom'>
<title>kernel/git/stable/linux.git/fs/btrfs, branch linux-rolling-lts</title>
<subtitle>Linux kernel stable tree</subtitle>
<id>https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/atom?h=linux-rolling-lts</id>
<link rel='self' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/atom?h=linux-rolling-lts'/>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/'/>
<updated>2026-09-14T11:35:43+00:00</updated>
<entry>
<title>btrfs: qgroup: fix a wrong length calculation in qgroup_free_reserved_data()</title>
<updated>2026-09-14T11:35:43+00:00</updated>
<author>
<name>Qu Wenruo</name>
<email>wqu@suse.com</email>
</author>
<published>2026-07-28T02:39:26+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=e6edde29990af8064b9d12217ec03db231ccd55d'/>
<id>urn:sha1:e6edde29990af8064b9d12217ec03db231ccd55d</id>
<content type='text'>
[ Upstream commit 9102b179512e11644fb0489ae62010a09afa199c ]

In that function, we round down the start position and round up the
ending position.

But during the calculation of @len, we use "round_up(start + len,
sectorsize)", which is the rounded up end position, not the rounded up
length.

Which results a much larger length, and later we are still using
"start + len", which is completely incorrect.

Fix it by declaring a local @aligned_start and @aligned_len and use them
instead.

Fixes: bc42bda22345 ("btrfs: qgroup: Fix qgroup reserved space underflow by only freeing reserved ranges")
Reviewed-by: Filipe Manana &lt;fdmanana@suse.com&gt;
Signed-off-by: Qu Wenruo &lt;wqu@suse.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: avoid GFP_ATOMIC allocations in qgroup free paths</title>
<updated>2026-09-14T11:35:43+00:00</updated>
<author>
<name>Leo Martins</name>
<email>loemra.dev@gmail.com</email>
</author>
<published>2026-03-19T23:49:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=b0059242735ea6bd893a40ae3856af293e6bdcb2'/>
<id>urn:sha1:b0059242735ea6bd893a40ae3856af293e6bdcb2</id>
<content type='text'>
[ Upstream commit e0a85137a882db789b1bccc1e7db06356ac8c69f ]

When qgroups are enabled, __btrfs_qgroup_release_data() and
qgroup_free_reserved_data() pass an extent_changeset to
btrfs_clear_record_extent_bits() to track how many bytes had their
EXTENT_QGROUP_RESERVED bits cleared. Inside the extent IO tree spinlock,
add_extent_changeset() calls ulist_add() with GFP_ATOMIC to record each
changed range. If this allocation fails, it hits a BUG_ON and panics the
kernel.

However, both of these callers only read changeset.bytes_changed
afterwards — the range_changed ulist is populated and immediately freed
without ever being iterated. The GFP_ATOMIC allocation is entirely
unnecessary for these paths.

Introduce extent_changeset_init_bytes_only() which uses a sentinel value
(EXTENT_CHANGESET_BYTES_ONLY) on the ulist's prealloc field to signal
that only bytes_changed should be tracked. add_extent_changeset() checks
for this sentinel and returns early after updating bytes_changed,
skipping the ulist_add() call entirely. This eliminates the GFP_ATOMIC
allocation and makes the BUG_ON unreachable for these paths.

Callers that need range tracking (qgroup_reserve_data,
qgroup_unreserve_range, btrfs_qgroup_check_reserved_leak) continue to
use extent_changeset_init() and are unaffected.

Reviewed-by: Qu Wenruo &lt;wqu@suse.com&gt;
Signed-off-by: Leo Martins &lt;loemra.dev@gmail.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Stable-dep-of: 9102b179512e ("btrfs: qgroup: fix a wrong length calculation in qgroup_free_reserved_data()")
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: use aligned range for locking in extent_fiemap()</title>
<updated>2026-09-14T11:35:43+00:00</updated>
<author>
<name>Qu Wenruo</name>
<email>wqu@suse.com</email>
</author>
<published>2026-07-28T10:04:09+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=f44616db1dbcec56a23d93af1ecca9173eef8c5f'/>
<id>urn:sha1:f44616db1dbcec56a23d93af1ecca9173eef8c5f</id>
<content type='text'>
[ Upstream commit 681e073614515b892cacef0eeec0c761a2c2ab87 ]

The @end parameter for all extent io tree helpers is inclusive, but
the call site in extent_fiemap() is passing an exclusive end into
btrfs_lock_extent(), which will step into the next block unexpectedly.

Pass the inclusive end into btrfs_lock_extent() and
btrfs_unlock_extent().

Fixes: ac3c0d36a2a2 ("btrfs: make fiemap more efficient and accurate reporting extent sharedness")
Reviewed-by: Filipe Manana &lt;fdmanana@suse.com&gt;
Signed-off-by: Qu Wenruo &lt;wqu@suse.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: zoned: don't clobber the extent buffer when zeroing it out</title>
<updated>2026-09-14T11:35:43+00:00</updated>
<author>
<name>Johannes Thumshirn</name>
<email>johannes.thumshirn@wdc.com</email>
</author>
<published>2026-07-23T15:54:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=92484ad014f2e2b0e0e7bbc1610951e978cf557c'/>
<id>urn:sha1:92484ad014f2e2b0e0e7bbc1610951e978cf557c</id>
<content type='text'>
[ Upstream commit db4b9eefc8ee0bcaeee4d5e6a7313905f6a2fe7c ]

On a zoned filesystem a freed-but-still-dirty tree block is written out
as zeros (EXTENT_BUFFER_ZONED_ZEROOUT) only to keep the zone write
pointer advancing. btree_csum_one_bio() implemented this by memzeroing
the extent buffer's own folios before submission.

That destroys the in-memory buffer while it may still be referenced. In
particular btrfs_free_tree_block() can run on it afterwards and reads
the header to add a delayed reference; once the header has been zeroed
it frees bytenr 0 and corrupts the extent tree (the
btrfs_header_bytenr(buf) != 0 ASSERT in btrfs_free_tree_block(), or an
"unable to find ref" abort). It is flaky and reproduces under fsstress,
e.g. generic/461 and generic/013.

Write the zeros to disk from the shared zero page instead and leave the
extent buffer content untouched, so any later reference - including the
delayed reference from btrfs_free_tree_block() - still sees a valid
header. end_bbio_meta_write() now clears writeback on the buffer's own
folios, as the bio no longer carries them.

Fixes: aa6313e6ff2b ("btrfs: zoned: don't clear dirty flag of extent buffer")
Assisted-by: LLM (debugging, commit message)
Reviewed-by: Boris Burkov &lt;boris@bur.io&gt;
Signed-off-by: Johannes Thumshirn &lt;johannes.thumshirn@wdc.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: retry verity reads for not-uptodate Merkle folios</title>
<updated>2026-09-14T11:35:43+00:00</updated>
<author>
<name>Yichong Chen</name>
<email>chenyichong@uniontech.com</email>
</author>
<published>2026-07-22T02:54:35+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=12b6d1a1715cbced2e445ca353f9c9987b8636e2'/>
<id>urn:sha1:12b6d1a1715cbced2e445ca353f9c9987b8636e2</id>
<content type='text'>
[ Upstream commit 8cc569696dac51fc62bb39b3b8f530582b916d29 ]

btrfs_read_merkle_tree_page() can find a folio in the mapping that is not
uptodate.  After taking the folio lock, the current code treats that state
as a read error and returns -EIO.

That can make a previous transient read failure sticky.  If the failed read
left a not-uptodate folio in the mapping, later callers find that folio and
fail instead of retrying the read.

Keep the existing page-cache insertion and locking order, but retry the
Merkle item read when a not-uptodate folio is found in the mapping.  Also
unlock the folio when read_key_bytes() fails so that a later caller can
lock it and retry the read.

Fixes: 06ed09351b67 ("btrfs: convert btrfs_read_merkle_tree_page() to use a folio")
Reviewed-by: Boris Burkov &lt;boris@bur.io&gt;
Signed-off-by: Yichong Chen &lt;chenyichong@uniontech.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: always wait for ordered extents to avoid OE races</title>
<updated>2026-09-14T11:35:42+00:00</updated>
<author>
<name>Qu Wenruo</name>
<email>wqu@suse.com</email>
</author>
<published>2026-06-26T23:32:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=58ce50b1802a446d3b26df1b9c58be1ab8913d0e'/>
<id>urn:sha1:58ce50b1802a446d3b26df1b9c58be1ab8913d0e</id>
<content type='text'>
[ Upstream commit ec78575dde998c21be7e0cb2503b5620f34b6255 ]

[BUG]
Syzbot reported a bug that there can be conflicting OEs for the same
range:

  BTRFS critical (device loop4): panic in insert_ordered_extent:264: overlapping ordered extents, existing oe file_offset 16384 num_bytes 430080 flags 0x1089, new oe file_offset 16384 num_bytes 430080 flags 0x80 (errno=-17 Object alrea[  179.162726][ T6897] BTRFS critical (device loop4): panic in insert_ordered_extent:264: overlapping ordered extents, existing oe file_offset 16384 num_bytes 430080 flags 0x1089, new oe file_offset 16384 num_bytes 430080 flags 0x80 (errno=-17 Object already exists)
  ------------[ cut here ]------------
  kernel BUG at fs/btrfs/ordered-data.c:264!
  Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026
  RIP: 0010:btrfs_alloc_ordered_extent+0x943/0xad0
  Call Trace:
   &lt;TASK&gt;
   cow_file_range+0x744/0x12a0
   fallback_to_cow+0x5ea/0xa00
   run_delalloc_nocow+0x110c/0x17a0
   btrfs_run_delalloc_range+0xbe4/0x1c20
   writepage_delalloc+0x104d/0x1ba0
   btrfs_writepages+0x1667/0x28b0
   do_writepages+0x338/0x560
   filemap_fdatawrite_range+0x1f2/0x300
   btrfs_fdatawrite_range+0x54/0xf0
   btrfs_direct_write+0x6a0/0xc30
   btrfs_do_write_iter+0x329/0x790
   do_iter_readv_writev+0x624/0x8d0
   vfs_writev+0x34c/0x990
   __se_sys_pwritev2+0x17a/0x2a0
   do_syscall_64+0x174/0x580
   entry_SYSCALL_64_after_hwframe+0x77/0x7f
   &lt;/TASK&gt;
  ---[ end trace 0000000000000000 ]---

[CAUSE]
Since commit ff66fe666233 ("btrfs: fix incorrect buffered IO fallback
for append direct writes"), if the direct IO finished short, we will
revert the isize back to the original one, so that append writes can be
respected during the buffered fallback.

Normally we rely on lock_and_cleanup_extent_if_need() function during
buffered writeback to wait for any existing ordered extents.

But that ordered extent waiting only happens if the start_pos is inside
the isize.
Since we have reverted the isize during failed direct IO, we will not
wait for any ordered extents.

This means we can have a race where the direct IO OE is still in the
tree, finished but not yet removed, then we're inserting the OE for the
buffered write, causing the above crash.

[FIX]
Make the OE wait to be unconditional, to handle the reverted isize
situation.

And since lock_and_cleanup_extent_if_need() now either lock the
extents or return -EAGAIN, also remove the branches that handles
no-extent-locked cases, and rename it to remove the "_if_need" suffix.

The following micro benchmark shows the runtime difference for
btrfs_buffered_write(), doing `xfs_io -f -c "pwrite 0 1m"` workload,
all values are the average runtime in nano seconds.

      function runtime              |   before    |     after
 -----------------------------------+-------------+---------------
 lock_and_cleanup_extent_if_need()  |     58.2    |    183.0
 btrfs_buffered_write()             |   2115.6    |   2973.3

The overall runtime of btrfs_buffered_write() is still pretty
tiny (still less than 3 micro seconds), I'd say the extra cost is still
acceptable.

An alternative to fix this problem is to wait ordered extents during
iomap_end() where the isize revert is done.

But that solution will break nowait requirement, as if a nowait direct
IO finished short, we have to wait for the OEs unconditionally or the
next append buffered IO can still hit the same problem.

So here we have to move the wait cost to buffered write, but at least
the code is slightly more streamline.

Reported-by: syzbot+ba2afde329fc27e3f22e@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=ba2afde329fc27e3f22e
Fixes: ff66fe666233 ("btrfs: fix incorrect buffered IO fallback for append direct writes")
Reviewed-by: Filipe Manana &lt;fdmanana@suse.com&gt;
Signed-off-by: Qu Wenruo &lt;wqu@suse.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: merge setting ret and return ret</title>
<updated>2026-09-14T11:35:42+00:00</updated>
<author>
<name>David Sterba</name>
<email>dsterba@suse.com</email>
</author>
<published>2025-12-09T19:06:49+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=76b7c505c74f4e5e77b39fc387d3306b58076de8'/>
<id>urn:sha1:76b7c505c74f4e5e77b39fc387d3306b58076de8</id>
<content type='text'>
[ Upstream commit 4b117be65ff41efae3694df449b9badb4e9d142e ]

In many places we have pattern:

	ret = ...;
	return ret;

This can be simplified to a direct return, removing 'ret' if not
otherwise needed. The places in self tests are not converted so we can
add more test cases without changing surrounding code
(extent-map-tests.c:test_case_4()).

Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Stable-dep-of: ec78575dde99 ("btrfs: always wait for ordered extents to avoid OE races")
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: make btrfs_repair_io_failure() handle bs &gt; ps cases without large folios</title>
<updated>2026-09-14T11:35:42+00:00</updated>
<author>
<name>Qu Wenruo</name>
<email>wqu@suse.com</email>
</author>
<published>2025-11-10T22:41:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=8a34b30bd1bfc2870fff1904a685888847a8b56e'/>
<id>urn:sha1:8a34b30bd1bfc2870fff1904a685888847a8b56e</id>
<content type='text'>
[ Upstream commit 2574e9011018a1d6d3da8d03d0bfc4e2675dee2a ]

Currently btrfs_repair_io_failure() only accept a single @paddr
parameter, and for bs &gt; ps cases it's required that @paddr is backed by
a large folio.

That assumption has quite some limitations, preventing us from utilizing
true zero-copy direct-io and encoded read/writes.

To address the problem, enhance btrfs_repair_io_failure() by:

- Accept an array of paddrs, up to 64K / PAGE_SIZE entries
  This kind of acts like a bio_vec, but with very limited entries, as the
  function is only utilized to repair one fs data block, or a tree block.

  Both have an upper size limit (BTRFS_MAX_BLOCK_SIZE, i.e. 64K), so we
  don't need the full bio_vec thing to handle it.

- Allocate a bio with multiple slots
  Previously even for bs &gt; ps cases, we only passed in a contiguous
  physical address range, thus a single slot will be enough.

  But not anymore, so we have to allocate a bio structure, other than
  using the on-stack one.

- Use on-stack memory to allocate @paddrs array
  It's at most 16 pages (4K page size, 64K block size), will take up at
  most 128 bytes.
  I think the on-stack cost is still acceptable.

- Add one extra check to make sure the repair bio is exactly one block

- Utilize btrfs_repair_io_failure() to submit a single bio for metadata
  This should improve the read-repair performance for metadata, as now
  we submit a node sized bio then wait, other than submit each block of
  the metadata and wait for each submitted block.

- Add one extra parameter indicating the step
  This is due to the fact that metadata step can be as large as
  nodesize, instead of sectorsize.
  So we need a way to distinguish metadata and data repair.

- Reduce the width of @length parameter of btrfs_repair_io_failure()
  Since we only call btrfs_repair_io_failure() on a single data or
  metadata block, u64 is overkilled.
  Use u32 instead and add one extra ASSERT()s to make sure the length
  never exceed BTRFS_MAX_BLOCK_SIZE.

Signed-off-by: Qu Wenruo &lt;wqu@suse.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Stable-dep-of: ec78575dde99 ("btrfs: always wait for ordered extents to avoid OE races")
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: defrag: fix deadlock between defrag and delalloc space reservation</title>
<updated>2026-09-14T11:35:42+00:00</updated>
<author>
<name>Filipe Manana</name>
<email>fdmanana@suse.com</email>
</author>
<published>2026-06-23T15:56:15+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=a9701dd06e82c345fe70debdf8835b7eb5000c59'/>
<id>urn:sha1:a9701dd06e82c345fe70debdf8835b7eb5000c59</id>
<content type='text'>
[ Upstream commit ba02eab28041f9a4bbe9fc90c7249644fef6de0f ]

While running fsstress with autodefrag and flushoncommit, hit a deadlock
due to the fact that defrag reserves delalloc space while it's holding
dirty and locked folios, besides the extent range lock. The stack traces
are the following:

   [958.624] task:kworker/u50:3   state:D stack:0     pid:20365 tgid:20365 ppid:2      task_flags:0x4208060 flags:0x00080000
   [958.626] Workqueue: events_unbound btrfs_async_reclaim_metadata_space [btrfs]
   [958.627] Call Trace:
   [958.628]  &lt;TASK&gt;
   [958.628]  __schedule+0x4be/0x10f0
   [958.629]  ? preempt_count_add+0x69/0xa0
   [958.630]  schedule+0x26/0xd0
   [958.631]  wait_current_trans+0x102/0x160 [btrfs]
   [958.632]  ? __pfx_autoremove_wake_function+0x10/0x10
   [958.633]  start_transaction+0x374/0x900 [btrfs]
   [958.634]  btrfs_commit_current_transaction+0x1d/0x70 [btrfs]
   [958.635]  flush_space+0xca/0x5e0 [btrfs]
   [958.636]  ? _raw_spin_unlock+0x15/0x30
   [958.637]  ? btrfs_reduce_alloc_profile+0x8c/0x190 [btrfs]
   [958.639]  ? _raw_spin_unlock+0x15/0x30
   [958.640]  ? calc_available_free_space.isra.0+0x6f/0x110 [btrfs]
   [958.641]  do_async_reclaim_metadata_space+0x84/0x190 [btrfs]
   [958.642]  btrfs_async_reclaim_metadata_space+0x64/0x80 [btrfs]
   [958.644]  process_one_work+0x19d/0x3a0
   [958.644]  worker_thread+0x1c4/0x330
   [958.645]  ? __pfx_worker_thread+0x10/0x10
   [958.646]  kthread+0xfc/0x130
   [958.647]  ? __pfx_kthread+0x10/0x10
   [958.648]  ret_from_fork+0x1f7/0x2c0
   [958.648]  ? __pfx_kthread+0x10/0x10
   [958.649]  ret_from_fork_asm+0x1a/0x30
   [958.650]  &lt;/TASK&gt;
   [958.651] task:kworker/u49:7   state:D stack:0     pid:52990 tgid:52990 ppid:2      task_flags:0x4208060 flags:0x00080000
   [958.653] Workqueue: writeback wb_workfn (flush-btrfs-334)
   [958.655] Call Trace:
   [958.655]  &lt;TASK&gt;
   [958.656]  __schedule+0x4be/0x10f0
   [958.657]  ? __blk_flush_plug+0xe9/0x140
   [958.658]  schedule+0x26/0xd0
   [958.658]  io_schedule+0x42/0x70
   [958.659]  folio_wait_bit_common+0x12b/0x330
   [958.660]  ? folio_wait_bit_common+0x100/0x330
   [958.662]  ? __pfx_wake_page_function+0x10/0x10
   [958.663]  extent_write_cache_pages+0x599/0x830 [btrfs]
   [958.664]  ? acpi_fwnode_get_reference_args+0x1fa/0x270
   [958.665]  btrfs_writepages+0x77/0x130 [btrfs]
   [958.666]  ? __pfx_end_bbio_data_write+0x10/0x10 [btrfs]
   [958.667]  do_writepages+0xc6/0x160
   [958.668]  __writeback_single_inode+0x42/0x310
   [958.669]  writeback_sb_inodes+0x231/0x570
   [958.670]  wb_writeback+0x8a/0x340
   [958.671]  wb_workfn+0xbf/0x450
   [958.672]  ? finish_task_switch.isra.0+0xc1/0x350
   [958.673]  process_one_work+0x19d/0x3a0
   [958.673]  worker_thread+0x1c4/0x330
   [958.674]  ? __pfx_worker_thread+0x10/0x10
   [958.675]  kthread+0xfc/0x130
   [958.676]  ? __pfx_kthread+0x10/0x10
   [958.676]  ret_from_fork+0x1f7/0x2c0
   [958.677]  ? __pfx_kthread+0x10/0x10
   [958.678]  ret_from_fork_asm+0x1a/0x30
   [958.679]  &lt;/TASK&gt;
   [958.679] task:btrfs-cleaner   state:D stack:0     pid:296750 tgid:296750 ppid:2      task_flags:0x208040 flags:0x00080000
   [958.681] Call Trace:
   [958.682]  &lt;TASK&gt;
   [958.682]  __schedule+0x4be/0x10f0
   [958.683]  schedule+0x26/0xd0
   [958.684]  handle_reserve_ticket+0x1b9/0x2c0 [btrfs]
   [958.685]  ? __pfx_autoremove_wake_function+0x10/0x10
   [958.686]  reserve_bytes+0x283/0x4c0 [btrfs]
   [958.687]  btrfs_reserve_metadata_bytes+0x18/0xb0 [btrfs]
   [958.688]  btrfs_delalloc_reserve_metadata+0x121/0x320 [btrfs]
   [958.690]  btrfs_delalloc_reserve_space+0x46/0xb0 [btrfs]
   [958.691]  btrfs_defrag_file+0x903/0x1110 [btrfs]
   [958.692]  btrfs_run_defrag_inodes+0x334/0x430 [btrfs]
   [958.694]  cleaner_kthread+0x97/0x1c0 [btrfs]
   [958.694]  ? __pfx_cleaner_kthread+0x10/0x10 [btrfs]
   [958.696]  kthread+0xfc/0x130
   [958.696]  ? __pfx_kthread+0x10/0x10
   [958.697]  ret_from_fork+0x1f7/0x2c0
   [958.698]  ? __pfx_kthread+0x10/0x10
   [958.699]  ret_from_fork_asm+0x1a/0x30
   [958.700]  &lt;/TASK&gt;
   [958.716] task:fsstress        state:D stack:0     pid:296769 tgid:296769 ppid:296768 task_flags:0x400140 flags:0x00080000
   [958.718] Call Trace:
   [958.719]  &lt;TASK&gt;
   [958.719]  __schedule+0x4be/0x10f0
   [958.720]  ? preempt_count_add+0x69/0xa0
   [958.721]  schedule+0x26/0xd0
   [958.722]  wb_wait_for_completion+0x79/0xc0
   [958.723]  ? __pfx_autoremove_wake_function+0x10/0x10
   [958.724]  __writeback_inodes_sb_nr+0xc5/0xf0
   [958.725]  try_to_writeback_inodes_sb+0x55/0x70
   [958.726]  btrfs_commit_transaction+0x19d/0xeb0 [btrfs]
   [958.727]  ? start_transaction+0x343/0x900 [btrfs]
   [958.728]  btrfs_mksubvol+0x28b/0x4e0 [btrfs]
   [958.729]  btrfs_mksnapshot+0x74/0xa0 [btrfs]
   [958.730]  __btrfs_ioctl_snap_create+0x194/0x210 [btrfs]
   [958.732]  btrfs_ioctl_snap_create_v2+0xef/0x150 [btrfs]
   [958.733]  btrfs_ioctl+0x7ec/0x2a70 [btrfs]
   [958.734]  ? __virt_addr_valid+0xe4/0x180
   [958.735]  ? __check_object_size+0x1cd/0x1f0
   [958.736]  ? kmem_cache_free+0x146/0x380
   [958.737]  ? _raw_spin_unlock+0x15/0x30
   [958.738]  ? do_sys_openat2+0x83/0xd0
   [958.739]  __x64_sys_ioctl+0x92/0xe0
   [958.740]  do_syscall_64+0x60/0x590
   [958.741]  ? clear_bhb_loop+0x60/0xb0
   [958.742]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
   [958.743] RIP: 0033:0x7f4431e108db
   [958.744] RSP: 002b:00007ffcd147db20 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
   [958.746] RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 00007f4431e108db
   [958.747] RDX: 00007ffcd147eb90 RSI: 0000000050009417 RDI: 0000000000000005
   [958.749] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
   [958.751] R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffcd147fbf0
   [958.752] R13: 00007ffcd147eb90 R14: 0000000000000005 R15: 0000000000000003
   [958.754]  &lt;/TASK&gt;

What happens is the following:

1) The cleaner kthread is running autodefrag, and in defrag_one_range()
   it acquired all the folios for the range and locked them.

   Then it locked the extent range in the inode's iotree.

   It got two subranges from defrag_collect_targets(), the first one
   with folio A and the second one with folio B.

   After it defragged the first subrange, folio A remains locked and
   dirty - it's only unlocked when defrag_one_range() returns.

   When it attempts to defrag the second subrange (containing folio B),
   btrfs_delalloc_reserve_space() creates a space reservation ticket,
   due to lack of free metadata space and blocks waiting for the async
   metadata reclaim task to free space and wake it up;

2) The async reclaim metadata task attempts to commit the current
   transaction, but it blocks because there is another task that
   started the commit first;

3) A task creating a snapshot is committing the transaction and
   because the fs was mounted with flushoncommit, it calls
   try_to_writeback_inodes_sb(), which spawns a task to flush
   delalloc and waits for it to complete;

4) The task flushing delalloc (kworker/u49:7), finds that folio A for
   the inode being defragged is dirty, so it tries to lock it...

   But it blocks because folio A is locked by the defrag task (the
   cleaner kthread) which is blocked waiting for the reservation
   ticket to be served, but the async reclaim metadata task is
   blocked waiting for the transaction commit, which in turn is
   blocked waiting for the delalloc flush task, which is trying to
   lock folio A, resulting in a deadlock.

The same type of problem can happen if the async reclaim task starts to
flush delalloc, as that requires both locking the folio and the extent
range in the inode's io tree, and in this case we don't need the fs to
be mounted with flushoncommit. This type of problem has ocurred several
times in the past with reflinks for example, where we had a dirty folio
while holding the extent range locked and then starting a transaction
blocked waiting for the async reclaim task due to lack of free metadata
space.

So fix this by reserving delalloc space before locking folios and locking
the extent range in the inode's iotree. We can not simply unlock the
folios for each subrange given by defrag_collect_targets() after we defrag
it because the same folio may be present too in the next subrange (due to
large folios).

Fixes: 22b398eeeed4 ("btrfs: defrag: introduce helper to defrag a contiguous prepared range")
Reviewed-by: Qu Wenruo &lt;wqu@suse.com&gt;
Signed-off-by: Filipe Manana &lt;fdmanana@suse.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Signed-off-by: Sasha Levin &lt;sashal@kernel.org&gt;
</content>
</entry>
<entry>
<title>btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag</title>
<updated>2026-09-07T15:22:32+00:00</updated>
<author>
<name>Qu Wenruo</name>
<email>wqu@suse.com</email>
</author>
<published>2026-06-08T23:13:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.landau.one/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=25128202a8df52bce037c48045f97a389c3860d5'/>
<id>urn:sha1:25128202a8df52bce037c48045f97a389c3860d5</id>
<content type='text'>
commit 15f7c86215e8d5f14b24127fa88af6c79363d50e upstream.

[TEST FAILURE]
The test case generic/628 will fail if MOUNT_OPTIONS is set to
"-o nodatasum":

  FSTYP         -- btrfs
  PLATFORM      -- Linux/x86_64 btrfs-vm 7.1.0-rc4-custom+ #383 SMP PREEMPT_DYNAMIC Sat May 30 07:35:42 ACST 2026
  MKFS_OPTIONS  -- -O bgt -K /dev/mapper/test-scratch1
  MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch

#  generic/628  1s ... - output mismatch (see /home/adam/xfstests/results//generic/628.out.bad)
#     --- tests/generic/628.out	2022-05-11 11:25:30.816666664 +0930
#     +++ /home/adam/xfstests/results//generic/628.out.bad	2026-06-08 18:56:49.878542927 +0930
#     @@ -8,8 +8,9 @@
#      310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
#      310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/d
#      test reflink flag not set iflag
#     +XFS_IOC_CLONE: Invalid argument
#      310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
#     -310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/b
#     +d41d8cd98f00b204e9800998ecf8427e  SCRATCH_MNT/b
#     ...

[CAUSE]
The direct cause is that after "chattr +S", the btrfs inode will lose its
NODATASUM flag inherited from the mount option. E.g.:

  # mkfs.btrfs -f $dev
  # mount $dev $mnt -o nodatasum
  # touch $mnt/foobar
  # sync
  # btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
         item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
         	generation 9 transid 9 size 0 nbytes 0
         	block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
         	sequence 1 flags 0x1(NODATASUM)
         	                     ^^^^^^^^^ Proper NODATASUM flag

  # chattr +S $mnt/foobar
  # sync
  # btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
  	item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
         	generation 9 transid 10 size 0 nbytes 0
         	block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
         	sequence 2 flags 0x20(SYNC)
         	                      ^^^^ Only the new SYNC flag

This makes the inode drop the old NODATASUM flag, while the new reflink
destination will still inherit the NODATASUM flag.  The mismatching
NODATASUM flags will cause the reflink to fail.

The root cause is that, inside btrfs_fileattr_set() if no FS_NOCOW_FL is
set, we remove both NODATASUM and NODATACOW flag.

However we should not touch NODATASUM flag, as data COW doesn't require
checksum.  Only NODATACOW implies NODATASUM, but DATACOW doesn't imply
DATASUM.

The deeper problems are:

- Fileattr API is too binary
  It either clears or sets a flag, there is no "do not change" option.
  So that why "chattr +S" implies "chattr -C", and is forcing us to
  change NODATACOW along with NODATASUM flag.

- No way to change NODATASUM through fileattr API
  In fact NODATASUM can only be modified through mount option.

The deeper problems are much harder to attack.

[FIX]
Remove NODATACOW flag when FS_NOCOW_FL is not set, but only remove
NODATASUM if "nodatasum" mount option is not set.

This allows the existing "chattr +C" then "chattr -C" to remove
both NODATACOW and NODATASUM flags on a default mount.

But for a mount with "nodatasum" option, the NODATASUM inode flag will
persist through either "chattr +C" and "chattr -C".

Fixes: 7e97b8daf634 ("btrfs: allow setting NOCOW for a zero sized file via ioctl")
Cc: stable@vger.kernel.org
Reviewed-by: Filipe Manana &lt;fdmanana@suse.com&gt;
Signed-off-by: Qu Wenruo &lt;wqu@suse.com&gt;
Reviewed-by: David Sterba &lt;dsterba@suse.com&gt;
Signed-off-by: David Sterba &lt;dsterba@suse.com&gt;
Signed-off-by: Greg Kroah-Hartman &lt;gregkh@linuxfoundation.org&gt;
</content>
</entry>
</feed>
