| File: | .build-ci/../plugins/intel/intel-nvme.c |
| Warning: | line 1413, column 10 Potential leak of memory pointed to by 'intel' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | #include <errno(*__errno_location ()).h> | |||
| 3 | #include <fcntl.h> | |||
| 4 | #include <inttypes.h> | |||
| 5 | #include <stdio.h> | |||
| 6 | #include <stdlib.h> | |||
| 7 | #include <unistd.h> | |||
| 8 | ||||
| 9 | #include <libnvme.h> | |||
| 10 | ||||
| 11 | #include <ccan/endian/endian.h> | |||
| 12 | #include <ccan/minmax/minmax.h> | |||
| 13 | #include <shared/compiler-attributes-util.h> | |||
| 14 | #include <shared/fs-util.h> | |||
| 15 | #include <shared/parse-util.h> | |||
| 16 | ||||
| 17 | #include "cleanup.h" | |||
| 18 | #include "global-ctx.h" | |||
| 19 | #include "nvme-cmds.h" | |||
| 20 | #include "nvme-print.h" | |||
| 21 | #include "plugin.h" | |||
| 22 | ||||
| 23 | struct __packed__attribute__((__packed__)) nvme_additional_smart_log_item { | |||
| 24 | __u8 key; | |||
| 25 | __u8 _kp[2]; | |||
| 26 | __u8 norm; | |||
| 27 | __u8 _np; | |||
| 28 | union __packed__attribute__((__packed__)) { | |||
| 29 | __u8 raw[6]; | |||
| 30 | struct __packed__attribute__((__packed__)) wear_level { | |||
| 31 | __le16 min; | |||
| 32 | __le16 max; | |||
| 33 | __le16 avg; | |||
| 34 | } wear_level; | |||
| 35 | struct __packed__attribute__((__packed__)) thermal_throttle { | |||
| 36 | __u8 pct; | |||
| 37 | __u32 count; | |||
| 38 | } thermal_throttle; | |||
| 39 | }; | |||
| 40 | __u8 _rp; | |||
| 41 | }; | |||
| 42 | ||||
| 43 | struct nvme_additional_smart_log { | |||
| 44 | struct nvme_additional_smart_log_item program_fail_cnt; | |||
| 45 | struct nvme_additional_smart_log_item erase_fail_cnt; | |||
| 46 | struct nvme_additional_smart_log_item wear_leveling_cnt; | |||
| 47 | struct nvme_additional_smart_log_item e2e_err_cnt; | |||
| 48 | struct nvme_additional_smart_log_item crc_err_cnt; | |||
| 49 | struct nvme_additional_smart_log_item timed_workload_media_wear; | |||
| 50 | struct nvme_additional_smart_log_item timed_workload_host_reads; | |||
| 51 | struct nvme_additional_smart_log_item timed_workload_timer; | |||
| 52 | struct nvme_additional_smart_log_item thermal_throttle_status; | |||
| 53 | struct nvme_additional_smart_log_item retry_buffer_overflow_cnt; | |||
| 54 | struct nvme_additional_smart_log_item pll_lock_loss_cnt; | |||
| 55 | struct nvme_additional_smart_log_item nand_bytes_written; | |||
| 56 | struct nvme_additional_smart_log_item host_bytes_written; | |||
| 57 | struct nvme_additional_smart_log_item host_ctx_wear_used; | |||
| 58 | struct nvme_additional_smart_log_item perf_stat_indicator; | |||
| 59 | struct nvme_additional_smart_log_item re_alloc_sectr_cnt; | |||
| 60 | struct nvme_additional_smart_log_item soft_ecc_err_rate; | |||
| 61 | struct nvme_additional_smart_log_item unexp_power_loss; | |||
| 62 | struct nvme_additional_smart_log_item media_bytes_read; | |||
| 63 | struct nvme_additional_smart_log_item avail_fw_downgrades; | |||
| 64 | }; | |||
| 65 | ||||
| 66 | struct nvme_vu_id_ctrl_field { /* CDR MR5 */ | |||
| 67 | __u8 rsvd1[3]; | |||
| 68 | __u8 ss; | |||
| 69 | __u8 health[20]; | |||
| 70 | __u8 cls; | |||
| 71 | __u8 nlw; | |||
| 72 | __u8 scap; | |||
| 73 | __u8 sstat; | |||
| 74 | __u8 bl[8]; | |||
| 75 | __u8 rsvd2[38]; | |||
| 76 | __u8 ww[8]; /* little endian */ | |||
| 77 | __u8 mic_bl[4]; | |||
| 78 | __u8 mic_fw[4]; | |||
| 79 | }; | |||
| 80 | ||||
| 81 | static void json_intel_id_ctrl(struct nvme_vu_id_ctrl_field *id, | |||
| 82 | char *health, char *bl, char *ww, char *mic_bl, char *mic_fw, | |||
| 83 | struct json_object *root) | |||
| 84 | { | |||
| 85 | json_object_add_value_int(root, "ss", id->ss)json_object_object_add(root, "ss", json_object_new_int(id-> ss)); | |||
| 86 | json_object_add_value_string(root, "health", health); | |||
| 87 | json_object_add_value_int(root, "cls", id->cls)json_object_object_add(root, "cls", json_object_new_int(id-> cls)); | |||
| 88 | json_object_add_value_int(root, "nlw", id->nlw)json_object_object_add(root, "nlw", json_object_new_int(id-> nlw)); | |||
| 89 | json_object_add_value_int(root, "scap", id->scap)json_object_object_add(root, "scap", json_object_new_int(id-> scap)); | |||
| 90 | json_object_add_value_int(root, "sstat", id->sstat)json_object_object_add(root, "sstat", json_object_new_int(id-> sstat)); | |||
| 91 | json_object_add_value_string(root, "bl", bl); | |||
| 92 | json_object_add_value_string(root, "ww", ww); | |||
| 93 | json_object_add_value_string(root, "mic_bl", mic_bl); | |||
| 94 | json_object_add_value_string(root, "mic_fw", mic_fw); | |||
| 95 | } | |||
| 96 | ||||
| 97 | static void intel_id_ctrl(__u8 *vs, struct json_object *root) | |||
| 98 | { | |||
| 99 | struct nvme_vu_id_ctrl_field *id = (struct nvme_vu_id_ctrl_field *)vs; | |||
| 100 | ||||
| 101 | char health[21] = { 0 }; | |||
| 102 | char bl[9] = { 0 }; | |||
| 103 | char ww[19] = { 0 }; | |||
| 104 | char mic_bl[5] = { 0 }; | |||
| 105 | char mic_fw[5] = { 0 }; | |||
| 106 | ||||
| 107 | if (id->health[0] == 0) | |||
| 108 | snprintf(health, 21, "%s", "healthy"); | |||
| 109 | else | |||
| 110 | snprintf(health, 21, "%.*s", (int)sizeof(id->health), | |||
| 111 | id->health); | |||
| 112 | ||||
| 113 | snprintf(bl, 9, "%.*s", (int)sizeof(id->bl), id->bl); | |||
| 114 | snprintf(ww, 19, "%02X%02X%02X%02X%02X%02X%02X%02X", id->ww[7], | |||
| 115 | id->ww[6], id->ww[5], id->ww[4], id->ww[3], id->ww[2], | |||
| 116 | id->ww[1], id->ww[0]); | |||
| 117 | snprintf(mic_bl, 5, "%.*s", (int)sizeof(id->mic_bl), id->mic_bl); | |||
| 118 | snprintf(mic_fw, 5, "%.*s", (int)sizeof(id->mic_fw), id->mic_fw); | |||
| 119 | ||||
| 120 | if (root) { | |||
| 121 | json_intel_id_ctrl(id, health, bl, ww, mic_bl, mic_fw, root); | |||
| 122 | return; | |||
| 123 | } | |||
| 124 | ||||
| 125 | printf("ss : %d\n", id->ss); | |||
| 126 | printf("health : %s\n", health); | |||
| 127 | printf("cls : %d\n", id->cls); | |||
| 128 | printf("nlw : %d\n", id->nlw); | |||
| 129 | printf("scap : %d\n", id->scap); | |||
| 130 | printf("sstat : %d\n", id->sstat); | |||
| 131 | printf("bl : %s\n", bl); | |||
| 132 | printf("ww : %s\n", ww); | |||
| 133 | printf("mic_bl : %s\n", mic_bl); | |||
| 134 | printf("mic_fw : %s\n", mic_fw); | |||
| 135 | } | |||
| 136 | ||||
| 137 | static int id_ctrl(int argc, char **argv, struct command *acmd, struct plugin *plugin) | |||
| 138 | { | |||
| 139 | return __id_ctrl(argc, argv, acmd, plugin, intel_id_ctrl); | |||
| 140 | } | |||
| 141 | ||||
| 142 | static void | |||
| 143 | show_intel_smart_log_jsn(struct nvme_additional_smart_log *smart, | |||
| 144 | unsigned int nsid, const char *devname) | |||
| 145 | { | |||
| 146 | struct json_object *root, *entry_stats, *dev_stats, *multi; | |||
| 147 | ||||
| 148 | root = json_create_object()json_object_new_object(); | |||
| 149 | json_object_add_value_string(root, "Intel Smart log", devname); | |||
| 150 | ||||
| 151 | dev_stats = json_create_object()json_object_new_object(); | |||
| 152 | ||||
| 153 | entry_stats = json_create_object()json_object_new_object(); | |||
| 154 | json_object_add_value_int(entry_stats, "normalized", smart->program_fail_cnt.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->program_fail_cnt.norm)); | |||
| 155 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->program_fail_cnt.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->program_fail_cnt.raw))); | |||
| 156 | json_object_add_value_object(dev_stats, "program_fail_count", entry_stats)json_object_object_add(dev_stats, "program_fail_count", entry_stats ); | |||
| 157 | ||||
| 158 | entry_stats = json_create_object()json_object_new_object(); | |||
| 159 | json_object_add_value_int(entry_stats, "normalized", smart->erase_fail_cnt.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->erase_fail_cnt.norm)); | |||
| 160 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->erase_fail_cnt.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->erase_fail_cnt.raw))); | |||
| 161 | json_object_add_value_object(dev_stats, "erase_fail_count", entry_stats)json_object_object_add(dev_stats, "erase_fail_count", entry_stats ); | |||
| 162 | ||||
| 163 | entry_stats = json_create_object()json_object_new_object(); | |||
| 164 | json_object_add_value_int(entry_stats, "normalized", smart->wear_leveling_cnt.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->wear_leveling_cnt.norm)); | |||
| 165 | multi = json_create_object()json_object_new_object(); | |||
| 166 | json_object_add_value_int(multi, "min", le16_to_cpu(smart->wear_leveling_cnt.wear_level.min))json_object_object_add(multi, "min", json_object_new_int(le16_to_cpu (smart->wear_leveling_cnt.wear_level.min))); | |||
| 167 | json_object_add_value_int(multi, "max", le16_to_cpu(smart->wear_leveling_cnt.wear_level.max))json_object_object_add(multi, "max", json_object_new_int(le16_to_cpu (smart->wear_leveling_cnt.wear_level.max))); | |||
| 168 | json_object_add_value_int(multi, "avg", le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg))json_object_object_add(multi, "avg", json_object_new_int(le16_to_cpu (smart->wear_leveling_cnt.wear_level.avg))); | |||
| 169 | json_object_add_value_object(entry_stats, "raw", multi)json_object_object_add(entry_stats, "raw", multi); | |||
| 170 | json_object_add_value_object(dev_stats, "wear_leveling", entry_stats)json_object_object_add(dev_stats, "wear_leveling", entry_stats ); | |||
| 171 | ||||
| 172 | entry_stats = json_create_object()json_object_new_object(); | |||
| 173 | json_object_add_value_int(entry_stats, "normalized", smart->e2e_err_cnt.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->e2e_err_cnt.norm)); | |||
| 174 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->e2e_err_cnt.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->e2e_err_cnt.raw))); | |||
| 175 | json_object_add_value_object(dev_stats, "end_to_end_error_detection_count", entry_stats)json_object_object_add(dev_stats, "end_to_end_error_detection_count" , entry_stats); | |||
| 176 | ||||
| 177 | entry_stats = json_create_object()json_object_new_object(); | |||
| 178 | json_object_add_value_int(entry_stats, "normalized", smart->crc_err_cnt.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->crc_err_cnt.norm)); | |||
| 179 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->crc_err_cnt.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->crc_err_cnt.raw))); | |||
| 180 | json_object_add_value_object(dev_stats, "crc_error_count", entry_stats)json_object_object_add(dev_stats, "crc_error_count", entry_stats ); | |||
| 181 | ||||
| 182 | entry_stats = json_create_object()json_object_new_object(); | |||
| 183 | json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_media_wear.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->timed_workload_media_wear.norm)); | |||
| 184 | json_object_add_value_double(entry_stats, "raw", ((long double)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024)json_object_object_add(entry_stats, "raw", util_json_object_new_double (((long double)int48_to_long(smart->timed_workload_media_wear .raw)) / 1024)); | |||
| 185 | json_object_add_value_object(dev_stats, "timed_workload_media_wear", entry_stats)json_object_object_add(dev_stats, "timed_workload_media_wear" , entry_stats); | |||
| 186 | ||||
| 187 | entry_stats = json_create_object()json_object_new_object(); | |||
| 188 | json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_host_reads.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->timed_workload_host_reads.norm)); | |||
| 189 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->timed_workload_host_reads.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->timed_workload_host_reads.raw))); | |||
| 190 | json_object_add_value_object(dev_stats, "timed_workload_host_reads", entry_stats)json_object_object_add(dev_stats, "timed_workload_host_reads" , entry_stats); | |||
| 191 | ||||
| 192 | entry_stats = json_create_object()json_object_new_object(); | |||
| 193 | json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_timer.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->timed_workload_timer.norm)); | |||
| 194 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->timed_workload_timer.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->timed_workload_timer.raw))); | |||
| 195 | json_object_add_value_object(dev_stats, "timed_workload_timer", entry_stats)json_object_object_add(dev_stats, "timed_workload_timer", entry_stats ); | |||
| 196 | ||||
| 197 | entry_stats = json_create_object()json_object_new_object(); | |||
| 198 | json_object_add_value_int(entry_stats, "normalized", smart->thermal_throttle_status.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->thermal_throttle_status.norm)); | |||
| 199 | multi = json_create_object()json_object_new_object(); | |||
| 200 | json_object_add_value_int(multi, "pct", smart->thermal_throttle_status.thermal_throttle.pct)json_object_object_add(multi, "pct", json_object_new_int(smart ->thermal_throttle_status.thermal_throttle.pct)); | |||
| 201 | json_object_add_value_int(multi, "cnt", smart->thermal_throttle_status.thermal_throttle.count)json_object_object_add(multi, "cnt", json_object_new_int(smart ->thermal_throttle_status.thermal_throttle.count)); | |||
| 202 | json_object_add_value_object(entry_stats, "raw", multi)json_object_object_add(entry_stats, "raw", multi); | |||
| 203 | json_object_add_value_object(dev_stats, "thermal_throttle_status", entry_stats)json_object_object_add(dev_stats, "thermal_throttle_status", entry_stats ); | |||
| 204 | ||||
| 205 | entry_stats = json_create_object()json_object_new_object(); | |||
| 206 | json_object_add_value_int(entry_stats, "normalized", smart->retry_buffer_overflow_cnt.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->retry_buffer_overflow_cnt.norm)); | |||
| 207 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->retry_buffer_overflow_cnt.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->retry_buffer_overflow_cnt.raw))); | |||
| 208 | json_object_add_value_object(dev_stats, "retry_buffer_overflow_count", entry_stats)json_object_object_add(dev_stats, "retry_buffer_overflow_count" , entry_stats); | |||
| 209 | ||||
| 210 | entry_stats = json_create_object()json_object_new_object(); | |||
| 211 | json_object_add_value_int(entry_stats, "normalized", smart->pll_lock_loss_cnt.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->pll_lock_loss_cnt.norm)); | |||
| 212 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->pll_lock_loss_cnt.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->pll_lock_loss_cnt.raw))); | |||
| 213 | json_object_add_value_object(dev_stats, "pll_lock_loss_count", entry_stats)json_object_object_add(dev_stats, "pll_lock_loss_count", entry_stats ); | |||
| 214 | ||||
| 215 | entry_stats = json_create_object()json_object_new_object(); | |||
| 216 | json_object_add_value_int(entry_stats, "normalized", smart->nand_bytes_written.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->nand_bytes_written.norm)); | |||
| 217 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->nand_bytes_written.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->nand_bytes_written.raw))); | |||
| 218 | json_object_add_value_object(dev_stats, "nand_bytes_written", entry_stats)json_object_object_add(dev_stats, "nand_bytes_written", entry_stats ); | |||
| 219 | ||||
| 220 | entry_stats = json_create_object()json_object_new_object(); | |||
| 221 | json_object_add_value_int(entry_stats, "normalized", smart->host_bytes_written.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->host_bytes_written.norm)); | |||
| 222 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->host_bytes_written.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->host_bytes_written.raw))); | |||
| 223 | json_object_add_value_object(dev_stats, "host_bytes_written", entry_stats)json_object_object_add(dev_stats, "host_bytes_written", entry_stats ); | |||
| 224 | ||||
| 225 | entry_stats = json_create_object()json_object_new_object(); | |||
| 226 | json_object_add_value_int(entry_stats, "normalized", smart->host_ctx_wear_used.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->host_ctx_wear_used.norm)); | |||
| 227 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->host_ctx_wear_used.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->host_ctx_wear_used.raw))); | |||
| 228 | json_object_add_value_object(dev_stats, "host_ctx_wear_used", entry_stats)json_object_object_add(dev_stats, "host_ctx_wear_used", entry_stats ); | |||
| 229 | ||||
| 230 | entry_stats = json_create_object()json_object_new_object(); | |||
| 231 | json_object_add_value_int(entry_stats, "normalized", smart->perf_stat_indicator.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->perf_stat_indicator.norm)); | |||
| 232 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->perf_stat_indicator.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->perf_stat_indicator.raw))); | |||
| 233 | json_object_add_value_object(dev_stats, "perf_stat_indicator", entry_stats)json_object_object_add(dev_stats, "perf_stat_indicator", entry_stats ); | |||
| 234 | ||||
| 235 | entry_stats = json_create_object()json_object_new_object(); | |||
| 236 | json_object_add_value_int(entry_stats, "normalized", smart->re_alloc_sectr_cnt.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->re_alloc_sectr_cnt.norm)); | |||
| 237 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->re_alloc_sectr_cnt.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->re_alloc_sectr_cnt.raw))); | |||
| 238 | json_object_add_value_object(dev_stats, "re_alloc_sectr_cnt", entry_stats)json_object_object_add(dev_stats, "re_alloc_sectr_cnt", entry_stats ); | |||
| 239 | ||||
| 240 | entry_stats = json_create_object()json_object_new_object(); | |||
| 241 | json_object_add_value_int(entry_stats, "normalized", smart->soft_ecc_err_rate.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->soft_ecc_err_rate.norm)); | |||
| 242 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->soft_ecc_err_rate.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->soft_ecc_err_rate.raw))); | |||
| 243 | json_object_add_value_object(dev_stats, "soft_ecc_err_rate", entry_stats)json_object_object_add(dev_stats, "soft_ecc_err_rate", entry_stats ); | |||
| 244 | ||||
| 245 | entry_stats = json_create_object()json_object_new_object(); | |||
| 246 | json_object_add_value_int(entry_stats, "normalized", smart->unexp_power_loss.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->unexp_power_loss.norm)); | |||
| 247 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->unexp_power_loss.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->unexp_power_loss.raw))); | |||
| 248 | json_object_add_value_object(dev_stats, "unexp_power_loss", entry_stats)json_object_object_add(dev_stats, "unexp_power_loss", entry_stats ); | |||
| 249 | ||||
| 250 | entry_stats = json_create_object()json_object_new_object(); | |||
| 251 | json_object_add_value_int(entry_stats, "normalized", smart->media_bytes_read.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->media_bytes_read.norm)); | |||
| 252 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->media_bytes_read.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->media_bytes_read.raw))); | |||
| 253 | json_object_add_value_object(dev_stats, "media_bytes_read", entry_stats)json_object_object_add(dev_stats, "media_bytes_read", entry_stats ); | |||
| 254 | ||||
| 255 | entry_stats = json_create_object()json_object_new_object(); | |||
| 256 | json_object_add_value_int(entry_stats, "normalized", smart->avail_fw_downgrades.norm)json_object_object_add(entry_stats, "normalized", json_object_new_int (smart->avail_fw_downgrades.norm)); | |||
| 257 | json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->avail_fw_downgrades.raw))json_object_object_add(entry_stats, "raw", json_object_new_int (int48_to_long(smart->avail_fw_downgrades.raw))); | |||
| 258 | json_object_add_value_object(dev_stats, "avail_fw_downgrades", entry_stats)json_object_object_add(dev_stats, "avail_fw_downgrades", entry_stats ); | |||
| 259 | ||||
| 260 | json_object_add_value_object(root, "Device stats", dev_stats)json_object_object_add(root, "Device stats", dev_stats); | |||
| 261 | ||||
| 262 | json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 << 1) | (1 << 4))); | |||
| 263 | printf("\n"); | |||
| 264 | json_free_object(root)json_object_put(root); | |||
| 265 | } | |||
| 266 | ||||
| 267 | static char *id_to_key(__u8 id) | |||
| 268 | { | |||
| 269 | switch (id) { | |||
| 270 | case 0xAB: | |||
| 271 | return "program_fail_count"; | |||
| 272 | case 0xAC: | |||
| 273 | return "erase_fail_count"; | |||
| 274 | case 0xAD: | |||
| 275 | return "wear_leveling_count"; | |||
| 276 | case 0xB8: | |||
| 277 | return "e2e_error_detect_count"; | |||
| 278 | case 0xC7: | |||
| 279 | return "crc_error_count"; | |||
| 280 | case 0xE2: | |||
| 281 | return "media_wear_percentage"; | |||
| 282 | case 0xE3: | |||
| 283 | return "host_reads"; | |||
| 284 | case 0xE4: | |||
| 285 | return "timed_work_load"; | |||
| 286 | case 0xEA: | |||
| 287 | return "thermal_throttle_status"; | |||
| 288 | case 0xF0: | |||
| 289 | return "retry_buff_overflow_count"; | |||
| 290 | case 0xF3: | |||
| 291 | return "pll_lock_loss_counter"; | |||
| 292 | case 0xF4: | |||
| 293 | return "nand_bytes_written"; | |||
| 294 | case 0xF5: | |||
| 295 | return "host_bytes_written"; | |||
| 296 | case 0xF6: | |||
| 297 | return "host_context_wear_used"; | |||
| 298 | case 0xF7: | |||
| 299 | return "performance_status_indicator"; | |||
| 300 | case 0xF8: | |||
| 301 | return "media_bytes_read"; | |||
| 302 | case 0xF9: | |||
| 303 | return "available_fw_downgrades"; | |||
| 304 | case 0x05: | |||
| 305 | return "re-allocated_sector_count"; | |||
| 306 | case 0x0D: | |||
| 307 | return "soft_ecc_error_rate"; | |||
| 308 | case 0xAE: | |||
| 309 | return "unexpected_power_loss"; | |||
| 310 | default: | |||
| 311 | return "Invalid ID"; | |||
| 312 | } | |||
| 313 | } | |||
| 314 | ||||
| 315 | static void print_intel_smart_log_items(struct nvme_additional_smart_log_item *item) | |||
| 316 | { | |||
| 317 | if (!item->key) | |||
| 318 | return; | |||
| 319 | ||||
| 320 | printf("%#x %-45s %3d %"PRIu64"l" "u""\n", | |||
| 321 | item->key, id_to_key(item->key), | |||
| 322 | item->norm, int48_to_long(item->raw)); | |||
| 323 | } | |||
| 324 | ||||
| 325 | static void show_intel_smart_log(struct nvme_additional_smart_log *smart, | |||
| 326 | unsigned int nsid, const char *devname) | |||
| 327 | { | |||
| 328 | struct nvme_additional_smart_log_item *iter = &smart->program_fail_cnt; | |||
| 329 | int num_items = sizeof(struct nvme_additional_smart_log) / | |||
| 330 | sizeof(struct nvme_additional_smart_log_item); | |||
| 331 | ||||
| 332 | printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", | |||
| 333 | devname, nsid); | |||
| 334 | printf("ID KEY Normalized Raw\n"); | |||
| 335 | ||||
| 336 | for (int i = 0; i < num_items; i++, iter++) | |||
| 337 | print_intel_smart_log_items(iter); | |||
| 338 | } | |||
| 339 | ||||
| 340 | static int get_additional_smart_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) | |||
| 341 | { | |||
| 342 | const char *desc = | |||
| 343 | "Get Intel vendor specific additional smart log (optionally, for the specified namespace), and show it."; | |||
| 344 | const char *namespace = "(optional) desired namespace"; | |||
| 345 | const char *raw = "Dump output in binary format"; | |||
| 346 | #ifdef CONFIG_JSONC | |||
| 347 | const char *json = "Dump output in json format"; | |||
| 348 | #endif /* CONFIG_JSONC */ | |||
| 349 | ||||
| 350 | struct nvme_additional_smart_log smart_log; | |||
| 351 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 352 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 353 | nvme_print_flags_t flags; | |||
| 354 | int err; | |||
| 355 | ||||
| 356 | struct config { | |||
| 357 | __u32 namespace_id; | |||
| 358 | bool_Bool raw_binary; | |||
| 359 | bool_Bool json; | |||
| 360 | }; | |||
| 361 | ||||
| 362 | struct config cfg = { | |||
| 363 | .namespace_id = NVME_NSID_ALL, | |||
| 364 | }; | |||
| 365 | ||||
| 366 | OPT_ARGS(opts)struct argconfig_commandline_options opts[] = { | |||
| 367 | OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace,{"namespace-id", 'n', "NUM", CFG_POSITIVE, _Generic(&cfg. namespace_id, uint32_t * : (&cfg.namespace_id)), ARGCONFIG_ARG_REQUIRED , namespace, 0, nvme_nsid_values} | |||
| 368 | nvme_nsid_values){"namespace-id", 'n', "NUM", CFG_POSITIVE, _Generic(&cfg. namespace_id, uint32_t * : (&cfg.namespace_id)), ARGCONFIG_ARG_REQUIRED , namespace, 0, nvme_nsid_values}, | |||
| 369 | OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw){"raw-binary", 'b', ((void*)0), CFG_FLAG, _Generic(&cfg.raw_binary , _Bool * : (&cfg.raw_binary)), ARGCONFIG_ARG_NONE, raw, 0 , }, | |||
| 370 | OPT_FLAG_JSON("json", 'j', &cfg.json, json){"json", 'j', ((void*)0), CFG_FLAG, _Generic(&cfg.json, _Bool * : (&cfg.json)), ARGCONFIG_ARG_NONE, json, 0, }, | |||
| 371 | OPT_END(){ ((void*)0) } | |||
| 372 | }; | |||
| 373 | ||||
| 374 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 375 | if (err) | |||
| 376 | return err; | |||
| 377 | ||||
| 378 | flags = nvme_args.output_flags; | |||
| 379 | ||||
| 380 | err = nvme_get_log_simple(hdl, 0xca, &smart_log, sizeof(smart_log)); | |||
| 381 | if (!err) { | |||
| 382 | if (flags & JSON || cfg.json) | |||
| 383 | show_intel_smart_log_jsn(&smart_log, cfg.namespace_id, | |||
| 384 | libnvme_transport_handle_get_name(hdl)); | |||
| 385 | else if (!cfg.raw_binary) | |||
| 386 | show_intel_smart_log(&smart_log, cfg.namespace_id, | |||
| 387 | libnvme_transport_handle_get_name(hdl)); | |||
| 388 | else | |||
| 389 | d_raw((unsigned char *)&smart_log, sizeof(smart_log)); | |||
| 390 | } else if (err > 0) { | |||
| 391 | nvme_show_status(err); | |||
| 392 | } | |||
| 393 | return err; | |||
| 394 | } | |||
| 395 | ||||
| 396 | static int get_market_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) | |||
| 397 | { | |||
| 398 | const char *desc = "Get Intel Marketing Name log and show it."; | |||
| 399 | const char *raw = "dump output in binary format"; | |||
| 400 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 401 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 402 | char log[512]; | |||
| 403 | int err; | |||
| 404 | ||||
| 405 | struct config { | |||
| 406 | bool_Bool raw_binary; | |||
| 407 | }; | |||
| 408 | ||||
| 409 | struct config cfg = { | |||
| 410 | }; | |||
| 411 | ||||
| 412 | OPT_ARGS(opts)struct argconfig_commandline_options opts[] = { | |||
| 413 | OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw){"raw-binary", 'b', ((void*)0), CFG_FLAG, _Generic(&cfg.raw_binary , _Bool * : (&cfg.raw_binary)), ARGCONFIG_ARG_NONE, raw, 0 , }, | |||
| 414 | OPT_END(){ ((void*)0) } | |||
| 415 | }; | |||
| 416 | ||||
| 417 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 418 | if (err) | |||
| 419 | return err; | |||
| 420 | ||||
| 421 | err = nvme_get_log_simple(hdl, 0xdd, log, sizeof(log)); | |||
| 422 | if (!err) { | |||
| 423 | if (!cfg.raw_binary) | |||
| 424 | printf("Intel Marketing Name Log:\n%.*s\n", | |||
| 425 | (int)sizeof(log), log); | |||
| 426 | else | |||
| 427 | d_raw((unsigned char *)&log, sizeof(log)); | |||
| 428 | } else if (err > 0) | |||
| 429 | nvme_show_status(err); | |||
| 430 | return err; | |||
| 431 | } | |||
| 432 | ||||
| 433 | struct intel_temp_stats { | |||
| 434 | __le64 curr; | |||
| 435 | __le64 last_overtemp; | |||
| 436 | __le64 life_overtemp; | |||
| 437 | __le64 highest_temp; | |||
| 438 | __le64 lowest_temp; | |||
| 439 | __u8 rsvd[40]; | |||
| 440 | __le64 max_operating_temp; | |||
| 441 | __le64 min_operating_temp; | |||
| 442 | __le64 est_offset; | |||
| 443 | }; | |||
| 444 | ||||
| 445 | static void show_temp_stats(struct intel_temp_stats *stats) | |||
| 446 | { | |||
| 447 | printf(" Intel Temperature Statistics\n"); | |||
| 448 | printf("--------------------------------\n"); | |||
| 449 | printf("Current temperature : %"PRIu64"l" "u""\n", le64_to_cpu(stats->curr)); | |||
| 450 | printf("Last critical overtemp flag : %"PRIu64"l" "u""\n", le64_to_cpu(stats->last_overtemp)); | |||
| 451 | printf("Life critical overtemp flag : %"PRIu64"l" "u""\n", le64_to_cpu(stats->life_overtemp)); | |||
| 452 | printf("Highest temperature : %"PRIu64"l" "u""\n", le64_to_cpu(stats->highest_temp)); | |||
| 453 | printf("Lowest temperature : %"PRIu64"l" "u""\n", le64_to_cpu(stats->lowest_temp)); | |||
| 454 | printf("Max operating temperature : %"PRIu64"l" "u""\n", le64_to_cpu(stats->max_operating_temp)); | |||
| 455 | printf("Min operating temperature : %"PRIu64"l" "u""\n", le64_to_cpu(stats->min_operating_temp)); | |||
| 456 | printf("Estimated offset : %"PRIu64"l" "u""\n", le64_to_cpu(stats->est_offset)); | |||
| 457 | } | |||
| 458 | ||||
| 459 | static int get_temp_stats_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) | |||
| 460 | { | |||
| 461 | struct intel_temp_stats stats; | |||
| 462 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 463 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 464 | int err; | |||
| 465 | ||||
| 466 | const char *desc = "Get Temperature Statistics log and show it."; | |||
| 467 | const char *raw = "dump output in binary format"; | |||
| 468 | struct config { | |||
| 469 | bool_Bool raw_binary; | |||
| 470 | }; | |||
| 471 | ||||
| 472 | struct config cfg = { | |||
| 473 | }; | |||
| 474 | ||||
| 475 | OPT_ARGS(opts)struct argconfig_commandline_options opts[] = { | |||
| 476 | OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw){"raw-binary", 'b', ((void*)0), CFG_FLAG, _Generic(&cfg.raw_binary , _Bool * : (&cfg.raw_binary)), ARGCONFIG_ARG_NONE, raw, 0 , }, | |||
| 477 | OPT_END(){ ((void*)0) } | |||
| 478 | }; | |||
| 479 | ||||
| 480 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 481 | if (err) | |||
| 482 | return err; | |||
| 483 | ||||
| 484 | err = nvme_get_log_simple(hdl, 0xc5, &stats, sizeof(stats)); | |||
| 485 | if (!err) { | |||
| 486 | if (!cfg.raw_binary) | |||
| 487 | show_temp_stats(&stats); | |||
| 488 | else | |||
| 489 | d_raw((unsigned char *)&stats, sizeof(stats)); | |||
| 490 | } else if (err > 0) | |||
| 491 | nvme_show_status(err); | |||
| 492 | return err; | |||
| 493 | } | |||
| 494 | ||||
| 495 | struct intel_lat_stats { | |||
| 496 | __u16 maj; | |||
| 497 | __u16 min; | |||
| 498 | __u32 data[1216]; | |||
| 499 | }; | |||
| 500 | ||||
| 501 | struct __packed__attribute__((__packed__)) optane_lat_stats { | |||
| 502 | __u16 maj; | |||
| 503 | __u16 min; | |||
| 504 | __u64 data[9]; | |||
| 505 | }; | |||
| 506 | ||||
| 507 | #define MEDIA_MAJOR_IDX0 0 | |||
| 508 | #define MEDIA_MINOR_IDX1 1 | |||
| 509 | #define MEDIA_MAX_LEN2 2 | |||
| 510 | #define OPTANE_V1000_BUCKET_LEN8 8 | |||
| 511 | #define OPTANE_V1000_BUCKET_LEN8 8 | |||
| 512 | #define NAND_LAT_STATS_LEN4868 4868 | |||
| 513 | ||||
| 514 | ||||
| 515 | static struct intel_lat_stats stats; | |||
| 516 | static struct optane_lat_stats v1000_stats; | |||
| 517 | ||||
| 518 | struct v1000_thresholds { | |||
| 519 | __u32 read[OPTANE_V1000_BUCKET_LEN8]; | |||
| 520 | __u32 write[OPTANE_V1000_BUCKET_LEN8]; | |||
| 521 | }; | |||
| 522 | ||||
| 523 | static struct v1000_thresholds v1000_bucket; | |||
| 524 | static __u16 media_version[MEDIA_MAX_LEN2] = {0}; | |||
| 525 | ||||
| 526 | enum FormatUnit { | |||
| 527 | US, | |||
| 528 | MS, | |||
| 529 | S | |||
| 530 | }; | |||
| 531 | ||||
| 532 | /* | |||
| 533 | * COL_WIDTH controls width of columns in human-readable output. | |||
| 534 | * BUFSIZE is for local temp char[] | |||
| 535 | * US_IN_S and US_IN_MS are for unit conversions when printing. | |||
| 536 | */ | |||
| 537 | #define COL_WIDTH12 12 | |||
| 538 | #define BUFSIZE10 10 | |||
| 539 | #define US_IN_S1000000 1000000 | |||
| 540 | #define US_IN_MS1000 1000 | |||
| 541 | ||||
| 542 | static enum FormatUnit get_seconds_magnitude(__u32 microseconds) | |||
| 543 | { | |||
| 544 | if (microseconds > US_IN_S1000000) | |||
| 545 | return S; | |||
| 546 | else if (microseconds > US_IN_MS1000) | |||
| 547 | return MS; | |||
| 548 | else | |||
| 549 | return US; | |||
| 550 | } | |||
| 551 | ||||
| 552 | static float convert_seconds(__u32 microseconds) | |||
| 553 | { | |||
| 554 | float divisor = 1.0; | |||
| 555 | ||||
| 556 | if (microseconds > US_IN_S1000000) | |||
| 557 | divisor = US_IN_S1000000; | |||
| 558 | else if (microseconds > US_IN_MS1000) | |||
| 559 | divisor = US_IN_MS1000; | |||
| 560 | return microseconds / divisor; | |||
| 561 | } | |||
| 562 | ||||
| 563 | /* | |||
| 564 | * For control over whether a string will format to +/-INF or | |||
| 565 | * print out ####.##US normally. | |||
| 566 | */ | |||
| 567 | enum inf_bound_type { | |||
| 568 | NEGINF, | |||
| 569 | POSINF, | |||
| 570 | NOINF | |||
| 571 | }; | |||
| 572 | ||||
| 573 | /* | |||
| 574 | * Edge buckets may have range [#s, inf) or (-inf, #US] in some | |||
| 575 | * latency statistics formats. | |||
| 576 | * Passing in NEGINF to POSINF to bound_type overrides the string to | |||
| 577 | * either of "-INF" or "+INF", respectively. | |||
| 578 | */ | |||
| 579 | static void set_unit_string(char *buffer, __u32 microseconds, | |||
| 580 | enum FormatUnit unit, | |||
| 581 | enum inf_bound_type bound_type) | |||
| 582 | { | |||
| 583 | char *string; | |||
| 584 | ||||
| 585 | if (bound_type != NOINF) { | |||
| 586 | snprintf(buffer, BUFSIZE10, "%s", bound_type ? "+INF" : "-INF"); | |||
| 587 | return; | |||
| 588 | } | |||
| 589 | ||||
| 590 | switch (unit) { | |||
| 591 | case US: | |||
| 592 | string = "us"; | |||
| 593 | break; | |||
| 594 | case MS: | |||
| 595 | string = "ms"; | |||
| 596 | break; | |||
| 597 | case S: | |||
| 598 | string = "s"; | |||
| 599 | break; | |||
| 600 | default: | |||
| 601 | string = "_s"; | |||
| 602 | break; | |||
| 603 | } | |||
| 604 | ||||
| 605 | snprintf(buffer, BUFSIZE10, "%4.2f%s", convert_seconds(microseconds), | |||
| 606 | string); | |||
| 607 | } | |||
| 608 | ||||
| 609 | static void init_buffer(char *buffer, size_t size) | |||
| 610 | { | |||
| 611 | size_t i; | |||
| 612 | ||||
| 613 | for (i = 0; i < size; i++) | |||
| 614 | buffer[i] = i + '0'; | |||
| 615 | } | |||
| 616 | ||||
| 617 | static void show_lat_stats_bucket(struct intel_lat_stats *stats, | |||
| 618 | __u32 lower_us, enum inf_bound_type start_type, | |||
| 619 | __u32 upper_us, enum inf_bound_type end_type, int i) | |||
| 620 | { | |||
| 621 | enum FormatUnit fu = S; | |||
| 622 | char buffer[BUFSIZE10]; | |||
| 623 | ||||
| 624 | init_buffer(buffer, BUFSIZE10); | |||
| 625 | printf("%-*d", COL_WIDTH12, i); | |||
| 626 | ||||
| 627 | fu = get_seconds_magnitude(lower_us); | |||
| 628 | set_unit_string(buffer, lower_us, fu, start_type); | |||
| 629 | printf("%-*s", COL_WIDTH12, buffer); | |||
| 630 | ||||
| 631 | fu = get_seconds_magnitude(upper_us); | |||
| 632 | set_unit_string(buffer, upper_us, fu, end_type); | |||
| 633 | printf("%-*s", COL_WIDTH12, buffer); | |||
| 634 | ||||
| 635 | printf("%-*d\n", COL_WIDTH12, stats->data[i]); | |||
| 636 | } | |||
| 637 | ||||
| 638 | static void show_optane_lat_stats_bucket(struct optane_lat_stats *stats, | |||
| 639 | __u32 lower_us, enum inf_bound_type start_type, | |||
| 640 | __u32 upper_us, enum inf_bound_type end_type, int i) | |||
| 641 | { | |||
| 642 | enum FormatUnit fu = S; | |||
| 643 | char buffer[BUFSIZE10]; | |||
| 644 | ||||
| 645 | init_buffer(buffer, BUFSIZE10); | |||
| 646 | printf("%-*d", COL_WIDTH12, i); | |||
| 647 | ||||
| 648 | fu = get_seconds_magnitude(lower_us); | |||
| 649 | set_unit_string(buffer, lower_us, fu, start_type); | |||
| 650 | printf("%-*s", COL_WIDTH12, buffer); | |||
| 651 | ||||
| 652 | fu = get_seconds_magnitude(upper_us); | |||
| 653 | set_unit_string(buffer, upper_us, fu, end_type); | |||
| 654 | printf("%-*s", COL_WIDTH12, buffer); | |||
| 655 | ||||
| 656 | printf("%-*lu\n", COL_WIDTH12, (unsigned long)stats->data[i]); | |||
| 657 | } | |||
| 658 | ||||
| 659 | ||||
| 660 | static void show_lat_stats_linear(struct intel_lat_stats *stats, | |||
| 661 | __u32 start_offset, __u32 end_offset, __u32 bytes_per, | |||
| 662 | __u32 us_step, bool_Bool nonzero_print) | |||
| 663 | { | |||
| 664 | for (int i = (start_offset / bytes_per) - 1; | |||
| 665 | i < end_offset / bytes_per; i++) { | |||
| 666 | if (nonzero_print && stats->data[i] == 0) | |||
| 667 | continue; | |||
| 668 | show_lat_stats_bucket(stats, us_step * i, NOINF, | |||
| 669 | us_step * (i + 1), NOINF, i); | |||
| 670 | } | |||
| 671 | } | |||
| 672 | ||||
| 673 | /* | |||
| 674 | * For 4.0-4.5 revision. | |||
| 675 | */ | |||
| 676 | #define LATENCY_STATS_V4_BASE_BITS6 6 | |||
| 677 | #define LATENCY_STATS_V4_BASE_VAL(1 << 6) (1 << LATENCY_STATS_V4_BASE_BITS6) | |||
| 678 | ||||
| 679 | static int lat_stats_log_scale(int i) | |||
| 680 | { | |||
| 681 | // if (i < 128) | |||
| 682 | if (i < (LATENCY_STATS_V4_BASE_VAL(1 << 6) << 1)) | |||
| 683 | return i; | |||
| 684 | ||||
| 685 | int error_bits = (i >> LATENCY_STATS_V4_BASE_BITS6) - 1; | |||
| 686 | int base = 1 << (error_bits + LATENCY_STATS_V4_BASE_BITS6); | |||
| 687 | int k = i % LATENCY_STATS_V4_BASE_VAL(1 << 6); | |||
| 688 | ||||
| 689 | return base + ((k + 0.5) * (1 << error_bits)); | |||
| 690 | } | |||
| 691 | ||||
| 692 | /* | |||
| 693 | * Creates a subroot in the following manner: | |||
| 694 | * { | |||
| 695 | * "latstats" : { | |||
| 696 | * "type" : "write" or "read", | |||
| 697 | * "values" : { | |||
| 698 | */ | |||
| 699 | static void lat_stats_make_json_root( | |||
| 700 | struct json_object *root, struct json_object *bucket_list, | |||
| 701 | int write) | |||
| 702 | { | |||
| 703 | struct json_object *subroot = json_create_object()json_object_new_object(); | |||
| 704 | ||||
| 705 | json_object_add_value_object(root, "latstats", subroot)json_object_object_add(root, "latstats", subroot); | |||
| 706 | json_object_add_value_string(subroot, "type", write ? "write" : "read"); | |||
| 707 | json_object_add_value_object(subroot, "values", bucket_list)json_object_object_add(subroot, "values", bucket_list); | |||
| 708 | } | |||
| 709 | ||||
| 710 | /* | |||
| 711 | * Creates a bucket under the "values" json_object. Format is: | |||
| 712 | * "values" : { | |||
| 713 | * "bucket" : { | |||
| 714 | * "id" : #, | |||
| 715 | * "start" : string, | |||
| 716 | * "end" : string, | |||
| 717 | * "value" : 0, | |||
| 718 | * }, | |||
| 719 | */ | |||
| 720 | static void json_add_bucket(struct intel_lat_stats *stats, | |||
| 721 | struct json_object *bucket_list, __u32 id, | |||
| 722 | __u32 lower_us, enum inf_bound_type start_type, | |||
| 723 | __u32 upper_us, enum inf_bound_type end_type, __u32 val) | |||
| 724 | { | |||
| 725 | char buffer[BUFSIZE10]; | |||
| 726 | struct json_object *bucket = json_create_object()json_object_new_object(); | |||
| 727 | ||||
| 728 | init_buffer(buffer, BUFSIZE10); | |||
| 729 | ||||
| 730 | json_object_array_add(bucket_list, bucket); | |||
| 731 | json_object_add_value_int(bucket, "id", id)json_object_object_add(bucket, "id", json_object_new_int(id)); | |||
| 732 | ||||
| 733 | set_unit_string(buffer, lower_us, | |||
| 734 | get_seconds_magnitude(lower_us), start_type); | |||
| 735 | json_object_add_value_string(bucket, "start", buffer); | |||
| 736 | ||||
| 737 | set_unit_string(buffer, upper_us, | |||
| 738 | get_seconds_magnitude(upper_us), end_type); | |||
| 739 | json_object_add_value_string(bucket, "end", buffer); | |||
| 740 | ||||
| 741 | json_object_add_value_int(bucket, "value", val)json_object_object_add(bucket, "value", json_object_new_int(val )); | |||
| 742 | } | |||
| 743 | ||||
| 744 | static void json_add_bucket_optane(struct json_object *bucket_list, __u32 id, | |||
| 745 | __u32 lower_us, enum inf_bound_type start_type, | |||
| 746 | __u32 upper_us, enum inf_bound_type end_type, __u32 val) | |||
| 747 | { | |||
| 748 | char buffer[BUFSIZE10]; | |||
| 749 | struct json_object *bucket = json_create_object()json_object_new_object(); | |||
| 750 | ||||
| 751 | init_buffer(buffer, BUFSIZE10); | |||
| 752 | ||||
| 753 | json_object_array_add(bucket_list, bucket); | |||
| 754 | json_object_add_value_int(bucket, "id", id)json_object_object_add(bucket, "id", json_object_new_int(id)); | |||
| 755 | ||||
| 756 | set_unit_string(buffer, lower_us, | |||
| 757 | get_seconds_magnitude(lower_us), start_type); | |||
| 758 | json_object_add_value_string(bucket, "start", buffer); | |||
| 759 | ||||
| 760 | set_unit_string(buffer, upper_us, | |||
| 761 | get_seconds_magnitude(upper_us), end_type); | |||
| 762 | json_object_add_value_string(bucket, "end", buffer); | |||
| 763 | ||||
| 764 | json_object_add_value_uint(bucket, "value", val)json_object_object_add(bucket, "value", json_object_new_uint64 (val)); | |||
| 765 | ||||
| 766 | } | |||
| 767 | ||||
| 768 | static void json_lat_stats_linear(struct intel_lat_stats *stats, | |||
| 769 | struct json_object *bucket_list, __u32 start_offset, | |||
| 770 | __u32 end_offset, __u32 bytes_per, | |||
| 771 | __u32 us_step, bool_Bool nonzero_print) | |||
| 772 | { | |||
| 773 | for (int i = (start_offset / bytes_per) - 1; | |||
| 774 | i < end_offset / bytes_per; i++) { | |||
| 775 | if (nonzero_print && stats->data[i] == 0) | |||
| 776 | continue; | |||
| 777 | ||||
| 778 | json_add_bucket(stats, bucket_list, | |||
| 779 | i, us_step * i, NOINF, us_step * (i + 1), | |||
| 780 | NOINF, stats->data[i]); | |||
| 781 | } | |||
| 782 | } | |||
| 783 | ||||
| 784 | static void json_lat_stats_3_0(struct intel_lat_stats *stats, int write) | |||
| 785 | { | |||
| 786 | struct json_object *root = json_create_object()json_object_new_object(); | |||
| 787 | struct json_object *bucket_list = json_object_new_array(); | |||
| 788 | ||||
| 789 | lat_stats_make_json_root(root, bucket_list, write); | |||
| 790 | ||||
| 791 | json_lat_stats_linear(stats, bucket_list, 4, 131, 4, 32, false0); | |||
| 792 | json_lat_stats_linear(stats, bucket_list, 132, 255, 4, 1024, false0); | |||
| 793 | json_lat_stats_linear(stats, bucket_list, 256, 379, 4, 32768, false0); | |||
| 794 | json_lat_stats_linear(stats, bucket_list, 380, 383, 4, 32, true1); | |||
| 795 | json_lat_stats_linear(stats, bucket_list, 384, 387, 4, 32, true1); | |||
| 796 | json_lat_stats_linear(stats, bucket_list, 388, 391, 4, 32, true1); | |||
| 797 | ||||
| 798 | json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 << 1) | (1 << 4))); | |||
| 799 | printf("\n"); | |||
| 800 | json_free_object(root)json_object_put(root); | |||
| 801 | } | |||
| 802 | ||||
| 803 | static void json_lat_stats_4_0(struct intel_lat_stats *stats, int write) | |||
| 804 | { | |||
| 805 | struct json_object *root = json_create_object()json_object_new_object(); | |||
| 806 | struct json_object *bucket_list = json_object_new_array(); | |||
| 807 | ||||
| 808 | lat_stats_make_json_root(root, bucket_list, write); | |||
| 809 | ||||
| 810 | __u32 lower_us = 0; | |||
| 811 | __u32 upper_us = 1; | |||
| 812 | bool_Bool end = false0; | |||
| 813 | int max = 1216; | |||
| 814 | ||||
| 815 | for (int i = 0; i < max; i++) { | |||
| 816 | lower_us = lat_stats_log_scale(i); | |||
| 817 | if (i >= max - 1) | |||
| 818 | end = true1; | |||
| 819 | else | |||
| 820 | upper_us = lat_stats_log_scale(i + 1); | |||
| 821 | ||||
| 822 | json_add_bucket(stats, bucket_list, i, | |||
| 823 | lower_us, NOINF, upper_us, | |||
| 824 | end ? POSINF : NOINF, stats->data[i]); | |||
| 825 | } | |||
| 826 | json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 << 1) | (1 << 4))); | |||
| 827 | printf("\n"); | |||
| 828 | json_free_object(root)json_object_put(root); | |||
| 829 | } | |||
| 830 | ||||
| 831 | static void show_lat_stats_3_0(struct intel_lat_stats *stats) | |||
| 832 | { | |||
| 833 | show_lat_stats_linear(stats, 4, 131, 4, 32, false0); | |||
| 834 | show_lat_stats_linear(stats, 132, 255, 4, 1024, false0); | |||
| 835 | show_lat_stats_linear(stats, 256, 379, 4, 32768, false0); | |||
| 836 | show_lat_stats_linear(stats, 380, 383, 4, 32, true1); | |||
| 837 | show_lat_stats_linear(stats, 384, 387, 4, 32, true1); | |||
| 838 | show_lat_stats_linear(stats, 388, 391, 4, 32, true1); | |||
| 839 | } | |||
| 840 | ||||
| 841 | static void show_lat_stats_4_0(struct intel_lat_stats *stats) | |||
| 842 | { | |||
| 843 | int lower_us = 0; | |||
| 844 | int upper_us = 1; | |||
| 845 | bool_Bool end = false0; | |||
| 846 | int max = 1216; | |||
| 847 | ||||
| 848 | for (int i = 0; i < max; i++) { | |||
| 849 | lower_us = lat_stats_log_scale(i); | |||
| 850 | if (i >= max - 1) | |||
| 851 | end = true1; | |||
| 852 | else | |||
| 853 | upper_us = lat_stats_log_scale(i + 1); | |||
| 854 | ||||
| 855 | show_lat_stats_bucket(stats, lower_us, NOINF, | |||
| 856 | upper_us, end ? POSINF : NOINF, i); | |||
| 857 | } | |||
| 858 | } | |||
| 859 | ||||
| 860 | static void json_lat_stats_v1000_0(struct optane_lat_stats *stats, int write) | |||
| 861 | { | |||
| 862 | int i; | |||
| 863 | struct json_object *root = json_create_object()json_object_new_object(); | |||
| 864 | struct json_object *bucket_list = json_object_new_array(); | |||
| 865 | ||||
| 866 | lat_stats_make_json_root(root, bucket_list, write); | |||
| 867 | ||||
| 868 | if (write) { | |||
| 869 | for (i = 0; i < OPTANE_V1000_BUCKET_LEN8 - 1; i++) | |||
| 870 | json_add_bucket_optane(bucket_list, i, | |||
| 871 | v1000_bucket.write[i], NOINF, | |||
| 872 | v1000_bucket.write[i + 1] - 1, | |||
| 873 | NOINF, | |||
| 874 | stats->data[i]); | |||
| 875 | ||||
| 876 | json_add_bucket_optane(bucket_list, | |||
| 877 | OPTANE_V1000_BUCKET_LEN8 - 1, | |||
| 878 | v1000_bucket.write[i], | |||
| 879 | NOINF, | |||
| 880 | v1000_bucket.write[i], | |||
| 881 | POSINF, stats->data[i]); | |||
| 882 | } else { | |||
| 883 | for (i = 0; i < OPTANE_V1000_BUCKET_LEN8 - 1; i++) | |||
| 884 | json_add_bucket_optane(bucket_list, i, | |||
| 885 | v1000_bucket.read[i], NOINF, | |||
| 886 | v1000_bucket.read[i + 1] - 1, | |||
| 887 | NOINF, | |||
| 888 | stats->data[i]); | |||
| 889 | ||||
| 890 | json_add_bucket_optane(bucket_list, | |||
| 891 | OPTANE_V1000_BUCKET_LEN8 - 1, | |||
| 892 | v1000_bucket.read[i], | |||
| 893 | NOINF, | |||
| 894 | v1000_bucket.read[i], | |||
| 895 | POSINF, stats->data[i]); | |||
| 896 | } | |||
| 897 | ||||
| 898 | struct json_object *subroot = json_create_object()json_object_new_object(); | |||
| 899 | ||||
| 900 | json_object_add_value_object(root, "Average latency since last reset", subroot)json_object_object_add(root, "Average latency since last reset" , subroot); | |||
| 901 | ||||
| 902 | json_object_add_value_uint(subroot, "value in us", stats->data[8])json_object_object_add(subroot, "value in us", json_object_new_uint64 (stats->data[8])); | |||
| 903 | ||||
| 904 | json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 << 1) | (1 << 4))); | |||
| 905 | printf("\n"); | |||
| 906 | json_free_object(root)json_object_put(root); | |||
| 907 | ||||
| 908 | } | |||
| 909 | ||||
| 910 | static void show_lat_stats_v1000_0(struct optane_lat_stats *stats, int write) | |||
| 911 | { | |||
| 912 | int i; | |||
| 913 | ||||
| 914 | if (write) { | |||
| 915 | for (i = 0; i < OPTANE_V1000_BUCKET_LEN8 - 1; i++) | |||
| 916 | show_optane_lat_stats_bucket(stats, | |||
| 917 | v1000_bucket.write[i], | |||
| 918 | NOINF, | |||
| 919 | v1000_bucket.write[i + 1] - 1, | |||
| 920 | NOINF, i); | |||
| 921 | ||||
| 922 | show_optane_lat_stats_bucket(stats, v1000_bucket.write[i], | |||
| 923 | NOINF, v1000_bucket.write[i], | |||
| 924 | POSINF, i); | |||
| 925 | } else { | |||
| 926 | for (i = 0; i < OPTANE_V1000_BUCKET_LEN8 - 1; i++) | |||
| 927 | show_optane_lat_stats_bucket(stats, | |||
| 928 | v1000_bucket.read[i], | |||
| 929 | NOINF, | |||
| 930 | v1000_bucket.read[i + 1] - 1, | |||
| 931 | NOINF, i); | |||
| 932 | ||||
| 933 | show_optane_lat_stats_bucket(stats, v1000_bucket.read[i], | |||
| 934 | NOINF, v1000_bucket.read[i], | |||
| 935 | POSINF, i); | |||
| 936 | } | |||
| 937 | ||||
| 938 | printf("Average latency since last reset: %lu us\n", (unsigned long)stats->data[8]); | |||
| 939 | ||||
| 940 | } | |||
| 941 | ||||
| 942 | static void json_lat_stats(int write) | |||
| 943 | { | |||
| 944 | switch (media_version[MEDIA_MAJOR_IDX0]) { | |||
| 945 | case 3: | |||
| 946 | json_lat_stats_3_0(&stats, write); | |||
| 947 | break; | |||
| 948 | case 4: | |||
| 949 | switch (media_version[MEDIA_MINOR_IDX1]) { | |||
| 950 | case 0: | |||
| 951 | case 1: | |||
| 952 | case 2: | |||
| 953 | case 3: | |||
| 954 | case 4: | |||
| 955 | case 5: | |||
| 956 | json_lat_stats_4_0(&stats, write); | |||
| 957 | break; | |||
| 958 | default: | |||
| 959 | nvme_show_error("Unsupported minor revision (%u.%u)",nvme_show_message(1, "Unsupported minor revision (%u.%u)", stats .maj, stats.min) | |||
| 960 | stats.maj, stats.min)nvme_show_message(1, "Unsupported minor revision (%u.%u)", stats .maj, stats.min); | |||
| 961 | break; | |||
| 962 | } | |||
| 963 | break; | |||
| 964 | case 1000: | |||
| 965 | switch (media_version[MEDIA_MINOR_IDX1]) { | |||
| 966 | case 0: | |||
| 967 | json_lat_stats_v1000_0(&v1000_stats, write); | |||
| 968 | break; | |||
| 969 | default: | |||
| 970 | nvme_show_error("Unsupported minor revision (%u.%u)",nvme_show_message(1, "Unsupported minor revision (%u.%u)", stats .maj, stats.min) | |||
| 971 | stats.maj, stats.min)nvme_show_message(1, "Unsupported minor revision (%u.%u)", stats .maj, stats.min); | |||
| 972 | break; | |||
| 973 | } | |||
| 974 | break; | |||
| 975 | default: | |||
| 976 | nvme_show_error("Unsupported revision (%u.%u)",nvme_show_message(1, "Unsupported revision (%u.%u)", stats.maj , stats.min) | |||
| 977 | stats.maj, stats.min)nvme_show_message(1, "Unsupported revision (%u.%u)", stats.maj , stats.min); | |||
| 978 | break; | |||
| 979 | } | |||
| 980 | printf("\n"); | |||
| 981 | } | |||
| 982 | ||||
| 983 | static void | |||
| 984 | print_dash_separator(int count) | |||
| 985 | { | |||
| 986 | for (int i = 0; i < count; i++) | |||
| 987 | putchar('-'); | |||
| 988 | putchar('\n'); | |||
| 989 | } | |||
| 990 | ||||
| 991 | static void show_lat_stats(int write) | |||
| 992 | { | |||
| 993 | static const int separator_length = 50; | |||
| 994 | ||||
| 995 | printf("Intel IO %s Command Latency Statistics\n", | |||
| 996 | write ? "Write" : "Read"); | |||
| 997 | printf("Major Revision : %u\nMinor Revision : %u\n", | |||
| 998 | media_version[MEDIA_MAJOR_IDX0], media_version[MEDIA_MINOR_IDX1]); | |||
| 999 | print_dash_separator(separator_length); | |||
| 1000 | printf("%-12s%-12s%-12s%-20s\n", "Bucket", "Start", "End", "Value"); | |||
| 1001 | print_dash_separator(separator_length); | |||
| 1002 | ||||
| 1003 | switch (media_version[MEDIA_MAJOR_IDX0]) { | |||
| 1004 | case 3: | |||
| 1005 | show_lat_stats_3_0(&stats); | |||
| 1006 | break; | |||
| 1007 | case 4: | |||
| 1008 | switch (media_version[MEDIA_MINOR_IDX1]) { | |||
| 1009 | case 0: | |||
| 1010 | case 1: | |||
| 1011 | case 2: | |||
| 1012 | case 3: | |||
| 1013 | case 4: | |||
| 1014 | case 5: | |||
| 1015 | case 6: | |||
| 1016 | show_lat_stats_4_0(&stats); | |||
| 1017 | break; | |||
| 1018 | default: | |||
| 1019 | nvme_show_error("Unsupported minor revision (%u.%u)",nvme_show_message(1, "Unsupported minor revision (%u.%u)", stats .maj, stats.min) | |||
| 1020 | stats.maj, stats.min)nvme_show_message(1, "Unsupported minor revision (%u.%u)", stats .maj, stats.min); | |||
| 1021 | break; | |||
| 1022 | } | |||
| 1023 | break; | |||
| 1024 | case 1000: | |||
| 1025 | switch (media_version[MEDIA_MINOR_IDX1]) { | |||
| 1026 | case 0: | |||
| 1027 | show_lat_stats_v1000_0(&v1000_stats, write); | |||
| 1028 | break; | |||
| 1029 | default: | |||
| 1030 | nvme_show_error("Unsupported minor revision (%u.%u)",nvme_show_message(1, "Unsupported minor revision (%u.%u)", stats .maj, stats.min) | |||
| 1031 | stats.maj, stats.min)nvme_show_message(1, "Unsupported minor revision (%u.%u)", stats .maj, stats.min); | |||
| 1032 | break; | |||
| 1033 | } | |||
| 1034 | break; | |||
| 1035 | default: | |||
| 1036 | nvme_show_error("Unsupported revision (%u.%u)",nvme_show_message(1, "Unsupported revision (%u.%u)", stats.maj , stats.min) | |||
| 1037 | stats.maj, stats.min)nvme_show_message(1, "Unsupported revision (%u.%u)", stats.maj , stats.min); | |||
| 1038 | break; | |||
| 1039 | } | |||
| 1040 | } | |||
| 1041 | ||||
| 1042 | static int get_lat_stats_log(int argc, char **argv, struct command *acmd, struct plugin *plugin) | |||
| 1043 | { | |||
| 1044 | __u8 data[NAND_LAT_STATS_LEN4868]; | |||
| 1045 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 1046 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 1047 | nvme_print_flags_t flags; | |||
| 1048 | int err; | |||
| 1049 | ||||
| 1050 | const char *desc = "Get Intel Latency Statistics log and show it."; | |||
| 1051 | const char *raw = "Dump output in binary format"; | |||
| 1052 | #ifdef CONFIG_JSONC | |||
| 1053 | const char *json = "Dump output in json format"; | |||
| 1054 | #endif /* CONFIG_JSONC */ | |||
| 1055 | const char *write = "Get write statistics (read default)"; | |||
| 1056 | ||||
| 1057 | struct config { | |||
| 1058 | bool_Bool raw_binary; | |||
| 1059 | bool_Bool json; | |||
| 1060 | bool_Bool write; | |||
| 1061 | }; | |||
| 1062 | ||||
| 1063 | struct config cfg = { | |||
| 1064 | }; | |||
| 1065 | ||||
| 1066 | OPT_ARGS(opts)struct argconfig_commandline_options opts[] = { | |||
| 1067 | OPT_FLAG("write", 'w', &cfg.write, write){"write", 'w', ((void*)0), CFG_FLAG, _Generic(&cfg.write, _Bool * : (&cfg.write)), ARGCONFIG_ARG_NONE, write, 0, }, | |||
| 1068 | OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw){"raw-binary", 'b', ((void*)0), CFG_FLAG, _Generic(&cfg.raw_binary , _Bool * : (&cfg.raw_binary)), ARGCONFIG_ARG_NONE, raw, 0 , }, | |||
| 1069 | OPT_FLAG_JSON("json", 'j', &cfg.json, json){"json", 'j', ((void*)0), CFG_FLAG, _Generic(&cfg.json, _Bool * : (&cfg.json)), ARGCONFIG_ARG_NONE, json, 0, }, | |||
| 1070 | OPT_END(){ ((void*)0) } | |||
| 1071 | }; | |||
| 1072 | ||||
| 1073 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 1074 | if (err) | |||
| 1075 | return err; | |||
| 1076 | ||||
| 1077 | flags = nvme_args.output_flags; | |||
| 1078 | ||||
| 1079 | /* For optate, latency stats are deleted every time their LID is pulled. | |||
| 1080 | * Therefore, we query the longest lat_stats log page first. | |||
| 1081 | */ | |||
| 1082 | err = nvme_get_log_simple(hdl, cfg.write ? 0xc2 : 0xc1, | |||
| 1083 | &data, sizeof(data)); | |||
| 1084 | ||||
| 1085 | media_version[0] = (data[1] << 8) | data[0]; | |||
| 1086 | media_version[1] = (data[3] << 8) | data[2]; | |||
| 1087 | ||||
| 1088 | if (err) | |||
| 1089 | return err; | |||
| 1090 | ||||
| 1091 | if (media_version[0] == 1000) { | |||
| 1092 | __u32 thresholds[OPTANE_V1000_BUCKET_LEN8] = {0}; | |||
| 1093 | __u64 result; | |||
| 1094 | ||||
| 1095 | err = nvme_get_features(hdl, 0, 0xf7, | |||
| 1096 | 0, cfg.write ? 0x1 : 0x0, | |||
| 1097 | 0, thresholds, sizeof(thresholds), | |||
| 1098 | &result); | |||
| 1099 | if (err) { | |||
| 1100 | nvme_show_error("Querying thresholds failed. ")nvme_show_message(1, "Querying thresholds failed. "); | |||
| 1101 | nvme_show_status(err); | |||
| 1102 | return err; | |||
| 1103 | } | |||
| 1104 | ||||
| 1105 | /* Update bucket thresholds to be printed */ | |||
| 1106 | if (cfg.write) { | |||
| 1107 | for (int i = 0; i < OPTANE_V1000_BUCKET_LEN8; i++) | |||
| 1108 | v1000_bucket.write[i] = thresholds[i]; | |||
| 1109 | } else { | |||
| 1110 | for (int i = 0; i < OPTANE_V1000_BUCKET_LEN8; i++) | |||
| 1111 | v1000_bucket.read[i] = thresholds[i]; | |||
| 1112 | ||||
| 1113 | } | |||
| 1114 | ||||
| 1115 | /* Move counter values to optate stats struct which has a | |||
| 1116 | * smaller size | |||
| 1117 | */ | |||
| 1118 | memcpy(&v1000_stats, (struct optane_lat_stats *)data, | |||
| 1119 | sizeof(struct optane_lat_stats)); | |||
| 1120 | } else { | |||
| 1121 | memcpy(&stats, (struct intel_lat_stats *)data, | |||
| 1122 | sizeof(struct intel_lat_stats)); | |||
| 1123 | } | |||
| 1124 | ||||
| 1125 | if (flags & JSON || cfg.json) | |||
| 1126 | json_lat_stats(cfg.write); | |||
| 1127 | else if (!cfg.raw_binary) | |||
| 1128 | show_lat_stats(cfg.write); | |||
| 1129 | else { | |||
| 1130 | if (media_version[0] == 1000) | |||
| 1131 | d_raw((unsigned char *)&v1000_stats, | |||
| 1132 | sizeof(v1000_stats)); | |||
| 1133 | else | |||
| 1134 | d_raw((unsigned char *)&stats, | |||
| 1135 | sizeof(stats)); | |||
| 1136 | } | |||
| 1137 | ||||
| 1138 | return 0; | |||
| 1139 | } | |||
| 1140 | ||||
| 1141 | struct intel_assert_dump { | |||
| 1142 | __u32 coreoffset; | |||
| 1143 | __u32 assertsize; | |||
| 1144 | __u8 assertdumptype; | |||
| 1145 | __u8 assertvalid; | |||
| 1146 | __u8 reserved[2]; | |||
| 1147 | }; | |||
| 1148 | ||||
| 1149 | struct intel_event_dump { | |||
| 1150 | __u32 numeventdumps; | |||
| 1151 | __u32 coresize; | |||
| 1152 | __u32 coreoffset; | |||
| 1153 | __u32 eventidoffset[16]; | |||
| 1154 | __u8 eventIdValidity[16]; | |||
| 1155 | }; | |||
| 1156 | ||||
| 1157 | struct intel_vu_version { | |||
| 1158 | __u16 major; | |||
| 1159 | __u16 minor; | |||
| 1160 | }; | |||
| 1161 | ||||
| 1162 | struct intel_event_header { | |||
| 1163 | __u32 eventidsize; | |||
| 1164 | struct intel_event_dump edumps[0]; | |||
| 1165 | }; | |||
| 1166 | ||||
| 1167 | struct intel_vu_log { | |||
| 1168 | struct intel_vu_version ver; | |||
| 1169 | __u32 header; | |||
| 1170 | __u32 size; | |||
| 1171 | __u32 numcores; | |||
| 1172 | __u8 reserved[4080]; | |||
| 1173 | }; | |||
| 1174 | ||||
| 1175 | struct intel_vu_nlog { | |||
| 1176 | struct intel_vu_version ver; | |||
| 1177 | __u32 logselect; | |||
| 1178 | __u32 totalnlogs; | |||
| 1179 | __u32 nlognum; | |||
| 1180 | __u32 nlogname; | |||
| 1181 | __u32 nlogbytesize; | |||
| 1182 | __u32 nlogprimarybuffsize; | |||
| 1183 | __u32 tickspersecond; | |||
| 1184 | __u32 corecount; | |||
| 1185 | __u32 nlogpausestatus; | |||
| 1186 | __u32 selectoffsetref; | |||
| 1187 | __u32 selectnlogpause; | |||
| 1188 | __u32 selectaddedoffset; | |||
| 1189 | __u32 nlogbufnum; | |||
| 1190 | __u32 nlogbufnummax; | |||
| 1191 | __u32 coreselected; | |||
| 1192 | __u32 reserved[3]; | |||
| 1193 | }; | |||
| 1194 | ||||
| 1195 | struct intel_cd_log { | |||
| 1196 | union { | |||
| 1197 | struct { | |||
| 1198 | __u32 selectLog : 3; | |||
| 1199 | __u32 selectCore : 2; | |||
| 1200 | __u32 selectNlog : 8; | |||
| 1201 | __u8 selectOffsetRef : 1; | |||
| 1202 | __u32 selectNlogPause : 2; | |||
| 1203 | __u32 reserved2 : 16; | |||
| 1204 | } fields; | |||
| 1205 | __u32 entireDword; | |||
| 1206 | } u; | |||
| 1207 | }; | |||
| 1208 | ||||
| 1209 | static void print_intel_nlog(struct intel_vu_nlog *intel_nlog) | |||
| 1210 | { | |||
| 1211 | printf("Version Major %u\n" | |||
| 1212 | "Version Minor %u\n" | |||
| 1213 | "Log_select %u\n" | |||
| 1214 | "totalnlogs %u\n" | |||
| 1215 | "nlognum %u\n" | |||
| 1216 | "nlogname %u\n" | |||
| 1217 | "nlogbytesze %u\n" | |||
| 1218 | "nlogprimarybuffsize %u\n" | |||
| 1219 | "tickspersecond %u\n" | |||
| 1220 | "corecount %u\n" | |||
| 1221 | "nlogpausestatus %u\n" | |||
| 1222 | "selectoffsetref %u\n" | |||
| 1223 | "selectnlogpause %u\n" | |||
| 1224 | "selectaddedoffset %u\n" | |||
| 1225 | "nlogbufnum %u\n" | |||
| 1226 | "nlogbufnummax %u\n" | |||
| 1227 | "coreselected %u\n", | |||
| 1228 | intel_nlog->ver.major, intel_nlog->ver.minor, | |||
| 1229 | intel_nlog->logselect, intel_nlog->totalnlogs, intel_nlog->nlognum, | |||
| 1230 | intel_nlog->nlogname, intel_nlog->nlogbytesize, | |||
| 1231 | intel_nlog->nlogprimarybuffsize, intel_nlog->tickspersecond, | |||
| 1232 | intel_nlog->corecount, intel_nlog->nlogpausestatus, | |||
| 1233 | intel_nlog->selectoffsetref, intel_nlog->selectnlogpause, | |||
| 1234 | intel_nlog->selectaddedoffset, intel_nlog->nlogbufnum, | |||
| 1235 | intel_nlog->nlogbufnummax, intel_nlog->coreselected); | |||
| 1236 | } | |||
| 1237 | ||||
| 1238 | static int read_entire_cmd(struct libnvme_passthru_cmd *cmd, int total_size, | |||
| 1239 | const size_t max_tfer, int out_fd, | |||
| 1240 | struct libnvme_transport_handle *hdl, __u8 *buf) | |||
| 1241 | { | |||
| 1242 | int err = 0; | |||
| 1243 | size_t dword_tfer = 0; | |||
| 1244 | ||||
| 1245 | dword_tfer = min(max_tfer, total_size)({ typeof(max_tfer) _a = (max_tfer); typeof(total_size) _b = ( total_size); do { } while (0); _a < _b ? _a : _b; }); | |||
| 1246 | while (total_size > 0) { | |||
| 1247 | err = libnvme_exec_admin_passthru(hdl, cmd); | |||
| 1248 | if (err) { | |||
| 1249 | nvme_show_error(nvme_show_message(1, "failed on cmd.data_len %u cmd.cdw13 %u cmd.cdw12 %x cmd.cdw10 %u err %x remaining size %d\n" , cmd->data_len, cmd->cdw13, cmd->cdw12, cmd->cdw10 , err, total_size) | |||
| 1250 | "failed on cmd.data_len %u cmd.cdw13 %u cmd.cdw12 %x cmd.cdw10 %u err %x remaining size %d\n",nvme_show_message(1, "failed on cmd.data_len %u cmd.cdw13 %u cmd.cdw12 %x cmd.cdw10 %u err %x remaining size %d\n" , cmd->data_len, cmd->cdw13, cmd->cdw12, cmd->cdw10 , err, total_size) | |||
| 1251 | cmd->data_len, cmd->cdw13, cmd->cdw12,nvme_show_message(1, "failed on cmd.data_len %u cmd.cdw13 %u cmd.cdw12 %x cmd.cdw10 %u err %x remaining size %d\n" , cmd->data_len, cmd->cdw13, cmd->cdw12, cmd->cdw10 , err, total_size) | |||
| 1252 | cmd->cdw10, err, total_size)nvme_show_message(1, "failed on cmd.data_len %u cmd.cdw13 %u cmd.cdw12 %x cmd.cdw10 %u err %x remaining size %d\n" , cmd->data_len, cmd->cdw13, cmd->cdw12, cmd->cdw10 , err, total_size); | |||
| 1253 | goto out; | |||
| 1254 | } | |||
| 1255 | ||||
| 1256 | if (out_fd > 0) { | |||
| 1257 | err = write(out_fd, buf, cmd->data_len); | |||
| 1258 | if (err < 0) { | |||
| 1259 | nvme_show_perror("write failure"); | |||
| 1260 | goto out; | |||
| 1261 | } | |||
| 1262 | err = 0; | |||
| 1263 | } | |||
| 1264 | total_size -= dword_tfer; | |||
| 1265 | cmd->cdw13 += dword_tfer; | |||
| 1266 | cmd->cdw10 = dword_tfer = min(max_tfer, total_size)({ typeof(max_tfer) _a = (max_tfer); typeof(total_size) _b = ( total_size); do { } while (0); _a < _b ? _a : _b; }); | |||
| 1267 | cmd->data_len = (min(max_tfer, total_size)({ typeof(max_tfer) _a = (max_tfer); typeof(total_size) _b = ( total_size); do { } while (0); _a < _b ? _a : _b; })) * 4; | |||
| 1268 | } | |||
| 1269 | ||||
| 1270 | out: | |||
| 1271 | return err; | |||
| 1272 | } | |||
| 1273 | ||||
| 1274 | static int write_header(__u8 *buf, int fd, size_t amnt) | |||
| 1275 | { | |||
| 1276 | if (write(fd, buf, amnt) < 0) | |||
| 1277 | return 1; | |||
| 1278 | return 0; | |||
| 1279 | } | |||
| 1280 | ||||
| 1281 | static int read_header(struct libnvme_passthru_cmd *cmd, __u8 *buf, | |||
| 1282 | struct libnvme_transport_handle *hdl, __u32 dw12, int nsid) | |||
| 1283 | { | |||
| 1284 | memset(cmd, 0, sizeof(*cmd)); | |||
| 1285 | memset(buf, 0, 4096); | |||
| 1286 | cmd->opcode = 0xd2; | |||
| 1287 | cmd->nsid = nsid; | |||
| 1288 | cmd->cdw10 = 0x400; | |||
| 1289 | cmd->cdw12 = dw12; | |||
| 1290 | cmd->data_len = 0x1000; | |||
| 1291 | cmd->addr = (uintptr_t)(void *)buf; | |||
| 1292 | return read_entire_cmd(cmd, 0x400, 0x400, -1, hdl, buf); | |||
| 1293 | } | |||
| 1294 | ||||
| 1295 | static int setup_file(char *f, size_t size, char *file, | |||
| 1296 | struct libnvme_transport_handle *hdl, int type) | |||
| 1297 | { | |||
| 1298 | struct nvme_id_ctrl ctrl; | |||
| 1299 | struct libnvme_passthru_cmd cmd; | |||
| 1300 | int err = 0, i = sizeof(ctrl.sn) - 1; | |||
| 1301 | ||||
| 1302 | nvme_init_identify_ctrl(&cmd, &ctrl); | |||
| 1303 | err = libnvme_exec_admin_passthru(hdl, &cmd); | |||
| 1304 | if (err) | |||
| 1305 | return err; | |||
| 1306 | ||||
| 1307 | /* Remove trailing spaces from the name */ | |||
| 1308 | while (i && ctrl.sn[i] == ' ') { | |||
| 1309 | ctrl.sn[i] = '\0'; | |||
| 1310 | i--; | |||
| 1311 | } | |||
| 1312 | ||||
| 1313 | snprintf(f, size, "%s_%-.*s.bin", type == 0 ? "Nlog" : | |||
| 1314 | type == 1 ? "EventLog" : "AssertLog", | |||
| 1315 | (int)sizeof(ctrl.sn), ctrl.sn); | |||
| 1316 | return err; | |||
| 1317 | } | |||
| 1318 | ||||
| 1319 | static int get_internal_log_old(__u8 *buf, int output, | |||
| 1320 | struct libnvme_transport_handle *hdl, | |||
| 1321 | struct libnvme_passthru_cmd *cmd) | |||
| 1322 | { | |||
| 1323 | struct intel_vu_log *intel; | |||
| 1324 | int err = 0; | |||
| 1325 | const int dwmax = 0x400; | |||
| 1326 | const int dmamax = 0x1000; | |||
| 1327 | ||||
| 1328 | intel = (struct intel_vu_log *)buf; | |||
| 1329 | ||||
| 1330 | printf("Log major:%d minor:%d header:%d size:%d\n", | |||
| 1331 | intel->ver.major, intel->ver.minor, intel->header, intel->size); | |||
| 1332 | ||||
| 1333 | err = write(output, buf, 0x1000); | |||
| 1334 | if (err < 0) { | |||
| 1335 | nvme_show_perror("write failure"); | |||
| 1336 | goto out; | |||
| 1337 | } | |||
| 1338 | intel->size -= 0x400; | |||
| 1339 | cmd->opcode = 0xd2; | |||
| 1340 | cmd->cdw10 = min(dwmax, intel->size)({ typeof(dwmax) _a = (dwmax); typeof(intel->size) _b = (intel ->size); do { } while (0); _a < _b ? _a : _b; }); | |||
| 1341 | cmd->data_len = min(dmamax, intel->size)({ typeof(dmamax) _a = (dmamax); typeof(intel->size) _b = ( intel->size); do { } while (0); _a < _b ? _a : _b; }); | |||
| 1342 | err = read_entire_cmd(cmd, intel->size, dwmax, output, hdl, buf); | |||
| 1343 | if (err) | |||
| 1344 | goto out; | |||
| 1345 | ||||
| 1346 | err = 0; | |||
| 1347 | out: | |||
| 1348 | return err; | |||
| 1349 | } | |||
| 1350 | ||||
| 1351 | static int get_internal_log(int argc, char **argv, struct command *acmd, | |||
| 1352 | struct plugin *plugin) | |||
| 1353 | { | |||
| 1354 | __u8 buf[0x2000]; | |||
| 1355 | char f[0x100]; | |||
| 1356 | int err, output, i, j, count = 0, core_num = 1; | |||
| 1357 | struct libnvme_passthru_cmd cmd; | |||
| 1358 | struct intel_cd_log cdlog; | |||
| 1359 | struct intel_vu_nlog *intel_nlog = (struct intel_vu_nlog *)buf; | |||
| 1360 | struct intel_assert_dump *ad; | |||
| 1361 | struct intel_event_header *ehdr; | |||
| 1362 | __cleanup_free__attribute__((cleanup(shr_freep))) struct intel_vu_log *intel = NULL((void*)0); | |||
| 1363 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 1364 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 1365 | ||||
| 1366 | const char *desc = "Get Intel Firmware Log and save it."; | |||
| 1367 | const char *log = "Log type: 0, 1, or 2 for nlog, event log, and assert log, respectively."; | |||
| 1368 | const char *core = "Select which region log should come from. -1 for all"; | |||
| 1369 | const char *nlognum = "Select which nlog to read. -1 for all nlogs"; | |||
| 1370 | const char *file = "Output file; defaults to device name provided"; | |||
| 1371 | const char *verbose = "To print out verbose nlog info"; | |||
| 1372 | const char *namespace_id = "Namespace to get logs from"; | |||
| 1373 | ||||
| 1374 | struct config { | |||
| 1375 | __u32 namespace_id; | |||
| 1376 | __u32 log; | |||
| 1377 | int core; | |||
| 1378 | int lnum; | |||
| 1379 | char *file; | |||
| 1380 | bool_Bool verbose; | |||
| 1381 | }; | |||
| 1382 | ||||
| 1383 | struct config cfg = { | |||
| 1384 | .namespace_id = -1, | |||
| 1385 | .file = NULL((void*)0), | |||
| 1386 | .lnum = -1, | |||
| 1387 | .core = -1 | |||
| 1388 | }; | |||
| 1389 | ||||
| 1390 | OPT_ARGS(opts)struct argconfig_commandline_options opts[] = { | |||
| 1391 | OPT_UINT("log", 'l', &cfg.log, log){"log", 'l', "NUM", CFG_POSITIVE, _Generic(&cfg.log, uint32_t * : (&cfg.log)), ARGCONFIG_ARG_REQUIRED, log, 0, }, | |||
| 1392 | OPT_INT("region", 'r', &cfg.core, core){"region", 'r', "NUM", CFG_INT, _Generic(&cfg.core, int * : (&cfg.core)), ARGCONFIG_ARG_REQUIRED, core, 0, }, | |||
| 1393 | OPT_INT("nlognum", 'm', &cfg.lnum, nlognum){"nlognum", 'm', "NUM", CFG_INT, _Generic(&cfg.lnum, int * : (&cfg.lnum)), ARGCONFIG_ARG_REQUIRED, nlognum, 0, }, | |||
| 1394 | OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id,{"namespace-id", 'n', "NUM", CFG_POSITIVE, _Generic(&cfg. namespace_id, uint32_t * : (&cfg.namespace_id)), ARGCONFIG_ARG_REQUIRED , namespace_id, 0, nvme_nsid_values} | |||
| 1395 | nvme_nsid_values){"namespace-id", 'n', "NUM", CFG_POSITIVE, _Generic(&cfg. namespace_id, uint32_t * : (&cfg.namespace_id)), ARGCONFIG_ARG_REQUIRED , namespace_id, 0, nvme_nsid_values}, | |||
| 1396 | OPT_FILE("output-file", 'O', &cfg.file, file){"output-file", 'O', "FILE", CFG_FILE, _Generic(&cfg.file , char ** : (&cfg.file), const char ** : (&cfg.file)) , ARGCONFIG_ARG_REQUIRED, file, 0, }, | |||
| 1397 | OPT_FLAG("verbose-nlog", 'V', &cfg.verbose, verbose){"verbose-nlog", 'V', ((void*)0), CFG_FLAG, _Generic(&cfg .verbose, _Bool * : (&cfg.verbose)), ARGCONFIG_ARG_NONE, verbose , 0, }, | |||
| 1398 | OPT_END(){ ((void*)0) } | |||
| 1399 | }; | |||
| 1400 | ||||
| 1401 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 1402 | if (err) | |||
| ||||
| 1403 | return err; | |||
| 1404 | ||||
| 1405 | intel = malloc(sizeof(*intel)); | |||
| 1406 | if (!intel) | |||
| 1407 | return -ENOMEM12; | |||
| 1408 | ||||
| 1409 | ad = (struct intel_assert_dump *)intel->reserved; | |||
| 1410 | ehdr = (struct intel_event_header *)intel->reserved; | |||
| 1411 | ||||
| 1412 | if (cfg.log > 2 || cfg.core > 4 || cfg.lnum > 255) { | |||
| 1413 | err = -EINVAL22; | |||
| ||||
| 1414 | goto out_free; | |||
| 1415 | } | |||
| 1416 | ||||
| 1417 | if (!cfg.file) { | |||
| 1418 | err = setup_file(f, sizeof(f), cfg.file, hdl, cfg.log); | |||
| 1419 | if (err) | |||
| 1420 | goto out_free; | |||
| 1421 | cfg.file = f; | |||
| 1422 | } | |||
| 1423 | ||||
| 1424 | cdlog.u.entireDword = 0; | |||
| 1425 | ||||
| 1426 | cdlog.u.fields.selectLog = cfg.log; | |||
| 1427 | cdlog.u.fields.selectCore = cfg.core < 0 ? 0 : cfg.core; | |||
| 1428 | cdlog.u.fields.selectNlog = cfg.lnum < 0 ? 0 : cfg.lnum; | |||
| 1429 | ||||
| 1430 | output = shr_open_rawdata(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666)open((cfg.file), (01 | 0100 | 01000), 0666); | |||
| 1431 | if (output < 0) { | |||
| 1432 | err = output; | |||
| 1433 | goto out_free; | |||
| 1434 | } | |||
| 1435 | ||||
| 1436 | err = read_header(&cmd, buf, hdl, cdlog.u.entireDword, | |||
| 1437 | cfg.namespace_id); | |||
| 1438 | if (err) | |||
| 1439 | goto out; | |||
| 1440 | memcpy(intel, buf, sizeof(*intel)); | |||
| 1441 | ||||
| 1442 | /* for 1.1 Fultondales will use old nlog, but current assert/event */ | |||
| 1443 | if ((intel->ver.major < 1 && intel->ver.minor < 1) || | |||
| 1444 | (intel->ver.major <= 1 && intel->ver.minor <= 1 && cfg.log == 0)) { | |||
| 1445 | cmd.addr = (uintptr_t)(void *)buf; | |||
| 1446 | err = get_internal_log_old(buf, output, hdl, &cmd); | |||
| 1447 | goto out; | |||
| 1448 | } | |||
| 1449 | ||||
| 1450 | if (cfg.log == 2) { | |||
| 1451 | if (cfg.verbose) | |||
| 1452 | printf("Log major:%d minor:%d header:%d size:%d numcores:%d\n", | |||
| 1453 | intel->ver.major, intel->ver.minor, | |||
| 1454 | intel->header, intel->size, intel->numcores); | |||
| 1455 | ||||
| 1456 | err = write_header(buf, output, 0x1000); | |||
| 1457 | if (err) { | |||
| 1458 | nvme_show_perror("write failure"); | |||
| 1459 | goto out; | |||
| 1460 | } | |||
| 1461 | ||||
| 1462 | count = intel->numcores; | |||
| 1463 | } else if (cfg.log == 0) { | |||
| 1464 | if (cfg.lnum < 0) | |||
| 1465 | count = intel_nlog->totalnlogs; | |||
| 1466 | else | |||
| 1467 | count = 1; | |||
| 1468 | if (cfg.core < 0) | |||
| 1469 | core_num = intel_nlog->corecount; | |||
| 1470 | } else if (cfg.log == 1) { | |||
| 1471 | core_num = intel->numcores; | |||
| 1472 | count = 1; | |||
| 1473 | err = write_header(buf, output, sizeof(*intel)); | |||
| 1474 | if (err) | |||
| 1475 | goto out; | |||
| 1476 | } | |||
| 1477 | ||||
| 1478 | for (j = (cfg.core < 0 ? 0 : cfg.core); | |||
| 1479 | j < (cfg.core < 0 ? core_num : cfg.core + 1); | |||
| 1480 | j++) { | |||
| 1481 | cdlog.u.fields.selectCore = j; | |||
| 1482 | for (i = 0; i < count; i++) { | |||
| 1483 | if (cfg.log == 2) { | |||
| 1484 | if (!ad[i].assertvalid) | |||
| 1485 | continue; | |||
| 1486 | cmd.cdw13 = ad[i].coreoffset; | |||
| 1487 | cmd.cdw10 = 0x400; | |||
| 1488 | cmd.data_len = min(0x400, ad[i].assertsize)({ typeof(0x400) _a = (0x400); typeof(ad[i].assertsize) _b = ( ad[i].assertsize); do { } while (0); _a < _b ? _a : _b; }) * 4; | |||
| 1489 | err = read_entire_cmd(&cmd, ad[i].assertsize, | |||
| 1490 | 0x400, output, | |||
| 1491 | hdl, | |||
| 1492 | buf); | |||
| 1493 | if (err) | |||
| 1494 | goto out; | |||
| 1495 | ||||
| 1496 | } else if (cfg.log == 0) { | |||
| 1497 | /* If the user selected to read the entire nlog */ | |||
| 1498 | if (count > 1) | |||
| 1499 | cdlog.u.fields.selectNlog = i; | |||
| 1500 | ||||
| 1501 | err = read_header(&cmd, buf, hdl, | |||
| 1502 | cdlog.u.entireDword, | |||
| 1503 | cfg.namespace_id); | |||
| 1504 | if (err) | |||
| 1505 | goto out; | |||
| 1506 | err = write_header(buf, output, sizeof(*intel_nlog)); | |||
| 1507 | if (err) | |||
| 1508 | goto out; | |||
| 1509 | if (cfg.verbose) | |||
| 1510 | print_intel_nlog(intel_nlog); | |||
| 1511 | cmd.cdw13 = 0x400; | |||
| 1512 | cmd.cdw10 = 0x400; | |||
| 1513 | cmd.data_len = min(0x1000, intel_nlog->nlogbytesize)({ typeof(0x1000) _a = (0x1000); typeof(intel_nlog->nlogbytesize ) _b = (intel_nlog->nlogbytesize); do { } while (0); _a < _b ? _a : _b; }); | |||
| 1514 | err = read_entire_cmd(&cmd, intel_nlog->nlogbytesize / 4, | |||
| 1515 | 0x400, output, | |||
| 1516 | hdl, | |||
| 1517 | buf); | |||
| 1518 | if (err) | |||
| 1519 | goto out; | |||
| 1520 | } else if (cfg.log == 1) { | |||
| 1521 | cmd.cdw13 = ehdr->edumps[j].coreoffset; | |||
| 1522 | cmd.cdw10 = 0x400; | |||
| 1523 | cmd.data_len = 0x400; | |||
| 1524 | err = read_entire_cmd(&cmd, ehdr->edumps[j].coresize, | |||
| 1525 | 0x400, output, | |||
| 1526 | hdl, | |||
| 1527 | buf); | |||
| 1528 | if (err) | |||
| 1529 | goto out; | |||
| 1530 | } | |||
| 1531 | } | |||
| 1532 | } | |||
| 1533 | err = 0; | |||
| 1534 | out: | |||
| 1535 | if (err) | |||
| 1536 | nvme_show_err(err, "intel log"); | |||
| 1537 | else | |||
| 1538 | nvme_show_verbose_result("Successfully wrote log to %s", cfg.file)nvme_show_verbose_message("Successfully wrote log to %s", cfg .file); | |||
| 1539 | close(output); | |||
| 1540 | out_free: | |||
| 1541 | return err; | |||
| 1542 | } | |||
| 1543 | ||||
| 1544 | static int enable_lat_stats_tracking(int argc, char **argv, | |||
| 1545 | struct command *command, struct plugin *plugin) | |||
| 1546 | { | |||
| 1547 | const char *desc = ( | |||
| 1548 | "Enable/Disable Intel Latency Statistics Tracking.\n" | |||
| 1549 | "No argument prints current status."); | |||
| 1550 | const char *enable_desc = "Enable LST"; | |||
| 1551 | const char *disable_desc = "Disable LST"; | |||
| 1552 | const __u32 nsid = 0; | |||
| 1553 | const __u8 fid = 0xe2; | |||
| 1554 | const __u8 sel = 0; | |||
| 1555 | const __u32 cdw11 = 0x0; | |||
| 1556 | const __u32 cdw12 = 0x0; | |||
| 1557 | const __u32 data_len = 32; | |||
| 1558 | const __u32 sv = 0; | |||
| 1559 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 1560 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 1561 | void *buf = NULL((void*)0); | |||
| 1562 | __u64 result; | |||
| 1563 | int err; | |||
| 1564 | ||||
| 1565 | struct config { | |||
| 1566 | bool_Bool enable, disable; | |||
| 1567 | }; | |||
| 1568 | ||||
| 1569 | struct config cfg = { | |||
| 1570 | .enable = false0, | |||
| 1571 | .disable = false0, | |||
| 1572 | }; | |||
| 1573 | ||||
| 1574 | OPT_ARGS(opts)struct argconfig_commandline_options opts[] = { | |||
| 1575 | OPT_FLAG("enable", 'e', &cfg.enable, enable_desc){"enable", 'e', ((void*)0), CFG_FLAG, _Generic(&cfg.enable , _Bool * : (&cfg.enable)), ARGCONFIG_ARG_NONE, enable_desc , 0, }, | |||
| 1576 | OPT_FLAG("disable", 'd', &cfg.disable, disable_desc){"disable", 'd', ((void*)0), CFG_FLAG, _Generic(&cfg.disable , _Bool * : (&cfg.disable)), ARGCONFIG_ARG_NONE, disable_desc , 0, }, | |||
| 1577 | OPT_END(){ ((void*)0) } | |||
| 1578 | }; | |||
| 1579 | ||||
| 1580 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 1581 | ||||
| 1582 | enum Option { | |||
| 1583 | None = -1, | |||
| 1584 | True = 1, | |||
| 1585 | False = 0, | |||
| 1586 | }; | |||
| 1587 | ||||
| 1588 | enum Option option = None; | |||
| 1589 | ||||
| 1590 | if (cfg.enable && cfg.disable) | |||
| 1591 | nvme_show_error("Cannot enable and disable simultaneously.")nvme_show_message(1, "Cannot enable and disable simultaneously." ); | |||
| 1592 | else if (cfg.enable || cfg.disable) | |||
| 1593 | option = cfg.enable; | |||
| 1594 | ||||
| 1595 | if (err) | |||
| 1596 | return err; | |||
| 1597 | ||||
| 1598 | switch (option) { | |||
| 1599 | case None: | |||
| 1600 | err = nvme_get_features(hdl, nsid, fid, sel, cdw11, 0, buf, | |||
| 1601 | data_len, &result); | |||
| 1602 | if (!err) { | |||
| 1603 | printf( | |||
| 1604 | "Latency Statistics Tracking (FID 0x%X) is currently (%"PRIu64"l" "u"").\n", | |||
| 1605 | fid, (uint64_t)result); | |||
| 1606 | } else { | |||
| 1607 | nvme_show_error("Could not read feature id 0xE2.")nvme_show_message(1, "Could not read feature id 0xE2."); | |||
| 1608 | return err; | |||
| 1609 | } | |||
| 1610 | break; | |||
| 1611 | case True: | |||
| 1612 | case False: | |||
| 1613 | err = nvme_set_features(hdl, nsid, fid, sv, option, cdw12, 0, 0, 0, buf, | |||
| 1614 | data_len, &result); | |||
| 1615 | if (err) { | |||
| 1616 | nvme_show_err(err, "Enable latency tracking"); | |||
| 1617 | } else { | |||
| 1618 | nvme_show_verbose_result("Successfully set enable bit for FID (0x%X) to %i.",nvme_show_verbose_message("Successfully set enable bit for FID (0x%X) to %i." , fid, option) | |||
| 1619 | fid, option)nvme_show_verbose_message("Successfully set enable bit for FID (0x%X) to %i." , fid, option); | |||
| 1620 | } | |||
| 1621 | break; | |||
| 1622 | default: | |||
| 1623 | nvme_show_error("%d not supported.", option)nvme_show_message(1, "%d not supported.", option); | |||
| 1624 | return -EINVAL22; | |||
| 1625 | } | |||
| 1626 | return err; | |||
| 1627 | } | |||
| 1628 | ||||
| 1629 | static int set_lat_stats_thresholds(int argc, char **argv, | |||
| 1630 | struct command *command, struct plugin *plugin) | |||
| 1631 | { | |||
| 1632 | const char *desc = "Write Intel Bucket Thresholds for Latency Statistics Tracking"; | |||
| 1633 | const char *bucket_thresholds = "Bucket Threshold List, comma separated list: 0, 10, 20 ..."; | |||
| 1634 | const char *write = "Set write bucket Thresholds for latency tracking (read default)"; | |||
| 1635 | ||||
| 1636 | const __u32 nsid = 0; | |||
| 1637 | const __u8 fid = 0xf7; | |||
| 1638 | const __u32 cdw12 = 0x0; | |||
| 1639 | const __u32 sv = 0; | |||
| 1640 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 1641 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 1642 | __u64 result; | |||
| 1643 | int err, num; | |||
| 1644 | ||||
| 1645 | struct config { | |||
| 1646 | bool_Bool write; | |||
| 1647 | char *bucket_thresholds; | |||
| 1648 | }; | |||
| 1649 | ||||
| 1650 | struct config cfg = { | |||
| 1651 | .write = false0, | |||
| 1652 | .bucket_thresholds = "", | |||
| 1653 | }; | |||
| 1654 | ||||
| 1655 | OPT_ARGS(opts)struct argconfig_commandline_options opts[] = { | |||
| 1656 | OPT_FLAG("write", 'w', &cfg.write, write){"write", 'w', ((void*)0), CFG_FLAG, _Generic(&cfg.write, _Bool * : (&cfg.write)), ARGCONFIG_ARG_NONE, write, 0, }, | |||
| 1657 | OPT_LIST("bucket-thresholds", 't', &cfg.bucket_thresholds,{"bucket-thresholds", 't', "LIST", CFG_STRING, _Generic(& cfg.bucket_thresholds, char ** : (&cfg.bucket_thresholds) , const char ** : (&cfg.bucket_thresholds)), ARGCONFIG_ARG_REQUIRED , bucket_thresholds, 0, } | |||
| 1658 | bucket_thresholds){"bucket-thresholds", 't', "LIST", CFG_STRING, _Generic(& cfg.bucket_thresholds, char ** : (&cfg.bucket_thresholds) , const char ** : (&cfg.bucket_thresholds)), ARGCONFIG_ARG_REQUIRED , bucket_thresholds, 0, }, | |||
| 1659 | OPT_END(){ ((void*)0) } | |||
| 1660 | }; | |||
| 1661 | ||||
| 1662 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 1663 | ||||
| 1664 | if (err) | |||
| 1665 | return err; | |||
| 1666 | ||||
| 1667 | /* Query maj and minor version first to figure out the amount of | |||
| 1668 | * valid buckets a user is allowed to modify. Read or write doesn't | |||
| 1669 | * matter | |||
| 1670 | */ | |||
| 1671 | err = nvme_get_log_simple(hdl, 0xc2, | |||
| 1672 | media_version, sizeof(media_version)); | |||
| 1673 | if (err) { | |||
| 1674 | nvme_show_error("Querying media version failed. ")nvme_show_message(1, "Querying media version failed. "); | |||
| 1675 | nvme_show_status(err); | |||
| 1676 | goto close_dev; | |||
| 1677 | } | |||
| 1678 | ||||
| 1679 | if (media_version[0] == 1000) { | |||
| 1680 | int thresholds[OPTANE_V1000_BUCKET_LEN8] = {0}; | |||
| 1681 | ||||
| 1682 | num = shr_parse_csv_int(cfg.bucket_thresholds, | |||
| 1683 | thresholds, | |||
| 1684 | sizeof(thresholds)); | |||
| 1685 | if (num == -1) { | |||
| 1686 | nvme_show_error("ERROR: Bucket list is malformed")nvme_show_message(1, "ERROR: Bucket list is malformed"); | |||
| 1687 | goto close_dev; | |||
| 1688 | ||||
| 1689 | } | |||
| 1690 | ||||
| 1691 | err = nvme_set_features(hdl, nsid, fid, sv, cfg.write ? 0x1 : 0x0, cdw12, | |||
| 1692 | 0, 0, 0, thresholds, sizeof(thresholds), &result); | |||
| 1693 | ||||
| 1694 | if (err) | |||
| 1695 | nvme_show_err(err, "Enable latency tracking"); | |||
| 1696 | } else { | |||
| 1697 | nvme_show_error("Unsupported command")nvme_show_message(1, "Unsupported command"); | |||
| 1698 | } | |||
| 1699 | ||||
| 1700 | close_dev: | |||
| 1701 | return err; | |||
| 1702 | } | |||
| 1703 | ||||
| 1704 | static struct command id_ctrl_cmd = { | |||
| 1705 | .name = "id-ctrl", | |||
| 1706 | .help = "Send NVMe Identify Controller", | |||
| 1707 | .fn = id_ctrl, | |||
| 1708 | }; | |||
| 1709 | ||||
| 1710 | static struct command get_internal_log_cmd = { | |||
| 1711 | .name = "internal-log", | |||
| 1712 | .help = "Retrieve Intel internal firmware log, save it", | |||
| 1713 | .fn = get_internal_log, | |||
| 1714 | }; | |||
| 1715 | ||||
| 1716 | static struct command get_lat_stats_log_cmd = { | |||
| 1717 | .name = "lat-stats", | |||
| 1718 | .help = "Retrieve Intel IO Latency Statistics log, show it", | |||
| 1719 | .fn = get_lat_stats_log, | |||
| 1720 | }; | |||
| 1721 | ||||
| 1722 | static struct command set_lat_stats_thresholds_cmd = { | |||
| 1723 | .name = "set-bucket-thresholds", | |||
| 1724 | .help = "Set Latency Stats Bucket Values, save it", | |||
| 1725 | .fn = set_lat_stats_thresholds, | |||
| 1726 | }; | |||
| 1727 | ||||
| 1728 | static struct command enable_lat_stats_tracking_cmd = { | |||
| 1729 | .name = "lat-stats-tracking", | |||
| 1730 | .help = "Enable and disable Latency Statistics logging.", | |||
| 1731 | .fn = enable_lat_stats_tracking, | |||
| 1732 | }; | |||
| 1733 | ||||
| 1734 | static struct command get_market_log_cmd = { | |||
| 1735 | .name = "market-name", | |||
| 1736 | .help = "Retrieve Intel Marketing Name log, show it", | |||
| 1737 | .fn = get_market_log, | |||
| 1738 | }; | |||
| 1739 | ||||
| 1740 | static struct command get_additional_smart_log_cmd = { | |||
| 1741 | .name = "smart-log-add", | |||
| 1742 | .help = "Retrieve Intel SMART Log, show it", | |||
| 1743 | .fn = get_additional_smart_log, | |||
| 1744 | }; | |||
| 1745 | ||||
| 1746 | static struct command get_temp_stats_log_cmd = { | |||
| 1747 | .name = "temp-stats", | |||
| 1748 | .help = "Retrieve Intel Temperature Statistics log, show it", | |||
| 1749 | .fn = get_temp_stats_log, | |||
| 1750 | }; | |||
| 1751 | ||||
| 1752 | static struct command *commands[] = { | |||
| 1753 | &id_ctrl_cmd, | |||
| 1754 | &get_internal_log_cmd, | |||
| 1755 | &get_lat_stats_log_cmd, | |||
| 1756 | &set_lat_stats_thresholds_cmd, | |||
| 1757 | &enable_lat_stats_tracking_cmd, | |||
| 1758 | &get_market_log_cmd, | |||
| 1759 | &get_additional_smart_log_cmd, | |||
| 1760 | &get_temp_stats_log_cmd, | |||
| 1761 | NULL((void*)0), | |||
| 1762 | }; | |||
| 1763 | ||||
| 1764 | static struct plugin plugin = { | |||
| 1765 | .name = "intel", | |||
| 1766 | .desc = "Intel vendor specific extensions", | |||
| 1767 | .version = NVME_VERSION"3.1", | |||
| 1768 | }; | |||
| 1769 | ||||
| 1770 | static void __shr_constructor__attribute__((constructor)) register_plugin(void) | |||
| 1771 | { | |||
| 1772 | plugin_add_group(&plugin, NULL((void*)0), commands); | |||
| 1773 | register_extension(&plugin); | |||
| 1774 | } | |||
| 1775 |