Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2dabb324 authored by Chandan Rajendra's avatar Chandan Rajendra Committed by David Sterba
Browse files

Btrfs: Direct I/O read: Work on sectorsized blocks



The direct I/O read's endio and corresponding repair functions work on
page sized blocks. This commit adds the ability for direct I/O read to work on
subpagesized blocks.

Signed-off-by: default avatarChandan Rajendra <chandan@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent c40a3d38
Loading
Loading
Loading
Loading
+75 −23
Original line number Diff line number Diff line
@@ -7751,9 +7751,9 @@ static int btrfs_check_dio_repairable(struct inode *inode,
}

static int dio_read_error(struct inode *inode, struct bio *failed_bio,
			  struct page *page, u64 start, u64 end,
			  int failed_mirror, bio_end_io_t *repair_endio,
			  void *repair_arg)
			struct page *page, unsigned int pgoff,
			u64 start, u64 end, int failed_mirror,
			bio_end_io_t *repair_endio, void *repair_arg)
{
	struct io_failure_record *failrec;
	struct bio *bio;
@@ -7774,7 +7774,9 @@ static int dio_read_error(struct inode *inode, struct bio *failed_bio,
		return -EIO;
	}

	if (failed_bio->bi_vcnt > 1)
	if ((failed_bio->bi_vcnt > 1)
		|| (failed_bio->bi_io_vec->bv_len
			> BTRFS_I(inode)->root->sectorsize))
		read_mode = READ_SYNC | REQ_FAILFAST_DEV;
	else
		read_mode = READ_SYNC;
@@ -7782,7 +7784,7 @@ static int dio_read_error(struct inode *inode, struct bio *failed_bio,
	isector = start - btrfs_io_bio(failed_bio)->logical;
	isector >>= inode->i_sb->s_blocksize_bits;
	bio = btrfs_create_repair_bio(inode, failed_bio, failrec, page,
				      0, isector, repair_endio, repair_arg);
				pgoff, isector, repair_endio, repair_arg);
	if (!bio) {
		free_io_failure(inode, failrec);
		return -EIO;
@@ -7812,12 +7814,17 @@ struct btrfs_retry_complete {
static void btrfs_retry_endio_nocsum(struct bio *bio)
{
	struct btrfs_retry_complete *done = bio->bi_private;
	struct inode *inode;
	struct bio_vec *bvec;
	int i;

	if (bio->bi_error)
		goto end;

	ASSERT(bio->bi_vcnt == 1);
	inode = bio->bi_io_vec->bv_page->mapping->host;
	ASSERT(bio->bi_io_vec->bv_len == BTRFS_I(inode)->root->sectorsize);

	done->uptodate = 1;
	bio_for_each_segment_all(bvec, bio, i)
		clean_io_failure(done->inode, done->start, bvec->bv_page, 0);
@@ -7829,23 +7836,33 @@ static void btrfs_retry_endio_nocsum(struct bio *bio)
static int __btrfs_correct_data_nocsum(struct inode *inode,
				       struct btrfs_io_bio *io_bio)
{
	struct btrfs_fs_info *fs_info;
	struct bio_vec *bvec;
	struct btrfs_retry_complete done;
	u64 start;
	unsigned int pgoff;
	u32 sectorsize;
	int nr_sectors;
	int i;
	int ret;

	fs_info = BTRFS_I(inode)->root->fs_info;
	sectorsize = BTRFS_I(inode)->root->sectorsize;

	start = io_bio->logical;
	done.inode = inode;

	bio_for_each_segment_all(bvec, &io_bio->bio, i) {
try_again:
		nr_sectors = BTRFS_BYTES_TO_BLKS(fs_info, bvec->bv_len);
		pgoff = bvec->bv_offset;

next_block_or_try_again:
		done.uptodate = 0;
		done.start = start;
		init_completion(&done.done);

		ret = dio_read_error(inode, &io_bio->bio, bvec->bv_page, start,
				     start + bvec->bv_len - 1,
		ret = dio_read_error(inode, &io_bio->bio, bvec->bv_page,
				pgoff, start, start + sectorsize - 1,
				io_bio->mirror_num,
				btrfs_retry_endio_nocsum, &done);
		if (ret)
@@ -7855,10 +7872,15 @@ static int __btrfs_correct_data_nocsum(struct inode *inode,

		if (!done.uptodate) {
			/* We might have another mirror, so try again */
			goto try_again;
			goto next_block_or_try_again;
		}

		start += bvec->bv_len;
		start += sectorsize;

		if (nr_sectors--) {
			pgoff += sectorsize;
			goto next_block_or_try_again;
		}
	}

	return 0;
@@ -7868,7 +7890,9 @@ static void btrfs_retry_endio(struct bio *bio)
{
	struct btrfs_retry_complete *done = bio->bi_private;
	struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
	struct inode *inode;
	struct bio_vec *bvec;
	u64 start;
	int uptodate;
	int ret;
	int i;
@@ -7877,13 +7901,20 @@ static void btrfs_retry_endio(struct bio *bio)
		goto end;

	uptodate = 1;

	start = done->start;

	ASSERT(bio->bi_vcnt == 1);
	inode = bio->bi_io_vec->bv_page->mapping->host;
	ASSERT(bio->bi_io_vec->bv_len == BTRFS_I(inode)->root->sectorsize);

	bio_for_each_segment_all(bvec, bio, i) {
		ret = __readpage_endio_check(done->inode, io_bio, i,
					     bvec->bv_page, 0,
					bvec->bv_page, bvec->bv_offset,
					done->start, bvec->bv_len);
		if (!ret)
			clean_io_failure(done->inode, done->start,
					 bvec->bv_page, 0);
					bvec->bv_page, bvec->bv_offset);
		else
			uptodate = 0;
	}
@@ -7897,20 +7928,34 @@ static void btrfs_retry_endio(struct bio *bio)
static int __btrfs_subio_endio_read(struct inode *inode,
				    struct btrfs_io_bio *io_bio, int err)
{
	struct btrfs_fs_info *fs_info;
	struct bio_vec *bvec;
	struct btrfs_retry_complete done;
	u64 start;
	u64 offset = 0;
	u32 sectorsize;
	int nr_sectors;
	unsigned int pgoff;
	int csum_pos;
	int i;
	int ret;

	fs_info = BTRFS_I(inode)->root->fs_info;
	sectorsize = BTRFS_I(inode)->root->sectorsize;

	err = 0;
	start = io_bio->logical;
	done.inode = inode;

	bio_for_each_segment_all(bvec, &io_bio->bio, i) {
		ret = __readpage_endio_check(inode, io_bio, i, bvec->bv_page,
					     0, start, bvec->bv_len);
		nr_sectors = BTRFS_BYTES_TO_BLKS(fs_info, bvec->bv_len);

		pgoff = bvec->bv_offset;
next_block:
		csum_pos = BTRFS_BYTES_TO_BLKS(fs_info, offset);
		ret = __readpage_endio_check(inode, io_bio, csum_pos,
					bvec->bv_page, pgoff, start,
					sectorsize);
		if (likely(!ret))
			goto next;
try_again:
@@ -7918,8 +7963,8 @@ static int __btrfs_subio_endio_read(struct inode *inode,
		done.start = start;
		init_completion(&done.done);

		ret = dio_read_error(inode, &io_bio->bio, bvec->bv_page, start,
				     start + bvec->bv_len - 1,
		ret = dio_read_error(inode, &io_bio->bio, bvec->bv_page,
				pgoff, start, start + sectorsize - 1,
				io_bio->mirror_num,
				btrfs_retry_endio, &done);
		if (ret) {
@@ -7934,8 +7979,15 @@ static int __btrfs_subio_endio_read(struct inode *inode,
			goto try_again;
		}
next:
		offset += bvec->bv_len;
		start += bvec->bv_len;
		offset += sectorsize;
		start += sectorsize;

		ASSERT(nr_sectors);

		if (--nr_sectors) {
			pgoff += sectorsize;
			goto next_block;
		}
	}

	return err;