@@ -2,6 +2,7 @@ config BTRFS_FS
tristate "Btrfs filesystem support"
select CRYPTO
select CRYPTO_CRC32C
+ select CRYPTO_XXH32
select ZLIB_INFLATE
select ZLIB_DEFLATE
select LZO_COMPRESS
@@ -88,3 +89,24 @@ config BTRFS_ASSERT
any of the assertions trip. This is meant for btrfs developers only.
If unsure, say N.
+
+choice
+ prompt "choose checksum algorithm"
+ default BTRFS_CRC32C
+ help
+ This option allows to select a checksum algorithm
+
+config BTRFS_CRC32C
+ depends on CRYPTO_CRC32C
+ bool "BTRFS_CRC32C"
+ help
+ crc32c
+
+config BTRFS_XXH32
+ depends on CRYPTO_XXH32
+ bool "BTRFS_XXH32"
+ help
+ xxhash
+
+endchoice
+
@@ -41,6 +41,7 @@
#include "compression.h"
#include "extent_io.h"
#include "extent_map.h"
+#include "hash.h"
struct compressed_bio {
/* number of bios pending for this compressed extent */
@@ -114,17 +115,16 @@ static int check_compressed_csum(struct inode *inode,
char *kaddr;
u32 csum;
u32 *cb_sum = &cb->sums;
+ struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
return 0;
for (i = 0; i < cb->nr_pages; i++) {
page = cb->compressed_pages[i];
- csum = ~(u32)0;
kaddr = kmap_atomic(page);
- csum = btrfs_csum_data(kaddr, csum, PAGE_CACHE_SIZE);
- btrfs_csum_final(csum, (char *)&csum);
+ btrfs_csum_data(fs_info, kaddr, PAGE_CACHE_SIZE, (char *)&csum);
kunmap_atomic(kaddr);
if (csum != *cb_sum) {
@@ -176,12 +176,16 @@ struct btrfs_ordered_sum;
/* 32 bytes in various csum fields */
#define BTRFS_CSUM_SIZE 32
-/* csum types */
+/*
+ * csum types,
+ * - 4 bytes for CRC32(crc32c)
+ * - 4 bytes for XXH32(xxhash)
+ */
#define BTRFS_CSUM_TYPE_CRC32 0
+#define BTRFS_CSUM_TYPE_XXH32 1
-static int btrfs_csum_sizes[] = { 4, 0 };
+static int btrfs_csum_sizes[] = { 4, 4, 0 };
-/* four bytes for CRC32 */
#define BTRFS_EMPTY_DIR_SIZE 0
/* spefic to btrfs_map_block(), therefore not in include/linux/blk_types.h */
@@ -1688,6 +1692,8 @@ struct btrfs_fs_info {
struct semaphore uuid_tree_rescan_sem;
unsigned int update_uuid_tree_gen:1;
+
+ struct crypto_shash *tfm;
};
struct btrfs_subvolume_writers {
@@ -87,7 +87,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
key.objectid = objectid;
btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
- key.offset = btrfs_name_hash(name, name_len);
+ key.offset = btrfs_name_hash(root->fs_info, name, name_len);
data_size = sizeof(*dir_item) + name_len + data_len;
dir_item = insert_with_overflow(trans, root, path, &key, data_size,
@@ -138,7 +138,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
key.objectid = btrfs_ino(dir);
btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
- key.offset = btrfs_name_hash(name, name_len);
+ key.offset = btrfs_name_hash(root->fs_info, name, name_len);
path = btrfs_alloc_path();
if (!path)
@@ -206,7 +206,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
key.objectid = dir;
btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
- key.offset = btrfs_name_hash(name, name_len);
+ key.offset = btrfs_name_hash(root->fs_info, name, name_len);
ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
if (ret < 0)
@@ -235,7 +235,7 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
key.objectid = dir;
btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
- key.offset = btrfs_name_hash(name, name_len);
+ key.offset = btrfs_name_hash(root->fs_info, name, name_len);
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
@@ -368,7 +368,7 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
key.objectid = dir;
btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
- key.offset = btrfs_name_hash(name, name_len);
+ key.offset = btrfs_name_hash(root->fs_info, name, name_len);
ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
if (ret < 0)
return ERR_PTR(ret);
@@ -242,34 +242,24 @@ out:
return em;
}
-u32 btrfs_csum_data(char *data, u32 seed, size_t len)
+int btrfs_gen_csum_tree_block(struct btrfs_fs_info *info, struct extent_buffer *buf, char *result)
{
- return btrfs_crc32c(seed, data, len);
-}
-
-void btrfs_csum_final(u32 crc, char *result)
-{
- put_unaligned_le32(~crc, result);
-}
-
-/*
- * compute the csum for a btree block, and either verify it or write it
- * into the csum field of the block.
- */
-static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
- int verify)
-{
- u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
- char *result = NULL;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(info->tfm)];
+ } desc;
unsigned long len;
unsigned long cur_len;
unsigned long offset = BTRFS_CSUM_SIZE;
- char *kaddr;
unsigned long map_start;
unsigned long map_len;
+ char *kaddr;
int err;
- u32 crc = ~(u32)0;
- unsigned long inline_result;
+
+ desc.shash.tfm = info->tfm;
+ desc.shash.flags = 0;
+
+ crypto_shash_init(&desc.shash);
len = buf->len - offset;
while (len > 0) {
@@ -278,11 +268,27 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
if (err)
return 1;
cur_len = min(len, map_len - (offset - map_start));
- crc = btrfs_csum_data(kaddr + offset - map_start,
- crc, cur_len);
+ crypto_shash_update(&desc.shash,
+ kaddr + offset - map_start, cur_len);
len -= cur_len;
offset += cur_len;
}
+
+ return crypto_shash_final(&desc.shash, result);
+}
+
+/*
+ * compute the csum for a btree block, and either verify it or write it
+ * into the csum field of the block.
+ */
+static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
+ int verify)
+{
+ u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
+ char *result = NULL;
+ unsigned long inline_result;
+ int err;
+
if (csum_size > sizeof(inline_result)) {
result = kzalloc(csum_size * sizeof(char), GFP_NOFS);
if (!result)
@@ -291,7 +297,12 @@ static int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
result = (char *)&inline_result;
}
- btrfs_csum_final(crc, result);
+ err = btrfs_gen_csum_tree_block(root->fs_info, buf, result);
+ if (err) {
+ if (result != (char *)&inline_result)
+ kfree(result);
+ return 1;
+ }
if (verify) {
if (memcmp_extent_buffer(buf, result, 0, csum_size)) {
@@ -376,43 +387,40 @@ out:
* Return 0 if the superblock checksum type matches the checksum value of that
* algorithm. Pass the raw disk superblock data.
*/
-static int btrfs_check_super_csum(char *raw_disk_sb)
+static int btrfs_check_super_csum(struct btrfs_fs_info *info, char *raw_disk_sb)
{
struct btrfs_super_block *disk_sb =
(struct btrfs_super_block *)raw_disk_sb;
u16 csum_type = btrfs_super_csum_type(disk_sb);
int ret = 0;
-
- if (csum_type == BTRFS_CSUM_TYPE_CRC32) {
- u32 crc = ~(u32)0;
- const int csum_size = sizeof(crc);
- char result[csum_size];
-
- /*
- * The super_block structure does not span the whole
- * BTRFS_SUPER_INFO_SIZE range, we expect that the unused space
- * is filled with zeros and is included in the checkum.
- */
- crc = btrfs_csum_data(raw_disk_sb + BTRFS_CSUM_SIZE,
- crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, result);
-
- if (memcmp(raw_disk_sb, result, csum_size))
- ret = 1;
-
- if (ret && btrfs_super_generation(disk_sb) < 10) {
- printk(KERN_WARNING
- "BTRFS: super block crcs don't match, older mkfs detected\n");
- ret = 0;
- }
- }
+ const int csum_size = btrfs_super_csum_size(disk_sb);
+ char result[csum_size];
if (csum_type >= ARRAY_SIZE(btrfs_csum_sizes)) {
printk(KERN_ERR "BTRFS: unsupported checksum algorithm %u\n",
csum_type);
ret = 1;
+ goto out;
}
+ /*
+ * The super_block structure does not span the whole
+ * BTRFS_SUPER_INFO_SIZE range, we expect that the unused space
+ * is filled with zeros and is included in the checkum.
+ */
+ btrfs_csum_data(info, raw_disk_sb + BTRFS_CSUM_SIZE,
+ BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE, result);
+
+ if (memcmp(raw_disk_sb, result, csum_size))
+ ret = 1;
+
+ if (ret && btrfs_super_generation(disk_sb) < 10) {
+ printk(KERN_WARNING
+ "BTRFS: super block crcs don't match, older mkfs detected\n");
+ ret = 0;
+ }
+
+out:
return ret;
}
@@ -2389,11 +2397,17 @@ int open_ctree(struct super_block *sb,
goto fail_alloc;
}
+ if (btrfs_hash_init(fs_info)) {
+ err = -EINVAL;
+ btrfs_err(fs_info, "BTRFS: hash init error");
+ goto fail_alloc;
+ }
+
/*
* We want to check superblock checksum, the type is stored inside.
* Pass the whole disk block of size BTRFS_SUPER_INFO_SIZE (4k).
*/
- if (btrfs_check_super_csum(bh->b_data)) {
+ if (btrfs_check_super_csum(fs_info, bh->b_data)) {
printk(KERN_ERR "BTRFS: superblock checksum mismatch\n");
err = -EINVAL;
goto fail_alloc;
@@ -2990,6 +3004,7 @@ fail_tree_roots:
fail_sb_buffer:
btrfs_stop_all_workers(fs_info);
fail_alloc:
+ btrfs_hash_exit(fs_info);
fail_iput:
btrfs_mapping_tree_free(&fs_info->mapping_tree);
@@ -3110,7 +3125,6 @@ static int write_dev_supers(struct btrfs_device *device,
int i;
int ret;
int errors = 0;
- u32 crc;
u64 bytenr;
if (max_mirrors == 0)
@@ -3141,12 +3155,8 @@ static int write_dev_supers(struct btrfs_device *device,
} else {
btrfs_set_super_bytenr(sb, bytenr);
- crc = ~(u32)0;
- crc = btrfs_csum_data((char *)sb +
- BTRFS_CSUM_SIZE, crc,
- BTRFS_SUPER_INFO_SIZE -
- BTRFS_CSUM_SIZE);
- btrfs_csum_final(crc, sb->csum);
+ btrfs_csum_data(device->dev_root->fs_info, (char *)sb + BTRFS_CSUM_SIZE,
+ BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE, sb->csum);
/*
* one reference for us, and we leave it for the
@@ -3658,6 +3668,8 @@ int close_ctree(struct btrfs_root *root)
btrfs_free_block_rsv(root, root->orphan_block_rsv);
root->orphan_block_rsv = NULL;
+ btrfs_hash_exit(fs_info);
+
return 0;
}
@@ -115,8 +115,6 @@ int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid,
int atomic);
int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid);
-u32 btrfs_csum_data(char *data, u32 seed, size_t len);
-void btrfs_csum_final(u32 crc, char *result);
int btrfs_bio_wq_end_io(struct btrfs_fs_info *info, struct bio *bio,
int metadata);
int btrfs_wq_submit_bio(struct btrfs_fs_info *fs_info, struct inode *inode,
@@ -36,6 +36,7 @@
#include "free-space-cache.h"
#include "math.h"
#include "sysfs.h"
+#include "hash.h"
#undef SCRAMBLE_DELAYED_REFS
@@ -1067,26 +1068,44 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
}
#endif
-static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
+static u64 hash_extent_data_ref(struct btrfs_fs_info *info, u64 root_objectid, u64 owner, u64 offset)
{
- u32 high_crc = ~(u32)0;
- u32 low_crc = ~(u32)0;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(info->tfm)];
+ } desc;
+ int err;
+ u32 high_crc;
+ u32 low_crc;
__le64 lenum;
+ desc.shash.tfm = info->tfm;
+ desc.shash.flags = 0;
+
+ /* high part */
lenum = cpu_to_le64(root_objectid);
- high_crc = btrfs_crc32c(high_crc, &lenum, sizeof(lenum));
+ crypto_shash_digest(&desc.shash, (u8 *)&lenum, sizeof(lenum), (char *)&high_crc);
+ high_crc = le32_to_cpu(high_crc);
+
+ /* low part */
+ crypto_shash_init(&desc.shash);
+
lenum = cpu_to_le64(owner);
- low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
+ crypto_shash_update(&desc.shash, (u8 *)&lenum, sizeof(lenum));
+
lenum = cpu_to_le64(offset);
- low_crc = btrfs_crc32c(low_crc, &lenum, sizeof(lenum));
+ crypto_shash_update(&desc.shash, (u8 *)&lenum, sizeof(lenum));
+
+ err = crypto_shash_final(&desc.shash, (char *)&low_crc);
+ low_crc = le32_to_cpu(low_crc);
return ((u64)high_crc << 31) ^ (u64)low_crc;
}
-static u64 hash_extent_data_ref_item(struct extent_buffer *leaf,
+static u64 hash_extent_data_ref_item(struct btrfs_fs_info *info, struct extent_buffer *leaf,
struct btrfs_extent_data_ref *ref)
{
- return hash_extent_data_ref(btrfs_extent_data_ref_root(leaf, ref),
+ return hash_extent_data_ref(info, btrfs_extent_data_ref_root(leaf, ref),
btrfs_extent_data_ref_objectid(leaf, ref),
btrfs_extent_data_ref_offset(leaf, ref));
}
@@ -1123,7 +1142,7 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans,
key.offset = parent;
} else {
key.type = BTRFS_EXTENT_DATA_REF_KEY;
- key.offset = hash_extent_data_ref(root_objectid,
+ key.offset = hash_extent_data_ref(root->fs_info, root_objectid,
owner, offset);
}
again:
@@ -1209,7 +1228,7 @@ static noinline int insert_extent_data_ref(struct btrfs_trans_handle *trans,
size = sizeof(struct btrfs_shared_data_ref);
} else {
key.type = BTRFS_EXTENT_DATA_REF_KEY;
- key.offset = hash_extent_data_ref(root_objectid,
+ key.offset = hash_extent_data_ref(root->fs_info, root_objectid,
owner, offset);
size = sizeof(struct btrfs_extent_data_ref);
}
@@ -1612,8 +1631,8 @@ again:
err = 0;
break;
}
- if (hash_extent_data_ref_item(leaf, dref) <
- hash_extent_data_ref(root_objectid, owner, offset))
+ if (hash_extent_data_ref_item(root->fs_info, leaf, dref) <
+ hash_extent_data_ref(root->fs_info, root_objectid, owner, offset))
break;
} else {
u64 ref_offset;
@@ -25,6 +25,7 @@
#include "transaction.h"
#include "volumes.h"
#include "print-tree.h"
+#include "hash.h"
#define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - \
sizeof(struct btrfs_item) * 2) / \
@@ -491,13 +492,9 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
}
data = kmap_atomic(bvec->bv_page);
- sums->sums[index] = ~(u32)0;
- sums->sums[index] = btrfs_csum_data(data + bvec->bv_offset,
- sums->sums[index],
- bvec->bv_len);
+ btrfs_csum_data(root->fs_info, data + bvec->bv_offset,
+ bvec->bv_len, (char *)(sums->sums + index));
kunmap_atomic(data);
- btrfs_csum_final(sums->sums[index],
- (char *)(sums->sums + index));
bio_index++;
index++;
@@ -27,6 +27,7 @@
#include "disk-io.h"
#include "extent_io.h"
#include "inode-map.h"
+#include "hash.h"
#define BITS_PER_BITMAP (PAGE_CACHE_SIZE * 8)
#define MAX_CACHE_BYTES_PER_GIG (32 * 1024)
@@ -418,7 +419,7 @@ static int io_ctl_check_generation(struct io_ctl *io_ctl, u64 generation)
static void io_ctl_set_crc(struct io_ctl *io_ctl, int index)
{
u32 *tmp;
- u32 crc = ~(u32)0;
+ u32 crc;
unsigned offset = 0;
if (!io_ctl->check_crcs) {
@@ -429,9 +430,8 @@ static void io_ctl_set_crc(struct io_ctl *io_ctl, int index)
if (index == 0)
offset = sizeof(u32) * io_ctl->num_pages;
- crc = btrfs_csum_data(io_ctl->orig + offset, crc,
- PAGE_CACHE_SIZE - offset);
- btrfs_csum_final(crc, (char *)&crc);
+ btrfs_csum_data(io_ctl->root->fs_info, io_ctl->orig + offset,
+ PAGE_CACHE_SIZE - offset, (char *)&crc);
io_ctl_unmap_page(io_ctl);
tmp = kmap(io_ctl->pages[0]);
tmp += index;
@@ -442,7 +442,7 @@ static void io_ctl_set_crc(struct io_ctl *io_ctl, int index)
static int io_ctl_check_crc(struct io_ctl *io_ctl, int index)
{
u32 *tmp, val;
- u32 crc = ~(u32)0;
+ u32 crc;
unsigned offset = 0;
if (!io_ctl->check_crcs) {
@@ -459,9 +459,8 @@ static int io_ctl_check_crc(struct io_ctl *io_ctl, int index)
kunmap(io_ctl->pages[0]);
io_ctl_map_page(io_ctl, 0);
- crc = btrfs_csum_data(io_ctl->orig + offset, crc,
- PAGE_CACHE_SIZE - offset);
- btrfs_csum_final(crc, (char *)&crc);
+ btrfs_csum_data(io_ctl->root->fs_info, io_ctl->orig + offset,
+ PAGE_CACHE_SIZE - offset, (char *)&crc);
if (val != crc) {
printk_ratelimited(KERN_ERR "BTRFS: csum mismatch on free "
"space cache\n");
@@ -11,40 +11,85 @@
* General Public License for more details.
*/
-#include <crypto/hash.h>
-#include <linux/err.h>
#include "hash.h"
-static struct crypto_shash *tfm;
-
-int __init btrfs_hash_init(void)
+int btrfs_hash_init(struct btrfs_fs_info *info)
{
+ struct crypto_shash *tfm = NULL;
+ int ret = -EINVAL;
+
+#if defined(CONFIG_BTRFS_CRC32C)
tfm = crypto_alloc_shash("crc32c", 0, 0);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
+ ret = 0;
+#elif defined(CONFIG_BTRFS_XXH32)
+ tfm = crypto_alloc_shash("xxh32", 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+ ret = 0;
+#endif
- return 0;
+ info->tfm = tfm;
+ return ret;
}
-void btrfs_hash_exit(void)
+void btrfs_hash_exit(struct btrfs_fs_info *info)
{
- crypto_free_shash(tfm);
+ crypto_free_shash(info->tfm);
}
-u32 btrfs_crc32c(u32 crc, const void *address, unsigned int length)
+int btrfs_csum_data(struct btrfs_fs_info *info, const void *address, unsigned int length, u8 *out)
{
struct {
struct shash_desc shash;
- char ctx[crypto_shash_descsize(tfm)];
+ char ctx[crypto_shash_descsize(info->tfm)];
} desc;
int err;
- desc.shash.tfm = tfm;
+ desc.shash.tfm = info->tfm;
desc.shash.flags = 0;
- *(u32 *)desc.ctx = crc;
- err = crypto_shash_update(&desc.shash, address, length);
- BUG_ON(err);
+ err = crypto_shash_digest(&desc.shash, address, length, out);
+ ASSERT(!err);
+
+ return err;
+}
+
+u64 btrfs_name_hash(struct btrfs_fs_info *info, const char *name, int len)
+{
+ u32 hash;
+
+ btrfs_csum_data(info, name, len, (char *)&hash);
- return *(u32 *)desc.ctx;
+ return le32_to_cpu(hash);
}
+
+/*
+ * Figure the key offset of an extended inode ref
+ */
+u64 btrfs_extref_hash(struct btrfs_fs_info *info, u64 parent_objectid, const char *name, int len)
+{
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(info->tfm)];
+ } desc;
+ int err;
+ u32 hash;
+
+ desc.shash.tfm = info->tfm;
+ desc.shash.flags = 0;
+
+ crypto_shash_init(&desc.shash);
+
+ crypto_shash_update(&desc.shash, (char *)&parent_objectid, sizeof(u64));
+
+ crypto_shash_update(&desc.shash, name, len);
+
+ err = crypto_shash_final(&desc.shash, (char *)&hash);
+ ASSERT(!err);
+
+ return le32_to_cpu(hash);
+}
+
+
@@ -19,24 +19,22 @@
#ifndef __HASH__
#define __HASH__
-int __init btrfs_hash_init(void);
+#include <crypto/hash.h>
+#include <linux/err.h>
+#include <crypto/xxhash.h>
+#include "ctree.h"
-void btrfs_hash_exit(void);
+int btrfs_hash_init(struct btrfs_fs_info *info);
-u32 btrfs_crc32c(u32 crc, const void *address, unsigned int length);
+void btrfs_hash_exit(struct btrfs_fs_info *info);
-static inline u64 btrfs_name_hash(const char *name, int len)
-{
- return btrfs_crc32c((u32)~1, name, len);
-}
+int btrfs_csum_data(struct btrfs_fs_info *info, const void *address, unsigned int length, u8 *out);
+
+u64 btrfs_name_hash(struct btrfs_fs_info *info, const char *name, int len);
/*
* Figure the key offset of an extended inode ref
*/
-static inline u64 btrfs_extref_hash(u64 parent_objectid, const char *name,
- int len)
-{
- return (u64) btrfs_crc32c(parent_objectid, name, len);
-}
+u64 btrfs_extref_hash(struct btrfs_fs_info *info, u64 parent_objectid, const char *name, int len);
#endif
@@ -106,7 +106,7 @@ btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
key.objectid = inode_objectid;
key.type = BTRFS_INODE_EXTREF_KEY;
- key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
+ key.offset = btrfs_extref_hash(root->fs_info, ref_objectid, name, name_len);
ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
if (ret < 0)
@@ -136,7 +136,7 @@ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
key.objectid = inode_objectid;
btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY);
- key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
+ key.offset = btrfs_extref_hash(root->fs_info, ref_objectid, name, name_len);
path = btrfs_alloc_path();
if (!path)
@@ -283,7 +283,7 @@ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
key.objectid = inode_objectid;
key.type = BTRFS_INODE_EXTREF_KEY;
- key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
+ key.offset = btrfs_extref_hash(root->fs_info, ref_objectid, name, name_len);
path = btrfs_alloc_path();
if (!path)
@@ -2825,7 +2825,7 @@ static int btrfs_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
char *kaddr;
struct btrfs_root *root = BTRFS_I(inode)->root;
u32 csum_expected;
- u32 csum = ~(u32)0;
+ u32 csum;
static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
@@ -2848,8 +2848,7 @@ static int btrfs_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
csum_expected = *(((u32 *)io_bio->csum) + phy_offset);
kaddr = kmap_atomic(page);
- csum = btrfs_csum_data(kaddr + offset, csum, end - start + 1);
- btrfs_csum_final(csum, (char *)&csum);
+ btrfs_csum_data(root->fs_info, kaddr + offset, end - start + 1, (char *)&csum);
if (csum != csum_expected)
goto zeroit;
@@ -3307,9 +3306,9 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
int scanned = 0;
if (!xattr_access) {
- xattr_access = btrfs_name_hash(POSIX_ACL_XATTR_ACCESS,
+ xattr_access = btrfs_name_hash(leaf->fs_info, POSIX_ACL_XATTR_ACCESS,
strlen(POSIX_ACL_XATTR_ACCESS));
- xattr_default = btrfs_name_hash(POSIX_ACL_XATTR_DEFAULT,
+ xattr_default = btrfs_name_hash(leaf->fs_info, POSIX_ACL_XATTR_DEFAULT,
strlen(POSIX_ACL_XATTR_DEFAULT));
}
@@ -7017,14 +7016,13 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
struct page *page = bvec->bv_page;
char *kaddr;
- u32 csum = ~(u32)0;
+ u32 csum;
unsigned long flags;
local_irq_save(flags);
kaddr = kmap_atomic(page);
- csum = btrfs_csum_data(kaddr + bvec->bv_offset,
- csum, bvec->bv_len);
- btrfs_csum_final(csum, (char *)&csum);
+ btrfs_csum_data(root->fs_info, kaddr + bvec->bv_offset,
+ bvec->bv_len, (char *)&csum);
kunmap_atomic(kaddr);
local_irq_restore(flags);
@@ -23,6 +23,8 @@
#include "transaction.h"
#include "xattr.h"
+static struct crypto_shash *tfm;
+
#define BTRFS_PROP_HANDLERS_HT_BITS 8
static DEFINE_HASHTABLE(prop_handlers_ht, BTRFS_PROP_HANDLERS_HT_BITS);
@@ -54,17 +56,46 @@ static struct prop_handler prop_handlers[] = {
}
};
-void __init btrfs_props_init(void)
+static u64 btrfs_prop_name_hash(const char *name, unsigned long len)
+{
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(tfm)];
+ } desc;
+ int err;
+ u32 h;
+
+ desc.shash.tfm = tfm;
+ desc.shash.flags = 0;
+
+ err = crypto_shash_digest(&desc.shash, name, len, (char *)&h);
+ ASSERT(!err);
+
+ return le32_to_cpu(h);
+}
+
+int __init btrfs_props_init(void)
{
struct prop_handler *p;
+ tfm = crypto_alloc_shash("xxhash", 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
hash_init(prop_handlers_ht);
for (p = &prop_handlers[0]; p->xattr_name; p++) {
- u64 h = btrfs_name_hash(p->xattr_name, strlen(p->xattr_name));
+ u64 h = btrfs_prop_name_hash(p->xattr_name, strlen(p->xattr_name));
hash_add(prop_handlers_ht, &p->node, h);
}
+
+ return 0;
+}
+
+void btrfs_props_exit(void)
+{
+ crypto_free_shash(tfm);
}
static const struct hlist_head *find_prop_handlers_by_hash(const u64 hash)
@@ -85,7 +116,7 @@ find_prop_handler(const char *name,
struct prop_handler *h;
if (!handlers) {
- u64 hash = btrfs_name_hash(name, strlen(name));
+ u64 hash = btrfs_prop_name_hash(name, strlen(name));
handlers = find_prop_handlers_by_hash(hash);
if (!handlers)
@@ -21,7 +21,8 @@
#include "ctree.h"
-void __init btrfs_props_init(void);
+int __init btrfs_props_init(void);
+void btrfs_props_exit(void);
int btrfs_set_prop(struct inode *inode,
const char *name,
@@ -29,6 +29,7 @@
#include "check-integrity.h"
#include "rcu-string.h"
#include "raid56.h"
+#include "hash.h"
/*
* This is only the first step towards a full-features scrub. It reads all
@@ -1368,8 +1369,11 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
{
int page_num;
u8 calculated_csum[BTRFS_CSUM_SIZE];
- u32 crc = ~(u32)0;
void *mapped_buffer;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(fs_info->tfm)];
+ } desc;
WARN_ON(!sblock->pagev[0]->page);
if (is_metadata) {
@@ -1395,13 +1399,19 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
mapped_buffer = kmap_atomic(sblock->pagev[0]->page);
}
+ desc.shash.tfm = fs_info->tfm;
+ desc.shash.flags = 0;
+
+ crypto_shash_init(&desc.shash);
+
for (page_num = 0;;) {
if (page_num == 0 && is_metadata)
- crc = btrfs_csum_data(
+ crypto_shash_update(&desc.shash,
((u8 *)mapped_buffer) + BTRFS_CSUM_SIZE,
- crc, PAGE_SIZE - BTRFS_CSUM_SIZE);
+ PAGE_SIZE - BTRFS_CSUM_SIZE);
else
- crc = btrfs_csum_data(mapped_buffer, crc, PAGE_SIZE);
+ crypto_shash_update(&desc.shash, mapped_buffer,
+ PAGE_SIZE);
kunmap_atomic(mapped_buffer);
page_num++;
@@ -1412,7 +1422,8 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
mapped_buffer = kmap_atomic(sblock->pagev[page_num]->page);
}
- btrfs_csum_final(crc, calculated_csum);
+ crypto_shash_final(&desc.shash, calculated_csum);
+
if (memcmp(calculated_csum, csum, csum_size))
sblock->checksum_error = 1;
}
@@ -1676,10 +1687,14 @@ static int scrub_checksum_data(struct scrub_block *sblock)
u8 *on_disk_csum;
struct page *page;
void *buffer;
- u32 crc = ~(u32)0;
int fail = 0;
u64 len;
int index;
+ struct btrfs_fs_info *fs_info = sctx->dev_root->fs_info;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(fs_info->tfm)];
+ } desc;
BUG_ON(sblock->page_count < 1);
if (!sblock->pagev[0]->have_csum)
@@ -1691,10 +1706,16 @@ static int scrub_checksum_data(struct scrub_block *sblock)
len = sctx->sectorsize;
index = 0;
+
+ desc.shash.tfm = fs_info->tfm;
+ desc.shash.flags = 0;
+
+ crypto_shash_init(&desc.shash);
+
for (;;) {
u64 l = min_t(u64, len, PAGE_SIZE);
- crc = btrfs_csum_data(buffer, crc, l);
+ crypto_shash_update(&desc.shash, buffer, l);
kunmap_atomic(buffer);
len -= l;
if (len == 0)
@@ -1706,7 +1727,8 @@ static int scrub_checksum_data(struct scrub_block *sblock)
buffer = kmap_atomic(page);
}
- btrfs_csum_final(crc, csum);
+ crypto_shash_final(&desc.shash, csum);
+
if (memcmp(csum, on_disk_csum, sctx->csum_size))
fail = 1;
@@ -1725,11 +1747,14 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock)
void *mapped_buffer;
u64 mapped_size;
void *p;
- u32 crc = ~(u32)0;
int fail = 0;
int crc_fail = 0;
u64 len;
int index;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(fs_info->tfm)];
+ } desc;
BUG_ON(sblock->page_count < 1);
page = sblock->pagev[0]->page;
@@ -1761,10 +1786,16 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock)
mapped_size = PAGE_SIZE - BTRFS_CSUM_SIZE;
p = ((u8 *)mapped_buffer) + BTRFS_CSUM_SIZE;
index = 0;
+
+ desc.shash.tfm = fs_info->tfm;
+ desc.shash.flags = 0;
+
+ crypto_shash_init(&desc.shash);
+
for (;;) {
u64 l = min_t(u64, len, mapped_size);
- crc = btrfs_csum_data(p, crc, l);
+ crypto_shash_update(&desc.shash, p, l);
kunmap_atomic(mapped_buffer);
len -= l;
if (len == 0)
@@ -1778,7 +1809,8 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock)
p = mapped_buffer;
}
- btrfs_csum_final(crc, calculated_csum);
+ crypto_shash_final(&desc.shash, calculated_csum);
+
if (memcmp(calculated_csum, on_disk_csum, sctx->csum_size))
++crc_fail;
@@ -1797,11 +1829,14 @@ static int scrub_checksum_super(struct scrub_block *sblock)
void *mapped_buffer;
u64 mapped_size;
void *p;
- u32 crc = ~(u32)0;
int fail_gen = 0;
int fail_cor = 0;
u64 len;
int index;
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(fs_info->tfm)];
+ } desc;
BUG_ON(sblock->page_count < 1);
page = sblock->pagev[0]->page;
@@ -1822,10 +1857,16 @@ static int scrub_checksum_super(struct scrub_block *sblock)
mapped_size = PAGE_SIZE - BTRFS_CSUM_SIZE;
p = ((u8 *)mapped_buffer) + BTRFS_CSUM_SIZE;
index = 0;
+
+ desc.shash.tfm = fs_info->tfm;
+ desc.shash.flags = 0;
+
+ crypto_shash_init(&desc.shash);
+
for (;;) {
u64 l = min_t(u64, len, mapped_size);
- crc = btrfs_csum_data(p, crc, l);
+ crypto_shash_update(&desc.shash, p, l);
kunmap_atomic(mapped_buffer);
len -= l;
if (len == 0)
@@ -1839,7 +1880,8 @@ static int scrub_checksum_super(struct scrub_block *sblock)
p = mapped_buffer;
}
- btrfs_csum_final(crc, calculated_csum);
+ crypto_shash_final(&desc.shash, calculated_csum);
+
if (memcmp(calculated_csum, on_disk_csum, sctx->csum_size))
++fail_cor;
@@ -664,13 +664,16 @@ static int send_cmd(struct send_ctx *sctx)
int ret;
struct btrfs_cmd_header *hdr;
u32 crc;
+ char *result;
hdr = (struct btrfs_cmd_header *)sctx->send_buf;
hdr->len = cpu_to_le32(sctx->send_size - sizeof(*hdr));
hdr->crc = 0;
- crc = btrfs_crc32c(0, (unsigned char *)sctx->send_buf, sctx->send_size);
- hdr->crc = cpu_to_le32(crc);
+ result = (char *)&crc;
+ btrfs_csum_data(sctx->send_root->fs_info, (unsigned char *)sctx->send_buf, sctx->send_size, result);
+ /* crc is already convert to __le32. */
+ hdr->crc = crc;
ret = write_buf(sctx->send_filp, sctx->send_buf, sctx->send_size,
&sctx->send_off);
@@ -880,6 +880,7 @@ static struct dentry *get_default_root(struct super_block *sb,
* it's always been there, but don't freak out, just try and
* mount to root most subvolume.
*/
+ btrfs_info(fs_info, "default NOT FOUND, go for fsroot\n");
btrfs_free_path(path);
dir_id = BTRFS_FIRST_FREE_OBJECTID;
new_root = fs_info->fs_root;
@@ -1903,12 +1904,10 @@ static int __init init_btrfs_fs(void)
{
int err;
- err = btrfs_hash_init();
+ err = btrfs_props_init();
if (err)
return err;
- btrfs_props_init();
-
err = btrfs_init_sysfs();
if (err)
goto free_hash;
@@ -1987,7 +1986,7 @@ free_compress:
btrfs_exit_compress();
btrfs_exit_sysfs();
free_hash:
- btrfs_hash_exit();
+ btrfs_props_exit();
return err;
}
@@ -2006,7 +2005,7 @@ static void __exit exit_btrfs_fs(void)
btrfs_exit_sysfs();
btrfs_cleanup_fs_uuids();
btrfs_exit_compress();
- btrfs_hash_exit();
+ btrfs_props_exit();
}
late_initcall(init_btrfs_fs);
@@ -1014,7 +1014,7 @@ again:
search_key.objectid = inode_objectid;
search_key.type = BTRFS_INODE_EXTREF_KEY;
- search_key.offset = btrfs_extref_hash(parent_objectid,
+ search_key.offset = btrfs_extref_hash(root->fs_info, parent_objectid,
victim_name,
victim_name_len);
ret = 0;
"xxHash is an extremely fast non-cryptographic Hash algorithm, working at speeds close to RAM limits."[1] And xxhash is 32-bits hash, same as crc32. This modifies btrfs's checksum API a bit and adopts xxhash as an alternative checksum algorithm. Note: We needs to update btrfs-progs side as well to set it up. [1]: https://code.google.com/p/xxhash/ Signed-off-by: Liu Bo <bo.li.liu@oracle.com> --- fs/btrfs/Kconfig | 22 ++++++++ fs/btrfs/compression.c | 6 +-- fs/btrfs/ctree.h | 12 +++-- fs/btrfs/dir-item.c | 10 ++-- fs/btrfs/disk-io.c | 126 ++++++++++++++++++++++++-------------------- fs/btrfs/disk-io.h | 2 - fs/btrfs/extent-tree.c | 43 ++++++++++----- fs/btrfs/file-item.c | 9 ++-- fs/btrfs/free-space-cache.c | 15 +++--- fs/btrfs/hash.c | 75 ++++++++++++++++++++------ fs/btrfs/hash.h | 22 ++++---- fs/btrfs/inode-item.c | 6 +-- fs/btrfs/inode.c | 16 +++--- fs/btrfs/props.c | 37 +++++++++++-- fs/btrfs/props.h | 3 +- fs/btrfs/scrub.c | 70 +++++++++++++++++++----- fs/btrfs/send.c | 7 ++- fs/btrfs/super.c | 9 ++-- fs/btrfs/tree-log.c | 2 +- 19 files changed, 331 insertions(+), 161 deletions(-)