summaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/argconfig.c96
-rw-r--r--util/argconfig.h10
-rw-r--r--util/cleanup.c5
-rw-r--r--util/cleanup.h20
-rw-r--r--util/json.h10
-rw-r--r--util/mem.c109
-rw-r--r--util/mem.h20
-rw-r--r--util/meson.build2
-rw-r--r--util/types.c7
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];