diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-08-05 10:13:57 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:06 -0400 |
commit | 7d2b4daa67379960477568abda62b8ba9ee3a8aa (patch) | |
tree | aa24628bd7a0db2f827da77b86c9df94c19d6355 /fs/btrfs | |
parent | ae01a0abf343aefe923ace5c1a8c634adfbe29c5 (diff) |
Btrfs: Fix the multi-bio code to save the original bio for completion
The multi-bio code is responsible for duplicating blocks in raid1 and
single spindle duplication. It has counters to make sure all of
the locations for a given extent are properly written before io completion
is returned to the higher layers.
But, it didn't always complete the same bio it was given, sometimes a
clone was completed instead. This lead to problems with the async
work queues because they saved a pointer to the bio in a struct off
bi_private.
The fix is to remember the original bio and only complete that one.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/volumes.c | 11 | ||||
-rw-r--r-- | fs/btrfs/volumes.h | 1 |
2 files changed, 11 insertions, 1 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 0de51e37e789..09311b3066df 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -2070,6 +2070,7 @@ static int end_bio_multi_stripe(struct bio *bio, | |||
2070 | #endif | 2070 | #endif |
2071 | { | 2071 | { |
2072 | struct btrfs_multi_bio *multi = bio->bi_private; | 2072 | struct btrfs_multi_bio *multi = bio->bi_private; |
2073 | int is_orig_bio = 0; | ||
2073 | 2074 | ||
2074 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) | 2075 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) |
2075 | if (bio->bi_size) | 2076 | if (bio->bi_size) |
@@ -2078,7 +2079,14 @@ static int end_bio_multi_stripe(struct bio *bio, | |||
2078 | if (err) | 2079 | if (err) |
2079 | atomic_inc(&multi->error); | 2080 | atomic_inc(&multi->error); |
2080 | 2081 | ||
2082 | if (bio == multi->orig_bio) | ||
2083 | is_orig_bio = 1; | ||
2084 | |||
2081 | if (atomic_dec_and_test(&multi->stripes_pending)) { | 2085 | if (atomic_dec_and_test(&multi->stripes_pending)) { |
2086 | if (!is_orig_bio) { | ||
2087 | bio_put(bio); | ||
2088 | bio = multi->orig_bio; | ||
2089 | } | ||
2082 | bio->bi_private = multi->private; | 2090 | bio->bi_private = multi->private; |
2083 | bio->bi_end_io = multi->end_io; | 2091 | bio->bi_end_io = multi->end_io; |
2084 | /* only send an error to the higher layers if it is | 2092 | /* only send an error to the higher layers if it is |
@@ -2101,7 +2109,7 @@ static int end_bio_multi_stripe(struct bio *bio, | |||
2101 | #else | 2109 | #else |
2102 | bio_endio(bio, err); | 2110 | bio_endio(bio, err); |
2103 | #endif | 2111 | #endif |
2104 | } else { | 2112 | } else if (!is_orig_bio) { |
2105 | bio_put(bio); | 2113 | bio_put(bio); |
2106 | } | 2114 | } |
2107 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) | 2115 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) |
@@ -2196,6 +2204,7 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio, | |||
2196 | } | 2204 | } |
2197 | multi->end_io = first_bio->bi_end_io; | 2205 | multi->end_io = first_bio->bi_end_io; |
2198 | multi->private = first_bio->bi_private; | 2206 | multi->private = first_bio->bi_private; |
2207 | multi->orig_bio = first_bio; | ||
2199 | atomic_set(&multi->stripes_pending, multi->num_stripes); | 2208 | atomic_set(&multi->stripes_pending, multi->num_stripes); |
2200 | 2209 | ||
2201 | while(dev_nr < total_devs) { | 2210 | while(dev_nr < total_devs) { |
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 48a44f7a9385..c50e50580b51 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h | |||
@@ -95,6 +95,7 @@ struct btrfs_bio_stripe { | |||
95 | struct btrfs_multi_bio { | 95 | struct btrfs_multi_bio { |
96 | atomic_t stripes_pending; | 96 | atomic_t stripes_pending; |
97 | bio_end_io_t *end_io; | 97 | bio_end_io_t *end_io; |
98 | struct bio *orig_bio; | ||
98 | void *private; | 99 | void *private; |
99 | atomic_t error; | 100 | atomic_t error; |
100 | int max_errors; | 101 | int max_errors; |