Commit 947a6299 authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba

btrfs: move tree block parentness check into validate_extent_buffer()

[BACKGROUND]
Although both btrfs metadata and data has their read time verification
done at endio time (btrfs_validate_metadata_buffer() and
btrfs_verify_data_csum()), metadata has extra verification, mostly
parentness check including first key/transid/owner_root/level, done at
read_tree_block() and btrfs_read_extent_buffer().

On the other hand, all the data verification is done at endio context.

[ENHANCEMENT]
This patch will make a new union in btrfs_bio, taking the space of the
old data checksums, thus it will not increase the memory usage.

With that extra btrfs_tree_parent_check inside btrfs_bio, we can just
pass the check parameter into read_extent_buffer_pages(), and before
submitting the bio, we can copy the check structure into btrfs_bio.

And finally at endio time, we can grab btrfs_bio::parent_check and pass
it to validate_extent_buffer(), to move the remaining checks into it.

This brings the following benefits:

- Much simpler btrfs_read_extent_buffer()
  Now it only needs to iterate through all mirrors.

- Simpler read-time transid check
  Previously we go verify_parent_transid() after reading out the extent
  buffer.
  Now the transid check is done inside the endio function, no other
  code can modify the content.
  Thus no need to use the extent lock anymore.
Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 789d6a3a
......@@ -266,7 +266,6 @@ int btrfs_read_extent_buffer(struct extent_buffer *eb,
struct btrfs_tree_parent_check *check)
{
struct btrfs_fs_info *fs_info = eb->fs_info;
struct extent_io_tree *io_tree;
int failed = 0;
int ret;
int num_copies = 0;
......@@ -275,21 +274,11 @@ int btrfs_read_extent_buffer(struct extent_buffer *eb,
ASSERT(check);
io_tree = &BTRFS_I(fs_info->btree_inode)->io_tree;
while (1) {
clear_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
ret = read_extent_buffer_pages(eb, WAIT_COMPLETE, mirror_num);
if (!ret) {
if (verify_parent_transid(io_tree, eb, check->transid, 0))
ret = -EIO;
else if (btrfs_verify_level_key(eb, check->level,
check->has_first_key ?
&check->first_key : NULL,
check->transid))
ret = -EUCLEAN;
else
break;
}
ret = read_extent_buffer_pages(eb, WAIT_COMPLETE, mirror_num, check);
if (!ret)
break;
num_copies = btrfs_num_copies(fs_info,
eb->start, eb->len);
......@@ -465,7 +454,8 @@ static int check_tree_block_fsid(struct extent_buffer *eb)
}
/* Do basic extent buffer checks at read time */
static int validate_extent_buffer(struct extent_buffer *eb)
static int validate_extent_buffer(struct extent_buffer *eb,
struct btrfs_tree_parent_check *check)
{
struct btrfs_fs_info *fs_info = eb->fs_info;
u64 found_start;
......@@ -475,6 +465,8 @@ static int validate_extent_buffer(struct extent_buffer *eb)
const u8 *header_csum;
int ret = 0;
ASSERT(check);
found_start = btrfs_header_bytenr(eb);
if (found_start != eb->start) {
btrfs_err_rl(fs_info,
......@@ -513,6 +505,45 @@ static int validate_extent_buffer(struct extent_buffer *eb)
goto out;
}
if (found_level != check->level) {
ret = -EIO;
goto out;
}
if (unlikely(check->transid &&
btrfs_header_generation(eb) != check->transid)) {
btrfs_err_rl(eb->fs_info,
"parent transid verify failed on logical %llu mirror %u wanted %llu found %llu",
eb->start, eb->read_mirror, check->transid,
btrfs_header_generation(eb));
ret = -EIO;
goto out;
}
if (check->has_first_key) {
struct btrfs_key *expect_key = &check->first_key;
struct btrfs_key found_key;
if (found_level)
btrfs_node_key_to_cpu(eb, &found_key, 0);
else
btrfs_item_key_to_cpu(eb, &found_key, 0);
if (unlikely(btrfs_comp_cpu_keys(expect_key, &found_key))) {
btrfs_err(fs_info,
"tree first key mismatch detected, bytenr=%llu parent_transid=%llu key expected=(%llu,%u,%llu) has=(%llu,%u,%llu)",
eb->start, check->transid,
expect_key->objectid,
expect_key->type, expect_key->offset,
found_key.objectid, found_key.type,
found_key.offset);
ret = -EUCLEAN;
goto out;
}
}
if (check->owner_root) {
ret = btrfs_check_eb_owner(eb, check->owner_root);
if (ret < 0)
goto out;
}
/*
* If this is a leaf block and it is corrupt, set the corrupt bit so
* that we don't try and read the other copies of this block, just
......@@ -537,13 +568,15 @@ static int validate_extent_buffer(struct extent_buffer *eb)
}
static int validate_subpage_buffer(struct page *page, u64 start, u64 end,
int mirror)
int mirror, struct btrfs_tree_parent_check *check)
{
struct btrfs_fs_info *fs_info = btrfs_sb(page->mapping->host->i_sb);
struct extent_buffer *eb;
bool reads_done;
int ret = 0;
ASSERT(check);
/*
* We don't allow bio merge for subpage metadata read, so we should
* only get one eb for each endio hook.
......@@ -567,7 +600,7 @@ static int validate_subpage_buffer(struct page *page, u64 start, u64 end,
ret = -EIO;
goto err;
}
ret = validate_extent_buffer(eb);
ret = validate_extent_buffer(eb, check);
if (ret < 0)
goto err;
......@@ -597,7 +630,8 @@ int btrfs_validate_metadata_buffer(struct btrfs_bio *bbio,
ASSERT(page->private);
if (btrfs_sb(page->mapping->host->i_sb)->nodesize < PAGE_SIZE)
return validate_subpage_buffer(page, start, end, mirror);
return validate_subpage_buffer(page, start, end, mirror,
&bbio->parent_check);
eb = (struct extent_buffer *)page->private;
......@@ -616,7 +650,7 @@ int btrfs_validate_metadata_buffer(struct btrfs_bio *bbio,
ret = -EIO;
goto err;
}
ret = validate_extent_buffer(eb);
ret = validate_extent_buffer(eb, &bbio->parent_check);
err:
if (ret) {
/*
......@@ -774,6 +808,7 @@ void btrfs_submit_metadata_bio(struct btrfs_inode *inode, struct bio *bio, int m
blk_status_t ret;
bio->bi_opf |= REQ_META;
bbio->is_metadata = 1;
if (btrfs_op(bio) != BTRFS_MAP_WRITE) {
btrfs_submit_bio(fs_info, bio, mirror_num);
......
......@@ -4934,7 +4934,8 @@ void set_extent_buffer_uptodate(struct extent_buffer *eb)
}
static int read_extent_buffer_subpage(struct extent_buffer *eb, int wait,
int mirror_num)
int mirror_num,
struct btrfs_tree_parent_check *check)
{
struct btrfs_fs_info *fs_info = eb->fs_info;
struct extent_io_tree *io_tree;
......@@ -4947,6 +4948,7 @@ static int read_extent_buffer_subpage(struct extent_buffer *eb, int wait,
ASSERT(!test_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags));
ASSERT(PagePrivate(page));
ASSERT(check);
io_tree = &BTRFS_I(fs_info->btree_inode)->io_tree;
if (wait == WAIT_NONE) {
......@@ -4990,6 +4992,7 @@ static int read_extent_buffer_subpage(struct extent_buffer *eb, int wait,
*/
atomic_dec(&eb->io_pages);
}
memcpy(&btrfs_bio(bio_ctrl.bio)->parent_check, check, sizeof(*check));
submit_one_bio(&bio_ctrl);
if (ret || wait != WAIT_COMPLETE) {
free_extent_state(cached_state);
......@@ -5003,7 +5006,8 @@ static int read_extent_buffer_subpage(struct extent_buffer *eb, int wait,
return ret;
}
int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num)
int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num,
struct btrfs_tree_parent_check *check)
{
int i;
struct page *page;
......@@ -5029,7 +5033,7 @@ int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num)
return -EIO;
if (eb->fs_info->nodesize < PAGE_SIZE)
return read_extent_buffer_subpage(eb, wait, mirror_num);
return read_extent_buffer_subpage(eb, wait, mirror_num, check);
num_pages = num_extent_pages(eb);
for (i = 0; i < num_pages; i++) {
......@@ -5106,6 +5110,7 @@ int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num)
}
}
memcpy(&btrfs_bio(bio_ctrl.bio)->parent_check, check, sizeof(*check));
submit_one_bio(&bio_ctrl);
if (ret || wait != WAIT_COMPLETE)
......@@ -5841,6 +5846,11 @@ int try_release_extent_buffer(struct page *page)
void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info,
u64 bytenr, u64 owner_root, u64 gen, int level)
{
struct btrfs_tree_parent_check check = {
.has_first_key = 0,
.level = level,
.transid = gen
};
struct extent_buffer *eb;
int ret;
......@@ -5853,7 +5863,7 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info,
return;
}
ret = read_extent_buffer_pages(eb, WAIT_NONE, 0);
ret = read_extent_buffer_pages(eb, WAIT_NONE, 0, &check);
if (ret < 0)
free_extent_buffer_stale(eb);
else
......
......@@ -66,6 +66,7 @@ struct btrfs_inode;
struct btrfs_fs_info;
struct io_failure_record;
struct extent_io_tree;
struct btrfs_tree_parent_check;
int __init extent_buffer_init_cachep(void);
void __cold extent_buffer_free_cachep(void);
......@@ -170,8 +171,8 @@ void free_extent_buffer_stale(struct extent_buffer *eb);
#define WAIT_NONE 0
#define WAIT_COMPLETE 1
#define WAIT_PAGE_LOCK 2
int read_extent_buffer_pages(struct extent_buffer *eb, int wait,
int mirror_num);
int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num,
struct btrfs_tree_parent_check *parent_check);
void wait_on_extent_buffer_writeback(struct extent_buffer *eb);
void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info,
u64 bytenr, u64 owner_root, u64 gen, int level);
......
......@@ -11,6 +11,7 @@
#include <linux/btrfs.h>
#include "async-thread.h"
#include "messages.h"
#include "disk-io.h"
#define BTRFS_MAX_DATA_CHUNK_SIZE (10ULL * SZ_1G)
......@@ -397,7 +398,15 @@ typedef void (*btrfs_bio_end_io_t)(struct btrfs_bio *bbio);
* Mostly for btrfs specific features like csum and mirror_num.
*/
struct btrfs_bio {
unsigned int mirror_num;
unsigned int mirror_num:7;
/*
* Extra indicator for metadata bios.
* For some btrfs bios they use pages without a mapping, thus
* we can not rely on page->mapping->host to determine if
* it's a metadata bio.
*/
unsigned int is_metadata:1;
struct bvec_iter iter;
/* for direct I/O */
......@@ -405,8 +414,16 @@ struct btrfs_bio {
/* @device is for stripe IO submission. */
struct btrfs_device *device;
u8 *csum;
u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE];
union {
/* For data checksum verification. */
struct {
u8 *csum;
u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE];
};
/* For metadata parentness verification. */
struct btrfs_tree_parent_check parent_check;
};
/* End I/O information supplied to btrfs_bio_alloc */
btrfs_bio_end_io_t end_io;
......@@ -443,6 +460,8 @@ static inline void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status)
static inline void btrfs_bio_free_csum(struct btrfs_bio *bbio)
{
if (bbio->is_metadata)
return;
if (bbio->csum != bbio->csum_inline) {
kfree(bbio->csum);
bbio->csum = NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment