| Age | Commit message (Collapse) | Author |
|
Inside reflink.c we still have a lot of functions passing VFS inode
pointers, then internally convert them into btrfs_inode pointers.
For example, inside btrfs_clone(), we have 12 BTRFS_I() call sites,
while only 3 callsites that really require a VFS inode pointer.
Do the cleanup to convert the following functions to pass a btrfs_inode
pointer instead of a vanilla inode pointer:
- btrfs_clone()
- btrfs_extent_same_range()
- clone_finish_inode_update().
Which covers all ad-hoc BTRFS_I() call sites inside reflink.c.
Reviewed-by: Daniel Vacek <neelx@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
We are using atomic types for the log_commit array of struct btrfs_root
but all we need is simple booleans. The log_commit array elements are
always protected by the root's log_mutex, both for writes and reads, so
we can use a simple boolean. The use of atomics if from the very early
days of the log tree code where the access to the fields was not protected
by any lock.
So switch to simple booleans, which results in cheaper code and slightly
reduces the object size too.
Reviewed-by: Boris Burkov <boris@bur.io>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
We check for the exit condition after we add ourselves to the wait queue
and before we unlock the root's log_mutex, sleep and lock again log_mutex.
This is not incorrect, but it's not optimal since in the first iteration
this is pointless because we already know that root->log_commit[index] is
not zero, so we should check the exit condition only after unlocking
log_mutex, sleeping, waking up and locking again the log_mutex.
So move the check for the exit condition to bottom of the loop, after we
were woken and locked log_mutex again.
Reviewed-by: Boris Burkov <boris@bur.io>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Instead of having every caller check for root->log_commit[] being non-zero
and then call wait_log_commit(), move the check into wait_log_commit() and
have the callers call it unconditionally.
Reviewed-by: Boris Burkov <boris@bur.io>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
We have the log batch counter defined per root which is now useless after
the previous patch (titled: "btrfs: stop sleeping for one jiffy in non-ssd
mounts during log commit"). The counter is incremented early in the fsync
path, before and after flushing dellaloc and waiting for writeback, and
then the counter is read during the log sync path. The goal was to wait
for tasks that are about to join a log transaction, so that we could
reduce the amount of IO and log syncing (flush all log tree extent buffers
and write super blocks), but that mechanism does not work since if there
are currently no log writers, btrfs_sync_log() does not unlock the root's
log_mutex, so no new log writers can join the log transaction. Having
concurrent fsync tasks increasing the log_batch counter only makes us loop
unnecessarily in btrfs_sync_log() - that is always true since the previous
patch mentioned above and was true before that patch only when not using
the "-o ssd" mount option (which is activated by default if the filesystem
does not have rotational devices).
So remove the log batch counter. No performance changes were observed
after removing it.
Reviewed-by: Boris Burkov <boris@bur.io>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Joining/starting a log transaction tracks if we ever had more than one task
concurrently logging by setting the flag BTRFS_ROOT_MULTI_LOG_TASKS in the
respective root. Once set, this flag remains for the rest of the lifetime
of the transaction, only cleared when we don't have a log root and need to
create a new one (transaction commits drop log roots).
During log commit, if we are not on a ssd mount (or use the -o nossd mount
option) and the BTRFS_ROOT_MULTI_LOG_TASKS flag is set, we sleep for one
jiffy with the excuse to allow future log writers to join and log inodes
and then commit a larger log transaction to reduce overall IO. However
this is extremely inefficient because:
1) If at some point we had multiple tasks logging concurrently but now
we have only one task at a time, we force it to wait for 1 jiffy;
2) One jiffy can vary between 1ms to 10ms, depending on the kernel
config option CONFIG_HZ, which by default has a value of 250HZ and
that corresponds to 4ms - that is a lot.
This massively reduces the latency of fsyncs for non-ssd mounts, even
on consumer grade spinning disks.
Remove this mechanism to track if we have (or ever had) multiple tasks
logging and wait for 1 jiffy.
The following fio test was used to benchmark:
$ cat fio-buffered-fsync.sh
DEV=/dev/sdj
MNT=/mnt/sdj
MOUNT_OPTIONS=""
MKFS_OPTIONS=""
if [ $# -ne 6 ]; then
echo "Use $0 NUM_JOBS FILE_SIZE IO_SIZE FSYNC_FREQ BLOCK_SIZE [write|randwrite]"
exit 1
fi
NUM_JOBS=$1
FILE_SIZE=$2
IO_SIZE=$3
FSYNC_FREQ=$4
BLOCK_SIZE=$5
WRITE_MODE=$6
if [ "$WRITE_MODE" != "write" ] && [ "$WRITE_MODE" != "randwrite" ]; then
echo "Invalid WRITE_MODE, must be 'write' or 'randwrite'"
exit 1
fi
cat <<EOF > /tmp/fio-job.ini
[writers]
rw=$WRITE_MODE
fsync=$FSYNC_FREQ
fallocate=none
group_reporting=1
direct=0
bs=$BLOCK_SIZE
ioengine=psync
filesize=$FILE_SIZE
io_size=$IO_SIZE
directory=$MNT
numjobs=$NUM_JOBS
EOF
echo
echo "Using config:"
echo
cat /tmp/fio-job.ini
echo
umount $MNT &> /dev/null
mkfs.btrfs -f $MKFS_OPTIONS $DEV
mount $MOUNT_OPTIONS $DEV $MNT
fio /tmp/fio-job.ini
umount $MNT
Running the script as: ./fio-buffered-fsync.sh 8 64M 64M 1 4K randwrite
Before patch:
WRITE: bw=2647KiB/s (2711kB/s), 2647KiB/s-2647KiB/s (2711kB/s-2711kB/s), io=512MiB (537MB), run=198055-198055msec
After patch:
WRITE: bw=14.9MiB/s (15.6MB/s), 14.9MiB/s-14.9MiB/s (15.6MB/s-15.6MB/s), io=512MiB (537MB), run=34471-34471msec
That's about 5.7 times faster.
Reviewed-by: Boris Burkov <boris@bur.io>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
The correct path of the "read_policy" module parameter should be
/sys/module/btrfs/parameters/read_policy. Fix it.
Acked-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Zenghui Yu <zenghui.yu@linux.dev>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
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 <boris@bur.io>
Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
During an interrupted mount, I got the following messages:
workqueue: Failed to create a rescuer kthread for wq "btrfs-qgroup-rescan": -EINTR
BTRFS error (device dm-3): open_ctree failed: -12
Workqueue code is outputting a human readable error string, meanwhile
we're still using a numeric error code.
So follow the workqueue code to use "%pe" format, which will
automatically convert an error pointer to the human readable string.
However this is a minor pitfall, if the return value is not an error
code, e.g. a positive number, "%pe" with "ERR_PTR(ret)" will output the
pointer as a hash value, e.g.:
ret=1 %pe out=0000000019414716
ret=-22 %pe out=-EINVAL
So we should not use this "%pe" output for callsites that are known to
return positive values.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
That macro is only utilized 4 times, all inside file.c, while we have
tons of open-coded usages. And since it's a macro, there is no proper
type checks at all.
There isn't much need for such a rarely utilized macro.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Commit f9a48549a15a ("btrfs: inhibit extent buffer writeback to prevent
COW amplification") tracks the extent buffers a transaction handle has
inhibited in a per-handle xarray. Keying the tracking to the transaction
handle is correct, but using an xarray for it causes two problems in
production.
First, a write_iops regression. Every COW calls
btrfs_inhibit_eb_writeback() from btrfs_force_cow_block() and
should_cow_block(), which does an xa_store() keyed by eb->start. The
kernel test robot reported a 22.6% fio.write_iops regression on a
single-task 4k randwrite workload (ftruncate ioengine, buffered IO) on
btrfs. The cost is the per-COW xarray store done on every COW'd block.
Replacing it with a non-allocating fixed buffer recovers the lost
throughput, and that buffer does more per-COW bookkeeping yet still
recovers, so the cost is the xarray operation itself rather than the
extra tracking work.
Second, an unbounded cleanup walk. btrfs_uninhibit_all_eb_writeback()
iterates every eb the handle inhibited with xa_for_each(). A single
handle that COWs a very large number of blocks (inode eviction, or
truncate of a file with many extents, where btrfs_truncate_inode_items()
loops over many search_again descents under one handle) makes that walk
arbitrarily long. It runs in __btrfs_end_transaction() before
num_writers is dropped, so it blocks the committing thread; this shows up
as multi-second stalls and RCU stall reports.
Replace the xarray with a fixed inline array on btrfs_trans_handle,
managed with a CLOCK (second-chance) eviction policy. Inhibiting a buffer
becomes an array append with no allocation and no tree walk, and the
end-of-handle cleanup is bounded by the array size.
The set that actually needs protection is the working set the handle
revisits across search_again descents, the search path frontier, which is
on the order of the tree height. It is not every block the handle ever
COWs. should_cow_block() re-inhibiting an already tracked buffer marks it
referenced, so revisited buffers survive eviction while write-once buffers
are reclaimed first. A small fixed buffer is therefore enough where a
non-evicting array would either overflow or have to grow without bound.
BTRFS_INHIBITED_EBS_SLOTS is 8 and the reference bits pack into a u32.
The CLOCK eviction is what justifies the extra complexity over a plain
non-evicting array. The test workload stresses amplification: it removes
16 heavily fragmented 64 MiB files in one transaction while background
writeback keeps writing out in-use metadata. A re-COW event is a buffer
already COWed in the running transaction that was written back and then
COWed again; the figure below is the ratio of re-COW events to first-COW
events summed across the eviction (n=5, lower is better):
tracking re-COW per first-COW
no inhibition 6.1
non-evicting array, 32 slots 3.8
CLOCK array, 8 slots (this patch) 1.6
unbounded xarray (reverted) 1.4
The non-evicting array fills with write-once buffers and stops covering
the buffers the handle keeps revisiting, so even at four times the slots
it leaves most of the amplification. CLOCK evicts the cold buffers and
keeps the revisited ones, recovering almost all of the unbounded benefit.
The eviction policy, not the buffer size, is what closes the gap.
eb->writeback_inhibitors and the WB_SYNC_ALL bypass in
lock_extent_buffer_for_io() are unchanged, so fsync and commit behavior
are unaffected. A reference is taken on each tracked buffer so it cannot
be freed while the array points at it; eviction drops that reference and
the inhibitor count.
There's another testing report, showing 20% latency improvement on
reflink and deduplication synthetic benchmark. Full detailed report at
https://github.com/lcf0399/linux-regression-evidence/tree/main/btrfs-remap-writeback-inhibition-v2 .
Link: https://lore.kernel.org/all/CANGjgd=fQkHht2PdDi-+EAdzWH7UtxxWhhJ7b80Rr17PbpgxOw@mail.gmail.com/
Reported-by: kernel test robot <oliver.sang@intel.com>
Fixes: f9a48549a15a ("btrfs: inhibit extent buffer writeback to prevent COW amplification")
Closes: https://lore.kernel.org/oe-lkp/202603112240.f7605968-lkp@intel.com
Tested-by: Chengfeng Lin <lin2530632123@gmail.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Sun YangKai <sunk67188@gmail.com>
Signed-off-by: Leo Martins <loemra.dev@gmail.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Avoid allocating the heuristic buckets separately from the workspace,
the lifetime is the same.
The new size of struct heuristic_ws is 2112. SLUB merges same/similar
sized structures for the named caches, so there's a chance such size
already exists on the system, like below:
$ grep 2112 /proc/slabinfo
sighand_cache 593 1335 2112 15 8
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
For a filesystem which has btrfs read-only property set to true, all
write operations including acl and xattr should be denied. However, acl
can still be set even if btrfs ro property is true.
This happens because no function on the set_acl code path checks the root
is readonly or not. It was checked in btrfs_setxattr_trans() but got
removed in commit 353c2ea735e4 ("btrfs: remove redundant readonly root
check in btrfs_setxattr_trans")
That commit didn't check if all the callers properly check the root's
read-only flag. A previous fix is commit b51111271b03 ("btrfs: check if
root is readonly while setting security xattr").
Always check if the root is read-only before performing the set acl
operation.
Fixes: 353c2ea735e4 ("btrfs: remove redundant readonly root check in btrfs_setxattr_trans")
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Sun YangKai <sunyangkai@fnnas.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
During relocation recovery, each fs root gets a reference to its relocation
root. If loading or adding a later root fails, or if the first transaction
commit fails, btrfs_recover_relocation() jumps to out_unset before
merge_reloc_roots() and clean_dirty_subvols().
put_reloc_control() drops the list-owned relocation root references, but it
does not clear fs_root->reloc_root or drop the references owned by those
pointers. Mount cleanup only drops them when BTRFS_FS_ERROR is set, so an
error such as -ENOMEM while processing a later root can leave references
behind.
Keep temporary references to the fs roots associated during recovery. On
failure, clear their reloc_root pointers and drop the corresponding
references. Once the first transaction commit succeeds, drop only the
temporary fs root references and let the normal merge and cleanup paths
handle the relocation roots.
Fault injection on a pending-relocation image confirmed the cleanup gap.
With an injected first-commit failure, 25 fs roots had reloc_root set with
fs_error=0. With this fix, the same failure path drops that count to 0
before mount fails.
Fixes: f44deb7442ed ("btrfs: hold a ref on the root->reloc_root")
CC: stable@vger.kernel.org
Signed-off-by: Guanghui Yang <3497809730@qq.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Add sctx NULL check in the for loop condition of the sort_clone_roots
cleanup path for consistency with the else branch.
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Recently kernel RAID56 lib is trying to remove the unexpected
single-data-RAID56 (2 disks RAID5 or 3 disk RAID5) support, meanwhile
btrfs still supports such setup, which means in the long run btrfs has
to handle such corner case by ourselves.
Thankfully single-data-RAID56 is really RAID1/RAID1C3, since data and
P/Q stripes all match each other, rotation also makes no difference.
This patch will disguise those single-data-RAID56 chunks as
RAID1/RAID1C3 chunks.
This is done at two timings:
- Chunk read
- Chunk allocation
This is done by introducing btrfs_chunk_map::on_disk_type member, which
stores the type read from the on-disk metadata.
Meanwhile btrfs_chunk_map::type is calculated using on_disk_type.
For most profiles @type matches @on_disk_type, but for
single-data-RAID56, the @type will be RAID1/RAID1C3.
This method has a minimal impact on the fs, all other operations like
scrub and read-repair, are all based on the chunk map type, so the
disguise method will require no extra modification to those call sites.
Although there are still some locations that are checking against
block_group->flags, e.g. scrub. Those call sites will still get extra
limits assuming the bg is RAID56. But it should not cause any extra
problem.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
In the function fill_dummy_bgs(), bg->flags is assigned twice.
Just remove the second assignment.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Those two members are read from on-disk metadata, but never utilized.
And for new chunks we always set those members to BTRFS_STRIPE_LEN
anyway.
Thus there is no need to keep them inside btrfs_chunk_map.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
[FALSE ALERTS]
There is a bug report that the warning inside
invalidate_and_check_btree_folios() got triggered during btrfs/298:
BTRFS info (device sdd): first mount of filesystem f9bf732a-a19b-44b9-99a7-614ddff168e2
BTRFS info (device sdd): using crc32c checksum algorithm
BTRFS error (device sdd): failed to find fsid cb2fdb42-b638-4f2f-badd-4127467ba674 when attempting to open seed devices
BTRFS error (device sdd): failed to read chunk tree: -2
------------[ cut here ]------------
WARNING: disk-io.c:3342 at invalidate_and_check_btree_folios+0x260/0x3c0 [btrfs], CPU#4: mount/125993
CPU: 4 UID: 0 PID: 125993 Comm: mount Tainted: G W OE 7.1.0-rc7-custom+ #1 PREEMPT(full)
Hardware name: QEMU KVM Virtual Machine, BIOS edk2-20250812-19.fc42 08/12/2025
Call trace:
invalidate_and_check_btree_folios+0x260/0x3c0 [btrfs] (P)
open_ctree+0x1f50/0x23b0 [btrfs]
btrfs_get_tree+0x89c/0xc48 [btrfs]
vfs_get_tree+0x30/0x110
vfs_cmd_create+0x58/0xe8
__arm64_sys_fsconfig+0x39c/0x518
invoke_syscall.constprop.0+0x48/0x120
el0_svc_common.constprop.0+0x40/0xe8
do_el0_svc+0x24/0x38
el0_svc+0x50/0x310
el0t_64_sync_handler+0xa0/0xe8
el0t_64_sync+0x198/0x1a0
---[ end trace 0000000000000000 ]---
BTRFS warning (device sdd): unable to release extent buffer 365985792 owner 3 gen 17 refs 3 flags 0x5
[CAUSE]
In that invalidate_and_check_btree_folios() we wait for the eb to finish
its read, then check if it's only held by us and the btree inode.
If not, then do a warning as it may be still held, and could cause
problems.
But there is a small window where the check can lead to false alerts:
Thread A (Read endio) | Thread B (Unmount)
----------------------------------+-------------------------------------
end_bbio_meta_read() |
| The eb has one extra ref held |
| by the reader, and has |
| EXTENT_BUFFER_READING flag set | invalidate_and_check_btree_folios()
| | |
|- clear_extent_buffer_reading() | |
| | |- wait_on_bit_io();
| | | The EXTENT_BUFFER_READING flag is
| | | cleared
| | |- if (refcount_read(eb->refs) > 2)
| | The eb is held by the read, us
| | and btree inode, thus it
| | will trigger the warning
|- free_extent_buffer() |
[FIX]
Introduce a helper, free_extent_buffer_clear_reading().
If the new parameter, @clear_reading, is set, we will hold the spinlock
at the beginning of free_extent_buffer_clear_reading() to make sure the
EXTENT_BUFFER_READING flag is cleared inside the same critical section
of decreasing refs.
Now free_extent_buffer() will just call
free_extent_buffer_clear_reading() with @clear_reading set to false, so
no behavior change.
But for end_bbio_meta_read(), it will not clear_extent_buffer_reading()
directly, but pass @clear_reading as true.
Then inside invalidate_and_check_btree_folios(), hold the refs_lock
before reading refs.
So that we eliminate the race window completely.
Reported-by: Su Yue <glass.su@suse.com>
Link: https://lore.kernel.org/linux-btrfs/DC0C775E-13B3-47D9-9AB2-895BB11C029D@suse.com/
Fixes: 83f7e52b7ed1 ("btrfs: warn about extent buffer that can not be released")
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
commit 095be159f3eb ("btrfs: unify folio dirty flag clearing") replaced
the folio_clear_dirty_for_io() call in extent_write_cache_pages() with a
plain folio_test_dirty() check. Besides clearing the dirty flag,
folio_clear_dirty_for_io() also calls folio_mkclean(), which write-protects
the shared mmap PTEs mapping the folio. Note that we still do call
folio_clear_dirty_for_io() later in submit_one_sector() when we clear
dirty on the last sector of the folio (the only sector for non-subpage
cases). But we lost this early call in extent_write_cache_pages().
Without the extra write-protection, a process with the file mmap-ed can
modify a sector while it is being used by writeback in a way that
expects a stable folio (checksumming, compressing, copying, etc...)
without faulting, which manifests as a handful of concrete bugs.
1. For large folios or subpage sectorsize, it is possible to submit a bio
which does not cover the whole folio. When this happens, we will have a
bio in flight for a folio that we have *not* called
folio_clear_dirty_for_io() on. If a task with an existing mmap-ed PTE
writes (without faulting..) in this window, it can result in
corruptions. If the write arrives while the checksumming or writing itself
is underway, this can result in an invalid checksum and later corruption
reports on read. If the write arrives after checksumming/writing is done
but before the last sector dirty is cleared, then the write is present
in page cache but doesn't affect the dirty tracking and will be lost
when the folio is fully finished being submitted and the dirty bit
is cleared. This results in losing the write even if fsync() is called.
2. For zoned submissions which are done in batch separate from the main
extent_writepage() loop, we also risk csum violations for those
submissions. Zoned writes are clamped to max_zone_append_size and are
not aligned with folios, so a submission can span two folios. The first
folio being processed in extent_write_cache_pages() will call
extent_write_locked_range() which will submit the partial range of the
next folio, while the rest of that folio could still be dirty. So
clearing dirty on the submitted sectors doesn't call
folio_clear_dirty_for_io() and we have the same issue. Since
extent_write_cache_pages() skips these batch submitted folios (they are
already marked for writeback from submission by the preceding folio), we
must add the extra write protection in lock_delalloc_folios().
3. For inline extents this will subtly risk losing writes that happen
after/while we copy the inline extent but before we clear dirty on
the folio.
4. For folios spanning EOF, mmap could tamper with the zeroed bytes past
EOF and cause them to be persisted where future faults would improperly
see them instead of zeros.
5. Finally, for compressed extents, we risk modifying the folios while we
work on compressing them which will result in corrupted compressed data.
Specifically, in run_delalloc_compressed() we queue up work to do
compress_file_range() in BTRFS_COMPRESSION_CHUNK_SIZE (512K) chunks which
will call btrfs_folio_clamp_clear_dirty() on the range. For non-subpage,
this will always clear the whole folio, safely. For subpage, we risk a
partial clear here as well. In particular, imagine a 2M folio broken up
into 512K chunks of work which might start compression work on one chunk
before all the chunks compress_file_range() workers have gotten far
enough to finish clearing all the dirty bitmaps of the folio and getting
to folio_clear_dirty_for_io(). Large folios on the edges of submission
ranges are similarly at risk to be only partly cleared.
This particular gap was introduced by a second patch in the same series:
commit a4ef54dbb576 ("btrfs: make extent_range_clear_dirty_for_io() to handle sector size < page size cases")
We cannot simply restore the call to folio_clear_dirty_for_io() because
that also drops the dirty flag off the folio which violates invariants
introduced for large folios by
commit 334509ce9d07 ("btrfs: use dirty flag to check if an ordered extent needs to be truncated")
and results in failing to invalidate clean folios past i_size, resulting
in deadlocks.
Therefore, to fix it, leave the existing semantics w.r.t. the folio's
dirty flag (to preserve the correct invalidate behavior) but ensure that
the other aspect of folio_clear_dirty_for_io(), folio_mkclean(), is run
on the folio when we lock it for writeback.
Finally, to help prevent similar regressions in the future, add a debug
warning that triggers at the known corruption sites if we have failed to
write protect the folio.
Assisted-by: LLM (debug, reproduce, research fix, review patch)
Fixes: 095be159f3eb ("btrfs: unify folio dirty flag clearing")
Fixes: a4ef54dbb576 ("btrfs: make extent_range_clear_dirty_for_io() to handle sector size < page size cases")
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Boris Burkov <boris@bur.io>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
[BUG]
There is a lockdep report related to device scan:
======================================================
WARNING: possible circular locking dependency detected
7.2.0-20260712.rc2.git0.e3321fa3034d.300.fc44.s390x+debug #1 Not tainted
------------------------------------------------------
(udev-worker)/1653 is trying to acquire lock:
0000006919232220 (&type->i_mutex_dir_key#2){++++}-{3:3}, at: lookup_slow+0x3e/0x70
but task is already holding lock:
00000069238564d8 (&fs_devs->device_list_mutex){+.+.}-{3:3}, at: device_list_add.constprop.0+0x148/0xc60
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #5 (&fs_devs->device_list_mutex){+.+.}-{3:3}:
lock_acquire+0x150/0x3f0
__mutex_lock+0xba/0xdc0
mutex_lock_nested+0x32/0x40
write_all_supers+0x7a/0x670
btrfs_sync_log+0xae6/0xdd0
btrfs_sync_file+0x4fa/0x7a0
__s390x_sys_fsync+0x52/0xa0
__do_syscall+0x172/0x750
system_call+0x72/0x90
-> #4 (&fs_info->tree_log_mutex){+.+.}-{3:3}:
lock_acquire+0x150/0x3f0
__mutex_lock+0xba/0xdc0
mutex_lock_nested+0x32/0x40
btrfs_sync_log+0xaba/0xdd0
btrfs_sync_file+0x4fa/0x7a0
__s390x_sys_fsync+0x52/0xa0
__do_syscall+0x172/0x750
system_call+0x72/0x90
-> #3 (btrfs_trans_num_extwriters){.+.+}-{0:0}:
lock_acquire+0x150/0x3f0
join_transaction+0x108/0x680
start_transaction+0x21a/0x660
btrfs_join_transaction+0x32/0x40
btrfs_dirty_inode+0x52/0xf0
touch_atime+0x90/0xc0
filemap_read+0x446/0x450
vfs_read+0x208/0x370
ksys_read+0x88/0x120
__do_syscall+0x172/0x750
system_call+0x72/0x90
-> #2 (btrfs_trans_num_writers){.+.+}-{0:0}:
reacquire_held_locks+0x14c/0x240
__lock_release.isra.0+0xd8/0x380
lock_release+0xf6/0x270
percpu_up_read+0x28/0xf0
__btrfs_end_transaction+0x178/0x1f0
btrfs_dirty_inode+0x82/0xf0
touch_atime+0x90/0xc0
btrfs_file_mmap_prepare+0x8c/0xa0
__mmap_region+0x214/0x780
mmap_region+0x108/0x160
do_mmap+0x402/0x5a0
vm_mmap_pgoff+0x156/0x230
ksys_mmap_pgoff+0x17e/0x220
__s390x_sys_old_mmap+0xa8/0x140
__do_syscall+0x172/0x750
system_call+0x72/0x90
-> #1 (&mm->mmap_lock){++++}-{3:3}:
lock_acquire+0x150/0x3f0
__might_fault+0x7a/0xa0
filldir64+0x11c/0x210
offset_readdir+0x92/0x200
iterate_dir+0xcc/0x2d0
__do_sys_getdents64+0x7a/0x130
__do_syscall+0x172/0x750
system_call+0x72/0x90
-> #0 (&type->i_mutex_dir_key#2){++++}-{3:3}:
check_prev_add+0x160/0xf40
__lock_acquire+0x12aa/0x15a0
lock_acquire+0x150/0x3f0
down_read+0x5a/0x280
lookup_slow+0x3e/0x70
path_lookupat+0x1f0/0x370
filename_lookup+0xce/0x1f0
kern_path+0x48/0x70
is_same_device+0x146/0x300
device_list_add.constprop.0+0x1be/0xc60
btrfs_scan_one_device+0x13a/0x2f0
btrfs_control_ioctl+0x110/0x1e0
__s390x_sys_ioctl+0xfa/0x130
__do_syscall+0x172/0x750
system_call+0x72/0x90
other info that might help us debug this:
Chain exists of:
&type->i_mutex_dir_key#2 --> &fs_info->tree_log_mutex --> &fs_devs->device_list_mutex
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(&fs_devs->device_list_mutex);
lock(&fs_info->tree_log_mutex);
lock(&fs_devs->device_list_mutex);
rlock(&type->i_mutex_dir_key#2);
*** DEADLOCK ***
2 locks held by (udev-worker)/1653:
#0: 0000016c727051c8 (uuid_mutex){+.+.}-{3:3}, at: btrfs_control_ioctl+0x102/0x1e0
#1: 00000069238564d8 (&fs_devs->device_list_mutex){+.+.}-{3:3}, at: device_list_add.constprop.0+0x148/0xc60
stack backtrace:
CPU: 2 UID: 0 PID: 1653 Comm: (udev-worker) Not tainted 7.2.0-20260712.rc2.git0.e3321fa3034d.300.fc44.s390x+debug #1 PREEMPT
Hardware name: IBM 3931 A01 701 (LPAR)
Call Trace:
[<0000016c70680e3e>] dump_stack_lvl+0xae/0x108
[<0000016c7078aa44>] print_circular_bug+0x1a4/0x230
[<0000016c7078ac5c>] check_noncircular+0x18c/0x1b0
[<0000016c7078c030>] check_prev_add+0x160/0xf40
[<0000016c7078fbaa>] __lock_acquire+0x12aa/0x15a0
[<0000016c7078fff0>] lock_acquire+0x150/0x3f0
[<0000016c7180e2fa>] down_read+0x5a/0x280
[<0000016c70b88dde>] lookup_slow+0x3e/0x70
[<0000016c70b8f5d0>] path_lookupat+0x1f0/0x370
[<0000016c70b900ae>] filename_lookup+0xce/0x1f0
[<0000016c70b90218>] kern_path+0x48/0x70
[<0000016c70f5b4a6>] is_same_device+0x146/0x300
[<0000016c70f67cfe>] device_list_add.constprop.0+0x1be/0xc60
[<0000016c70f688da>] btrfs_scan_one_device+0x13a/0x2f0
[<0000016c70ed9cb0>] btrfs_control_ioctl+0x110/0x1e0
[<0000016c70b97d0a>] __s390x_sys_ioctl+0xfa/0x130
[<0000016c718004d2>] __do_syscall+0x172/0x750
[<0000016c718155d2>] system_call+0x72/0x90
[CAUSE]
Btrfs device scan will call is_same_device() with device_list_mutex
held. But is_same_device() will call kern_path() which will do path
resolution and lock the inode.
So device scan has the following lock sequence:
mutex_lock(device_list_mutex) from device_list_add()
|
v
inode_lock_shared() from lookup_slow() during kern_path().
Meanwhile another thread is fsyncing, which has the following
lock sequence:
inode_lock() from btrfs_inode_lock() inside btrfs_direct_write()
|
v
mutex_lock(tree_log_mutex() from btrfs_sync_log(), which is further
triggered from
iomap_dio_complete()->generic_write_sync()->btrfs_sync_file().
|
v
mutex_lock(device_list_mutex) from write_all_supers() inside
btrfs_sync_log().
So the device scan has a reversed lock sequence, compared to the fsync
one, this means we can have the following deadlock:
Device scan | Fsync
----------------------------------------+--------------------------------
device_list_mutex locked |
| inode locked
| try to lock device_list_mutex
try to lock inode |
[FIX]
Instead of a full path lookup, use dev_t to determine if two device
paths are pointing to the same block device.
Inside kernel dev_t is going to uniquely determine a block device, and
the device path lookup is already done by lookup_bdev(), which is done
without device_list_mutex held, thus no reversed locking sequence.
Reported-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Link: https://lore.kernel.org/linux-btrfs/5a9d9847-4ae6-43c4-afdc-6e5fa51d6117@linux.ibm.com/
Fixes: 2e8b6bc0ab41 ("btrfs: avoid unnecessary device path update for the same device")
Tested-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
The reclaim_mark field in struct btrfs_block_group was a u64 that was
incremented when marking block groups for reclaim during sweeping, but
the actual counter value was never used - only the zero/non-zero state
mattered for determining if a block group needed reclaim.
Convert it to a bool to properly reflect its usage and reduce memory
footprint by 8 bytes. Update assignments to use true/false instead of
increment and zero.
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Sun YangKai <sunk67188@gmail.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
The 'idmap' parameter is derived from 'file' that we also pass to
__btrfs_ioctl_snap_create(), assign it inside the function.
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Currently we're using scrub_stripe::folios[] to store all contents of a
stripe.
This means we need all the extra work to handle things like sub-page
cases, and also require larger folios to handle bs > ps cases.
On the other hand, it's not hard to allocate a 64K large folio to cover
the full stripe, getting rid of the cross-page handling.
Furthermore, even if that large folio allocation failed, we can still
use vmalloc() to allocate a virtually contiguous space and still get rid
of cross-page handling.
This patch will go with kvmalloc() to allocate 64K of memory for
the stripe buffer, thus getting rid of all the complex cross-page
handling.
The following aspects can be greatly simplified:
- Checksum verification for both data and metadata
No more per-page iteration, all in one go.
- RAID56 data caching
Just copy the buffer into the RAID56 pages.
- No more kaddr/paddr grabbing
For most cases the virtual address is enough for csum calculation and
io submission.
- Bio assembly
There is already the helper bio_add_vmalloc() to queue vmallocated
memory into a bio.
Although it means we have something else to be concerned about:
- Bio assembly
If the memory is vmallocated, we need to use bio_add_vmalloc()
Otherwise use the existing bio_add_page().
- Read endio
For vmallocated memory, we need to call
invalidate_kernel_vmap_range().
- Scrub bbio bvec size
Since scrub_stripe::buffer is kvmallocated, we also need to enlarge
the scrub bbio, to be able to handle the worst case, where all 64KiB is
allocated by discontiguous 4K physical pages.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Currently calc_sector_number() is implemented by comparing the first
bvec of the bbio against all blocks inside a scrub_stripe.
This implementation is a little inefficient, and depends on how the
scrub buffer is implemented.
One of the reason implementing such complex function is that, we do not
save the original bvec_iter inside a write btrfs_bio.
Although a read bbio has btrfs_bio::saved_iter to get the original
logical bytenr, it's not implemented for write bios.
On the other hand, since commit 81cea6cd7041 ("btrfs: remove
btrfs_bio::fs_info by extracting it from btrfs_bio::inode"), we always
set the btrfs_bio::file_offset as the logical bytenr for scrub, and that
member will not be modified during IO.
So this means we have a stable way to determine the logical bytenr for a
scrub bio, now calc_sector_number() is just as simple as:
return (bbio->file_offset - stripe->logical) >> sectorsize_bits;
Since we're here, also add an ASSERT() to make sure the bbio is inside
the stripe, and change the return type to unsigned int to be extra safe.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
For both scrub_repair_read_endio() and scrub_read_endio(), they share
the same bitmap update and bio put. Factor out the common code into a
helper to reduce duplication.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
The last user of this macro is removed in commit 001e3fc263ce ("btrfs:
scrub: remove scrub_block and scrub_sector structures").
Now that macro is only utilized in an ASSERT(), which no longer makes
much sense.
Just remove it completely.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
There's no point in having a label where under it we do nothing but return
a variable. So remove it and directly return where we used to goto.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
btrfs_dio_iomap_begin() calls btrfs_get_extent(), which returns an
extent map reference that must be dropped on all exit paths.
For direct writes into a NOCOW range, btrfs_get_blocks_direct_write()
keeps using that extent map and asks btrfs_create_dio_extent() to
allocate the ordered extent. If that fails, for example because
btrfs_alloc_ordered_extent() fails, the function returns the error
without dropping the input extent map. The PREALLOC path avoided this by
dropping the input extent map before replacing it with the newly created
one.
Check the error from btrfs_create_dio_extent() before replacing the
map and drop the input extent map on failure.
Fixes: 5f9a8a51d8b9 ("Btrfs: add semaphore to synchronize direct IO writes with fsync")
CC: stable@vger.kernel.org
Reviewed-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
This mount option is marked deprecated since the introduction of
"rescue=" mount option group, in v5.9.
That's already a long time ago, and it should be safe to completely
remove the old "usebackuproot" mount option now.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
The mount option "rescue=all" should be a shortcut to include all
"rescue=" mount options. But unfortunately "rescue=usebackuproot" is not
included.
Include that option so "rescue=all" has a better chance to mount a
corrupted fs.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
According to btrfs(5) man page, all rescue options should require a
read-only mount.
But that read-only check is only introduced for newer rescue options,
not for the pre-existing "usebackuproot" one.
Furthermore, a filesystem that requires "rescue=" mount option already
means it's corrupted, even if "rescue=usebackuproot" allowed the fs to
be mounted RW, one should not trust such fs anymore until a
comprehensive btrfs-check run and proper evaluation.
Change the behavior to match the document, and since
"rescue=usebackuproot" is now a full RO mount option, it is no longer a
one-shot option, therefore remove it from btrfs_clear_oneshot_options().
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
The 'tree_id' parameter in btrfs_search_path_in_tree() was only being
used in order to fetch the root tree to be considered for the
search. For this same reason this function was also requiring a 'struct
btrfs_fs_info' parameter. This commit replaces these two parameters with
a single 'struct btrfs_root' one, which identifies from which root tree
the search should happen.
This function only has one caller, the inode lookup ioctl, which knows
how to provide the root tree for each case. In fact, if args->treeid ==
0, then we don't even have to allocate a new root tree object, and we
can reuse the one provided by the ioctl system call, thus avoiding an
extra allocation.
Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Previously btrfs forces direct writes to fall back to buffered ones if the
inode has data checksum or the profile has duplication.
That fallback is to avoid the content being modified that the final
content may mismatch with the checksum or the other mirrors.
That brings a pretty huge performance cost, which already caused some
concern at that time.
But later upstream commit c9d114846b38 ("iomap: add a flag to bounce
buffer direct I/O") introduced a new method by copying the content into
new pages, and do all the operations based on the newly allocated pages.
So let btrfs to utilize the new flag for direct writes if we require
stable folios.
There is a quick benchmark, using the following fio setup:
fio --name=randwrite --filename $mnt/foobar --ioengine=libaio --size=4G \
--rw=randwrite --iodepth=64 --runtime=60 --time_based --direct=1 \
--bs=$blocksize
Unit is MiB/s.
Blocksize | Zero-copy (*) | Buffered | Bounce
-----------+---------------+----------+-----------
4K | 35.1 | 17.1 | 33.8
64K | 522 | 251 | 492
*: This is done by reverting the commit 968f19c5b1b7 ("btrfs: always
fallback to buffered write if the inode requires checksum")
Although with page bouncing the performance is only around 95% of
true-zero copy, it's still almost double the performance of buffered
fallback.
There will be a small change in behavior, since we're using
IOMAP_DIO_BOUNCE flag to allocate new folios, NOWAIT flag will
immediately fail.
So for true NOWAIT direct IOs, NODATASUM and RAID0/SINGLE profiles are
still required.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
[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:
<TASK>
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
</TASK>
---[ 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 <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
There's no need to call list_del_init() against each entry when freeing
the list, as the list is local and we are freeing the entry.
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
When freeing the entries from the list there is no need to initialize
the list member in an entry, since we are immediately freeing it. So use
simple list_del() instead of list_del_init().
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Use AUTO_KFREE() for the folios array, avoiding two kfree() calls, one of
them in a very specific error path.
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
There's no need to have one list for each loop to defrag each subrange and
then another one to free each subrange (struct defrag_target_range).
We can do it in a single loop, freeing each subrange after defragging,
plus no need to delete each subrange from the list since we immediately
free it.
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Syzbot reported the following warning recently:
[157.672][ T6611] BTRFS info (device loop0): turning on flush-on-commit
[157.672][ T6611] BTRFS info (device loop0): enabling free space tree
[157.672][ T6611] BTRFS info (device loop0): enabling auto defrag
[157.672][ T6611] BTRFS info (device loop0): use lzo compression, level 1
[157.672][ T6611] BTRFS info (device loop0): max_inline set to 4096
[158.094][ T5608] BTRFS info (device loop2): last unmount of filesystem c9fe44da-de57-406a-8241-57ec7d4412cf
[160.073][ T6656] BTRFS info (device loop0 state M): max_inline set to 4096
[160.418][ T5611] BTRFS info (device loop0): last unmount of filesystem ab8108e1-bea5-4a9f-94c9-a3ff208d732a
[160.432][ T6662] loop2: detected capacity change from 0 to 32768
[160.438][ T6662] BTRFS: device fsid c9fe44da-de57-406a-8241-57ec7d4412cf devid 1 transid 8 /dev/loop2 (7:2) scanned by syz.2.74 (6662)
[160.459][ T6662] BTRFS info (device loop2): first mount of filesystem c9fe44da-de57-406a-8241-57ec7d4412cf
[160.459][ T6662] BTRFS info (device loop2): using crc32c checksum algorithm
[160.634][ T1187] ------------[ cut here ]------------
[160.634][ T1187] test_bit(BTRFS_FS_STATE_NO_DELAYED_IPUT, &fs_info->fs_state)
[160.634][ T1187] WARNING: fs/btrfs/inode.c:3596 at btrfs_add_delayed_iput+0x2e3/0x340, CPU#0: kworker/u8:10/1187
[160.634][ T1187] Modules linked in:
[160.634][ T1187] CPU: 0 UID: 0 PID: 1187 Comm: kworker/u8:10 Not tainted syzkaller #0 PREEMPT_{RT,(full)}
[160.634][ T1187] Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
[160.634][ T1187] Workqueue: btrfs-endio-write btrfs_work_helper
[160.634][ T1187] RIP: 0010:btrfs_add_delayed_iput+0x2e3/0x340
[160.634][ T1187] Code: 53 a3 45 (...)
[160.634][ T1187] RSP: 0018:ffffc900065d77c8 EFLAGS: 00010293
[160.634][ T1187] RAX: ffffffff83e5f502 RBX: ffff88805aba0000 RCX: ffff888029768000
[160.634][ T1187] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
[160.634][ T1187] RBP: dffffc0000000000 R08: 0000000000000000 R09: 0000000000000000
[160.634][ T1187] R10: dffffc0000000000 R11: ffffed100b574497 R12: 0000000000000001
[160.634][ T1187] R13: dffffc0000000000 R14: ffff888061194788 R15: 0000000000000200
[160.634][ T1187] FS: 0000000000000000(0000) GS:ffff888126186000(0000) knlGS:0000000000000000
[160.634][ T1187] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[160.634][ T1187] CR2: 00007fe553a3f000 CR3: 00000000596c2000 CR4: 00000000003526f0
[160.634][ T1187] Call Trace:
[160.634][ T1187] <TASK>
[160.634][ T1187] btrfs_put_ordered_extent+0x18f/0x430
[160.634][ T1187] btrfs_finish_one_ordered+0xf63/0x2680
[160.634][ T1187] ? __pfx_btrfs_finish_one_ordered+0x10/0x10
[160.634][ T1187] ? do_raw_spin_lock+0x12b/0x2f0
[160.634][ T1187] ? lock_acquire+0x106/0x350
[160.634][ T1187] ? __pfx_do_raw_spin_lock+0x10/0x10
[160.634][ T1187] btrfs_work_helper+0x38b/0xc20
[160.634][ T1187] ? process_scheduled_works+0xa70/0x1860
[160.634][ T1187] process_scheduled_works+0xb5d/0x1860
[160.634][ T1187] ? __pfx_process_scheduled_works+0x10/0x10
[160.634][ T1187] ? assign_work+0x3d5/0x5e0
[160.634][ T1187] worker_thread+0xa53/0xfc0
[160.634][ T1187] kthread+0x388/0x470
[160.634][ T1187] ? __pfx_worker_thread+0x10/0x10
[160.635][ T1187] ? __pfx_kthread+0x10/0x10
[160.635][ T1187] ret_from_fork+0x514/0xb70
[160.635][ T1187] ? __pfx_ret_from_fork+0x10/0x10
[160.635][ T1187] ? __switch_to+0xc79/0x1410
[160.635][ T1187] ? __pfx_kthread+0x10/0x10
[160.635][ T1187] ret_from_fork_asm+0x1a/0x30
[160.635][ T1187] </TASK>
[160.635][ T1187] Kernel panic - not syncing: kernel: panic_on_warn set ...
It means we add a delayed iput created after we last ran delayed iputs in
close_ctree() and set the flag BTRFS_FS_STATE_NO_DELAYED_IPUT in fs_info.
This happens when using autodefrag and more likely to happen if we use
flushoncommit too. The steps are the following:
1) Unmount starts, all delalloc is flushed and we enter close_ctree();
2) In close_ctree() we park the cleaner kthread, but while we wait for it
to park, it's in:
btrfs_run_defrag_inodes()
btrfs_run_defrag_inode()
btrfs_defrag_file()
defrag_one_cluster()
defrag_one_range()
defrag_one_locked_target()
And dirties some folios from an inode;
3) The cleaner kthread parks and we proceed in close_ctree(), waiting
for all ordered extents, running delayed iputs and setting the flag
BTRFS_FS_STATE_NO_DELAYED_IPUT in fs_info;
4) Later in close_ctree() we call btrfs_commit_super(), which commits the
current transaction. Because we are mounted with flushoncommit, the
transaction commit flushes delalloc and waits for the resulting ordered
extent to complete;
5) The ordered extents from the flushed delalloc created by autodefrag
complete and create delayed iputs, triggering the warning:
WARN_ON_ONCE(test_bit(BTRFS_FS_STATE_NO_DELAYED_IPUT, &fs_info->fs_state));
in btrfs_add_delayed_iput()
6) Further below in close_ctree() we will hit the following assertion:
ASSERT(list_empty(&fs_info->delayed_iputs));
Since we don't expect any more delayed iputs.
Fix this by flushing delalloc and waiting for the ordered extents right
after we parked the cleaner kthread and waiting for autodefrag in
close_ctree().
Reported-by: syzbot+6a843bf8604711c8fab0@syzkaller.appspotmail.com
Link: https://lore.kernel.org/linux-btrfs/6a1ee507.b4221f80.1326c5.0004.GAE@google.com/
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
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] <TASK>
[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] </TASK>
[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] <TASK>
[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] </TASK>
[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] <TASK>
[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] </TASK>
[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] <TASK>
[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] </TASK>
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 <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Btrfs does not support variable stripe length yet, all RAID0/5/6/10
chunks have the fixed stripe length 64K for now.
Furthermore, btrfs_fs_info::stripesize is not the real chunk stripe
length, it's always the same value as sectorsize.
Remove btrfs_fs_info::stripesize, and for the only callsite utilizing
that member, replace it with fs_info->sectorsize instead.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
The nodesize and sectorsize are all u32 values, there is no need to use
u64 for local usage.
Furthermore some call sites also use "blocksize" or "bs" for sectorsize,
also change them to use the minimal type u32 instead.
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
prefixes
In case the current inode's path is a prefix of the given path, the helper
is_current_inode_path() will return true, which causes the single caller
to reset the current inode's path. While this is not a functional issue,
it makes the caller recompute the current inode's path later. It could
also become a problem in the future in case get new callers for
is_current_inode_path() in more sensitive contexts.
Example: the current inode path is "/foo/bar" and the path we compare
against is "/foo/bar_xyz".
Fix this by returning true only if we have exact matches.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Daniel Vacek <neelx@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
The comment is wrong, because it's not about storing the ID of new
directories that were already created, instead it's about storing utimes
values for directories (both new and existing). The comment is wrong
because it was copy pasted from SEND_MAX_DIR_CREATED_CACHE_SIZE, but
forgot to update it afterwards.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Daniel Vacek <neelx@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
On a zoned FS, btrfs_delayed_refs_rsv_refill() returns -EAGAIN whenever
the over-committed metadata plus the zone_unusable bytes exceeds the
usable size in a metadata block-group to avoid heavy over-commit of
metadata and early ENOSPC in one transaction.
If this happens while doing reclaim, the transaction is getting aborted.
Treat -EAGAIN as a soft, retryable condition in case of block-group
reclaim.
Reported-by: Damien Le Moal <dlemoal@kernel.org>
Fixes: 7bcb04de982f ("btrfs: zoned: cap delayed refs metadata reservation to avoid overcommit")
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Since v5.15 btrfs has support for block size < page size, but we still
only support 4K block size, while there is no special reason that we
cannot support 8K/16K/32K block sizes for 64K page size.
That 4K limit is completely arbitrary, and mostly to reduce test runtime
so we do not need to test all the extra block size combinations.
However that also limits the user choices, some users may understand
what they are doing, and want larger block sizes. In that case, fixed
4K block size for subpage routine is blocking our way.
Just remove that fixed 4K requirement for block size < page size.
This should not affect regular end users, since mkfs is already using 4K
block size as default for quite a while, and the existing bs == ps support is
always there.
But for power users, this allows extra block size support, and may
provide extra test coverage.
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
Since commit bac3c2910c0c ("btrfs: remove 2K block size support") there
is no 2K block size support inside btrfs anymore.
Remove the stale comments of btrfs_supported_blocksize().
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
V2 space cache has been the default mkfs option since btrfs-progs v5.15,
and commit 1e7bec1f7d65 ("btrfs: emit a warning about space cache v1
being deprecated") has already added a warning to show v1 space cache
has been deprecated.
It has been long enough that we should remove v1 space cache completely.
As the first step, disable v1 space cache by:
- Make "space_cache" mount option fallback to "nospace_cache"
- Make "space_cache=v1" fall back to "nospace_cache"
This is safer than forcing "space_cache=v2", as forcing v2 cache
requires removal of v1 cache and regenerating v2 cache.
Such operation can be slow, and takes extra metadata space, thus
it is not always safe for existing filesystems.
With this done, v1 cache mount will always fallback to nospace cache,
and mount option will not be able to force v1 space cache usage.
For example, even for a fs with v1 cache:
# btrfs ins dump-super test.img
superblock: bytenr=65536, device=test.img
---------------------------------------------------------
csum_type 0 (crc32c)
csum_size 4
csum 0xdce44b2c [match]
bytenr 65536
flags 0x1
( WRITTEN )
magic _BHRfS_M [match]
fsid 7d7c3bba-8211-4206-868d-10eedd5703f8
metadata_uuid 00000000-0000-0000-0000-000000000000
label
generation 9
root 30605312
[...]
compat_ro_flags 0x0 <<< No FST feature
incompat_flags 0x361
( MIXED_BACKREF |
BIG_METADATA |
EXTENDED_IREF |
SKINNY_METADATA |
NO_HOLES )
cache_generation 9 <<< Matches generation
uuid_tree_generation 9
Attempting to mount it will lead to no space cache other than v1 space cache:
# mount test.img /mnt/btrfs
# dmesg -t | tail -n 5
BTRFS: device fsid 7d7c3bba-8211-4206-868d-10eedd5703f8 devid 1 transid 9 /dev/loop0 (7:0) scanned by mount (1264)
BTRFS info (device loop0): first mount of filesystem 7d7c3bba-8211-4206-868d-10eedd5703f8
BTRFS info (device loop0): using crc32c checksum algorithm
BTRFS info (device loop0): turning on async discard
BTRFS info (device loop0): last unmount of filesystem 7d7c3bba-8211-4206-868d-10eedd5703f8
Even forcing v1 cache will not work, but fallback to the usual
nospace_cache:
# mount test.img -o space_cache=v1 /mnt/btrfs
# dmesg -t | tail -n 6
BTRFS warning: v1 space cache is deprecated, fallback to no space cache
BTRFS: device fsid 7d7c3bba-8211-4206-868d-10eedd5703f8 devid 1 transid 9 /dev/loop0 (7:0) scanned by mount (1264)
BTRFS info (device loop0): first mount of filesystem 7d7c3bba-8211-4206-868d-10eedd5703f8
BTRFS info (device loop0): using crc32c checksum algorithm
BTRFS info (device loop0): turning on async discard
BTRFS info (device loop0): last unmount of filesystem 7d7c3bba-8211-4206-868d-10eedd5703f8
And there will be no way to force converting a v2 cache back to v1, such
attempt will only clear free space tree and fallback to no space cache.
# mkfs.btrfs -f -O fst,^bgt test.img
# mount -o clear_cache,space_cache=v1 test.img /mnt/btrfs
# dmesg -t | tail -n 11
BTRFS warning: v1 space cache is deprecated, fallback to no space cache
BTRFS: device fsid f59daad2-3ab5-4f33-b752-a36cfb09b674 devid 1 transid 8 /dev/loop0 (7:0) scanned by mount (1419)
BTRFS info (device loop0): first mount of filesystem f59daad2-3ab5-4f33-b752-a36cfb09b674
BTRFS info (device loop0): using crc32c checksum algorithm
BTRFS info (device loop0): rebuilding free space tree
BTRFS info (device loop0): disabling free space tree
BTRFS info (device loop0): clearing compat-ro feature flag for FREE_SPACE_TREE (0x1)
BTRFS info (device loop0): clearing compat-ro feature flag for FREE_SPACE_TREE_VALID (0x2)
BTRFS info (device loop0): checking UUID tree
BTRFS info (device loop0): turning on async discard
BTRFS info (device loop0): force clearing of disk cache
# mount | grep /mnt/btrfs
/mnt/test.img on /mnt/btrfs type btrfs (rw,relatime,discard=async,nospace_cache,subvolid=5,subvol=/)
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
A swap file on btrfs will pin down block groups that cover the swap file
extent.
Pinned down block groups will be skipped for scrub and relocation.
These degradation on critical btrfs maintenance operations is never
properly educated to end users, and have already caused problems
including:
- Scrub finished too quick
Because the enabled swap file has pinned down most of the block
groups. Thus any file extents in those block groups, even not utilized
by the swap file, will be skipped from scrub.
- Unbalanced data and metadata usage, meanwhile relocation won't help
The same reason, pinned down block groups will not be considered as
relocation target, thus data extents that are not utilized by the swap
file can still be skipped from relocation.
Although we already have kernel messages for both scrub and balance, the
balance one is still info level.
To better communicate those potential long term problems, add the
following output into dmesg:
- Change the message level to warn for __btrfs_balance()
- Total pinned down block group number and size during swapfile activation
- Total released block group number and size during swapfile deactivation
The above messages have info level.
- The fact that pinned down block groups will not be scrubbed nor
balanced
The above message has warning level.
The example output would look like the following, for enabling a 1.2G
swapfile, which pinned down 2G block groups:
BTRFS info (device dm-3): swapfile activated on root 5 ino 257, pinned down 2147483648 bytes from 2 block group(s)
BTRFS warning (device dm-3): block groups with swapfile extents will not be scrubbed or balanced
Adding 1257468k swap on /mnt/btrfs/foobar. Priority:-1 extents:1 across:1257468k
BTRFS info (device dm-3): swapfile deactivated on root 5 ino 257, released 2147483648 bytes from 2 block group(s)
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
|