// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007 Oracle. All rights reserved.
* Copyright (C) 2022 Christoph Hellwig.
*/
#include <linux/blk_types.h>
#include <linux/bio.h>
#include "bio.h"
#include "ctree.h"
#include "volumes.h"
#include "raid56.h"
#include "async-thread.h"
#include "dev-replace.h"
#include "zoned.h"
#include "file-item.h"
#include "raid-stripe-tree.h"
static struct bio_set btrfs_bioset;
static struct bio_set btrfs_clone_bioset;
static struct bio_set btrfs_repair_bioset;
static mempool_t btrfs_failed_bio_pool;
struct btrfs_failed_bio {
struct btrfs_bio *bbio;
int num_copies;
atomic_t repair_count;
};
/* Is this a data path I/O that needs storage layer checksum and repair? */
static inline bool is_data_bbio(const struct btrfs_bio *bbio)
{
return bbio->inode && is_data_inode(bbio->inode);
}
static bool bbio_has_ordered_extent(const struct btrfs_bio *bbio)
{
return is_data_bbio(bbio) && btrfs_op(&bbio->bio) == BTRFS_MAP_WRITE;
}
/*
* Initialize a btrfs_bio structure. This skips the embedded bio itself as it
* is already initialized by the block layer.
*/
void btrfs_bio_init(struct btrfs_bio *bbio, struct btrfs_inode *inode, u64 file_offset,
btrfs_bio_end_io_t end_io, void *private)
{
/* @inode parameter is mandatory. */
ASSERT(inode);
memset(bbio, 0, offsetof(struct btrfs_bio, bio));
bbio->inode = inode;
bbio->end_io = end_io;
bbio->private = private;
bbio->file_offset = file_offset;
atomic_set(&bbio->pending_ios, 1);
WRITE_ONCE(bbio->status, BLK_STS_OK);
}
/*
* Allocate a btrfs_bio structure. The btrfs_bio is the main I/O container for
* btrfs, and is used for all I/O submitted through btrfs_submit_bbio().
*
* Just like the underlying bio_alloc_bioset it will not fail as it is backed by
* a mempool.
*/
struct btrfs_bio *btrfs_bio_alloc(unsigned int nr_vecs, blk_opf_t opf,
struct btrfs_inode *inode, u64 file_offset,
btrfs_bio_end_io_t end_io, void *private)
{
struct btrfs_bio *bbio;
struct bio *bio;
bio = bio_alloc_bioset(NULL, nr_vecs, opf, GFP_NOFS, &btrfs_bioset);
bbio = btrfs_bio(bio);
btrfs_bio_init(bbio, inode, file_offset, end_io, private);
return bbio;
}
static struct btrfs_bio *btrfs_split_bio(struct btrfs_fs_info *fs_info,
struct btrfs_bio *orig_bbio,
u64 map_length)
{
struct btrfs_bio *bbio;
struct bio *bio;
bio = bio_split(&orig_bbio->bio, map_length >> SECTOR_SHIFT, GFP_NOFS,
&btrfs_clone_bioset);
if (IS_ERR(bio))
return ERR_CAST(bio);
bbio = btrfs_bio(bio);
btrfs_bio_init(bbio, orig_bbio->inode, orig_bbio->file_offset, NULL, orig_bbio);
orig_bbio->file_offset += map_length;
if (bbio_has_ordered_extent(bbio)) {
refcount_inc(&orig_bbio->ordered->refs);
bbio->ordered = orig_bbio->ordered;
bbio->orig_logical = orig_bbio->orig_logical;
orig_bbio->orig_logical += map_length;
}
bbio->csum_search_commit_root = orig_bbio->csum_search_commit_root;
bbio->can_use_append = orig_bbio->can_use_append;
bbio->is_scrub = orig_bbio->is_scrub;
bbio->is_remap = orig_bbio->is_remap;
bbio->async_csum = orig_bbio->async_csum;
atomic_inc(&orig_bbio->pending_ios);
return bbio;
}
void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status)
{
/* Make sure we're already in task context. */
ASSERT(in_task());
if (bbio->async_csum)
wait_for_completion(&bbio->csum_done);
bbio->bio.bi_status = status;