diff --git a/src/utils/dlck/tests/README.md b/src/utils/dlck/tests/README.md new file mode 100644 index 00000000000..a31690a1f1b --- /dev/null +++ b/src/utils/dlck/tests/README.md @@ -0,0 +1,38 @@ +# DLCK Test Helper + +Simple utility which takes an existing VOS pool and creates and populates a container inside this pool according to provided parameters. + +## Basic usage for MD-on-PMEM pool + +`dlck_test_helper` accepts the same parameters as `daos_engine` and `dlck`, for example: + +```sh +./dlck_test_helper --file 102d950b-7cd7-4a02-9ae2-30d08d1f5900,0 --storage /dev/shm/daos +``` + +**Note**: To use `dlck_test_helper` against an MD-on-SSD pool provide the `--nvme` parameter. + +## Container's contents + +Default parameters are hand-picked to produce a small but interesting enough VOS tree for testing purposes, which may look like this: + +``` +OID 471378945.1581695481 +├── DKEY 53262252 +│ └── AKEY 3409551c +├── DKEY 1e81b75f +│ ├── AKEY 1e81b75f +│ └── AKEY 53262252 (x2) +└── DKEY 3409551c + └── AKEY 1e81b75f +OID 1185667690.2136093645 +├── DKEY 53262252 +│ ├── AKEY 3409551c +│ └── AKEY 1e81b75f +└── DKEY 17d9b927 + └── AKEY 17d9b927 +``` + +**Note**: Since all OIDs and keys are generated randomly and picked at random while generating updates, the effective VOS tree changes when parameters or the implementation change. + +You may control the output to some extent by providing extra parameters: `--oids_num`, `--keys_num` and `--updates_num`. diff --git a/src/utils/dlck/tests/SConscript b/src/utils/dlck/tests/SConscript index d83805c5390..097ff393219 100644 --- a/src/utils/dlck/tests/SConscript +++ b/src/utils/dlck/tests/SConscript @@ -14,6 +14,7 @@ def build_dlck_test_helper(henv): srcs = [ 'dlck_test_helper.c', + 'dlck_test_helper_args.c', '../dlck_abt.c', '../dlck_args_engine.c', '../dlck_args_files.c', diff --git a/src/utils/dlck/tests/dlck_test_helper.c b/src/utils/dlck/tests/dlck_test_helper.c index b232282e571..458873789a3 100644 --- a/src/utils/dlck/tests/dlck_test_helper.c +++ b/src/utils/dlck/tests/dlck_test_helper.c @@ -7,10 +7,6 @@ #include #include -#include -#include -#include -#include #include #include #include @@ -21,27 +17,29 @@ #include #include -#include "../dlck_args.h" +#include "dlck_test_helper_args.h" + #include "../dlck_bitmap.h" #include "../dlck_engine.h" #include "../dlck_pool.h" -#define SRAND_SEED 0x4321 -#define UPDATES_NUM 125 +#define SRAND_SEED 0x4321 extern struct dss_module dtx_module; -struct dlck_helper_args { - struct dlck_args_files files; - struct dlck_args_engine engine; +struct dlck_helper_paths { + uuid_t co_uuid; + daos_unit_oid_t *oids; + uint64_t *keys; }; struct bundle { struct dlck_args_engine *args_engine; struct dlck_args_files *args_files; - struct dlck_engine *engine; - uuid_t *co_uuids; + struct dlck_helper_workload_args *args_workload; + struct dlck_engine *engine; unsigned int seed; + struct dlck_helper_paths paths; }; struct io { @@ -60,10 +58,11 @@ struct xstream_state { /** input */ struct dlck_args_engine *args_engine; struct dlck_args_files *args_files; + struct dlck_helper_workload_args *args_workload; struct dlck_engine *engine; - struct dlck_xstream *xs; - uuid_t co_uuid; + struct dlck_xstream *xs; unsigned int seed; + struct dlck_helper_paths *paths; /** run-time variables */ daos_handle_t poh; daos_handle_t coh; @@ -91,7 +90,7 @@ random_uuid(uuid_t uuid, unsigned int *seedp) random_uuid_str(uuid_str, seedp); rc = uuid_parse(uuid_str, uuid); - assert_int_equal(rc, 0); + assert(rc == 0); } static void @@ -102,10 +101,10 @@ cont_setup(struct xstream_state *xst, uuid_t co_uuid) (void)vos_cont_destroy(xst->poh, co_uuid); rc = vos_cont_create(xst->poh, co_uuid); - assert_int_equal(rc, 0); + assert(rc == 0); rc = vos_cont_open(xst->poh, co_uuid, &xst->coh); - assert_int_equal(rc, 0); + assert(rc == 0); } static void @@ -114,11 +113,12 @@ cont_teardown(struct xstream_state *xst) int rc; rc = vos_cont_close(xst->coh); - assert_int_equal(rc, 0); + assert(rc == 0); } static void -dtx_update(daos_handle_t coh, uuid_t dti_uuid, struct io *io, bool is_leader, bool commit) +dtx_update(daos_handle_t coh, uuid_t dti_uuid, struct io *io, bool is_leader, bool commit, + bool keep_act) { struct dtx_id dti = {0}; struct dtx_epoch epoch = {0}; @@ -137,49 +137,60 @@ dtx_update(daos_handle_t coh, uuid_t dti_uuid, struct io *io, bool is_leader, bo if (is_leader) { rc = dtx_leader_begin(coh, &dti, &epoch, 1, 0, &leader_oid, NULL, 0, NULL, 0, 0, NULL, NULL, &dlh); - assert_int_equal(rc, 0); + assert(rc == 0); dth = &dlh->dlh_handle; } else { rc = dtx_begin(coh, &dti, &epoch, 1, 0, &leader_oid, NULL, 0, flags, NULL, &dth); - assert_int_equal(rc, 0); + assert(rc == 0); } rc = dtx_sub_init(dth, &io->oid, 0); - assert_int_equal(rc, 0); + assert(rc == 0); rc = vos_obj_update_ex(coh, io->oid, 0, 0, 0, &io->dkey, 1, &io->iod, NULL, &io->sgl, dth); - assert_int_equal(rc, 0); + if (rc == -DER_NO_PERM) { + /** Current implementation does not track already used paths so the same path may be + * reused with a different data type which is not allowed. */ + + (void)dtx_end(dth, NULL, rc); + return; + } + assert(rc == 0); /** * XXX Normally, the leader probably won't use this API to end its transaction. * It also probably messes up a little with the DTX leader handle memory allocation. */ rc = dtx_end(dth, NULL, DER_SUCCESS); - assert_int_equal(rc, 0); + assert(rc == 0); if (commit) { - rc = vos_dtx_commit(coh, &dti, 1, true, NULL); - assert_int_equal(rc, 1); /** total number of committed */ + rc = vos_dtx_commit(coh, &dti, 1, keep_act, NULL); + if (keep_act) { + assert(rc == 0); + } else { + assert(rc == 1); /** total number of committed */ + } } } static void -io_init_random(struct io *io, const char *value, daos_iod_type_t iod_type, unsigned int *seedp) +io_init_random(struct xstream_state *xst, const char *value, daos_iod_type_t iod_type, + struct io *io) { int rc; memset(io, 0, sizeof(*io)); /** random OID */ - io->oid.id_pub.hi = rand_r(seedp); - io->oid.id_pub.lo = rand_r(seedp); + io->oid.id_pub = xst->paths->oids[rand_r(&xst->seed) % xst->args_workload->oids_num].id_pub; /** random DKEY */ - io->dkey_buf = rand_r(seedp); + io->dkey_buf = xst->paths->keys[rand_r(&xst->seed) % xst->args_workload->keys_num]; d_iov_set(&io->dkey, (void *)&io->dkey_buf, sizeof(io->dkey_buf)); /** random AKEY */ - io->akey_buf = rand_r(seedp); + io->akey_buf = xst->paths->keys[rand_r(&xst->seed) % xst->args_workload->keys_num]; d_iov_set(&io->akey, (void *)&io->akey_buf, sizeof(io->akey_buf)); /** populate the IO descriptor */ @@ -196,12 +207,12 @@ io_init_random(struct io *io, const char *value, daos_iod_type_t iod_type, unsig io->rex.rx_nr = 1; io->iod.iod_recxs = &io->rex; } else { - assert_true(false); + assert(false); } /** populate the SG list */ rc = d_sgl_init(&io->sgl, 1); - assert_int_equal(rc, 0); + assert(rc == 0); d_iov_set(&io->sgl.sg_iovs[0], (void *)value, io->iod.iod_size); } @@ -212,7 +223,8 @@ io_fini(struct io *io) } static void -update_one(struct xstream_state *xst, daos_iod_type_t iod_type, bool is_leader, bool commit) +update_one(struct xstream_state *xst, daos_iod_type_t iod_type, bool is_leader, bool commit, + bool keep_act) { uuid_t dti_uuid; struct io *io = &xst->io; @@ -220,35 +232,54 @@ update_one(struct xstream_state *xst, daos_iod_type_t iod_type, bool is_leader, random_uuid(dti_uuid, &xst->seed); random_uuid_str(value, &xst->seed); - io_init_random(io, value, iod_type, &xst->seed); - dtx_update(xst->coh, dti_uuid, io, is_leader, commit); + io_init_random(xst, value, iod_type, io); + printf("Update: " DF_OID "/%" PRIx64 "/%" PRIx64 "\n", DP_OID(io->oid.id_pub), io->dkey_buf, + io->akey_buf); + dtx_update(xst->coh, dti_uuid, io, is_leader, commit, keep_act); io_fini(io); } static void cont_process(struct xstream_state *xst, uuid_t co_uuid) { - bool is_leader; + daos_iod_type_t iod_types[] = {DAOS_IOD_SINGLE, DAOS_IOD_ARRAY}; + daos_iod_type_t iod_type; + bool is_leader; + bool commit; + bool keep_act; - cont_setup(xst, xst->co_uuid); + cont_setup(xst, co_uuid); - /** - * 2 (IOD types) * 125 * 4 = 1000 total updates - */ - for (daos_iod_type_t iod_type = DAOS_IOD_SINGLE; iod_type <= DAOS_IOD_ARRAY; ++iod_type) { - for (int i = 0; i < UPDATES_NUM; ++i) { - is_leader = true; - update_one(xst, iod_type, is_leader, false /** commit */); - update_one(xst, iod_type, is_leader, true /** commit */); - is_leader = false; - update_one(xst, iod_type, is_leader, false /** commit */); - update_one(xst, iod_type, is_leader, true /** commit */); - } + for (int i = 0; i < xst->args_workload->updates_num; ++i) { + iod_type = iod_types[rand_r(&xst->seed) % ARRAY_SIZE(iod_types)]; + is_leader = rand_r(&xst->seed) % 2; + commit = rand_r(&xst->seed) % 2; + keep_act = commit ? (rand_r(&xst->seed) % 2) : false; + + update_one(xst, iod_type, is_leader, commit, keep_act); } cont_teardown(xst); } +static int +create_pool(struct xstream_state *xst, uuid_t po_uuid) +{ + static const daos_size_t scm_sz = 256 * 1024 * 1024; /* 256 MiB */ + char uuid_str[UUID_STR_LEN]; + char *path; + int rc; + + uuid_unparse(po_uuid, uuid_str); + D_ASPRINTF(path, "%s/%s/vos-%d", xst->args_engine->storage_path, uuid_str, xst->xs->tgt_id); + assert(path != NULL); + + rc = vos_pool_create(path, po_uuid, scm_sz, 0, 0, 0, 0, NULL); + D_FREE(path); + + return rc; +} + static void exec_one(void *arg) { @@ -262,6 +293,14 @@ exec_one(void *arg) continue; } + if (xst->args_workload->create_pools) { + rc = create_pool(xst, file->po_uuid); + if (rc != DER_SUCCESS) { + xst->rc = rc; + break; + } + } + rc = dlck_pool_open(xst->args_engine->storage_path, file->po_uuid, xst->xs->tgt_id, &xst->poh); if (rc != DER_SUCCESS) { @@ -269,7 +308,7 @@ exec_one(void *arg) break; } - cont_process(xst, xst->co_uuid); + cont_process(xst, xst->paths->co_uuid); rc = vos_pool_close(xst->poh); if (rc != DER_SUCCESS) { @@ -297,9 +336,10 @@ arg_alloc(struct dlck_engine *engine, int idx, void *input_arg, void **output_ar xst->args_engine = bundle->args_engine; xst->args_files = bundle->args_files; + xst->args_workload = bundle->args_workload; xst->engine = engine; xst->xs = &engine->xss[idx]; - uuid_copy(xst->co_uuid, bundle->co_uuids[idx]); + xst->paths = &bundle->paths; xst->seed = rand_r(&bundle->seed); *output_arg = xst; @@ -319,43 +359,10 @@ arg_free(void *unused, void **arg) return 0; } -/** command-line argument parsing */ - -extern struct argp argp_file; -extern struct argp argp_engine; - -static struct argp_option _automagic[] = {OPT_HEADER("Other options:", GROUP_AUTOMAGIC), {0}}; - -static struct argp automagic = {_automagic, NULL}; - -static struct argp_child children[] = {{&argp_file}, {&argp_engine}, {&automagic}, {0}}; - -/** - * \brief Main parser. - * - * Just provides inputs for the child parsers. - */ -error_t -parser(int key, char *arg, struct argp_state *state) -{ - struct dlck_helper_args *args = state->input; - - /** state changes */ - switch (key) { - case ARGP_KEY_INIT: - state->child_inputs[0] = &args->files; - state->child_inputs[1] = &args->engine; - return 0; - } - - return ARGP_ERR_UNKNOWN; -} - -static struct argp argp = {NULL, parser, NULL /** usage */, NULL, children}; - static int setup(struct dlck_helper_args *args, struct bundle *bundle) { + struct dlck_helper_paths *paths = &bundle->paths; struct dlck_engine *engine; unsigned int seed = SRAND_SEED; int rc; @@ -390,18 +397,26 @@ setup(struct dlck_helper_args *args, struct bundle *bundle) goto fail_engine_stop; } - D_ALLOC_ARRAY(bundle->co_uuids, args->engine.targets); - if (bundle->co_uuids == NULL) { - rc = -DER_NOMEM; - goto fail_engine_stop; + /** generate random elements of VOS path */ + random_uuid(paths->co_uuid, &seed); + + D_ALLOC_ARRAY(paths->oids, args->workload.oids_num); + assert(paths->oids != NULL); + for (int i = 0; i < args->workload.oids_num; ++i) { + paths->oids[i].id_pub.hi = rand_r(&seed); + paths->oids[i].id_pub.lo = rand_r(&seed); } - for (int i = 0; i < args->engine.targets; ++i) { - random_uuid(bundle->co_uuids[i], &seed); + D_ALLOC_ARRAY(paths->keys, args->workload.keys_num); + assert(paths->keys != NULL); + for (int i = 0; i < args->workload.keys_num; ++i) { + paths->keys[i] = rand_r(&seed); } + /** write relevant bits into the bundle */ bundle->args_engine = &args->engine; bundle->args_files = &args->files; + bundle->args_workload = &args->workload; bundle->engine = engine; bundle->seed = seed; @@ -421,7 +436,8 @@ teardown(struct bundle *bundle) int rc_abt; int rc; - D_FREE(bundle->co_uuids); + D_FREE(bundle->paths.keys); + D_FREE(bundle->paths.oids); rc = dlck_engine_stop(bundle->engine); if (rc != DER_SUCCESS) { @@ -451,7 +467,11 @@ main(int argc, char **argv) int rc; - argp_parse(&argp, argc, argv, 0, 0, &args); + dlck_helper_args_parse(argc, argv, &args); + + if (args.workload.verbose) { + (void)daos_debug_init_ex(DAOS_LOG_DEFAULT, DLOG_ERR); + } rc = setup(&args, &bundle); if (rc != DER_SUCCESS) { diff --git a/src/utils/dlck/tests/dlck_test_helper_args.c b/src/utils/dlck/tests/dlck_test_helper_args.c new file mode 100644 index 00000000000..d1a4178bce4 --- /dev/null +++ b/src/utils/dlck/tests/dlck_test_helper_args.c @@ -0,0 +1,155 @@ +/** + * (C) Copyright 2025-2026 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ + +#include + +#include "dlck_test_helper_args.h" + +/** these options cannot collide with keys already taken in dlck_args.h */ +#define KEY_WORKLOAD_OIDS_NUM 'b' +#define KEY_WORKLOAD_KEYS_NUM 'k' +#define KEY_WORKLOAD_UPDATES_NUM 'u' +#define KEY_WORKLOAD_CREATE_POOLS 'c' + +#define GROUP_WORKLOAD 2 + +#define DLCK_HELPER_DEFAULT_OIDS_NUM 2 +#define DLCK_HELPER_DEFAULT_KEYS_NUM 4 +#define DLCK_HELPER_DEFAULT_UPDATES_NUM 8 + +static struct argp_option args_workload_options[] = { + OPT_HEADER("Options:", GROUP_OPTIONS), + OPT_HEADER("Workload options:", GROUP_WORKLOAD), + /** entries below inherits the group number of the header entry */ + {"oids_num", KEY_WORKLOAD_OIDS_NUM, "N", 0, + "Number of OIDs to use. Default: " STRINGIFY(DLCK_HELPER_DEFAULT_OIDS_NUM) "."}, + {"keys_num", KEY_WORKLOAD_KEYS_NUM, "N", 0, + "Number of keys to use. Default: " STRINGIFY(DLCK_HELPER_DEFAULT_KEYS_NUM) "."}, + {"updates_num", KEY_WORKLOAD_UPDATES_NUM, "N", 0, + "Number of updates to make. Default: " STRINGIFY(DLCK_HELPER_DEFAULT_UPDATES_NUM) "."}, + {"create_pools", KEY_WORKLOAD_CREATE_POOLS, 0, 0, + "Create pools before attempting to populate them."}, + /** this is expected to be necessary only while solving issues with the tool itself so it seems + to fit better with a different group */ + {"verbose", KEY_COMMON_VERBOSE, 0, 0, + "Print DAOS log messages. All standard environment variables apply.", GROUP_AUTOMAGIC}, + {0}}; + +static void +args_workload_init(struct dlck_helper_workload_args *args) +{ + memset(args, 0, sizeof(*args)); + /** set defaults */ + args->oids_num = DLCK_HELPER_DEFAULT_OIDS_NUM; + args->keys_num = DLCK_HELPER_DEFAULT_KEYS_NUM; + args->updates_num = DLCK_HELPER_DEFAULT_UPDATES_NUM; + args->verbose = false; +} + +static int +args_workload_check(struct argp_state *state, struct dlck_helper_workload_args *args) +{ + if (args->oids_num == 0) { + RETURN_FAIL(state, EINVAL, "oids_num > 0 is required."); + } + if (args->keys_num == 0) { + RETURN_FAIL(state, EINVAL, "keys_num > 0 is required."); + } + if (args->updates_num == 0) { + RETURN_FAIL(state, EINVAL, "updates_num > 0 is required."); + } + + return 0; +} + +error_t +args_workload_parser(int key, char *arg, struct argp_state *state) +{ + struct dlck_helper_workload_args *args = state->input; + int rc = 0; + + /** state changes */ + switch (key) { + case ARGP_KEY_INIT: + args_workload_init(args); + return 0; + case ARGP_KEY_END: + return args_workload_check(state, args); + case ARGP_KEY_SUCCESS: + case ARGP_KEY_FINI: + return 0; + } + + /** options */ + switch (key) { + case KEY_WORKLOAD_OIDS_NUM: + rc = parse_unsigned(arg, &args->oids_num, state); + break; + case KEY_WORKLOAD_KEYS_NUM: + rc = parse_unsigned(arg, &args->keys_num, state); + break; + case KEY_WORKLOAD_UPDATES_NUM: + rc = parse_unsigned(arg, &args->updates_num, state); + break; + case KEY_COMMON_VERBOSE: + args->verbose = true; + break; + case KEY_WORKLOAD_CREATE_POOLS: + args->create_pools = true; + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return rc; +} + +struct argp argp_workload = {args_workload_options, args_workload_parser, NULL, NULL, NULL}; + +/** command-line argument parsing */ + +extern struct argp argp_file; +extern struct argp argp_engine; + +static struct argp_option _automagic[] = {OPT_HEADER("Other options:", GROUP_AUTOMAGIC), {0}}; + +static struct argp automagic = {_automagic, NULL}; + +static struct argp_child children[] = { + {&argp_file}, {&argp_engine}, {&argp_workload}, {&automagic}, {0}}; + +/** + * \brief Main parser. + * + * Just provides inputs for the child parsers. + */ +static error_t +parser(int key, char *arg, struct argp_state *state) +{ + struct dlck_helper_args *args = state->input; + + /** state changes */ + switch (key) { + case ARGP_KEY_INIT: + state->child_inputs[0] = &args->files; + state->child_inputs[1] = &args->engine; + state->child_inputs[2] = &args->workload; + return 0; + } + + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp_helper = {NULL, parser, NULL /** usage */, NULL, children}; + +void +dlck_helper_args_parse(int argc, char **argv, struct dlck_helper_args *args) +{ + error_t ret = argp_parse(&argp_helper, argc, argv, ARGP_NO_EXIT, 0, args); + if (ret != 0) { + exit(ret); + } +} diff --git a/src/utils/dlck/tests/dlck_test_helper_args.h b/src/utils/dlck/tests/dlck_test_helper_args.h new file mode 100644 index 00000000000..d375da13075 --- /dev/null +++ b/src/utils/dlck/tests/dlck_test_helper_args.h @@ -0,0 +1,30 @@ +/** + * (C) Copyright 2026 Hewlett Packard Enterprise Development LP + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ + +#ifndef __DLCK_TEST_HELPER_ARGS__ +#define __DLCK_TEST_HELPER_ARGS__ + +#include "../dlck_args.h" + +struct dlck_helper_workload_args { + unsigned int oids_num; + unsigned int keys_num; + unsigned int updates_num; + + bool verbose; + bool create_pools; +}; + +struct dlck_helper_args { + struct dlck_args_files files; + struct dlck_args_engine engine; + struct dlck_helper_workload_args workload; +}; + +void +dlck_helper_args_parse(int argc, char **argv, struct dlck_helper_args *args); + +#endif /* __DLCK_TEST_HELPER_ARGS__ */