diff options
author | Christophe Leroy <christophe.leroy@c-s.fr> | 2019-08-21 11:05:55 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2019-09-09 08:59:14 -0400 |
commit | 3acd48507dc43eeeb0a1fe965b8bad91cab904a7 (patch) | |
tree | 5dd3e0e79f99ed6e28f36dc267ae7c22dbcba542 | |
parent | 62fdaa52a3d00a875da771719b6dc537ca79fce1 (diff) |
btrfs: fix allocation of free space cache v1 bitmap pages
Various notifications of type "BUG kmalloc-4096 () : Redzone
overwritten" have been observed recently in various parts of the kernel.
After some time, it has been made a relation with the use of BTRFS
filesystem and with SLUB_DEBUG turned on.
[ 22.809700] BUG kmalloc-4096 (Tainted: G W ): Redzone overwritten
[ 22.810286] INFO: 0xbe1a5921-0xfbfc06cd. First byte 0x0 instead of 0xcc
[ 22.810866] INFO: Allocated in __load_free_space_cache+0x588/0x780 [btrfs] age=22 cpu=0 pid=224
[ 22.811193] __slab_alloc.constprop.26+0x44/0x70
[ 22.811345] kmem_cache_alloc_trace+0xf0/0x2ec
[ 22.811588] __load_free_space_cache+0x588/0x780 [btrfs]
[ 22.811848] load_free_space_cache+0xf4/0x1b0 [btrfs]
[ 22.812090] cache_block_group+0x1d0/0x3d0 [btrfs]
[ 22.812321] find_free_extent+0x680/0x12a4 [btrfs]
[ 22.812549] btrfs_reserve_extent+0xec/0x220 [btrfs]
[ 22.812785] btrfs_alloc_tree_block+0x178/0x5f4 [btrfs]
[ 22.813032] __btrfs_cow_block+0x150/0x5d4 [btrfs]
[ 22.813262] btrfs_cow_block+0x194/0x298 [btrfs]
[ 22.813484] commit_cowonly_roots+0x44/0x294 [btrfs]
[ 22.813718] btrfs_commit_transaction+0x63c/0xc0c [btrfs]
[ 22.813973] close_ctree+0xf8/0x2a4 [btrfs]
[ 22.814107] generic_shutdown_super+0x80/0x110
[ 22.814250] kill_anon_super+0x18/0x30
[ 22.814437] btrfs_kill_super+0x18/0x90 [btrfs]
[ 22.814590] INFO: Freed in proc_cgroup_show+0xc0/0x248 age=41 cpu=0 pid=83
[ 22.814841] proc_cgroup_show+0xc0/0x248
[ 22.814967] proc_single_show+0x54/0x98
[ 22.815086] seq_read+0x278/0x45c
[ 22.815190] __vfs_read+0x28/0x17c
[ 22.815289] vfs_read+0xa8/0x14c
[ 22.815381] ksys_read+0x50/0x94
[ 22.815475] ret_from_syscall+0x0/0x38
Commit 69d2480456d1 ("btrfs: use copy_page for copying pages instead of
memcpy") changed the way bitmap blocks are copied. But allthough bitmaps
have the size of a page, they were allocated with kzalloc().
Most of the time, kzalloc() allocates aligned blocks of memory, so
copy_page() can be used. But when some debug options like SLAB_DEBUG are
activated, kzalloc() may return unaligned pointer.
On powerpc, memcpy(), copy_page() and other copying functions use
'dcbz' instruction which provides an entire zeroed cacheline to avoid
memory read when the intention is to overwrite a full line. Functions
like memcpy() are writen to care about partial cachelines at the start
and end of the destination, but copy_page() assumes it gets pages. As
pages are naturally cache aligned, copy_page() doesn't care about
partial lines. This means that when copy_page() is called with a
misaligned pointer, a few leading bytes are zeroed.
To fix it, allocate bitmaps through kmem_cache instead of using kzalloc()
The cache pool is created with PAGE_SIZE alignment constraint.
Reported-by: Erhard F. <erhard_f@mailbox.org>
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=204371
Fixes: 69d2480456d1 ("btrfs: use copy_page for copying pages instead of memcpy")
Cc: stable@vger.kernel.org # 4.19+
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
Reviewed-by: David Sterba <dsterba@suse.com>
[ rename to btrfs_free_space_bitmap ]
Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r-- | fs/btrfs/ctree.h | 1 | ||||
-rw-r--r-- | fs/btrfs/free-space-cache.c | 20 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 8 |
3 files changed, 22 insertions, 7 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index d27b39858339..ef40fffb5e46 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -43,6 +43,7 @@ extern struct kmem_cache *btrfs_trans_handle_cachep; | |||
43 | extern struct kmem_cache *btrfs_bit_radix_cachep; | 43 | extern struct kmem_cache *btrfs_bit_radix_cachep; |
44 | extern struct kmem_cache *btrfs_path_cachep; | 44 | extern struct kmem_cache *btrfs_path_cachep; |
45 | extern struct kmem_cache *btrfs_free_space_cachep; | 45 | extern struct kmem_cache *btrfs_free_space_cachep; |
46 | extern struct kmem_cache *btrfs_free_space_bitmap_cachep; | ||
46 | struct btrfs_ordered_sum; | 47 | struct btrfs_ordered_sum; |
47 | struct btrfs_ref; | 48 | struct btrfs_ref; |
48 | 49 | ||
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 265dc75f7a7a..ab806d82fe12 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c | |||
@@ -765,7 +765,8 @@ static int __load_free_space_cache(struct btrfs_root *root, struct inode *inode, | |||
765 | } else { | 765 | } else { |
766 | ASSERT(num_bitmaps); | 766 | ASSERT(num_bitmaps); |
767 | num_bitmaps--; | 767 | num_bitmaps--; |
768 | e->bitmap = kzalloc(PAGE_SIZE, GFP_NOFS); | 768 | e->bitmap = kmem_cache_zalloc( |
769 | btrfs_free_space_bitmap_cachep, GFP_NOFS); | ||
769 | if (!e->bitmap) { | 770 | if (!e->bitmap) { |
770 | kmem_cache_free( | 771 | kmem_cache_free( |
771 | btrfs_free_space_cachep, e); | 772 | btrfs_free_space_cachep, e); |
@@ -1882,7 +1883,7 @@ static void free_bitmap(struct btrfs_free_space_ctl *ctl, | |||
1882 | struct btrfs_free_space *bitmap_info) | 1883 | struct btrfs_free_space *bitmap_info) |
1883 | { | 1884 | { |
1884 | unlink_free_space(ctl, bitmap_info); | 1885 | unlink_free_space(ctl, bitmap_info); |
1885 | kfree(bitmap_info->bitmap); | 1886 | kmem_cache_free(btrfs_free_space_bitmap_cachep, bitmap_info->bitmap); |
1886 | kmem_cache_free(btrfs_free_space_cachep, bitmap_info); | 1887 | kmem_cache_free(btrfs_free_space_cachep, bitmap_info); |
1887 | ctl->total_bitmaps--; | 1888 | ctl->total_bitmaps--; |
1888 | ctl->op->recalc_thresholds(ctl); | 1889 | ctl->op->recalc_thresholds(ctl); |
@@ -2136,7 +2137,8 @@ new_bitmap: | |||
2136 | } | 2137 | } |
2137 | 2138 | ||
2138 | /* allocate the bitmap */ | 2139 | /* allocate the bitmap */ |
2139 | info->bitmap = kzalloc(PAGE_SIZE, GFP_NOFS); | 2140 | info->bitmap = kmem_cache_zalloc(btrfs_free_space_bitmap_cachep, |
2141 | GFP_NOFS); | ||
2140 | spin_lock(&ctl->tree_lock); | 2142 | spin_lock(&ctl->tree_lock); |
2141 | if (!info->bitmap) { | 2143 | if (!info->bitmap) { |
2142 | ret = -ENOMEM; | 2144 | ret = -ENOMEM; |
@@ -2147,7 +2149,9 @@ new_bitmap: | |||
2147 | 2149 | ||
2148 | out: | 2150 | out: |
2149 | if (info) { | 2151 | if (info) { |
2150 | kfree(info->bitmap); | 2152 | if (info->bitmap) |
2153 | kmem_cache_free(btrfs_free_space_bitmap_cachep, | ||
2154 | info->bitmap); | ||
2151 | kmem_cache_free(btrfs_free_space_cachep, info); | 2155 | kmem_cache_free(btrfs_free_space_cachep, info); |
2152 | } | 2156 | } |
2153 | 2157 | ||
@@ -2811,7 +2815,8 @@ out: | |||
2811 | if (entry->bytes == 0) { | 2815 | if (entry->bytes == 0) { |
2812 | ctl->free_extents--; | 2816 | ctl->free_extents--; |
2813 | if (entry->bitmap) { | 2817 | if (entry->bitmap) { |
2814 | kfree(entry->bitmap); | 2818 | kmem_cache_free(btrfs_free_space_bitmap_cachep, |
2819 | entry->bitmap); | ||
2815 | ctl->total_bitmaps--; | 2820 | ctl->total_bitmaps--; |
2816 | ctl->op->recalc_thresholds(ctl); | 2821 | ctl->op->recalc_thresholds(ctl); |
2817 | } | 2822 | } |
@@ -3615,7 +3620,7 @@ again: | |||
3615 | } | 3620 | } |
3616 | 3621 | ||
3617 | if (!map) { | 3622 | if (!map) { |
3618 | map = kzalloc(PAGE_SIZE, GFP_NOFS); | 3623 | map = kmem_cache_zalloc(btrfs_free_space_bitmap_cachep, GFP_NOFS); |
3619 | if (!map) { | 3624 | if (!map) { |
3620 | kmem_cache_free(btrfs_free_space_cachep, info); | 3625 | kmem_cache_free(btrfs_free_space_cachep, info); |
3621 | return -ENOMEM; | 3626 | return -ENOMEM; |
@@ -3644,7 +3649,8 @@ again: | |||
3644 | 3649 | ||
3645 | if (info) | 3650 | if (info) |
3646 | kmem_cache_free(btrfs_free_space_cachep, info); | 3651 | kmem_cache_free(btrfs_free_space_cachep, info); |
3647 | kfree(map); | 3652 | if (map) |
3653 | kmem_cache_free(btrfs_free_space_bitmap_cachep, map); | ||
3648 | return 0; | 3654 | return 0; |
3649 | } | 3655 | } |
3650 | 3656 | ||
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index b52282df8c4d..d79ad5abd06e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -74,6 +74,7 @@ static struct kmem_cache *btrfs_inode_cachep; | |||
74 | struct kmem_cache *btrfs_trans_handle_cachep; | 74 | struct kmem_cache *btrfs_trans_handle_cachep; |
75 | struct kmem_cache *btrfs_path_cachep; | 75 | struct kmem_cache *btrfs_path_cachep; |
76 | struct kmem_cache *btrfs_free_space_cachep; | 76 | struct kmem_cache *btrfs_free_space_cachep; |
77 | struct kmem_cache *btrfs_free_space_bitmap_cachep; | ||
77 | 78 | ||
78 | static int btrfs_setsize(struct inode *inode, struct iattr *attr); | 79 | static int btrfs_setsize(struct inode *inode, struct iattr *attr); |
79 | static int btrfs_truncate(struct inode *inode, bool skip_writeback); | 80 | static int btrfs_truncate(struct inode *inode, bool skip_writeback); |
@@ -9409,6 +9410,7 @@ void __cold btrfs_destroy_cachep(void) | |||
9409 | kmem_cache_destroy(btrfs_trans_handle_cachep); | 9410 | kmem_cache_destroy(btrfs_trans_handle_cachep); |
9410 | kmem_cache_destroy(btrfs_path_cachep); | 9411 | kmem_cache_destroy(btrfs_path_cachep); |
9411 | kmem_cache_destroy(btrfs_free_space_cachep); | 9412 | kmem_cache_destroy(btrfs_free_space_cachep); |
9413 | kmem_cache_destroy(btrfs_free_space_bitmap_cachep); | ||
9412 | } | 9414 | } |
9413 | 9415 | ||
9414 | int __init btrfs_init_cachep(void) | 9416 | int __init btrfs_init_cachep(void) |
@@ -9438,6 +9440,12 @@ int __init btrfs_init_cachep(void) | |||
9438 | if (!btrfs_free_space_cachep) | 9440 | if (!btrfs_free_space_cachep) |
9439 | goto fail; | 9441 | goto fail; |
9440 | 9442 | ||
9443 | btrfs_free_space_bitmap_cachep = kmem_cache_create("btrfs_free_space_bitmap", | ||
9444 | PAGE_SIZE, PAGE_SIZE, | ||
9445 | SLAB_RED_ZONE, NULL); | ||
9446 | if (!btrfs_free_space_bitmap_cachep) | ||
9447 | goto fail; | ||
9448 | |||
9441 | return 0; | 9449 | return 0; |
9442 | fail: | 9450 | fail: |
9443 | btrfs_destroy_cachep(); | 9451 | btrfs_destroy_cachep(); |