From 888ab647e55bf8f72c5c9a4564151e3b34e0c180 Mon Sep 17 00:00:00 2001 From: Krzysztof Filipek Date: Thu, 12 Dec 2024 17:45:31 +0100 Subject: [PATCH 1/3] Move PMDK CTL sources --- src/ctl/ctl.c | 578 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/ctl/ctl.h | 202 ++++++++++++++++++ 2 files changed, 780 insertions(+) create mode 100644 src/ctl/ctl.c create mode 100644 src/ctl/ctl.h diff --git a/src/ctl/ctl.c b/src/ctl/ctl.c new file mode 100644 index 000000000..d6afba88e --- /dev/null +++ b/src/ctl/ctl.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright 2016-2020, Intel Corporation */ + +/* + * ctl.c -- implementation of the interface for examination and modification of + * the library's internal state + */ +#include "ctl.h" +#include "os.h" +#include "alloc.h" + +#define CTL_MAX_ENTRIES 100 + +#define MAX_CONFIG_FILE_LEN (1 << 20) /* 1 megabyte */ + +#define CTL_STRING_QUERY_SEPARATOR ";" +#define CTL_NAME_VALUE_SEPARATOR "=" +#define CTL_QUERY_NODE_SEPARATOR "." +#define CTL_VALUE_ARG_SEPARATOR "," + +static int ctl_global_first_free = 0; +static struct ctl_node CTL_NODE(global)[CTL_MAX_ENTRIES]; + +/* + * This is the top level node of the ctl tree structure. Each node can contain + * children and leaf nodes. + * + * Internal nodes simply create a new path in the tree whereas child nodes are + * the ones providing the read/write functionality by the means of callbacks. + * + * Each tree node must be NULL-terminated, CTL_NODE_END macro is provided for + * convenience. + */ +struct ctl { + struct ctl_node root[CTL_MAX_ENTRIES]; + int first_free; +}; + +/* + * ctl_find_node -- (internal) searches for a matching entry point in the + * provided nodes + * + * The caller is responsible for freeing all of the allocated indexes, + * regardless of the return value. + */ +static const struct ctl_node * +ctl_find_node(const struct ctl_node *nodes, const char *name, + struct ctl_indexes *indexes) +{ + LOG(3, "nodes %p name %s indexes %p", nodes, name, indexes); + + const struct ctl_node *n = NULL; + char *sptr = NULL; + char *parse_str = Strdup(name); + if (parse_str == NULL) + return NULL; + + char *node_name = strtok_r(parse_str, CTL_QUERY_NODE_SEPARATOR, &sptr); + + /* + * Go through the string and separate tokens that correspond to nodes + * in the main ctl tree. + */ + while (node_name != NULL) { + char *endptr; + /* + * Ignore errno from strtol: FreeBSD returns EINVAL if no + * conversion is performed. Linux does not, but endptr + * check is valid in both cases. + */ + int tmp_errno = errno; + long index_value = strtol(node_name, &endptr, 0); + errno = tmp_errno; + struct ctl_index *index_entry = NULL; + if (endptr != node_name) { /* a valid index */ + index_entry = Malloc(sizeof(*index_entry)); + if (index_entry == NULL) + goto error; + index_entry->value = index_value; + PMDK_SLIST_INSERT_HEAD(indexes, index_entry, entry); + } + + for (n = &nodes[0]; n->name != NULL; ++n) { + if (index_entry && n->type == CTL_NODE_INDEXED) + break; + else if (strcmp(n->name, node_name) == 0) + break; + } + if (n->name == NULL) + goto error; + + if (index_entry) + index_entry->name = n->name; + + nodes = n->children; + node_name = strtok_r(NULL, CTL_QUERY_NODE_SEPARATOR, &sptr); + } + + Free(parse_str); + return n; + +error: + Free(parse_str); + return NULL; +} + +/* + * ctl_delete_indexes -- + * (internal) removes and frees all entries on the index list + */ +static void +ctl_delete_indexes(struct ctl_indexes *indexes) +{ + while (!PMDK_SLIST_EMPTY(indexes)) { + struct ctl_index *index = PMDK_SLIST_FIRST(indexes); + PMDK_SLIST_REMOVE_HEAD(indexes, entry); + Free(index); + } +} + +/* + * ctl_parse_args -- (internal) parses a string argument based on the node + * structure + */ +static void * +ctl_parse_args(const struct ctl_argument *arg_proto, char *arg) +{ + ASSERTne(arg, NULL); + + char *dest_arg = Malloc(arg_proto->dest_size); + if (dest_arg == NULL) { + ERR("!Malloc"); + return NULL; + } + + char *sptr = NULL; + char *arg_sep = strtok_r(arg, CTL_VALUE_ARG_SEPARATOR, &sptr); + for (const struct ctl_argument_parser *p = arg_proto->parsers; + p->parser != NULL; ++p) { + ASSERT(p->dest_offset + p->dest_size <= arg_proto->dest_size); + if (arg_sep == NULL) { + ERR("!strtok_r"); + goto error_parsing; + } + + if (p->parser(arg_sep, dest_arg + p->dest_offset, + p->dest_size) != 0) + goto error_parsing; + + arg_sep = strtok_r(NULL, CTL_VALUE_ARG_SEPARATOR, &sptr); + } + + return dest_arg; + +error_parsing: + Free(dest_arg); + return NULL; +} + +/* + * ctl_query_get_real_args -- (internal) returns a pointer with actual argument + * structure as required by the node callback + */ +static void * +ctl_query_get_real_args(const struct ctl_node *n, void *write_arg, + enum ctl_query_source source) +{ + void *real_arg = NULL; + switch (source) { + case CTL_QUERY_CONFIG_INPUT: + real_arg = ctl_parse_args(n->arg, write_arg); + break; + case CTL_QUERY_PROGRAMMATIC: + real_arg = write_arg; + break; + default: + ASSERT(0); + break; + } + + return real_arg; +} + +/* + * ctl_query_cleanup_real_args -- (internal) cleanups relevant argument + * structures allocated as a result of the get_real_args call + */ +static void +ctl_query_cleanup_real_args(const struct ctl_node *n, void *real_arg, + enum ctl_query_source source) +{ + switch (source) { + case CTL_QUERY_CONFIG_INPUT: + Free(real_arg); + break; + case CTL_QUERY_PROGRAMMATIC: + break; + default: + ASSERT(0); + break; + } +} + +/* + * ctl_exec_query_read -- (internal) calls the read callback of a node + */ +static int +ctl_exec_query_read(void *ctx, const struct ctl_node *n, + enum ctl_query_source source, void *arg, struct ctl_indexes *indexes) +{ + if (arg == NULL) { + ERR("read queries require non-NULL argument"); + errno = EINVAL; + return -1; + } + + return n->cb[CTL_QUERY_READ](ctx, source, arg, indexes); +} + +/* + * ctl_exec_query_write -- (internal) calls the write callback of a node + */ +static int +ctl_exec_query_write(void *ctx, const struct ctl_node *n, + enum ctl_query_source source, void *arg, struct ctl_indexes *indexes) +{ + if (arg == NULL) { + ERR("write queries require non-NULL argument"); + errno = EINVAL; + return -1; + } + + void *real_arg = ctl_query_get_real_args(n, arg, source); + if (real_arg == NULL) { + LOG(1, "Invalid arguments"); + return -1; + } + + int ret = n->cb[CTL_QUERY_WRITE](ctx, source, real_arg, indexes); + ctl_query_cleanup_real_args(n, real_arg, source); + + return ret; +} + +/* + * ctl_exec_query_runnable -- (internal) calls the run callback of a node + */ +static int +ctl_exec_query_runnable(void *ctx, const struct ctl_node *n, + enum ctl_query_source source, void *arg, struct ctl_indexes *indexes) +{ + return n->cb[CTL_QUERY_RUNNABLE](ctx, source, arg, indexes); +} + +static int (*ctl_exec_query[MAX_CTL_QUERY_TYPE])(void *ctx, + const struct ctl_node *n, enum ctl_query_source source, void *arg, + struct ctl_indexes *indexes) = { + ctl_exec_query_read, + ctl_exec_query_write, + ctl_exec_query_runnable, +}; + +/* + * ctl_query -- (internal) parses the name and calls the appropriate methods + * from the ctl tree + */ +int +ctl_query(struct ctl *ctl, void *ctx, enum ctl_query_source source, + const char *name, enum ctl_query_type type, void *arg) +{ + LOG(3, "ctl %p ctx %p source %d name %s type %d arg %p", + ctl, ctx, source, name, type, arg); + + if (name == NULL) { + ERR("invalid query"); + errno = EINVAL; + return -1; + } + + /* + * All of the indexes are put on this list so that the handlers can + * easily retrieve the index values. The list is cleared once the ctl + * query has been handled. + */ + struct ctl_indexes indexes; + PMDK_SLIST_INIT(&indexes); + + int ret = -1; + + const struct ctl_node *n = ctl_find_node(CTL_NODE(global), + name, &indexes); + + if (n == NULL && ctl) { + ctl_delete_indexes(&indexes); + n = ctl_find_node(ctl->root, name, &indexes); + } + + if (n == NULL || n->type != CTL_NODE_LEAF || n->cb[type] == NULL) { + ERR("invalid query entry point %s", name); + errno = EINVAL; + goto out; + } + + ret = ctl_exec_query[type](ctx, n, source, arg, &indexes); + +out: + ctl_delete_indexes(&indexes); + + return ret; +} + +/* + * ctl_register_module_node -- adds a new node to the CTL tree root. + */ +void +ctl_register_module_node(struct ctl *c, const char *name, struct ctl_node *n) +{ + struct ctl_node *nnode = c == NULL ? + &CTL_NODE(global)[ctl_global_first_free++] : + &c->root[c->first_free++]; + + nnode->children = n; + nnode->type = CTL_NODE_NAMED; + nnode->name = name; +} + +/* + * ctl_parse_query -- (internal) splits an entire query string + * into name and value + */ +static int +ctl_parse_query(char *qbuf, char **name, char **value) +{ + if (qbuf == NULL) + return -1; + + char *sptr; + *name = strtok_r(qbuf, CTL_NAME_VALUE_SEPARATOR, &sptr); + if (*name == NULL) + return -1; + + *value = strtok_r(NULL, CTL_NAME_VALUE_SEPARATOR, &sptr); + if (*value == NULL) + return -1; + + /* the value itself mustn't include CTL_NAME_VALUE_SEPARATOR */ + char *extra = strtok_r(NULL, CTL_NAME_VALUE_SEPARATOR, &sptr); + if (extra != NULL) + return -1; + + return 0; +} + +/* + * ctl_load_config -- executes the entire query collection from a provider + */ +static int +ctl_load_config(struct ctl *ctl, void *ctx, char *buf) +{ + int r = 0; + char *sptr = NULL; /* for internal use of strtok */ + char *name; + char *value; + + ASSERTne(buf, NULL); + + char *qbuf = strtok_r(buf, CTL_STRING_QUERY_SEPARATOR, &sptr); + while (qbuf != NULL) { + r = ctl_parse_query(qbuf, &name, &value); + if (r != 0) { + ERR("failed to parse query %s", qbuf); + return -1; + } + + r = ctl_query(ctl, ctx, CTL_QUERY_CONFIG_INPUT, + name, CTL_QUERY_WRITE, value); + + if (r < 0 && ctx != NULL) + return -1; + + qbuf = strtok_r(NULL, CTL_STRING_QUERY_SEPARATOR, &sptr); + } + + return 0; +} + +/* + * ctl_load_config_from_string -- loads obj configuration from string + */ +int +ctl_load_config_from_string(struct ctl *ctl, void *ctx, const char *cfg_string) +{ + LOG(3, "ctl %p ctx %p cfg_string \"%s\"", ctl, ctx, cfg_string); + + char *buf = Strdup(cfg_string); + if (buf == NULL) { + ERR("!Strdup"); + return -1; + } + + int ret = ctl_load_config(ctl, ctx, buf); + + Free(buf); + return ret; +} + +/* + * ctl_load_config_from_file -- loads obj configuration from file + * + * This function opens up the config file, allocates a buffer of size equal to + * the size of the file, reads its content and sanitizes it for ctl_load_config. + */ +int +ctl_load_config_from_file(struct ctl *ctl, void *ctx, const char *cfg_file) +{ + LOG(3, "ctl %p ctx %p cfg_file \"%s\"", ctl, ctx, cfg_file); + + int ret = -1; + + FILE *fp = os_fopen(cfg_file, "r"); + if (fp == NULL) + return ret; + + int err; + if ((err = fseek(fp, 0, SEEK_END)) != 0) + goto error_file_parse; + + long fsize = ftell(fp); + if (fsize == -1) + goto error_file_parse; + + if (fsize > MAX_CONFIG_FILE_LEN) { + ERR("Config file too large"); + goto error_file_parse; + } + + if ((err = fseek(fp, 0, SEEK_SET)) != 0) + goto error_file_parse; + + char *buf = Zalloc((size_t)fsize + 1); /* +1 for NULL-termination */ + if (buf == NULL) { + ERR("!Zalloc"); + goto error_file_parse; + } + + size_t bufpos = 0; + + int c; + int is_comment_section = 0; + while ((c = fgetc(fp)) != EOF) { + if (c == '#') + is_comment_section = 1; + else if (c == '\n') + is_comment_section = 0; + else if (!is_comment_section && !isspace(c)) + buf[bufpos++] = (char)c; + } + + ret = ctl_load_config(ctl, ctx, buf); + + Free(buf); + +error_file_parse: + (void) fclose(fp); + return ret; +} + +/* + * ctl_new -- allocates and initializes ctl data structures + */ +struct ctl * +ctl_new(void) +{ + struct ctl *c = Zalloc(sizeof(struct ctl)); + if (c == NULL) { + ERR("!Zalloc"); + return NULL; + } + + c->first_free = 0; + return c; +} + +/* + * ctl_delete -- deletes ctl + */ +void +ctl_delete(struct ctl *c) +{ + Free(c); +} + +/* + * ctl_parse_ll -- (internal) parses and returns a long long signed integer + */ +static long long +ctl_parse_ll(const char *str) +{ + char *endptr; + int olderrno = errno; + errno = 0; + long long val = strtoll(str, &endptr, 0); + if (endptr == str || errno != 0) + return LLONG_MIN; + errno = olderrno; + + return val; +} + +/* + * ctl_arg_boolean -- checks whether the provided argument contains + * either a 1 or y or Y. + */ +int +ctl_arg_boolean(const void *arg, void *dest, size_t dest_size) +{ + int *intp = dest; + char in = ((char *)arg)[0]; + + if (tolower(in) == 'y' || in == '1') { + *intp = 1; + return 0; + } else if (tolower(in) == 'n' || in == '0') { + *intp = 0; + return 0; + } + + return -1; +} + +/* + * ctl_arg_integer -- parses signed integer argument + */ +int +ctl_arg_integer(const void *arg, void *dest, size_t dest_size) +{ + long long val = ctl_parse_ll(arg); + if (val == LLONG_MIN) + return -1; + + switch (dest_size) { + case sizeof(int): + if (val > INT_MAX || val < INT_MIN) + return -1; + *(int *)dest = (int)val; + break; + case sizeof(long long): + *(long long *)dest = val; + break; + case sizeof(uint8_t): + if (val > UINT8_MAX || val < 0) + return -1; + *(uint8_t *)dest = (uint8_t)val; + break; + default: + ERR("invalid destination size %zu", dest_size); + errno = EINVAL; + return -1; + } + + return 0; +} + +/* + * ctl_arg_string -- verifies length and copies a string argument into a zeroed + * buffer + */ +int +ctl_arg_string(const void *arg, void *dest, size_t dest_size) +{ + /* check if the incoming string is longer or equal to dest_size */ + if (strnlen(arg, dest_size) == dest_size) + return -1; + + strncpy(dest, arg, dest_size); + + return 0; +} diff --git a/src/ctl/ctl.h b/src/ctl/ctl.h new file mode 100644 index 000000000..f70322a6f --- /dev/null +++ b/src/ctl/ctl.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright 2016-2020, Intel Corporation */ + +/* + * ctl.h -- internal declaration of statistics and control related structures + */ + +#ifndef PMDK_CTL_H +#define PMDK_CTL_H 1 + +#include "queue.h" +#include "errno.h" +#include "out.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ctl; + +struct ctl_index { + const char *name; + long value; + PMDK_SLIST_ENTRY(ctl_index) entry; +}; + +PMDK_SLIST_HEAD(ctl_indexes, ctl_index); + +enum ctl_query_source { + CTL_UNKNOWN_QUERY_SOURCE, + /* query executed directly from the program */ + CTL_QUERY_PROGRAMMATIC, + /* query executed from the config file */ + CTL_QUERY_CONFIG_INPUT, + + MAX_CTL_QUERY_SOURCE +}; + +enum ctl_query_type { + CTL_QUERY_READ, + CTL_QUERY_WRITE, + CTL_QUERY_RUNNABLE, + + MAX_CTL_QUERY_TYPE +}; + +typedef int (*node_callback)(void *ctx, enum ctl_query_source type, + void *arg, struct ctl_indexes *indexes); + +enum ctl_node_type { + CTL_NODE_UNKNOWN, + CTL_NODE_NAMED, + CTL_NODE_LEAF, + CTL_NODE_INDEXED, + + MAX_CTL_NODE +}; + +typedef int (*ctl_arg_parser)(const void *arg, void *dest, size_t dest_size); + +struct ctl_argument_parser { + size_t dest_offset; /* offset of the field inside of the argument */ + size_t dest_size; /* size of the field inside of the argument */ + ctl_arg_parser parser; +}; + +struct ctl_argument { + size_t dest_size; /* sizeof the entire argument */ + struct ctl_argument_parser parsers[]; /* array of 'fields' in arg */ +}; + +#define sizeof_member(t, m) sizeof(((t *)0)->m) + +#define CTL_ARG_PARSER(t, p)\ +{0, sizeof(t), p} + +#define CTL_ARG_PARSER_STRUCT(t, m, p)\ +{offsetof(t, m), sizeof_member(t, m), p} + +#define CTL_ARG_PARSER_END {0, 0, NULL} + +/* + * CTL Tree node structure, do not use directly. All the necessary functionality + * is provided by the included macros. + */ +struct ctl_node { + const char *name; + enum ctl_node_type type; + + node_callback cb[MAX_CTL_QUERY_TYPE]; + const struct ctl_argument *arg; + + const struct ctl_node *children; +}; + +struct ctl *ctl_new(void); +void ctl_delete(struct ctl *stats); + +int ctl_load_config_from_string(struct ctl *ctl, void *ctx, + const char *cfg_string); +int ctl_load_config_from_file(struct ctl *ctl, void *ctx, + const char *cfg_file); + +/* Use through CTL_REGISTER_MODULE, never directly */ +void ctl_register_module_node(struct ctl *c, + const char *name, struct ctl_node *n); + +int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size); +#define CTL_ARG_BOOLEAN {sizeof(int),\ + {{0, sizeof(int), ctl_arg_boolean},\ + CTL_ARG_PARSER_END}}; + +int ctl_arg_integer(const void *arg, void *dest, size_t dest_size); +#define CTL_ARG_INT {sizeof(int),\ + {{0, sizeof(int), ctl_arg_integer},\ + CTL_ARG_PARSER_END}}; + +#define CTL_ARG_LONG_LONG {sizeof(long long),\ + {{0, sizeof(long long), ctl_arg_integer},\ + CTL_ARG_PARSER_END}}; + +int ctl_arg_string(const void *arg, void *dest, size_t dest_size); +#define CTL_ARG_STRING(len) {len,\ + {{0, len, ctl_arg_string},\ + CTL_ARG_PARSER_END}}; + +#define CTL_STR(name) #name + +#define CTL_NODE_END {NULL, CTL_NODE_UNKNOWN, {NULL, NULL, NULL}, NULL, NULL} + +#define CTL_NODE(name, ...)\ +ctl_node_##__VA_ARGS__##_##name + +int ctl_query(struct ctl *ctl, void *ctx, enum ctl_query_source source, + const char *name, enum ctl_query_type type, void *arg); + +/* Declaration of a new child node */ +#define CTL_CHILD(name, ...)\ +{CTL_STR(name), CTL_NODE_NAMED, {NULL, NULL, NULL}, NULL,\ + (struct ctl_node *)CTL_NODE(name, __VA_ARGS__)} + +/* Declaration of a new indexed node */ +#define CTL_INDEXED(name, ...)\ +{CTL_STR(name), CTL_NODE_INDEXED, {NULL, NULL, NULL}, NULL,\ + (struct ctl_node *)CTL_NODE(name, __VA_ARGS__)} + +#define CTL_READ_HANDLER(name, ...)\ +ctl_##__VA_ARGS__##_##name##_read + +#define CTL_WRITE_HANDLER(name, ...)\ +ctl_##__VA_ARGS__##_##name##_write + +#define CTL_RUNNABLE_HANDLER(name, ...)\ +ctl_##__VA_ARGS__##_##name##_runnable + +#define CTL_ARG(name)\ +ctl_arg_##name + +/* + * Declaration of a new read-only leaf. If used the corresponding read function + * must be declared by CTL_READ_HANDLER macro. + */ +#define CTL_LEAF_RO(name, ...)\ +{CTL_STR(name), CTL_NODE_LEAF, \ + {CTL_READ_HANDLER(name, __VA_ARGS__), NULL, NULL}, NULL, NULL} + +/* + * Declaration of a new write-only leaf. If used the corresponding write + * function must be declared by CTL_WRITE_HANDLER macro. + */ +#define CTL_LEAF_WO(name, ...)\ +{CTL_STR(name), CTL_NODE_LEAF, \ + {NULL, CTL_WRITE_HANDLER(name, __VA_ARGS__), NULL},\ + &CTL_ARG(name), NULL} + +/* + * Declaration of a new runnable leaf. If used the corresponding run + * function must be declared by CTL_RUNNABLE_HANDLER macro. + */ +#define CTL_LEAF_RUNNABLE(name, ...)\ +{CTL_STR(name), CTL_NODE_LEAF, \ + {NULL, NULL, CTL_RUNNABLE_HANDLER(name, __VA_ARGS__)},\ + NULL, NULL} + +/* + * Declaration of a new read-write leaf. If used both read and write function + * must be declared by CTL_READ_HANDLER and CTL_WRITE_HANDLER macros. + */ +#define CTL_LEAF_RW(name)\ +{CTL_STR(name), CTL_NODE_LEAF,\ + {CTL_READ_HANDLER(name), CTL_WRITE_HANDLER(name), NULL},\ + &CTL_ARG(name), NULL} + +#define CTL_REGISTER_MODULE(_ctl, name)\ +ctl_register_module_node((_ctl), CTL_STR(name),\ +(struct ctl_node *)CTL_NODE(name)) + +#ifdef __cplusplus +} +#endif + +#endif From ab2a19c6c8d998fcd29c09b08e57c5d8f436f2b0 Mon Sep 17 00:00:00 2001 From: Krzysztof Filipek Date: Thu, 12 Dec 2024 17:52:14 +0100 Subject: [PATCH 2/3] [CMake] Disable pedantic mode and disable cast qualifier warning --- cmake/helpers.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/helpers.cmake b/cmake/helpers.cmake index 0a165bc3a..ddcd5f03d 100644 --- a/cmake/helpers.cmake +++ b/cmake/helpers.cmake @@ -232,9 +232,8 @@ function(add_umf_target_compile_options name) PRIVATE -fPIC -Wall -Wextra - -Wpedantic -Wformat-security - -Wcast-qual + -Wno-cast-qual $<$:-fdiagnostics-color=auto>) if(CMAKE_BUILD_TYPE STREQUAL "Release") target_compile_definitions(${name} PRIVATE -D_FORTIFY_SOURCE=2) From 05db80c5d76723ac4575ff2bf15b9630c9ee1a21 Mon Sep 17 00:00:00 2001 From: Krzysztof Filipek Date: Thu, 12 Dec 2024 17:46:32 +0100 Subject: [PATCH 3/3] CTL: Add a CTL functionality to the UMF This commit introduces sources and compilation of the CTL. The CTL sources are copied from: https://github.com/pmem/pmdk/tree/master/src/common Signed-off-by: Krzysztof Filipek --- src/CMakeLists.txt | 3 + src/ctl/ctl.c | 827 ++++++++++++++++++++++--------------------- src/ctl/ctl.h | 207 ++++++----- test/CMakeLists.txt | 5 + test/ctl/config.txt | 1 + test/ctl/ctl_debug.c | 72 ++++ test/ctl/ctl_debug.h | 32 ++ test/ctl/test.cpp | 28 ++ 8 files changed, 672 insertions(+), 503 deletions(-) create mode 100644 test/ctl/config.txt create mode 100644 test/ctl/ctl_debug.c create mode 100644 test/ctl/ctl_debug.h create mode 100644 test/ctl/test.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ffd928f7c..4edaa5957 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,8 @@ add_subdirectory(coarse) set(UMF_LIBS $ $) +set(CTL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ctl/ctl.c) + if(LINUX) set(BA_SOURCES ${BA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/base_alloc/base_alloc_linux.c) @@ -45,6 +47,7 @@ set(HWLOC_DEPENDENT_SOURCES topology.c) set(UMF_SOURCES ${BA_SOURCES} + ${CTL_SOURCES} libumf.c ipc.c ipc_cache.c diff --git a/src/ctl/ctl.c b/src/ctl/ctl.c index d6afba88e..68bcc5072 100644 --- a/src/ctl/ctl.c +++ b/src/ctl/ctl.c @@ -1,25 +1,47 @@ +/* + * + * Copyright (C) 2016-2024 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + // SPDX-License-Identifier: BSD-3-Clause -/* Copyright 2016-2020, Intel Corporation */ +/* Copyright 2016-2024, Intel Corporation */ /* * ctl.c -- implementation of the interface for examination and modification of * the library's internal state */ + #include "ctl.h" -#include "os.h" -#include "alloc.h" + +#include +#include +#include +#include +#include + +#include "base_alloc/base_alloc_global.h" +#include "utils/utils_common.h" +#include "utlist.h" + +#ifdef _WIN32 +#define strtok_r strtok_s +#endif #define CTL_MAX_ENTRIES 100 #define MAX_CONFIG_FILE_LEN (1 << 20) /* 1 megabyte */ -#define CTL_STRING_QUERY_SEPARATOR ";" +#define CTL_STRING_QUERY_SEPARATOR ";:" #define CTL_NAME_VALUE_SEPARATOR "=" #define CTL_QUERY_NODE_SEPARATOR "." #define CTL_VALUE_ARG_SEPARATOR "," static int ctl_global_first_free = 0; -static struct ctl_node CTL_NODE(global)[CTL_MAX_ENTRIES]; +static struct ctl_node CTL_NODE(global, )[CTL_MAX_ENTRIES]; /* * This is the top level node of the ctl tree structure. Each node can contain @@ -32,10 +54,31 @@ static struct ctl_node CTL_NODE(global)[CTL_MAX_ENTRIES]; * convenience. */ struct ctl { - struct ctl_node root[CTL_MAX_ENTRIES]; - int first_free; + struct ctl_node root[CTL_MAX_ENTRIES]; + int first_free; }; +void *Malloc(size_t sz) { return umf_ba_global_alloc(sz); } + +void *Zalloc(size_t sz) { + void *ptr = umf_ba_global_alloc(sz); + if (ptr) { + memset(ptr, 0, sz); + } + return ptr; +} + +void Free(void *ptr) { umf_ba_global_free(ptr); } + +char *Strdup(const char *s) { + size_t len = strlen(s) + 1; + char *p = umf_ba_global_alloc(len); + if (p) { + memcpy(p, s, len); + } + return p; +} + /* * ctl_find_node -- (internal) searches for a matching entry point in the * provided nodes @@ -43,365 +86,340 @@ struct ctl { * The caller is responsible for freeing all of the allocated indexes, * regardless of the return value. */ -static const struct ctl_node * -ctl_find_node(const struct ctl_node *nodes, const char *name, - struct ctl_indexes *indexes) -{ - LOG(3, "nodes %p name %s indexes %p", nodes, name, indexes); - - const struct ctl_node *n = NULL; - char *sptr = NULL; - char *parse_str = Strdup(name); - if (parse_str == NULL) - return NULL; - - char *node_name = strtok_r(parse_str, CTL_QUERY_NODE_SEPARATOR, &sptr); - - /* +static const struct ctl_node *ctl_find_node(const struct ctl_node *nodes, + const char *name, + struct ctl_index_utlist *indexes) { + const struct ctl_node *n = NULL; + char *sptr = NULL; + char *parse_str = Strdup(name); + if (parse_str == NULL) { + return NULL; + } + + char *node_name = strtok_r(parse_str, CTL_QUERY_NODE_SEPARATOR, &sptr); + + /* * Go through the string and separate tokens that correspond to nodes * in the main ctl tree. */ - while (node_name != NULL) { - char *endptr; - /* - * Ignore errno from strtol: FreeBSD returns EINVAL if no - * conversion is performed. Linux does not, but endptr - * check is valid in both cases. + while (node_name != NULL) { + char *endptr; + /* + * Ignore ERANGE or EINVA errno from strtol. + * endptr is used to check conversion result. */ - int tmp_errno = errno; - long index_value = strtol(node_name, &endptr, 0); - errno = tmp_errno; - struct ctl_index *index_entry = NULL; - if (endptr != node_name) { /* a valid index */ - index_entry = Malloc(sizeof(*index_entry)); - if (index_entry == NULL) - goto error; - index_entry->value = index_value; - PMDK_SLIST_INSERT_HEAD(indexes, index_entry, entry); - } - - for (n = &nodes[0]; n->name != NULL; ++n) { - if (index_entry && n->type == CTL_NODE_INDEXED) - break; - else if (strcmp(n->name, node_name) == 0) - break; - } - if (n->name == NULL) - goto error; - - if (index_entry) - index_entry->name = n->name; - - nodes = n->children; - node_name = strtok_r(NULL, CTL_QUERY_NODE_SEPARATOR, &sptr); - } - - Free(parse_str); - return n; + int tmp_errno = errno; + long index_value = strtol(node_name, &endptr, 0); + errno = tmp_errno; + struct ctl_index_utlist *index_entry = NULL; + if (endptr != node_name) { /* a valid index */ + index_entry = Malloc(sizeof(*index_entry)); + if (index_entry == NULL) { + goto error; + } + index_entry->value = index_value; + LL_PREPEND(indexes, index_entry); + } + + for (n = &nodes[0]; n->name != NULL; ++n) { + if (index_entry && n->type == CTL_NODE_INDEXED) { + break; + } else if (strcmp(n->name, node_name) == 0) { + break; + } + } + if (n->name == NULL) { + goto error; + } + + if (index_entry) { + index_entry->name = n->name; + } + + nodes = n->children; + node_name = strtok_r(NULL, CTL_QUERY_NODE_SEPARATOR, &sptr); + } + + Free(parse_str); + return n; error: - Free(parse_str); - return NULL; + Free(parse_str); + return NULL; } /* * ctl_delete_indexes -- * (internal) removes and frees all entries on the index list */ -static void -ctl_delete_indexes(struct ctl_indexes *indexes) -{ - while (!PMDK_SLIST_EMPTY(indexes)) { - struct ctl_index *index = PMDK_SLIST_FIRST(indexes); - PMDK_SLIST_REMOVE_HEAD(indexes, entry); - Free(index); - } +static void ctl_delete_indexes(struct ctl_index_utlist *indexes) { + if (!indexes) { + return; + } + struct ctl_index_utlist *elem, *tmp; + LL_FOREACH_SAFE(indexes, elem, tmp) { + LL_DELETE(indexes, elem); + if (elem) { + Free(elem); + } + } } /* * ctl_parse_args -- (internal) parses a string argument based on the node * structure */ -static void * -ctl_parse_args(const struct ctl_argument *arg_proto, char *arg) -{ - ASSERTne(arg, NULL); - - char *dest_arg = Malloc(arg_proto->dest_size); - if (dest_arg == NULL) { - ERR("!Malloc"); - return NULL; - } - - char *sptr = NULL; - char *arg_sep = strtok_r(arg, CTL_VALUE_ARG_SEPARATOR, &sptr); - for (const struct ctl_argument_parser *p = arg_proto->parsers; - p->parser != NULL; ++p) { - ASSERT(p->dest_offset + p->dest_size <= arg_proto->dest_size); - if (arg_sep == NULL) { - ERR("!strtok_r"); - goto error_parsing; - } - - if (p->parser(arg_sep, dest_arg + p->dest_offset, - p->dest_size) != 0) - goto error_parsing; - - arg_sep = strtok_r(NULL, CTL_VALUE_ARG_SEPARATOR, &sptr); - } - - return dest_arg; +static void *ctl_parse_args(const struct ctl_argument *arg_proto, char *arg) { + char *dest_arg = Malloc(arg_proto->dest_size); + if (dest_arg == NULL) { + return NULL; + } + + char *sptr = NULL; + char *arg_sep = strtok_r(arg, CTL_VALUE_ARG_SEPARATOR, &sptr); + for (const struct ctl_argument_parser *p = arg_proto->parsers; + p->parser != NULL; ++p) { + if (arg_sep == NULL) { + goto error_parsing; + } + + if (p->parser(arg_sep, dest_arg + p->dest_offset, p->dest_size) != 0) { + goto error_parsing; + } + + arg_sep = strtok_r(NULL, CTL_VALUE_ARG_SEPARATOR, &sptr); + } + + return dest_arg; error_parsing: - Free(dest_arg); - return NULL; + Free(dest_arg); + return NULL; } /* * ctl_query_get_real_args -- (internal) returns a pointer with actual argument * structure as required by the node callback */ -static void * -ctl_query_get_real_args(const struct ctl_node *n, void *write_arg, - enum ctl_query_source source) -{ - void *real_arg = NULL; - switch (source) { - case CTL_QUERY_CONFIG_INPUT: - real_arg = ctl_parse_args(n->arg, write_arg); - break; - case CTL_QUERY_PROGRAMMATIC: - real_arg = write_arg; - break; - default: - ASSERT(0); - break; - } - - return real_arg; +static void *ctl_query_get_real_args(const struct ctl_node *n, void *write_arg, + enum ctl_query_source source) { + void *real_arg = NULL; + switch (source) { + case CTL_QUERY_CONFIG_INPUT: + real_arg = ctl_parse_args(n->arg, write_arg); + break; + case CTL_QUERY_PROGRAMMATIC: + real_arg = write_arg; + break; + default: + break; + } + + return real_arg; } /* * ctl_query_cleanup_real_args -- (internal) cleanups relevant argument * structures allocated as a result of the get_real_args call */ -static void -ctl_query_cleanup_real_args(const struct ctl_node *n, void *real_arg, - enum ctl_query_source source) -{ - switch (source) { - case CTL_QUERY_CONFIG_INPUT: - Free(real_arg); - break; - case CTL_QUERY_PROGRAMMATIC: - break; - default: - ASSERT(0); - break; - } +static void ctl_query_cleanup_real_args(const struct ctl_node *n, + void *real_arg, + enum ctl_query_source source) { + /* suppress unused-parameter errors */ + (void)n; + + switch (source) { + case CTL_QUERY_CONFIG_INPUT: + Free(real_arg); + break; + case CTL_QUERY_PROGRAMMATIC: + break; + default: + break; + } } /* * ctl_exec_query_read -- (internal) calls the read callback of a node */ -static int -ctl_exec_query_read(void *ctx, const struct ctl_node *n, - enum ctl_query_source source, void *arg, struct ctl_indexes *indexes) -{ - if (arg == NULL) { - ERR("read queries require non-NULL argument"); - errno = EINVAL; - return -1; - } - - return n->cb[CTL_QUERY_READ](ctx, source, arg, indexes); +static int ctl_exec_query_read(void *ctx, const struct ctl_node *n, + enum ctl_query_source source, void *arg, + struct ctl_index_utlist *indexes) { + if (arg == NULL) { + errno = EINVAL; + return -1; + } + + return n->cb[CTL_QUERY_READ](ctx, source, arg, indexes); } /* * ctl_exec_query_write -- (internal) calls the write callback of a node */ -static int -ctl_exec_query_write(void *ctx, const struct ctl_node *n, - enum ctl_query_source source, void *arg, struct ctl_indexes *indexes) -{ - if (arg == NULL) { - ERR("write queries require non-NULL argument"); - errno = EINVAL; - return -1; - } - - void *real_arg = ctl_query_get_real_args(n, arg, source); - if (real_arg == NULL) { - LOG(1, "Invalid arguments"); - return -1; - } - - int ret = n->cb[CTL_QUERY_WRITE](ctx, source, real_arg, indexes); - ctl_query_cleanup_real_args(n, real_arg, source); - - return ret; +static int ctl_exec_query_write(void *ctx, const struct ctl_node *n, + enum ctl_query_source source, void *arg, + struct ctl_index_utlist *indexes) { + if (arg == NULL) { + errno = EINVAL; + return -1; + } + + void *real_arg = ctl_query_get_real_args(n, arg, source); + if (real_arg == NULL) { + return -1; + } + + int ret = n->cb[CTL_QUERY_WRITE](ctx, source, real_arg, indexes); + ctl_query_cleanup_real_args(n, real_arg, source); + + return ret; } /* * ctl_exec_query_runnable -- (internal) calls the run callback of a node */ -static int -ctl_exec_query_runnable(void *ctx, const struct ctl_node *n, - enum ctl_query_source source, void *arg, struct ctl_indexes *indexes) -{ - return n->cb[CTL_QUERY_RUNNABLE](ctx, source, arg, indexes); +static int ctl_exec_query_runnable(void *ctx, const struct ctl_node *n, + enum ctl_query_source source, void *arg, + struct ctl_index_utlist *indexes) { + return n->cb[CTL_QUERY_RUNNABLE](ctx, source, arg, indexes); } -static int (*ctl_exec_query[MAX_CTL_QUERY_TYPE])(void *ctx, - const struct ctl_node *n, enum ctl_query_source source, void *arg, - struct ctl_indexes *indexes) = { - ctl_exec_query_read, - ctl_exec_query_write, - ctl_exec_query_runnable, +static int (*ctl_exec_query[MAX_CTL_QUERY_TYPE])( + void *ctx, const struct ctl_node *n, enum ctl_query_source source, + void *arg, struct ctl_index_utlist *indexes) = { + ctl_exec_query_read, + ctl_exec_query_write, + ctl_exec_query_runnable, }; /* * ctl_query -- (internal) parses the name and calls the appropriate methods * from the ctl tree */ -int -ctl_query(struct ctl *ctl, void *ctx, enum ctl_query_source source, - const char *name, enum ctl_query_type type, void *arg) -{ - LOG(3, "ctl %p ctx %p source %d name %s type %d arg %p", - ctl, ctx, source, name, type, arg); - - if (name == NULL) { - ERR("invalid query"); - errno = EINVAL; - return -1; - } - - /* +int ctl_query(struct ctl *ctl, void *ctx, enum ctl_query_source source, + const char *name, enum ctl_query_type type, void *arg) { + if (name == NULL) { + errno = EINVAL; + return -1; + } + + /* * All of the indexes are put on this list so that the handlers can * easily retrieve the index values. The list is cleared once the ctl * query has been handled. */ - struct ctl_indexes indexes; - PMDK_SLIST_INIT(&indexes); + struct ctl_index_utlist *indexes = NULL; + indexes = Zalloc(sizeof(*indexes)); - int ret = -1; + int ret = -1; - const struct ctl_node *n = ctl_find_node(CTL_NODE(global), - name, &indexes); + const struct ctl_node *n = ctl_find_node(CTL_NODE(global, ), name, indexes); - if (n == NULL && ctl) { - ctl_delete_indexes(&indexes); - n = ctl_find_node(ctl->root, name, &indexes); - } + if (n == NULL && ctl) { + ctl_delete_indexes(indexes); + indexes = NULL; + n = ctl_find_node(ctl->root, name, indexes); + } - if (n == NULL || n->type != CTL_NODE_LEAF || n->cb[type] == NULL) { - ERR("invalid query entry point %s", name); - errno = EINVAL; - goto out; - } + if (n == NULL || n->type != CTL_NODE_LEAF || n->cb[type] == NULL) { + errno = EINVAL; + goto out; + } - ret = ctl_exec_query[type](ctx, n, source, arg, &indexes); + ret = ctl_exec_query[type](ctx, n, source, arg, indexes); out: - ctl_delete_indexes(&indexes); + ctl_delete_indexes(indexes); - return ret; + return ret; } /* * ctl_register_module_node -- adds a new node to the CTL tree root. */ -void -ctl_register_module_node(struct ctl *c, const char *name, struct ctl_node *n) -{ - struct ctl_node *nnode = c == NULL ? - &CTL_NODE(global)[ctl_global_first_free++] : - &c->root[c->first_free++]; - - nnode->children = n; - nnode->type = CTL_NODE_NAMED; - nnode->name = name; +void ctl_register_module_node(struct ctl *c, const char *name, + struct ctl_node *n) { + struct ctl_node *nnode = c == NULL + ? &CTL_NODE(global, )[ctl_global_first_free++] + : &c->root[c->first_free++]; + + nnode->children = n; + nnode->type = CTL_NODE_NAMED; + nnode->name = name; } /* * ctl_parse_query -- (internal) splits an entire query string * into name and value */ -static int -ctl_parse_query(char *qbuf, char **name, char **value) -{ - if (qbuf == NULL) - return -1; - - char *sptr; - *name = strtok_r(qbuf, CTL_NAME_VALUE_SEPARATOR, &sptr); - if (*name == NULL) - return -1; - - *value = strtok_r(NULL, CTL_NAME_VALUE_SEPARATOR, &sptr); - if (*value == NULL) - return -1; - - /* the value itself mustn't include CTL_NAME_VALUE_SEPARATOR */ - char *extra = strtok_r(NULL, CTL_NAME_VALUE_SEPARATOR, &sptr); - if (extra != NULL) - return -1; - - return 0; +static int ctl_parse_query(char *qbuf, char **name, char **value) { + if (qbuf == NULL) { + return -1; + } + + char *sptr = NULL; + *name = strtok_r(qbuf, CTL_NAME_VALUE_SEPARATOR, &sptr); + if (*name == NULL) { + return -1; + } + + *value = strtok_r(NULL, CTL_NAME_VALUE_SEPARATOR, &sptr); + if (*value == NULL) { + return -1; + } + + /* the value itself mustn't include CTL_NAME_VALUE_SEPARATOR */ + char *extra = strtok_r(NULL, CTL_NAME_VALUE_SEPARATOR, &sptr); + if (extra != NULL) { + return -1; + } + + return 0; } /* * ctl_load_config -- executes the entire query collection from a provider */ -static int -ctl_load_config(struct ctl *ctl, void *ctx, char *buf) -{ - int r = 0; - char *sptr = NULL; /* for internal use of strtok */ - char *name; - char *value; - - ASSERTne(buf, NULL); - - char *qbuf = strtok_r(buf, CTL_STRING_QUERY_SEPARATOR, &sptr); - while (qbuf != NULL) { - r = ctl_parse_query(qbuf, &name, &value); - if (r != 0) { - ERR("failed to parse query %s", qbuf); - return -1; - } - - r = ctl_query(ctl, ctx, CTL_QUERY_CONFIG_INPUT, - name, CTL_QUERY_WRITE, value); - - if (r < 0 && ctx != NULL) - return -1; - - qbuf = strtok_r(NULL, CTL_STRING_QUERY_SEPARATOR, &sptr); - } - - return 0; +static int ctl_load_config(struct ctl *ctl, void *ctx, char *buf) { + int r = 0; + char *sptr = NULL; /* for internal use of strtok */ + char *name; + char *value; + char *qbuf = strtok_r(buf, CTL_STRING_QUERY_SEPARATOR, &sptr); + + while (qbuf != NULL) { + r = ctl_parse_query(qbuf, &name, &value); + if (r != 0) { + return -1; + } + + r = ctl_query(ctl, ctx, CTL_QUERY_CONFIG_INPUT, name, CTL_QUERY_WRITE, + value); + + if (r < 0 && ctx != NULL) { + return -1; + } + + qbuf = strtok_r(NULL, CTL_STRING_QUERY_SEPARATOR, &sptr); + } + + return 0; } /* * ctl_load_config_from_string -- loads obj configuration from string */ -int -ctl_load_config_from_string(struct ctl *ctl, void *ctx, const char *cfg_string) -{ - LOG(3, "ctl %p ctx %p cfg_string \"%s\"", ctl, ctx, cfg_string); +int ctl_load_config_from_string(struct ctl *ctl, void *ctx, + const char *cfg_string) { + char *buf = Strdup(cfg_string); + if (buf == NULL) { + return -1; + } - char *buf = Strdup(cfg_string); - if (buf == NULL) { - ERR("!Strdup"); - return -1; - } + int ret = ctl_load_config(ctl, ctx, buf); - int ret = ctl_load_config(ctl, ctx, buf); - - Free(buf); - return ret; + Free(buf); + return ret; } /* @@ -410,169 +428,166 @@ ctl_load_config_from_string(struct ctl *ctl, void *ctx, const char *cfg_string) * This function opens up the config file, allocates a buffer of size equal to * the size of the file, reads its content and sanitizes it for ctl_load_config. */ -int -ctl_load_config_from_file(struct ctl *ctl, void *ctx, const char *cfg_file) -{ - LOG(3, "ctl %p ctx %p cfg_file \"%s\"", ctl, ctx, cfg_file); - - int ret = -1; - - FILE *fp = os_fopen(cfg_file, "r"); - if (fp == NULL) - return ret; - - int err; - if ((err = fseek(fp, 0, SEEK_END)) != 0) - goto error_file_parse; - - long fsize = ftell(fp); - if (fsize == -1) - goto error_file_parse; - - if (fsize > MAX_CONFIG_FILE_LEN) { - ERR("Config file too large"); - goto error_file_parse; - } - - if ((err = fseek(fp, 0, SEEK_SET)) != 0) - goto error_file_parse; - - char *buf = Zalloc((size_t)fsize + 1); /* +1 for NULL-termination */ - if (buf == NULL) { - ERR("!Zalloc"); - goto error_file_parse; - } - - size_t bufpos = 0; - - int c; - int is_comment_section = 0; - while ((c = fgetc(fp)) != EOF) { - if (c == '#') - is_comment_section = 1; - else if (c == '\n') - is_comment_section = 0; - else if (!is_comment_section && !isspace(c)) - buf[bufpos++] = (char)c; - } - - ret = ctl_load_config(ctl, ctx, buf); - - Free(buf); +#ifdef WINDOWS_API_NEEDED +int ctl_load_config_from_file(struct ctl *ctl, void *ctx, + const char *cfg_file) { + int ret = -1; + long fsize = 0; + char *buf = NULL; + + FILE *fp = utils_file_open(cfg_file, "r"); + if (fp == NULL) { + return ret; + } + + int err; + if ((err = fseek(fp, 0, SEEK_END)) != 0) { + goto error_file_parse; + } + + fsize = ftell(fp); + if (fsize == -1) { + goto error_file_parse; + } + + if (fsize > MAX_CONFIG_FILE_LEN) { + goto error_file_parse; + } + + if ((err = fseek(fp, 0, SEEK_SET)) != 0) { + goto error_file_parse; + } + + buf = Zalloc((size_t)fsize + 1); /* +1 for NULL-termination */ + if (buf == NULL) { + goto error_file_parse; + } + + { + size_t bufpos = 0; + int c; + int is_comment_section = 0; + while ((c = fgetc(fp)) != EOF) { + if (c == '#') { + is_comment_section = 1; + } else if (c == '\n') { + is_comment_section = 0; + } else if (!is_comment_section && !isspace(c)) { + buf[bufpos++] = (char)c; + } + } + } + + ret = ctl_load_config(ctl, ctx, buf); + + Free(buf); error_file_parse: - (void) fclose(fp); - return ret; + (void)fclose(fp); + return ret; } +#endif /* * ctl_new -- allocates and initializes ctl data structures */ -struct ctl * -ctl_new(void) -{ - struct ctl *c = Zalloc(sizeof(struct ctl)); - if (c == NULL) { - ERR("!Zalloc"); - return NULL; - } - - c->first_free = 0; - return c; +struct ctl *ctl_new(void) { + struct ctl *c = Zalloc(sizeof(struct ctl)); + if (c == NULL) { + return NULL; + } + + c->first_free = 0; + return c; } /* * ctl_delete -- deletes ctl */ -void -ctl_delete(struct ctl *c) -{ - Free(c); -} +void ctl_delete(struct ctl *c) { Free(c); } /* * ctl_parse_ll -- (internal) parses and returns a long long signed integer */ -static long long -ctl_parse_ll(const char *str) -{ - char *endptr; - int olderrno = errno; - errno = 0; - long long val = strtoll(str, &endptr, 0); - if (endptr == str || errno != 0) - return LLONG_MIN; - errno = olderrno; - - return val; +static long long ctl_parse_ll(const char *str) { + char *endptr; + int olderrno = errno; + errno = 0; + long long val = strtoll(str, &endptr, 0); + if (endptr == str || errno != 0) { + return LLONG_MIN; + } + errno = olderrno; + + return val; } /* * ctl_arg_boolean -- checks whether the provided argument contains * either a 1 or y or Y. */ -int -ctl_arg_boolean(const void *arg, void *dest, size_t dest_size) -{ - int *intp = dest; - char in = ((char *)arg)[0]; - - if (tolower(in) == 'y' || in == '1') { - *intp = 1; - return 0; - } else if (tolower(in) == 'n' || in == '0') { - *intp = 0; - return 0; - } - - return -1; +int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size) { + /* suppress unused-parameter errors */ + (void)dest_size; + + int *intp = dest; + char in = ((const char *)arg)[0]; + + if (tolower(in) == 'y' || in == '1') { + *intp = 1; + return 0; + } else if (tolower(in) == 'n' || in == '0') { + *intp = 0; + return 0; + } + + return -1; } /* * ctl_arg_integer -- parses signed integer argument */ -int -ctl_arg_integer(const void *arg, void *dest, size_t dest_size) -{ - long long val = ctl_parse_ll(arg); - if (val == LLONG_MIN) - return -1; - - switch (dest_size) { - case sizeof(int): - if (val > INT_MAX || val < INT_MIN) - return -1; - *(int *)dest = (int)val; - break; - case sizeof(long long): - *(long long *)dest = val; - break; - case sizeof(uint8_t): - if (val > UINT8_MAX || val < 0) - return -1; - *(uint8_t *)dest = (uint8_t)val; - break; - default: - ERR("invalid destination size %zu", dest_size); - errno = EINVAL; - return -1; - } - - return 0; +int ctl_arg_integer(const void *arg, void *dest, size_t dest_size) { + long long val = ctl_parse_ll(arg); + if (val == LLONG_MIN) { + return -1; + } + + switch (dest_size) { + case sizeof(int): + if (val > INT_MAX || val < INT_MIN) { + return -1; + } + *(int *)dest = (int)val; + break; + case sizeof(long long): + *(long long *)dest = val; + break; + case sizeof(uint8_t): + if (val > UINT8_MAX || val < 0) { + return -1; + } + *(uint8_t *)dest = (uint8_t)val; + break; + default: + errno = EINVAL; + return -1; + } + + return 0; } /* * ctl_arg_string -- verifies length and copies a string argument into a zeroed * buffer */ -int -ctl_arg_string(const void *arg, void *dest, size_t dest_size) -{ - /* check if the incoming string is longer or equal to dest_size */ - if (strnlen(arg, dest_size) == dest_size) - return -1; +int ctl_arg_string(const void *arg, void *dest, size_t dest_size) { + /* check if the incoming string is longer or equal to dest_size */ + if (strnlen(arg, dest_size) == dest_size) { + return -1; + } - strncpy(dest, arg, dest_size); + strncpy(dest, arg, dest_size); - return 0; + return 0; } diff --git a/src/ctl/ctl.h b/src/ctl/ctl.h index f70322a6f..bfcdeb76f 100644 --- a/src/ctl/ctl.h +++ b/src/ctl/ctl.h @@ -1,3 +1,12 @@ +/* + * + * Copyright (C) 2016-2024 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + /* SPDX-License-Identifier: BSD-3-Clause */ /* Copyright 2016-2020, Intel Corporation */ @@ -5,12 +14,11 @@ * ctl.h -- internal declaration of statistics and control related structures */ -#ifndef PMDK_CTL_H -#define PMDK_CTL_H 1 +#ifndef UMF_CTL_H +#define UMF_CTL_H 1 -#include "queue.h" -#include "errno.h" -#include "out.h" +#include +#include #ifdef __cplusplus extern "C" { @@ -18,182 +26,187 @@ extern "C" { struct ctl; -struct ctl_index { - const char *name; - long value; - PMDK_SLIST_ENTRY(ctl_index) entry; +struct ctl_index_utlist { + const char *name; + long value; + struct ctl_index_utlist *next; }; -PMDK_SLIST_HEAD(ctl_indexes, ctl_index); - enum ctl_query_source { - CTL_UNKNOWN_QUERY_SOURCE, - /* query executed directly from the program */ - CTL_QUERY_PROGRAMMATIC, - /* query executed from the config file */ - CTL_QUERY_CONFIG_INPUT, + CTL_UNKNOWN_QUERY_SOURCE, + /* query executed directly from the program */ + CTL_QUERY_PROGRAMMATIC, + /* query executed from the config file */ + CTL_QUERY_CONFIG_INPUT, - MAX_CTL_QUERY_SOURCE + MAX_CTL_QUERY_SOURCE }; enum ctl_query_type { - CTL_QUERY_READ, - CTL_QUERY_WRITE, - CTL_QUERY_RUNNABLE, + CTL_QUERY_READ, + CTL_QUERY_WRITE, + CTL_QUERY_RUNNABLE, - MAX_CTL_QUERY_TYPE + MAX_CTL_QUERY_TYPE }; -typedef int (*node_callback)(void *ctx, enum ctl_query_source type, - void *arg, struct ctl_indexes *indexes); +typedef int (*node_callback)(void *ctx, enum ctl_query_source type, void *arg, + struct ctl_index_utlist *indexes); enum ctl_node_type { - CTL_NODE_UNKNOWN, - CTL_NODE_NAMED, - CTL_NODE_LEAF, - CTL_NODE_INDEXED, + CTL_NODE_UNKNOWN, + CTL_NODE_NAMED, + CTL_NODE_LEAF, + CTL_NODE_INDEXED, - MAX_CTL_NODE + MAX_CTL_NODE }; typedef int (*ctl_arg_parser)(const void *arg, void *dest, size_t dest_size); struct ctl_argument_parser { - size_t dest_offset; /* offset of the field inside of the argument */ - size_t dest_size; /* size of the field inside of the argument */ - ctl_arg_parser parser; + size_t dest_offset; /* offset of the field inside of the argument */ + size_t dest_size; /* size of the field inside of the argument */ + ctl_arg_parser parser; }; struct ctl_argument { - size_t dest_size; /* sizeof the entire argument */ - struct ctl_argument_parser parsers[]; /* array of 'fields' in arg */ + size_t dest_size; /* sizeof the entire argument */ + struct ctl_argument_parser parsers[]; /* array of 'fields' in arg */ }; #define sizeof_member(t, m) sizeof(((t *)0)->m) -#define CTL_ARG_PARSER(t, p)\ -{0, sizeof(t), p} +#define CTL_ARG_PARSER(t, p) \ + { 0, sizeof(t), p } -#define CTL_ARG_PARSER_STRUCT(t, m, p)\ -{offsetof(t, m), sizeof_member(t, m), p} +#define CTL_ARG_PARSER_STRUCT(t, m, p) \ + { offsetof(t, m), sizeof_member(t, m), p } -#define CTL_ARG_PARSER_END {0, 0, NULL} +#define CTL_ARG_PARSER_END \ + { 0, 0, NULL } /* * CTL Tree node structure, do not use directly. All the necessary functionality * is provided by the included macros. */ struct ctl_node { - const char *name; - enum ctl_node_type type; + const char *name; + enum ctl_node_type type; - node_callback cb[MAX_CTL_QUERY_TYPE]; - const struct ctl_argument *arg; + node_callback cb[MAX_CTL_QUERY_TYPE]; + const struct ctl_argument *arg; - const struct ctl_node *children; + const struct ctl_node *children; }; struct ctl *ctl_new(void); void ctl_delete(struct ctl *stats); int ctl_load_config_from_string(struct ctl *ctl, void *ctx, - const char *cfg_string); -int ctl_load_config_from_file(struct ctl *ctl, void *ctx, - const char *cfg_file); + const char *cfg_string); +int ctl_load_config_from_file(struct ctl *ctl, void *ctx, const char *cfg_file); /* Use through CTL_REGISTER_MODULE, never directly */ -void ctl_register_module_node(struct ctl *c, - const char *name, struct ctl_node *n); +void ctl_register_module_node(struct ctl *c, const char *name, + struct ctl_node *n); int ctl_arg_boolean(const void *arg, void *dest, size_t dest_size); -#define CTL_ARG_BOOLEAN {sizeof(int),\ - {{0, sizeof(int), ctl_arg_boolean},\ - CTL_ARG_PARSER_END}}; +#define CTL_ARG_BOOLEAN \ + {sizeof(int), {{0, sizeof(int), ctl_arg_boolean}, CTL_ARG_PARSER_END}}; int ctl_arg_integer(const void *arg, void *dest, size_t dest_size); -#define CTL_ARG_INT {sizeof(int),\ - {{0, sizeof(int), ctl_arg_integer},\ - CTL_ARG_PARSER_END}}; +#define CTL_ARG_INT \ + {sizeof(int), {{0, sizeof(int), ctl_arg_integer}, CTL_ARG_PARSER_END}}; -#define CTL_ARG_LONG_LONG {sizeof(long long),\ - {{0, sizeof(long long), ctl_arg_integer},\ - CTL_ARG_PARSER_END}}; +#define CTL_ARG_LONG_LONG \ + { \ + sizeof(long long), { \ + {0, sizeof(long long), ctl_arg_integer}, CTL_ARG_PARSER_END \ + } \ + } int ctl_arg_string(const void *arg, void *dest, size_t dest_size); -#define CTL_ARG_STRING(len) {len,\ - {{0, len, ctl_arg_string},\ - CTL_ARG_PARSER_END}}; +#define CTL_ARG_STRING(len) \ + {len, {{0, len, ctl_arg_string}, CTL_ARG_PARSER_END}}; #define CTL_STR(name) #name -#define CTL_NODE_END {NULL, CTL_NODE_UNKNOWN, {NULL, NULL, NULL}, NULL, NULL} +#define CTL_NODE_END \ + { NULL, CTL_NODE_UNKNOWN, {NULL, NULL, NULL}, NULL, NULL } -#define CTL_NODE(name, ...)\ -ctl_node_##__VA_ARGS__##_##name +#define CTL_NODE(name, ...) ctl_node_##__VA_ARGS__##_##name int ctl_query(struct ctl *ctl, void *ctx, enum ctl_query_source source, - const char *name, enum ctl_query_type type, void *arg); + const char *name, enum ctl_query_type type, void *arg); /* Declaration of a new child node */ -#define CTL_CHILD(name, ...)\ -{CTL_STR(name), CTL_NODE_NAMED, {NULL, NULL, NULL}, NULL,\ - (struct ctl_node *)CTL_NODE(name, __VA_ARGS__)} +#define CTL_CHILD(name, ...) \ + { \ + CTL_STR(name), CTL_NODE_NAMED, {NULL, NULL, NULL}, NULL, \ + (struct ctl_node *)CTL_NODE(name, __VA_ARGS__) \ + } /* Declaration of a new indexed node */ -#define CTL_INDEXED(name, ...)\ -{CTL_STR(name), CTL_NODE_INDEXED, {NULL, NULL, NULL}, NULL,\ - (struct ctl_node *)CTL_NODE(name, __VA_ARGS__)} +#define CTL_INDEXED(name, ...) \ + { \ + CTL_STR(name), CTL_NODE_INDEXED, {NULL, NULL, NULL}, NULL, \ + (struct ctl_node *)CTL_NODE(name, __VA_ARGS__) \ + } -#define CTL_READ_HANDLER(name, ...)\ -ctl_##__VA_ARGS__##_##name##_read +#define CTL_READ_HANDLER(name, ...) ctl_##__VA_ARGS__##_##name##_read -#define CTL_WRITE_HANDLER(name, ...)\ -ctl_##__VA_ARGS__##_##name##_write +#define CTL_WRITE_HANDLER(name, ...) ctl_##__VA_ARGS__##_##name##_write -#define CTL_RUNNABLE_HANDLER(name, ...)\ -ctl_##__VA_ARGS__##_##name##_runnable +#define CTL_RUNNABLE_HANDLER(name, ...) ctl_##__VA_ARGS__##_##name##_runnable -#define CTL_ARG(name)\ -ctl_arg_##name +#define CTL_ARG(name) ctl_arg_##name /* * Declaration of a new read-only leaf. If used the corresponding read function * must be declared by CTL_READ_HANDLER macro. */ -#define CTL_LEAF_RO(name, ...)\ -{CTL_STR(name), CTL_NODE_LEAF, \ - {CTL_READ_HANDLER(name, __VA_ARGS__), NULL, NULL}, NULL, NULL} +#define CTL_LEAF_RO(name, ...) \ + { \ + CTL_STR(name), CTL_NODE_LEAF, \ + {CTL_READ_HANDLER(name, __VA_ARGS__), NULL, NULL}, NULL, NULL \ + } /* * Declaration of a new write-only leaf. If used the corresponding write * function must be declared by CTL_WRITE_HANDLER macro. */ -#define CTL_LEAF_WO(name, ...)\ -{CTL_STR(name), CTL_NODE_LEAF, \ - {NULL, CTL_WRITE_HANDLER(name, __VA_ARGS__), NULL},\ - &CTL_ARG(name), NULL} +#define CTL_LEAF_WO(name, ...) \ + { \ + CTL_STR(name), CTL_NODE_LEAF, \ + {NULL, CTL_WRITE_HANDLER(name, __VA_ARGS__), NULL}, \ + &CTL_ARG(name), NULL \ + } /* * Declaration of a new runnable leaf. If used the corresponding run * function must be declared by CTL_RUNNABLE_HANDLER macro. */ -#define CTL_LEAF_RUNNABLE(name, ...)\ -{CTL_STR(name), CTL_NODE_LEAF, \ - {NULL, NULL, CTL_RUNNABLE_HANDLER(name, __VA_ARGS__)},\ - NULL, NULL} +#define CTL_LEAF_RUNNABLE(name, ...) \ + { \ + CTL_STR(name), CTL_NODE_LEAF, \ + {NULL, NULL, CTL_RUNNABLE_HANDLER(name, __VA_ARGS__)}, NULL, NULL \ + } /* * Declaration of a new read-write leaf. If used both read and write function * must be declared by CTL_READ_HANDLER and CTL_WRITE_HANDLER macros. */ -#define CTL_LEAF_RW(name)\ -{CTL_STR(name), CTL_NODE_LEAF,\ - {CTL_READ_HANDLER(name), CTL_WRITE_HANDLER(name), NULL},\ - &CTL_ARG(name), NULL} - -#define CTL_REGISTER_MODULE(_ctl, name)\ -ctl_register_module_node((_ctl), CTL_STR(name),\ -(struct ctl_node *)CTL_NODE(name)) +#define CTL_LEAF_RW(name) \ + { \ + CTL_STR(name), CTL_NODE_LEAF, \ + {CTL_READ_HANDLER(name, ), CTL_WRITE_HANDLER(name, ), NULL}, \ + &CTL_ARG(name), NULL \ + } + +#define CTL_REGISTER_MODULE(_ctl, name) \ + ctl_register_module_node((_ctl), CTL_STR(name), \ + (struct ctl_node *)CTL_NODE(name, )) #ifdef __cplusplus } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b23742866..4b50a8802 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -192,6 +192,11 @@ add_umf_test( SRCS utils/utils_log.cpp ${UMF_UTILS_SOURCES} LIBS ${UMF_LOGGER_LIBS}) +add_umf_test( + NAME ctl + SRCS ctl/test.cpp ctl/ctl_debug.c ../src/ctl/ctl.c ${BA_SOURCES_FOR_TEST} + LIBS ${UMF_UTILS_FOR_TEST}) + add_umf_test( NAME utils_common SRCS utils/utils.cpp diff --git a/test/ctl/config.txt b/test/ctl/config.txt new file mode 100644 index 000000000..9778842bb --- /dev/null +++ b/test/ctl/config.txt @@ -0,0 +1 @@ +debug.heap.alloc_pattern=1 \ No newline at end of file diff --git a/test/ctl/ctl_debug.c b/test/ctl/ctl_debug.c new file mode 100644 index 000000000..d7315b9b1 --- /dev/null +++ b/test/ctl/ctl_debug.c @@ -0,0 +1,72 @@ +/* + * + * Copyright (C) 2024 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +/* + * ctl_debug.c -- implementation of the debug CTL namespace + */ + +#include "ctl_debug.h" + +static struct ctl *ctl_debug; + +static int alloc_pattern = 0; + +struct ctl *get_debug_ctl(void) { return ctl_debug; } + +/* + * CTL_WRITE_HANDLER(alloc_pattern) -- sets the alloc_pattern field in heap + */ +static int +CTL_WRITE_HANDLER(alloc_pattern, )(void *ctx, enum ctl_query_source source, + void *arg, + struct ctl_index_utlist *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + int arg_in = *(int *)arg; + alloc_pattern = arg_in; + return 0; +} + +/* + * CTL_READ_HANDLER(alloc_pattern) -- returns alloc_pattern heap field + */ +static int CTL_READ_HANDLER(alloc_pattern, )(void *ctx, + enum ctl_query_source source, + void *arg, + struct ctl_index_utlist *indexes) { + /* suppress unused-parameter errors */ + (void)source, (void)indexes, (void)ctx; + + int *arg_out = arg; + *arg_out = alloc_pattern; + return 0; +} + +static const struct ctl_argument CTL_ARG(alloc_pattern) = CTL_ARG_LONG_LONG; + +static const struct ctl_node CTL_NODE(heap, )[] = {CTL_LEAF_RW(alloc_pattern), + + CTL_NODE_END}; + +static const struct ctl_node CTL_NODE(debug, )[] = {CTL_CHILD(heap, ), + + CTL_NODE_END}; + +/* + * debug_ctl_register -- registers ctl nodes for "debug" module + */ +void debug_ctl_register(struct ctl *ctl) { CTL_REGISTER_MODULE(ctl, debug); } + +void initialize_debug_ctl(void) { + ctl_debug = ctl_new(); + debug_ctl_register(ctl_debug); +} + +void deinitialize_debug_ctl(void) { ctl_delete(ctl_debug); } diff --git a/test/ctl/ctl_debug.h b/test/ctl/ctl_debug.h new file mode 100644 index 000000000..3573746f7 --- /dev/null +++ b/test/ctl/ctl_debug.h @@ -0,0 +1,32 @@ +/* + * + * Copyright (C) 2024 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +/* + * ctl_debug.h -- definitions for CTL test + */ + +#ifndef CTL_DEBUG_H +#define CTL_DEBUG_H 1 + +#include "../src/ctl/ctl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void debug_ctl_register(struct ctl *ctl); +struct ctl *get_debug_ctl(void); +void initialize_debug_ctl(void); +void deinitialize_debug_ctl(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/ctl/test.cpp b/test/ctl/test.cpp new file mode 100644 index 000000000..6cff8edbe --- /dev/null +++ b/test/ctl/test.cpp @@ -0,0 +1,28 @@ +/* + * + * Copyright (C) 2024 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ + +#include "../common/base.hpp" +#include "ctl/ctl_debug.h" + +using namespace umf_test; + +TEST_F(test, ctl_debug) { + initialize_debug_ctl(); + auto ctl_handler = get_debug_ctl(); + ctl_load_config_from_string(ctl_handler, NULL, + "debug.heap.alloc_pattern=1"); + + int value = 0; + ctl_query(ctl_handler, NULL, CTL_QUERY_PROGRAMMATIC, + "debug.heap.alloc_pattern", CTL_QUERY_READ, &value); + ASSERT_EQ(value, 1); + + debug_ctl_register(ctl_handler); + deinitialize_debug_ctl(); +}