diff --git a/src/control/cmd/ddb/commands_wrapper.go b/src/control/cmd/ddb/commands_wrapper.go index 95aa9aa3847..95d7b8aff86 100644 --- a/src/control/cmd/ddb/commands_wrapper.go +++ b/src/control/cmd/ddb/commands_wrapper.go @@ -290,3 +290,12 @@ func ddbDevReplace(ctx *DdbContext, db_path string, old_devid string, new_devid /* Run the c code command */ return daosError(C.ddb_run_dev_replace(&ctx.ctx, &options)) } + +func ddbDtxStat(ctx *DdbContext, path string) error { + /* Set up the options */ + options := C.struct_dtx_stat_options{} + options.path = C.CString(path) + defer freeString(options.path) + /* Run the c code command */ + return daosError(C.ddb_run_dtx_stat(&ctx.ctx, &options)) +} diff --git a/src/control/cmd/ddb/ddb_commands.go b/src/control/cmd/ddb/ddb_commands.go index dfbc0e20ace..27968cf5fec 100644 --- a/src/control/cmd/ddb/ddb_commands.go +++ b/src/control/cmd/ddb/ddb_commands.go @@ -381,4 +381,19 @@ the path must include the extent, otherwise, it must not.`, }, Completer: nil, }) + // Command dtx_stat + app.AddCommand(&grumble.Command{ + Name: "dtx_stat", + Aliases: nil, + Help: "Stat on DTX entries", + LongHelp: "Print statistic on the DTX entries", + HelpGroup: "vos", + Args: func(a *grumble.Args) { + a.String("path", "Optional, VOS tree path to query.", grumble.Default("")) + }, + Run: func(c *grumble.Context) error { + return ddbDtxStat(ctx, c.Args.String("path")) + }, + Completer: nil, + }) } diff --git a/src/dtx/tests/SConscript b/src/dtx/tests/SConscript index f949838ee42..1a97b9bdff0 100644 --- a/src/dtx/tests/SConscript +++ b/src/dtx/tests/SConscript @@ -61,6 +61,7 @@ def scons(): 'sched_mock.c', 'srv_mock.c', 'srv_module_mock.c', + 'dts_count.c' ] dtx_ut = tenv.d_program('dtx_ut', test_src + vos_src, LIBS=libraries) diff --git a/src/dtx/tests/dts_count.c b/src/dtx/tests/dts_count.c new file mode 100644 index 00000000000..3e6b823caf2 --- /dev/null +++ b/src/dtx/tests/dts_count.c @@ -0,0 +1,140 @@ +/** + * (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 +#include +#include +#include +#include + +#include "vos_internal.h" + +/* mocks */ + +#define DTX_CMT_BLOB_MAGIC 0x2502191c +#define DBD_COUNT 0x3 +#define DBD_BLOB_DF_CAP 0x8 +#define CELL_SIZE \ + sizeof(struct vos_dtx_blob_df) + DBD_BLOB_DF_CAP * sizeof(struct vos_dtx_cmt_ent_df) + +static struct vos_pool mock_pool; +static struct vos_container mock_cont; +static struct vos_cont_df mock_cont_df; +static struct vos_dtx_blob_df *mock_dbds[DBD_COUNT]; +static umem_off_t mock_dbds_off[DBD_COUNT]; +static daos_handle_t mock_coh; + +/* setup & teardown */ + +static int +test_setup(void **unused) +{ + int i; + + memset(&mock_pool, 0, sizeof(mock_pool)); + memset(&mock_cont, 0, sizeof(mock_cont)); + memset(&mock_cont_df, 0, sizeof(mock_cont_df)); + + for (i = 0; i < DBD_COUNT; i++) { + D_ALLOC(mock_dbds[i], CELL_SIZE); + assert_non_null(mock_dbds[i]); + } + + for (i = 0; i < DBD_COUNT; i++) { + mock_dbds[i]->dbd_magic = DTX_CMT_BLOB_MAGIC; + mock_dbds[i]->dbd_cap = DBD_BLOB_DF_CAP; + if (i == DBD_COUNT - 1) + mock_dbds[i]->dbd_next = UMOFF_NULL; + else + mock_dbds[i]->dbd_next = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i + 1]); + if (i == 0) + mock_dbds[i]->dbd_prev = UMOFF_NULL; + else + mock_dbds[i]->dbd_prev = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i - 1]); + mock_dbds[i]->dbd_count = (i + 1) * 2; + assert_true(mock_dbds[i]->dbd_count <= DBD_BLOB_DF_CAP); + + mock_dbds_off[i] = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i]); + } + + mock_cont.vc_pool = &mock_pool; + mock_cont.vc_cont_df = &mock_cont_df; + mock_cont_df.cd_dtx_committed_head = mock_dbds_off[0]; + mock_cont_df.cd_dtx_committed_tail = mock_dbds_off[DBD_COUNT - 1]; + mock_coh.cookie = (uint64_t)&mock_cont; + + return 0; +} + +static int +test_teardown(void **unused) +{ + int i; + + for (i = 0; i < DBD_COUNT; i++) + D_FREE(mock_dbds[i]); + + return 0; +} + +/* tests */ + +static void +test_asserts(void **unused) +{ + daos_handle_t hdl_null = {0}; + uint32_t cnt; + int rc; + + /* Invalid arguments. */ + rc = vos_dtx_get_cmt_cnt(hdl_null, &cnt); + assert_rc_equal(rc, -DER_INVAL); + rc = vos_dtx_get_cmt_cnt(mock_coh, NULL); + assert_rc_equal(rc, -DER_INVAL); + + /* Corrupted dbd */ + mock_dbds[1]->dbd_magic = 42; + rc = vos_dtx_get_cmt_cnt(mock_coh, &cnt); + assert_rc_equal(rc, -DER_INVAL); +} + +static void +test_count(void **unused) +{ + uint32_t cnt; + int rc; + + /* Container with several DTX entries table */ + rc = vos_dtx_get_cmt_cnt(mock_coh, &cnt); + assert_rc_equal(rc, 0); + assert_int_equal(cnt, DBD_COUNT * (DBD_COUNT + 1)); +} + +/* compilation unit's entry point */ +#define TEST(name, func) \ + { \ + name ": vos_dtx_get_cmt_cnt - " #func, func, test_setup, test_teardown \ + } + +static const struct CMUnitTest vos_dtx_count_tests_all[] = { + TEST("DTX600", test_asserts), + TEST("DTX601", test_count), +}; + +int +run_dtx_count_tests(void) +{ + const char *test_name = "vos_dtx_get_cmt_cnt"; + + return cmocka_run_group_tests_name(test_name, vos_dtx_count_tests_all, NULL, NULL); +} diff --git a/src/dtx/tests/dtx_ut.c b/src/dtx/tests/dtx_ut.c index e608c6c6964..dcccb713161 100644 --- a/src/dtx/tests/dtx_ut.c +++ b/src/dtx/tests/dtx_ut.c @@ -21,6 +21,8 @@ int run_structs_tests(void); int run_discard_invalid_tests(void); +int +run_dtx_count_tests(void); static void print_usage() @@ -40,6 +42,7 @@ run_all_tests(int keys) failed += run_structs_tests(); failed += run_discard_invalid_tests(); + failed += run_dtx_count_tests(); return failed; } @@ -77,7 +80,7 @@ main(int argc, char **argv) #if CMOCKA_FILTER_SUPPORTED == 1 /** requires cmocka 1.1.5 */ cmocka_set_skip_filter(optarg); #else - D_PRINT("filter not enabled"); + D_PRINT("filter not enabled\n"); #endif break; @@ -92,7 +95,7 @@ main(int argc, char **argv) printf("Test filter: %s\n", filter); } #else - D_PRINT("filter not enabled"); + D_PRINT("filter not enabled\n"); #endif break; default: diff --git a/src/include/daos_srv/vos.h b/src/include/daos_srv/vos.h index c4238bbc933..1b798b5c4c9 100644 --- a/src/include/daos_srv/vos.h +++ b/src/include/daos_srv/vos.h @@ -1777,4 +1777,15 @@ vos_pin_objects(daos_handle_t coh, daos_unit_oid_t oids[], int count, struct vos bool vos_oi_exist(daos_handle_t coh, daos_unit_oid_t oid); +/** + * Return the number of DTX committed entries of a container. + * + * \param[in] coh container open handle. + * \param[out] cnt number of DTX committed entries. + * + * \return 0 on success, error otherwise. + */ +int +vos_dtx_get_cmt_cnt(daos_handle_t coh, uint32_t *cnt); + #endif /* __VOS_API_H */ diff --git a/src/utils/ddb/ddb.c b/src/utils/ddb/ddb.c index 1135b500904..fb5f5021004 100644 --- a/src/utils/ddb/ddb.c +++ b/src/utils/ddb/ddb.c @@ -8,39 +8,44 @@ #include #include #include +#include +#include #include "ddb.h" +#include "daos/common.h" +#include "daos_errno.h" #include "ddb_common.h" #include "ddb_parse.h" -#define MAX_COMMAND_LEN 1024 - -#define same(a, b) (strcmp((a), (b)) == 0) -#define COMMAND_NAME_HELP "help" -#define COMMAND_NAME_QUIT "quit" -#define COMMAND_NAME_LS "ls" -#define COMMAND_NAME_OPEN "open" -#define COMMAND_NAME_VERSION "version" -#define COMMAND_NAME_CLOSE "close" -#define COMMAND_NAME_SUPERBLOCK_DUMP "superblock_dump" -#define COMMAND_NAME_VALUE_DUMP "value_dump" -#define COMMAND_NAME_RM "rm" -#define COMMAND_NAME_VALUE_LOAD "value_load" -#define COMMAND_NAME_ILOG_DUMP "ilog_dump" -#define COMMAND_NAME_ILOG_COMMIT "ilog_commit" -#define COMMAND_NAME_ILOG_CLEAR "ilog_clear" -#define COMMAND_NAME_DTX_DUMP "dtx_dump" -#define COMMAND_NAME_DTX_CMT_CLEAR "dtx_cmt_clear" -#define COMMAND_NAME_SMD_SYNC "smd_sync" -#define COMMAND_NAME_VEA_DUMP "vea_dump" -#define COMMAND_NAME_VEA_UPDATE "vea_update" -#define COMMAND_NAME_DTX_ACT_COMMIT "dtx_act_commit" -#define COMMAND_NAME_DTX_ACT_ABORT "dtx_act_abort" -#define COMMAND_NAME_FEATURE "feature" -#define COMMAND_NAME_RM_POOL "rm_pool" +#define MAX_COMMAND_LEN 1024 + +#define same(a, b) (strcmp((a), (b)) == 0) +#define COMMAND_NAME_HELP "help" +#define COMMAND_NAME_QUIT "quit" +#define COMMAND_NAME_LS "ls" +#define COMMAND_NAME_OPEN "open" +#define COMMAND_NAME_VERSION "version" +#define COMMAND_NAME_CLOSE "close" +#define COMMAND_NAME_SUPERBLOCK_DUMP "superblock_dump" +#define COMMAND_NAME_VALUE_DUMP "value_dump" +#define COMMAND_NAME_RM "rm" +#define COMMAND_NAME_VALUE_LOAD "value_load" +#define COMMAND_NAME_ILOG_DUMP "ilog_dump" +#define COMMAND_NAME_ILOG_COMMIT "ilog_commit" +#define COMMAND_NAME_ILOG_CLEAR "ilog_clear" +#define COMMAND_NAME_DTX_DUMP "dtx_dump" +#define COMMAND_NAME_DTX_CMT_CLEAR "dtx_cmt_clear" +#define COMMAND_NAME_SMD_SYNC "smd_sync" +#define COMMAND_NAME_VEA_DUMP "vea_dump" +#define COMMAND_NAME_VEA_UPDATE "vea_update" +#define COMMAND_NAME_DTX_ACT_COMMIT "dtx_act_commit" +#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" #define COMMAND_NAME_DEV_LIST "dev_list" #define COMMAND_NAME_DEV_REPLACE "dev_replace" +#define COMMAND_NAME_DTX_STAT "dtx_stat" /* Parse command line options for the 'ls' command */ static int @@ -814,6 +819,40 @@ dev_replace_option_parse(struct ddb_ctx *ctx, struct dev_replace_options *cmd_ar return 0; } +/* Parse command line options for the 'dtx_stat' command */ +static int +dtx_stat_option_parse(struct ddb_ctx *ctx, struct dtx_stat_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; + cmd_args->path = NULL; + if (argc - index > 0 && *argv[index] != '\0') { + cmd_args->path = argv[index]; + index++; + } + + if (argc - index > 0) { + ddb_printf(ctx, "Unexpected argument: %s\n", argv[index]); + return -DER_INVAL; + } + + return 0; +} + int ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_cmd_info *info) { @@ -935,6 +974,11 @@ ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_c argv); } + if (same(cmd, COMMAND_NAME_DTX_STAT)) { + info->dci_cmd = DDB_CMD_DTX_STAT; + return dtx_stat_option_parse(ctx, &info->dci_cmd_option.dci_dtx_stat, argc, argv); + } + ddb_errorf(ctx, "'%s' is not a valid command. Available commands are:" "'help', " @@ -960,7 +1004,8 @@ ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_c "'feature', " "'rm_pool', " "'dev_list', " - "'dev_replace'\n", + "'dev_replace', " + "'dtx_stat'\n", cmd); return -DER_INVAL; @@ -1136,6 +1181,10 @@ ddb_run_cmd(struct ddb_ctx *ctx, const char *cmd_str) rc = ddb_run_dev_replace(ctx, &info.dci_cmd_option.dci_dev_replace); break; + case DDB_CMD_DTX_STAT: + rc = ddb_run_dtx_stat(ctx, &info.dci_cmd_option.dci_dtx_stat); + break; + case DDB_CMD_UNKNOWN: ddb_error(ctx, "Unknown command\n"); rc = -DER_INVAL; @@ -1341,6 +1390,13 @@ ddb_commands_help(struct ddb_ctx *ctx) ddb_print(ctx, " -n, --new_dev\n"); ddb_print(ctx, "\tSpecify the new device UUID\n"); ddb_print(ctx, "\n"); + + /* Command: dtx_stat */ + ddb_print(ctx, "dtx_stat [path]\n"); + ddb_print(ctx, "\tPrint statistic on the DTX entries.\n"); + ddb_print(ctx, " [path]\n"); + ddb_print(ctx, "\tOptional, VOS tree path to query.\n"); + ddb_print(ctx, "\n"); } void @@ -1408,4 +1464,5 @@ ddb_program_help(struct ddb_ctx *ctx) ddb_print(ctx, " rm_pool Remove pool shard\n"); ddb_print(ctx, " dev_list List all devices\n"); ddb_print(ctx, " dev_replace Replace an old device with a new unused device\n"); + ddb_print(ctx, " dtx_stat Stat on DTX entries\n"); } diff --git a/src/utils/ddb/ddb.h b/src/utils/ddb/ddb.h index 86c00825efe..8cbf80bd385 100644 --- a/src/utils/ddb/ddb.h +++ b/src/utils/ddb/ddb.h @@ -7,8 +7,8 @@ #ifndef __DDB_RUN_CMDS_H #define __DDB_RUN_CMDS_H - #include +#include typedef int (*ddb_io_line_cb)(void *cb_args, char *line, uint32_t str_len); @@ -121,6 +121,7 @@ enum ddb_cmd { DDB_CMD_DTX_ACT_DISCARD_INVALID = 23, DDB_CMD_DEV_LIST = 24, DDB_CMD_DEV_REPLACE = 25, + DDB_CMD_DTX_STAT = 26, }; /* option and argument structures for commands that need them */ @@ -209,54 +210,79 @@ struct dev_replace_options { char *new_devid; }; +struct dtx_stat_options { + char *path; +}; + struct ddb_cmd_info { enum ddb_cmd dci_cmd; union { - struct ls_options dci_ls; - struct open_options dci_open; - struct value_dump_options dci_value_dump; - struct rm_options dci_rm; - struct value_load_options dci_value_load; - struct ilog_dump_options dci_ilog_dump; - struct ilog_commit_options dci_ilog_commit; - struct ilog_clear_options dci_ilog_clear; - struct dtx_dump_options dci_dtx_dump; + struct ls_options dci_ls; + struct open_options dci_open; + struct value_dump_options dci_value_dump; + struct rm_options dci_rm; + struct value_load_options dci_value_load; + struct ilog_dump_options dci_ilog_dump; + struct ilog_commit_options dci_ilog_commit; + struct ilog_clear_options dci_ilog_clear; + 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 feature_options dci_feature; - struct rm_pool_options dci_rm_pool; - struct dtx_act_options dci_dtx_act; - struct dev_list_options dci_dev_list; - struct dev_replace_options dci_dev_replace; + struct smd_sync_options dci_smd_sync; + 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; + struct dev_list_options dci_dev_list; + struct dev_replace_options dci_dev_replace; + struct dtx_stat_options dci_dtx_stat; } dci_cmd_option; }; -int ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_cmd_info *info); +int +ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_cmd_info *info); int ddb_parse_cmd_str(struct ddb_ctx *ctx, const char *cmd_str, bool *open); int - ddb_run_cmd(struct ddb_ctx *ctx, const char *cmd_str); +ddb_run_cmd(struct ddb_ctx *ctx, const char *cmd_str); /* Run commands ... */ -int ddb_run_help(struct ddb_ctx *ctx); -int ddb_run_quit(struct ddb_ctx *ctx); -int ddb_run_ls(struct ddb_ctx *ctx, struct ls_options *opt); -bool ddb_pool_is_open(struct ddb_ctx *ctx); -int ddb_run_open(struct ddb_ctx *ctx, struct open_options *opt); -int ddb_run_version(struct ddb_ctx *ctx); -int ddb_run_close(struct ddb_ctx *ctx); -int ddb_run_superblock_dump(struct ddb_ctx *ctx); -int ddb_run_value_dump(struct ddb_ctx *ctx, struct value_dump_options *opt); -int ddb_run_rm(struct ddb_ctx *ctx, struct rm_options *opt); -int ddb_run_value_load(struct ddb_ctx *ctx, struct value_load_options *opt); -int ddb_run_ilog_dump(struct ddb_ctx *ctx, struct ilog_dump_options *opt); -int ddb_run_ilog_commit(struct ddb_ctx *ctx, struct ilog_commit_options *opt); -int ddb_run_ilog_clear(struct ddb_ctx *ctx, struct ilog_clear_options *opt); -int ddb_run_dtx_dump(struct ddb_ctx *ctx, struct dtx_dump_options *opt); -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_help(struct ddb_ctx *ctx); +int +ddb_run_quit(struct ddb_ctx *ctx); +int +ddb_run_ls(struct ddb_ctx *ctx, struct ls_options *opt); +bool +ddb_pool_is_open(struct ddb_ctx *ctx); +int +ddb_run_open(struct ddb_ctx *ctx, struct open_options *opt); +int +ddb_run_version(struct ddb_ctx *ctx); +int +ddb_run_close(struct ddb_ctx *ctx); +int +ddb_run_superblock_dump(struct ddb_ctx *ctx); +int +ddb_run_value_dump(struct ddb_ctx *ctx, struct value_dump_options *opt); +int +ddb_run_rm(struct ddb_ctx *ctx, struct rm_options *opt); +int +ddb_run_value_load(struct ddb_ctx *ctx, struct value_load_options *opt); +int +ddb_run_ilog_dump(struct ddb_ctx *ctx, struct ilog_dump_options *opt); +int +ddb_run_ilog_commit(struct ddb_ctx *ctx, struct ilog_commit_options *opt); +int +ddb_run_ilog_clear(struct ddb_ctx *ctx, struct ilog_clear_options *opt); +int +ddb_run_dtx_dump(struct ddb_ctx *ctx, struct dtx_dump_options *opt); +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_options *opt); int @@ -269,13 +295,17 @@ ddb_feature_string2flags(struct ddb_ctx *ctx, const char *string, uint64_t *comp int 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); +ddb_run_dtx_act_discard_invalid(struct ddb_ctx *ctx, struct dtx_act_options *opt); int ddb_run_dev_list(struct ddb_ctx *ctx, struct dev_list_options *opt); int - ddb_run_dev_replace(struct ddb_ctx *ctx, struct dev_replace_options *opt); +ddb_run_dev_replace(struct ddb_ctx *ctx, struct dev_replace_options *opt); +int +ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt); -void ddb_program_help(struct ddb_ctx *ctx); -void ddb_commands_help(struct ddb_ctx *ctx); +void +ddb_program_help(struct ddb_ctx *ctx); +void +ddb_commands_help(struct ddb_ctx *ctx); #endif /* __DDB_RUN_CMDS_H */ diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 860ad436653..1ff733b3162 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -7,6 +7,12 @@ #include #include +#include +#include + +#include "daos_errno.h" +#include "daos_srv/vos_types.h" +#include "daos_types.h" #include "ddb_common.h" #include "ddb_parse.h" #include "ddb.h" @@ -14,6 +20,8 @@ #include "ddb_printer.h" #include "daos.h" #include "ddb_tree_path.h" +#include "gurt/common.h" +#include "gurt/debug.h" #define ilog_path_required_error_message "Path to object, dkey, or akey required\n" #define error_msg_write_mode_only "Can only modify the VOS tree in 'write mode'\n" @@ -477,7 +485,7 @@ ddb_run_dtx_dump(struct ddb_ctx *ctx, struct dtx_dump_options *opt) itp_free(&itp); return rc; } - ddb_printf(ctx, "%d Active Entries\n", args.entry_count); + ddb_printf(ctx, "%" PRIu32 " Active Entries\n", args.entry_count); } if (both || opt->committed) { args.entry_count = 0; @@ -487,7 +495,7 @@ ddb_run_dtx_dump(struct ddb_ctx *ctx, struct dtx_dump_options *opt) itp_free(&itp); return rc; } - ddb_printf(ctx, "%d Committed Entries\n", args.entry_count); + ddb_printf(ctx, "%" PRIu32 " Committed Entries\n", args.entry_count); } dv_cont_close(&coh); @@ -891,12 +899,12 @@ ddb_run_vea_update(struct ddb_ctx *ctx, struct vea_update_options *opt) } offset = parse_uint32_t(opt->offset); - if (offset <= 0) { + if (offset == 0) { ddb_errorf(ctx, "'%s' is not a valid offset\n", opt->offset); return -DER_INVAL; } blk_cnt = parse_uint32_t(opt->blk_cnt); - if (blk_cnt <= 0) { + if (blk_cnt == 0) { ddb_errorf(ctx, "'%s' is not a valid block size\n", opt->blk_cnt); return -DER_INVAL; } @@ -905,7 +913,7 @@ ddb_run_vea_update(struct ddb_ctx *ctx, struct vea_update_options *opt) if (!SUCCESS(rc)) return rc; - ddb_printf(ctx, "Adding free region to vea {%lu, %d}\n", offset, blk_cnt); + ddb_printf(ctx, "Adding free region to vea {%" PRIu64 ", %" PRIu32 "}\n", offset, blk_cnt); rc = dv_vea_free_region(ctx->dc_poh, offset, blk_cnt); if (!SUCCESS(rc)) ddb_errorf(ctx, "Unable to add new free region: "DF_RC"\n", DP_RC(rc)); @@ -1139,7 +1147,7 @@ dtx_active_entry_discard_invalid(struct dv_dtx_active_entry *entry, void *cb_arg ddb_errorf(ctx, "Error: " DF_RC "\n", DP_RC(rc)); } - return 0; + return rc; } int @@ -1275,3 +1283,171 @@ ddb_run_dev_replace(struct ddb_ctx *ctx, struct dev_replace_options *opt) return rc; } + +enum { TIMESPEC_FMT_SIZE = sizeof("1970-01-01 00:00:00.000000000") }; + +static int +timespec2str(struct timespec *tspec, char *buf, size_t buf_size) +{ + struct tm date; + size_t buf_len; + int rc; + + tzset(); + if (localtime_r(&(tspec->tv_sec), &date) == NULL) + return d_errno2der(errno); + + buf_len = strftime(buf, buf_size, "%F %T", &date); + if (buf_len == 0) + return -DER_INVAL; + + rc = snprintf(&buf[buf_len], buf_size - buf_len, ".%09ld", tspec->tv_nsec); + if (rc < 0) + return -DER_INVAL; + if (rc >= buf_size - buf_len) + return -DER_TRUNC; + + return 0; +} + +static int +dtx_print_time_stat(struct ddb_ctx *ctx, char *stat_name, uint64_t epoch, char *align) +{ + struct timespec tspec; + char buf[TIMESPEC_FMT_SIZE]; + int rc; + + rc = d_hlc2timespec(epoch, &tspec); + if (!SUCCESS(rc)) { + ddb_errorf(ctx, "Conversion error of the DTX stat %s: " DF_RC "\n", stat_name, + DP_RC(rc)); + return rc; + } + rc = timespec2str(&tspec, buf, TIMESPEC_FMT_SIZE); + if (!SUCCESS(rc)) { + ddb_errorf(ctx, "Conversion error of the DTX stat %s: " DF_RC "\n", stat_name, + DP_RC(rc)); + return rc; + } + ddb_printf(ctx, "\t- %s time:%s%s, %" PRIu64 "\n", stat_name, align, buf, epoch); + + return 0; +} + +struct dtx_stat_args { + struct ddb_ctx *ctx; + struct dtx_stat_options *opt; + uint32_t cont_seen; + uint32_t cmt_cnt; +}; + +static int +dtx_stat(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint32_t *cnt) +{ + struct dtx_stat stat; + daos_handle_t coh = {0}; + uint32_t cnt_tmp; + int rc; + + rc = dv_cont_open(ctx->dc_poh, itp_cont(itp), &coh); + if (!SUCCESS(rc)) + goto done; + + rc = vos_dtx_get_cmt_cnt(coh, &cnt_tmp); + if (!SUCCESS(rc)) + goto done; + vos_dtx_stat(coh, &stat, 0); + + ddb_print(ctx, "DTX entries statistics of "); + itp_print_full(ctx, itp); + ddb_print(ctx, "\n"); + ddb_printf(ctx, "\t- Number of committed DTX of the container:\t%" PRIu32 "\n", cnt_tmp); + rc = + dtx_print_time_stat(ctx, "DTX newest aggregated", stat.dtx_newest_aggregated, "\t\t\t"); + if (!SUCCESS(rc)) + goto done; + if (cnt != NULL) + *cnt = cnt_tmp; +done: + dv_cont_close(&coh); + + return rc; +} + +static int +dtx_stat_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, + vos_iter_param_t *param, void *cb_arg, unsigned int *acts) +{ + struct ddb_ctx *ctx; + struct dv_indexed_tree_path itp = {0}; + struct dtx_stat_args *args; + uint32_t cnt; + int rc; + + if (type != VOS_ITER_COUUID) { + rc = 0; + goto done; + } + + args = (struct dtx_stat_args *)cb_arg; + ctx = args->ctx; + + itp_set_cont(&itp, entry->ie_couuid, args->cont_seen); + ++(args->cont_seen); + + rc = dtx_stat(ctx, &itp, &cnt); + if (!SUCCESS(rc)) + goto done; + args->cmt_cnt += cnt; + +done: + itp_free(&itp); + + return rc; +} + +int +ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) +{ + vos_iter_param_t param = {0}; + struct dtx_stat_args args = {0}; + struct vos_iter_anchors anchors = {0}; + int rc; + + if (daos_handle_is_inval(ctx->dc_poh)) { + ddb_error(ctx, "Not connected to a pool. Use 'open' to connect to a pool.\n"); + return -DER_NONEXIST; + } + + if (opt->path != NULL && opt->path[0] != '\0') { + struct dv_indexed_tree_path itp = {0}; + + rc = init_path(ctx, opt->path, &itp); + if (!SUCCESS(rc)) + goto done; + + if (!itp_has_cont(&itp)) { + rc = -DER_INVAL; + goto done; + } + + rc = dtx_stat(ctx, &itp, NULL); +done: + itp_free(&itp); + return rc; + } + + args.ctx = ctx; + args.opt = opt; + param.ip_hdl = ctx->dc_poh; + param.ip_epr.epr_hi = DAOS_EPOCH_MAX; + do { + rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_stat_cb, &args, + NULL); + } while (rc > 0); + if (rc == 0) + ddb_printf(ctx, "Number of committed DTX of the pool:\t\t\t%" PRIu32 "\n", + args.cmt_cnt); + + return rc; +} diff --git a/src/utils/ddb/ddb_parse.c b/src/utils/ddb/ddb_parse.c index 2e55615d016..9bbdc5e3b16 100644 --- a/src/utils/ddb/ddb_parse.c +++ b/src/utils/ddb/ddb_parse.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 */ @@ -7,6 +8,7 @@ #include #include #include +#include "daos_errno.h" #include "ddb_common.h" #include "ddb_parse.h" @@ -477,3 +479,24 @@ ddb_parse_key(const char *input, daos_key_t *key) key_parse_typed(input, key) : key_parse_str(input, key); } + +int +ddb_date2epoch(const char *date, uint64_t *epoch) +{ + struct tm date_tm = {0}; + struct timespec date_tspec = {0}; + char *endptr; + + if (date == NULL || epoch == NULL) + return -DER_INVAL; + + endptr = strptime(date, "%Y-%m-%d %H:%M:%S", &date_tm); + if (endptr == NULL || *endptr != '\0') + return -DER_INVAL; + + date_tspec.tv_sec = mktime(&date_tm); + if (date_tspec.tv_sec == (time_t)-1) + return daos_errno2der(errno); + + return d_timespec2hlc(date_tspec, epoch); +} diff --git a/src/utils/ddb/ddb_parse.h b/src/utils/ddb/ddb_parse.h index fcd28754445..93b7ef7c064 100644 --- a/src/utils/ddb/ddb_parse.h +++ b/src/utils/ddb/ddb_parse.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 */ @@ -50,4 +51,8 @@ int ddb_parse_key(const char *input, daos_key_t *key); */ int ddb_parse_dtx_id(const char *dtx_id_str, struct dtx_id *dtx_id); +/* Parse a string representing a date into an epoch */ +int +ddb_date2epoch(const char *date, uint64_t *epoch); + #endif /** __DAOS_DDB_PARSE_H */ diff --git a/src/utils/ddb/tests/SConscript b/src/utils/ddb/tests/SConscript index dc29dc8f848..3cf6297893c 100644 --- a/src/utils/ddb/tests/SConscript +++ b/src/utils/ddb/tests/SConscript @@ -6,25 +6,17 @@ def scons(): """Execute build""" Import('env', 'prereqs') + # ---------------------- + # Common build configs denv = env.Clone() - - # Add runtime paths for daos libraries denv.AppendUnique(RPATH_FULL=['$PREFIX/lib64/daos_srv']) - denv.AppendUnique(RPATH_FULL=['$PREFIX/lib64']) - - prereqs.require(denv, 'argobots', 'protobufc', 'pmdk', 'spdk') - # for ddb includes denv.AppendUnique(CPPPATH=[Dir('../').srcnode()]) denv.AppendUnique(LIBPATH=[Dir('../')]) - # Add include directory for vos internal headers denv.AppendUnique(CPPPATH=[Dir('../../../vos/').srcnode()]) denv.AppendUnique(CPPPATH=[Dir('../../../vos/')]) - denv.AppendUnique(CPPDEFINES='_GNU_SOURCE') - libs = ['vos', 'daos_common_pmem', 'abt', 'gurt', 'uuid', 'bio', 'cart', - 'cmocka', 'ddb'] # spdk libraries spdk_libs = ['rte_bus_pci', 'rte_eal', 'rte_kvargs', 'rte_mbuf', 'rte_mempool_ring', 'rte_mempool', 'rte_pci', 'rte_power', 'rte_ring', 'spdk_bdev_aio', @@ -33,36 +25,44 @@ def scons(): 'spdk_init', 'spdk_init', 'spdk_json', 'spdk_json', 'spdk_jsonrpc', 'spdk_log', 'spdk_log', 'spdk_log', 'spdk_notify', 'spdk_nvme', 'spdk_nvme', 'spdk_rpc', 'spdk_sock', 'spdk_thread', 'spdk_thread', 'spdk_trace', 'spdk_util', 'spdk_vmd'] + + # ---------------------- + # Build integration test + tenv = denv.Clone() + tenv.AppendUnique(CPPDEFINES='_GNU_SOURCE') + # Add runtime paths for daos libraries + tenv.AppendUnique(RPATH_FULL=['$PREFIX/lib64']) + prereqs.require(tenv, 'argobots', 'protobufc', 'pmdk', 'spdk') + + libs = ['vos', 'uuid', 'daos_common_pmem', 'abt', 'gurt', 'bio', 'cart', 'cmocka', 'ddb'] src = ['ddb_cmd_options_tests.c', 'ddb_commands_print_tests.c', 'ddb_commands_tests.c', 'ddb_main_tests.c', 'ddb_parse_tests.c', 'ddb_path_tests.c', 'ddb_test_driver.c', 'ddb_vos_tests.c'] - ddb_tests = denv.d_program('ddb_tests', [src], LIBS=libs + spdk_libs) + ddb_tests = tenv.d_program('ddb_tests', src, LIBS=libs + spdk_libs) - denv.Install('$PREFIX/bin/', ddb_tests) + tenv.Install('$PREFIX/bin/', ddb_tests) - # Build unit tests - denv = env.Clone() - prereqs.require(denv, 'argobots', 'spdk', 'pmdk') - libs = ['uuid', 'daos_common_pmem', 'gurt', 'vea', 'abt', 'bio', 'cmocka', 'pthread', 'pmemobj'] - 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()]) + # --------------- + # Build unit test + tenv = denv.Clone() + tenv.AppendUnique(CPPPATH=[Dir('../../../bio/').srcnode()]) + tenv.Append(CPPDEFINES=['-DDAOS_PMEM_BUILD']) + prereqs.require(tenv, 'argobots', 'spdk', 'pmdk') # 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']) + tenv.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') + libs = ['uuid', 'daos_common_pmem', 'gurt', 'vea', 'abt', 'bio', 'cmocka', 'pthread', 'pmemobj'] + src = ['ddb_ut.c', 'ddb_vos_ut.c'] + # vos and mock object files to wrap vos symbols + vos_obj = tenv.Object(Glob('../../../vos/*.c')) + mock_obj = tenv.Object(Glob('../../../dtx/tests/*_mock.c')) # Pylint does not like the exclude keyword below, but it works. # pylint: disable=unexpected-keyword-arg - ddb_src = Glob('../*.c', exclude=['../ddb_entry.c']) - src = ['ddb_ut.c', 'ddb_vos_ut.c'] - ddb_ut = denv.d_program('ddb_ut', src + vos_src + mock_src + ddb_src, LIBS=libs + spdk_libs) + ddb_obj = tenv.Object(Glob('../*.c', exclude=['../ddb_entry.c'])) + ddb_ut = tenv.d_program('ddb_ut', src + vos_obj + mock_obj + ddb_obj, LIBS=libs + spdk_libs) - denv.Install('$PREFIX/bin/', ddb_ut) + tenv.Install('$PREFIX/bin/', ddb_ut) if __name__ == "SCons.Script": diff --git a/src/utils/ddb/tests/ddb_cmd_options_tests.c b/src/utils/ddb/tests/ddb_cmd_options_tests.c index 9481748af0a..199099abc77 100644 --- a/src/utils/ddb/tests/ddb_cmd_options_tests.c +++ b/src/utils/ddb/tests/ddb_cmd_options_tests.c @@ -297,6 +297,25 @@ dtx_act_abort_options_parsing(void **state) assert_non_null(options->dtx_id); } +static void +dtx_stat_parsing(void **state) +{ + struct ddb_cmd_info info = {0}; + struct dtx_stat_options *options = &info.dci_cmd_option.dci_dtx_stat; + + /* test invalid arguments and options */ + test_run_inval_cmd("dtx_stat", "path", "extra"); /* too many argument */ + test_run_inval_cmd("dtx_stat", "-z"); /* invalid option */ + + /* test all arguments */ + test_run_cmd(&info, "dtx_stat", "path"); + assert_non_null(options->path); + + /* test all arguments */ + test_run_cmd(&info, "dtx_stat"); + assert_null(options->path); +} + /* * ----------------------------------------------- * Execute @@ -307,20 +326,21 @@ int ddb_cmd_options_tests_run() { static const struct CMUnitTest tests[] = { - TEST(ls_options_parsing), - TEST(open_options_parsing), - TEST(value_dump_options_parsing), - TEST(rm_options_parsing), - TEST(value_load_options_parsing), - TEST(ilog_dump_options_parsing), - TEST(ilog_commit_options_parsing), - TEST(ilog_clear_options_parsing), - TEST(dtx_dump_options_parsing), - TEST(dtx_cmt_clear_options_parsing), - TEST(smd_sync_options_parsing), - TEST(vea_update_options_parsing), - TEST(dtx_act_commit_options_parsing), - TEST(dtx_act_abort_options_parsing), + TEST(ls_options_parsing), + TEST(open_options_parsing), + TEST(value_dump_options_parsing), + TEST(rm_options_parsing), + TEST(value_load_options_parsing), + TEST(ilog_dump_options_parsing), + TEST(ilog_commit_options_parsing), + TEST(ilog_clear_options_parsing), + TEST(dtx_dump_options_parsing), + TEST(dtx_cmt_clear_options_parsing), + TEST(smd_sync_options_parsing), + TEST(vea_update_options_parsing), + TEST(dtx_act_commit_options_parsing), + TEST(dtx_act_abort_options_parsing), + TEST(dtx_stat_parsing), }; return cmocka_run_group_tests_name("DDB commands option parsing tests", tests, diff --git a/src/utils/ddb/tests/ddb_cmocka.h b/src/utils/ddb/tests/ddb_cmocka.h index dd38715e376..a5948b6a048 100644 --- a/src/utils/ddb/tests/ddb_cmocka.h +++ b/src/utils/ddb/tests/ddb_cmocka.h @@ -1,5 +1,6 @@ /** * (C) Copyright 2022 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -7,8 +8,10 @@ #ifndef DAOS_DDB_CMOCKA_H #define DAOS_DDB_CMOCKA_H #include +#include #include #include +#include #include #define assert_uuid_equal(a, b) \ @@ -69,4 +72,52 @@ #define assert_invalid(x) assert_rc_equal(-DER_INVAL, (x)) #define assert_nonexist(x) assert_rc_equal(-DER_NONEXIST, (x)) +/* XXX Needs of testing _buf to avoid false positive error from gcc: '%s' directive argument is null + * [-Werror=format-overflow=] */ +#define assert_regex_match(str, regex) \ + do { \ + char *_str_dup; \ + char *_token; \ + regex_t _preg; \ + int _rc = regcomp(&_preg, regex, REG_NOSUB | REG_EXTENDED); \ + if (_rc != 0) { \ + char *_buf; \ + size_t _buf_size = regerror(_rc, NULL, NULL, 0); \ + D_ALLOC_ARRAY(_buf, _buf_size); \ + assert_non_null(_buf); \ + regerror(_rc, NULL, _buf, _buf_size); \ + if (_buf) \ + print_error("ERROR: invalid regex '%s': %s\n", regex, _buf); \ + D_FREE(_buf); \ + fail(); \ + } \ + D_STRNDUP(_str_dup, str, strlen(str)); \ + assert_non_null(_str_dup); \ + _token = strtok(_str_dup, "\n"); \ + while (_token != NULL) { \ + _rc = regexec(&_preg, _token, 0, NULL, 0); \ + if (_rc == 0) \ + break; \ + if (_rc != REG_NOMATCH) { \ + char *_buf; \ + size_t _buf_size = regerror(_rc, &_preg, NULL, 0); \ + D_ALLOC_ARRAY(_buf, _buf_size); \ + assert_non_null(_buf); \ + regerror(_rc, &_preg, _buf, _buf_size); \ + if (_buf) \ + print_error("ERROR: invalid regex '%s': %s\n", regex, \ + _buf); \ + D_FREE(_buf); \ + regfree(&_preg); \ + D_FREE(_str_dup); \ + fail(); \ + } \ + _token = strtok(NULL, "\n"); \ + } \ + D_FREE(_str_dup); \ + regfree(&_preg); \ + if (_rc == REG_NOMATCH) \ + fail_msg("'%s' regex not matched in '%s'", regex, str); \ + } while (0) + #endif /* DAOS_DDB_CMOCKA_H */ diff --git a/src/utils/ddb/tests/ddb_commands_tests.c b/src/utils/ddb/tests/ddb_commands_tests.c index b0fdbf5ad89..3e61649ec2b 100644 --- a/src/utils/ddb/tests/ddb_commands_tests.c +++ b/src/utils/ddb/tests/ddb_commands_tests.c @@ -4,7 +4,15 @@ * * SPDX-License-Identifier: BSD-2-Clause-Patent */ +#include +#include +#include +#include +#include + +#include "gurt/common.h" #include +#include "daos/common.h" #include #include #include @@ -397,6 +405,38 @@ feature_cmd_tests(void **state) assert_success(ddb_run_feature(&g_ctx, &opt)); } +static void +dtx_stat_tests(void **state) +{ + struct dt_vos_pool_ctx *tctx = *state; + struct ddb_ctx ctx = {0}; + struct dtx_stat_options opt = {0}; + + ctx.dc_poh = tctx->dvt_poh; + ctx.dc_io_ft.ddb_print_message = dvt_fake_print; + ctx.dc_io_ft.ddb_print_error = dvt_fake_print; + ctx.dc_write_mode = false; + opt.path = "[0]"; + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt)); + assert_regex_match( + dvt_fake_print_buffer, + "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+1$"); + assert_regex_match(dvt_fake_print_buffer, + "^[[:blank:]]+- DTX newest aggregated time:.+, 0$"); + + opt.path = ""; + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt)); + assert_regex_match( + dvt_fake_print_buffer, + "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+1$"); + assert_regex_match(dvt_fake_print_buffer, + "^[[:blank:]]+- DTX newest aggregated time:.+, 0$"); + assert_regex_match(dvt_fake_print_buffer, + "^Number of committed DTX of the pool:[[:blank:]]+1$"); +} + /* * -------------------------------------------------------------- * End test functions @@ -447,6 +487,7 @@ ddb_commands_tests_run() TEST(dump_ilog_cmd_tests), TEST(dump_superblock_cmd_tests), TEST(dump_dtx_cmd_tests), + TEST(dtx_stat_tests), TEST(rm_cmd_tests), TEST(load_cmd_tests), TEST(rm_ilog_cmd_tests), diff --git a/src/utils/ddb/tests/ddb_parse_tests.c b/src/utils/ddb/tests/ddb_parse_tests.c index 155b9c1bcc4..fdc1b025927 100644 --- a/src/utils/ddb/tests/ddb_parse_tests.c +++ b/src/utils/ddb/tests/ddb_parse_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 */ @@ -8,6 +9,7 @@ #include #include #include +#include "daos_errno.h" #include "ddb_cmocka.h" #include "ddb_test_driver.h" @@ -348,6 +350,34 @@ pool_flags_tests(void **state) assert_rc_equal(-DER_INVAL, rc); } +static void +date2epoch_tests(void **state) +{ + uint64_t epoch = -1; + int rc; + + rc = ddb_date2epoch(NULL, &epoch); + assert_rc_equal(rc, -DER_INVAL); + assert_int_equal(epoch, -1); + rc = ddb_date2epoch("1970-01-01 00:00:00", NULL); + assert_rc_equal(rc, -DER_INVAL); + assert_int_equal(epoch, -1); + rc = ddb_date2epoch(NULL, NULL); + assert_rc_equal(rc, -DER_INVAL); + assert_int_equal(epoch, -1); + rc = ddb_date2epoch("foo", NULL); + assert_rc_equal(rc, -DER_INVAL); + assert_int_equal(epoch, -1); + rc = ddb_date2epoch("0000-00-00 00:00:00", &epoch); + assert_rc_equal(rc, -DER_INVAL); + assert_int_equal(epoch, -1); + + rc = ddb_date2epoch("1970-01-01 00:00:00", &epoch); + assert_success(rc); + assert_int_equal(rc, 0); + assert_int_equal(epoch, 0x9aa0d62878400000ull); +} + /* * ----------------------------------------------- * Execute @@ -360,6 +390,7 @@ ddb_parse_tests_run() static const struct CMUnitTest tests[] = { TEST(vos_file_parts_tests), TEST(string_to_argv_tests), TEST(parse_args_tests), TEST(parse_dtx_id_tests), TEST(keys_are_parsed_correctly), TEST(pool_flags_tests), + TEST(date2epoch_tests), }; return cmocka_run_group_tests_name("DDB helper parsing function tests", tests, NULL, NULL); diff --git a/src/utils/ddb/tests/ddb_test_driver.h b/src/utils/ddb/tests/ddb_test_driver.h index e5cc649d0d6..4573a395e82 100644 --- a/src/utils/ddb/tests/ddb_test_driver.h +++ b/src/utils/ddb/tests/ddb_test_driver.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 */ @@ -77,7 +78,7 @@ int ddb_test_pool_setup(struct dt_vos_pool_ctx *tctx); extern uint32_t dvt_fake_print_called; extern bool dvt_fake_print_just_count; -#define DVT_FAKE_PRINT_BUFFER_SIZE (1024) +#define DVT_FAKE_PRINT_BUFFER_SIZE (2048) extern char dvt_fake_print_buffer[DVT_FAKE_PRINT_BUFFER_SIZE]; int dvt_fake_print(const char *fmt, ...); void dvt_fake_print_reset(void); diff --git a/src/vos/tests/vts_dtx.c b/src/vos/tests/vts_dtx.c index 00e5d85fedf..944fdc0f744 100644 --- a/src/vos/tests/vts_dtx.c +++ b/src/vos/tests/vts_dtx.c @@ -791,21 +791,22 @@ dtx_17(void **state) static void dtx_18(void **state) { - struct io_test_args *args = *state; - struct dtx_id xid[10]; - daos_iod_t iod = { 0 }; - d_sg_list_t sgl = { 0 }; - daos_recx_t rex = { 0 }; - daos_key_t dkey; - daos_key_t akey; - d_iov_t val_iov; - uint64_t epoch; - char dkey_buf[UPDATE_DKEY_SIZE]; - char akey_buf[UPDATE_AKEY_SIZE]; - char update_buf[UPDATE_BUF_SIZE]; - char fetch_buf[UPDATE_BUF_SIZE]; - int rc; - int i; + struct io_test_args *args = *state; + struct dtx_id xid[10]; + daos_iod_t iod = {0}; + d_sg_list_t sgl = {0}; + daos_recx_t rex = {0}; + daos_key_t dkey; + daos_key_t akey; + d_iov_t val_iov; + uint64_t epoch; + uint32_t cnt; + char dkey_buf[UPDATE_DKEY_SIZE]; + char akey_buf[UPDATE_AKEY_SIZE]; + char update_buf[UPDATE_BUF_SIZE]; + char fetch_buf[UPDATE_BUF_SIZE]; + int rc; + int i; /* Assume I am the leader. */ for (i = 0; i < 10; i++) { @@ -841,10 +842,18 @@ dtx_18(void **state) sleep(3); + rc = vos_dtx_get_cmt_cnt(args->ctx.tc_co_hdl, &cnt); + assert_rc_equal(rc, 0); + assert_int_equal(cnt, 10); + /* Aggregate the DTXs. */ rc = vos_dtx_aggregate(args->ctx.tc_co_hdl); assert_rc_equal(rc, 0); + rc = vos_dtx_get_cmt_cnt(args->ctx.tc_co_hdl, &cnt); + assert_rc_equal(rc, 0); + assert_int_equal(cnt, 0); + for (i = 0; i < 10; i++) { rc = vos_dtx_check(args->ctx.tc_co_hdl, &xid[i], NULL, NULL, NULL, false); assert_rc_equal(rc, -DER_NONEXIST); diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index 7838f9c6048..7801bf7830d 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -4006,3 +4006,45 @@ vos_dtx_local_end(struct dtx_handle *dth, int result) return result; } + +int +vos_dtx_get_cmt_cnt(daos_handle_t coh, uint32_t *cnt) +{ + struct umem_instance *umm; + struct vos_container *cont; + struct vos_dtx_blob_df *dbd; + uint32_t tmp; + int rc; + + cont = vos_hdl2cont(coh); + if (cont == NULL) { + rc = -DER_INVAL; + goto out; + } + if (cnt == NULL) { + rc = -DER_INVAL; + goto out; + } + + tmp = 0; + umm = vos_cont2umm(cont); + dbd = umem_off2ptr(umm, cont->vc_cont_df->cd_dtx_committed_head); + while (dbd != NULL) { + if (dbd->dbd_magic != DTX_CMT_BLOB_MAGIC) { + D_ERROR("Committed DTX blob with bad magic: container=" DF_UUID + ", wait=%#x, get=%#x\n", + DP_UUID(cont->vc_id), DTX_CMT_BLOB_MAGIC, dbd->dbd_magic); + rc = -DER_INVAL; + goto out; + } + tmp += dbd->dbd_count; + + dbd = umem_off2ptr(umm, dbd->dbd_next); + } + + *cnt = tmp; + rc = 0; + +out: + return rc; +}