diff options
Diffstat (limited to '')
-rw-r--r-- | util/argconfig.c | 96 | ||||
-rw-r--r-- | util/argconfig.h | 10 | ||||
-rw-r--r-- | util/cleanup.c | 5 | ||||
-rw-r--r-- | util/cleanup.h | 20 | ||||
-rw-r--r-- | util/json.h | 10 | ||||
-rw-r--r-- | util/mem.c | 109 | ||||
-rw-r--r-- | util/mem.h | 20 | ||||
-rw-r--r-- | util/meson.build | 2 | ||||
-rw-r--r-- | util/types.c | 7 |
9 files changed, 229 insertions, 50 deletions
diff --git a/util/argconfig.c b/util/argconfig.c index effeea2..5ec3d6f 100644 --- a/util/argconfig.c +++ b/util/argconfig.c @@ -41,6 +41,7 @@ #include <stdarg.h> #include <string.h> #include <stdbool.h> +#include <locale.h> static const char *append_usage_str = ""; @@ -163,6 +164,8 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, struct char *endptr; int ret = 0; + errno = 0; /* To distinguish success/failure after strtol/stroul call */ + switch (s->config_type) { case CFG_STRING: *((char **)value) = optarg; @@ -177,15 +180,6 @@ static int argconfig_parse_type(struct argconfig_commandline_options *s, struct if (errno || optarg == endptr) ret = argconfig_error("integer", option[index].name, optarg); break; - case CFG_BOOL: { - int tmp = strtol(optarg, &endptr, 0); - - if (errno || tmp < 0 || tmp > 1 || optarg == endptr) - ret = argconfig_error("0 or 1", option[index].name, optarg); - else - *((int *)value) = tmp; - break; - } case CFG_BYTE: ret = argconfig_parse_byte(option[index].name, optarg, (uint8_t *)value); break; @@ -313,23 +307,11 @@ static int argconfig_parse_val(struct argconfig_commandline_options *s, struct o return argconfig_parse_type(s, option, index); } -bool argconfig_output_format_json(bool set) -{ - static bool output_format_json; - - if (set) - output_format_json = true; - - return output_format_json; -} - -static bool argconfig_check_output_format_json(struct argconfig_commandline_options *s) +static bool argconfig_check_human_readable(struct argconfig_commandline_options *s) { for (; s && s->option; s++) { - if (strcmp(s->option, "output-format") || s->config_type != CFG_STRING) - continue; - if (!strcmp(*(char **)s->default_value, "json")) - return true; + if (!strcmp(s->option, "human-readable") && s->config_type == CFG_FLAG) + return s->seen; } return false; @@ -375,14 +357,10 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, } long_opts[option_index].name = "help"; - long_opts[option_index++].val = 'h'; - - long_opts[option_index].name = "json"; - long_opts[option_index].val = 'j'; + long_opts[option_index].val = 'h'; short_opts[short_index++] = '?'; - short_opts[short_index++] = 'h'; - short_opts[short_index] = 'j'; + short_opts[short_index] = 'h'; optind = 0; while ((c = getopt_long_only(argc, argv, short_opts, long_opts, &option_index)) != -1) { @@ -392,8 +370,6 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, ret = -EINVAL; break; } - if (c == 'j') - argconfig_output_format_json(true); for (option_index = 0; option_index < options_count; option_index++) { if (c == options[option_index].short_option) break; @@ -416,8 +392,8 @@ int argconfig_parse(int argc, char *argv[], const char *program_desc, break; } - if (argconfig_check_output_format_json(options)) - argconfig_output_format_json(true); + if (!argconfig_check_human_readable(options)) + setlocale(LC_ALL, "C"); out: free(short_opts); @@ -549,6 +525,58 @@ int argconfig_parse_comma_sep_array_long(char *string, unsigned long long *val, } } +#define DEFINE_ARGCONFIG_PARSE_COMMA_SEP_ARRAY_UINT_FUNC(size) \ +int argconfig_parse_comma_sep_array_u##size(char *string, \ + __u##size *val, \ + unsigned int max_length) \ +{ \ + int ret = 0; \ + uintmax_t v; \ + char *tmp; \ + char *p; \ + \ + if (!string || !strlen(string)) \ + return 0; \ + \ + tmp = strtok(string, ","); \ + if (!tmp) \ + return 0; \ + \ + v = strtoumax(tmp, &p, 0); \ + if (*p != 0) \ + return -1; \ + if (v > UINT##size##_MAX) { \ + fprintf(stderr, "%s out of range\n", tmp); \ + return -1; \ + } \ + val[ret] = v; \ + \ + ret++; \ + while (1) { \ + tmp = strtok(NULL, ","); \ + \ + if (tmp == NULL) \ + return ret; \ + \ + if (ret >= max_length) \ + return -1; \ + \ + v = strtoumax(tmp, &p, 0); \ + if (*p != 0) \ + return -1; \ + if (v > UINT##size##_MAX) { \ + fprintf(stderr, "%s out of range\n", tmp); \ + return -1; \ + } \ + val[ret] = v; \ + ret++; \ + } \ +} + +DEFINE_ARGCONFIG_PARSE_COMMA_SEP_ARRAY_UINT_FUNC(16); +DEFINE_ARGCONFIG_PARSE_COMMA_SEP_ARRAY_UINT_FUNC(32); +DEFINE_ARGCONFIG_PARSE_COMMA_SEP_ARRAY_UINT_FUNC(64); + bool argconfig_parse_seen(struct argconfig_commandline_options *s, const char *option) { diff --git a/util/argconfig.h b/util/argconfig.h index eaf8375..2a04a32 100644 --- a/util/argconfig.h +++ b/util/argconfig.h @@ -41,6 +41,8 @@ #include <stdbool.h> #include <stdint.h> +#include <linux/types.h> + enum argconfig_types { CFG_FLAG, CFG_STRING, @@ -49,7 +51,6 @@ enum argconfig_types { CFG_LONG, CFG_LONG_SUFFIX, CFG_DOUBLE, - CFG_BOOL, CFG_BYTE, CFG_SHORT, CFG_POSITIVE, @@ -174,10 +175,15 @@ int argconfig_parse_comma_sep_array_short(char *string, unsigned short *ret, unsigned int max_length); int argconfig_parse_comma_sep_array_long(char *string, unsigned long long *ret, unsigned int max_length); +int argconfig_parse_comma_sep_array_u16(char *string, __u16 *val, + unsigned int max_length); +int argconfig_parse_comma_sep_array_u32(char *string, __u32 *val, + unsigned int max_length); +int argconfig_parse_comma_sep_array_u64(char *string, __u64 *val, + unsigned int max_length); int argconfig_parse_byte(const char *opt, const char *str, unsigned char *val); void print_word_wrapped(const char *s, int indent, int start, FILE *stream); bool argconfig_parse_seen(struct argconfig_commandline_options *options, const char *option); -bool argconfig_output_format_json(bool set); #endif diff --git a/util/cleanup.c b/util/cleanup.c deleted file mode 100644 index d6ac7c6..0000000 --- a/util/cleanup.c +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include <stdlib.h> -#include "cleanup.h" - -DEFINE_CLEANUP_FUNC(cleanup_charp, char *, free); diff --git a/util/cleanup.h b/util/cleanup.h index 575a25d..ee9b120 100644 --- a/util/cleanup.h +++ b/util/cleanup.h @@ -2,6 +2,11 @@ #ifndef __CLEANUP_H #define __CLEANUP_H +#include <unistd.h> +#include <stdlib.h> + +#include "util/mem.h" + #define __cleanup__(fn) __attribute__((cleanup(fn))) #define DECLARE_CLEANUP_FUNC(name, type) \ @@ -14,6 +19,19 @@ DECLARE_CLEANUP_FUNC(name, type) \ free_fn(*__p); \ } -DECLARE_CLEANUP_FUNC(cleanup_charp, char *); +static inline void freep(void *p) +{ + free(*(void**) p); +} +#define _cleanup_free_ __cleanup__(freep) + +#define _cleanup_huge_ __cleanup__(nvme_free_huge) + +static inline void close_file(int *f) +{ + if (*f > STDERR_FILENO) + close(*f); +} +#define _cleanup_file_ __cleanup__(close_file) #endif diff --git a/util/json.h b/util/json.h index c362408..54e33e3 100644 --- a/util/json.h +++ b/util/json.h @@ -28,16 +28,18 @@ json_object_object_add(o, k, util_json_object_new_double(v)) #define json_object_add_value_float(o, k, v) \ json_object_object_add(o, k, json_object_new_double(v)) -#define json_object_add_value_string(o, k, v) \ - json_object_object_add(o, k, json_object_new_string(v)) +static inline int json_object_add_value_string(struct json_object *o, const char *k, const char *v) { + return json_object_object_add(o, k, v ? json_object_new_string(v) : NULL); +} #define json_object_add_value_array(o, k, v) \ json_object_object_add(o, k, v) #define json_object_add_value_object(o, k, v) \ json_object_object_add(o, k, v) #define json_array_add_value_object(o, k) \ json_object_array_add(o, k) -#define json_array_add_value_string(o, v) \ - json_object_array_add(o, json_object_new_string(v)) +static inline int json_array_add_value_string(struct json_object *o, const char *v) { + return json_object_array_add(o, v ? json_object_new_string(v) : NULL); +} #define json_print_object(o, u) \ printf("%s", json_object_to_json_string_ext(o, \ JSON_C_TO_STRING_PRETTY | \ diff --git a/util/mem.c b/util/mem.c new file mode 100644 index 0000000..d2be46e --- /dev/null +++ b/util/mem.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include <stdlib.h> +#include <unistd.h> +#include <malloc.h> +#include <string.h> +#include <sys/mman.h> + +#include "mem.h" + +#include "common.h" + +#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) +#define HUGE_MIN 0x80000 + +void *nvme_alloc(size_t len) +{ + void *p; + + len = ROUND_UP(len, 0x1000); + if (posix_memalign((void *)&p, getpagesize(), len)) + return NULL; + + memset(p, 0, len); + return p; +} + +void *nvme_realloc(void *p, size_t len) +{ + size_t old_len = malloc_usable_size(p); + + void *result = nvme_alloc(len); + + if (p) { + memcpy(result, p, min(old_len, len)); + free(p); + } + + return result; +} + +void *nvme_alloc_huge(size_t len, struct nvme_mem_huge *mh) +{ + memset(mh, 0, sizeof(*mh)); + + len = ROUND_UP(len, 0x1000); + + /* + * For smaller allocation we just use posix_memalign and hope the kernel + * is able to convert to a contiguous memory region. + */ + if (len < HUGE_MIN) { + mh->p = nvme_alloc(len); + if (!mh->p) + return NULL; + mh->posix_memalign = true; + mh->len = len; + return mh->p; + } + + /* + * Larger allocation will almost certainly fail with the small + * allocation approach. Instead try pre-allocating memory from the + * HugeTLB pool. + * + * https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt + */ + mh->p = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0); + if (mh->p != MAP_FAILED) { + mh->len = len; + return mh->p; + } + + /* + * And if mmap fails because the pool is empty, try to use + * posix_memalign/madvise as fallback with a 2MB aligmnent in order to + * fullfil the request. This gives the kernel a chance to try to claim + * some huge pages. This might still fail though. + */ + len = ROUND_UP(len, 0x200000); + if (posix_memalign(&mh->p, 0x200000, len)) + return NULL; + mh->posix_memalign = true; + mh->len = len; + + memset(mh->p, 0, mh->len); + + if (madvise(mh->p, mh->len, MADV_HUGEPAGE) < 0) { + nvme_free_huge(mh); + return NULL; + } + + return mh->p; +} + +void nvme_free_huge(struct nvme_mem_huge *mh) + +{ + if (!mh || mh->len == 0) + return; + + if (mh->posix_memalign) + free(mh->p); + else + munmap(mh->p, mh->len); + + mh->len = 0; + mh->p = NULL; +} diff --git a/util/mem.h b/util/mem.h new file mode 100644 index 0000000..d13eb3a --- /dev/null +++ b/util/mem.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef MEM_H_ +#define MEM_H_ + +#include <stddef.h> +#include <stdbool.h> + +void *nvme_alloc(size_t len); +void *nvme_realloc(void *p, size_t len); + +struct nvme_mem_huge { + size_t len; + bool posix_memalign; /* p has been allocated using posix_memalign */ + void *p; +}; + +void *nvme_alloc_huge(size_t len, struct nvme_mem_huge *mh); +void nvme_free_huge(struct nvme_mem_huge *mh); + +#endif /* MEM_H_ */ diff --git a/util/meson.build b/util/meson.build index f149d03..dfc683b 100644 --- a/util/meson.build +++ b/util/meson.build @@ -3,8 +3,8 @@ sources += [ 'util/argconfig.c', 'util/base64.c', - 'util/cleanup.c', 'util/crc32.c', + 'util/mem.c', 'util/suffix.c', 'util/types.c', ] diff --git a/util/types.c b/util/types.c index 044391d..376c734 100644 --- a/util/types.c +++ b/util/types.c @@ -67,20 +67,21 @@ static char *__uint128_t_to_string(nvme_uint128_t val, bool l10n) int idx = 60; __u64 div, rem; char *sep = NULL; - int i, len = 0; + int i, len = 0, cl = 0; if (l10n) { sep = localeconv()->thousands_sep; len = strlen(sep); + cl = 1; } /* terminate at the end, and build up from the ones */ str[--idx] = '\0'; do { - if (len && !((sizeof(str) - idx) % (3 + len))) { + if (len && !((sizeof(str) - idx) % (3 + cl))) { for (i = 0; i < len; i++) - str[--idx] = sep[i]; + str[--idx] = sep[len - i - 1]; } rem = val.words[0]; |