summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-07 12:27:11 +0100
committerMark Brown <broonie@kernel.org>2026-09-07 12:27:11 +0100
commite065266845694e0608f2a00a8a0a093e7db97bc8 (patch)
tree6d3c0bf43e692f91d4106a566e5c3c22e4cc5c5e
parent693947ef9f04ea9d82696b18ea9d7222ed4de609 (diff)
parent966bb86e7420c64f20edaac5ea096da9b16dc458 (diff)
downloadlinux-next-e065266845694e0608f2a00a8a0a093e7db97bc8.tar.gz
linux-next-e065266845694e0608f2a00a8a0a093e7db97bc8.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git
-rw-r--r--fs/btrfs/Kconfig1
-rw-r--r--fs/btrfs/bio.c141
-rw-r--r--fs/btrfs/bio.h5
-rw-r--r--fs/btrfs/block-group.c18
-rw-r--r--fs/btrfs/btrfs_inode.h12
-rw-r--r--fs/btrfs/dev-replace.c10
-rw-r--r--fs/btrfs/disk-io.c27
-rw-r--r--fs/btrfs/extent-tree.c10
-rw-r--r--fs/btrfs/extent_io.c64
-rw-r--r--fs/btrfs/extent_map.c8
-rw-r--r--fs/btrfs/file-item.c19
-rw-r--r--fs/btrfs/fs.h2
-rw-r--r--fs/btrfs/inode.c114
-rw-r--r--fs/btrfs/ioctl.c2
-rw-r--r--fs/btrfs/qgroup.c126
-rw-r--r--fs/btrfs/qgroup.h16
-rw-r--r--fs/btrfs/raid56.c46
-rw-r--r--fs/btrfs/sysfs.c129
-rw-r--r--fs/btrfs/tree-checker.c66
-rw-r--r--fs/btrfs/verity.c31
-rw-r--r--fs/btrfs/volumes.c39
-rw-r--r--fs/btrfs/volumes.h1
-rw-r--r--fs/btrfs/zoned.c5
-rw-r--r--include/uapi/linux/btrfs_tree.h21
24 files changed, 459 insertions, 454 deletions
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
index 4b10d78ed99b..0b8d8905e38e 100644
--- a/fs/btrfs/Kconfig
+++ b/fs/btrfs/Kconfig
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+# misc-next marker
config BTRFS_FS
tristate "Btrfs filesystem support"
diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index cc0bd03048ba..19b4855969f5 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -180,30 +180,13 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
struct btrfs_failed_bio *fbio = repair_bbio->private;
struct btrfs_inode *inode = repair_bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- /*
- * We can not move forward the saved_iter, as it will be later
- * utilized by repair_bbio again.
- */
- struct bvec_iter saved_iter = repair_bbio->saved_iter;
- const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
- const u64 logical = repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT;
- const u32 nr_steps = repair_bbio->saved_iter.bi_size / step;
int mirror = repair_bbio->mirror_num;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- phys_addr_t paddr;
- unsigned int slot = 0;
- /* Repair bbio should be eaxctly one block sized. */
+ /* Repair bbio should be exactly one block sized. */
ASSERT(repair_bbio->saved_iter.bi_size == fs_info->sectorsize);
- btrfs_bio_for_each_block(paddr, &repair_bbio->bio, &saved_iter, step) {
- ASSERT(slot < nr_steps);
- paddrs[slot] = paddr;
- slot++;
- }
-
if (repair_bbio->bio.bi_status ||
- !btrfs_data_csum_ok(repair_bbio, dev, 0, paddrs)) {
+ !btrfs_bio_data_csum_ok(repair_bbio, &repair_bbio->saved_iter, dev)) {
bio_reset(&repair_bbio->bio, NULL, REQ_OP_READ);
repair_bbio->bio.bi_iter = repair_bbio->saved_iter;
@@ -220,9 +203,8 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
do {
mirror = prev_repair_mirror(fbio, mirror);
- btrfs_repair_io_failure(fs_info, btrfs_ino(inode),
- repair_bbio->file_offset, fs_info->sectorsize,
- logical, paddrs, step, mirror);
+ btrfs_repair_bbio_failure(repair_bbio, &repair_bbio->saved_iter,
+ fs_info->sectorsize, mirror);
} while (mirror != fbio->bbio->mirror_num);
done:
@@ -238,25 +220,21 @@ done:
* read succeeded to restore the redundancy.
*/
static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
- u32 bio_offset,
- phys_addr_t paddrs[],
+ const struct bvec_iter *orig_iter,
struct btrfs_failed_bio *fbio)
{
struct btrfs_inode *inode = failed_bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- const u32 sectorsize = fs_info->sectorsize;
- const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
- const u32 nr_steps = sectorsize / step;
- /*
- * For bs > ps cases, the saved_iter can be partially moved forward.
- * In that case we should round it down to the block boundary.
- */
- const u64 logical = round_down(failed_bbio->saved_iter.bi_sector << SECTOR_SHIFT,
- sectorsize);
struct btrfs_bio *repair_bbio;
struct bio *repair_bio;
+ struct bvec_iter iter = *orig_iter;
+ const u32 sectorsize = fs_info->sectorsize;
+ const u32 bio_offset = ((iter.bi_sector - failed_bbio->saved_iter.bi_sector) <<
+ SECTOR_SHIFT);
+ const u64 logical = (iter.bi_sector << SECTOR_SHIFT);
int num_copies;
int mirror;
+ u32 cur = 0;
btrfs_debug(fs_info, "repair read error: read error at %llu",
failed_bbio->file_offset + bio_offset);
@@ -277,17 +255,21 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
atomic_inc(&fbio->repair_count);
- repair_bio = bio_alloc_bioset(NULL, nr_steps, REQ_OP_READ, GFP_NOFS,
- &btrfs_repair_bioset);
+ repair_bio = bio_alloc_bioset(NULL, max(1, sectorsize >> PAGE_SHIFT),
+ REQ_OP_READ, GFP_NOFS, &btrfs_repair_bioset);
repair_bio->bi_iter.bi_sector = logical >> SECTOR_SHIFT;
- for (int i = 0; i < nr_steps; i++) {
+ while (cur < sectorsize) {
+ struct page *page = bio_iter_page(&failed_bbio->bio, iter);
+ const u32 pg_off = bio_iter_offset(&failed_bbio->bio, iter);
+ const u32 cur_len = min(bio_iter_len(&failed_bbio->bio, iter),
+ sectorsize - cur);
int ret;
- ASSERT(offset_in_page(paddrs[i]) + step <= PAGE_SIZE);
+ ret = bio_add_page(repair_bio, page, cur_len, pg_off);
+ ASSERT(ret == cur_len);
- ret = bio_add_page(repair_bio, phys_to_page(paddrs[i]), step,
- offset_in_page(paddrs[i]));
- ASSERT(ret == step);
+ bio_advance_iter_single(&failed_bbio->bio, &iter, cur_len);
+ cur += cur_len;
}
repair_bbio = btrfs_bio(repair_bio);
@@ -305,18 +287,16 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
const u32 sectorsize = fs_info->sectorsize;
- const u32 step = min(sectorsize, PAGE_SIZE);
- const u32 nr_steps = sectorsize / step;
- struct bvec_iter *iter = &bbio->saved_iter;
+ struct bvec_iter iter;
blk_status_t status = bbio->bio.bi_status;
struct btrfs_failed_bio *fbio = NULL;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- phys_addr_t paddr;
- u32 offset = 0;
/* Read-repair requires the inode field to be set by the submitter. */
ASSERT(inode);
+ /* The original bbio should be sectorsize aligned. */
+ ASSERT(IS_ALIGNED(bbio->saved_iter.bi_size, sectorsize));
+
/*
* Hand off repair bios to the repair code as there is no upper level
* submitter for them.
@@ -329,16 +309,10 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
/* Clear the I/O error. A failed repair will reset it. */
bbio->bio.bi_status = BLK_STS_OK;
- btrfs_bio_for_each_block(paddr, &bbio->bio, iter, step) {
- paddrs[(offset / step) % nr_steps] = paddr;
- offset += step;
-
- if (IS_ALIGNED(offset, sectorsize)) {
- if (status ||
- !btrfs_data_csum_ok(bbio, dev, offset - sectorsize, paddrs))
- fbio = repair_one_sector(bbio, offset - sectorsize,
- paddrs, fbio);
- }
+ for (iter = bbio->saved_iter; iter.bi_size;
+ bio_advance_iter(&bbio->bio, &iter, sectorsize)) {
+ if (status || !btrfs_bio_data_csum_ok(bbio, &iter, dev))
+ fbio = repair_one_sector(bbio, &iter, fbio);
}
if (bbio->csum != bbio->csum_inline)
kvfree(bbio->csum);
@@ -925,21 +899,23 @@ void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num)
* The I/O is issued synchronously to block the repair read completion from
* freeing the bio.
*
- * @ino: Offending inode number
- * @fileoff: File offset inside the inode
+ * @bbio: Original bbio where the repair is needed
+ * @orig_iter: Points to where the repair starts
* @length: Length of the repair write
- * @logical: Logical address of the range
- * @paddrs: Physical address array of the content
- * @step: Length of for each paddrs
* @mirror_num: Mirror number to write to. Must not be zero
*/
-int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
- u32 length, u64 logical, const phys_addr_t paddrs[],
- unsigned int step, int mirror_num)
+int btrfs_repair_bbio_failure(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
+ u32 length, int mirror_num)
{
- const u32 nr_steps = DIV_ROUND_UP_POW2(length, step);
+ struct btrfs_inode *inode = bbio->inode;
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct btrfs_io_stripe smap = { 0 };
- struct bio *bio = NULL;
+ struct bvec_iter iter = *orig_iter;
+ struct bio *repair_bio = NULL;
+ const u64 logical = iter.bi_sector << SECTOR_SHIFT;
+ const u64 fileoff = bbio->file_offset +
+ ((iter.bi_sector - bbio->saved_iter.bi_sector) << SECTOR_SHIFT);
+ u32 cur = 0;
int ret = 0;
BUG_ON(!mirror_num);
@@ -950,8 +926,9 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
ASSERT(IS_ALIGNED(fileoff, fs_info->sectorsize));
/* Either it's a single data or metadata block. */
ASSERT(length <= BTRFS_MAX_BLOCKSIZE);
- ASSERT(step <= length);
- ASSERT(is_power_of_2(step));
+
+ /* Our current iter should not be before the original bbio saved_iter. */
+ ASSERT(iter.bi_sector >= bbio->saved_iter.bi_sector);
/*
* The fs either mounted RO or hit critical errors, no need
@@ -979,15 +956,22 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
goto out_counter_dec;
}
- bio = bio_alloc(smap.dev->bdev, nr_steps, REQ_OP_WRITE | REQ_SYNC, GFP_NOFS);
- bio->bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
- for (int i = 0; i < nr_steps; i++) {
- ret = bio_add_page(bio, phys_to_page(paddrs[i]), step, offset_in_page(paddrs[i]));
- /* We should have allocated enough slots to contain all the different pages. */
- ASSERT(ret == step);
+ repair_bio = bio_alloc(smap.dev->bdev, max(1, length >> PAGE_SHIFT),
+ REQ_OP_WRITE | REQ_SYNC, GFP_NOFS);
+ repair_bio->bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
+ while (cur < length) {
+ struct page *page = bio_iter_page(&bbio->bio, iter);
+ const u32 pg_off = bio_iter_offset(&bbio->bio, iter);
+ const u32 cur_len = min(bio_iter_len(&bbio->bio, iter), length - cur);
+
+ ret = bio_add_page(repair_bio, page, cur_len, pg_off);
+ ASSERT(ret == cur_len);
+ bio_advance_iter_single(&bbio->bio, &iter, cur_len);
+ cur += cur_len;
}
- ret = submit_bio_wait(bio);
- bio_put(bio);
+
+ ret = submit_bio_wait(repair_bio);
+ bio_put(repair_bio);
if (ret) {
/* try to remap that extent elsewhere? */
btrfs_dev_stat_inc_and_print(smap.dev, BTRFS_DEV_STAT_WRITE_ERRS);
@@ -995,8 +979,9 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
}
btrfs_info_rl(fs_info,
- "read error corrected: ino %llu off %llu (dev %s sector %llu)",
- ino, fileoff, btrfs_dev_name(smap.dev),
+ "read error corrected: root %llu ino %llu off %llu (dev %s sector %llu)",
+ btrfs_root_id(inode->root), btrfs_ino(inode), fileoff,
+ btrfs_dev_name(smap.dev),
smap.physical >> SECTOR_SHIFT);
ret = 0;
diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h
index 303ed6c7103d..b7bd377a0162 100644
--- a/fs/btrfs/bio.h
+++ b/fs/btrfs/bio.h
@@ -126,8 +126,7 @@ void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status);
void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num);
void btrfs_submit_repair_write(struct btrfs_bio *bbio, int mirror_num, bool dev_replace);
-int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
- u32 length, u64 logical, const phys_addr_t paddrs[],
- unsigned int step, int mirror_num);
+int btrfs_repair_bbio_failure(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
+ u32 length, int mirror_num);
#endif
diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index 830460a40e86..ee182369254c 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -3074,21 +3074,25 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran
return ERR_PTR(ret);
}
- ret = btrfs_add_new_free_space(cache, chunk_offset, chunk_offset + size, NULL);
- btrfs_free_excluded_extents(cache);
- if (ret) {
- btrfs_put_block_group(cache);
- return ERR_PTR(ret);
- }
-
/*
* Ensure the corresponding space_info object is created and
* assigned to our block group. We want our bg to be added to the rbtree
* with its ->space_info set.
+ *
+ * On a zoned filesystem btrfs_add_new_free_space() ends up in
+ * __btrfs_add_free_space_zoned(), which dereferences
+ * block_group->space_info, so it has to be set beforehand.
*/
cache->space_info = space_info;
ASSERT(cache->space_info);
+ ret = btrfs_add_new_free_space(cache, chunk_offset, chunk_offset + size, NULL);
+ btrfs_free_excluded_extents(cache);
+ if (ret) {
+ btrfs_put_block_group(cache);
+ return ERR_PTR(ret);
+ }
+
ret = btrfs_add_block_group_cache(cache);
if (ret) {
btrfs_remove_free_space_cache(cache);
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 1082fa92c145..89e5e9c0c904 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -507,14 +507,10 @@ static inline void btrfs_set_inode_mapping_order(struct btrfs_inode *inode)
inode->root->fs_info->block_max_order);
}
-void btrfs_calculate_block_csum_folio(struct btrfs_fs_info *fs_info,
- const phys_addr_t paddr, u8 *dest);
-void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
- const phys_addr_t paddrs[], u8 *dest);
-int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
- const u8 * const csum_expected);
-bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
- u32 bio_offset, const phys_addr_t paddrs[]);
+bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
+ struct btrfs_device *dev);
+void btrfs_csum_one_bio_block(struct btrfs_fs_info *fs_info, struct bio *bio,
+ const struct bvec_iter *orig_iter, u8 *csum);
noinline int can_nocow_extent(struct btrfs_inode *inode, u64 offset, u64 *len,
struct btrfs_file_extent *file_extent,
bool nowait);
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index af1b898029e8..22eea188a328 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -235,7 +235,8 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
struct btrfs_device **device_out)
{
struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
- struct btrfs_device *device;
+ struct btrfs_device *device = NULL;
+ struct btrfs_device *tmp_device;
struct file *bdev_file;
struct block_device *bdev;
u64 devid = BTRFS_DEV_REPLACE_DEVID;
@@ -264,8 +265,8 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
sync_blockdev(bdev);
- list_for_each_entry(device, &fs_devices->devices, dev_list) {
- if (device->bdev == bdev) {
+ list_for_each_entry(tmp_device, &fs_devices->devices, dev_list) {
+ if (tmp_device->bdev == bdev) {
btrfs_err(fs_info,
"target device is in the filesystem!");
ret = -EEXIST;
@@ -285,6 +286,7 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
device = btrfs_alloc_device(NULL, &devid, NULL, device_path);
if (IS_ERR(device)) {
ret = PTR_ERR(device);
+ device = NULL;
goto error;
}
@@ -328,6 +330,8 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
error:
/* Undo the open-time freeze deny. */
+ if (device)
+ btrfs_free_device(device);
btrfs_release_device_allow_freeze(bdev_file);
return ret;
}
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 819727460bcf..a1d83ad9a4c0 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -176,19 +176,24 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb,
int mirror_num)
{
struct btrfs_fs_info *fs_info = eb->fs_info;
- const u32 step = min(fs_info->nodesize, PAGE_SIZE);
- const u32 nr_steps = eb->len / step;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
+ struct btrfs_bio *bbio;
+ int ret;
if (sb_rdonly(fs_info->sb))
return -EROFS;
+ /*
+ * This bbio is only to queue all pages for btrfs_repair_bbio_failure().
+ * Thus it will never get its endio called.
+ */
+ bbio = btrfs_bio_alloc(max(1, fs_info->nodesize >> PAGE_SHIFT), REQ_OP_READ,
+ BTRFS_I(fs_info->btree_inode), eb->start, NULL, NULL);
+ bbio->bio.bi_iter.bi_sector = eb->start >> SECTOR_SHIFT;
for (int i = 0; i < num_extent_pages(eb); i++) {
struct folio *folio = eb->folios[i];
/* No large folio support yet. */
ASSERT(folio_order(folio) == 0);
- ASSERT(i < nr_steps);
/*
* For nodesize < page size, there is just one paddr, with some
@@ -197,11 +202,17 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb,
* For nodesize >= page size, it's one or more paddrs, and eb->start
* must be aligned to page boundary.
*/
- paddrs[i] = page_to_phys(&folio->page) + offset_in_page(eb->start);
+ ret = bio_add_page(&bbio->bio, &folio->page, min(PAGE_SIZE, fs_info->nodesize),
+ offset_in_page(eb->start));
+ ASSERT(ret == min(PAGE_SIZE, fs_info->nodesize));
}
+ /* Since the bbio is never submitted, we have to save the iter manually. */
+ bbio->saved_iter = bbio->bio.bi_iter;
- return btrfs_repair_io_failure(fs_info, 0, eb->start, eb->len,
- eb->start, paddrs, step, mirror_num);
+ ret = btrfs_repair_bbio_failure(bbio, &bbio->saved_iter, fs_info->nodesize,
+ mirror_num);
+ bio_put(&bbio->bio);
+ return ret;
}
/*
@@ -1485,7 +1496,9 @@ static int cleaner_kthread(void *arg)
btrfs_run_delayed_iputs(fs_info);
+ set_bit(BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN, &fs_info->qgroup_flags);
again = btrfs_clean_one_deleted_snapshot(fs_info);
+ clear_bit(BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN, &fs_info->qgroup_flags);
mutex_unlock(&fs_info->cleaner_mutex);
/*
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index d6a4390ee34a..a0d5ab03aae2 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -6315,6 +6315,16 @@ int btrfs_drop_snapshot(struct btrfs_root *root, bool update_ref, bool for_reloc
set_bit(BTRFS_ROOT_DELETING, &root->state);
unfinished_drop = test_bit(BTRFS_ROOT_UNFINISHED_DROP, &root->state);
+ /*
+ * For subvolume dropping, check if the subvolume is large enough so
+ * that we need to mark qgroup inconsistent to avoid long qgroup stall.
+ *
+ * Even for a subvolume without any snapshot, there can still be
+ * a lot of qgroup records queued into one transaction.
+ */
+ if (!for_reloc)
+ btrfs_qgroup_check_tree_drop(fs_info, rootid,
+ btrfs_header_level(root->node));
if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
level = btrfs_header_level(root->node);
path->nodes[level] = btrfs_lock_root_node(root);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index d7600e5fa3d9..a221b63bdb20 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -1808,6 +1808,22 @@ out:
return 0;
}
+static struct btrfs_ordered_extent *get_oe_from_bbio(const struct btrfs_bio *bbio,
+ u64 filepos)
+{
+ struct btrfs_ordered_extent *oe;
+
+ if (!bbio || !bbio->ordered)
+ return NULL;
+
+ oe = bbio->ordered;
+ if (!in_range(filepos, oe->file_offset, oe->num_bytes))
+ return NULL;
+
+ refcount_inc(&oe->refs);
+ return oe;
+}
+
/*
* Return 0 if we have submitted or queued the sector for submission.
* Return <0 for critical errors, and the involved sector will be cleaned up.
@@ -1820,11 +1836,10 @@ static int submit_one_sector(struct btrfs_inode *inode,
loff_t i_size)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- struct extent_map *em;
+ struct btrfs_ordered_extent *oe;
u64 block_start;
u64 disk_bytenr;
u64 extent_offset;
- u64 em_end;
const u32 sectorsize = fs_info->sectorsize;
unsigned int queued;
@@ -1833,8 +1848,11 @@ static int submit_one_sector(struct btrfs_inode *inode,
/* @filepos >= i_size case should be handled by the caller. */
ASSERT(filepos < i_size);
- em = btrfs_get_extent(inode, NULL, filepos, sectorsize);
- if (IS_ERR(em)) {
+ /* Try to reuse the existing OE from bbio first. */
+ oe = get_oe_from_bbio(bio_ctrl->bbio, filepos);
+ if (!oe)
+ oe = btrfs_lookup_ordered_extent(inode, filepos);
+ if (unlikely(!oe)) {
/*
* bio_ctrl may contain a bio crossing several folios.
* Submit it immediately so that the bio has a chance
@@ -1857,31 +1875,25 @@ static int submit_one_sector(struct btrfs_inode *inode,
*/
btrfs_mark_ordered_io_finished(inode, filepos, fs_info->sectorsize,
false);
- return PTR_ERR(em);
+ btrfs_err_rl(fs_info,
+ "no ordered extent for root %lld ino %llu filepos %llu",
+ btrfs_root_id(inode->root), btrfs_ino(inode),
+ filepos);
+ return -EUCLEAN;
}
- extent_offset = filepos - em->start;
- em_end = btrfs_extent_map_end(em);
- ASSERT(filepos <= em_end);
- ASSERT(IS_ALIGNED(em->start, sectorsize));
- ASSERT(IS_ALIGNED(em->len, sectorsize));
-
- block_start = btrfs_extent_map_block_start(em);
- disk_bytenr = btrfs_extent_map_block_start(em) + extent_offset;
+ extent_offset = filepos - oe->file_offset;
+ ASSERT(filepos < oe->file_offset + oe->num_bytes);
+ ASSERT(IS_ALIGNED(oe->file_offset, sectorsize));
+ ASSERT(IS_ALIGNED(oe->num_bytes, sectorsize));
+ ASSERT(oe->compress_type == BTRFS_COMPRESS_NONE);
+ ASSERT(!test_bit(BTRFS_ORDERED_COMPRESSED, &oe->flags));
- ASSERT(!btrfs_extent_map_is_compressed(em));
- ASSERT(block_start != EXTENT_MAP_HOLE);
- ASSERT(block_start != EXTENT_MAP_INLINE);
+ block_start = oe->disk_bytenr + oe->offset;
+ disk_bytenr = block_start + extent_offset;
- btrfs_free_extent_map(em);
- em = NULL;
+ btrfs_put_ordered_extent(oe);
- /*
- * Although the PageDirty bit is cleared before entering this
- * function, subpage dirty bit is not cleared.
- * So clear subpage dirty bit here so next time we won't submit
- * a folio for a range already written to disk.
- */
btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize);
/*
@@ -1898,6 +1910,10 @@ static int submit_one_sector(struct btrfs_inode *inode,
btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize);
btrfs_mark_ordered_io_finished(inode, filepos, fs_info->sectorsize,
false);
+ btrfs_err_rl(fs_info,
+ "failed to queue sector for root %lld ino %llu filepos %llu",
+ btrfs_root_id(inode->root),
+ btrfs_ino(inode), filepos);
return -EUCLEAN;
}
return 0;
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 6ad7b39ae358..86d9c6f5ff4b 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -1220,6 +1220,14 @@ static struct btrfs_inode *find_first_inode_to_shrink(struct btrfs_root *root,
tree = &inode->extent_tree;
/*
+ * Most inodes have no extent maps, so check without the lock.
+ * The race is harmless: a false empty just defers the inode to
+ * a later scan, and a false non-empty is caught under the lock.
+ */
+ if (data_race(RB_EMPTY_ROOT(&tree->root)))
+ goto next;
+
+ /*
* We want to be fast so if the lock is busy we don't want to
* spend time waiting for it (some task is about to do IO for
* the inode).
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index cf50fd623f41..581ca5653be9 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -801,25 +801,16 @@ static void csum_one_bio(struct btrfs_bio *bbio, struct bvec_iter *src)
{
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- struct bio *bio = &bbio->bio;
struct btrfs_ordered_sum *sums = bbio->sums;
- struct bvec_iter iter = *src;
- phys_addr_t paddr;
+ struct bvec_iter iter;
const u32 blocksize = fs_info->sectorsize;
- const u32 step = min(blocksize, PAGE_SIZE);
- const u32 nr_steps = blocksize / step;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- u32 offset = 0;
int index = 0;
- btrfs_bio_for_each_block(paddr, bio, &iter, step) {
- paddrs[(offset / step) % nr_steps] = paddr;
- offset += step;
+ for (iter = *src; iter.bi_size; bio_advance_iter(&bbio->bio, &iter, blocksize)) {
+ btrfs_csum_one_bio_block(fs_info, &bbio->bio, &iter,
+ sums->sums + index);
- if (IS_ALIGNED(offset, blocksize)) {
- btrfs_calculate_block_csum_pages(fs_info, paddrs, sums->sums + index);
- index += fs_info->csum_size;
- }
+ index += fs_info->csum_size;
}
}
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 10e15a319b93..3eba8438593c 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -811,7 +811,7 @@ struct btrfs_fs_info {
struct btrfs_discard_ctl discard_ctl;
/* Is qgroup tracking in a consistent state? */
- u64 qgroup_flags;
+ unsigned long qgroup_flags;
/* Holds configuration and tracking. Protected by qgroup_lock. */
struct rb_root qgroup_tree;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 93ef3cec191e..2967a307d8f0 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3456,79 +3456,30 @@ int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered)
return btrfs_finish_one_ordered(ordered);
}
-/*
- * Calculate the checksum of an fs block at physical memory address @paddr,
- * and save the result to @dest.
- *
- * The folio containing @paddr must be large enough to contain a full fs block.
- */
-void btrfs_calculate_block_csum_folio(struct btrfs_fs_info *fs_info,
- const phys_addr_t paddr, u8 *dest)
-{
- struct folio *folio = page_folio(phys_to_page(paddr));
- const u32 blocksize = fs_info->sectorsize;
- const u32 step = min(blocksize, PAGE_SIZE);
- const u32 nr_steps = blocksize / step;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
-
- /* The full block must be inside the folio. */
- ASSERT(offset_in_folio(folio, paddr) + blocksize <= folio_size(folio));
-
- for (int i = 0; i < nr_steps; i++) {
- u32 pindex = offset_in_folio(folio, paddr + i * step) >> PAGE_SHIFT;
-
- /*
- * For bs <= ps cases, we will only run the loop once, so the offset
- * inside the page will only added to paddrs[0].
- *
- * For bs > ps cases, the block must be page aligned, thus offset
- * inside the page will always be 0.
- */
- paddrs[i] = page_to_phys(folio_page(folio, pindex)) + offset_in_page(paddr);
- }
- return btrfs_calculate_block_csum_pages(fs_info, paddrs, dest);
-}
-
-/*
- * Calculate the checksum of a fs block backed by multiple noncontiguous pages
- * at @paddrs[] and save the result to @dest.
- *
- * The folio containing @paddr must be large enough to contain a full fs block.
- */
-void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
- const phys_addr_t paddrs[], u8 *dest)
+/* Generate data checksum for a single fs block, pointed to by @orig_iter. */
+void btrfs_csum_one_bio_block(struct btrfs_fs_info *fs_info, struct bio *bio,
+ const struct bvec_iter *orig_iter, u8 *csum)
{
+ struct btrfs_csum_ctx cctx;
+ struct bvec_iter iter = *orig_iter;
const u32 blocksize = fs_info->sectorsize;
- const u32 step = min(blocksize, PAGE_SIZE);
- const u32 nr_steps = blocksize / step;
- struct btrfs_csum_ctx csum;
+ u32 cur = 0;
- btrfs_csum_init(&csum, fs_info->csum_type);
- for (int i = 0; i < nr_steps; i++) {
- const phys_addr_t paddr = paddrs[i];
+ btrfs_csum_init(&cctx, fs_info->csum_type);
+ while (cur < blocksize) {
+ struct page *page = bio_iter_page(bio, iter);
+ const u32 pg_off = bio_iter_offset(bio, iter);
+ const u32 cur_len = min(bio_iter_len(bio, iter), blocksize - cur);
void *kaddr;
- ASSERT(offset_in_page(paddr) + step <= PAGE_SIZE);
- kaddr = kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr);
- btrfs_csum_update(&csum, kaddr, step);
+ kaddr = kmap_local_page(page) + pg_off;
+ btrfs_csum_update(&cctx, kaddr, cur_len);
kunmap_local(kaddr);
- }
- btrfs_csum_final(&csum, dest);
-}
-/*
- * Verify the checksum for a single sector without any extra action that depend
- * on the type of I/O.
- *
- * @kaddr must be a properly kmapped address.
- */
-int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
- const u8 * const csum_expected)
-{
- btrfs_calculate_block_csum_folio(fs_info, paddr, csum);
- if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
- return -EIO;
- return 0;
+ bio_advance_iter_single(bio, &iter, cur_len);
+ cur += cur_len;
+ }
+ btrfs_csum_final(&cctx, csum);
}
/*
@@ -3536,27 +3487,30 @@ int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8
* different noncontiguous pages.
*
* @bbio: btrfs_io_bio which contains the csum
- * @dev: device the sector is on
- * @bio_offset: offset to the beginning of the bio (in bytes)
- * @paddrs: physical addresses which back the fs block
+ * @orig_iter: bvec iter pointing to the start of the block
+ * @dev: device the sector is on (optional)
*
* Check if the checksum on a data block is valid. When a checksum mismatch is
* detected, report the error and fill the corrupted range with zero.
*
* Return %true if the sector is ok or had no checksum to start with, else %false.
*/
-bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
- u32 bio_offset, const phys_addr_t paddrs[])
+bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio,
+ const struct bvec_iter *orig_iter,
+ struct btrfs_device *dev)
{
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ struct bvec_iter iter = *orig_iter;
const u32 blocksize = fs_info->sectorsize;
- const u32 step = min(blocksize, PAGE_SIZE);
- const u32 nr_steps = blocksize / step;
+ const u32 bio_offset = (iter.bi_sector - bbio->saved_iter.bi_sector) << SECTOR_SHIFT;
u64 file_offset = bbio->file_offset + bio_offset;
u64 end = file_offset + blocksize - 1;
u8 *csum_expected;
u8 csum[BTRFS_CSUM_SIZE];
+ u32 cur = 0;
+
+ ASSERT(iter.bi_sector >= bbio->saved_iter.bi_sector);
if (!bbio->csum)
return true;
@@ -3572,7 +3526,7 @@ bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
csum_expected = bbio->csum + (bio_offset >> fs_info->sectorsize_bits) *
fs_info->csum_size;
- btrfs_calculate_block_csum_pages(fs_info, paddrs, csum);
+ btrfs_csum_one_bio_block(fs_info, &bbio->bio, orig_iter, csum);
if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
goto zeroit;
return true;
@@ -3582,8 +3536,16 @@ zeroit:
bbio->mirror_num);
if (dev)
btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_CORRUPTION_ERRS);
- for (int i = 0; i < nr_steps; i++)
- memzero_page(phys_to_page(paddrs[i]), offset_in_page(paddrs[i]), step);
+ while (cur < blocksize) {
+ struct page *page = bio_iter_page(&bbio->bio, iter);
+ const u32 pg_off = bio_iter_offset(&bbio->bio, iter);
+ const u32 cur_len = min(bio_iter_len(&bbio->bio, iter), blocksize - cur);
+
+ memzero_page(page, pg_off, cur_len);
+
+ bio_advance_iter_single(&bbio->bio, &iter, cur_len);
+ cur += cur_len;
+ }
return false;
}
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index e4b2da31a0d5..54960351fbd1 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3881,7 +3881,7 @@ static long btrfs_ioctl_quota_rescan_status(struct btrfs_fs_info *fs_info,
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags)) {
qsa.flags = 1;
qsa.progress = fs_info->qgroup_rescan_progress.objectid;
}
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index f68b696b4bf7..05e35eb126dc 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -34,7 +34,7 @@ enum btrfs_qgroup_mode btrfs_qgroup_mode(const struct btrfs_fs_info *fs_info)
{
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
return BTRFS_QGROUP_MODE_DISABLED;
- if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE)
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_SIMPLE_MODE, &fs_info->qgroup_flags))
return BTRFS_QGROUP_MODE_SIMPLE;
return BTRFS_QGROUP_MODE_FULL;
}
@@ -384,14 +384,14 @@ static bool squota_check_parent_usage(struct btrfs_fs_info *fs_info, struct btrf
__printf(2, 3)
static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info, const char *fmt, ...)
{
- const u64 old_flags = fs_info->qgroup_flags;
+ const unsigned long old_flags = fs_info->qgroup_flags;
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
return;
- fs_info->qgroup_flags |= (BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT |
- BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN |
- BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING);
- if (!(old_flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT)) {
+ set_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags);
+ set_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags);
+ set_bit(BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING, &fs_info->qgroup_flags);
+ if (!test_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &old_flags)) {
struct va_format vaf;
va_list args;
@@ -426,7 +426,6 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
struct extent_buffer *l;
int slot;
int ret = 0;
- u64 flags = 0;
u64 rescan_progress = 0;
if (!fs_info->quota_root)
@@ -473,8 +472,12 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
"old qgroup version, quota disabled");
goto out;
}
- fs_info->qgroup_flags = btrfs_qgroup_status_flags(l, ptr);
- if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE)
+ if (btrfs_qgroup_status_flags(l, ptr) > ULONG_MAX) {
+ btrfs_err(fs_info, "invalid qgroup status flags, quota disabled");
+ goto out;
+ }
+ fs_info->qgroup_flags = (unsigned long)btrfs_qgroup_status_flags(l, ptr);
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_SIMPLE_MODE, &fs_info->qgroup_flags))
qgroup_read_enable_gen(fs_info, l, slot, ptr);
else if (btrfs_qgroup_status_generation(l, ptr) != fs_info->generation)
qgroup_mark_inconsistent(fs_info, "qgroup generation mismatch");
@@ -609,14 +612,13 @@ next2:
}
out:
btrfs_free_path(path);
- fs_info->qgroup_flags |= flags;
if (ret >= 0) {
- if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON)
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags))
set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
- if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN)
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags))
ret = qgroup_rescan_init(fs_info, rescan_progress, 0);
} else {
- fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
btrfs_sysfs_del_qgroups(fs_info);
}
@@ -1101,9 +1103,9 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
struct btrfs_qgroup_status_item);
btrfs_set_qgroup_status_generation(leaf, ptr, trans->transid);
btrfs_set_qgroup_status_version(leaf, ptr, BTRFS_QGROUP_STATUS_VERSION);
- fs_info->qgroup_flags = BTRFS_QGROUP_STATUS_FLAG_ON;
+ set_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags);
if (simple) {
- fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
+ set_bit(BTRFS_QGROUP_STATUS_BIT_SIMPLE_MODE, &fs_info->qgroup_flags);
btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA);
/*
* Set the enable generation to the next transaction, as we cannot
@@ -1113,7 +1115,7 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
*/
btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid + 1);
} else {
- fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
+ set_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags);
}
btrfs_set_qgroup_status_flags(leaf, ptr, fs_info->qgroup_flags &
BTRFS_QGROUP_STATUS_FLAGS_MASK);
@@ -1403,8 +1405,14 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
spin_lock(&fs_info->qgroup_lock);
quota_root = fs_info->quota_root;
fs_info->quota_root = NULL;
- fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON;
- fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
+ /*
+ * Clear all on-disk and runtime bits, except RESCAN related ones, that
+ * are either handled by rescan thread, or the caller who rejects rescan.
+ */
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags);
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_SIMPLE_MODE, &fs_info->qgroup_flags);
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags);
+ clear_bit(BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING, &fs_info->qgroup_flags);
fs_info->qgroup_drop_subtree_thres = BTRFS_QGROUP_DROP_SUBTREE_THRES_DEFAULT;
spin_unlock(&fs_info->qgroup_lock);
@@ -1554,7 +1562,7 @@ static int quick_update_accounting(struct btrfs_fs_info *fs_info,
}
out:
if (ret)
- fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
+ set_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags);
return ret;
}
@@ -1875,7 +1883,7 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
* very frequently.
*/
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_FULL &&
- !(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT)) {
+ !test_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags)) {
if (unlikely(qgroup->rfer || qgroup->excl ||
qgroup->rfer_cmpr || qgroup->excl_cmpr)) {
DEBUG_WARN();
@@ -2120,7 +2128,7 @@ int btrfs_qgroup_trace_extent_post(struct btrfs_trans_handle *trans,
*/
ASSERT(trans != NULL);
- if (fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING)
+ if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING, &fs_info->qgroup_flags))
return 0;
ret = btrfs_find_all_roots(&ctx, true);
@@ -2740,6 +2748,24 @@ walk_down:
return 0;
}
+void btrfs_qgroup_check_tree_drop(struct btrfs_fs_info *fs_info, u64 rootid, u8 level)
+{
+ u8 drop_subtree_thres;
+
+ if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_FULL)
+ return;
+
+ if (!btrfs_is_fstree(rootid))
+ return;
+
+ spin_lock(&fs_info->qgroup_lock);
+ drop_subtree_thres = fs_info->qgroup_drop_subtree_thres;
+ spin_unlock(&fs_info->qgroup_lock);
+
+ if (level >= drop_subtree_thres)
+ qgroup_mark_inconsistent(fs_info, "subtree level reached threshold");
+}
+
static void qgroup_iterator_nested_add(struct list_head *head, struct btrfs_qgroup *qgroup)
{
if (!list_empty(&qgroup->nested_iterator))
@@ -2961,7 +2987,7 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
* we can't just exit here.
*/
if (!btrfs_qgroup_full_accounting(fs_info) ||
- fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING)
+ test_bit(BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING, &fs_info->qgroup_flags))
goto out_free;
if (new_roots) {
@@ -2983,7 +3009,7 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
num_bytes, nr_old_roots, nr_new_roots);
mutex_lock(&fs_info->qgroup_rescan_lock);
- if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags)) {
if (fs_info->qgroup_rescan_progress.objectid <= bytenr) {
mutex_unlock(&fs_info->qgroup_rescan_lock);
ret = 0;
@@ -3044,8 +3070,8 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
num_dirty_extents++;
trace_btrfs_qgroup_account_extents(fs_info, record, bytenr);
- if (!ret && !(fs_info->qgroup_flags &
- BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING)) {
+ if (!ret && !test_bit(BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING,
+ &fs_info->qgroup_flags)) {
struct btrfs_backref_walk_ctx ctx = { 0 };
ctx.bytenr = bytenr;
@@ -3152,9 +3178,9 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans)
spin_lock(&fs_info->qgroup_lock);
}
if (btrfs_qgroup_enabled(fs_info))
- fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_ON;
+ set_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags);
else
- fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON;
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags);
spin_unlock(&fs_info->qgroup_lock);
ret = update_qgroup_status_item(trans);
@@ -3844,7 +3870,7 @@ static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
return true;
if (!btrfs_qgroup_enabled(fs_info))
return true;
- if (fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN)
+ if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags))
return true;
return false;
}
@@ -3894,12 +3920,10 @@ out:
btrfs_free_path(path);
mutex_lock(&fs_info->qgroup_rescan_lock);
- if (ret > 0 &&
- fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT) {
- fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
- } else if (ret < 0 || stopped) {
- fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
- }
+ if (ret > 0)
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags);
+ else if (ret < 0 || stopped)
+ set_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags);
mutex_unlock(&fs_info->qgroup_rescan_lock);
/*
@@ -3923,9 +3947,9 @@ out:
}
mutex_lock(&fs_info->qgroup_rescan_lock);
- if (!stopped ||
- fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN)
- fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+ if (!stopped || test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN,
+ &fs_info->qgroup_flags))
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
if (trans) {
int ret2 = update_qgroup_status_item(trans);
@@ -3935,7 +3959,7 @@ out:
}
}
fs_info->qgroup_rescan_running = false;
- fs_info->qgroup_flags &= ~BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN;
+ clear_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags);
complete_all(&fs_info->qgroup_rescan_completion);
mutex_unlock(&fs_info->qgroup_rescan_lock);
@@ -3946,7 +3970,7 @@ out:
if (stopped) {
btrfs_info(fs_info, "qgroup scan paused");
- } else if (fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN) {
+ } else if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags)) {
btrfs_info(fs_info, "qgroup scan cancelled");
} else if (ret >= 0) {
btrfs_info(fs_info, "qgroup scan completed%s",
@@ -3973,13 +3997,11 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
if (!init_flags) {
/* we're resuming qgroup rescan at mount time */
- if (!(fs_info->qgroup_flags &
- BTRFS_QGROUP_STATUS_FLAG_RESCAN)) {
+ if (!(test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags))) {
btrfs_debug(fs_info,
"qgroup rescan init failed, qgroup rescan is not queued");
ret = -EINVAL;
- } else if (!(fs_info->qgroup_flags &
- BTRFS_QGROUP_STATUS_FLAG_ON)) {
+ } else if (!(test_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags))) {
btrfs_debug(fs_info,
"qgroup rescan init failed, qgroup is not enabled");
ret = -ENOTCONN;
@@ -3992,10 +4014,12 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
mutex_lock(&fs_info->qgroup_rescan_lock);
if (init_flags) {
- if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN,
+ &fs_info->qgroup_flags) ||
+ test_bit(BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN,
+ &fs_info->qgroup_flags)) {
ret = -EINPROGRESS;
- } else if (!(fs_info->qgroup_flags &
- BTRFS_QGROUP_STATUS_FLAG_ON)) {
+ } else if (!test_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags)) {
btrfs_debug(fs_info,
"qgroup rescan init failed, qgroup is not enabled");
ret = -ENOTCONN;
@@ -4008,13 +4032,13 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
mutex_unlock(&fs_info->qgroup_rescan_lock);
return ret;
}
- fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+ set_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
}
memset(&fs_info->qgroup_rescan_progress, 0,
sizeof(fs_info->qgroup_rescan_progress));
- fs_info->qgroup_flags &= ~(BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN |
- BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING);
+ clear_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags);
+ clear_bit(BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING, &fs_info->qgroup_flags);
fs_info->qgroup_rescan_progress.objectid = progress_objectid;
init_completion(&fs_info->qgroup_rescan_completion);
mutex_unlock(&fs_info->qgroup_rescan_lock);
@@ -4065,7 +4089,7 @@ btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info)
ret = btrfs_commit_current_transaction(fs_info->fs_root);
if (ret) {
- fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
return ret;
}
@@ -4118,7 +4142,7 @@ int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info,
void
btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info)
{
- if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags)) {
mutex_lock(&fs_info->qgroup_rescan_lock);
fs_info->qgroup_rescan_running = true;
btrfs_queue_work(fs_info->qgroup_rescan_workers,
diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h
index 80dd2dacd56d..c64b26b09c22 100644
--- a/fs/btrfs/qgroup.h
+++ b/fs/btrfs/qgroup.h
@@ -121,8 +121,19 @@ struct btrfs_qgroup_swapped_blocks;
* To minimize the chance of collision with new persisted status flags, these
* count backwards from the MSB.
*/
-#define BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN (1ULL << 63)
-#define BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING (1ULL << 62)
+#define BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN (BITS_PER_LONG - 1)
+#define BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING (BITS_PER_LONG - 2)
+
+/*
+ * No new rescan allowed when set.
+ *
+ * During huge subtree dropping, qgroup will be marked inconsistent, and skip
+ * all future accounting to avoid long stall. But, an immediate rescan will
+ * re-enable qgroup and still stall the system.
+ *
+ * This bit is to avoid such rescan during the duration of a subvolume dropping.
+ */
+#define BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN (BITS_PER_LONG - 3)
#define BTRFS_QGROUP_DROP_SUBTREE_THRES_DEFAULT (3)
@@ -365,6 +376,7 @@ int btrfs_qgroup_trace_leaf_items(struct btrfs_trans_handle *trans,
int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
struct extent_buffer *root_eb,
u64 root_gen, int root_level);
+void btrfs_qgroup_check_tree_drop(struct btrfs_fs_info *fs_info, u64 rootid, u8 level);
int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
u64 num_bytes, struct ulist *old_roots,
struct ulist *new_roots);
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 1ee52a9dcee3..a5d0ef09d92a 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1652,12 +1652,7 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
struct bio *bio)
{
struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
- const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
- const u32 nr_steps = rbio->sector_nsteps;
int total_sector_nr = get_bio_sector_nr(rbio, bio);
- u32 offset = 0;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- phys_addr_t paddr;
/* No data csum for the whole stripe, no need to verify. */
if (!rbio->csum_bitmap || !rbio->csum_buf)
@@ -1667,28 +1662,20 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
if (total_sector_nr >= rbio->nr_data * rbio->stripe_nsectors)
return;
- btrfs_bio_for_each_block_all(paddr, bio, step) {
+ for (struct bvec_iter iter = init_bvec_iter_for_bio(bio);
+ iter.bi_size;
+ bio_advance_iter(bio, &iter, fs_info->sectorsize), total_sector_nr++) {
u8 csum_buf[BTRFS_CSUM_SIZE];
u8 *expected_csum;
- paddrs[(offset / step) % nr_steps] = paddr;
- offset += step;
-
- /* Not yet covering the full fs block, continue to the next step. */
- if (!IS_ALIGNED(offset, fs_info->sectorsize))
- continue;
-
/* No csum for this sector, skip to the next sector. */
- if (!test_bit(total_sector_nr, rbio->csum_bitmap)) {
- total_sector_nr++;
+ if (!test_bit(total_sector_nr, rbio->csum_bitmap))
continue;
- }
expected_csum = rbio->csum_buf + total_sector_nr * fs_info->csum_size;
- btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf);
+ btrfs_csum_one_bio_block(fs_info, bio, &iter, csum_buf);
if (unlikely(memcmp(csum_buf, expected_csum, fs_info->csum_size) != 0))
set_bit(total_sector_nr, rbio->error_bitmap);
- total_sector_nr++;
}
}
@@ -1879,6 +1866,27 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc)
start_async_work(rbio, rmw_rbio_work);
}
+static void calculate_block_csum_paddrs(struct btrfs_fs_info *fs_info,
+ const phys_addr_t paddrs[], u8 *dest)
+{
+ const u32 blocksize = fs_info->sectorsize;
+ const u32 step = min(blocksize, PAGE_SIZE);
+ const u32 nr_steps = blocksize / step;
+ struct btrfs_csum_ctx csum;
+
+ btrfs_csum_init(&csum, fs_info->csum_type);
+ for (int i = 0; i < nr_steps; i++) {
+ const phys_addr_t paddr = paddrs[i];
+ void *kaddr;
+
+ ASSERT(offset_in_page(paddr) + step <= PAGE_SIZE);
+ kaddr = kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr);
+ btrfs_csum_update(&csum, kaddr, step);
+ kunmap_local(kaddr);
+ }
+ btrfs_csum_final(&csum, dest);
+}
+
static int verify_one_sector(struct btrfs_raid_bio *rbio,
int stripe_nr, int sector_nr)
{
@@ -1906,7 +1914,7 @@ static int verify_one_sector(struct btrfs_raid_bio *rbio,
csum_expected = rbio->csum_buf +
(stripe_nr * rbio->stripe_nsectors + sector_nr) *
fs_info->csum_size;
- btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf);
+ calculate_block_csum_paddrs(fs_info, paddrs, csum_buf);
if (unlikely(memcmp(csum_buf, csum_expected, fs_info->csum_size) != 0))
return -EIO;
return 0;
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
index 39cb01ee441a..c5bb1c7eac6a 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -83,8 +83,7 @@ struct raid_kobject {
#define BTRFS_FEAT_ATTR(_name, _feature_set, _feature_prefix, _feature_bit) \
static struct btrfs_feature_attr btrfs_attr_features_##_name = { \
.kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO, \
- btrfs_feature_attr_show, \
- btrfs_feature_attr_store), \
+ btrfs_feature_attr_show, NULL), \
.feature_set = _feature_set, \
.feature_bit = _feature_prefix ##_## _feature_bit, \
}
@@ -130,130 +129,20 @@ static u64 get_features(struct btrfs_fs_info *fs_info,
return btrfs_super_incompat_flags(disk_super);
}
-static void set_features(struct btrfs_fs_info *fs_info,
- enum btrfs_feature_set set, u64 features)
-{
- struct btrfs_super_block *disk_super = fs_info->super_copy;
- if (set == FEAT_COMPAT)
- btrfs_set_super_compat_flags(disk_super, features);
- else if (set == FEAT_COMPAT_RO)
- btrfs_set_super_compat_ro_flags(disk_super, features);
- else
- btrfs_set_super_incompat_flags(disk_super, features);
-}
-
-static int can_modify_feature(struct btrfs_feature_attr *fa)
-{
- int val = 0;
- u64 set, clear;
- switch (fa->feature_set) {
- case FEAT_COMPAT:
- set = BTRFS_FEATURE_COMPAT_SAFE_SET;
- clear = BTRFS_FEATURE_COMPAT_SAFE_CLEAR;
- break;
- case FEAT_COMPAT_RO:
- set = BTRFS_FEATURE_COMPAT_RO_SAFE_SET;
- clear = BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR;
- break;
- case FEAT_INCOMPAT:
- set = BTRFS_FEATURE_INCOMPAT_SAFE_SET;
- clear = BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR;
- break;
- default:
- btrfs_warn(NULL, "sysfs: unknown feature set %d", fa->feature_set);
- return 0;
- }
-
- if (set & fa->feature_bit)
- val |= 1;
- if (clear & fa->feature_bit)
- val |= 2;
-
- return val;
-}
-
static ssize_t btrfs_feature_attr_show(struct kobject *kobj,
struct kobj_attribute *a, char *buf)
{
int val = 0;
struct btrfs_fs_info *fs_info = to_fs_info(kobj);
struct btrfs_feature_attr *fa = to_btrfs_feature_attr(a);
+
if (fs_info) {
u64 features = get_features(fs_info, fa->feature_set);
if (features & fa->feature_bit)
val = 1;
- } else
- val = can_modify_feature(fa);
-
- return sysfs_emit(buf, "%d\n", val);
-}
-
-static ssize_t btrfs_feature_attr_store(struct kobject *kobj,
- struct kobj_attribute *a,
- const char *buf, size_t count)
-{
- struct btrfs_fs_info *fs_info;
- struct btrfs_feature_attr *fa = to_btrfs_feature_attr(a);
- u64 features, set, clear;
- unsigned long val;
- int ret;
-
- fs_info = to_fs_info(kobj);
- if (!fs_info)
- return -EPERM;
-
- if (sb_rdonly(fs_info->sb))
- return -EROFS;
-
- ret = kstrtoul(skip_spaces(buf), 0, &val);
- if (ret)
- return ret;
-
- if (fa->feature_set == FEAT_COMPAT) {
- set = BTRFS_FEATURE_COMPAT_SAFE_SET;
- clear = BTRFS_FEATURE_COMPAT_SAFE_CLEAR;
- } else if (fa->feature_set == FEAT_COMPAT_RO) {
- set = BTRFS_FEATURE_COMPAT_RO_SAFE_SET;
- clear = BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR;
- } else {
- set = BTRFS_FEATURE_INCOMPAT_SAFE_SET;
- clear = BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR;
- }
-
- features = get_features(fs_info, fa->feature_set);
-
- /* Nothing to do */
- if ((val && (features & fa->feature_bit)) ||
- (!val && !(features & fa->feature_bit)))
- return count;
-
- if ((val && !(set & fa->feature_bit)) ||
- (!val && !(clear & fa->feature_bit))) {
- btrfs_info(fs_info,
- "%sabling feature %s on mounted fs is not supported.",
- val ? "En" : "Dis", fa->kobj_attr.attr.name);
- return -EPERM;
}
- btrfs_info(fs_info, "%s %s feature flag",
- val ? "Setting" : "Clearing", fa->kobj_attr.attr.name);
-
- spin_lock(&fs_info->super_lock);
- features = get_features(fs_info, fa->feature_set);
- if (val)
- features |= fa->feature_bit;
- else
- features &= ~fa->feature_bit;
- set_features(fs_info, fa->feature_set, features);
- spin_unlock(&fs_info->super_lock);
-
- /*
- * We don't want to do full transaction commit from inside sysfs
- */
- set_bit(BTRFS_FS_NEED_TRANS_COMMIT, &fs_info->flags);
- wake_up_process(fs_info->transaction_kthread);
-
- return count;
+ return sysfs_emit(buf, "%d\n", val);
}
static umode_t btrfs_feature_visible(struct kobject *kobj,
@@ -269,9 +158,7 @@ static umode_t btrfs_feature_visible(struct kobject *kobj,
fa = attr_to_btrfs_feature_attr(attr);
features = get_features(fs_info, fa->feature_set);
- if (can_modify_feature(fa))
- mode |= S_IWUSR;
- else if (!(features & fa->feature_bit))
+ if (!(features & fa->feature_bit))
mode = 0;
}
@@ -2359,9 +2246,7 @@ static ssize_t qgroup_enabled_show(struct kobject *qgroups_kobj,
struct btrfs_fs_info *fs_info = to_fs_info(qgroups_kobj->parent);
bool enabled;
- spin_lock(&fs_info->qgroup_lock);
- enabled = fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON;
- spin_unlock(&fs_info->qgroup_lock);
+ enabled = test_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags);
return sysfs_emit(buf, "%d\n", enabled);
}
@@ -2401,9 +2286,7 @@ static ssize_t qgroup_inconsistent_show(struct kobject *qgroups_kobj,
struct btrfs_fs_info *fs_info = to_fs_info(qgroups_kobj->parent);
bool inconsistent;
- spin_lock(&fs_info->qgroup_lock);
- inconsistent = (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT);
- spin_unlock(&fs_info->qgroup_lock);
+ inconsistent = test_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags);
return sysfs_emit(buf, "%d\n", inconsistent);
}
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 0ce91396b517..622334ffd240 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -163,6 +163,12 @@ static void dir_item_err(const struct extent_buffer *eb, int slot,
va_end(args);
}
+/* Record info for the last hit inode. */
+struct saved_inode_info {
+ u64 ino;
+ u32 mode;
+};
+
/*
* This functions checks prev_key->objectid, to ensure current key and prev_key
* share the same objectid as inode number.
@@ -204,15 +210,41 @@ static bool check_prev_ino(struct extent_buffer *leaf,
prev_key->objectid, key->objectid);
return false;
}
+
+static bool can_have_extent_data(struct extent_buffer *leaf,
+ struct btrfs_key *key, int slot, u8 fi_type,
+ const struct saved_inode_info *inode_info)
+{
+ /* No inode item in this leaf. */
+ if (inode_info->ino != key->objectid)
+ return true;
+ if (S_ISREG(inode_info->mode))
+ return true;
+ if (S_ISLNK(inode_info->mode)) {
+ /* For a symlink, the file extent item should always be inlined. */
+ if (unlikely(fi_type != BTRFS_FILE_EXTENT_INLINE))
+ return false;
+ return true;
+ }
+
+ /*
+ * The rest are special files, e.g. block/FIFO files, which cannnot
+ * have any file extent.
+ */
+ return false;
+}
+
static int check_extent_data_item(struct extent_buffer *leaf,
struct btrfs_key *key, int slot,
- struct btrfs_key *prev_key)
+ struct btrfs_key *prev_key,
+ const struct saved_inode_info *inode_info)
{
struct btrfs_fs_info *fs_info = leaf->fs_info;
struct btrfs_file_extent_item *fi;
u32 sectorsize = fs_info->sectorsize;
u32 item_size = btrfs_item_size(leaf, slot);
u64 extent_end;
+ u8 fi_type;
if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) {
file_extent_err(leaf, slot,
@@ -243,12 +275,18 @@ static int check_extent_data_item(struct extent_buffer *leaf,
SZ_4K);
return -EUCLEAN;
}
- if (unlikely(btrfs_file_extent_type(leaf, fi) >=
- BTRFS_NR_FILE_EXTENT_TYPES)) {
+ fi_type = btrfs_file_extent_type(leaf, fi);
+ if (unlikely(fi_type >= BTRFS_NR_FILE_EXTENT_TYPES)) {
file_extent_err(leaf, slot,
"invalid type for file extent, have %u expect range [0, %u]",
- btrfs_file_extent_type(leaf, fi),
- BTRFS_NR_FILE_EXTENT_TYPES - 1);
+ fi_type, BTRFS_NR_FILE_EXTENT_TYPES - 1);
+ return -EUCLEAN;
+ }
+
+ if (unlikely(!can_have_extent_data(leaf, key, slot, fi_type, inode_info))) {
+ file_extent_err(leaf, slot,
+ "unexpected file extent item type %u for inode mode 0%o",
+ fi_type, inode_info->mode);
return -EUCLEAN;
}
@@ -270,7 +308,8 @@ static int check_extent_data_item(struct extent_buffer *leaf,
btrfs_file_extent_encryption(leaf, fi));
return -EUCLEAN;
}
- if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
+
+ if (fi_type == BTRFS_FILE_EXTENT_INLINE) {
/* Inline extent must have 0 as key offset */
if (unlikely(key->offset)) {
file_extent_err(leaf, slot,
@@ -1206,7 +1245,8 @@ static int check_dev_item(struct extent_buffer *leaf,
}
static int check_inode_item(struct extent_buffer *leaf,
- struct btrfs_key *key, int slot)
+ struct btrfs_key *key, int slot,
+ struct saved_inode_info *inode_info)
{
struct btrfs_fs_info *fs_info = leaf->fs_info;
struct btrfs_inode_item *iitem;
@@ -1291,6 +1331,8 @@ static int check_inode_item(struct extent_buffer *leaf,
ro_flags);
return -EUCLEAN;
}
+ inode_info->ino = key->objectid;
+ inode_info->mode = mode;
return 0;
}
@@ -2319,14 +2361,15 @@ static int check_free_space_bitmap(struct extent_buffer *leaf,
static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf,
struct btrfs_key *key,
int slot,
- struct btrfs_key *prev_key)
+ struct btrfs_key *prev_key,
+ struct saved_inode_info *inode_info)
{
int ret = 0;
struct btrfs_chunk *chunk;
switch (key->type) {
case BTRFS_EXTENT_DATA_KEY:
- ret = check_extent_data_item(leaf, key, slot, prev_key);
+ ret = check_extent_data_item(leaf, key, slot, prev_key, inode_info);
break;
case BTRFS_EXTENT_CSUM_KEY:
ret = check_csum_item(leaf, key, slot, prev_key);
@@ -2356,7 +2399,7 @@ static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf,
ret = check_dev_extent_item(leaf, key, slot, prev_key);
break;
case BTRFS_INODE_ITEM_KEY:
- ret = check_inode_item(leaf, key, slot);
+ ret = check_inode_item(leaf, key, slot, inode_info);
break;
case BTRFS_ROOT_ITEM_KEY:
ret = check_root_item(leaf, key, slot);
@@ -2404,6 +2447,7 @@ static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf,
enum btrfs_tree_block_status __btrfs_check_leaf(struct extent_buffer *leaf)
{
struct btrfs_fs_info *fs_info = leaf->fs_info;
+ struct saved_inode_info inode_info = { 0 };
/* No valid key type is 0, so all key should be larger than this key */
struct btrfs_key prev_key = {0, 0, 0};
struct btrfs_key key;
@@ -2539,7 +2583,7 @@ enum btrfs_tree_block_status __btrfs_check_leaf(struct extent_buffer *leaf)
}
/* Check if the item size and content meet other criteria. */
- ret = check_leaf_item(leaf, &key, slot, &prev_key);
+ ret = check_leaf_item(leaf, &key, slot, &prev_key, &inode_info);
if (unlikely(ret != BTRFS_TREE_BLOCK_CLEAN))
return ret;
diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index 4e0ab5842274..d432fec21c15 100644
--- a/fs/btrfs/verity.c
+++ b/fs/btrfs/verity.c
@@ -272,21 +272,17 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
* @dest: Buffer to read into. This parameter has slightly tricky
* semantics. If it is NULL, the function will not do any copying
* and will just return the size of all the items up to len bytes.
- * If dest_page is passed, then the function will kmap_local the
- * page and ignore dest, but it must still be non-NULL to avoid the
- * counting-only behavior.
* @len: length in bytes to read
- * @dest_folio: copy into this folio instead of the dest buffer
*
* Helper function to read items from the btree. This returns the number of
* bytes read or < 0 for errors. We can return short reads if the items don't
* exist on disk or aren't big enough to fill the desired length. Supports
- * reading into a provided buffer (dest) or into the page cache
+ * reading into a provided buffer (dest).
*
* Returns number of bytes read or a negative error code on failure.
*/
static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
- char *dest, u64 len, struct folio *dest_folio)
+ char *dest, u64 len)
{
BTRFS_PATH_AUTO_FREE(path);
struct btrfs_root *root = inode->root;
@@ -306,7 +302,11 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
if (!path)
return -ENOMEM;
- if (dest_folio)
+ /*
+ * Merkle items can be large and split across multiple items, so enable
+ * readahead for such cases.
+ */
+ if (key_type == BTRFS_VERITY_MERKLE_ITEM_KEY)
path->reada = READA_FORWARD;
key.objectid = btrfs_ino(inode);
@@ -350,7 +350,7 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
break;
}
- /* desc = NULL to just sum all the item lengths */
+ /* dest == NULL to just sum all the item lengths */
if (!dest)
copy_end = item_end;
else
@@ -363,16 +363,10 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
copy_offset = offset - key.offset;
if (dest) {
- if (dest_folio)
- kaddr = kmap_local_folio(dest_folio, 0);
-
data = btrfs_item_ptr(leaf, path->slots[0], void);
read_extent_buffer(leaf, kaddr + dest_offset,
(unsigned long)data + copy_offset,
copy_bytes);
-
- if (dest_folio)
- kunmap_local(kaddr);
}
offset += copy_bytes;
@@ -663,7 +657,7 @@ int btrfs_get_verity_descriptor(struct inode *inode, void *buf, size_t buf_size)
memset(&item, 0, sizeof(item));
ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_DESC_ITEM_KEY, 0,
- (char *)&item, sizeof(item), NULL);
+ (char *)&item, sizeof(item));
if (ret < 0)
return ret;
@@ -680,7 +674,7 @@ int btrfs_get_verity_descriptor(struct inode *inode, void *buf, size_t buf_size)
return -ERANGE;
ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_DESC_ITEM_KEY, 1,
- buf, buf_size, NULL);
+ buf, buf_size);
if (ret < 0)
return ret;
if (ret != true_size)
@@ -706,6 +700,7 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode,
struct folio *folio;
u64 off = (u64)index << PAGE_SHIFT;
loff_t merkle_pos = merkle_file_pos(inode);
+ void *kaddr;
int ret;
if (merkle_pos < 0)
@@ -749,6 +744,7 @@ again:
}
read_folio:
+ kaddr = kmap_local_folio(folio, 0);
/*
* Merkle item keys are indexed from byte 0 in the merkle tree.
* They have the form:
@@ -756,7 +752,8 @@ read_folio:
* [ inode objectid, BTRFS_MERKLE_ITEM_KEY, offset in bytes ]
*/
ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, off,
- folio_address(folio), PAGE_SIZE, folio);
+ kaddr, PAGE_SIZE);
+ kunmap_local(kaddr);
if (ret < 0) {
folio_unlock(folio);
folio_put(folio);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 74584669507f..949e40baff33 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -403,7 +403,7 @@ static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid)
return fs_devs;
}
-static void btrfs_free_device(struct btrfs_device *device)
+void btrfs_free_device(struct btrfs_device *device)
{
WARN_ON(!list_empty(&device->post_commit_list));
/*
@@ -2783,6 +2783,41 @@ static void btrfs_setup_sprout(struct btrfs_fs_info *fs_info,
btrfs_set_super_flags(disk_super, super_flags);
}
+static void btrfs_rollback_sprout(struct btrfs_fs_info *fs_info,
+ struct btrfs_fs_devices *seed_devices)
+{
+ struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+ struct btrfs_super_block *disk_super = fs_info->super_copy;
+ struct btrfs_device *device;
+ u64 super_flags;
+
+ lockdep_assert_held(&uuid_mutex);
+ lockdep_assert_held(&fs_devices->device_list_mutex);
+
+ list_del_init(&seed_devices->seed_list);
+ list_splice_init_rcu(&seed_devices->devices, &fs_devices->devices, synchronize_rcu);
+ list_for_each_entry(device, &fs_devices->devices, dev_list) {
+ device->fs_devices = fs_devices;
+ }
+
+ fs_devices->seeding = true;
+ fs_devices->num_devices = seed_devices->num_devices;
+ fs_devices->open_devices = seed_devices->open_devices;
+ fs_devices->missing_devices = seed_devices->missing_devices;
+ fs_devices->rotating = seed_devices->rotating;
+ fs_devices->latest_dev = seed_devices->latest_dev;
+
+ memcpy(fs_devices->fsid, seed_devices->fsid, BTRFS_FSID_SIZE);
+ memcpy(fs_devices->metadata_uuid, seed_devices->metadata_uuid, BTRFS_FSID_SIZE);
+ memcpy(disk_super->fsid, seed_devices->fsid, BTRFS_FSID_SIZE);
+
+ super_flags = (btrfs_super_flags(disk_super) | BTRFS_SUPER_FLAG_SEEDING);
+ btrfs_set_super_flags(disk_super, super_flags);
+
+ seed_devices->opened = 0;
+ free_fs_devices(seed_devices);
+}
+
/*
* Store the expected generation for seed devices in device items.
*/
@@ -3134,6 +3169,8 @@ error_sysfs:
orig_super_total_bytes);
btrfs_set_super_num_devices(fs_info->super_copy,
orig_super_num_devices);
+ if (seeding_dev)
+ btrfs_rollback_sprout(fs_info, seed_devices);
btrfs_update_per_profile_avail(fs_info);
mutex_unlock(&fs_info->chunk_mutex);
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 0415d74cad9b..337d7007d9e2 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -799,6 +799,7 @@ void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_device *srcdev);
void btrfs_rm_dev_replace_free_srcdev(struct btrfs_device *srcdev);
void btrfs_destroy_dev_replace_tgtdev(struct btrfs_device *tgtdev,
bool allow_freeze);
+void btrfs_free_device(struct btrfs_device *device);
unsigned long btrfs_full_stripe_len(struct btrfs_fs_info *fs_info,
u64 logical);
u64 btrfs_calc_stripe_length(const struct btrfs_chunk_map *map);
diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index 9cc2c9c1a606..08a15465a087 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -2688,6 +2688,11 @@ bool btrfs_can_activate_zone(struct btrfs_fs_devices *fs_devices, u64 flags)
switch (flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
case 0: /* single */
+ case BTRFS_BLOCK_GROUP_RAID0:
+ case BTRFS_BLOCK_GROUP_RAID1:
+ case BTRFS_BLOCK_GROUP_RAID1C3:
+ case BTRFS_BLOCK_GROUP_RAID1C4:
+ case BTRFS_BLOCK_GROUP_RAID10:
ret = (atomic_read(&zinfo->active_zones_left) >= (1 + reserved));
break;
case BTRFS_BLOCK_GROUP_DUP:
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index cc3b9f7dccaf..b6ccaf848e4b 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -1255,13 +1255,16 @@ static inline __u16 btrfs_qgroup_level(__u64 qgroupid)
}
/*
- * is subvolume quota turned on?
- */
-#define BTRFS_QGROUP_STATUS_FLAG_ON (1ULL << 0)
-/*
- * RESCAN is set during the initialization phase
+ * The following BTRFS_QGROUP_STATUS_BIT_* are for * btrfs_qgroup_status_item::flags.
+ *
+ * Is subvolume quota turned on?
*/
-#define BTRFS_QGROUP_STATUS_FLAG_RESCAN (1ULL << 1)
+#define BTRFS_QGROUP_STATUS_BIT_ON (0)
+#define BTRFS_QGROUP_STATUS_FLAG_ON (1UL << BTRFS_QGROUP_STATUS_BIT_ON)
+
+/* RESCAN is set during the initialization phase */
+#define BTRFS_QGROUP_STATUS_BIT_RESCAN (1)
+#define BTRFS_QGROUP_STATUS_FLAG_RESCAN (1UL << BTRFS_QGROUP_STATUS_BIT_RESCAN)
/*
* Some qgroup entries are known to be out of date,
* either because the configuration has changed in a way that
@@ -1269,14 +1272,16 @@ static inline __u16 btrfs_qgroup_level(__u64 qgroupid)
* with a non-qgroup-aware version.
* Turning qouta off and on again makes it inconsistent, too.
*/
-#define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1ULL << 2)
+#define BTRFS_QGROUP_STATUS_BIT_INCONSISTENT (2)
+#define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1UL << BTRFS_QGROUP_STATUS_BIT_INCONSISTENT)
/*
* Whether or not this filesystem is using simple quotas. Not exactly the
* incompat bit, because we support using simple quotas, disabling it, then
* going back to full qgroup quotas.
*/
-#define BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE (1ULL << 3)
+#define BTRFS_QGROUP_STATUS_BIT_SIMPLE_MODE (3)
+#define BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE (1UL << BTRFS_QGROUP_STATUS_BIT_SIMPLE_MODE)
#define BTRFS_QGROUP_STATUS_FLAGS_MASK (BTRFS_QGROUP_STATUS_FLAG_ON | \
BTRFS_QGROUP_STATUS_FLAG_RESCAN | \