From: Tokunori Ikegami Date: Wed, 28 Dec 2022 06:18:25 +0000 (+0900) Subject: nvme-print: Display smart log data units read and written SI value X-Git-Tag: v2.3~17^2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=2a8bc9483316d2335bcb6f1d0185bc4ac1214479;p=users%2Fsagi%2Fnvme-cli.git nvme-print: Display smart log data units read and written SI value This resolves for the issue #1745. Also change smart log uint128_t values as group the output with thousands' grouping characters and add test-iunt128-si unit tests. Signed-off-by: Tokunori Ikegami --- diff --git a/nvme-print.c b/nvme-print.c index fc15d8f6..f4477a46 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -6255,10 +6255,14 @@ void nvme_show_smart_log(struct nvme_smart_log *smart, unsigned int nsid, smart->percent_used); printf("endurance group critical warning summary: %#x\n", smart->endu_grp_crit_warn_sumry); - printf("data_units_read : %s\n", - uint128_t_to_string(le128_to_cpu(smart->data_units_read))); - printf("data_units_written : %s\n", - uint128_t_to_string(le128_to_cpu(smart->data_units_written))); + printf("Data Units Read : %s (%s)\n", + uint128_t_to_string(le128_to_cpu(smart->data_units_read)), + uint128_t_to_si_string(le128_to_cpu(smart->data_units_read), + 1000 * 512)); + printf("Data Units Written : %s (%s)\n", + uint128_t_to_string(le128_to_cpu(smart->data_units_written)), + uint128_t_to_si_string(le128_to_cpu(smart->data_units_written), + 1000 * 512)); printf("host_read_commands : %s\n", uint128_t_to_string(le128_to_cpu(smart->host_reads))); printf("host_write_commands : %s\n", diff --git a/unit/meson.build b/unit/meson.build index f9d2e108..d4ff925c 100644 --- a/unit/meson.build +++ b/unit/meson.build @@ -2,7 +2,7 @@ test_uint128 = executable( 'test-uint128', - ['test-uint128.c', '../util/types.c'], + ['test-uint128.c', '../util/types.c', '../util/suffix.c'], include_directories: [incdir, '..'], dependencies: [libnvme_dep], ) @@ -17,3 +17,12 @@ test_suffix_si_parse = executable( ) test('suffix_si_parse', test_suffix_si_parse) + +test_uint128_si = executable( + 'test-uint128-si', + ['test-uint128-si.c', '../util/types.c', '../util/suffix.c'], + include_directories: [incdir, '..'], + dependencies: [libnvme_dep], +) + +test('uint128-si', test_uint128_si) diff --git a/unit/test-uint128-si.c b/unit/test-uint128-si.c new file mode 100644 index 00000000..cc13450c --- /dev/null +++ b/unit/test-uint128-si.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include "../util/types.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +/* create a uint128_t from four uint32_ts. w0 is the most significant value, + * w2 the least */ +#define U128(w0, w1, w2, w3) { .words = { w0, w1, w2, w3 } } + +static int test_rc; + +static void check_str(nvme_uint128_t val, __u32 bytes_per_unit, const char *exp, + const char *res) +{ + if (!strcmp(res, exp)) + return; + + printf("ERROR: printing {%08x.%08x.%08x.%08x} (bytes per unit %u), got '%s', expected '%s'\n", + val.words[3], val.words[2], val.words[1], val.words[0], + bytes_per_unit, res, exp); + + test_rc = 1; +} + +struct tostr_test { + nvme_uint128_t val; + __u32 bytes_per_unit; + const char *exp; +}; + +static struct tostr_test tostr_tests[] = { + { U128(0, 0, 0, 0), 1, "0.00 B" }, + { U128(0, 0, 0, 1), 1, "1.00 B" }, + { U128(0, 0, 0, 10), 1, "10.00 B" }, + { U128(4, 3, 2, 1), 1, "316.91 RB" }, + { U128(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff), 1, + "340282366.92 QB" }, + { U128(0, 0, 0, 0xae0dc2), 1000 * 512, "5.84 TB" }, + { U128(0, 0, 0, 0xf9c546), 1000 * 512, "8.38 TB" }, + { U128(0, 0, 0, 0x4c2aa594), 1000 * 512, "654.27 TB" }, + { U128(0, 0, 0, 0x5b013de8), 1000 * 512, "781.73 TB" }, +}; + +void tostr_test(struct tostr_test *test) +{ + char *str; + str = uint128_t_to_si_string(test->val, test->bytes_per_unit); + check_str(test->val, test->bytes_per_unit, test->exp, str); +} + +int main(void) +{ + unsigned int i; + + test_rc = 0; + + for (i = 0; i < ARRAY_SIZE(tostr_tests); i++) + tostr_test(&tostr_tests[i]); + + return test_rc ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/util/suffix.c b/util/suffix.c index 3b80fcea..b84f918e 100644 --- a/util/suffix.c +++ b/util/suffix.c @@ -38,35 +38,31 @@ #include static struct si_suffix { - double magnitude; + long double magnitude; const char *suffix; } si_suffixes[] = { + {1e30, "Q"}, + {1e27, "R"}, + {1e24, "Y"}, + {1e21, "Z"}, + {1e18, "E"}, {1e15, "P"}, {1e12, "T"}, {1e9, "G"}, {1e6, "M"}, {1e3, "k"}, {1e0, ""}, - {1e-3, "m"}, - {1e-6, "u"}, - {1e-9, "n"}, - {1e-12, "p"}, - {1e-15, "f"}, {0} }; const char *suffix_si_get(double *value) { - struct si_suffix *s; + long double value_ld = *value; + const char *suffix = suffix_si_get_ld(&value_ld); - for (s = si_suffixes; s->magnitude != 0; s++) { - if (*value >= s->magnitude) { - *value /= s->magnitude; - return s->suffix; - } - } + *value = value_ld; - return ""; + return suffix; } uint64_t suffix_si_parse(const char *value, bool *suffixed) @@ -94,6 +90,20 @@ uint64_t suffix_si_parse(const char *value, bool *suffixed) return (uint64_t)ret; } +const char *suffix_si_get_ld(long double *value) +{ + struct si_suffix *s; + + for (s = si_suffixes; s->magnitude != 0; s++) { + if (*value >= s->magnitude) { + *value /= s->magnitude; + return s->suffix; + } + } + + return ""; +} + static struct binary_suffix { int shift; const char *suffix; diff --git a/util/suffix.h b/util/suffix.h index 7cf2237c..b367ce41 100644 --- a/util/suffix.h +++ b/util/suffix.h @@ -37,6 +37,7 @@ const char *suffix_si_get(double *value); uint64_t suffix_si_parse(const char *value, bool *suffixed); +const char *suffix_si_get_ld(long double *value); const char *suffix_binary_get(long long *value); const char *suffix_dbinary_get(double *value); uint64_t suffix_binary_parse(const char *value); diff --git a/util/types.c b/util/types.c index 06bb8d4e..18ced770 100644 --- a/util/types.c +++ b/util/types.c @@ -3,10 +3,12 @@ #include #include #include +#include #include #include "types.h" +#include "util/suffix.h" nvme_uint128_t le128_to_cpu(__u8 *data) { @@ -46,14 +48,22 @@ uint64_t int48_to_long(__u8 *data) char *uint128_t_to_string(nvme_uint128_t val) { - static char str[40]; - int idx = 40; + static char str[60]; + int idx = 60; __u64 div, rem; + char *sep = localeconv()->thousands_sep; + int len = sep ? strlen(sep) : 0; + int i; /* terminate at the end, and build up from the ones */ str[--idx] = '\0'; do { + if (len && !((sizeof(str) - idx) % (3 + len))) { + for (i = 0; i < len; i++) + str[--idx] = sep[i]; + } + rem = val.words[0]; div = rem / 10; @@ -78,6 +88,35 @@ char *uint128_t_to_string(nvme_uint128_t val) return str + idx; } +static long double uint128_t_to_double(nvme_uint128_t data) +{ + int i; + long double result = 0; + + for (i = 0; i < sizeof(data.words) / sizeof(*data.words); i++) { + result *= 4294967296; + result += data.words[i]; + } + + return result; +} + +char *uint128_t_to_si_string(nvme_uint128_t val, __u32 bytes_per_unit) +{ + static char str[40]; + long double bytes = uint128_t_to_double(val) * bytes_per_unit; + const char *suffix = suffix_si_get_ld(&bytes); + int n = snprintf(str, sizeof(str), "%.2Lf %sB", bytes, suffix); + + if (n <= 0) + return ""; + + if (n >= sizeof(str)) + str[sizeof(str) - 1] = '\0'; + + return str; +} + const char *util_uuid_to_string(unsigned char uuid[NVME_UUID_LEN]) { static char uuid_str[NVME_UUID_LEN_STRING]; diff --git a/util/types.h b/util/types.h index 6d451086..2e887175 100644 --- a/util/types.h +++ b/util/types.h @@ -29,6 +29,7 @@ long double int128_to_double(__u8 *data); uint64_t int48_to_long(__u8 *data); char *uint128_t_to_string(nvme_uint128_t val); +char *uint128_t_to_si_string(nvme_uint128_t val, __u32 bytes_per_unit); const char *util_uuid_to_string(unsigned char uuid[NVME_UUID_LEN]); const char *util_fw_to_string(char *c);