2019-06-20 22:37:44 +03:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
|
|
|
|
|
|
|
#ifndef BTRFS_BLOCK_GROUP_H
|
|
|
|
#define BTRFS_BLOCK_GROUP_H
|
|
|
|
|
2019-08-21 20:57:04 +03:00
|
|
|
#include "free-space-cache.h"
|
|
|
|
|
2019-06-20 22:37:44 +03:00
|
|
|
enum btrfs_disk_cache_state {
|
|
|
|
BTRFS_DC_WRITTEN,
|
|
|
|
BTRFS_DC_ERROR,
|
|
|
|
BTRFS_DC_CLEAR,
|
|
|
|
BTRFS_DC_SETUP,
|
|
|
|
};
|
|
|
|
|
2019-12-14 03:22:16 +03:00
|
|
|
/*
|
|
|
|
* This describes the state of the block_group for async discard. This is due
|
|
|
|
* to the two pass nature of it where extent discarding is prioritized over
|
|
|
|
* bitmap discarding. BTRFS_DISCARD_RESET_CURSOR is set when we are resetting
|
|
|
|
* between lists to prevent contention for discard state variables
|
|
|
|
* (eg. discard_cursor).
|
|
|
|
*/
|
|
|
|
enum btrfs_discard_state {
|
|
|
|
BTRFS_DISCARD_EXTENTS,
|
|
|
|
BTRFS_DISCARD_BITMAPS,
|
|
|
|
BTRFS_DISCARD_RESET_CURSOR,
|
|
|
|
};
|
|
|
|
|
2019-06-20 22:38:04 +03:00
|
|
|
/*
|
|
|
|
* Control flags for do_chunk_alloc's force field CHUNK_ALLOC_NO_FORCE means to
|
|
|
|
* only allocate a chunk if we really need one.
|
|
|
|
*
|
|
|
|
* CHUNK_ALLOC_LIMITED means to only try and allocate one if we have very few
|
|
|
|
* chunks already allocated. This is used as part of the clustering code to
|
|
|
|
* help make sure we have a good pool of storage to cluster in, without filling
|
|
|
|
* the FS with empty chunks
|
|
|
|
*
|
|
|
|
* CHUNK_ALLOC_FORCE means it must try to allocate one
|
2022-03-22 12:11:34 +03:00
|
|
|
*
|
|
|
|
* CHUNK_ALLOC_FORCE_FOR_EXTENT like CHUNK_ALLOC_FORCE but called from
|
|
|
|
* find_free_extent() that also activaes the zone
|
2019-06-20 22:38:04 +03:00
|
|
|
*/
|
|
|
|
enum btrfs_chunk_alloc_enum {
|
|
|
|
CHUNK_ALLOC_NO_FORCE,
|
|
|
|
CHUNK_ALLOC_LIMITED,
|
|
|
|
CHUNK_ALLOC_FORCE,
|
2022-03-22 12:11:34 +03:00
|
|
|
CHUNK_ALLOC_FORCE_FOR_EXTENT,
|
2019-06-20 22:38:04 +03:00
|
|
|
};
|
|
|
|
|
2022-07-15 22:45:24 +03:00
|
|
|
/* Block group flags set at runtime */
|
|
|
|
enum btrfs_block_group_flags {
|
|
|
|
BLOCK_GROUP_FLAG_IREF,
|
|
|
|
BLOCK_GROUP_FLAG_REMOVED,
|
|
|
|
BLOCK_GROUP_FLAG_TO_COPY,
|
|
|
|
BLOCK_GROUP_FLAG_RELOCATING_REPAIR,
|
|
|
|
BLOCK_GROUP_FLAG_CHUNK_ITEM_INSERTED,
|
|
|
|
BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE,
|
|
|
|
BLOCK_GROUP_FLAG_ZONED_DATA_RELOC,
|
|
|
|
};
|
|
|
|
|
2022-09-15 02:04:37 +03:00
|
|
|
enum btrfs_caching_type {
|
|
|
|
BTRFS_CACHE_NO,
|
|
|
|
BTRFS_CACHE_STARTED,
|
|
|
|
BTRFS_CACHE_FINISHED,
|
|
|
|
BTRFS_CACHE_ERROR,
|
|
|
|
};
|
|
|
|
|
2019-06-20 22:37:44 +03:00
|
|
|
struct btrfs_caching_control {
|
|
|
|
struct list_head list;
|
|
|
|
struct mutex mutex;
|
|
|
|
wait_queue_head_t wait;
|
|
|
|
struct btrfs_work work;
|
2019-10-29 21:20:18 +03:00
|
|
|
struct btrfs_block_group *block_group;
|
2019-06-20 22:37:44 +03:00
|
|
|
refcount_t count;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Once caching_thread() finds this much free space, it will wake up waiters. */
|
|
|
|
#define CACHING_CTL_WAKE_UP SZ_2M
|
|
|
|
|
2022-09-15 02:04:38 +03:00
|
|
|
/*
|
|
|
|
* Tree to record all locked full stripes of a RAID5/6 block group
|
|
|
|
*/
|
|
|
|
struct btrfs_full_stripe_locks_tree {
|
|
|
|
struct rb_root root;
|
|
|
|
struct mutex lock;
|
|
|
|
};
|
|
|
|
|
2019-10-29 21:20:18 +03:00
|
|
|
struct btrfs_block_group {
|
2019-06-20 22:37:44 +03:00
|
|
|
struct btrfs_fs_info *fs_info;
|
|
|
|
struct inode *inode;
|
|
|
|
spinlock_t lock;
|
2019-10-23 19:48:22 +03:00
|
|
|
u64 start;
|
|
|
|
u64 length;
|
2019-06-20 22:37:44 +03:00
|
|
|
u64 pinned;
|
|
|
|
u64 reserved;
|
2019-10-23 19:48:11 +03:00
|
|
|
u64 used;
|
2019-06-20 22:37:44 +03:00
|
|
|
u64 delalloc_bytes;
|
|
|
|
u64 bytes_super;
|
|
|
|
u64 flags;
|
|
|
|
u64 cache_generation;
|
2021-12-15 23:40:08 +03:00
|
|
|
u64 global_root_id;
|
2019-06-20 22:37:44 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the free space extent count exceeds this number, convert the block
|
|
|
|
* group to bitmaps.
|
|
|
|
*/
|
|
|
|
u32 bitmap_high_thresh;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the free space extent count drops below this number, convert the
|
|
|
|
* block group back to extents.
|
|
|
|
*/
|
|
|
|
u32 bitmap_low_thresh;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is just used for the delayed data space allocation because
|
|
|
|
* only the data space allocation and the relative metadata update
|
|
|
|
* can be done cross the transaction.
|
|
|
|
*/
|
|
|
|
struct rw_semaphore data_rwsem;
|
|
|
|
|
|
|
|
/* For raid56, this is a full stripe, without parity */
|
|
|
|
unsigned long full_stripe_len;
|
2022-07-15 22:45:24 +03:00
|
|
|
unsigned long runtime_flags;
|
2019-06-20 22:37:44 +03:00
|
|
|
|
|
|
|
unsigned int ro;
|
|
|
|
|
|
|
|
int disk_cache_state;
|
|
|
|
|
|
|
|
/* Cache tracking stuff */
|
|
|
|
int cached;
|
|
|
|
struct btrfs_caching_control *caching_ctl;
|
|
|
|
|
|
|
|
struct btrfs_space_info *space_info;
|
|
|
|
|
|
|
|
/* Free space cache stuff */
|
|
|
|
struct btrfs_free_space_ctl *free_space_ctl;
|
|
|
|
|
|
|
|
/* Block group cache stuff */
|
|
|
|
struct rb_node cache_node;
|
|
|
|
|
|
|
|
/* For block groups in the same raid type */
|
|
|
|
struct list_head list;
|
|
|
|
|
2020-07-06 16:14:11 +03:00
|
|
|
refcount_t refs;
|
2019-06-20 22:37:44 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* List of struct btrfs_free_clusters for this block group.
|
|
|
|
* Today it will only have one thing on it, but that may change
|
|
|
|
*/
|
|
|
|
struct list_head cluster_list;
|
|
|
|
|
|
|
|
/* For delayed block group creation or deletion of empty block groups */
|
|
|
|
struct list_head bg_list;
|
|
|
|
|
|
|
|
/* For read-only block groups */
|
|
|
|
struct list_head ro_list;
|
|
|
|
|
2020-05-08 13:01:47 +03:00
|
|
|
/*
|
|
|
|
* When non-zero it means the block group's logical address and its
|
|
|
|
* device extents can not be reused for future block group allocations
|
|
|
|
* until the counter goes down to 0. This is to prevent them from being
|
|
|
|
* reused while some task is still using the block group after it was
|
|
|
|
* deleted - we want to make sure they can only be reused for new block
|
|
|
|
* groups after that task is done with the deleted block group.
|
|
|
|
*/
|
|
|
|
atomic_t frozen;
|
|
|
|
|
2019-12-14 03:22:14 +03:00
|
|
|
/* For discard operations */
|
|
|
|
struct list_head discard_list;
|
|
|
|
int discard_index;
|
|
|
|
u64 discard_eligible_time;
|
2019-12-14 03:22:16 +03:00
|
|
|
u64 discard_cursor;
|
|
|
|
enum btrfs_discard_state discard_state;
|
2019-06-20 22:37:44 +03:00
|
|
|
|
|
|
|
/* For dirty block groups */
|
|
|
|
struct list_head dirty_list;
|
|
|
|
struct list_head io_list;
|
|
|
|
|
|
|
|
struct btrfs_io_ctl io_ctl;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Incremented when doing extent allocations and holding a read lock
|
|
|
|
* on the space_info's groups_sem semaphore.
|
|
|
|
* Decremented when an ordered extent that represents an IO against this
|
|
|
|
* block group's range is created (after it's added to its inode's
|
|
|
|
* root's list of ordered extents) or immediately after the allocation
|
|
|
|
* if it's a metadata extent or fallocate extent (for these cases we
|
|
|
|
* don't create ordered extents).
|
|
|
|
*/
|
|
|
|
atomic_t reservations;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Incremented while holding the spinlock *lock* by a task checking if
|
|
|
|
* it can perform a nocow write (incremented if the value for the *ro*
|
|
|
|
* field is 0). Decremented by such tasks once they create an ordered
|
|
|
|
* extent or before that if some error happens before reaching that step.
|
|
|
|
* This is to prevent races between block group relocation and nocow
|
|
|
|
* writes through direct IO.
|
|
|
|
*/
|
|
|
|
atomic_t nocow_writers;
|
|
|
|
|
|
|
|
/* Lock for free space tree operations. */
|
|
|
|
struct mutex free_space_lock;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Does the block group need to be added to the free space tree?
|
|
|
|
* Protected by free_space_lock.
|
|
|
|
*/
|
|
|
|
int needs_free_space;
|
|
|
|
|
2021-02-04 13:22:03 +03:00
|
|
|
/* Flag indicating this block group is placed on a sequential zone */
|
|
|
|
bool seq_zone;
|
|
|
|
|
btrfs: fix race between writes to swap files and scrub
When we active a swap file, at btrfs_swap_activate(), we acquire the
exclusive operation lock to prevent the physical location of the swap
file extents to be changed by operations such as balance and device
replace/resize/remove. We also call there can_nocow_extent() which,
among other things, checks if the block group of a swap file extent is
currently RO, and if it is we can not use the extent, since a write
into it would result in COWing the extent.
However we have no protection against a scrub operation running after we
activate the swap file, which can result in the swap file extents to be
COWed while the scrub is running and operating on the respective block
group, because scrub turns a block group into RO before it processes it
and then back again to RW mode after processing it. That means an attempt
to write into a swap file extent while scrub is processing the respective
block group, will result in COWing the extent, changing its physical
location on disk.
Fix this by making sure that block groups that have extents that are used
by active swap files can not be turned into RO mode, therefore making it
not possible for a scrub to turn them into RO mode. When a scrub finds a
block group that can not be turned to RO due to the existence of extents
used by swap files, it proceeds to the next block group and logs a warning
message that mentions the block group was skipped due to active swap
files - this is the same approach we currently use for balance.
Fixes: ed46ff3d42378 ("Btrfs: support swap files")
CC: stable@vger.kernel.org # 5.4+
Reviewed-by: Anand Jain <anand.jain@oracle.com>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-02-05 15:55:37 +03:00
|
|
|
/*
|
|
|
|
* Number of extents in this block group used for swap files.
|
|
|
|
* All accesses protected by the spinlock 'lock'.
|
|
|
|
*/
|
|
|
|
int swap_extents;
|
|
|
|
|
2019-06-20 22:37:44 +03:00
|
|
|
/* Record locked full stripes for RAID5/6 block group */
|
|
|
|
struct btrfs_full_stripe_locks_tree full_stripe_locks_root;
|
2021-02-04 13:21:50 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocation offset for the block group to implement sequential
|
|
|
|
* allocation. This is used only on a zoned filesystem.
|
|
|
|
*/
|
|
|
|
u64 alloc_offset;
|
2021-02-04 13:21:52 +03:00
|
|
|
u64 zone_unusable;
|
2021-08-19 15:19:08 +03:00
|
|
|
u64 zone_capacity;
|
2021-02-04 13:22:08 +03:00
|
|
|
u64 meta_write_pointer;
|
2021-08-19 15:19:16 +03:00
|
|
|
struct map_lookup *physical_map;
|
2021-08-19 15:19:17 +03:00
|
|
|
struct list_head active_bg_list;
|
2022-05-04 03:48:53 +03:00
|
|
|
struct work_struct zone_finish_work;
|
|
|
|
struct extent_buffer *last_eb;
|
2019-06-20 22:37:44 +03:00
|
|
|
};
|
|
|
|
|
2019-12-14 03:22:14 +03:00
|
|
|
static inline u64 btrfs_block_group_end(struct btrfs_block_group *block_group)
|
|
|
|
{
|
|
|
|
return (block_group->start + block_group->length);
|
|
|
|
}
|
|
|
|
|
2020-01-03 00:26:40 +03:00
|
|
|
static inline bool btrfs_is_block_group_data_only(
|
|
|
|
struct btrfs_block_group *block_group)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* In mixed mode the fragmentation is expected to be high, lowering the
|
|
|
|
* efficiency, so only proper data block groups are considered.
|
|
|
|
*/
|
|
|
|
return (block_group->flags & BTRFS_BLOCK_GROUP_DATA) &&
|
|
|
|
!(block_group->flags & BTRFS_BLOCK_GROUP_METADATA);
|
|
|
|
}
|
|
|
|
|
2019-06-20 22:37:44 +03:00
|
|
|
#ifdef CONFIG_BTRFS_DEBUG
|
|
|
|
static inline int btrfs_should_fragment_free_space(
|
2019-10-29 21:20:18 +03:00
|
|
|
struct btrfs_block_group *block_group)
|
2019-06-20 22:37:44 +03:00
|
|
|
{
|
|
|
|
struct btrfs_fs_info *fs_info = block_group->fs_info;
|
|
|
|
|
|
|
|
return (btrfs_test_opt(fs_info, FRAGMENT_METADATA) &&
|
|
|
|
block_group->flags & BTRFS_BLOCK_GROUP_METADATA) ||
|
|
|
|
(btrfs_test_opt(fs_info, FRAGMENT_DATA) &&
|
|
|
|
block_group->flags & BTRFS_BLOCK_GROUP_DATA);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-10-29 21:20:18 +03:00
|
|
|
struct btrfs_block_group *btrfs_lookup_first_block_group(
|
2019-06-20 22:37:45 +03:00
|
|
|
struct btrfs_fs_info *info, u64 bytenr);
|
2019-10-29 21:20:18 +03:00
|
|
|
struct btrfs_block_group *btrfs_lookup_block_group(
|
2019-06-20 22:37:45 +03:00
|
|
|
struct btrfs_fs_info *info, u64 bytenr);
|
2019-10-29 21:20:18 +03:00
|
|
|
struct btrfs_block_group *btrfs_next_block_group(
|
|
|
|
struct btrfs_block_group *cache);
|
|
|
|
void btrfs_get_block_group(struct btrfs_block_group *cache);
|
|
|
|
void btrfs_put_block_group(struct btrfs_block_group *cache);
|
2019-06-20 22:37:47 +03:00
|
|
|
void btrfs_dec_block_group_reservations(struct btrfs_fs_info *fs_info,
|
|
|
|
const u64 start);
|
2019-10-29 21:20:18 +03:00
|
|
|
void btrfs_wait_block_group_reservations(struct btrfs_block_group *bg);
|
btrfs: avoid double search for block group during NOCOW writes
When doing a NOCOW write, either through direct IO or buffered IO, we do
two lookups for the block group that contains the target extent: once
when we call btrfs_inc_nocow_writers() and then later again when we call
btrfs_dec_nocow_writers() after creating the ordered extent.
The lookups require taking a lock and navigating the red black tree used
to track all block groups, which can take a non-negligible amount of time
for a large filesystem with thousands of block groups, as well as lock
contention and cache line bouncing.
Improve on this by having a single block group search: making
btrfs_inc_nocow_writers() return the block group to its caller and then
have the caller pass that block group to btrfs_dec_nocow_writers().
This is part of a patchset comprised of the following patches:
btrfs: remove search start argument from first_logical_byte()
btrfs: use rbtree with leftmost node cached for tracking lowest block group
btrfs: use a read/write lock for protecting the block groups tree
btrfs: return block group directly at btrfs_next_block_group()
btrfs: avoid double search for block group during NOCOW writes
The following test was used to test these changes from a performance
perspective:
$ cat test.sh
#!/bin/bash
modprobe null_blk nr_devices=0
NULL_DEV_PATH=/sys/kernel/config/nullb/nullb0
mkdir $NULL_DEV_PATH
if [ $? -ne 0 ]; then
echo "Failed to create nullb0 directory."
exit 1
fi
echo 2 > $NULL_DEV_PATH/submit_queues
echo 16384 > $NULL_DEV_PATH/size # 16G
echo 1 > $NULL_DEV_PATH/memory_backed
echo 1 > $NULL_DEV_PATH/power
DEV=/dev/nullb0
MNT=/mnt/nullb0
LOOP_MNT="$MNT/loop"
MOUNT_OPTIONS="-o ssd -o nodatacow"
MKFS_OPTIONS="-R free-space-tree -O no-holes"
cat <<EOF > /tmp/fio-job.ini
[io_uring_writes]
rw=randwrite
fsync=0
fallocate=posix
group_reporting=1
direct=1
ioengine=io_uring
iodepth=64
bs=64k
filesize=1g
runtime=300
time_based
directory=$LOOP_MNT
numjobs=8
thread
EOF
echo performance | \
tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
echo
echo "Using config:"
echo
cat /tmp/fio-job.ini
echo
umount $MNT &> /dev/null
mkfs.btrfs -f $MKFS_OPTIONS $DEV &> /dev/null
mount $MOUNT_OPTIONS $DEV $MNT
mkdir $LOOP_MNT
truncate -s 4T $MNT/loopfile
mkfs.btrfs -f $MKFS_OPTIONS $MNT/loopfile &> /dev/null
mount $MOUNT_OPTIONS $MNT/loopfile $LOOP_MNT
# Trigger the allocation of about 3500 data block groups, without
# actually consuming space on underlying filesystem, just to make
# the tree of block group large.
fallocate -l 3500G $LOOP_MNT/filler
fio /tmp/fio-job.ini
umount $LOOP_MNT
umount $MNT
echo 0 > $NULL_DEV_PATH/power
rmdir $NULL_DEV_PATH
The test was run on a non-debug kernel (Debian's default kernel config),
the result were the following.
Before patchset:
WRITE: bw=1455MiB/s (1526MB/s), 1455MiB/s-1455MiB/s (1526MB/s-1526MB/s), io=426GiB (458GB), run=300006-300006msec
After patchset:
WRITE: bw=1503MiB/s (1577MB/s), 1503MiB/s-1503MiB/s (1577MB/s-1577MB/s), io=440GiB (473GB), run=300006-300006msec
+3.3% write throughput and +3.3% IO done in the same time period.
The test has somewhat limited coverage scope, as with only NOCOW writes
we get less contention on the red black tree of block groups, since we
don't have the extra contention caused by COW writes, namely when
allocating data extents, pinning and unpinning data extents, but on the
hand there's access to tree in the NOCOW path, when incrementing a block
group's number of NOCOW writers.
Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2022-04-13 18:20:43 +03:00
|
|
|
struct btrfs_block_group *btrfs_inc_nocow_writers(struct btrfs_fs_info *fs_info,
|
|
|
|
u64 bytenr);
|
|
|
|
void btrfs_dec_nocow_writers(struct btrfs_block_group *bg);
|
2019-10-29 21:20:18 +03:00
|
|
|
void btrfs_wait_nocow_writers(struct btrfs_block_group *bg);
|
|
|
|
void btrfs_wait_block_group_cache_progress(struct btrfs_block_group *cache,
|
2019-06-20 22:37:48 +03:00
|
|
|
u64 num_bytes);
|
btrfs: fix space cache corruption and potential double allocations
When testing space_cache v2 on a large set of machines, we encountered a
few symptoms:
1. "unable to add free space :-17" (EEXIST) errors.
2. Missing free space info items, sometimes caught with a "missing free
space info for X" error.
3. Double-accounted space: ranges that were allocated in the extent tree
and also marked as free in the free space tree, ranges that were
marked as allocated twice in the extent tree, or ranges that were
marked as free twice in the free space tree. If the latter made it
onto disk, the next reboot would hit the BUG_ON() in
add_new_free_space().
4. On some hosts with no on-disk corruption or error messages, the
in-memory space cache (dumped with drgn) disagreed with the free
space tree.
All of these symptoms have the same underlying cause: a race between
caching the free space for a block group and returning free space to the
in-memory space cache for pinned extents causes us to double-add a free
range to the space cache. This race exists when free space is cached
from the free space tree (space_cache=v2) or the extent tree
(nospace_cache, or space_cache=v1 if the cache needs to be regenerated).
struct btrfs_block_group::last_byte_to_unpin and struct
btrfs_block_group::progress are supposed to protect against this race,
but commit d0c2f4fa555e ("btrfs: make concurrent fsyncs wait less when
waiting for a transaction commit") subtly broke this by allowing
multiple transactions to be unpinning extents at the same time.
Specifically, the race is as follows:
1. An extent is deleted from an uncached block group in transaction A.
2. btrfs_commit_transaction() is called for transaction A.
3. btrfs_run_delayed_refs() -> __btrfs_free_extent() runs the delayed
ref for the deleted extent.
4. __btrfs_free_extent() -> do_free_extent_accounting() ->
add_to_free_space_tree() adds the deleted extent back to the free
space tree.
5. do_free_extent_accounting() -> btrfs_update_block_group() ->
btrfs_cache_block_group() queues up the block group to get cached.
block_group->progress is set to block_group->start.
6. btrfs_commit_transaction() for transaction A calls
switch_commit_roots(). It sets block_group->last_byte_to_unpin to
block_group->progress, which is block_group->start because the block
group hasn't been cached yet.
7. The caching thread gets to our block group. Since the commit roots
were already switched, load_free_space_tree() sees the deleted extent
as free and adds it to the space cache. It finishes caching and sets
block_group->progress to U64_MAX.
8. btrfs_commit_transaction() advances transaction A to
TRANS_STATE_SUPER_COMMITTED.
9. fsync calls btrfs_commit_transaction() for transaction B. Since
transaction A is already in TRANS_STATE_SUPER_COMMITTED and the
commit is for fsync, it advances.
10. btrfs_commit_transaction() for transaction B calls
switch_commit_roots(). This time, the block group has already been
cached, so it sets block_group->last_byte_to_unpin to U64_MAX.
11. btrfs_commit_transaction() for transaction A calls
btrfs_finish_extent_commit(), which calls unpin_extent_range() for
the deleted extent. It sees last_byte_to_unpin set to U64_MAX (by
transaction B!), so it adds the deleted extent to the space cache
again!
This explains all of our symptoms above:
* If the sequence of events is exactly as described above, when the free
space is re-added in step 11, it will fail with EEXIST.
* If another thread reallocates the deleted extent in between steps 7
and 11, then step 11 will silently re-add that space to the space
cache as free even though it is actually allocated. Then, if that
space is allocated *again*, the free space tree will be corrupted
(namely, the wrong item will be deleted).
* If we don't catch this free space tree corruption, it will continue
to get worse as extents are deleted and reallocated.
The v1 space_cache is synchronously loaded when an extent is deleted
(btrfs_update_block_group() with alloc=0 calls btrfs_cache_block_group()
with load_cache_only=1), so it is not normally affected by this bug.
However, as noted above, if we fail to load the space cache, we will
fall back to caching from the extent tree and may hit this bug.
The easiest fix for this race is to also make caching from the free
space tree or extent tree synchronous. Josef tested this and found no
performance regressions.
A few extra changes fall out of this change. Namely, this fix does the
following, with step 2 being the crucial fix:
1. Factor btrfs_caching_ctl_wait_done() out of
btrfs_wait_block_group_cache_done() to allow waiting on a caching_ctl
that we already hold a reference to.
2. Change the call in btrfs_cache_block_group() of
btrfs_wait_space_cache_v1_finished() to
btrfs_caching_ctl_wait_done(), which makes us wait regardless of the
space_cache option.
3. Delete the now unused btrfs_wait_space_cache_v1_finished() and
space_cache_v1_done().
4. Change btrfs_cache_block_group()'s `int load_cache_only` parameter to
`bool wait` to more accurately describe its new meaning.
5. Change a few callers which had a separate call to
btrfs_wait_block_group_cache_done() to use wait = true instead.
6. Make btrfs_wait_block_group_cache_done() static now that it's not
used outside of block-group.c anymore.
Fixes: d0c2f4fa555e ("btrfs: make concurrent fsyncs wait less when waiting for a transaction commit")
CC: stable@vger.kernel.org # 5.12+
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Omar Sandoval <osandov@fb.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2022-08-23 21:28:13 +03:00
|
|
|
int btrfs_cache_block_group(struct btrfs_block_group *cache, bool wait);
|
2019-06-20 22:37:50 +03:00
|
|
|
void btrfs_put_caching_control(struct btrfs_caching_control *ctl);
|
|
|
|
struct btrfs_caching_control *btrfs_get_caching_control(
|
2019-10-29 21:20:18 +03:00
|
|
|
struct btrfs_block_group *cache);
|
|
|
|
u64 add_new_free_space(struct btrfs_block_group *block_group,
|
2019-08-06 17:43:19 +03:00
|
|
|
u64 start, u64 end);
|
2019-06-20 22:37:55 +03:00
|
|
|
struct btrfs_trans_handle *btrfs_start_trans_remove_block_group(
|
|
|
|
struct btrfs_fs_info *fs_info,
|
|
|
|
const u64 chunk_offset);
|
|
|
|
int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
|
|
|
|
u64 group_start, struct extent_map *em);
|
|
|
|
void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info);
|
2019-10-29 21:20:18 +03:00
|
|
|
void btrfs_mark_bg_unused(struct btrfs_block_group *bg);
|
2021-04-19 10:41:02 +03:00
|
|
|
void btrfs_reclaim_bgs_work(struct work_struct *work);
|
|
|
|
void btrfs_reclaim_bgs(struct btrfs_fs_info *fs_info);
|
|
|
|
void btrfs_mark_bg_to_reclaim(struct btrfs_block_group *bg);
|
2019-06-20 22:37:57 +03:00
|
|
|
int btrfs_read_block_groups(struct btrfs_fs_info *info);
|
btrfs: rework chunk allocation to avoid exhaustion of the system chunk array
Commit eafa4fd0ad0607 ("btrfs: fix exhaustion of the system chunk array
due to concurrent allocations") fixed a problem that resulted in
exhausting the system chunk array in the superblock when there are many
tasks allocating chunks in parallel. Basically too many tasks enter the
first phase of chunk allocation without previous tasks having finished
their second phase of allocation, resulting in too many system chunks
being allocated. That was originally observed when running the fallocate
tests of stress-ng on a PowerPC machine, using a node size of 64K.
However that commit also introduced a deadlock where a task in phase 1 of
the chunk allocation waited for another task that had allocated a system
chunk to finish its phase 2, but that other task was waiting on an extent
buffer lock held by the first task, therefore resulting in both tasks not
making any progress. That change was later reverted by a patch with the
subject "btrfs: fix deadlock with concurrent chunk allocations involving
system chunks", since there is no simple and short solution to address it
and the deadlock is relatively easy to trigger on zoned filesystems, while
the system chunk array exhaustion is not so common.
This change reworks the chunk allocation to avoid the system chunk array
exhaustion. It accomplishes that by making the first phase of chunk
allocation do the updates of the device items in the chunk btree and the
insertion of the new chunk item in the chunk btree. This is done while
under the protection of the chunk mutex (fs_info->chunk_mutex), in the
same critical section that checks for available system space, allocates
a new system chunk if needed and reserves system chunk space. This way
we do not have chunk space reserved until the second phase completes.
The same logic is applied to chunk removal as well, since it keeps
reserved system space long after it is done updating the chunk btree.
For direct allocation of system chunks, the previous behaviour remains,
because otherwise we would deadlock on extent buffers of the chunk btree.
Changes to the chunk btree are by large done by chunk allocation and chunk
removal, which first reserve chunk system space and then later do changes
to the chunk btree. The other remaining cases are uncommon and correspond
to adding a device, removing a device and resizing a device. All these
other cases do not pre-reserve system space, they modify the chunk btree
right away, so they don't hold reserved space for a long period like chunk
allocation and chunk removal do.
The diff of this change is huge, but more than half of it is just addition
of comments describing both how things work regarding chunk allocation and
removal, including both the new behavior and the parts of the old behavior
that did not change.
CC: stable@vger.kernel.org # 5.12+
Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Tested-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Tested-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-06-29 16:43:06 +03:00
|
|
|
struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *trans,
|
|
|
|
u64 bytes_used, u64 type,
|
|
|
|
u64 chunk_offset, u64 size);
|
2019-06-20 22:37:57 +03:00
|
|
|
void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans);
|
btrfs: scrub: Don't check free space before marking a block group RO
[BUG]
When running btrfs/072 with only one online CPU, it has a pretty high
chance to fail:
btrfs/072 12s ... _check_dmesg: something found in dmesg (see xfstests-dev/results//btrfs/072.dmesg)
- output mismatch (see xfstests-dev/results//btrfs/072.out.bad)
--- tests/btrfs/072.out 2019-10-22 15:18:14.008965340 +0800
+++ /xfstests-dev/results//btrfs/072.out.bad 2019-11-14 15:56:45.877152240 +0800
@@ -1,2 +1,3 @@
QA output created by 072
Silence is golden
+Scrub find errors in "-m dup -d single" test
...
And with the following call trace:
BTRFS info (device dm-5): scrub: started on devid 1
------------[ cut here ]------------
BTRFS: Transaction aborted (error -27)
WARNING: CPU: 0 PID: 55087 at fs/btrfs/block-group.c:1890 btrfs_create_pending_block_groups+0x3e6/0x470 [btrfs]
CPU: 0 PID: 55087 Comm: btrfs Tainted: G W O 5.4.0-rc1-custom+ #13
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015
RIP: 0010:btrfs_create_pending_block_groups+0x3e6/0x470 [btrfs]
Call Trace:
__btrfs_end_transaction+0xdb/0x310 [btrfs]
btrfs_end_transaction+0x10/0x20 [btrfs]
btrfs_inc_block_group_ro+0x1c9/0x210 [btrfs]
scrub_enumerate_chunks+0x264/0x940 [btrfs]
btrfs_scrub_dev+0x45c/0x8f0 [btrfs]
btrfs_ioctl+0x31a1/0x3fb0 [btrfs]
do_vfs_ioctl+0x636/0xaa0
ksys_ioctl+0x67/0x90
__x64_sys_ioctl+0x43/0x50
do_syscall_64+0x79/0xe0
entry_SYSCALL_64_after_hwframe+0x49/0xbe
---[ end trace 166c865cec7688e7 ]---
[CAUSE]
The error number -27 is -EFBIG, returned from the following call chain:
btrfs_end_transaction()
|- __btrfs_end_transaction()
|- btrfs_create_pending_block_groups()
|- btrfs_finish_chunk_alloc()
|- btrfs_add_system_chunk()
This happens because we have used up all space of
btrfs_super_block::sys_chunk_array.
The root cause is, we have the following bad loop of creating tons of
system chunks:
1. The only SYSTEM chunk is being scrubbed
It's very common to have only one SYSTEM chunk.
2. New SYSTEM bg will be allocated
As btrfs_inc_block_group_ro() will check if we have enough space
after marking current bg RO. If not, then allocate a new chunk.
3. New SYSTEM bg is still empty, will be reclaimed
During the reclaim, we will mark it RO again.
4. That newly allocated empty SYSTEM bg get scrubbed
We go back to step 2, as the bg is already mark RO but still not
cleaned up yet.
If the cleaner kthread doesn't get executed fast enough (e.g. only one
CPU), then we will get more and more empty SYSTEM chunks, using up all
the space of btrfs_super_block::sys_chunk_array.
[FIX]
Since scrub/dev-replace doesn't always need to allocate new extent,
especially chunk tree extent, so we don't really need to do chunk
pre-allocation.
To break above spiral, here we introduce a new parameter to
btrfs_inc_block_group(), @do_chunk_alloc, which indicates whether we
need extra chunk pre-allocation.
For relocation, we pass @do_chunk_alloc=true, while for scrub, we pass
@do_chunk_alloc=false.
This should keep unnecessary empty chunks from popping up for scrub.
Also, since there are two parameters for btrfs_inc_block_group_ro(),
add more comment for it.
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2019-11-15 05:09:00 +03:00
|
|
|
int btrfs_inc_block_group_ro(struct btrfs_block_group *cache,
|
|
|
|
bool do_chunk_alloc);
|
2019-10-29 21:20:18 +03:00
|
|
|
void btrfs_dec_block_group_ro(struct btrfs_block_group *cache);
|
2019-06-20 22:38:00 +03:00
|
|
|
int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans);
|
|
|
|
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans);
|
|
|
|
int btrfs_setup_space_cache(struct btrfs_trans_handle *trans);
|
2019-06-20 22:38:01 +03:00
|
|
|
int btrfs_update_block_group(struct btrfs_trans_handle *trans,
|
2021-10-13 09:05:14 +03:00
|
|
|
u64 bytenr, u64 num_bytes, bool alloc);
|
2019-10-29 21:20:18 +03:00
|
|
|
int btrfs_add_reserved_bytes(struct btrfs_block_group *cache,
|
2019-06-20 22:38:01 +03:00
|
|
|
u64 ram_bytes, u64 num_bytes, int delalloc);
|
2019-10-29 21:20:18 +03:00
|
|
|
void btrfs_free_reserved_bytes(struct btrfs_block_group *cache,
|
2019-06-20 22:38:01 +03:00
|
|
|
u64 num_bytes, int delalloc);
|
2019-06-20 22:38:04 +03:00
|
|
|
int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, u64 flags,
|
|
|
|
enum btrfs_chunk_alloc_enum force);
|
|
|
|
int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans, u64 type);
|
|
|
|
void check_system_chunk(struct btrfs_trans_handle *trans, const u64 type);
|
btrfs: fix deadlock between chunk allocation and chunk btree modifications
When a task is doing some modification to the chunk btree and it is not in
the context of a chunk allocation or a chunk removal, it can deadlock with
another task that is currently allocating a new data or metadata chunk.
These contexts are the following:
* When relocating a system chunk, when we need to COW the extent buffers
that belong to the chunk btree;
* When adding a new device (ioctl), where we need to add a new device item
to the chunk btree;
* When removing a device (ioctl), where we need to remove a device item
from the chunk btree;
* When resizing a device (ioctl), where we need to update a device item in
the chunk btree and may need to relocate a system chunk that lies beyond
the new device size when shrinking a device.
The problem happens due to a sequence of steps like the following:
1) Task A starts a data or metadata chunk allocation and it locks the
chunk mutex;
2) Task B is relocating a system chunk, and when it needs to COW an extent
buffer of the chunk btree, it has locked both that extent buffer as
well as its parent extent buffer;
3) Since there is not enough available system space, either because none
of the existing system block groups have enough free space or because
the only one with enough free space is in RO mode due to the relocation,
task B triggers a new system chunk allocation. It blocks when trying to
acquire the chunk mutex, currently held by task A;
4) Task A enters btrfs_chunk_alloc_add_chunk_item(), in order to insert
the new chunk item into the chunk btree and update the existing device
items there. But in order to do that, it has to lock the extent buffer
that task B locked at step 2, or its parent extent buffer, but task B
is waiting on the chunk mutex, which is currently locked by task A,
therefore resulting in a deadlock.
One example report when the deadlock happens with system chunk relocation:
INFO: task kworker/u9:5:546 blocked for more than 143 seconds.
Not tainted 5.15.0-rc3+ #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:kworker/u9:5 state:D stack:25936 pid: 546 ppid: 2 flags:0x00004000
Workqueue: events_unbound btrfs_async_reclaim_metadata_space
Call Trace:
context_switch kernel/sched/core.c:4940 [inline]
__schedule+0xcd9/0x2530 kernel/sched/core.c:6287
schedule+0xd3/0x270 kernel/sched/core.c:6366
rwsem_down_read_slowpath+0x4ee/0x9d0 kernel/locking/rwsem.c:993
__down_read_common kernel/locking/rwsem.c:1214 [inline]
__down_read kernel/locking/rwsem.c:1223 [inline]
down_read_nested+0xe6/0x440 kernel/locking/rwsem.c:1590
__btrfs_tree_read_lock+0x31/0x350 fs/btrfs/locking.c:47
btrfs_tree_read_lock fs/btrfs/locking.c:54 [inline]
btrfs_read_lock_root_node+0x8a/0x320 fs/btrfs/locking.c:191
btrfs_search_slot_get_root fs/btrfs/ctree.c:1623 [inline]
btrfs_search_slot+0x13b4/0x2140 fs/btrfs/ctree.c:1728
btrfs_update_device+0x11f/0x500 fs/btrfs/volumes.c:2794
btrfs_chunk_alloc_add_chunk_item+0x34d/0xea0 fs/btrfs/volumes.c:5504
do_chunk_alloc fs/btrfs/block-group.c:3408 [inline]
btrfs_chunk_alloc+0x84d/0xf50 fs/btrfs/block-group.c:3653
flush_space+0x54e/0xd80 fs/btrfs/space-info.c:670
btrfs_async_reclaim_metadata_space+0x396/0xa90 fs/btrfs/space-info.c:953
process_one_work+0x9df/0x16d0 kernel/workqueue.c:2297
worker_thread+0x90/0xed0 kernel/workqueue.c:2444
kthread+0x3e5/0x4d0 kernel/kthread.c:319
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:295
INFO: task syz-executor:9107 blocked for more than 143 seconds.
Not tainted 5.15.0-rc3+ #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz-executor state:D stack:23200 pid: 9107 ppid: 7792 flags:0x00004004
Call Trace:
context_switch kernel/sched/core.c:4940 [inline]
__schedule+0xcd9/0x2530 kernel/sched/core.c:6287
schedule+0xd3/0x270 kernel/sched/core.c:6366
schedule_preempt_disabled+0xf/0x20 kernel/sched/core.c:6425
__mutex_lock_common kernel/locking/mutex.c:669 [inline]
__mutex_lock+0xc96/0x1680 kernel/locking/mutex.c:729
btrfs_chunk_alloc+0x31a/0xf50 fs/btrfs/block-group.c:3631
find_free_extent_update_loop fs/btrfs/extent-tree.c:3986 [inline]
find_free_extent+0x25cb/0x3a30 fs/btrfs/extent-tree.c:4335
btrfs_reserve_extent+0x1f1/0x500 fs/btrfs/extent-tree.c:4415
btrfs_alloc_tree_block+0x203/0x1120 fs/btrfs/extent-tree.c:4813
__btrfs_cow_block+0x412/0x1620 fs/btrfs/ctree.c:415
btrfs_cow_block+0x2f6/0x8c0 fs/btrfs/ctree.c:570
btrfs_search_slot+0x1094/0x2140 fs/btrfs/ctree.c:1768
relocate_tree_block fs/btrfs/relocation.c:2694 [inline]
relocate_tree_blocks+0xf73/0x1770 fs/btrfs/relocation.c:2757
relocate_block_group+0x47e/0xc70 fs/btrfs/relocation.c:3673
btrfs_relocate_block_group+0x48a/0xc60 fs/btrfs/relocation.c:4070
btrfs_relocate_chunk+0x96/0x280 fs/btrfs/volumes.c:3181
__btrfs_balance fs/btrfs/volumes.c:3911 [inline]
btrfs_balance+0x1f03/0x3cd0 fs/btrfs/volumes.c:4301
btrfs_ioctl_balance+0x61e/0x800 fs/btrfs/ioctl.c:4137
btrfs_ioctl+0x39ea/0x7b70 fs/btrfs/ioctl.c:4949
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:874 [inline]
__se_sys_ioctl fs/ioctl.c:860 [inline]
__x64_sys_ioctl+0x193/0x200 fs/ioctl.c:860
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x44/0xae
So fix this by making sure that whenever we try to modify the chunk btree
and we are neither in a chunk allocation context nor in a chunk remove
context, we reserve system space before modifying the chunk btree.
Reported-by: Hao Sun <sunhao.th@gmail.com>
Link: https://lore.kernel.org/linux-btrfs/CACkBjsax51i4mu6C0C3vJqQN3NR_iVuucoeG3U1HXjrgzn5FFQ@mail.gmail.com/
Fixes: 79bd37120b1495 ("btrfs: rework chunk allocation to avoid exhaustion of the system chunk array")
CC: stable@vger.kernel.org # 5.14+
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-10-13 12:12:49 +03:00
|
|
|
void btrfs_reserve_chunk_metadata(struct btrfs_trans_handle *trans,
|
|
|
|
bool is_item_insertion);
|
2019-06-20 22:38:05 +03:00
|
|
|
u64 btrfs_get_alloc_profile(struct btrfs_fs_info *fs_info, u64 orig_flags);
|
2019-06-20 22:38:06 +03:00
|
|
|
void btrfs_put_block_group_cache(struct btrfs_fs_info *info);
|
|
|
|
int btrfs_free_block_groups(struct btrfs_fs_info *info);
|
2021-02-04 13:22:02 +03:00
|
|
|
int btrfs_rmap_block(struct btrfs_fs_info *fs_info, u64 chunk_start,
|
|
|
|
struct block_device *bdev, u64 physical, u64 **logical,
|
|
|
|
int *naddrs, int *stripe_len);
|
2019-06-20 22:38:05 +03:00
|
|
|
|
|
|
|
static inline u64 btrfs_data_alloc_profile(struct btrfs_fs_info *fs_info)
|
|
|
|
{
|
|
|
|
return btrfs_get_alloc_profile(fs_info, BTRFS_BLOCK_GROUP_DATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u64 btrfs_metadata_alloc_profile(struct btrfs_fs_info *fs_info)
|
|
|
|
{
|
|
|
|
return btrfs_get_alloc_profile(fs_info, BTRFS_BLOCK_GROUP_METADATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u64 btrfs_system_alloc_profile(struct btrfs_fs_info *fs_info)
|
|
|
|
{
|
|
|
|
return btrfs_get_alloc_profile(fs_info, BTRFS_BLOCK_GROUP_SYSTEM);
|
|
|
|
}
|
2019-06-20 22:37:48 +03:00
|
|
|
|
2019-10-29 21:20:18 +03:00
|
|
|
static inline int btrfs_block_group_done(struct btrfs_block_group *cache)
|
2019-06-20 22:37:48 +03:00
|
|
|
{
|
|
|
|
smp_mb();
|
|
|
|
return cache->cached == BTRFS_CACHE_FINISHED ||
|
|
|
|
cache->cached == BTRFS_CACHE_ERROR;
|
|
|
|
}
|
2019-06-20 22:37:45 +03:00
|
|
|
|
2020-05-08 13:01:59 +03:00
|
|
|
void btrfs_freeze_block_group(struct btrfs_block_group *cache);
|
|
|
|
void btrfs_unfreeze_block_group(struct btrfs_block_group *cache);
|
|
|
|
|
btrfs: fix race between writes to swap files and scrub
When we active a swap file, at btrfs_swap_activate(), we acquire the
exclusive operation lock to prevent the physical location of the swap
file extents to be changed by operations such as balance and device
replace/resize/remove. We also call there can_nocow_extent() which,
among other things, checks if the block group of a swap file extent is
currently RO, and if it is we can not use the extent, since a write
into it would result in COWing the extent.
However we have no protection against a scrub operation running after we
activate the swap file, which can result in the swap file extents to be
COWed while the scrub is running and operating on the respective block
group, because scrub turns a block group into RO before it processes it
and then back again to RW mode after processing it. That means an attempt
to write into a swap file extent while scrub is processing the respective
block group, will result in COWing the extent, changing its physical
location on disk.
Fix this by making sure that block groups that have extents that are used
by active swap files can not be turned into RO mode, therefore making it
not possible for a scrub to turn them into RO mode. When a scrub finds a
block group that can not be turned to RO due to the existence of extents
used by swap files, it proceeds to the next block group and logs a warning
message that mentions the block group was skipped due to active swap
files - this is the same approach we currently use for balance.
Fixes: ed46ff3d42378 ("Btrfs: support swap files")
CC: stable@vger.kernel.org # 5.4+
Reviewed-by: Anand Jain <anand.jain@oracle.com>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
2021-02-05 15:55:37 +03:00
|
|
|
bool btrfs_inc_block_group_swap_extents(struct btrfs_block_group *bg);
|
|
|
|
void btrfs_dec_block_group_swap_extents(struct btrfs_block_group *bg, int amount);
|
|
|
|
|
2019-06-20 22:37:44 +03:00
|
|
|
#endif /* BTRFS_BLOCK_GROUP_H */
|