aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorWang Shilong <wangsl.fnst@cn.fujitsu.com>2014-06-18 22:42:52 -0400
committerChris Mason <clm@fb.com>2014-06-19 17:20:55 -0400
commit298a8f9cf17d2f2e1ffc41e5e247fa3695a8a76f (patch)
tree639c13227f07fefe4eea2a13988d3792848f67bf /fs
parentced96edc48ba455b0982c3aa64d3cc3bf2d0816a (diff)
Btrfs: fix NULL pointer crash when running balance and scrub concurrently
While running balance, scrub, fsstress concurrently we hit the following kernel crash: [56561.448845] BTRFS info (device sde): relocating block group 11005853696 flags 132 [56561.524077] BUG: unable to handle kernel NULL pointer dereference at 0000000000000078 [56561.524237] IP: [<ffffffffa038956d>] scrub_chunk.isra.12+0xdd/0x130 [btrfs] [56561.524297] PGD 9be28067 PUD 7f3dd067 PMD 0 [56561.524325] Oops: 0000 [#1] SMP [....] [56561.527237] Call Trace: [56561.527309] [<ffffffffa038980e>] scrub_enumerate_chunks+0x24e/0x490 [btrfs] [56561.527392] [<ffffffff810abe00>] ? abort_exclusive_wait+0x50/0xb0 [56561.527476] [<ffffffffa038add4>] btrfs_scrub_dev+0x1a4/0x530 [btrfs] [56561.527561] [<ffffffffa0368107>] btrfs_ioctl+0x13f7/0x2a90 [btrfs] [56561.527639] [<ffffffff811c82f0>] do_vfs_ioctl+0x2e0/0x4c0 [56561.527712] [<ffffffff8109c384>] ? vtime_account_user+0x54/0x60 [56561.527788] [<ffffffff810f768c>] ? __audit_syscall_entry+0x9c/0xf0 [56561.527870] [<ffffffff811c8551>] SyS_ioctl+0x81/0xa0 [56561.527941] [<ffffffff815707f7>] tracesys+0xdd/0xe2 [...] [56561.528304] RIP [<ffffffffa038956d>] scrub_chunk.isra.12+0xdd/0x130 [btrfs] [56561.528395] RSP <ffff88004c0f5be8> [56561.528454] CR2: 0000000000000078 This is because in btrfs_relocate_chunk(), we will free @bdev directly while scrub may still hold extent mapping, and may access freed memory. Fix this problem by wrapping freeing @bdev work into free_extent_map() which is based on reference count. Reported-by: Qu Wenruo <quwenruo@cn.fujitsu.com> Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com> Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/extent_map.c2
-rw-r--r--fs/btrfs/extent_map.h1
-rw-r--r--fs/btrfs/volumes.c10
3 files changed, 6 insertions, 7 deletions
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 1874aee69c86..225302b39afb 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -75,6 +75,8 @@ void free_extent_map(struct extent_map *em)
75 if (atomic_dec_and_test(&em->refs)) { 75 if (atomic_dec_and_test(&em->refs)) {
76 WARN_ON(extent_map_in_tree(em)); 76 WARN_ON(extent_map_in_tree(em));
77 WARN_ON(!list_empty(&em->list)); 77 WARN_ON(!list_empty(&em->list));
78 if (test_bit(EXTENT_FLAG_FS_MAPPING, &em->flags))
79 kfree(em->bdev);
78 kmem_cache_free(extent_map_cache, em); 80 kmem_cache_free(extent_map_cache, em);
79 } 81 }
80} 82}
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index e7fd8a56a140..b2991fd8583e 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -15,6 +15,7 @@
15#define EXTENT_FLAG_PREALLOC 3 /* pre-allocated extent */ 15#define EXTENT_FLAG_PREALLOC 3 /* pre-allocated extent */
16#define EXTENT_FLAG_LOGGING 4 /* Logging this extent */ 16#define EXTENT_FLAG_LOGGING 4 /* Logging this extent */
17#define EXTENT_FLAG_FILLING 5 /* Filling in a preallocated extent */ 17#define EXTENT_FLAG_FILLING 5 /* Filling in a preallocated extent */
18#define EXTENT_FLAG_FS_MAPPING 6 /* filesystem extent mapping type */
18 19
19struct extent_map { 20struct extent_map {
20 struct rb_node rb_node; 21 struct rb_node rb_node;
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index ffeed6d6326f..19c298a47a6f 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -2543,9 +2543,6 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
2543 remove_extent_mapping(em_tree, em); 2543 remove_extent_mapping(em_tree, em);
2544 write_unlock(&em_tree->lock); 2544 write_unlock(&em_tree->lock);
2545 2545
2546 kfree(map);
2547 em->bdev = NULL;
2548
2549 /* once for the tree */ 2546 /* once for the tree */
2550 free_extent_map(em); 2547 free_extent_map(em);
2551 /* once for us */ 2548 /* once for us */
@@ -4301,9 +4298,11 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
4301 4298
4302 em = alloc_extent_map(); 4299 em = alloc_extent_map();
4303 if (!em) { 4300 if (!em) {
4301 kfree(map);
4304 ret = -ENOMEM; 4302 ret = -ENOMEM;
4305 goto error; 4303 goto error;
4306 } 4304 }
4305 set_bit(EXTENT_FLAG_FS_MAPPING, &em->flags);
4307 em->bdev = (struct block_device *)map; 4306 em->bdev = (struct block_device *)map;
4308 em->start = start; 4307 em->start = start;
4309 em->len = num_bytes; 4308 em->len = num_bytes;
@@ -4346,7 +4345,6 @@ error_del_extent:
4346 /* One for the tree reference */ 4345 /* One for the tree reference */
4347 free_extent_map(em); 4346 free_extent_map(em);
4348error: 4347error:
4349 kfree(map);
4350 kfree(devices_info); 4348 kfree(devices_info);
4351 return ret; 4349 return ret;
4352} 4350}
@@ -4558,7 +4556,6 @@ void btrfs_mapping_tree_free(struct btrfs_mapping_tree *tree)
4558 write_unlock(&tree->map_tree.lock); 4556 write_unlock(&tree->map_tree.lock);
4559 if (!em) 4557 if (!em)
4560 break; 4558 break;
4561 kfree(em->bdev);
4562 /* once for us */ 4559 /* once for us */
4563 free_extent_map(em); 4560 free_extent_map(em);
4564 /* once for the tree */ 4561 /* once for the tree */
@@ -5822,6 +5819,7 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
5822 return -ENOMEM; 5819 return -ENOMEM;
5823 } 5820 }
5824 5821
5822 set_bit(EXTENT_FLAG_FS_MAPPING, &em->flags);
5825 em->bdev = (struct block_device *)map; 5823 em->bdev = (struct block_device *)map;
5826 em->start = logical; 5824 em->start = logical;
5827 em->len = length; 5825 em->len = length;
@@ -5846,7 +5844,6 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
5846 map->stripes[i].dev = btrfs_find_device(root->fs_info, devid, 5844 map->stripes[i].dev = btrfs_find_device(root->fs_info, devid,
5847 uuid, NULL); 5845 uuid, NULL);
5848 if (!map->stripes[i].dev && !btrfs_test_opt(root, DEGRADED)) { 5846 if (!map->stripes[i].dev && !btrfs_test_opt(root, DEGRADED)) {
5849 kfree(map);
5850 free_extent_map(em); 5847 free_extent_map(em);
5851 return -EIO; 5848 return -EIO;
5852 } 5849 }
@@ -5854,7 +5851,6 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
5854 map->stripes[i].dev = 5851 map->stripes[i].dev =
5855 add_missing_dev(root, devid, uuid); 5852 add_missing_dev(root, devid, uuid);
5856 if (!map->stripes[i].dev) { 5853 if (!map->stripes[i].dev) {
5857 kfree(map);
5858 free_extent_map(em); 5854 free_extent_map(em);
5859 return -EIO; 5855 return -EIO;
5860 } 5856 }