diff --git a/debian/changelog b/debian/changelog index 484b32f3533..18a0bfd0294 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +daos (2.7.101-5) unstable; urgency=medium + [ Jan Michalski ] + * Add ddb_ut and dtx_ut to the server-tests package + + -- Jan Michalski Wed, 22 Jan 2025 14:31:00 +0000 + daos (2.7.101-3) unstable; urgency=medium [ Jeff Olivier ] * Switch from libfuse3 to libfused diff --git a/debian/daos-server-tests.install b/debian/daos-server-tests.install index ae15938b219..56fb0ad8742 100644 --- a/debian/daos-server-tests.install +++ b/debian/daos-server-tests.install @@ -1,4 +1,5 @@ usr/bin/dtx_tests +usr/bin/dtx_ut usr/bin/jump_pl_map usr/bin/ring_pl_map usr/bin/evt_ctl @@ -9,6 +10,7 @@ usr/bin/bio_ut usr/bin/vea_ut usr/bin/vos_tests usr/bin/ddb_tests +usr/bin/ddb_ut usr/bin/vea_stress usr/bin/vos_perf usr/bin/obj_ctl diff --git a/src/control/cmd/ddb/commands_wrapper.go b/src/control/cmd/ddb/commands_wrapper.go index e19cff9a51f..c8ab7e5543e 100644 --- a/src/control/cmd/ddb/commands_wrapper.go +++ b/src/control/cmd/ddb/commands_wrapper.go @@ -1,5 +1,6 @@ // // (C) Copyright 2022-2024 Intel Corporation. +// (C) Copyright 2025 Hewlett Packard Enterprise Development LP. // // SPDX-License-Identifier: BSD-2-Clause-Patent // @@ -204,7 +205,7 @@ func ddbVeaUpdate(ctx *DdbContext, offset string, blk_cnt string) error { func ddbDtxActCommit(ctx *DdbContext, path string, dtx_id string) error { /* Set up the options */ - options := C.struct_dtx_act_commit_options{} + options := C.struct_dtx_act_options{} options.path = C.CString(path) defer freeString(options.path) options.dtx_id = C.CString(dtx_id) @@ -215,7 +216,7 @@ func ddbDtxActCommit(ctx *DdbContext, path string, dtx_id string) error { func ddbDtxActAbort(ctx *DdbContext, path string, dtx_id string) error { /* Set up the options */ - options := C.struct_dtx_act_abort_options{} + options := C.struct_dtx_act_options{} options.path = C.CString(path) defer freeString(options.path) options.dtx_id = C.CString(dtx_id) @@ -256,3 +257,14 @@ func ddbRmPool(ctx *DdbContext, path string) error { /* Run the c code command */ return daosError(C.ddb_run_rm_pool(&ctx.ctx, &options)) } + +func ddbDtxActDiscardInvalid(ctx *DdbContext, path string, dtx_id string) error { + /* Set up the options */ + options := C.struct_dtx_act_options{} + options.path = C.CString(path) + defer freeString(options.path) + options.dtx_id = C.CString(dtx_id) + defer freeString(options.dtx_id) + /* Run the c code command */ + return daosError(C.ddb_run_dtx_act_discard_invalid(&ctx.ctx, &options)) +} diff --git a/src/control/cmd/ddb/ddb_commands.go b/src/control/cmd/ddb/ddb_commands.go index b87ea681dc4..63dc0b78b1b 100644 --- a/src/control/cmd/ddb/ddb_commands.go +++ b/src/control/cmd/ddb/ddb_commands.go @@ -1,5 +1,6 @@ // // (C) Copyright 2022-2024 Intel Corporation. +// (C) Copyright 2025 Hewlett Packard Enterprise Development LP. // // SPDX-License-Identifier: BSD-2-Clause-Patent // @@ -332,4 +333,20 @@ the path must include the extent, otherwise, it must not.`, }, Completer: rmPoolCompleter, }) + // Command: dtx_act_discard_invalid + app.AddCommand(&grumble.Command{ + Name: "dtx_act_discard_invalid", + Aliases: nil, + Help: "Discard the active DTX entry's records if invalid.", + LongHelp: "Committing or aborting the DTX entry with discarded records may result in inconsistencies. Allow DTX to resync instead.", + HelpGroup: "vos", + Args: func(a *grumble.Args) { + a.String("path", "VOS tree path to a container.") + a.String("dtx_id", "DTX id of the entry to validate.") + }, + Run: func(c *grumble.Context) error { + return ddbDtxActDiscardInvalid(ctx, c.Args.String("path"), c.Args.String("dtx_id")) + }, + Completer: nil, + }) } diff --git a/src/dtx/tests/SConscript b/src/dtx/tests/SConscript index 160bff9113f..54a14127932 100644 --- a/src/dtx/tests/SConscript +++ b/src/dtx/tests/SConscript @@ -5,6 +5,8 @@ def scons(): """Execute build""" Import('denv', 'vts_objs') + # build dtx_tests + libraries = ['abt', 'bio', 'dtx', 'vos', 'gurt', 'daos_common_pmem', 'cmocka', 'pthread', 'uuid', 'cart', 'daos_tests'] @@ -19,10 +21,35 @@ def scons(): test_src = ['dtx_tests.c', 'sched_mock.c', 'ult_mock.c', 'srv_mock.c', 'pl_map_mock.c', '../../common/tls.c', 'dts_utils.c', 'dts_local.c', 'dts_local_rdb.c', - 'dts_structs.c', vts_objs] + vts_objs] dtx_tests = tenv.d_program('dtx_tests', test_src, LIBS=libraries) - tenv.Install('$PREFIX/bin/', [dtx_tests]) + # build dtx_ut + + libraries = ['abt', 'bio', 'cmocka', 'daos_common_pmem', 'gurt', 'uuid', 'vea', 'pthread'] + + tenv = denv.Clone() + tenv.Append(CPPPATH=[Dir('../../vos').srcnode()]) + tenv.require('pmdk') + tenv.AppendUnique(RPATH_FULL=['$PREFIX/lib64/daos_srv']) + tenv.Append(OBJPREFIX="c_") + + # Required for vos_dtx_discard_invalid() tests. + # These functions are validated by their respective unit tests. + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=ilog_is_valid']) + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=vos_irec_is_valid']) + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=evt_desc_is_valid']) + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=dbtree_lookup']) + + vos_src = Glob('../../vos/*.c') + + test_src = ['dtx_ut.c', 'dts_discard_invalid.c', 'dts_structs.c', + 'srv_mock.c', 'sched_mock.c'] + dtx_ut = tenv.d_program('dtx_ut', test_src + vos_src, LIBS=libraries) + + # install both + + tenv.Install('$PREFIX/bin/', [dtx_tests, dtx_ut]) if __name__ == "SCons.Script": diff --git a/src/dtx/tests/dts_discard_invalid.c b/src/dtx/tests/dts_discard_invalid.c new file mode 100644 index 00000000000..186cee8cd8d --- /dev/null +++ b/src/dtx/tests/dts_discard_invalid.c @@ -0,0 +1,600 @@ +/** + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +#define D_LOGFAC DD_FAC(tests) + +#include +#include +#include +#include +#include + +#include "ilog.h" +#include "vos_layout.h" +#include "vos_internal.h" + +/* mocks */ + +static struct vos_pool Pool; +static struct vos_container Cont; +static daos_handle_t Coh; +static bool In_tx = false; + +static struct vos_dtx_act_ent Dae; +static struct vos_dtx_act_ent_df Dae_df; +static struct vos_dtx_act_ent_df Dae_df_exp; + +#define RECORDS_MAX 26 +static umem_off_t Records[RECORDS_MAX]; +static umem_off_t Records_df[RECORDS_MAX]; +static umem_off_t Records_df_exp[RECORDS_MAX]; + +#define DTX_ID_PTR ((struct dtx_id *)0x907) +#define VC_DTX_ACTIVE_HDL 0x456 +#define DBTREE_LOOKUP_ERROR_RC (-DER_NONEXIST) + +int +__wrap_dbtree_lookup(daos_handle_t coh, d_iov_t *key, d_iov_t *val_out) +{ + assert_int_equal(coh.cookie, VC_DTX_ACTIVE_HDL); + assert_non_null(key); + assert_int_equal(key->iov_len, key->iov_buf_len); + assert_int_equal(key->iov_len, sizeof(struct dtx_id)); + assert_ptr_equal(key->iov_buf, DTX_ID_PTR); + assert_non_null(val_out); + assert_int_equal(val_out->iov_len, 0); + assert_int_equal(val_out->iov_buf_len, 0); + assert_null(val_out->iov_buf); + val_out->iov_buf = (void *)mock(); + if (val_out->iov_buf != NULL) { + val_out->iov_len = val_out->iov_buf_len = sizeof(struct vos_dtx_act_ent); + return 0; + } + return DBTREE_LOOKUP_ERROR_RC; +} + +#define REC_UMEM_OFFSET 0x1267 +#define DTX_LID 0x356 +#define EPOCH 0x557 + +bool +__wrap_ilog_is_valid(struct umem_instance *umm, umem_off_t rec, uint32_t dtx_lid, + daos_epoch_t epoch) +{ + assert_ptr_equal(umm, &Pool.vp_umm); + check_expected(umem_off2offset(rec)); + assert_int_equal(dtx_lid, DTX_LID); + assert_int_equal(epoch, EPOCH); + return mock(); +} + +bool +__wrap_vos_irec_is_valid(const struct vos_irec_df *svt, uint32_t dtx_lid) +{ + check_expected(svt); + assert_int_equal(dtx_lid, DTX_LID); + return mock(); +} + +bool +__wrap_evt_desc_is_valid(const struct evt_desc *evt, uint32_t dtx_lid) +{ + check_expected(evt); + assert_int_equal(dtx_lid, DTX_LID); + return mock(); +} + +int +tx_begin(struct umem_instance *umm, struct umem_tx_stage_data *txd) +{ + assert_ptr_equal(umm, &Pool.vp_umm); + assert_null(txd); + int rc = mock(); + if (rc == 0) { + In_tx = true; + } + return rc; +} + +int +tx_commit(struct umem_instance *umm, void *data) +{ + assert_ptr_equal(umm, &Pool.vp_umm); + assert_null(data); + assert_true(In_tx); + In_tx = false; + return mock(); +} + +int +tx_abort(struct umem_instance *umm, int error) +{ + assert_ptr_equal(umm, &Pool.vp_umm); + check_expected(error); + assert_true(In_tx); + In_tx = false; + if (error) { + return error; + } + return mock(); +} + +int +tx_add_ptr(struct umem_instance *umm, void *ptr, size_t size) +{ + assert_ptr_equal(umm, &Pool.vp_umm); + check_expected(ptr); + check_expected(size); + return mock(); +} + +/* tests */ + +static void +test_missing_things(void **unused) +{ + daos_handle_t hdl_null = {0}; + int discarded = 0; + int rc; + + /* 1. Missing arguments. */ + expect_assert_failure(vos_dtx_discard_invalid(hdl_null, NULL, NULL)); + expect_assert_failure(vos_dtx_discard_invalid(Coh, NULL, NULL)); + expect_assert_failure(vos_dtx_discard_invalid(Coh, DTX_ID_PTR, NULL)); + expect_assert_failure(vos_dtx_discard_invalid(Coh, NULL, &discarded)); + + /* 2. DAE not in the DTX active table. */ + will_return(__wrap_dbtree_lookup, NULL); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, DBTREE_LOOKUP_ERROR_RC); +} + +static void +test_aborted_dae(void **unused) +{ + int discarded = 0; + int rc; + + /* 3.a Aborting. */ + memset(&Dae, 0, sizeof(Dae)); + Dae.dae_aborting = 1; + will_return(__wrap_dbtree_lookup, &Dae); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, 0); + + /* 3.b Aborted. */ + memset(&Dae, 0, sizeof(Dae)); + Dae.dae_aborted = 1; + will_return(__wrap_dbtree_lookup, &Dae); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, 0); +} + +struct rec_valid { + enum vos_dtx_record_types type; + bool valid; +}; + +static bool +prep_records_common(struct rec_valid tmpl[], int num, umem_off_t *rec, umem_off_t *rec_df, + umem_off_t *rec_df_exp) +{ + bool discarded = false; + + for (int i = 0; i < num; ++i) { + umem_off_t off = REC_UMEM_OFFSET + i; + rec[i] = off; + dtx_type2umoff_flag(&rec[i], tmpl[i].type); + rec_df[i] = rec[i]; + + switch (tmpl[i].type) { + case DTX_RT_ILOG: + expect_value(__wrap_ilog_is_valid, umem_off2offset(rec), off); + will_return(__wrap_ilog_is_valid, tmpl[i].valid); + break; + case DTX_RT_SVT: + expect_value(__wrap_vos_irec_is_valid, svt, off); + will_return(__wrap_vos_irec_is_valid, tmpl[i].valid); + break; + case DTX_RT_EVT: + expect_value(__wrap_evt_desc_is_valid, evt, off); + will_return(__wrap_evt_desc_is_valid, tmpl[i].valid); + break; + default: + fail_msg("Unknown record type: %d", tmpl[i].type); + } + + if (tmpl[i].valid) { + rec_df_exp[i] = rec[i]; + } else { + rec_df_exp[i] = UMOFF_NULL; + discarded = true; + } + } + + return discarded; +} + +static bool +prep_records_inline(struct rec_valid tmpl[], int num) +{ + Dae.dae_base.dae_rec_cnt = num; + + bool discarded = prep_records_common(tmpl, num, Dae.dae_base.dae_rec_inline, + Dae_df.dae_rec_inline, Dae_df_exp.dae_rec_inline); + if (discarded) { + expect_value(tx_add_ptr, ptr, &Dae_df.dae_rec_inline); + expect_value(tx_add_ptr, size, sizeof(umem_off_t) * num); + } + + return discarded; +} + +static bool +prep_records_noninline(struct rec_valid tmpl[], int num) +{ + /* link both volatile and durable format noninline records */ + Dae.dae_records = Records; + DAE_REC_OFF(&Dae) = umem_ptr2off(&Pool.vp_umm, &Records_df); + + /* noninline records come always on top off the inline records */ + Dae.dae_base.dae_rec_cnt = DTX_INLINE_REC_CNT + num; + + bool discarded = prep_records_common(tmpl, num, Records, Records_df, Records_df_exp); + if (discarded) { + expect_value(tx_add_ptr, ptr, &Records_df); + expect_value(tx_add_ptr, size, sizeof(umem_off_t) * num); + } + + return discarded; +} + +#define TX_ERROR_RC 0x156 + +static void +test_tx_begin_fail(void **unused) +{ + int discarded = 0; + int rc; + + /* 4. tx_begin() fails. */ + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, TX_ERROR_RC); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, TX_ERROR_RC); +} + +static void +test_tx_abort_fail(void **unused) +{ + int discarded = 0; + int rc; + + /* 5. tx_abort() (when nothing to commit) fails. */ + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + expect_value(tx_abort, error, 0); + will_return(tx_abort, TX_ERROR_RC); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, TX_ERROR_RC); +} + +struct rec_valid One_rec[] = {{.type = DTX_RT_ILOG, .valid = false}}; + +static void +test_tx_add_ptr_inline_fail(void **unused) +{ + int discarded = 0; + int rc; + + /* 6a. tx_add_ptr() for inline records fails. */ + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + prep_records_inline(One_rec, ARRAY_SIZE(One_rec)); + will_return(tx_add_ptr, TX_ERROR_RC); + expect_value(tx_abort, error, TX_ERROR_RC); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, TX_ERROR_RC); +} + +static void +test_tx_add_ptr_noninline_fail(void **unused) +{ + int discarded = 0; + int rc; + + /* 6b. tx_add_ptr() for non-inline records fails. */ + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + prep_records_noninline(One_rec, ARRAY_SIZE(One_rec)); + will_return(tx_add_ptr, TX_ERROR_RC); + expect_value(tx_abort, error, TX_ERROR_RC); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, TX_ERROR_RC); +} + +static void +test_tx_commit_fail(void **unused) +{ + int discarded = 0; + int rc; + + /* 7. tx_commit() fails. */ + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + prep_records_noninline(One_rec, ARRAY_SIZE(One_rec)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, TX_ERROR_RC); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, TX_ERROR_RC); +} + +static void +reset_dfs(); + +#define DTX_RT_MIN DTX_RT_ILOG +#define DTX_RT_MAX DTX_RT_EVT +#define DTX_RT_NUM 3 + +static void +test_discard_inline_all(void **unused) +{ + struct rec_valid recs[] = { + {DTX_RT_ILOG, false}, + {DTX_RT_SVT, false}, + {DTX_RT_EVT, false}, + {DTX_RT_ILOG, false}, + }; + + int discarded = 0; + int rc; + + /* 8. discard all inline records at once */ + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + prep_records_inline(recs, ARRAY_SIZE(recs)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, 0); + assert_int_equal(discarded, ARRAY_SIZE(recs)); + assert_memory_equal(&Dae_df, &Dae_df_exp, sizeof(Dae_df)); + assert_memory_equal(&Records_df, &Records_df_exp, sizeof(Records_df)); +} + +typedef void (*execute_fn)(struct rec_valid *recs, int num); + +static void +prep_discard_one_common(execute_fn execute) +{ + struct rec_valid recs[4]; + + /* pick the type of the record to be discarded */ + for (enum vos_dtx_record_types type = DTX_RT_MIN; type <= DTX_RT_MAX; ++type) { + enum vos_dtx_record_types other_type = (type + 1) % DTX_RT_NUM + DTX_RT_MIN; + /* pick which entry will be discarded */ + for (int i = 0; i < ARRAY_SIZE(recs); ++i) { + /* initialize the array describing the scenario */ + for (int j = 0; j < ARRAY_SIZE(recs); ++j) { + if (j == i) { + recs[j].type = type; + recs[j].valid = false; + } else { + recs[j].type = other_type; + recs[j].valid = true; + } + } + /* reset durable format mocks */ + reset_dfs(); + execute(recs, ARRAY_SIZE(recs)); + } + } +} + +static void +discard_inline_one_execute(struct rec_valid *recs, int num) +{ + int discarded = 0; + int rc; + + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + prep_records_inline(recs, num); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, 0); + assert_int_equal(discarded, 1); + assert_memory_equal(&Dae_df, &Dae_df_exp, sizeof(Dae_df)); + assert_memory_equal(&Records_df, &Records_df_exp, sizeof(Records_df)); +} + +static void +test_discard_inline_one(void **unused) +{ + /* 9. discard just one inline record */ + prep_discard_one_common(discard_inline_one_execute); +} + +static void +test_discard_noninline_all(void **unused) +{ + struct rec_valid recs[] = { + {DTX_RT_ILOG, false}, + {DTX_RT_SVT, false}, + {DTX_RT_EVT, false}, + {DTX_RT_ILOG, false}, + }; + + int discarded = 0; + int rc; + + /* 10. discard all noninline records at once */ + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + prep_records_noninline(recs, ARRAY_SIZE(recs)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, 0); + assert_int_equal(discarded, ARRAY_SIZE(recs)); + assert_memory_equal(&Dae_df, &Dae_df, sizeof(Dae_df)); + assert_memory_equal(&Records_df, &Records_df_exp, sizeof(umem_off_t) * ARRAY_SIZE(recs)); +} + +static void +discard_noninline_one_execute(struct rec_valid *recs, int num) +{ + int discarded = 0; + int rc; + + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + prep_records_noninline(recs, num); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, 0); + assert_int_equal(discarded, 1); + assert_memory_equal(&Dae_df, &Dae_df_exp, sizeof(Dae_df)); + assert_memory_equal(&Records_df, &Records_df_exp, sizeof(umem_off_t) * num); +} + +static void +test_discard_noninline_one(void **unused) +{ + /* 11. discard just one noninline record */ + prep_discard_one_common(discard_noninline_one_execute); +} + +#define RAND_SEED 2025 +#define RAND_RECORDS_NUM_MAX (RECORDS_MAX + DTX_INLINE_REC_CNT) + +static void +test_discard_rand(void **unused) +{ + int discarded = 0; + int discarded_exp = 0; + /* tx_add_ptr() it is called on condition at least one record in a group is about to be + * discarded */ + bool call_tx_add_ptr; + int rc; + + srand(RAND_SEED); + + int num = rand() % RAND_RECORDS_NUM_MAX; + struct rec_valid *recs = calloc(num, sizeof(struct rec_valid)); + for (int i = 0; i < num; ++i) { + recs[i].type = rand() % DTX_RT_MAX + DTX_RT_MIN; + recs[i].valid = (rand() % 2 == 0 ? true : false); + + if (!recs[i].valid) { + ++discarded_exp; + } + } + + printf("srand(%d), num=%d, discarded=%d\n", RAND_SEED, num, discarded_exp); + + will_return(__wrap_dbtree_lookup, &Dae); + will_return(tx_begin, 0); + + /* Note: The nonline records are processed first hence they have to be initialized first as + * well. */ + if (num > DTX_INLINE_REC_CNT) { + call_tx_add_ptr = + prep_records_noninline(&recs[DTX_INLINE_REC_CNT], num - DTX_INLINE_REC_CNT); + if (call_tx_add_ptr) { + will_return(tx_add_ptr, 0); + } + } + call_tx_add_ptr = prep_records_inline(recs, min(num, DTX_INLINE_REC_CNT)); + if (call_tx_add_ptr) { + will_return(tx_add_ptr, 0); + } + + /* Account for both inline and noninline records. */ + Dae.dae_base.dae_rec_cnt = num; + + will_return(tx_commit, 0); + rc = vos_dtx_discard_invalid(Coh, DTX_ID_PTR, &discarded); + assert_int_equal(rc, 0); + assert_int_equal(discarded, discarded_exp); + assert_memory_equal(&Dae_df, &Dae_df_exp, sizeof(Dae_df)); + if (num > DTX_INLINE_REC_CNT) { + assert_memory_equal(&Records_df, &Records_df_exp, + sizeof(umem_off_t) * (num - DTX_INLINE_REC_CNT)); + } +} + +/* setup & teardown */ + +static umem_ops_t umm_ops = {.mo_tx_begin = tx_begin, + .mo_tx_commit = tx_commit, + .mo_tx_abort = tx_abort, + .mo_tx_add_ptr = tx_add_ptr}; + +static void +reset_dfs() +{ + /* durable format mocks primed with a pattern intentionally to detect UMOFF_NULL (discard) + * when set */ + memset(&Dae_df, 0xef, sizeof(Dae_df)); + memset(&Dae_df_exp, 0xef, sizeof(Dae_df)); + memset(&Records_df, 0xef, sizeof(Records_df)); + memset(&Records_df_exp, 0xef, sizeof(Records_df)); +} + +static int +setup_cont(void **unused) +{ + /* reset globals */ + memset(&Pool, 0, sizeof(Pool)); + memset(&Cont, 0, sizeof(Cont)); + memset(&Dae, 0, sizeof(Dae)); + memset(&Records, 0, sizeof(Records)); + In_tx = false; + + reset_dfs(); + + Pool.vp_umm.umm_ops = &umm_ops; + Cont.vc_pool = &Pool; + Cont.vc_dtx_active_hdl.cookie = VC_DTX_ACTIVE_HDL; + Coh.cookie = (uint64_t)&Cont; + Dae.dae_df_off = umem_ptr2off(&Pool.vp_umm, &Dae_df); + DAE_LID(&Dae) = DTX_LID; + DAE_EPOCH(&Dae) = EPOCH; + + return 0; +} + +static int +teardown_cont(void **unused) +{ + /* nop */ + return 0; +} + +/* compilation unit's entry point */ +#define TEST(name, func) \ + { \ + name ": vos_dtx_discard_invalid - " #func, test_##func, setup_cont, teardown_cont \ + } + +static const struct CMUnitTest discard_invalid_tests_all[] = { + TEST("DTX400", missing_things), TEST("DTX401", aborted_dae), + TEST("DTX402", tx_begin_fail), TEST("DTX403", tx_abort_fail), + TEST("DTX404", tx_add_ptr_inline_fail), TEST("DTX405", tx_add_ptr_noninline_fail), + TEST("DTX406", tx_commit_fail), TEST("DTX407", discard_inline_all), + TEST("DTX408", discard_inline_one), TEST("DTX409", discard_noninline_all), + TEST("DTX410", discard_noninline_one), TEST("DTX411", discard_rand), +}; + +int +run_discard_invalid_tests(void) +{ + const char *test_name = "vos_dtx_discard_invalid"; + + return cmocka_run_group_tests_name(test_name, discard_invalid_tests_all, NULL, NULL); +} diff --git a/src/dtx/tests/dts_structs.c b/src/dtx/tests/dts_structs.c index f73eaad6e2f..bddfdf9816c 100644 --- a/src/dtx/tests/dts_structs.c +++ b/src/dtx/tests/dts_structs.c @@ -1,6 +1,6 @@ /** * (C) Copyright 2024 Intel Corporation. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -11,11 +11,13 @@ #include #include +#include +#include +#include #include #include #include - -#include "vts_io.h" +#include #define SET_STRUCT_COMMON(a, c) memset((void *)(&(a)), c, sizeof(a)) @@ -129,7 +131,7 @@ static const struct CMUnitTest structs_tests_all[] = { }; int -run_structs_tests(const char *cfg) +run_structs_tests(void) { const char *test_name = "DTX structs checks"; diff --git a/src/dtx/tests/dtx_tests.c b/src/dtx/tests/dtx_tests.c index 6197e3680bb..e5ca2100d1a 100644 --- a/src/dtx/tests/dtx_tests.c +++ b/src/dtx/tests/dtx_tests.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2023-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -23,8 +24,6 @@ int run_local_tests(const char *cfg); int run_local_rdb_tests(const char *cfg); -int -run_structs_tests(const char *cfg); static void print_usage() @@ -49,7 +48,6 @@ run_all_tests(int keys) failed += run_local_tests(cfg_desc_io); failed += run_local_rdb_tests(cfg_desc_io); - failed += run_structs_tests(cfg_desc_io); return failed; } diff --git a/src/dtx/tests/dtx_ut.c b/src/dtx/tests/dtx_ut.c new file mode 100644 index 00000000000..e608c6c6964 --- /dev/null +++ b/src/dtx/tests/dtx_ut.c @@ -0,0 +1,113 @@ +/** + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +/** + * Launcher for all DTX unit tests. + */ +#define D_LOGFAC DD_FAC(tests) + +#include +#include +#include +#include +#include +#include +#include +#include + +int +run_structs_tests(void); +int +run_discard_invalid_tests(void); + +static void +print_usage() +{ + print_message("Use one of these opt(s) for specific test\n"); + print_message("dtx_ut -h|--help\n"); + print_message("Default runs all tests\n"); + print_message("The following options can be used with any of the above:\n"); + print_message(" -f|--filter \n"); + print_message(" -e|--exclude \n"); +} + +static inline int +run_all_tests(int keys) +{ + int failed = 0; + + failed += run_structs_tests(); + failed += run_discard_invalid_tests(); + + return failed; +} + +int +main(int argc, char **argv) +{ + int rc = 0; + int nr_failed = 0; + int opt = 0; + int index = 0; + const char *short_options = "he:f:"; + static struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"exclude", required_argument, 0, 'e'}, + {"filter", required_argument, 0, 'f'}, + {NULL}, + }; + + d_register_alt_assert(mock_assert); + + rc = daos_debug_init(DAOS_LOG_DEFAULT); + if (rc) { + print_error("Error initializing debug system\n"); + return rc; + } + + while ((opt = getopt_long(argc, argv, short_options, long_options, &index)) != -1) { + switch (opt) { + case 'h': + print_usage(); + goto exit_0; + + case 'e': +#if CMOCKA_FILTER_SUPPORTED == 1 /** requires cmocka 1.1.5 */ + cmocka_set_skip_filter(optarg); +#else + D_PRINT("filter not enabled"); +#endif + + break; + case 'f': +#if CMOCKA_FILTER_SUPPORTED == 1 /** requires cmocka 1.1.5 */ + { + /** Add wildcards for easier filtering */ + char filter[sizeof(optarg) + 2]; + + sprintf(filter, "*%s*", optarg); + cmocka_set_test_filter(filter); + printf("Test filter: %s\n", filter); + } +#else + D_PRINT("filter not enabled"); +#endif + break; + default: + break; + } + } + + nr_failed = run_all_tests(0); + + if (nr_failed) + print_error("ERROR, %i TEST(S) FAILED\n", nr_failed); + else + print_message("\nSUCCESS! NO TEST FAILURES\n"); + +exit_0: + daos_debug_fini(); + return nr_failed; +} diff --git a/src/dtx/tests/sched_mock.c b/src/dtx/tests/sched_mock.c index 470246ebec2..a85d72897b3 100644 --- a/src/dtx/tests/sched_mock.c +++ b/src/dtx/tests/sched_mock.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -59,3 +60,10 @@ sched_req_wait(struct sched_request *req, bool abort) { assert_true(false); } + +uint64_t +sched_cur_seq(void) +{ + assert_true(false); + return UINT64_MAX; +} diff --git a/src/include/daos_srv/evtree.h b/src/include/daos_srv/evtree.h index 292c8848c87..a29f9312168 100644 --- a/src/include/daos_srv/evtree.h +++ b/src/include/daos_srv/evtree.h @@ -1,5 +1,6 @@ /** * (C) Copyright 2017-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -814,4 +815,16 @@ evt_feats_get(struct evt_root *root) */ int evt_feats_set(struct evt_root *root, struct umem_instance *umm, uint64_t feats); +/** Validate the provided evt. + * + * Note: It is designed for catastrophic recovery. Not to perform at run-time. + * + * \param evt[in] + * \param dtx_lid[in] local id of the DTX entry the evt is supposed to belong to + * + * \return true if evt is valid. + **/ +bool +evt_desc_is_valid(const struct evt_desc *evt, uint32_t dtx_lid); + #endif /* __DAOS_EV_TREE_H__ */ diff --git a/src/include/daos_srv/vos.h b/src/include/daos_srv/vos.h index 536efc778ef..f3d011190ee 100644 --- a/src/include/daos_srv/vos.h +++ b/src/include/daos_srv/vos.h @@ -1,6 +1,6 @@ /** * (C) Copyright 2015-2024 Intel Corporation. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -190,6 +190,18 @@ vos_dtx_commit(daos_handle_t coh, struct dtx_id dtis[], int count, bool keep_act int vos_dtx_abort(daos_handle_t coh, struct dtx_id *dti, daos_epoch_t epoch); +/** + * Discard the active DTX entry's records if invalid. + * + * \param coh [IN] Container open handle. + * \param dti [IN] The DTX identifier to be validated. + * \param discarded [OUT] The number of discarded records. + * + * \return Zero on success, negative value if error. + */ +int +vos_dtx_discard_invalid(daos_handle_t coh, struct dtx_id *dti, int *discarded); + /** * Set flags on the active DTXs. * diff --git a/src/utils/ddb/ddb.c b/src/utils/ddb/ddb.c index 8493852f24f..07b96aa031f 100644 --- a/src/utils/ddb/ddb.c +++ b/src/utils/ddb/ddb.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2022-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -37,6 +38,7 @@ #define COMMAND_NAME_DTX_ACT_ABORT "dtx_act_abort" #define COMMAND_NAME_FEATURE "feature" #define COMMAND_NAME_RM_POOL "rm_pool" +#define COMMAND_NAME_DTX_ACT_DISCARD_INVALID "dtx_act_discard_invalid" /* Parse command line options for the 'ls' command */ static int @@ -542,55 +544,13 @@ vea_update_option_parse(struct ddb_ctx *ctx, struct vea_update_options *cmd_args return 0; } -/* Parse command line options for the 'dtx_act_commit' command */ -static int -dtx_act_commit_option_parse(struct ddb_ctx *ctx, struct dtx_act_commit_options *cmd_args, - uint32_t argc, char **argv) -{ - char *options_short = ""; - int index = 0; - struct option options_long[] = { - { NULL } - }; - - memset(cmd_args, 0, sizeof(*cmd_args)); - - /* Restart getopt */ - optind = 1; - opterr = 0; - if (getopt_long(argc, argv, options_short, options_long, &index) != -1) { - ddb_printf(ctx, "Unknown option: '%c'\n", optopt); - return -DER_INVAL; - } - - index = optind; - if (argc - index > 0) { - cmd_args->path = argv[index]; - index++; - } else { - ddb_print(ctx, "Expected argument 'path'\n"); - return -DER_INVAL; - } - if (argc - index > 0) { - cmd_args->dtx_id = argv[index]; - index++; - } else { - ddb_print(ctx, "Expected argument 'dtx_id'\n"); - return -DER_INVAL; - } - - if (argc - index > 0) { - ddb_printf(ctx, "Unexpected argument: %s\n", argv[index]); - return -DER_INVAL; - } - - return 0; -} - -/* Parse command line options for the 'dtx_act_abort' command */ +/** + * Parse command line options for the 'dtx_act_commit', 'dtx_act_abort', and 'dtx_act_abort' + * commands. + */ static int -dtx_act_abort_option_parse(struct ddb_ctx *ctx, struct dtx_act_abort_options *cmd_args, - uint32_t argc, char **argv) +dtx_act_option_parse(struct ddb_ctx *ctx, struct dtx_act_options *cmd_args, uint32_t argc, + char **argv) { char *options_short = ""; int index = 0; @@ -854,13 +814,15 @@ ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_c } if (same(cmd, COMMAND_NAME_DTX_ACT_COMMIT)) { info->dci_cmd = DDB_CMD_DTX_ACT_COMMIT; - return dtx_act_commit_option_parse(ctx, &info->dci_cmd_option.dci_dtx_act_commit, - argc, argv); + return dtx_act_option_parse(ctx, &info->dci_cmd_option.dci_dtx_act, argc, argv); } if (same(cmd, COMMAND_NAME_DTX_ACT_ABORT)) { info->dci_cmd = DDB_CMD_DTX_ACT_ABORT; - return dtx_act_abort_option_parse(ctx, &info->dci_cmd_option.dci_dtx_act_abort, - argc, argv); + return dtx_act_option_parse(ctx, &info->dci_cmd_option.dci_dtx_act, argc, argv); + } + if (same(cmd, COMMAND_NAME_DTX_ACT_DISCARD_INVALID)) { + info->dci_cmd = DDB_CMD_DTX_ACT_DISCARD_INVALID; + return dtx_act_option_parse(ctx, &info->dci_cmd_option.dci_dtx_act, argc, argv); } if (same(cmd, COMMAND_NAME_RM_POOL)) { info->dci_cmd = DDB_CMD_RM_POOL; @@ -1043,11 +1005,15 @@ ddb_run_cmd(struct ddb_ctx *ctx, const char *cmd_str) break; case DDB_CMD_DTX_ACT_COMMIT: - rc = ddb_run_dtx_act_commit(ctx, &info.dci_cmd_option.dci_dtx_act_commit); + rc = ddb_run_dtx_act_commit(ctx, &info.dci_cmd_option.dci_dtx_act); break; case DDB_CMD_DTX_ACT_ABORT: - rc = ddb_run_dtx_act_abort(ctx, &info.dci_cmd_option.dci_dtx_act_abort); + rc = ddb_run_dtx_act_abort(ctx, &info.dci_cmd_option.dci_dtx_act); + break; + + case DDB_CMD_DTX_ACT_DISCARD_INVALID: + rc = ddb_run_dtx_act_discard_invalid(ctx, &info.dci_cmd_option.dci_dtx_act); break; case DDB_CMD_FEATURE: diff --git a/src/utils/ddb/ddb.h b/src/utils/ddb/ddb.h index a82bb292239..df8cf699d5d 100644 --- a/src/utils/ddb/ddb.h +++ b/src/utils/ddb/ddb.h @@ -1,5 +1,6 @@ /** * (C) Copyright 2022-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -94,29 +95,30 @@ int ddb_init(void); void ddb_fini(void); enum ddb_cmd { - DDB_CMD_UNKNOWN = 0, - DDB_CMD_HELP = 1, - DDB_CMD_QUIT = 2, - DDB_CMD_LS = 3, - DDB_CMD_OPEN = 4, - DDB_CMD_VERSION = 5, - DDB_CMD_CLOSE = 6, - DDB_CMD_SUPERBLOCK_DUMP = 7, - DDB_CMD_VALUE_DUMP = 8, - DDB_CMD_RM = 9, - DDB_CMD_VALUE_LOAD = 10, - DDB_CMD_ILOG_DUMP = 11, - DDB_CMD_ILOG_COMMIT = 12, - DDB_CMD_ILOG_CLEAR = 13, - DDB_CMD_DTX_DUMP = 14, - DDB_CMD_DTX_CMT_CLEAR = 15, - DDB_CMD_SMD_SYNC = 16, - DDB_CMD_VEA_DUMP = 17, - DDB_CMD_VEA_UPDATE = 18, - DDB_CMD_DTX_ACT_COMMIT = 19, - DDB_CMD_DTX_ACT_ABORT = 20, - DDB_CMD_FEATURE = 21, - DDB_CMD_RM_POOL = 22, + DDB_CMD_UNKNOWN = 0, + DDB_CMD_HELP = 1, + DDB_CMD_QUIT = 2, + DDB_CMD_LS = 3, + DDB_CMD_OPEN = 4, + DDB_CMD_VERSION = 5, + DDB_CMD_CLOSE = 6, + DDB_CMD_SUPERBLOCK_DUMP = 7, + DDB_CMD_VALUE_DUMP = 8, + DDB_CMD_RM = 9, + DDB_CMD_VALUE_LOAD = 10, + DDB_CMD_ILOG_DUMP = 11, + DDB_CMD_ILOG_COMMIT = 12, + DDB_CMD_ILOG_CLEAR = 13, + DDB_CMD_DTX_DUMP = 14, + DDB_CMD_DTX_CMT_CLEAR = 15, + DDB_CMD_SMD_SYNC = 16, + DDB_CMD_VEA_DUMP = 17, + DDB_CMD_VEA_UPDATE = 18, + DDB_CMD_DTX_ACT_COMMIT = 19, + DDB_CMD_DTX_ACT_ABORT = 20, + DDB_CMD_FEATURE = 21, + DDB_CMD_RM_POOL = 22, + DDB_CMD_DTX_ACT_DISCARD_INVALID = 23, }; /* option and argument structures for commands that need them */ @@ -177,12 +179,7 @@ struct vea_update_options { char *blk_cnt; }; -struct dtx_act_commit_options { - char *path; - char *dtx_id; -}; - -struct dtx_act_abort_options { +struct dtx_act_options { char *path; char *dtx_id; }; @@ -214,11 +211,10 @@ struct ddb_cmd_info { struct dtx_dump_options dci_dtx_dump; struct dtx_cmt_clear_options dci_dtx_cmt_clear; struct smd_sync_options dci_smd_sync; - struct vea_update_options dci_vea_update; - struct dtx_act_commit_options dci_dtx_act_commit; - struct dtx_act_abort_options dci_dtx_act_abort; + struct vea_update_options dci_vea_update; struct feature_options dci_feature; struct rm_pool_options dci_rm_pool; + struct dtx_act_options dci_dtx_act; } dci_cmd_option; }; @@ -247,15 +243,19 @@ int ddb_run_dtx_cmt_clear(struct ddb_ctx *ctx, struct dtx_cmt_clear_options *opt int ddb_run_smd_sync(struct ddb_ctx *ctx, struct smd_sync_options *opt); int ddb_run_vea_dump(struct ddb_ctx *ctx); int ddb_run_vea_update(struct ddb_ctx *ctx, struct vea_update_options *opt); -int ddb_run_dtx_act_commit(struct ddb_ctx *ctx, struct dtx_act_commit_options *opt); -int ddb_run_dtx_act_abort(struct ddb_ctx *ctx, struct dtx_act_abort_options *opt); +int +ddb_run_dtx_act_commit(struct ddb_ctx *ctx, struct dtx_act_options *opt); +int +ddb_run_dtx_act_abort(struct ddb_ctx *ctx, struct dtx_act_options *opt); int ddb_run_feature(struct ddb_ctx *ctx, struct feature_options *opt); int ddb_feature_string2flags(struct ddb_ctx *ctx, const char *string, uint64_t *compat_flags, uint64_t *incompat_flags); int - ddb_run_rm_pool(struct ddb_ctx *ctx, struct rm_pool_options *opt); +ddb_run_rm_pool(struct ddb_ctx *ctx, struct rm_pool_options *opt); +int + ddb_run_dtx_act_discard_invalid(struct ddb_ctx *ctx, struct dtx_act_options *opt); void ddb_program_help(struct ddb_ctx *ctx); void ddb_commands_help(struct ddb_ctx *ctx); diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 3dd2261f504..fca43b71f57 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2022-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -886,6 +887,7 @@ ddb_run_vea_update(struct ddb_ctx *ctx, struct vea_update_options *opt) struct dtx_modify_args { struct dv_indexed_tree_path itp; struct dtx_id dti; + bool dti_all; daos_handle_t coh; }; @@ -914,10 +916,12 @@ dtx_modify_init(struct ddb_ctx *ctx, char *path, char *dtx_id_str, struct dtx_mo D_GOTO(error, rc); } - rc = ddb_parse_dtx_id(dtx_id_str, &args->dti); - if (!SUCCESS(rc)) { - ddb_errorf(ctx, "Invalid dtx_id: %s\n", dtx_id_str); - D_GOTO(error, rc); + if (!args->dti_all) { + rc = ddb_parse_dtx_id(dtx_id_str, &args->dti); + if (!SUCCESS(rc)) { + ddb_errorf(ctx, "Invalid dtx_id: %s\n", dtx_id_str); + D_GOTO(error, rc); + } } return 0; @@ -935,7 +939,7 @@ dtx_modify_fini(struct dtx_modify_args *args) } int -ddb_run_dtx_act_commit(struct ddb_ctx *ctx, struct dtx_act_commit_options *opt) +ddb_run_dtx_act_commit(struct ddb_ctx *ctx, struct dtx_act_options *opt) { struct dtx_modify_args args = {0}; int rc; @@ -964,7 +968,8 @@ ddb_run_dtx_act_commit(struct ddb_ctx *ctx, struct dtx_act_commit_options *opt) return rc; } -int ddb_run_dtx_act_abort(struct ddb_ctx *ctx, struct dtx_act_abort_options *opt) +int +ddb_run_dtx_act_abort(struct ddb_ctx *ctx, struct dtx_act_options *opt) { struct dtx_modify_args args = {0}; int rc; @@ -1075,3 +1080,69 @@ ddb_run_rm_pool(struct ddb_ctx *ctx, struct rm_pool_options *opt) return dv_pool_destroy(opt->path); } + +#define DTI_ALL "all" + +struct dtx_active_entry_discard_invalid_cb_arg { + struct ddb_ctx *ctx; + struct dtx_modify_args *args; +}; + +static int +dtx_active_entry_discard_invalid(struct dv_dtx_active_entry *entry, void *cb_arg) +{ + struct dtx_active_entry_discard_invalid_cb_arg *bundle = cb_arg; + struct ddb_ctx *ctx = bundle->ctx; + struct dtx_modify_args *args = bundle->args; + int discarded = 0; + int rc; + + ddb_printf(ctx, "ID: " DF_DTIF "\n", DP_DTI(&entry->ddtx_id)); + + rc = dv_dtx_active_entry_discard_invalid(args->coh, &entry->ddtx_id, &discarded); + if (SUCCESS(rc)) { + ddb_printf(ctx, "Entry's record(s) discarded: %d\n", discarded); + } else if (rc == -DER_NONEXIST) { + ddb_print(ctx, "No entry found\n"); + rc = 0; + } else { + ddb_errorf(ctx, "Error: " DF_RC "\n", DP_RC(rc)); + } + + return 0; +} + +int +ddb_run_dtx_act_discard_invalid(struct ddb_ctx *ctx, struct dtx_act_options *opt) +{ + struct dtx_modify_args args = {0}; + struct dtx_active_entry_discard_invalid_cb_arg bundle = {.ctx = ctx, .args = &args}; + int rc; + + if (!ctx->dc_write_mode) { + ddb_error(ctx, error_msg_write_mode_only); + return -DER_INVAL; + } + + if (opt->dtx_id != NULL && strcmp(opt->dtx_id, DTI_ALL) == 0) { + args.dti_all = true; + } + + rc = dtx_modify_init(ctx, opt->path, opt->dtx_id, &args); + if (!SUCCESS(rc)) { + return rc; + } + + if (args.dti_all) { + rc = dv_dtx_get_act_table(args.coh, dtx_active_entry_discard_invalid, &bundle); + if (!SUCCESS(rc)) { + return rc; + } + } else { + struct dv_dtx_active_entry entry = {.ddtx_id = args.dti}; + dtx_active_entry_discard_invalid(&entry, &bundle); + } + + dtx_modify_fini(&args); + return rc; +} diff --git a/src/utils/ddb/ddb_vos.c b/src/utils/ddb/ddb_vos.c index fe36ceffed7..ecf6ba5ba3d 100644 --- a/src/utils/ddb/ddb_vos.c +++ b/src/utils/ddb/ddb_vos.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2022-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -1424,6 +1425,12 @@ dv_dtx_abort_active_entry(daos_handle_t coh, struct dtx_id *dti) return vos_dtx_abort(coh, dti, DAOS_EPOCH_MAX); } +int +dv_dtx_active_entry_discard_invalid(daos_handle_t coh, struct dtx_id *dti, int *discarded) +{ + return vos_dtx_discard_invalid(coh, dti, discarded); +} + int dv_delete(daos_handle_t poh, struct dv_tree_path *vtp) { diff --git a/src/utils/ddb/ddb_vos.h b/src/utils/ddb/ddb_vos.h index e4a2ad26992..790d62f431b 100644 --- a/src/utils/ddb/ddb_vos.h +++ b/src/utils/ddb/ddb_vos.h @@ -1,5 +1,6 @@ /** * (C) Copyright 2022-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -190,6 +191,8 @@ int dv_dtx_get_act_table(daos_handle_t coh, dv_dtx_act_handler handler_cb, void int dv_dtx_clear_cmt_table(daos_handle_t coh); int dv_dtx_commit_active_entry(daos_handle_t coh, struct dtx_id *dti); int dv_dtx_abort_active_entry(daos_handle_t coh, struct dtx_id *dti); +int +dv_dtx_active_entry_discard_invalid(daos_handle_t coh, struct dtx_id *dti, int *discarded); /* Sync the smd table with information saved in blobs */ typedef int (*dv_smd_sync_complete)(void *cb_args, uuid_t pool_id, uint32_t vos_id, diff --git a/src/utils/ddb/tests/SConscript b/src/utils/ddb/tests/SConscript index f502847a597..c26e7deb768 100644 --- a/src/utils/ddb/tests/SConscript +++ b/src/utils/ddb/tests/SConscript @@ -26,16 +26,16 @@ def scons(): libs = ['vos', 'daos_common_pmem', 'abt', 'gurt', 'uuid', 'bio', 'cart', 'cmocka', 'ddb'] # spdk libraries - libs += ['spdk_event', 'spdk_log'] - libs += ['spdk_bdev', 'spdk_blob', 'spdk_blob_bdev', 'spdk_json'] - libs += ['spdk_nvme', 'spdk_init', 'spdk_thread', 'spdk_log'] - libs += ['spdk_env_dpdk', 'spdk_thread', 'spdk_bdev', 'rte_mempool'] - libs += ['rte_mempool_ring', 'rte_bus_pci', 'rte_pci', 'rte_ring'] - libs += ['rte_mbuf', 'rte_eal', 'rte_kvargs', 'spdk_bdev_aio'] - libs += ['spdk_bdev_nvme', 'spdk_blob', 'spdk_nvme', 'spdk_util'] - libs += ['spdk_json', 'spdk_jsonrpc', 'spdk_rpc', 'spdk_trace'] - libs += ['spdk_sock', 'spdk_log', 'spdk_notify', 'spdk_blob_bdev'] - libs += ['spdk_vmd', 'spdk_event_bdev', 'spdk_init', 'rte_power'] + spdk_libs = ['spdk_event', 'spdk_log'] + spdk_libs += ['spdk_bdev', 'spdk_blob', 'spdk_blob_bdev', 'spdk_json'] + spdk_libs += ['spdk_nvme', 'spdk_init', 'spdk_thread', 'spdk_log'] + spdk_libs += ['spdk_env_dpdk', 'spdk_thread', 'spdk_bdev', 'rte_mempool'] + spdk_libs += ['rte_mempool_ring', 'rte_bus_pci', 'rte_pci', 'rte_ring'] + spdk_libs += ['rte_mbuf', 'rte_eal', 'rte_kvargs', 'spdk_bdev_aio'] + spdk_libs += ['spdk_bdev_nvme', 'spdk_blob', 'spdk_nvme', 'spdk_util'] + spdk_libs += ['spdk_json', 'spdk_jsonrpc', 'spdk_rpc', 'spdk_trace'] + spdk_libs += ['spdk_sock', 'spdk_log', 'spdk_notify', 'spdk_blob_bdev'] + spdk_libs += ['spdk_vmd', 'spdk_event_bdev', 'spdk_init', 'rte_power'] src = ['ddb_cmd_options_tests.c', 'ddb_commands_tests.c', 'ddb_main_tests.c', @@ -44,10 +44,41 @@ def scons(): 'ddb_test_driver.c', 'ddb_vos_tests.c', 'ddb_commands_print_tests.c'] - ddb_tests = denv.d_program('ddb_tests', [src], LIBS=libs) + ddb_tests = denv.d_program('ddb_tests', [src], LIBS=libs + spdk_libs) denv.Install('$PREFIX/bin/', ddb_tests) + # Build unit tests + denv = env.Clone() + prereqs.require(denv, 'argobots', 'spdk') + libs = ['uuid', 'daos_common_pmem', 'gurt', 'vea', 'abt', 'bio', 'cmocka', 'pthread'] + denv.AppendUnique(RPATH_FULL=['$PREFIX/lib64/daos_srv']) + denv.AppendUnique(CPPPATH=[Dir('../').srcnode()]) + denv.AppendUnique(CPPPATH=[Dir('../../../vos/').srcnode()]) + denv.AppendUnique(CPPPATH=[Dir('../../../bio/').srcnode()]) + # Required for dtx_act_discard_invalid tests. + # This function is validated by its respective unit tests. + denv.AppendUnique(LINKFLAGS=['-Wl,--wrap=vos_dtx_discard_invalid']) + + denv.Append(CPPDEFINES=['-DDAOS_PMEM_BUILD']) + vos_src = Glob('../../../vos/*.c') + + mock_src = Glob('../../../dtx/tests/*_mock.c') + src = ['ddb_ut.c', + 'ddb_vos_ut.c', + '../ddb_commands.c', + '../ddb_vos.c', + '../ddb_main.c', + '../ddb_printer.c', + '../ddb_tree_path.c', + '../ddb_parse.c', + '../ddb.c', + '../ddb_spdk.c', + ] + ddb_ut = denv.d_program('ddb_ut', src + vos_src + mock_src, LIBS=libs + spdk_libs) + + denv.Install('$PREFIX/bin/', ddb_ut) + if __name__ == "SCons.Script": scons() diff --git a/src/utils/ddb/tests/ddb_cmd_options_tests.c b/src/utils/ddb/tests/ddb_cmd_options_tests.c index 643815bb629..9481748af0a 100644 --- a/src/utils/ddb/tests/ddb_cmd_options_tests.c +++ b/src/utils/ddb/tests/ddb_cmd_options_tests.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2022 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -268,7 +269,7 @@ static void dtx_act_commit_options_parsing(void **state) { struct ddb_cmd_info info = {0}; - struct dtx_act_commit_options *options = &info.dci_cmd_option.dci_dtx_act_commit; + struct dtx_act_options *options = &info.dci_cmd_option.dci_dtx_act; /* test invalid arguments and options */ test_run_inval_cmd("dtx_act_commit", "path", "dtx_id", "extra"); /* too many argument */ @@ -284,7 +285,7 @@ static void dtx_act_abort_options_parsing(void **state) { struct ddb_cmd_info info = {0}; - struct dtx_act_abort_options *options = &info.dci_cmd_option.dci_dtx_act_abort; + struct dtx_act_options *options = &info.dci_cmd_option.dci_dtx_act; /* test invalid arguments and options */ test_run_inval_cmd("dtx_act_abort", "path", "dtx_id", "extra"); /* too many argument */ diff --git a/src/utils/ddb/tests/ddb_commands_tests.c b/src/utils/ddb/tests/ddb_commands_tests.c index ee3b8175d6a..b0fdbf5ad89 100644 --- a/src/utils/ddb/tests/ddb_commands_tests.c +++ b/src/utils/ddb/tests/ddb_commands_tests.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2022-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -339,7 +340,7 @@ clear_cmt_dtx_cmd_tests(void **state) static void dtx_commit_entry_tests(void **state) { - struct dtx_act_commit_options opt = {0}; + struct dtx_act_options opt = {0}; assert_invalid(ddb_run_dtx_act_commit(&g_ctx, &opt)); opt.path = "[0]/[0]"; @@ -352,7 +353,7 @@ dtx_commit_entry_tests(void **state) static void dtx_abort_entry_tests(void **state) { - struct dtx_act_abort_options opt = {0}; + struct dtx_act_options opt = {0}; assert_invalid(ddb_run_dtx_act_abort(&g_ctx, &opt)); @@ -362,6 +363,27 @@ dtx_abort_entry_tests(void **state) assert_success(ddb_run_dtx_act_abort(&g_ctx, &opt)); } +static void +dtx_act_discard_invalid_tests(void **state) +{ + struct dtx_act_options opt = {0}; + + g_ctx.dc_write_mode = false; + assert_invalid(ddb_run_dtx_act_discard_invalid(&g_ctx, &opt)); + + g_ctx.dc_write_mode = true; + assert_invalid(ddb_run_dtx_act_discard_invalid(&g_ctx, &opt)); + + opt.path = "[0]/[0]"; + assert_invalid(ddb_run_dtx_act_discard_invalid(&g_ctx, &opt)); + + opt.dtx_id = "12345678-1234-1234-1234-123456789012.1234"; + assert_success(ddb_run_dtx_act_discard_invalid(&g_ctx, &opt)); + + opt.dtx_id = "all"; + assert_success(ddb_run_dtx_act_discard_invalid(&g_ctx, &opt)); +} + static void feature_cmd_tests(void **state) { @@ -431,6 +453,7 @@ ddb_commands_tests_run() TEST(process_ilog_cmd_tests), TEST(clear_cmt_dtx_cmd_tests), TEST(dtx_commit_entry_tests), + TEST(dtx_act_discard_invalid_tests), TEST(dtx_abort_entry_tests), TEST(feature_cmd_tests), }; diff --git a/src/utils/ddb/tests/ddb_ut.c b/src/utils/ddb/tests/ddb_ut.c new file mode 100644 index 00000000000..3ea69695d55 --- /dev/null +++ b/src/utils/ddb/tests/ddb_ut.c @@ -0,0 +1,104 @@ +/** + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ddb.h" + +int +ddb_vos_tests_run(void); + +struct ddb_test_driver_arguments { + bool dtda_create_vos_file; +}; + +static int +ddb_test_driver_arguments_parse(uint32_t argc, char **argv, struct ddb_test_driver_arguments *args) +{ + struct option program_options[] = {{"create_vos", optional_argument, NULL, 'c'}, {NULL}}; + int index = 0, opt; + + memset(args, 0, sizeof(*args)); + + optind = 1; + opterr = 0; + while ((opt = getopt_long(argc, argv, "c", program_options, &index)) != -1) { + switch (opt) { + case 'c': + args->dtda_create_vos_file = true; + break; + case '?': + printf("'%c' is unknown\n", optopt); + return -DER_INVAL; + default: + return -DER_INVAL; + } + } + + return 0; +} + +static bool +char_in_tests(char a, char *str, uint32_t str_len) +{ + int i; + + if (strlen(str) == 0) /* if there is no filter, always return true */ + return true; + for (i = 0; i < str_len; i++) { + if (a == str[i]) + return true; + } + + return false; +} + +/* + * ----------------------------------------------- + * Execute + * ----------------------------------------------- + */ +int +main(int argc, char *argv[]) +{ + struct ddb_test_driver_arguments args = {0}; + int rc; + + rc = ddb_init(); + if (rc != 0) + return -rc; + + ddb_test_driver_arguments_parse(argc, argv, &args); + + assert_false(args.dtda_create_vos_file); + +#define RUN_TEST_SUIT(c, func) \ + do { \ + if (char_in_tests(c, test_suites, ARRAY_SIZE(test_suites))) \ + rc += func(); \ + } while (0) + + /* filtering suites and tests */ + char test_suites[] = ""; +#if CMOCKA_FILTER_SUPPORTED == 1 /** requires cmocka 1.1.5 */ + cmocka_set_test_filter("*dtx_act_discard_invalid*"); +#endif + RUN_TEST_SUIT('c', ddb_vos_tests_run); + + ddb_fini(); + if (rc > 0) + printf("%d test(s) failed!\n", rc); + else + printf("All tests successful!\n"); + return rc; +} diff --git a/src/utils/ddb/tests/ddb_vos_ut.c b/src/utils/ddb/tests/ddb_vos_ut.c new file mode 100644 index 00000000000..a4f943bd6ad --- /dev/null +++ b/src/utils/ddb/tests/ddb_vos_ut.c @@ -0,0 +1,57 @@ +/** + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +#include +#include +#include +#include +#include "ddb_vos.h" + +#define COH_COOKIE 0x1515 +#define DTX_ID_PTR ((struct dtx_id *)0x6367) +#define DISCARDED_PTR ((int *)0x9303) + +int +__wrap_vos_dtx_discard_invalid(daos_handle_t coh, struct dtx_id *dti, int *discarded) +{ + assert_int_equal(coh.cookie, COH_COOKIE); + assert_ptr_equal(dti, DTX_ID_PTR); + assert_ptr_equal(discarded, DISCARDED_PTR); + + return mock(); +} + +#define SOME_ERROR (-DER_BAD_CERT) + +static void +dtx_act_discard_invalid_test(void **state) +{ + daos_handle_t coh = {.cookie = COH_COOKIE}; + int rc; + + will_return(__wrap_vos_dtx_discard_invalid, SOME_ERROR); + rc = dv_dtx_active_entry_discard_invalid(coh, DTX_ID_PTR, DISCARDED_PTR); + assert_int_equal(rc, SOME_ERROR); + + will_return(__wrap_vos_dtx_discard_invalid, 0); + rc = dv_dtx_active_entry_discard_invalid(coh, DTX_ID_PTR, DISCARDED_PTR); + assert_int_equal(rc, 0); +} + +#define TEST(x) \ + { \ + #x, x##_test, NULL, NULL \ + } + +const struct CMUnitTest dv_test_cases[] = { + TEST(dtx_act_discard_invalid), +}; + +int +ddb_vos_tests_run() +{ + return cmocka_run_group_tests_name("DDB VOS Interface Unit Tests", dv_test_cases, NULL, + NULL); +} diff --git a/src/vos/evtree.c b/src/vos/evtree.c index 59f8855c3c1..20b803192bd 100644 --- a/src/vos/evtree.c +++ b/src/vos/evtree.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2017-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -4088,3 +4089,12 @@ evt_feats_set(struct evt_root *root, struct umem_instance *umm, uint64_t feats) return rc; } +bool +evt_desc_is_valid(const struct evt_desc *evt, uint32_t dtx_lid) +{ + if (evt == NULL || evt->dc_magic != EVT_DESC_MAGIC) { + return false; + } + + return (evt->dc_dtx == dtx_lid); +} diff --git a/src/vos/ilog.c b/src/vos/ilog.c index 1d1d6508087..657394d1022 100644 --- a/src/vos/ilog.c +++ b/src/vos/ilog.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2019-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -17,8 +18,7 @@ #include "vos_layout.h" #include "vos_ts.h" #include "ilog.h" - -#define ILOG_TREE_ORDER 11 +#include "ilog_internal.h" enum { ILOG_ITER_NONE, @@ -27,27 +27,6 @@ enum { ILOG_ITER_FINI, }; -/** The ilog is split into two parts. If there is one entry, the ilog - * is embedded into the root df struct. If not, a b+tree is used. - * The tree is used more like a set where only the key is used. - */ - -struct ilog_tree { - umem_off_t it_root; - uint64_t it_embedded; -}; - -struct ilog_array { - /** Current length of array */ - uint32_t ia_len; - /** Allocated length of array */ - uint32_t ia_max_len; - /** Pad to 16 bytes */ - uint64_t ia_pad; - /** Entries in array */ - struct ilog_id ia_id[0]; -}; - struct ilog_array_cache { /** Pointer to entries */ struct ilog_id *ac_entries; @@ -57,15 +36,6 @@ struct ilog_array_cache { uint32_t ac_nr; }; -struct ilog_root { - union { - struct ilog_id lr_id; - struct ilog_tree lr_tree; - }; - uint32_t lr_ts_idx; - uint32_t lr_magic; -}; - struct ilog_context { /** Root pointer */ struct ilog_root *ic_root; @@ -193,14 +163,6 @@ ilog_init(void) return 0; } -/* 4 bit magic number + version */ -#define ILOG_MAGIC 0x00000006 -#define ILOG_MAGIC_BITS 4 -#define ILOG_MAGIC_MASK ((1 << ILOG_MAGIC_BITS) - 1) -#define ILOG_VERSION_INC (1 << ILOG_MAGIC_BITS) -#define ILOG_VERSION_MASK ~(ILOG_VERSION_INC - 1) -#define ILOG_MAGIC_VALID(magic) (((magic) & ILOG_MAGIC_MASK) == ILOG_MAGIC) - static inline uint32_t ilog_mag2ver(uint32_t magic) { if (!ILOG_MAGIC_VALID(magic)) @@ -278,13 +240,6 @@ ilog_tx_end(struct ilog_context *lctx, int rc) return umem_tx_end(lctx->ic_umm, rc); } -static inline bool -ilog_empty(struct ilog_root *root) -{ - return !root->lr_tree.it_embedded && - root->lr_tree.it_root == UMOFF_NULL; -} - static void ilog_addref(struct ilog_context *lctx) { @@ -1621,3 +1576,35 @@ ilog_version_get(daos_handle_t loh) return ilog_mag2ver(lctx->ic_root->lr_magic); } + +bool +ilog_is_valid(struct umem_instance *umm, umem_off_t rec, uint32_t dtx_lid, daos_epoch_t epoch) +{ + struct ilog_root *root = umem_off2ptr(umm, umem_off2offset(rec)); + struct ilog_array *array; + struct ilog_id *id; + + // !ILOG_ASSERT_VALID(ilog) + if (root == NULL || !ILOG_MAGIC_VALID(root->lr_magic)) { + return false; + } + + if (ilog_empty(root)) { + return false; + } + + if (root->lr_tree.it_embedded) { + id = &root->lr_id; + return (id->id_tx_id == dtx_lid && id->id_epoch == epoch); + } else { + array = umem_off2ptr(umm, root->lr_tree.it_root); + for (int i = 0; i < array->ia_len; ++i) { + id = &array->ia_id[i]; + if (id->id_tx_id == dtx_lid && id->id_epoch == epoch) { + return true; + } + } + + return false; + } +} diff --git a/src/vos/ilog.h b/src/vos/ilog.h index 0cc7ceb5c4f..467b6819813 100644 --- a/src/vos/ilog.h +++ b/src/vos/ilog.h @@ -1,5 +1,6 @@ /** * (C) Copyright 2019-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -319,4 +320,18 @@ ilog_is_punch(const struct ilog_entry *entry) entry->ie_id.id_update_minor_eph; } +/** Validate the provided ilog. + * + * Note: It is designed for catastrophic recovery. Not to perform at run-time. + * + * \param umm[in] unified memory class instance + * \param rec[in] offset of the ilog + * \param dtx_lid[in] expected local DTX id + * \param epoch[in] expected epoch + * + * \return true if ilog is valid. + **/ +bool +ilog_is_valid(struct umem_instance *umm, umem_off_t rec, uint32_t dtx_lid, daos_epoch_t epoch); + #endif /* __ILOG_H__ */ diff --git a/src/vos/ilog_internal.h b/src/vos/ilog_internal.h new file mode 100644 index 00000000000..e729f76ea4b --- /dev/null +++ b/src/vos/ilog_internal.h @@ -0,0 +1,61 @@ +/** + * (C) Copyright 2019-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +/** + * VOS Object/Key incarnation log + * vos/ilog_internal.h + * + * Author: Jeff Olivier + */ + +#ifndef __ILOG_INTERNAL_H__ +#define __ILOG_INTERNAL_H__ + +/* 4 bit magic number + version */ +#define ILOG_MAGIC 0x00000006 +#define ILOG_MAGIC_BITS 4 +#define ILOG_MAGIC_MASK ((1 << ILOG_MAGIC_BITS) - 1) +#define ILOG_VERSION_INC (1 << ILOG_MAGIC_BITS) +#define ILOG_VERSION_MASK ~(ILOG_VERSION_INC - 1) +#define ILOG_MAGIC_VALID(magic) (((magic)&ILOG_MAGIC_MASK) == ILOG_MAGIC) + +/** The ilog is split into two parts. If there is one entry, the ilog + * is embedded into the root df struct. If not, a b+tree is used. + * The tree is used more like a set where only the key is used. + */ + +struct ilog_tree { + umem_off_t it_root; + uint64_t it_embedded; +}; + +struct ilog_root { + union { + struct ilog_id lr_id; + struct ilog_tree lr_tree; + }; + uint32_t lr_ts_idx; + uint32_t lr_magic; +}; + +static inline bool +ilog_empty(struct ilog_root *root) +{ + return !root->lr_tree.it_embedded && root->lr_tree.it_root == UMOFF_NULL; +} + +struct ilog_array { + /** Current length of array */ + uint32_t ia_len; + /** Allocated length of array */ + uint32_t ia_max_len; + /** Pad to 16 bytes */ + uint64_t ia_pad; + /** Entries in array */ + struct ilog_id ia_id[0]; +}; + +#endif /* __ILOG_INTERNAL_H__ */ diff --git a/src/vos/tests/SConscript b/src/vos/tests/SConscript index 85ec6b80660..1931efa4b34 100644 --- a/src/vos/tests/SConscript +++ b/src/vos/tests/SConscript @@ -30,7 +30,7 @@ def scons(): vos_test_src = ['vos_tests.c', vts_objs, 'vts_pool.c', 'vts_container.c', 'vts_aggregate.c', 'vts_gc.c', 'vts_checksum.c', 'vts_ilog.c', 'vts_array.c', 'vts_pm.c', 'vts_ts.c', 'vts_mvcc.c', - 'vos_cmd.c', 'vts_wal.c'] + 'vos_cmd.c', 'vts_wal.c', 'vts_evtree.c', 'vts_tree.c'] vos_tests = tenv.d_program('vos_tests', vos_test_src, LIBS=libraries) tenv.AppendUnique(CPPPATH=[Dir('../../common/tests').srcnode()]) evt_ctl = tenv.d_program('evt_ctl', ['evt_ctl.c', utest_utils, cmd_parser], LIBS=libraries) diff --git a/src/vos/tests/vos_tests.c b/src/vos/tests/vos_tests.c index 17db53f28c8..41bc56e8f36 100644 --- a/src/vos/tests/vos_tests.c +++ b/src/vos/tests/vos_tests.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2016-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -89,6 +90,8 @@ run_all_tests(int keys) failed += run_ilog_tests(cfg_desc_io); failed += run_csum_extent_tests(cfg_desc_io); failed += run_wal_tests(cfg_desc_io); + failed += run_evtree_tests(cfg_desc_io); + failed += run_tree_tests(cfg_desc_io); failed += run_io_test(&type_list[0], ARRAY_SIZE(type_list), keys, cfg_desc_io); diff --git a/src/vos/tests/vts_common.h b/src/vos/tests/vts_common.h index 11529f040b7..a5b72a00d3a 100644 --- a/src/vos/tests/vts_common.h +++ b/src/vos/tests/vts_common.h @@ -1,5 +1,6 @@ /** * (C) Copyright 2016-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -141,6 +142,10 @@ int run_csum_extent_tests(const char *cfg); int run_mvcc_tests(const char *cfg); int run_wal_tests(const char *cfg); int +run_evtree_tests(const char *cfg); +int +run_tree_tests(const char *cfg); +int run_vos_command(const char *arg0, const char *cmd); void diff --git a/src/vos/tests/vts_evtree.c b/src/vos/tests/vts_evtree.c new file mode 100644 index 00000000000..e68b7e8ff1e --- /dev/null +++ b/src/vos/tests/vts_evtree.c @@ -0,0 +1,51 @@ +/** + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +/** + * This file is part of vos/tests/ + * + * vos/tests/vts_evtree.c + */ +#define D_LOGFAC DD_FAC(tests) + +#include + +#include +#include +#include +#include + +#include "evt_priv.h" + +/* values picked arbitrarily where invalid means not as expected by the caller */ +#define DTX_LID_VALID ((uint32_t)123) +#define DTX_LID_INVALID ((uint32_t)DTX_LID_VALID + 1) + +static const struct evt_desc invalid_magic = {.dc_magic = (EVT_DESC_MAGIC + 1)}; + +static const struct evt_desc invalid_dtx_lid = {.dc_magic = EVT_DESC_MAGIC, + .dc_dtx = DTX_LID_INVALID}; + +static const struct evt_desc valid = {.dc_magic = EVT_DESC_MAGIC, .dc_dtx = DTX_LID_VALID}; + +static void +evt_desc_is_valid_test(void **state) +{ + assert_false(evt_desc_is_valid(NULL, DTX_LID_VALID)); + assert_false(evt_desc_is_valid(&invalid_magic, DTX_LID_VALID)); + assert_false(evt_desc_is_valid(&invalid_dtx_lid, DTX_LID_VALID)); + assert_true(evt_desc_is_valid(&valid, DTX_LID_VALID)); +} + +static const struct CMUnitTest evtree_tests_all[] = { + {"VOS1000: evt_desc_is_valid", evt_desc_is_valid_test, NULL, NULL}, +}; + +int +run_evtree_tests(const char *cfg) +{ + char *test_name = "evtree"; + return cmocka_run_group_tests_name(test_name, evtree_tests_all, NULL, NULL); +} diff --git a/src/vos/tests/vts_ilog.c b/src/vos/tests/vts_ilog.c index c696ff0b487..0b9c07332c9 100644 --- a/src/vos/tests/vts_ilog.c +++ b/src/vos/tests/vts_ilog.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2019-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -15,6 +16,7 @@ #include #include "vts_io.h" #include +#include "ilog_internal.h" #define LOG_FAIL(rc, expected_value, format, ...) \ do { \ @@ -1055,17 +1057,139 @@ ilog_test_discard(void **state) ilog_fetch_finish(&ilents); } +/* values picked arbitrarily where invalid means not as expected by the caller */ +#define DTX_LID_VALID ((uint32_t)123) +#define DTX_LID_INVALID (DTX_LID_VALID + 1) +#define EPOCH_VALID ((daos_epoch_t)225) +#define EPOCH_INVALID (EPOCH_VALID + 1) + +static uint32_t dtx_lid_all[] = {DTX_LID_VALID, DTX_LID_INVALID}; +static uint32_t epoch_all[] = {EPOCH_VALID, EPOCH_INVALID}; + +#define BOOL2STR(x) ((x) ? "true" : "false") + +#define ILOG_ARRAY_MAX 3 +#define ILOG_ARRAY_SIZE (sizeof(struct ilog_id) * ILOG_ARRAY_MAX) + +/* all cases of 3-item arrays containing and not containing the valid epoch */ +static struct ilog_id no_valid_epoch1[] = { + {.id_epoch = EPOCH_VALID - 3}, {.id_epoch = EPOCH_VALID - 2}, {.id_epoch = EPOCH_VALID - 1}}; +static struct ilog_id valid_epoch1[] = { + {.id_epoch = EPOCH_VALID - 2}, {.id_epoch = EPOCH_VALID - 1}, {.id_epoch = EPOCH_VALID}}; +static struct ilog_id valid_epoch2[] = { + {.id_epoch = EPOCH_VALID - 1}, {.id_epoch = EPOCH_VALID}, {.id_epoch = EPOCH_VALID + 1}}; +static struct ilog_id valid_epoch3[] = { + {.id_epoch = EPOCH_VALID}, {.id_epoch = EPOCH_VALID + 1}, {.id_epoch = EPOCH_VALID + 2}}; +static struct ilog_id no_valid_epoch2[] = { + {.id_epoch = EPOCH_VALID + 1}, {.id_epoch = EPOCH_VALID + 2}, {.id_epoch = EPOCH_VALID + 3}}; + +static struct ilog_id *no_valid_epoch_all[] = {no_valid_epoch1, no_valid_epoch2}; +static struct ilog_id *valid_epoch_all[] = {valid_epoch1, valid_epoch2, valid_epoch3}; + +static void +ilog_is_valid_test(void **state) +{ + struct umem_instance umm; + umem_off_t rec; + struct ilog_root *root; + struct ilog_array *array; + + struct umem_attr uma = {.uma_id = UMEM_CLASS_VMEM, .uma_pool = NULL}; + + umem_class_init(&uma, &umm); + + /* 1. ILOG rec is a NULL pointer. */ + rec = UMOFF_NULL; + assert_false(ilog_is_valid(&umm, rec, DTX_LID_VALID, EPOCH_VALID)); + + /* 2. Invalid magic. */ + rec = umem_zalloc(&umm, sizeof(struct ilog_root)); + root = umem_off2ptr(&umm, rec); + root->lr_magic = ILOG_MAGIC + 1; + assert_false(ILOG_MAGIC_VALID(root->lr_magic)); + assert_false(ilog_is_valid(&umm, rec, DTX_LID_VALID, EPOCH_VALID)); + + /* Set valid magic for all cases down below. */ + root->lr_magic = ILOG_MAGIC; + assert_true(ILOG_MAGIC_VALID(root->lr_magic)); + + /* 3. Empty ILOG can't reference dtx_lid nor epoch. */ + root->lr_tree.it_embedded = 0; + root->lr_tree.it_root = UMOFF_NULL; + assert_true(ilog_empty(root)); + assert_false(ilog_is_valid(&umm, rec, DTX_LID_VALID, EPOCH_VALID)); + + /* 4. Embedded - all cases */ + root->lr_tree.it_embedded = 1; + for (int i = 0; i < ARRAY_SIZE(dtx_lid_all); ++i) { + root->lr_id.id_tx_id = dtx_lid_all[i]; + for (int j = 0; j < ARRAY_SIZE(epoch_all); ++j) { + root->lr_id.id_epoch = epoch_all[j]; + bool exp = (dtx_lid_all[i] == DTX_LID_VALID && epoch_all[j] == EPOCH_VALID); + bool result = ilog_is_valid(&umm, rec, DTX_LID_VALID, EPOCH_VALID); + if (result != exp) { + fail_msg("ilog_is_valid() result is not as expected %s != %s for " + "{dtx_lid=%u, epoch=%u}", + BOOL2STR(result), BOOL2STR(exp), dtx_lid_all[i], + epoch_all[j]); + } + } + } + + /* Prepare ILOG array for all cases below. */ + root->lr_tree.it_embedded = 0; + root->lr_tree.it_root = umem_zalloc(&umm, sizeof(struct ilog_array) + ILOG_ARRAY_SIZE); + array = umem_off2ptr(&umm, root->lr_tree.it_root); + array->ia_len = ILOG_ARRAY_MAX; + array->ia_max_len = ILOG_ARRAY_MAX; + + /* 5. Array - no valid epoch */ + for (int i = 0; i < ARRAY_SIZE(dtx_lid_all); ++i) { + uint32_t dtx_lid = dtx_lid_all[i]; + for (int j = 0; j < ARRAY_SIZE(no_valid_epoch_all); ++j) { + /* prepare an array of ILOG id's with epochs from the template */ + memcpy(array->ia_id, no_valid_epoch_all[j], ILOG_ARRAY_SIZE); + /* fill-in dtx_lid for all of the array's entries */ + for (int k = 0; k < ILOG_ARRAY_MAX; ++k) { + array->ia_id[k].id_tx_id = dtx_lid; + } + if (ilog_is_valid(&umm, rec, DTX_LID_VALID, EPOCH_VALID)) { + fail_msg("ilog_is_valid() result is not as expected true != false " + "using no_valid_epoch_all[%d] and dtx_lid=%u", + j, dtx_lid); + } + } + } + + /* 6. Array - with valid epoch */ + for (int i = 0; i < ARRAY_SIZE(dtx_lid_all); ++i) { + uint32_t dtx_lid = dtx_lid_all[i]; + for (int j = 0; j < ARRAY_SIZE(valid_epoch_all); ++j) { + /* prepare an array of ILOG id's with epochs from the template */ + memcpy(array->ia_id, valid_epoch_all[j], ILOG_ARRAY_SIZE); + /* fill-in dtx_lid for all of the array's entries */ + for (int k = 0; k < ILOG_ARRAY_MAX; ++k) { + array->ia_id[k].id_tx_id = dtx_lid; + } + /* the valid epoch is there so dtx_lid's validity is decisive */ + bool exp = (dtx_lid == DTX_LID_VALID); + bool result = ilog_is_valid(&umm, rec, DTX_LID_VALID, EPOCH_VALID); + if (exp != result) { + fail_msg("ilog_is_valid() result is not as expected %s != %s using " + "valid_epoch_all[%d] and dtx_lid=%u", + BOOL2STR(result), BOOL2STR(exp), j, dtx_lid); + } + } + } +} + static const struct CMUnitTest inc_tests[] = { - { "VOS500.1: VOS incarnation log UPDATE", ilog_test_update, NULL, - NULL}, - { "VOS500.2: VOS incarnation log ABORT test", ilog_test_abort, NULL, - NULL}, - { "VOS500.3: VOS incarnation log PERSIST test", ilog_test_persist, NULL, - NULL}, - { "VOS500.4: VOS incarnation log AGGREGATE test", ilog_test_aggregate, - NULL, NULL}, - { "VOS500.5: VOS incarnation log DISCARD test", ilog_test_discard, - NULL, NULL}, + {"VOS500.1: VOS incarnation log UPDATE", ilog_test_update, NULL, NULL}, + {"VOS500.2: VOS incarnation log ABORT test", ilog_test_abort, NULL, NULL}, + {"VOS500.3: VOS incarnation log PERSIST test", ilog_test_persist, NULL, NULL}, + {"VOS500.4: VOS incarnation log AGGREGATE test", ilog_test_aggregate, NULL, NULL}, + {"VOS500.5: VOS incarnation log DISCARD test", ilog_test_discard, NULL, NULL}, + {"VOS501: ilog_is_valid", ilog_is_valid_test, NULL, NULL}, }; int diff --git a/src/vos/tests/vts_tree.c b/src/vos/tests/vts_tree.c new file mode 100644 index 00000000000..52d48f739e2 --- /dev/null +++ b/src/vos/tests/vts_tree.c @@ -0,0 +1,45 @@ +/** + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +/** + * This file is part of vos/tests/ + * + * vos/tests/vts_tree.c + */ +#define D_LOGFAC DD_FAC(tests) + +#include +#include +#include +#include + +#include "vos_internal.h" + +/* values picked arbitrarily where invalid means not as expected by the caller */ +#define DTX_LID_VALID ((uint32_t)123) +#define DTX_LID_INVALID ((uint32_t)DTX_LID_VALID + 1) + +static const struct vos_irec_df invalid_dtx_lid = {.ir_dtx = DTX_LID_INVALID}; + +static const struct vos_irec_df valid = {.ir_dtx = DTX_LID_VALID}; + +static void +vos_irec_is_valid_test(void **state) +{ + assert_false(vos_irec_is_valid(NULL, DTX_LID_VALID)); + assert_false(vos_irec_is_valid(&invalid_dtx_lid, DTX_LID_VALID)); + assert_true(vos_irec_is_valid(&valid, DTX_LID_VALID)); +} + +static const struct CMUnitTest tree_tests_all[] = { + {"VOS1100: vos_irec_is_valid", vos_irec_is_valid_test, NULL, NULL}, +}; + +int +run_tree_tests(const char *cfg) +{ + char *test_name = "tree"; + return cmocka_run_group_tests_name(test_name, tree_tests_all, NULL, NULL); +} diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index 5ed24e20909..de659e963e6 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -1,6 +1,6 @@ /** * (C) Copyright 2019-2024 Intel Corporation. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -24,12 +24,6 @@ #define DTX_ACT_BLOB_MAGIC 0x14130a2b #define DTX_CMT_BLOB_MAGIC 0x2502191c -enum { - DTX_UMOFF_ILOG = (1 << 0), - DTX_UMOFF_SVT = (1 << 1), - DTX_UMOFF_EVT = (1 << 2), -}; - #define DTX_UMOFF_TYPES (DTX_UMOFF_ILOG | DTX_UMOFF_SVT | DTX_UMOFF_EVT) #define DTX_INDEX_INVAL (int32_t)(-1) @@ -47,28 +41,6 @@ enum { DAE_EPOCH(dae)); \ } while (0) -static inline void -dtx_type2umoff_flag(umem_off_t *rec, uint32_t type) -{ - uint8_t flag = 0; - - switch (type) { - case DTX_RT_ILOG: - flag = DTX_UMOFF_ILOG; - break; - case DTX_RT_SVT: - flag = DTX_UMOFF_SVT; - break; - case DTX_RT_EVT: - flag = DTX_UMOFF_EVT; - break; - default: - D_ASSERT(0); - } - - umem_off_set_flags(rec, flag); -} - static inline uint32_t dtx_umoff_flag2type(umem_off_t umoff) { @@ -2706,6 +2678,144 @@ vos_dtx_abort(daos_handle_t coh, struct dtx_id *dti, daos_epoch_t epoch) return rc; } +static void +do_dtx_rec_discard_invalid(struct umem_instance *umm, struct vos_dtx_act_ent *dae, umem_off_t *rec, + int *discarded) +{ + bool valid; + + if (UMOFF_IS_NULL(*rec)) + return; + + switch (dtx_umoff_flag2type(*rec)) { + case DTX_RT_ILOG: { + valid = ilog_is_valid(umm, *rec, DAE_LID(dae), DAE_EPOCH(dae)); + break; + } + case DTX_RT_SVT: { + struct vos_irec_df *svt = umem_off2ptr(umm, *rec); + valid = vos_irec_is_valid(svt, DAE_LID(dae)); + break; + } + case DTX_RT_EVT: { + struct evt_desc *evt = umem_off2ptr(umm, *rec); + valid = evt_desc_is_valid(evt, DAE_LID(dae)); + break; + } + default: + /* On-disk data corruption case. */ + valid = false; + break; + } + + if (!valid) { + *rec = UMOFF_NULL; + *discarded += 1; + } +} + +static int +vos_dtx_discard_invalid_internal(struct vos_container *cont, struct vos_dtx_act_ent *dae, + int *discarded) +{ + struct umem_instance *umm = vos_cont2umm(cont); + int discarded_noninline = 0; + int discarded_inline = 0; + int count; + int i; + + /* go through the non-inlined records first if present */ + if (dae->dae_records != NULL) { + D_ASSERT(DAE_REC_CNT(dae) > DTX_INLINE_REC_CNT); + + count = DAE_REC_CNT(dae) - DTX_INLINE_REC_CNT; + for (i = 0; i < count; i++) { + do_dtx_rec_discard_invalid(umm, dae, &dae->dae_records[i], + &discarded_noninline); + } + + if (discarded_noninline > 0) { + /* copy the whole array to the durable format */ + size_t size = sizeof(umem_off_t) * count; + void *rec_df = umem_off2ptr(umm, DAE_REC_OFF(dae)); + int rc = umem_tx_add_ptr(umm, rec_df, size); + if (rc != 0) { + return rc; + } + memcpy(rec_df, dae->dae_records, size); + } + + count = DTX_INLINE_REC_CNT; + } else { + count = DAE_REC_CNT(dae); + } + + /* go through the inlined records */ + for (i = 0; i < count; i++) { + do_dtx_rec_discard_invalid(umm, dae, &DAE_REC_INLINE(dae)[i], &discarded_inline); + } + + if (discarded_inline > 0) { + /* copy the whole array to durable format */ + struct vos_dtx_act_ent_df *dae_df = umem_off2ptr(umm, dae->dae_df_off); + size_t size = sizeof(umem_off_t) * count; + int rc = umem_tx_add_ptr(umm, &dae_df->dae_rec_inline, size); + if (rc != 0) { + return rc; + } + memcpy(&dae_df->dae_rec_inline, &DAE_REC_INLINE(dae), size); + } + + *discarded = discarded_inline + discarded_noninline; + + return 0; +} + +int +vos_dtx_discard_invalid(daos_handle_t coh, struct dtx_id *dti, int *discarded) +{ + struct vos_container *cont; + struct vos_dtx_act_ent *dae = NULL; + d_iov_t riov; + d_iov_t kiov; + int rc; + + cont = vos_hdl2cont(coh); + D_ASSERT(cont != NULL); + + D_ASSERT(dti != NULL); + D_ASSERT(discarded != NULL); + + /* lookup the DTX entry */ + d_iov_set(&kiov, dti, sizeof(*dti)); + d_iov_set(&riov, NULL, 0); + rc = dbtree_lookup(cont->vc_dtx_active_hdl, &kiov, &riov); + if (rc != 0) { + return rc; + } + dae = riov.iov_buf; + + if (vos_dae_is_abort(dae)) { + /* Only not aborted transactions are considered. */ + return 0; + } + + rc = umem_tx_begin(vos_cont2umm(cont), NULL); + if (rc == 0) { + rc = vos_dtx_discard_invalid_internal(cont, dae, discarded); + if (rc == 0 && *discarded > 0) { + rc = umem_tx_commit(vos_cont2umm(cont)); + } else { + rc = umem_tx_abort(vos_cont2umm(cont), rc); + if (rc == -DER_CANCELED) { + rc = 0; + } + } + } + + return rc; +} + static int vos_dtx_set_flags_one(struct vos_container *cont, struct dtx_id *dti, uint32_t flags) { diff --git a/src/vos/vos_internal.h b/src/vos/vos_internal.h index acd9e685dd7..a64e4f19dd1 100644 --- a/src/vos/vos_internal.h +++ b/src/vos/vos_internal.h @@ -1,6 +1,6 @@ /** * (C) Copyright 2016-2024 Intel Corporation. - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -2067,4 +2067,44 @@ int vos_bkt_array_add(struct vos_bkt_array *bkts, uint32_t bkt_id); int vos_bkt_array_pin(struct vos_pool *pool, struct vos_bkt_array *bkts, struct umem_pin_handle **pin_hdl); +/** Validate the provided svt. + * + * Note: It is designed for catastrophic recovery. Not to perform at run-time. + * + * \param svt[in] + * \param dtx_lid[in] local id of the DTX entry the evt is supposed to belong to + * + * \return true if svt is valid. + **/ +bool +vos_irec_is_valid(const struct vos_irec_df *svt, uint32_t dtx_lid); + +enum { + DTX_UMOFF_ILOG = (1 << 0), + DTX_UMOFF_SVT = (1 << 1), + DTX_UMOFF_EVT = (1 << 2), +}; + +static inline void +dtx_type2umoff_flag(umem_off_t *rec, uint32_t type) +{ + uint8_t flag = 0; + + switch (type) { + case DTX_RT_ILOG: + flag = DTX_UMOFF_ILOG; + break; + case DTX_RT_SVT: + flag = DTX_UMOFF_SVT; + break; + case DTX_RT_EVT: + flag = DTX_UMOFF_EVT; + break; + default: + D_ASSERT(0); + } + + umem_off_set_flags(rec, flag); +} + #endif /* __VOS_INTERNAL_H__ */ diff --git a/src/vos/vos_tree.c b/src/vos/vos_tree.c index c7aa8b57f5e..7133557c33c 100644 --- a/src/vos/vos_tree.c +++ b/src/vos/vos_tree.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2016-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -1354,3 +1355,13 @@ obj_tree_find_attr(unsigned tree_class, int flags) return NULL; } } + +bool +vos_irec_is_valid(const struct vos_irec_df *svt, uint32_t dtx_lid) +{ + if (svt == NULL) { + return false; + } + + return svt->ir_dtx == dtx_lid; +} diff --git a/utils/rpms/daos.rpmlintrc b/utils/rpms/daos.rpmlintrc index 67285ef5b50..004c24d1266 100644 --- a/utils/rpms/daos.rpmlintrc +++ b/utils/rpms/daos.rpmlintrc @@ -20,7 +20,7 @@ addFilter("daos-client\.x86_64: E: post(i|u)n-without-ldconfig \/usr\/lib64\/lib addFilter("daos-(client|server)\.x86_64: W: dangerous-command-in-%post(un)? rm") # lots of missing manpages -addFilter("W: no-manual-page-for-binary (cart_ctl|daos_agent|dfuse|self_test|acl_dump_test|agent_tests|crt_launch|daos_debug_set_params|daos_gen_io_conf|daos_perf|daos_racer|daos_run_io_conf|daos_test|dfs_test|dfuse_test|drpc_engine_test|drpc_test|eq_tests|fault_status|hello_drpc|job_tests|jobtest|security_test|daos_firmware|daos_admin|daos_engine|daos_metrics|daos_server|daos_storage_estimator.py|evt_ctl|jump_pl_map|obj_ctl|pl_bench|rdbt|ring_pl_map|smd_ut|bio_ut|vea_stress|vea_ut|vos_perf|vos_tests|dtx_tests|ddb|ddb_tests)") +addFilter("W: no-manual-page-for-binary (cart_ctl|daos_agent|dfuse|self_test|acl_dump_test|agent_tests|crt_launch|daos_debug_set_params|daos_gen_io_conf|daos_perf|daos_racer|daos_run_io_conf|daos_test|dfs_test|dfuse_test|drpc_engine_test|drpc_test|eq_tests|fault_status|hello_drpc|job_tests|jobtest|security_test|daos_firmware|daos_admin|daos_engine|daos_metrics|daos_server|daos_storage_estimator.py|evt_ctl|jump_pl_map|obj_ctl|pl_bench|rdbt|ring_pl_map|smd_ut|bio_ut|vea_stress|vea_ut|vos_perf|vos_tests|dtx_tests|dtx_ut|ddb|ddb_tests|ddb_ut)") addFilter("daos-(server|firmware)\.x86_64: W: non-standard-(u|g)id \/.+ daos_server") diff --git a/utils/rpms/daos.spec b/utils/rpms/daos.spec index ec793fc5dcc..b22678e9ece 100644 --- a/utils/rpms/daos.spec +++ b/utils/rpms/daos.spec @@ -16,7 +16,7 @@ Name: daos Version: 2.7.101 -Release: 4%{?relval}%{?dist} +Release: 5%{?relval}%{?dist} Summary: DAOS Storage Engine License: BSD-2-Clause-Patent @@ -547,6 +547,7 @@ getent passwd daos_agent >/dev/null || useradd -s /sbin/nologin -r -g daos_agent %files server-tests %doc README.md %{_bindir}/dtx_tests +%{_bindir}/dtx_ut %{_bindir}/evt_ctl %{_bindir}/jump_pl_map %{_bindir}/pl_bench @@ -558,6 +559,7 @@ getent passwd daos_agent >/dev/null || useradd -s /sbin/nologin -r -g daos_agent %{_bindir}/vos_tests %{_bindir}/vea_stress %{_bindir}/ddb_tests +%{_bindir}/ddb_ut %{_bindir}/obj_ctl %{_bindir}/vos_perf @@ -592,6 +594,9 @@ getent passwd daos_agent >/dev/null || useradd -s /sbin/nologin -r -g daos_agent # No files in a shim package %changelog +* Wed Jan 22 2025 Jan Michalski 2.7.101-5 +- Add ddb_ut and dtx_ut to the server-tests package + * Fri Dec 20 2024 Jeff Olivier 2.7.101-4 - Switch libfuse3 to libfused diff --git a/utils/utest.yaml b/utils/utest.yaml index df6ae51cf7a..afc8bb9b920 100644 --- a/utils/utest.yaml +++ b/utils/utest.yaml @@ -1,4 +1,5 @@ # (C) Copyright 2023-2024 Intel Corporation. +# (C) Copyright 2025 Hewlett Packard Enterprise Development LP. # # SPDX-License-Identifier: BSD-2-Clause-Patent - name: common @@ -56,6 +57,7 @@ base: "PREFIX" tests: - cmd: ["bin/dtx_tests"] + - cmd: ["bin/dtx_ut"] - name: placement base: "PREFIX" tests: @@ -184,6 +186,7 @@ base: "PREFIX" tests: - cmd: ["bin/ddb_tests"] + - cmd: ["bin/ddb_ut"] - name: Source metadata testing gha: True memcheck: False