diff options
author | Filipe David Borba Manana <fdmanana@gmail.com> | 2014-01-11 21:22:46 -0500 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-01-28 16:20:31 -0500 |
commit | 14a958e678cd77cae475b60ca46c0797b1c006a1 (patch) | |
tree | 94bf5974065fc34d230e5c036b14f611389d621b /fs/btrfs | |
parent | c57c2b3ed248b3f1712e4172eb85b361199582f2 (diff) |
Btrfs: fix btrfs boot when compiled as built-in
After the change titled "Btrfs: add support for inode properties", if
btrfs was built-in the kernel (i.e. not as a module), it would cause a
kernel panic, as reported recently by Fengguang:
[ 2.024722] BUG: unable to handle kernel NULL pointer dereference at (null)
[ 2.027814] IP: [<ffffffff81501594>] crc32c+0xc/0x6b
[ 2.028684] PGD 0
[ 2.028684] Oops: 0000 [#1] SMP
[ 2.028684] Modules linked in:
[ 2.028684] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.13.0-rc7-04795-ga7b57c2 #1
[ 2.028684] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[ 2.028684] task: ffff88000edba100 ti: ffff88000edd6000 task.ti: ffff88000edd6000
[ 2.028684] RIP: 0010:[<ffffffff81501594>] [<ffffffff81501594>] crc32c+0xc/0x6b
[ 2.028684] RSP: 0000:ffff88000edd7e58 EFLAGS: 00010246
[ 2.028684] RAX: 0000000000000000 RBX: ffffffff82295550 RCX: 0000000000000000
[ 2.028684] RDX: 0000000000000011 RSI: ffffffff81efe393 RDI: 00000000fffffffe
[ 2.028684] RBP: ffff88000edd7e60 R08: 0000000000000003 R09: 0000000000015d20
[ 2.028684] R10: ffffffff81ef225e R11: ffffffff811b0222 R12: ffffffffffffffff
[ 2.028684] R13: 0000000000000239 R14: 0000000000000000 R15: 0000000000000000
[ 2.028684] FS: 0000000000000000(0000) GS:ffff88000fa00000(0000) knlGS:0000000000000000
[ 2.028684] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[ 2.028684] CR2: 0000000000000000 CR3: 000000000220c000 CR4: 00000000000006f0
[ 2.028684] Stack:
[ 2.028684] ffffffff82295550 ffff88000edd7e80 ffffffff8238af62 ffffffff8238ac05
[ 2.028684] 0000000000000000 ffff88000edd7e98 ffffffff8238ac0f ffffffff8238ac05
[ 2.028684] ffff88000edd7f08 ffffffff810002ba ffff88000edd7f00 ffffffff810e2404
[ 2.028684] Call Trace:
[ 2.028684] [<ffffffff8238af62>] btrfs_props_init+0x4f/0x96
[ 2.028684] [<ffffffff8238ac05>] ? ftrace_define_fields_btrfs_space_reservation+0x145/0x145
[ 2.028684] [<ffffffff8238ac0f>] init_btrfs_fs+0xa/0xf0
[ 2.028684] [<ffffffff8238ac05>] ? ftrace_define_fields_btrfs_space_reservation+0x145/0x145
[ 2.028684] [<ffffffff810002ba>] do_one_initcall+0xa4/0x13a
[ 2.028684] [<ffffffff810e2404>] ? parse_args+0x25f/0x33d
[ 2.028684] [<ffffffff8234cf75>] kernel_init_freeable+0x1aa/0x230
[ 2.028684] [<ffffffff8234c785>] ? do_early_param+0x88/0x88
[ 2.028684] [<ffffffff819f61b5>] ? rest_init+0x89/0x89
[ 2.028684] [<ffffffff819f61c3>] kernel_init+0xe/0x109
The issue here is that the initialization function of btrfs (super.c:init_btrfs_fs)
started using crc32c (from lib/libcrc32c.c). But when it needs to call crc32c (as
part of the properties initialization routine), the libcrc32c is not yet initialized,
so crc32c derreferenced a NULL pointer (lib/libcrc32c.c:tfm), causing the kernel
panic on boot.
The approach to fix this is to use crypto component directly to use its crc32c (which
is basically what lib/libcrc32c.c is, a wrapper around crypto). This is what ext4 is
doing as well, it uses crypto directly to get crc32c functionality.
Verified this works both when btrfs is built-in and when it's loadable kernel module.
Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/Kconfig | 3 | ||||
-rw-r--r-- | fs/btrfs/Makefile | 2 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 6 | ||||
-rw-r--r-- | fs/btrfs/hash.c | 50 | ||||
-rw-r--r-- | fs/btrfs/hash.h | 11 | ||||
-rw-r--r-- | fs/btrfs/super.c | 10 |
6 files changed, 73 insertions, 9 deletions
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index aa976eced2d2..a66768ebc8d1 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig | |||
@@ -1,6 +1,7 @@ | |||
1 | config BTRFS_FS | 1 | config BTRFS_FS |
2 | tristate "Btrfs filesystem support" | 2 | tristate "Btrfs filesystem support" |
3 | select LIBCRC32C | 3 | select CRYPTO |
4 | select CRYPTO_CRC32C | ||
4 | select ZLIB_INFLATE | 5 | select ZLIB_INFLATE |
5 | select ZLIB_DEFLATE | 6 | select ZLIB_DEFLATE |
6 | select LZO_COMPRESS | 7 | select LZO_COMPRESS |
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index af7f000e905c..f341a98031d2 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile | |||
@@ -9,7 +9,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ | |||
9 | export.o tree-log.o free-space-cache.o zlib.o lzo.o \ | 9 | export.o tree-log.o free-space-cache.o zlib.o lzo.o \ |
10 | compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ | 10 | compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ |
11 | reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ | 11 | reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ |
12 | uuid-tree.o props.o | 12 | uuid-tree.o props.o hash.o |
13 | 13 | ||
14 | btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o | 14 | btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o |
15 | btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o | 15 | btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index b5322596d60b..db1e32ffcfbb 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -1074,11 +1074,11 @@ static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset) | |||
1074 | __le64 lenum; | 1074 | __le64 lenum; |
1075 | 1075 | ||
1076 | lenum = cpu_to_le64(root_objectid); | 1076 | lenum = cpu_to_le64(root_objectid); |
1077 | high_crc = crc32c(high_crc, &lenum, sizeof(lenum)); | 1077 | high_crc = btrfs_crc32c(high_crc, &lenum, sizeof(lenum)); |
1078 | lenum = cpu_to_le64(owner); | 1078 | lenum = cpu_to_le64(owner); |
1079 | low_crc = crc32c(low_crc, &lenum, sizeof(lenum)); | 1079 | low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum)); |
1080 | lenum = cpu_to_le64(offset); | 1080 | lenum = cpu_to_le64(offset); |
1081 | low_crc = crc32c(low_crc, &lenum, sizeof(lenum)); | 1081 | low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum)); |
1082 | 1082 | ||
1083 | return ((u64)high_crc << 31) ^ (u64)low_crc; | 1083 | return ((u64)high_crc << 31) ^ (u64)low_crc; |
1084 | } | 1084 | } |
diff --git a/fs/btrfs/hash.c b/fs/btrfs/hash.c new file mode 100644 index 000000000000..85889aa82c62 --- /dev/null +++ b/fs/btrfs/hash.c | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Filipe David Borba Manana <fdmanana@gmail.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public | ||
6 | * License v2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
11 | * General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <crypto/hash.h> | ||
15 | #include <linux/err.h> | ||
16 | #include "hash.h" | ||
17 | |||
18 | static struct crypto_shash *tfm; | ||
19 | |||
20 | int __init btrfs_hash_init(void) | ||
21 | { | ||
22 | tfm = crypto_alloc_shash("crc32c", 0, 0); | ||
23 | if (IS_ERR(tfm)) | ||
24 | return PTR_ERR(tfm); | ||
25 | |||
26 | return 0; | ||
27 | } | ||
28 | |||
29 | void btrfs_hash_exit(void) | ||
30 | { | ||
31 | crypto_free_shash(tfm); | ||
32 | } | ||
33 | |||
34 | u32 btrfs_crc32c(u32 crc, const void *address, unsigned int length) | ||
35 | { | ||
36 | struct { | ||
37 | struct shash_desc shash; | ||
38 | char ctx[crypto_shash_descsize(tfm)]; | ||
39 | } desc; | ||
40 | int err; | ||
41 | |||
42 | desc.shash.tfm = tfm; | ||
43 | desc.shash.flags = 0; | ||
44 | *(u32 *)desc.ctx = crc; | ||
45 | |||
46 | err = crypto_shash_update(&desc.shash, address, length); | ||
47 | BUG_ON(err); | ||
48 | |||
49 | return *(u32 *)desc.ctx; | ||
50 | } | ||
diff --git a/fs/btrfs/hash.h b/fs/btrfs/hash.h index 1d982812ab67..118a2316e5d3 100644 --- a/fs/btrfs/hash.h +++ b/fs/btrfs/hash.h | |||
@@ -19,10 +19,15 @@ | |||
19 | #ifndef __HASH__ | 19 | #ifndef __HASH__ |
20 | #define __HASH__ | 20 | #define __HASH__ |
21 | 21 | ||
22 | #include <linux/crc32c.h> | 22 | int __init btrfs_hash_init(void); |
23 | |||
24 | void btrfs_hash_exit(void); | ||
25 | |||
26 | u32 btrfs_crc32c(u32 crc, const void *address, unsigned int length); | ||
27 | |||
23 | static inline u64 btrfs_name_hash(const char *name, int len) | 28 | static inline u64 btrfs_name_hash(const char *name, int len) |
24 | { | 29 | { |
25 | return crc32c((u32)~1, name, len); | 30 | return btrfs_crc32c((u32)~1, name, len); |
26 | } | 31 | } |
27 | 32 | ||
28 | /* | 33 | /* |
@@ -31,7 +36,7 @@ static inline u64 btrfs_name_hash(const char *name, int len) | |||
31 | static inline u64 btrfs_extref_hash(u64 parent_objectid, const char *name, | 36 | static inline u64 btrfs_extref_hash(u64 parent_objectid, const char *name, |
32 | int len) | 37 | int len) |
33 | { | 38 | { |
34 | return (u64) crc32c(parent_objectid, name, len); | 39 | return (u64) btrfs_crc32c(parent_objectid, name, len); |
35 | } | 40 | } |
36 | 41 | ||
37 | #endif | 42 | #endif |
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 461e41cb8ca7..f44cc6a0eb27 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #include "transaction.h" | 48 | #include "transaction.h" |
49 | #include "btrfs_inode.h" | 49 | #include "btrfs_inode.h" |
50 | #include "print-tree.h" | 50 | #include "print-tree.h" |
51 | #include "hash.h" | ||
51 | #include "props.h" | 52 | #include "props.h" |
52 | #include "xattr.h" | 53 | #include "xattr.h" |
53 | #include "volumes.h" | 54 | #include "volumes.h" |
@@ -1866,11 +1867,15 @@ static int __init init_btrfs_fs(void) | |||
1866 | { | 1867 | { |
1867 | int err; | 1868 | int err; |
1868 | 1869 | ||
1870 | err = btrfs_hash_init(); | ||
1871 | if (err) | ||
1872 | return err; | ||
1873 | |||
1869 | btrfs_props_init(); | 1874 | btrfs_props_init(); |
1870 | 1875 | ||
1871 | err = btrfs_init_sysfs(); | 1876 | err = btrfs_init_sysfs(); |
1872 | if (err) | 1877 | if (err) |
1873 | return err; | 1878 | goto free_hash; |
1874 | 1879 | ||
1875 | btrfs_init_compress(); | 1880 | btrfs_init_compress(); |
1876 | 1881 | ||
@@ -1945,6 +1950,8 @@ free_cachep: | |||
1945 | free_compress: | 1950 | free_compress: |
1946 | btrfs_exit_compress(); | 1951 | btrfs_exit_compress(); |
1947 | btrfs_exit_sysfs(); | 1952 | btrfs_exit_sysfs(); |
1953 | free_hash: | ||
1954 | btrfs_hash_exit(); | ||
1948 | return err; | 1955 | return err; |
1949 | } | 1956 | } |
1950 | 1957 | ||
@@ -1963,6 +1970,7 @@ static void __exit exit_btrfs_fs(void) | |||
1963 | btrfs_exit_sysfs(); | 1970 | btrfs_exit_sysfs(); |
1964 | btrfs_cleanup_fs_uuids(); | 1971 | btrfs_cleanup_fs_uuids(); |
1965 | btrfs_exit_compress(); | 1972 | btrfs_exit_compress(); |
1973 | btrfs_hash_exit(); | ||
1966 | } | 1974 | } |
1967 | 1975 | ||
1968 | module_init(init_btrfs_fs) | 1976 | module_init(init_btrfs_fs) |