]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
utils: Fix uint128_t usage
authorJeremy Kerr <jk@codeconstruct.com.au>
Sun, 25 Sep 2022 12:12:34 +0000 (20:12 +0800)
committerJeremy Kerr <jk@codeconstruct.com.au>
Sun, 25 Sep 2022 12:25:56 +0000 (20:25 +0800)
Currently, nvme-cli will not compile on 32-bit platforms, as uint128_t
is not available.

This change, based on suggestions from Daniel Wagner <dwagner@suse.de>,
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 <jk@codeconstruct.com.au>
nvme-print.c
tests/meson.build
tests/test-uint128.c [new file with mode: 0644]
util/json.c
util/json.h
util/types.c
util/types.h

index cf61e14fd4fe8d39aecf9e6f2ca107d5193b32dd..cfea03428761b165b11ea84ab4b6d2bb67070d94 100644 (file)
@@ -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();
index bc49d05f6ce3b92b914b169f49e1d10e6f1f6487..1ce6463a4c1bbfdbec732ff004f2b2bf869d50db 100644 (file)
@@ -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 (file)
index 0000000..6301a38
--- /dev/null
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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;
+}
index e10463e9186fee3c6b5bd9708c6c38ad5528efb9..cbe8312662cad262470ca7891755b04ab62e32e6 100644 (file)
@@ -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));
index c4abd26427c6305f98bef882828501ef4b8bbe2d..bf2cd38fb604b4141e7cb01dbaebd87ca2ac4d75 100644 (file)
@@ -3,6 +3,7 @@
 #define __JSON__H
 
 #include <json.h>
+#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
index e52c35a8b049c612b998755ece0d5499011389a3..d5de2f2768b899dfc0281755f9986ab4ccef3a5e 100644 (file)
@@ -2,19 +2,23 @@
 
 #include <inttypes.h>
 #include <stdio.h>
+#include <string.h>
 
 #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)
index be98059fdd7c2907c638422e5aba491a011ce5a0..d48dda18b7421b6c797585fc30511086ae05b55a 100644 (file)
@@ -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);