| File: | .build-ci/../plugins/solidigm/solidigm-latency-tracking.c |
| Warning: | line 162, column 32 The result of left shift is undefined because the right operand is negative |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | /* | |||
| 3 | * Copyright (c) 2022-2024 Solidigm. | |||
| 4 | * | |||
| 5 | * Author: leonardo.da.cunha@solidigm.com | |||
| 6 | */ | |||
| 7 | ||||
| 8 | #include <errno(*__errno_location ()).h> | |||
| 9 | #include <stdio.h> | |||
| 10 | #include <stdlib.h> | |||
| 11 | #include <unistd.h> | |||
| 12 | #include <inttypes.h> | |||
| 13 | ||||
| 14 | #include <libnvme.h> | |||
| 15 | ||||
| 16 | #include "common.h" | |||
| 17 | #include "nvme-cmds.h" | |||
| 18 | #include "nvme-print.h" | |||
| 19 | #include "nvme.h" | |||
| 20 | #include "plugin.h" | |||
| 21 | ||||
| 22 | #include "solidigm-util.h" | |||
| 23 | ||||
| 24 | #define BUCKET_LIST_SIZE_4_0152 152 | |||
| 25 | #define BUCKET_LIST_SIZE_4_11216 1216 | |||
| 26 | ||||
| 27 | #define BASE_RANGE_BITS_4_03 3 | |||
| 28 | #define BASE_RANGE_BITS_4_16 6 | |||
| 29 | ||||
| 30 | struct latency_statistics { | |||
| 31 | __u16 version_major; | |||
| 32 | __u16 version_minor; | |||
| 33 | __u32 data[BUCKET_LIST_SIZE_4_11216]; | |||
| 34 | __u64 average_latency; | |||
| 35 | }; | |||
| 36 | ||||
| 37 | struct config { | |||
| 38 | bool_Bool enable; | |||
| 39 | bool_Bool disable; | |||
| 40 | bool_Bool read; | |||
| 41 | bool_Bool write; | |||
| 42 | unsigned char type; | |||
| 43 | }; | |||
| 44 | ||||
| 45 | struct latency_tracker { | |||
| 46 | struct libnvme_transport_handle *hdl; | |||
| 47 | __u8 uuid_index; | |||
| 48 | struct config cfg; | |||
| 49 | nvme_print_flags_t print_flags; | |||
| 50 | struct latency_statistics stats; | |||
| 51 | struct json_object *bucket_list; | |||
| 52 | __u32 bucket_list_size; | |||
| 53 | __u8 base_range_bits; | |||
| 54 | bool_Bool has_average_latency_field; | |||
| 55 | }; | |||
| 56 | ||||
| 57 | /* COL_WIDTH controls width of columns in NORMAL output. */ | |||
| 58 | #define COL_WIDTH12 12 | |||
| 59 | #define BUCKET_LABEL_MAX_SIZE10 10 | |||
| 60 | ||||
| 61 | #define US_IN_S1000000 1000000 | |||
| 62 | #define US_IN_MS1000 1000 | |||
| 63 | ||||
| 64 | /* | |||
| 65 | * Edge buckets may have range [#s, inf) in some | |||
| 66 | * latency statistics formats. | |||
| 67 | */ | |||
| 68 | static void get_time_unit_label(char *label, __u32 microseconds, | |||
| 69 | bool_Bool bonded) | |||
| 70 | { | |||
| 71 | char *string = "us"; | |||
| 72 | int divisor = 1; | |||
| 73 | ||||
| 74 | if (!bonded) { | |||
| 75 | snprintf(label, BUCKET_LABEL_MAX_SIZE10, "%s", "+INF"); | |||
| 76 | return; | |||
| 77 | } | |||
| 78 | ||||
| 79 | if (microseconds > US_IN_S1000000) { | |||
| 80 | string = "s"; | |||
| 81 | divisor = US_IN_S1000000; | |||
| 82 | } else if (microseconds > US_IN_MS1000) { | |||
| 83 | string = "ms"; | |||
| 84 | divisor = US_IN_MS1000; | |||
| 85 | } | |||
| 86 | ||||
| 87 | snprintf(label, BUCKET_LABEL_MAX_SIZE10, "%4.2f%s", (float) microseconds / divisor, | |||
| 88 | string); | |||
| 89 | } | |||
| 90 | ||||
| 91 | static void latency_tracker_bucket_parse(const struct latency_tracker *lt, int id, | |||
| 92 | __u32 lower_us, __u32 upper_us, bool_Bool upper_bounded) | |||
| 93 | { | |||
| 94 | char buffer[BUCKET_LABEL_MAX_SIZE10] = ""; | |||
| 95 | __u32 bucket_data = le32_to_cpu(lt->stats.data[id]); | |||
| 96 | ||||
| 97 | if (lt->print_flags == NORMAL) { | |||
| 98 | printf("%-*d", COL_WIDTH12, id); | |||
| 99 | ||||
| 100 | get_time_unit_label(buffer, lower_us, true1); | |||
| 101 | printf("%-*s", COL_WIDTH12, buffer); | |||
| 102 | ||||
| 103 | get_time_unit_label(buffer, upper_us, upper_bounded); | |||
| 104 | printf("%-*s", COL_WIDTH12, buffer); | |||
| 105 | ||||
| 106 | printf("%-*d\n", COL_WIDTH12, bucket_data); | |||
| 107 | } | |||
| 108 | ||||
| 109 | if (lt->print_flags == JSON) { | |||
| 110 | /* | |||
| 111 | * Creates a bucket under the "values" json_object. Format is: | |||
| 112 | * "values" : { | |||
| 113 | * "bucket" : { | |||
| 114 | * "id" : #, | |||
| 115 | * "start" : string, | |||
| 116 | * "end" : string, | |||
| 117 | * "value" : 0, | |||
| 118 | * }, | |||
| 119 | */ | |||
| 120 | struct json_object *bucket = json_create_object()json_object_new_object(); | |||
| 121 | ||||
| 122 | json_object_array_add(lt->bucket_list, bucket); | |||
| 123 | json_object_add_value_int(bucket, "id", id)json_object_object_add(bucket, "id", json_object_new_int(id)); | |||
| 124 | ||||
| 125 | get_time_unit_label(buffer, lower_us, true1); | |||
| 126 | json_object_add_value_string(bucket, "start", buffer); | |||
| 127 | ||||
| 128 | get_time_unit_label(buffer, upper_us, upper_bounded); | |||
| 129 | json_object_add_value_string(bucket, "end", buffer); | |||
| 130 | ||||
| 131 | json_object_add_value_int(bucket, "value", bucket_data)json_object_object_add(bucket, "value", json_object_new_int(bucket_data )); | |||
| 132 | } | |||
| 133 | } | |||
| 134 | ||||
| 135 | static void latency_tracker_parse_linear(const struct latency_tracker *lt, | |||
| 136 | __u32 start_offset, __u32 end_offset, | |||
| 137 | __u32 bytes_per, __u32 us_step, | |||
| 138 | bool_Bool nonzero_print) | |||
| 139 | { | |||
| 140 | for (int i = (start_offset / bytes_per) - 1; i < end_offset / bytes_per; i++) { | |||
| 141 | if (nonzero_print && !lt->stats.data[i]) | |||
| 142 | continue; | |||
| 143 | latency_tracker_bucket_parse(lt, i, us_step * i, us_step * (i + 1), true1); | |||
| 144 | } | |||
| 145 | } | |||
| 146 | ||||
| 147 | /* | |||
| 148 | * Calculates bucket time slot. Valid starting on 4.0 revision. | |||
| 149 | */ | |||
| 150 | ||||
| 151 | static int latency_tracker_bucket_pos2us(const struct latency_tracker *lt, int i) | |||
| 152 | { | |||
| 153 | __u32 base_val = 1 << lt->base_range_bits; | |||
| 154 | ||||
| 155 | if (i < (base_val << 1)) | |||
| 156 | return i; | |||
| 157 | ||||
| 158 | int error_bits = (i >> lt->base_range_bits) - 1; | |||
| 159 | int base = 1 << (error_bits + lt->base_range_bits); | |||
| 160 | int k = i % base_val; | |||
| 161 | ||||
| 162 | return base + ((k + 0.5) * (1 << error_bits)); | |||
| ||||
| 163 | } | |||
| 164 | ||||
| 165 | /* | |||
| 166 | * Creates a subroot in the following manner: | |||
| 167 | * { | |||
| 168 | * "latstats" : { | |||
| 169 | * "type" : "write" or "read", | |||
| 170 | * "values" : { | |||
| 171 | */ | |||
| 172 | static void latency_tracker_populate_json_root(const struct latency_tracker *lt, | |||
| 173 | struct json_object *root) | |||
| 174 | { | |||
| 175 | struct json_object *subroot = json_create_object()json_object_new_object(); | |||
| 176 | ||||
| 177 | json_object_add_value_object(root, "latstats", subroot)json_object_object_add(root, "latstats", subroot); | |||
| 178 | json_object_add_value_string(subroot, "type", lt->cfg.write ? "write" : "read"); | |||
| 179 | if (lt->has_average_latency_field) | |||
| 180 | json_object_add_value_uint64(subroot, "average_latency",json_object_object_add(subroot, "average_latency", json_object_new_uint64 (le64_to_cpu(lt->stats.average_latency))) | |||
| 181 | le64_to_cpu(lt->stats.average_latency))json_object_object_add(subroot, "average_latency", json_object_new_uint64 (le64_to_cpu(lt->stats.average_latency))); | |||
| 182 | json_object_add_value_object(subroot, "values", lt->bucket_list)json_object_object_add(subroot, "values", lt->bucket_list); | |||
| 183 | } | |||
| 184 | ||||
| 185 | static void latency_tracker_parse_3_0(const struct latency_tracker *lt) | |||
| 186 | { | |||
| 187 | latency_tracker_parse_linear(lt, 4, 131, 4, 32, false0); | |||
| 188 | latency_tracker_parse_linear(lt, 132, 255, 4, 1024, false0); | |||
| 189 | latency_tracker_parse_linear(lt, 256, 379, 4, 32768, false0); | |||
| 190 | latency_tracker_parse_linear(lt, 380, 383, 4, 32, true1); | |||
| 191 | latency_tracker_parse_linear(lt, 384, 387, 4, 32, true1); | |||
| 192 | latency_tracker_parse_linear(lt, 388, 391, 4, 32, true1); | |||
| 193 | } | |||
| 194 | ||||
| 195 | static void latency_tracker_parse_4_0(const struct latency_tracker *lt) | |||
| 196 | { | |||
| 197 | for (unsigned int i = 0; i < lt->bucket_list_size; i++) { | |||
| 198 | int lower_us = latency_tracker_bucket_pos2us(lt, i); | |||
| 199 | int upper_us = latency_tracker_bucket_pos2us(lt, i + 1); | |||
| 200 | ||||
| 201 | latency_tracker_bucket_parse(lt, i, lower_us, upper_us, | |||
| 202 | i < (lt->bucket_list_size - 1)); | |||
| 203 | } | |||
| 204 | } | |||
| 205 | ||||
| 206 | static void print_dash_separator(void) | |||
| 207 | { | |||
| 208 | printf("--------------------------------------------------\n"); | |||
| 209 | } | |||
| 210 | ||||
| 211 | static void latency_tracker_pre_parse(struct latency_tracker *lt) | |||
| 212 | { | |||
| 213 | if (lt->print_flags == NORMAL) { | |||
| 214 | printf("Solidigm IO %s Command Latency Tracking Statistics type %d\n", | |||
| 215 | lt->cfg.write ? "Write" : "Read", lt->cfg.type); | |||
| 216 | printf("UUID-idx: %d\n", lt->uuid_index); | |||
| 217 | printf("Major Revision: %u\nMinor Revision: %u\n", | |||
| 218 | le16_to_cpu(lt->stats.version_major), le16_to_cpu(lt->stats.version_minor)); | |||
| 219 | if (lt->has_average_latency_field) | |||
| 220 | printf("Average Latency: %" PRIu64"l" "u" "\n", le64_to_cpu(lt->stats.average_latency)); | |||
| 221 | print_dash_separator(); | |||
| 222 | printf("%-12s%-12s%-12s%-20s\n", "Bucket", "Start", "End", "Value"); | |||
| 223 | print_dash_separator(); | |||
| 224 | } | |||
| 225 | if (lt->print_flags == JSON) | |||
| 226 | lt->bucket_list = json_object_new_array(); | |||
| 227 | } | |||
| 228 | ||||
| 229 | static void latency_tracker_post_parse(struct latency_tracker *lt) | |||
| 230 | { | |||
| 231 | if (lt->print_flags == JSON) { | |||
| 232 | struct json_object *root = json_create_object()json_object_new_object(); | |||
| 233 | ||||
| 234 | latency_tracker_populate_json_root(lt, root); | |||
| 235 | json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 << 1) | (1 << 4))); | |||
| 236 | json_free_object(root)json_object_put(root); | |||
| 237 | printf("\n"); | |||
| 238 | } | |||
| 239 | } | |||
| 240 | ||||
| 241 | static void latency_tracker_parse(struct latency_tracker *lt) | |||
| 242 | { | |||
| 243 | __u16 version_major = le16_to_cpu(lt->stats.version_major); | |||
| 244 | __u16 version_minor = le16_to_cpu(lt->stats.version_minor); | |||
| 245 | ||||
| 246 | switch (version_major) { | |||
| 247 | case 3: | |||
| 248 | latency_tracker_pre_parse(lt); | |||
| 249 | latency_tracker_parse_3_0(lt); | |||
| 250 | break; | |||
| 251 | case 4: | |||
| 252 | if (version_minor >= 8) | |||
| 253 | lt->has_average_latency_field = true1; | |||
| 254 | latency_tracker_pre_parse(lt); | |||
| 255 | if (!version_minor) { | |||
| 256 | lt->base_range_bits = BASE_RANGE_BITS_4_03; | |||
| 257 | lt->bucket_list_size = BUCKET_LIST_SIZE_4_0152; | |||
| 258 | } | |||
| 259 | latency_tracker_parse_4_0(lt); | |||
| 260 | break; | |||
| 261 | default: | |||
| 262 | printf("Unsupported revision (%u.%u)\n", | |||
| 263 | version_major, version_minor); | |||
| 264 | break; | |||
| 265 | } | |||
| 266 | ||||
| 267 | latency_tracker_post_parse(lt); | |||
| 268 | } | |||
| 269 | ||||
| 270 | #define LATENCY_TRACKING_FID0xe2 0xe2 | |||
| 271 | #define LATENCY_TRACKING_FID_DATA_LEN32 32 | |||
| 272 | ||||
| 273 | static int latency_tracking_is_enable(struct latency_tracker *lt, __u64 *enabled) | |||
| 274 | { | |||
| 275 | return nvme_get_features(lt->hdl, 0, LATENCY_TRACKING_FID0xe2, 0, 0, | |||
| 276 | lt->uuid_index, NULL((void*)0), | |||
| 277 | LATENCY_TRACKING_FID_DATA_LEN32, enabled); | |||
| 278 | } | |||
| 279 | ||||
| 280 | static int latency_tracking_enable(struct latency_tracker *lt) | |||
| 281 | { | |||
| 282 | __u64 result; | |||
| 283 | int err; | |||
| 284 | ||||
| 285 | if (!(lt->cfg.enable || lt->cfg.disable)) | |||
| 286 | return 0; | |||
| 287 | ||||
| 288 | if (lt->cfg.enable && lt->cfg.disable) { | |||
| 289 | fprintf(stderrstderr, "Cannot enable and disable simultaneously.\n"); | |||
| 290 | return -EINVAL22; | |||
| 291 | } | |||
| 292 | ||||
| 293 | err = nvme_set_features(lt->hdl, 0, LATENCY_TRACKING_FID0xe2, 0, | |||
| 294 | lt->cfg.enable, 0, 0, lt->uuid_index, 0, NULL((void*)0), | |||
| 295 | LATENCY_TRACKING_FID_DATA_LEN32, &result); | |||
| 296 | if (err > 0) { | |||
| 297 | nvme_show_status(err); | |||
| 298 | } else if (err < 0) { | |||
| 299 | perror("Enable latency tracking"); | |||
| 300 | fprintf(stderrstderr, "Command failed while parsing.\n"); | |||
| 301 | } else { | |||
| 302 | if (lt->print_flags == NORMAL) { | |||
| 303 | printf("Successfully set enable bit for UUID-idx:%d FID:0x%X, to %i.\n", | |||
| 304 | lt->uuid_index, LATENCY_TRACKING_FID0xe2, lt->cfg.enable); | |||
| 305 | } | |||
| 306 | } | |||
| 307 | return err; | |||
| 308 | } | |||
| 309 | ||||
| 310 | #define READ_LOG_ID0xc1 0xc1 | |||
| 311 | #define WRITE_LOG_ID0xc2 0xc2 | |||
| 312 | ||||
| 313 | static int latency_tracker_get_log(struct latency_tracker *lt) | |||
| 314 | { | |||
| 315 | struct libnvme_passthru_cmd cmd; | |||
| 316 | int err; | |||
| 317 | ||||
| 318 | if (lt->cfg.read && lt->cfg.write) { | |||
| 319 | fprintf(stderrstderr, "Cannot capture read and write logs simultaneously.\n"); | |||
| 320 | return -EINVAL22; | |||
| 321 | } | |||
| 322 | ||||
| 323 | if (!(lt->cfg.read
| |||
| 324 | return 0; | |||
| 325 | ||||
| 326 | nvme_init_get_log(&cmd, NVME_NSID_ALL, | |||
| 327 | lt->cfg.write
| |||
| 328 | NVME_CSI_NVM, <->stats, sizeof(lt->stats)); | |||
| 329 | cmd.cdw10 |= NVME_FIELD_ENCODE(lt->cfg.type,(((__u32)(lt->cfg.type) & (NVME_LOG_CDW10_LSP_MASK)) << (NVME_LOG_CDW10_LSP_SHIFT)) | |||
| 330 | NVME_LOG_CDW10_LSP_SHIFT,(((__u32)(lt->cfg.type) & (NVME_LOG_CDW10_LSP_MASK)) << (NVME_LOG_CDW10_LSP_SHIFT)) | |||
| 331 | NVME_LOG_CDW10_LSP_MASK)(((__u32)(lt->cfg.type) & (NVME_LOG_CDW10_LSP_MASK)) << (NVME_LOG_CDW10_LSP_SHIFT)); | |||
| 332 | cmd.cdw14 |= NVME_FIELD_ENCODE(lt->uuid_index,(((__u32)(lt->uuid_index) & (NVME_LOG_CDW14_UUID_MASK) ) << (NVME_LOG_CDW14_UUID_SHIFT)) | |||
| 333 | NVME_LOG_CDW14_UUID_SHIFT,(((__u32)(lt->uuid_index) & (NVME_LOG_CDW14_UUID_MASK) ) << (NVME_LOG_CDW14_UUID_SHIFT)) | |||
| 334 | NVME_LOG_CDW14_UUID_MASK)(((__u32)(lt->uuid_index) & (NVME_LOG_CDW14_UUID_MASK) ) << (NVME_LOG_CDW14_UUID_SHIFT)); | |||
| 335 | err = libnvme_get_log(lt->hdl, &cmd, false0, NVME_LOG_PAGE_PDU_SIZE4096); | |||
| 336 | if (err) | |||
| 337 | return err; | |||
| 338 | ||||
| 339 | if (lt->print_flags & BINARY) | |||
| 340 | d_raw((unsigned char *)<->stats, | |||
| 341 | sizeof(lt->stats)); | |||
| 342 | else { | |||
| 343 | latency_tracker_parse(lt); | |||
| 344 | } | |||
| 345 | return err; | |||
| 346 | } | |||
| 347 | ||||
| 348 | int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *acmd, | |||
| 349 | struct plugin *plugin) | |||
| 350 | { | |||
| 351 | const char *desc = "Get and Parse Solidigm Latency Tracking Statistics log."; | |||
| 352 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 353 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 354 | __u64 enabled; | |||
| 355 | int err; | |||
| 356 | ||||
| 357 | struct latency_tracker lt = { | |||
| 358 | .uuid_index = 0, | |||
| 359 | .base_range_bits = BASE_RANGE_BITS_4_16, | |||
| 360 | .bucket_list_size = BUCKET_LIST_SIZE_4_11216, | |||
| 361 | .has_average_latency_field = false0, | |||
| 362 | }; | |||
| 363 | ||||
| 364 | NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"enable", 'e', ((void*)0), CFG_FLAG, <.cfg.enable , 0, "Enable Latency Tracking", 0, }, {"disable", 'd', ((void *)0), CFG_FLAG, <.cfg.disable, 0, "Disable Latency Tracking" , 0, }, {"read", 'r', ((void*)0), CFG_FLAG, <.cfg.read, 0, "Get read statistics", 0, }, {"write", 'w', ((void*)0), CFG_FLAG , <.cfg.write, 0, "Get write statistics", 0, }, {"type" , 't', "NUM", CFG_BYTE, <.cfg.type, 1, "Log type to get" , 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0 , "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT , &nvme_args.verbose, 0, "Increase output verbosity", 0, } , {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format , 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout" , 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds" , 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run , 0, "show command instead of executing", 0, }, {"no-retries" , 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors" , 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args .no_ioctl_probing, 0, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, & nvme_args.output_format_ver, 1, "output format version: 1|2", 0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args .verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } } | |||
| 365 | OPT_FLAG("enable", 'e', <.cfg.enable, "Enable Latency Tracking"),struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"enable", 'e', ((void*)0), CFG_FLAG, <.cfg.enable , 0, "Enable Latency Tracking", 0, }, {"disable", 'd', ((void *)0), CFG_FLAG, <.cfg.disable, 0, "Disable Latency Tracking" , 0, }, {"read", 'r', ((void*)0), CFG_FLAG, <.cfg.read, 0, "Get read statistics", 0, }, {"write", 'w', ((void*)0), CFG_FLAG , <.cfg.write, 0, "Get write statistics", 0, }, {"type" , 't', "NUM", CFG_BYTE, <.cfg.type, 1, "Log type to get" , 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0 , "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT , &nvme_args.verbose, 0, "Increase output verbosity", 0, } , {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format , 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout" , 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds" , 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run , 0, "show command instead of executing", 0, }, {"no-retries" , 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors" , 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args .no_ioctl_probing, 0, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, & nvme_args.output_format_ver, 1, "output format version: 1|2", 0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args .verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } } | |||
| 366 | OPT_FLAG("disable", 'd', <.cfg.disable, "Disable Latency Tracking"),struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"enable", 'e', ((void*)0), CFG_FLAG, <.cfg.enable , 0, "Enable Latency Tracking", 0, }, {"disable", 'd', ((void *)0), CFG_FLAG, <.cfg.disable, 0, "Disable Latency Tracking" , 0, }, {"read", 'r', ((void*)0), CFG_FLAG, <.cfg.read, 0, "Get read statistics", 0, }, {"write", 'w', ((void*)0), CFG_FLAG , <.cfg.write, 0, "Get write statistics", 0, }, {"type" , 't', "NUM", CFG_BYTE, <.cfg.type, 1, "Log type to get" , 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0 , "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT , &nvme_args.verbose, 0, "Increase output verbosity", 0, } , {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format , 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout" , 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds" , 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run , 0, "show command instead of executing", 0, }, {"no-retries" , 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors" , 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args .no_ioctl_probing, 0, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, & nvme_args.output_format_ver, 1, "output format version: 1|2", 0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args .verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } } | |||
| 367 | OPT_FLAG("read", 'r', <.cfg.read, "Get read statistics"),struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"enable", 'e', ((void*)0), CFG_FLAG, <.cfg.enable , 0, "Enable Latency Tracking", 0, }, {"disable", 'd', ((void *)0), CFG_FLAG, <.cfg.disable, 0, "Disable Latency Tracking" , 0, }, {"read", 'r', ((void*)0), CFG_FLAG, <.cfg.read, 0, "Get read statistics", 0, }, {"write", 'w', ((void*)0), CFG_FLAG , <.cfg.write, 0, "Get write statistics", 0, }, {"type" , 't', "NUM", CFG_BYTE, <.cfg.type, 1, "Log type to get" , 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0 , "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT , &nvme_args.verbose, 0, "Increase output verbosity", 0, } , {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format , 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout" , 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds" , 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run , 0, "show command instead of executing", 0, }, {"no-retries" , 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors" , 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args .no_ioctl_probing, 0, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, & nvme_args.output_format_ver, 1, "output format version: 1|2", 0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args .verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } } | |||
| 368 | OPT_FLAG("write", 'w', <.cfg.write, "Get write statistics"),struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"enable", 'e', ((void*)0), CFG_FLAG, <.cfg.enable , 0, "Enable Latency Tracking", 0, }, {"disable", 'd', ((void *)0), CFG_FLAG, <.cfg.disable, 0, "Disable Latency Tracking" , 0, }, {"read", 'r', ((void*)0), CFG_FLAG, <.cfg.read, 0, "Get read statistics", 0, }, {"write", 'w', ((void*)0), CFG_FLAG , <.cfg.write, 0, "Get write statistics", 0, }, {"type" , 't', "NUM", CFG_BYTE, <.cfg.type, 1, "Log type to get" , 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0 , "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT , &nvme_args.verbose, 0, "Increase output verbosity", 0, } , {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format , 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout" , 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds" , 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run , 0, "show command instead of executing", 0, }, {"no-retries" , 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors" , 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args .no_ioctl_probing, 0, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, & nvme_args.output_format_ver, 1, "output format version: 1|2", 0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args .verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } } | |||
| 369 | OPT_BYTE("type", 't', <.cfg.type, "Log type to get"))struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"enable", 'e', ((void*)0), CFG_FLAG, <.cfg.enable , 0, "Enable Latency Tracking", 0, }, {"disable", 'd', ((void *)0), CFG_FLAG, <.cfg.disable, 0, "Disable Latency Tracking" , 0, }, {"read", 'r', ((void*)0), CFG_FLAG, <.cfg.read, 0, "Get read statistics", 0, }, {"write", 'w', ((void*)0), CFG_FLAG , <.cfg.write, 0, "Get write statistics", 0, }, {"type" , 't', "NUM", CFG_BYTE, <.cfg.type, 1, "Log type to get" , 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0 , "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT , &nvme_args.verbose, 0, "Increase output verbosity", 0, } , {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format , 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout" , 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds" , 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run , 0, "show command instead of executing", 0, }, {"no-retries" , 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors" , 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args .no_ioctl_probing, 0, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, & nvme_args.output_format_ver, 1, "output format version: 1|2", 0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args .verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }; | |||
| 370 | ||||
| 371 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 372 | if (err) | |||
| ||||
| 373 | return err; | |||
| 374 | ||||
| 375 | lt.hdl = hdl; | |||
| 376 | ||||
| 377 | err = validate_output_format(nvme_args.output_format, <.print_flags); | |||
| 378 | if (err < 0) { | |||
| 379 | fprintf(stderrstderr, "Invalid output format '%s'\n", | |||
| 380 | nvme_args.output_format); | |||
| 381 | return -EINVAL22; | |||
| 382 | } | |||
| 383 | ||||
| 384 | if (lt.cfg.type > 0xf) { | |||
| 385 | fprintf(stderrstderr, "Invalid Log type value '%d'\n", lt.cfg.type); | |||
| 386 | return -EINVAL22; | |||
| 387 | } | |||
| 388 | ||||
| 389 | if (lt.cfg.type && !(lt.cfg.read || lt.cfg.write)) { | |||
| 390 | fprintf(stderrstderr, "Log type option valid only when retrieving statistics\n"); | |||
| 391 | return -EINVAL22; | |||
| 392 | } | |||
| 393 | ||||
| 394 | sldgm_get_uuid_index(hdl, <.uuid_index); | |||
| 395 | ||||
| 396 | err = latency_tracking_enable(<); | |||
| 397 | if (err
| |||
| 398 | return err; | |||
| 399 | ||||
| 400 | err = latency_tracker_get_log(<); | |||
| 401 | if (err) | |||
| 402 | return err; | |||
| 403 | ||||
| 404 | if ((lt.cfg.read || lt.cfg.write || lt.cfg.enable || lt.cfg.disable)) | |||
| 405 | return 0; | |||
| 406 | ||||
| 407 | err = latency_tracking_is_enable(<, &enabled); | |||
| 408 | if (!err) { | |||
| 409 | if (lt.print_flags == JSON) { | |||
| 410 | struct json_object *root = json_create_object()json_object_new_object(); | |||
| 411 | ||||
| 412 | json_object_add_value_int(root, "enabled", enabled)json_object_object_add(root, "enabled", json_object_new_int(enabled )); | |||
| 413 | json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 << 1) | (1 << 4))); | |||
| 414 | json_free_object(root)json_object_put(root); | |||
| 415 | printf("\n"); | |||
| 416 | } else if (lt.print_flags == BINARY) { | |||
| 417 | putchar(enabled); | |||
| 418 | } else { | |||
| 419 | printf("Latency Statistics Tracking (UUID-idx:%d, FID:0x%X) is currently %"PRIu64"l" "u"".\n", | |||
| 420 | lt.uuid_index, LATENCY_TRACKING_FID0xe2, | |||
| 421 | (uint64_t)enabled); | |||
| 422 | } | |||
| 423 | } else { | |||
| 424 | fprintf(stderrstderr, "Could not read feature id 0xE2.\n"); | |||
| 425 | } | |||
| 426 | return err; | |||
| 427 | } |