]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - fs/btrfs/volumes.c
Btrfs: Fix the multi-bio code to save the original bio for completion
[mirror_ubuntu-hirsute-kernel.git] / fs / btrfs / volumes.c
index 18db4cbe27941ac3d7779c2c2a4d2504975c2940..09311b3066dfd6c7ac1dce4641b1b7f82eae0a07 100644 (file)
@@ -180,7 +180,11 @@ loop:
                pending = pending->bi_next;
                cur->bi_next = NULL;
                atomic_dec(&device->dev_root->fs_info->nr_async_submits);
+
+               BUG_ON(atomic_read(&cur->bi_cnt) == 0);
+               bio_get(cur);
                submit_bio(cur->bi_rw, cur);
+               bio_put(cur);
                num_run++;
 
                /*
@@ -188,10 +192,11 @@ loop:
                 * is now congested.  Back off and let other work structs
                 * run instead
                 */
-               if (pending && num_run && bdi_write_congested(bdi)) {
+               if (pending && bdi_write_congested(bdi)) {
                        struct bio *old_head;
 
                        spin_lock(&device->io_lock);
+
                        old_head = device->pending_bios;
                        device->pending_bios = pending;
                        if (device->pending_bio_tail)
@@ -2065,6 +2070,7 @@ static int end_bio_multi_stripe(struct bio *bio,
 #endif
 {
        struct btrfs_multi_bio *multi = bio->bi_private;
+       int is_orig_bio = 0;
 
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
        if (bio->bi_size)
@@ -2073,7 +2079,14 @@ static int end_bio_multi_stripe(struct bio *bio,
        if (err)
                atomic_inc(&multi->error);
 
+       if (bio == multi->orig_bio)
+               is_orig_bio = 1;
+
        if (atomic_dec_and_test(&multi->stripes_pending)) {
+               if (!is_orig_bio) {
+                       bio_put(bio);
+                       bio = multi->orig_bio;
+               }
                bio->bi_private = multi->private;
                bio->bi_end_io = multi->end_io;
                /* only send an error to the higher layers if it is
@@ -2096,7 +2109,7 @@ static int end_bio_multi_stripe(struct bio *bio,
 #else
                bio_endio(bio, err);
 #endif
-       } else {
+       } else if (!is_orig_bio) {
                bio_put(bio);
        }
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
@@ -2125,7 +2138,9 @@ int schedule_bio(struct btrfs_root *root, struct btrfs_device *device,
 
        /* don't bother with additional async steps for reads, right now */
        if (!(rw & (1 << BIO_RW))) {
+               bio_get(bio);
                submit_bio(rw, bio);
+               bio_put(bio);
                return 0;
        }
 
@@ -2136,6 +2151,7 @@ int schedule_bio(struct btrfs_root *root, struct btrfs_device *device,
         * on a queue for later
         */
        atomic_inc(&root->fs_info->nr_async_submits);
+       WARN_ON(bio->bi_next);
        bio->bi_next = NULL;
        bio->bi_rw |= rw;
 
@@ -2188,6 +2204,7 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
        }
        multi->end_io = first_bio->bi_end_io;
        multi->private = first_bio->bi_private;
+       multi->orig_bio = first_bio;
        atomic_set(&multi->stripes_pending, multi->num_stripes);
 
        while(dev_nr < total_devs) {