]> git.hungrycats.org Git - linux/commitdiff
Btrfs: do file data check by sub-bio's self
authorMiao Xie <miaox@cn.fujitsu.com>
Fri, 12 Sep 2014 10:43:56 +0000 (18:43 +0800)
committerZygo Blaxell <zblaxell@serenity.furryterror.org>
Wed, 3 Dec 2014 15:17:03 +0000 (10:17 -0500)
Direct IO splits the original bio to several sub-bios because of the limit of
raid stripe, and the filesystem will wait for all sub-bios and then run final
end io process.

But it was very hard to implement the data repair when dio read failure happens,
because at the final end io function, we didn't know which mirror the data was
read from. So in order to implement the data repair, we have to move the file data
check in the final end io function to the sub-bio end io function, in which we can
get the mirror number of the device we access. This patch did this work as the
first step of the direct io data repair implementation.

Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Chris Mason <clm@fb.com>
(cherry picked from commit c1dc08967f69c6b5067f8302c600f6628123f3bf)

fs/btrfs/btrfs_inode.h
fs/btrfs/extent_io.c
fs/btrfs/inode.c
fs/btrfs/volumes.h

index 25cadd23722e53a5a0f8d58e61bd741ddd01ec59..21cb250f2b3a6136e4cd9bb4b4062c6da3b538be 100644 (file)
@@ -254,8 +254,11 @@ static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
        return 0;
 }
 
+#define BTRFS_DIO_ORIG_BIO_SUBMITTED   0x1
+
 struct btrfs_dio_private {
        struct inode *inode;
+       unsigned long flags;
        u64 logical_offset;
        u64 disk_bytenr;
        u64 bytes;
@@ -272,6 +275,12 @@ struct btrfs_dio_private {
 
        /* dio_bio came from fs/direct-io.c */
        struct bio *dio_bio;
+
+       /*
+        * The original bio may be splited to several sub-bios, this is
+        * done during endio of sub-bios
+        */
+       int (*subio_endio)(struct inode *, struct btrfs_io_bio *);
 };
 
 /*
index 77b8a810f765fd51609b841205fc9bd2dcadccb6..46194ab55ce1d5837c0cf502e6944bff06900912 100644 (file)
@@ -2472,7 +2472,7 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
                struct inode *inode = page->mapping->host;
 
                pr_debug("end_bio_extent_readpage: bi_sector=%llu, err=%d, "
-                        "mirror=%lu\n", (u64)bio->bi_iter.bi_sector, err,
+                        "mirror=%u\n", (u64)bio->bi_iter.bi_sector, err,
                         io_bio->mirror_num);
                tree = &BTRFS_I(inode)->io_tree;
 
index e8f3aca37534ad02488eae89f3060efd22d28653..9cde6989dbe68a31346cf09e19cc780fbf2200e2 100644 (file)
@@ -7242,29 +7242,40 @@ unlock_err:
        return ret;
 }
 
-static void btrfs_endio_direct_read(struct bio *bio, int err)
+static int btrfs_subio_endio_read(struct inode *inode,
+                                 struct btrfs_io_bio *io_bio)
 {
-       struct btrfs_dio_private *dip = bio->bi_private;
        struct bio_vec *bvec;
-       struct inode *inode = dip->inode;
-       struct bio *dio_bio;
-       struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
        u64 start;
-       int ret;
        int i;
+       int ret;
+       int err = 0;
 
-       if (err || (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
-               goto skip_checksum;
+       if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
+               return 0;
 
-       start = dip->logical_offset;
-       bio_for_each_segment_all(bvec, bio, i) {
+       start = io_bio->logical;
+       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);
                if (ret)
                        err = -EIO;
                start += bvec->bv_len;
        }
-skip_checksum:
+
+       return err;
+}
+
+static void btrfs_endio_direct_read(struct bio *bio, int err)
+{
+       struct btrfs_dio_private *dip = bio->bi_private;
+       struct inode *inode = dip->inode;
+       struct bio *dio_bio;
+       struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+
+       if (!err && (dip->flags & BTRFS_DIO_ORIG_BIO_SUBMITTED))
+               err = btrfs_subio_endio_read(inode, io_bio);
+
        unlock_extent(&BTRFS_I(inode)->io_tree, dip->logical_offset,
                      dip->logical_offset + dip->bytes - 1);
        dio_bio = dip->dio_bio;
@@ -7342,6 +7353,7 @@ static int __btrfs_submit_bio_start_direct_io(struct inode *inode, int rw,
 static void btrfs_end_dio_bio(struct bio *bio, int err)
 {
        struct btrfs_dio_private *dip = bio->bi_private;
+       int ret;
 
        if (err) {
                btrfs_err(BTRFS_I(dip->inode)->root->fs_info,
@@ -7349,6 +7361,13 @@ static void btrfs_end_dio_bio(struct bio *bio, int err)
                      btrfs_ino(dip->inode), bio->bi_rw,
                      (unsigned long long)bio->bi_iter.bi_sector,
                      bio->bi_iter.bi_size, err);
+       } else if (dip->subio_endio) {
+               ret = dip->subio_endio(dip->inode, btrfs_io_bio(bio));
+               if (ret)
+                       err = ret;
+       }
+
+       if (err) {
                dip->errors = 1;
 
                /*
@@ -7379,6 +7398,38 @@ static struct bio *btrfs_dio_bio_alloc(struct block_device *bdev,
        return btrfs_bio_alloc(bdev, first_sector, nr_vecs, gfp_flags);
 }
 
+static inline int btrfs_lookup_and_bind_dio_csum(struct btrfs_root *root,
+                                                struct inode *inode,
+                                                struct btrfs_dio_private *dip,
+                                                struct bio *bio,
+                                                u64 file_offset)
+{
+       struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+       struct btrfs_io_bio *orig_io_bio = btrfs_io_bio(dip->orig_bio);
+       int ret;
+
+       /*
+        * We load all the csum data we need when we submit
+        * the first bio to reduce the csum tree search and
+        * contention.
+        */
+       if (dip->logical_offset == file_offset) {
+               ret = btrfs_lookup_bio_sums_dio(root, inode, dip->orig_bio,
+                                               file_offset);
+               if (ret)
+                       return ret;
+       }
+
+       if (bio == dip->orig_bio)
+               return 0;
+
+       file_offset -= dip->logical_offset;
+       file_offset >>= inode->i_sb->s_blocksize_bits;
+       io_bio->csum = (u8 *)(((u32 *)orig_io_bio->csum) + file_offset);
+
+       return 0;
+}
+
 static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
                                         int rw, u64 file_offset, int skip_sum,
                                         int async_submit)
@@ -7418,16 +7469,8 @@ static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
                if (ret)
                        goto err;
        } else {
-               /*
-                * We have loaded all the csum data we need when we submit
-                * the first bio, so skip it.
-                */
-               if (dip->logical_offset != file_offset)
-                       goto map;
-
-               /* Load all csum data at once. */
-               ret = btrfs_lookup_bio_sums_dio(root, inode, dip->orig_bio,
-                                               file_offset);
+               ret = btrfs_lookup_and_bind_dio_csum(root, inode, dip, bio,
+                                                    file_offset);
                if (ret)
                        goto err;
        }
@@ -7462,6 +7505,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
 
        if (map_length >= orig_bio->bi_iter.bi_size) {
                bio = orig_bio;
+               dip->flags |= BTRFS_DIO_ORIG_BIO_SUBMITTED;
                goto submit;
        }
 
@@ -7478,6 +7522,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
 
        bio->bi_private = dip;
        bio->bi_end_io = btrfs_end_dio_bio;
+       btrfs_io_bio(bio)->logical = file_offset;
        atomic_inc(&dip->pending_bios);
 
        while (bvec <= (orig_bio->bi_io_vec + orig_bio->bi_vcnt - 1)) {
@@ -7512,6 +7557,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
                                goto out_err;
                        bio->bi_private = dip;
                        bio->bi_end_io = btrfs_end_dio_bio;
+                       btrfs_io_bio(bio)->logical = file_offset;
 
                        map_length = orig_bio->bi_iter.bi_size;
                        ret = btrfs_map_block(root->fs_info, rw,
@@ -7568,7 +7614,7 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio,
                goto free_ordered;
        }
 
-       dip = kmalloc(sizeof(*dip), GFP_NOFS);
+       dip = kzalloc(sizeof(*dip), GFP_NOFS);
        if (!dip) {
                ret = -ENOMEM;
                goto free_io_bio;
@@ -7580,21 +7626,23 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio,
        dip->bytes = dio_bio->bi_iter.bi_size;
        dip->disk_bytenr = (u64)dio_bio->bi_iter.bi_sector << 9;
        io_bio->bi_private = dip;
-       dip->errors = 0;
        dip->orig_bio = io_bio;
        dip->dio_bio = dio_bio;
        atomic_set(&dip->pending_bios, 0);
+       btrfs_bio = btrfs_io_bio(io_bio);
+       btrfs_bio->logical = file_offset;
 
-       if (write)
+       if (write) {
                io_bio->bi_end_io = btrfs_endio_direct_write;
-       else
+       } else {
                io_bio->bi_end_io = btrfs_endio_direct_read;
+               dip->subio_endio = btrfs_subio_endio_read;
+       }
 
        ret = btrfs_submit_direct_hook(rw, dip, skip_sum);
        if (!ret)
                return;
 
-       btrfs_bio = btrfs_io_bio(io_bio);
        if (btrfs_bio->end_io)
                btrfs_bio->end_io(btrfs_bio, ret);
 free_io_bio:
index 08db79584cac066960a02ba446161a1e8c092f72..a265b3fb8e1fb82d5691e2da73f5c6cf1c1d80e3 100644 (file)
@@ -269,8 +269,9 @@ struct btrfs_fs_devices {
  */
 typedef void (btrfs_io_bio_end_io_t) (struct btrfs_io_bio *bio, int err);
 struct btrfs_io_bio {
-       unsigned long mirror_num;
-       unsigned long stripe_index;
+       unsigned int mirror_num;
+       unsigned int stripe_index;
+       u64 logical;
        u8 *csum;
        u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE];
        u8 *csum_allocated;