From: Jeremy Kerr Date: Sun, 25 Sep 2022 12:12:34 +0000 (+0800) Subject: utils: Fix uint128_t usage X-Git-Tag: v2.2~26^2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=b509ccb39a8ca242a0c42400408d1a8d86911ba1;p=users%2Fsagi%2Fnvme-cli.git utils: Fix uint128_t usage Currently, nvme-cli will not compile on 32-bit platforms, as uint128_t is not available. This change, based on suggestions from Daniel Wagner , adds our own nvme_uint128_t type, which needs a custom to_string() function. In order to make the latter straightforward, we use four 32-bit words for the internal representation of the 128-bit type. We then use 64-bit arithmetic to avoid having to handle the carry-bit explicitly. Also, add a small test case for the printing. Signed-off-by: Jeremy Kerr --- diff --git a/nvme-print.c b/nvme-print.c index cf61e14f..cfea0342 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -129,7 +129,7 @@ static void json_nvme_id_ns(struct nvme_id_ns *ns, bool cap_only) struct json_object *lbafs; int i; - __uint128_t nvmcap = le128_to_cpu(ns->nvmcap); + nvme_uint128_t nvmcap = le128_to_cpu(ns->nvmcap); root = json_create_object(); @@ -216,10 +216,10 @@ static void json_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, struct json_object *root; struct json_object *psds; - __uint128_t tnvmcap = le128_to_cpu(ctrl->tnvmcap); - __uint128_t unvmcap = le128_to_cpu(ctrl->unvmcap); - __uint128_t megcap = le128_to_cpu(ctrl->megcap); - __uint128_t maxdna = le128_to_cpu(ctrl->maxdna); + nvme_uint128_t tnvmcap = le128_to_cpu(ctrl->tnvmcap); + nvme_uint128_t unvmcap = le128_to_cpu(ctrl->unvmcap); + nvme_uint128_t megcap = le128_to_cpu(ctrl->megcap); + nvme_uint128_t maxdna = le128_to_cpu(ctrl->maxdna); char sn[sizeof(ctrl->sn) + 1], mn[sizeof(ctrl->mn) + 1], fr[sizeof(ctrl->fr) + 1], subnqn[sizeof(ctrl->subnqn) + 1]; @@ -560,21 +560,21 @@ static void json_endurance_log(struct nvme_endurance_group_log *endurance_group, { struct json_object *root; - __uint128_t endurance_estimate = + nvme_uint128_t endurance_estimate = le128_to_cpu(endurance_group->endurance_estimate); - __uint128_t data_units_read = + nvme_uint128_t data_units_read = le128_to_cpu(endurance_group->data_units_read); - __uint128_t data_units_written = + nvme_uint128_t data_units_written = le128_to_cpu(endurance_group->data_units_written); - __uint128_t media_units_written = + nvme_uint128_t media_units_written = le128_to_cpu(endurance_group->media_units_written); - __uint128_t host_read_cmds = + nvme_uint128_t host_read_cmds = le128_to_cpu(endurance_group->host_read_cmds); - __uint128_t host_write_cmds = + nvme_uint128_t host_write_cmds = le128_to_cpu(endurance_group->host_write_cmds); - __uint128_t media_data_integrity_err = + nvme_uint128_t media_data_integrity_err = le128_to_cpu(endurance_group->media_data_integrity_err); - __uint128_t num_err_info_log_entries = + nvme_uint128_t num_err_info_log_entries = le128_to_cpu(endurance_group->num_err_info_log_entries); root = json_create_object(); @@ -592,7 +592,7 @@ static void json_endurance_log(struct nvme_endurance_group_log *endurance_group, json_object_add_value_uint128(root, "data_units_read", data_units_read); json_object_add_value_uint128(root, "data_units_written", data_units_written); - json_object_add_value_double(root, "media_units_written", + json_object_add_value_uint128(root, "media_units_written", media_units_written); json_object_add_value_uint128(root, "host_read_cmds", host_read_cmds); json_object_add_value_uint128(root, "host_write_cmds", host_write_cmds); @@ -616,16 +616,16 @@ static void json_smart_log(struct nvme_smart_log *smart, unsigned int nsid, unsigned int temperature = ((smart->temperature[1] << 8) | smart->temperature[0]); - __uint128_t data_units_read = le128_to_cpu(smart->data_units_read); - __uint128_t data_units_written = le128_to_cpu(smart->data_units_written); - __uint128_t host_read_commands = le128_to_cpu(smart->host_reads); - __uint128_t host_write_commands = le128_to_cpu(smart->host_writes); - __uint128_t controller_busy_time = le128_to_cpu(smart->ctrl_busy_time); - __uint128_t power_cycles = le128_to_cpu(smart->power_cycles); - __uint128_t power_on_hours = le128_to_cpu(smart->power_on_hours); - __uint128_t unsafe_shutdowns = le128_to_cpu(smart->unsafe_shutdowns); - __uint128_t media_errors = le128_to_cpu(smart->media_errors); - __uint128_t num_err_log_entries = le128_to_cpu(smart->num_err_log_entries); + nvme_uint128_t data_units_read = le128_to_cpu(smart->data_units_read); + nvme_uint128_t data_units_written = le128_to_cpu(smart->data_units_written); + nvme_uint128_t host_read_commands = le128_to_cpu(smart->host_reads); + nvme_uint128_t host_write_commands = le128_to_cpu(smart->host_writes); + nvme_uint128_t controller_busy_time = le128_to_cpu(smart->ctrl_busy_time); + nvme_uint128_t power_cycles = le128_to_cpu(smart->power_cycles); + nvme_uint128_t power_on_hours = le128_to_cpu(smart->power_on_hours); + nvme_uint128_t unsafe_shutdowns = le128_to_cpu(smart->unsafe_shutdowns); + nvme_uint128_t media_errors = le128_to_cpu(smart->media_errors); + nvme_uint128_t num_err_log_entries = le128_to_cpu(smart->num_err_log_entries); root = json_create_object(); @@ -1233,16 +1233,16 @@ static void json_persistent_event_log(void *pevent_log_info, __u32 size) unsigned int temperature = ((smart_event->temperature[1] << 8) | smart_event->temperature[0]); - __uint128_t data_units_read = le128_to_cpu(smart_event->data_units_read); - __uint128_t data_units_written = le128_to_cpu(smart_event->data_units_written); - __uint128_t host_read_commands = le128_to_cpu(smart_event->host_reads); - __uint128_t host_write_commands = le128_to_cpu(smart_event->host_writes); - __uint128_t controller_busy_time = le128_to_cpu(smart_event->ctrl_busy_time); - __uint128_t power_cycles = le128_to_cpu(smart_event->power_cycles); - __uint128_t power_on_hours = le128_to_cpu(smart_event->power_on_hours); - __uint128_t unsafe_shutdowns = le128_to_cpu(smart_event->unsafe_shutdowns); - __uint128_t media_errors = le128_to_cpu(smart_event->media_errors); - __uint128_t num_err_log_entries = le128_to_cpu(smart_event->num_err_log_entries); + nvme_uint128_t data_units_read = le128_to_cpu(smart_event->data_units_read); + nvme_uint128_t data_units_written = le128_to_cpu(smart_event->data_units_written); + nvme_uint128_t host_read_commands = le128_to_cpu(smart_event->host_reads); + nvme_uint128_t host_write_commands = le128_to_cpu(smart_event->host_writes); + nvme_uint128_t controller_busy_time = le128_to_cpu(smart_event->ctrl_busy_time); + nvme_uint128_t power_cycles = le128_to_cpu(smart_event->power_cycles); + nvme_uint128_t power_on_hours = le128_to_cpu(smart_event->power_on_hours); + nvme_uint128_t unsafe_shutdowns = le128_to_cpu(smart_event->unsafe_shutdowns); + nvme_uint128_t media_errors = le128_to_cpu(smart_event->media_errors); + nvme_uint128_t num_err_log_entries = le128_to_cpu(smart_event->num_err_log_entries); json_object_add_value_int(valid_attrs, "critical_warning", smart_event->critical_warning); @@ -5679,7 +5679,7 @@ static void json_id_domain_list(struct nvme_id_domain_list *id_dom) struct json_object *entries; struct json_object *entry; int i; - __uint128_t dom_cap, unalloc_dom_cap, max_egrp_dom_cap; + nvme_uint128_t dom_cap, unalloc_dom_cap, max_egrp_dom_cap; root = json_create_object(); entries = json_create_array(); diff --git a/tests/meson.build b/tests/meson.build index bc49d05f..1ce6463a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -49,6 +49,14 @@ if runtests.found() endforeach endif +test_uint128 = executable( + 'test-uint128', + ['test-uint128.c', '../util/types.c'], + dependencies: [libuuid_dep], +) + +test('uint128', test_uint128) + python_module = import('python') python = python_module.find_installation('python3') diff --git a/tests/test-uint128.c b/tests/test-uint128.c new file mode 100644 index 00000000..6301a383 --- /dev/null +++ b/tests/test-uint128.c @@ -0,0 +1,62 @@ +// 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, const char *exp, const char *res) +{ + if (!strcmp(res, exp)) + return; + + printf("ERROR: printing {%08x.%08x.%08x.%08x}, got '%s', expected '%s'\n", + val.words[3], val.words[2], val.words[1], val.words[0], + res, exp); + + test_rc = 1; +} + +struct tostr_test { + nvme_uint128_t val; + const char *exp; +}; + +static struct tostr_test tostr_tests[] = { + { U128(0, 0, 0, 0), "0" }, + { U128(0, 0, 0, 1), "1" }, + { U128(0, 0, 0, 10), "10" }, + { U128(4, 3, 2, 1), "316912650112397582603894390785" }, + { + U128(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff), + "340282366920938463463374607431768211455" + }, +}; + +void tostr_test(struct tostr_test *test) +{ + char *str; + str = uint128_t_to_string(test->val); + check_str(test->val, 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/json.c b/util/json.c index e10463e9..cbe83126 100644 --- a/util/json.c +++ b/util/json.c @@ -34,7 +34,7 @@ struct json_object *util_json_object_new_uint64(uint64_t i) } -struct json_object *util_json_object_new_uint128(__uint128_t val) +struct json_object *util_json_object_new_uint128(nvme_uint128_t val) { struct json_object *obj; obj = json_object_new_string(uint128_t_to_string(val)); diff --git a/util/json.h b/util/json.h index c4abd264..bf2cd38f 100644 --- a/util/json.h +++ b/util/json.h @@ -3,6 +3,7 @@ #define __JSON__H #include +#include "util/types.h" /* Wrappers around json-c's API */ @@ -44,6 +45,6 @@ struct json_object *util_json_object_new_double(long double d); struct json_object *util_json_object_new_uint64(uint64_t i); -struct json_object *util_json_object_new_uint128(__uint128_t val); +struct json_object *util_json_object_new_uint128(nvme_uint128_t val); #endif diff --git a/util/types.c b/util/types.c index e52c35a8..d5de2f27 100644 --- a/util/types.c +++ b/util/types.c @@ -2,19 +2,23 @@ #include #include +#include #include "types.h" -__uint128_t le128_to_cpu(__u8 *data) +nvme_uint128_t le128_to_cpu(__u8 *data) { - int i; - __uint128_t result = 0; + nvme_uint128_t u; - for (i = 0; i < 16; i++) { - result *= 256; - result += data[15 - i]; - } - return result; + memcpy(u.bytes, data, 16); + +#if HAVE_BIG_ENDIAN + u.words[0] = le32_to_cpu(u.words[3]); + u.words[1] = le32_to_cpu(u.words[2]); + u.words[2] = le32_to_cpu(u.words[1]); + u.words[3] = le32_to_cpu(u.words[0]); +#endif + return u; } long double int128_to_double(__u8 *data) @@ -41,23 +45,38 @@ uint64_t int48_to_long(__u8 *data) return result; } -char str_uint128[40]; -char *uint128_t_to_string(__uint128_t val) +char *uint128_t_to_string(nvme_uint128_t val) { - char str_rev[40]; /* __uint128_t maximum string length is 39 */ - int i, j; + static char str[40]; + int idx = 40; + __u64 div, rem; - for (i = 0; val > 0; i++) { - str_rev[i] = (val % 10) + 48; - val /= 10; - } + /* terminate at the end, and build up from the ones */ + str[--idx] = '\0'; - for (j = 0; i >= 0;) { - str_uint128[j++] = str_rev[--i]; - } - str_uint128[j] = '\0'; + do { + rem = val.words[0]; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[1]; + val.words[0] = div; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[2]; + val.words[1] = div; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[3]; + val.words[2] = div; + + div = rem / 10; + rem = rem - div * 10; + val.words[3] = div; + + str[--idx] = '0' + rem; + } while (val.words[0] || val.words[1] || val.words[2] || val.words[3]); - return str_uint128; + return str + idx; } const char *util_uuid_to_string(uuid_t uuid) diff --git a/util/types.h b/util/types.h index be98059f..d48dda18 100644 --- a/util/types.h +++ b/util/types.h @@ -15,11 +15,19 @@ static inline long kelvin_to_celsius(long t) return t + ABSOLUTE_ZERO_CELSIUS; } -__uint128_t le128_to_cpu(__u8 *data); +/* uint128_t is not always available, define our own. */ +union nvme_uint128 { + __u8 bytes[16]; + __u32 words[4]; /* [0] is most significant word */ +}; + +typedef union nvme_uint128 nvme_uint128_t; + +nvme_uint128_t le128_to_cpu(__u8 *data); long double int128_to_double(__u8 *data); uint64_t int48_to_long(__u8 *data); -char *uint128_t_to_string(__uint128_t val); +char *uint128_t_to_string(nvme_uint128_t val); const char *util_uuid_to_string(uuid_t uuid); const char *util_fw_to_string(char *c);