aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiao Xie <miaox@cn.fujitsu.com>2014-11-25 03:39:28 -0500
committerMiao Xie <miaox@cn.fujitsu.com>2014-12-02 21:18:47 -0500
commit4245215d6a8dba1a51c50533b6667919687c0b89 (patch)
treed3d988c61ff1dcf8fe6886e3c2ba3783814d614e
parent7603597690147a16b5cc77047d7570fa22a22673 (diff)
Btrfs, raid56: fix use-after-free problem in the final device replace procedure on raid56
The commit c404e0dc (Btrfs: fix use-after-free in the finishing procedure of the device replace) fixed a use-after-free problem which happened when removing the source device at the end of device replace, but at that time, btrfs didn't support device replace on raid56, so we didn't fix the problem on the raid56 profile. Currently, we implemented device replace for raid56, so we need kick that problem out before we enable that function for raid56. The fix method is very simple, we just increase the bio per-cpu counter before we submit a raid56 io, and decrease the counter when the raid56 io ends. Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
-rw-r--r--fs/btrfs/ctree.h7
-rw-r--r--fs/btrfs/dev-replace.c4
-rw-r--r--fs/btrfs/raid56.c41
-rw-r--r--fs/btrfs/raid56.h4
-rw-r--r--fs/btrfs/scrub.c2
-rw-r--r--fs/btrfs/volumes.c7
6 files changed, 45 insertions, 20 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index fe69edda11fb..470e3177a7e8 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -4097,7 +4097,12 @@ int btrfs_scrub_progress(struct btrfs_root *root, u64 devid,
4097/* dev-replace.c */ 4097/* dev-replace.c */
4098void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info); 4098void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info);
4099void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info); 4099void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info);
4100void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info); 4100void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount);
4101
4102static inline void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info)
4103{
4104 btrfs_bio_counter_sub(fs_info, 1);
4105}
4101 4106
4102/* reada.c */ 4107/* reada.c */
4103struct reada_control { 4108struct reada_control {
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index 6f662b34ba0e..fa27b4e3b6c8 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -920,9 +920,9 @@ void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info)
920 percpu_counter_inc(&fs_info->bio_counter); 920 percpu_counter_inc(&fs_info->bio_counter);
921} 921}
922 922
923void btrfs_bio_counter_dec(struct btrfs_fs_info *fs_info) 923void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount)
924{ 924{
925 percpu_counter_dec(&fs_info->bio_counter); 925 percpu_counter_sub(&fs_info->bio_counter, amount);
926 926
927 if (waitqueue_active(&fs_info->replace_wait)) 927 if (waitqueue_active(&fs_info->replace_wait))
928 wake_up(&fs_info->replace_wait); 928 wake_up(&fs_info->replace_wait);
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 5ece565bc5f0..8ab2a17bbba8 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -162,6 +162,8 @@ struct btrfs_raid_bio {
162 */ 162 */
163 int bio_list_bytes; 163 int bio_list_bytes;
164 164
165 int generic_bio_cnt;
166
165 atomic_t refs; 167 atomic_t refs;
166 168
167 atomic_t stripes_pending; 169 atomic_t stripes_pending;
@@ -354,6 +356,7 @@ static void merge_rbio(struct btrfs_raid_bio *dest,
354{ 356{
355 bio_list_merge(&dest->bio_list, &victim->bio_list); 357 bio_list_merge(&dest->bio_list, &victim->bio_list);
356 dest->bio_list_bytes += victim->bio_list_bytes; 358 dest->bio_list_bytes += victim->bio_list_bytes;
359 dest->generic_bio_cnt += victim->generic_bio_cnt;
357 bio_list_init(&victim->bio_list); 360 bio_list_init(&victim->bio_list);
358} 361}
359 362
@@ -891,6 +894,10 @@ static void rbio_orig_end_io(struct btrfs_raid_bio *rbio, int err, int uptodate)
891{ 894{
892 struct bio *cur = bio_list_get(&rbio->bio_list); 895 struct bio *cur = bio_list_get(&rbio->bio_list);
893 struct bio *next; 896 struct bio *next;
897
898 if (rbio->generic_bio_cnt)
899 btrfs_bio_counter_sub(rbio->fs_info, rbio->generic_bio_cnt);
900
894 free_raid_bio(rbio); 901 free_raid_bio(rbio);
895 902
896 while (cur) { 903 while (cur) {
@@ -1775,6 +1782,7 @@ int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
1775 struct btrfs_raid_bio *rbio; 1782 struct btrfs_raid_bio *rbio;
1776 struct btrfs_plug_cb *plug = NULL; 1783 struct btrfs_plug_cb *plug = NULL;
1777 struct blk_plug_cb *cb; 1784 struct blk_plug_cb *cb;
1785 int ret;
1778 1786
1779 rbio = alloc_rbio(root, bbio, raid_map, stripe_len); 1787 rbio = alloc_rbio(root, bbio, raid_map, stripe_len);
1780 if (IS_ERR(rbio)) { 1788 if (IS_ERR(rbio)) {
@@ -1785,12 +1793,19 @@ int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
1785 rbio->bio_list_bytes = bio->bi_iter.bi_size; 1793 rbio->bio_list_bytes = bio->bi_iter.bi_size;
1786 rbio->operation = BTRFS_RBIO_WRITE; 1794 rbio->operation = BTRFS_RBIO_WRITE;
1787 1795
1796 btrfs_bio_counter_inc_noblocked(root->fs_info);
1797 rbio->generic_bio_cnt = 1;
1798
1788 /* 1799 /*
1789 * don't plug on full rbios, just get them out the door 1800 * don't plug on full rbios, just get them out the door
1790 * as quickly as we can 1801 * as quickly as we can
1791 */ 1802 */
1792 if (rbio_is_full(rbio)) 1803 if (rbio_is_full(rbio)) {
1793 return full_stripe_write(rbio); 1804 ret = full_stripe_write(rbio);
1805 if (ret)
1806 btrfs_bio_counter_dec(root->fs_info);
1807 return ret;
1808 }
1794 1809
1795 cb = blk_check_plugged(btrfs_raid_unplug, root->fs_info, 1810 cb = blk_check_plugged(btrfs_raid_unplug, root->fs_info,
1796 sizeof(*plug)); 1811 sizeof(*plug));
@@ -1801,10 +1816,13 @@ int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
1801 INIT_LIST_HEAD(&plug->rbio_list); 1816 INIT_LIST_HEAD(&plug->rbio_list);
1802 } 1817 }
1803 list_add_tail(&rbio->plug_list, &plug->rbio_list); 1818 list_add_tail(&rbio->plug_list, &plug->rbio_list);
1819 ret = 0;
1804 } else { 1820 } else {
1805 return __raid56_parity_write(rbio); 1821 ret = __raid56_parity_write(rbio);
1822 if (ret)
1823 btrfs_bio_counter_dec(root->fs_info);
1806 } 1824 }
1807 return 0; 1825 return ret;
1808} 1826}
1809 1827
1810/* 1828/*
@@ -2139,19 +2157,17 @@ cleanup:
2139 */ 2157 */
2140int raid56_parity_recover(struct btrfs_root *root, struct bio *bio, 2158int raid56_parity_recover(struct btrfs_root *root, struct bio *bio,
2141 struct btrfs_bio *bbio, u64 *raid_map, 2159 struct btrfs_bio *bbio, u64 *raid_map,
2142 u64 stripe_len, int mirror_num, int hold_bbio) 2160 u64 stripe_len, int mirror_num, int generic_io)
2143{ 2161{
2144 struct btrfs_raid_bio *rbio; 2162 struct btrfs_raid_bio *rbio;
2145 int ret; 2163 int ret;
2146 2164
2147 rbio = alloc_rbio(root, bbio, raid_map, stripe_len); 2165 rbio = alloc_rbio(root, bbio, raid_map, stripe_len);
2148 if (IS_ERR(rbio)) { 2166 if (IS_ERR(rbio)) {
2149 __free_bbio_and_raid_map(bbio, raid_map, !hold_bbio); 2167 __free_bbio_and_raid_map(bbio, raid_map, generic_io);
2150 return PTR_ERR(rbio); 2168 return PTR_ERR(rbio);
2151 } 2169 }
2152 2170
2153 if (hold_bbio)
2154 set_bit(RBIO_HOLD_BBIO_MAP_BIT, &rbio->flags);
2155 rbio->operation = BTRFS_RBIO_READ_REBUILD; 2171 rbio->operation = BTRFS_RBIO_READ_REBUILD;
2156 bio_list_add(&rbio->bio_list, bio); 2172 bio_list_add(&rbio->bio_list, bio);
2157 rbio->bio_list_bytes = bio->bi_iter.bi_size; 2173 rbio->bio_list_bytes = bio->bi_iter.bi_size;
@@ -2159,11 +2175,18 @@ int raid56_parity_recover(struct btrfs_root *root, struct bio *bio,
2159 rbio->faila = find_logical_bio_stripe(rbio, bio); 2175 rbio->faila = find_logical_bio_stripe(rbio, bio);
2160 if (rbio->faila == -1) { 2176 if (rbio->faila == -1) {
2161 BUG(); 2177 BUG();
2162 __free_bbio_and_raid_map(bbio, raid_map, !hold_bbio); 2178 __free_bbio_and_raid_map(bbio, raid_map, generic_io);
2163 kfree(rbio); 2179 kfree(rbio);
2164 return -EIO; 2180 return -EIO;
2165 } 2181 }
2166 2182
2183 if (generic_io) {
2184 btrfs_bio_counter_inc_noblocked(root->fs_info);
2185 rbio->generic_bio_cnt = 1;
2186 } else {
2187 set_bit(RBIO_HOLD_BBIO_MAP_BIT, &rbio->flags);
2188 }
2189
2167 /* 2190 /*
2168 * reconstruct from the q stripe if they are 2191 * reconstruct from the q stripe if they are
2169 * asking for mirror 3 2192 * asking for mirror 3
diff --git a/fs/btrfs/raid56.h b/fs/btrfs/raid56.h
index 3d4ddb3d861d..31d4a157b5e3 100644
--- a/fs/btrfs/raid56.h
+++ b/fs/btrfs/raid56.h
@@ -43,8 +43,8 @@ struct btrfs_raid_bio;
43struct btrfs_device; 43struct btrfs_device;
44 44
45int raid56_parity_recover(struct btrfs_root *root, struct bio *bio, 45int raid56_parity_recover(struct btrfs_root *root, struct bio *bio,
46 struct btrfs_bio *bbio, u64 *raid_map, 46 struct btrfs_bio *bbio, u64 *raid_map,
47 u64 stripe_len, int mirror_num, int hold_bbio); 47 u64 stripe_len, int mirror_num, int generic_io);
48int raid56_parity_write(struct btrfs_root *root, struct bio *bio, 48int raid56_parity_write(struct btrfs_root *root, struct bio *bio,
49 struct btrfs_bio *bbio, u64 *raid_map, 49 struct btrfs_bio *bbio, u64 *raid_map,
50 u64 stripe_len); 50 u64 stripe_len);
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 0ae837fd676d..27f2e16cd259 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -1477,7 +1477,7 @@ static int scrub_submit_raid56_bio_wait(struct btrfs_fs_info *fs_info,
1477 ret = raid56_parity_recover(fs_info->fs_root, bio, page->recover->bbio, 1477 ret = raid56_parity_recover(fs_info->fs_root, bio, page->recover->bbio,
1478 page->recover->raid_map, 1478 page->recover->raid_map,
1479 page->recover->map_length, 1479 page->recover->map_length,
1480 page->mirror_num, 1); 1480 page->mirror_num, 0);
1481 if (ret) 1481 if (ret)
1482 return ret; 1482 return ret;
1483 1483
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 6d8a5e8d8c39..cbb766577f31 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -5843,12 +5843,9 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
5843 } else { 5843 } else {
5844 ret = raid56_parity_recover(root, bio, bbio, 5844 ret = raid56_parity_recover(root, bio, bbio,
5845 raid_map, map_length, 5845 raid_map, map_length,
5846 mirror_num, 0); 5846 mirror_num, 1);
5847 } 5847 }
5848 /* 5848
5849 * FIXME, replace dosen't support raid56 yet, please fix
5850 * it in the future.
5851 */
5852 btrfs_bio_counter_dec(root->fs_info); 5849 btrfs_bio_counter_dec(root->fs_info);
5853 return ret; 5850 return ret;
5854 } 5851 }