mirror of
https://git.proxmox.com/git/mirror_ubuntu-kernels.git
synced 2025-11-25 12:44:01 +00:00
When inserting a batch of items into a btree, we end up looping over the data sizes array 3 times: 1) Once in the caller of btrfs_insert_empty_items(), when it populates the array with the data sizes for each item; 2) Once at btrfs_insert_empty_items() to sum the elements of the data sizes array and compute the total data size; 3) And then once again at setup_items_for_insert(), where we do exactly the same as what we do at btrfs_insert_empty_items(), to compute the total data size. That is not bad for small arrays, but when the arrays have hundreds of elements, the time spent on looping is not negligible. For example when doing batch inserts of delayed items for dir index items or when logging a directory, it's common to have 200 to 260 dir index items in a single batch when using a leaf size of 16K and using file names between 8 and 12 characters. For a 64K leaf size, multiply that by 4. Taking into account that during directory logging or when flushing delayed dir index items we can have many of those large batches, the time spent on the looping adds up quickly. It's also more important to avoid it at setup_items_for_insert(), since we are holding a write lock on a leaf and, in some cases, on upper nodes of the btree, which causes us to block other tasks that want to access the leaf and nodes for longer than necessary. So change the code so that setup_items_for_insert() and btrfs_insert_empty_items() no longer compute the total data size, and instead rely on the caller to supply it. This makes us loop over the array only once, where we can both populate the data size array and compute the total data size, taking advantage of spatial and temporal locality. To make this more manageable, use a structure to contain all the relevant details for a batch of items (keys array, data sizes array, total data size, number of items), and use it as an argument for btrfs_insert_empty_items() and setup_items_for_insert(). This patch is part of a small patchset that is comprised of the following patches: btrfs: loop only once over data sizes array when inserting an item batch btrfs: unexport setup_items_for_insert() btrfs: use single bulk copy operations when logging directories This is patch 1/3 and performance results, and the specific tests, are included in the changelog of patch 3/3. Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
225 lines
5.3 KiB
C
225 lines
5.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2013 Fusion IO. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include "btrfs-tests.h"
|
|
#include "../ctree.h"
|
|
#include "../extent_io.h"
|
|
#include "../disk-io.h"
|
|
|
|
static int test_btrfs_split_item(u32 sectorsize, u32 nodesize)
|
|
{
|
|
struct btrfs_fs_info *fs_info;
|
|
struct btrfs_path *path = NULL;
|
|
struct btrfs_root *root = NULL;
|
|
struct extent_buffer *eb;
|
|
struct btrfs_item *item;
|
|
char *value = "mary had a little lamb";
|
|
char *split1 = "mary had a little";
|
|
char *split2 = " lamb";
|
|
char *split3 = "mary";
|
|
char *split4 = " had a little";
|
|
char buf[32];
|
|
struct btrfs_key key;
|
|
u32 value_len = strlen(value);
|
|
int ret = 0;
|
|
|
|
test_msg("running btrfs_split_item tests");
|
|
|
|
fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize);
|
|
if (!fs_info) {
|
|
test_std_err(TEST_ALLOC_FS_INFO);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
root = btrfs_alloc_dummy_root(fs_info);
|
|
if (IS_ERR(root)) {
|
|
test_std_err(TEST_ALLOC_ROOT);
|
|
ret = PTR_ERR(root);
|
|
goto out;
|
|
}
|
|
|
|
path = btrfs_alloc_path();
|
|
if (!path) {
|
|
test_std_err(TEST_ALLOC_PATH);
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
path->nodes[0] = eb = alloc_dummy_extent_buffer(fs_info, nodesize);
|
|
if (!eb) {
|
|
test_std_err(TEST_ALLOC_EXTENT_BUFFER);
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
path->slots[0] = 0;
|
|
|
|
key.objectid = 0;
|
|
key.type = BTRFS_EXTENT_CSUM_KEY;
|
|
key.offset = 0;
|
|
|
|
setup_item_for_insert(root, path, &key, value_len);
|
|
item = btrfs_item_nr(0);
|
|
write_extent_buffer(eb, value, btrfs_item_ptr_offset(eb, 0),
|
|
value_len);
|
|
|
|
key.offset = 3;
|
|
|
|
/*
|
|
* Passing NULL trans here should be safe because we have plenty of
|
|
* space in this leaf to split the item without having to split the
|
|
* leaf.
|
|
*/
|
|
ret = btrfs_split_item(NULL, root, path, &key, 17);
|
|
if (ret) {
|
|
test_err("split item failed %d", ret);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Read the first slot, it should have the original key and contain only
|
|
* 'mary had a little'
|
|
*/
|
|
btrfs_item_key_to_cpu(eb, &key, 0);
|
|
if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
|
|
key.offset != 0) {
|
|
test_err("invalid key at slot 0");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
item = btrfs_item_nr(0);
|
|
if (btrfs_item_size(eb, item) != strlen(split1)) {
|
|
test_err("invalid len in the first split");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0),
|
|
strlen(split1));
|
|
if (memcmp(buf, split1, strlen(split1))) {
|
|
test_err(
|
|
"data in the buffer doesn't match what it should in the first split have='%.*s' want '%s'",
|
|
(int)strlen(split1), buf, split1);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
btrfs_item_key_to_cpu(eb, &key, 1);
|
|
if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
|
|
key.offset != 3) {
|
|
test_err("invalid key at slot 1");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
item = btrfs_item_nr(1);
|
|
if (btrfs_item_size(eb, item) != strlen(split2)) {
|
|
test_err("invalid len in the second split");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1),
|
|
strlen(split2));
|
|
if (memcmp(buf, split2, strlen(split2))) {
|
|
test_err(
|
|
"data in the buffer doesn't match what it should in the second split");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
key.offset = 1;
|
|
/* Do it again so we test memmoving the other items in the leaf */
|
|
ret = btrfs_split_item(NULL, root, path, &key, 4);
|
|
if (ret) {
|
|
test_err("second split item failed %d", ret);
|
|
goto out;
|
|
}
|
|
|
|
btrfs_item_key_to_cpu(eb, &key, 0);
|
|
if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
|
|
key.offset != 0) {
|
|
test_err("invalid key at slot 0");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
item = btrfs_item_nr(0);
|
|
if (btrfs_item_size(eb, item) != strlen(split3)) {
|
|
test_err("invalid len in the first split");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0),
|
|
strlen(split3));
|
|
if (memcmp(buf, split3, strlen(split3))) {
|
|
test_err(
|
|
"data in the buffer doesn't match what it should in the third split");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
btrfs_item_key_to_cpu(eb, &key, 1);
|
|
if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
|
|
key.offset != 1) {
|
|
test_err("invalid key at slot 1");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
item = btrfs_item_nr(1);
|
|
if (btrfs_item_size(eb, item) != strlen(split4)) {
|
|
test_err("invalid len in the second split");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1),
|
|
strlen(split4));
|
|
if (memcmp(buf, split4, strlen(split4))) {
|
|
test_err(
|
|
"data in the buffer doesn't match what it should in the fourth split");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
btrfs_item_key_to_cpu(eb, &key, 2);
|
|
if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY ||
|
|
key.offset != 3) {
|
|
test_err("invalid key at slot 2");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
item = btrfs_item_nr(2);
|
|
if (btrfs_item_size(eb, item) != strlen(split2)) {
|
|
test_err("invalid len in the second split");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 2),
|
|
strlen(split2));
|
|
if (memcmp(buf, split2, strlen(split2))) {
|
|
test_err(
|
|
"data in the buffer doesn't match what it should in the last chunk");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
out:
|
|
btrfs_free_path(path);
|
|
btrfs_free_dummy_root(root);
|
|
btrfs_free_dummy_fs_info(fs_info);
|
|
return ret;
|
|
}
|
|
|
|
int btrfs_test_extent_buffer_operations(u32 sectorsize, u32 nodesize)
|
|
{
|
|
test_msg("running extent buffer operation tests");
|
|
return test_btrfs_split_item(sectorsize, nodesize);
|
|
}
|