| File: | .build-ci/../plugins/solidigm/solidigm-internal-logs.c |
| Warning: | line 513, column 23 Access to field 'cfg' results in a dereference of a null pointer (loaded from variable 'ilog') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | /* | |||
| 3 | * Copyright (c) 2023 Solidigm. | |||
| 4 | * | |||
| 5 | * Authors: leonardo.da.cunha@solidigm.com | |||
| 6 | * shankaralingegowda.singonahalli@solidigm.com | |||
| 7 | * haro.panosyan@solidigm.com | |||
| 8 | */ | |||
| 9 | ||||
| 10 | #include <fcntl.h> | |||
| 11 | #include <errno(*__errno_location ()).h> | |||
| 12 | #include <stdio.h> | |||
| 13 | #include <stdlib.h> | |||
| 14 | #include <unistd.h> | |||
| 15 | #include <inttypes.h> | |||
| 16 | #include <time.h> | |||
| 17 | ||||
| 18 | #include <libnvme.h> | |||
| 19 | ||||
| 20 | #include "common.h" | |||
| 21 | #include "nvme-cmds.h" | |||
| 22 | #include "nvme-print.h" | |||
| 23 | #include "nvme.h" | |||
| 24 | #include "plugin.h" | |||
| 25 | ||||
| 26 | #include "solidigm-util.h" | |||
| 27 | ||||
| 28 | #define DWORD_SIZE4 4 | |||
| 29 | #define LOG_FILE_PERMISSION0644 0644 | |||
| 30 | #define ATMOS_MODEL_PREFIX"SOLIDIGM SB5" "SOLIDIGM SB5" | |||
| 31 | ||||
| 32 | enum log_type { | |||
| 33 | NLOG = 0, | |||
| 34 | EVENTLOG = 1, | |||
| 35 | ASSERTLOG = 2, | |||
| 36 | HIT, | |||
| 37 | CIT, | |||
| 38 | ALL, | |||
| 39 | EXTENDED | |||
| 40 | }; | |||
| 41 | ||||
| 42 | #pragma pack(push, internal_logs, 1) | |||
| 43 | struct version { | |||
| 44 | __u16 major; | |||
| 45 | __u16 minor; | |||
| 46 | }; | |||
| 47 | ||||
| 48 | struct event_dump_instance { | |||
| 49 | __u32 numeventdumps; | |||
| 50 | __u32 coresize; | |||
| 51 | __u32 coreoffset; | |||
| 52 | __u32 eventidoffset[16]; | |||
| 53 | __u8 eventIdValidity[16]; | |||
| 54 | }; | |||
| 55 | ||||
| 56 | struct commom_header { | |||
| 57 | struct version ver; | |||
| 58 | __u32 header_size; | |||
| 59 | __u32 log_size; | |||
| 60 | __u32 numcores; | |||
| 61 | }; | |||
| 62 | ||||
| 63 | struct event_dump_header { | |||
| 64 | struct commom_header header; | |||
| 65 | __u32 eventidsize; | |||
| 66 | struct event_dump_instance edumps[0]; | |||
| 67 | }; | |||
| 68 | ||||
| 69 | struct assert_dump_core { | |||
| 70 | __u32 coreoffset; | |||
| 71 | __u32 assertsize; | |||
| 72 | __u8 assertdumptype; | |||
| 73 | __u8 assertvalid; | |||
| 74 | __u8 reserved[2]; | |||
| 75 | }; | |||
| 76 | ||||
| 77 | struct assert_dump_header { | |||
| 78 | struct commom_header header; | |||
| 79 | struct assert_dump_core core[]; | |||
| 80 | }; | |||
| 81 | ||||
| 82 | struct nlog_dump_header_common { | |||
| 83 | struct version ver; | |||
| 84 | __u32 logselect; | |||
| 85 | __u32 totalnlogs; | |||
| 86 | __u32 nlognum; | |||
| 87 | char nlogname[4]; | |||
| 88 | __u32 nlogbytesize; | |||
| 89 | __u32 nlogprimarybuffsize; | |||
| 90 | __u32 tickspersecond; | |||
| 91 | __u32 corecount; | |||
| 92 | }; | |||
| 93 | ||||
| 94 | struct nlog_dump_header3_0 { | |||
| 95 | struct nlog_dump_header_common common; | |||
| 96 | __u32 nlogpausestatus; | |||
| 97 | __u32 selectoffsetref; | |||
| 98 | __u32 selectnlogpause; | |||
| 99 | __u32 selectaddedoffset; | |||
| 100 | __u32 nlogbufnum; | |||
| 101 | __u32 nlogbufnummax; | |||
| 102 | }; | |||
| 103 | ||||
| 104 | struct nlog_dump_header4_0 { | |||
| 105 | struct nlog_dump_header_common common; | |||
| 106 | __u64 nlogpausestatus; | |||
| 107 | __u32 selectoffsetref; | |||
| 108 | __u32 selectnlogpause; | |||
| 109 | __u32 selectaddedoffset; | |||
| 110 | __u32 nlogbufnum; | |||
| 111 | __u32 nlogbufnummax; | |||
| 112 | __u32 coreselected; | |||
| 113 | __u32 reserved[2]; | |||
| 114 | }; | |||
| 115 | ||||
| 116 | struct nlog_dump_header4_1 { | |||
| 117 | struct nlog_dump_header_common common; | |||
| 118 | __u64 nlogpausestatus; | |||
| 119 | __u32 selectoffsetref; | |||
| 120 | __u32 selectnlogpause; | |||
| 121 | __u32 selectaddedoffset; | |||
| 122 | __u32 nlogbufnum; | |||
| 123 | __u32 nlogbufnummax; | |||
| 124 | __u32 coreselected; | |||
| 125 | __u32 lpaPointer1High; | |||
| 126 | __u32 lpaPointer1Low; | |||
| 127 | __u32 lpaPointer2High; | |||
| 128 | __u32 lpaPointer2Low; | |||
| 129 | }; | |||
| 130 | ||||
| 131 | #pragma pack(pop, internal_logs) | |||
| 132 | ||||
| 133 | struct config { | |||
| 134 | char *out_dir; | |||
| 135 | char *type; | |||
| 136 | bool_Bool verbose; | |||
| 137 | }; | |||
| 138 | ||||
| 139 | struct ilog { | |||
| 140 | struct config *cfg; | |||
| 141 | int count; | |||
| 142 | struct nvme_id_ctrl id_ctrl; | |||
| 143 | }; | |||
| 144 | ||||
| 145 | static void print_nlog_header(__u8 *buffer) | |||
| 146 | { | |||
| 147 | struct nlog_dump_header_common *nlog_header = (struct nlog_dump_header_common *) buffer; | |||
| 148 | ||||
| 149 | if (nlog_header->ver.major >= 3) { | |||
| 150 | printf("Version Major %u\n", nlog_header->ver.major); | |||
| 151 | printf("Version Minor %u\n", nlog_header->ver.minor); | |||
| 152 | printf("Log_select %u\n", nlog_header->logselect); | |||
| 153 | printf("totalnlogs %u\n", nlog_header->totalnlogs); | |||
| 154 | printf("nlognum %u\n", nlog_header->nlognum); | |||
| 155 | printf("nlogname %c%c%c%c\n", nlog_header->nlogname[3], nlog_header->nlogname[2], | |||
| 156 | nlog_header->nlogname[1], nlog_header->nlogname[0]); | |||
| 157 | printf("nlogbytesize %u\n", nlog_header->nlogbytesize); | |||
| 158 | printf("nlogprimarybuffsize %u\n", nlog_header->nlogprimarybuffsize); | |||
| 159 | printf("tickspersecond %u\n", nlog_header->tickspersecond); | |||
| 160 | printf("corecount %u\n", nlog_header->corecount); | |||
| 161 | } | |||
| 162 | if (nlog_header->ver.major >= 4) { | |||
| 163 | struct nlog_dump_header4_0 *nlog_header = (struct nlog_dump_header4_0 *) buffer; | |||
| 164 | ||||
| 165 | printf("nlogpausestatus %"PRIu64"l" "u""\n", (uint64_t)nlog_header->nlogpausestatus); | |||
| 166 | printf("selectoffsetref %u\n", nlog_header->selectoffsetref); | |||
| 167 | printf("selectnlogpause %u\n", nlog_header->selectnlogpause); | |||
| 168 | printf("selectaddedoffset %u\n", nlog_header->selectaddedoffset); | |||
| 169 | printf("nlogbufnum %u\n", nlog_header->nlogbufnum); | |||
| 170 | printf("nlogbufnummax %u\n", nlog_header->nlogbufnummax); | |||
| 171 | printf("coreselected %u\n\n", nlog_header->coreselected); | |||
| 172 | } | |||
| 173 | } | |||
| 174 | ||||
| 175 | #define INTERNAL_LOG_MAX_BYTE_TRANSFER4096 4096 | |||
| 176 | #define INTERNAL_LOG_MAX_DWORD_TRANSFER(4096 / 4) (INTERNAL_LOG_MAX_BYTE_TRANSFER4096 / 4) | |||
| 177 | ||||
| 178 | static int cmd_dump_repeat(struct libnvme_passthru_cmd *cmd, __u32 total_dw_size, | |||
| 179 | int out_fd, struct libnvme_transport_handle *hdl, bool_Bool force_max_transfer) | |||
| 180 | { | |||
| 181 | int err = 0; | |||
| 182 | ||||
| 183 | while (total_dw_size > 0) { | |||
| 184 | size_t dword_tfer = min(INTERNAL_LOG_MAX_DWORD_TRANSFER, total_dw_size)(((4096 / 4)) > (total_dw_size) ? (total_dw_size) : ((4096 / 4))); | |||
| 185 | ||||
| 186 | cmd->cdw10 = force_max_transfer ? INTERNAL_LOG_MAX_DWORD_TRANSFER(4096 / 4) : dword_tfer; | |||
| 187 | cmd->data_len = dword_tfer * 4; | |||
| 188 | err = libnvme_exec_admin_passthru(hdl, cmd); | |||
| 189 | if (err) | |||
| 190 | return err; | |||
| 191 | ||||
| 192 | if (out_fd > 0) { | |||
| 193 | err = write(out_fd, (const void *)(uintptr_t)cmd->addr, cmd->data_len); | |||
| 194 | if (err < 0) { | |||
| 195 | perror("write failure"); | |||
| 196 | return err; | |||
| 197 | } | |||
| 198 | err = 0; | |||
| 199 | } | |||
| 200 | total_dw_size -= dword_tfer; | |||
| 201 | cmd->cdw13 += dword_tfer; | |||
| 202 | } | |||
| 203 | return err; | |||
| 204 | } | |||
| 205 | ||||
| 206 | static int write_header(__u8 *buf, int fd, size_t amnt) | |||
| 207 | { | |||
| 208 | if (write(fd, buf, amnt) < 0) | |||
| 209 | return 1; | |||
| 210 | return 0; | |||
| 211 | } | |||
| 212 | ||||
| 213 | static int read_header(struct libnvme_passthru_cmd *cmd, struct libnvme_transport_handle *hdl) | |||
| 214 | { | |||
| 215 | memset((void *)(uintptr_t)cmd->addr, 0, INTERNAL_LOG_MAX_BYTE_TRANSFER4096); | |||
| 216 | return cmd_dump_repeat(cmd, INTERNAL_LOG_MAX_DWORD_TRANSFER(4096 / 4), -1, hdl, false0); | |||
| 217 | } | |||
| 218 | ||||
| 219 | static int get_serial_number(char *str, struct libnvme_transport_handle *hdl) | |||
| 220 | { | |||
| 221 | struct nvme_id_ctrl ctrl = {0}; | |||
| 222 | int err; | |||
| 223 | ||||
| 224 | err = nvme_identify_ctrl(hdl, &ctrl); | |||
| 225 | if (err) | |||
| 226 | return err; | |||
| 227 | ||||
| 228 | /* Remove trailing spaces */ | |||
| 229 | for (int i = sizeof(ctrl.sn) - 1; i && ctrl.sn[i] == ' '; i--) | |||
| 230 | ctrl.sn[i] = '\0'; | |||
| 231 | sprintf(str, "%-.*s", (int)sizeof(ctrl.sn), ctrl.sn); | |||
| 232 | return err; | |||
| 233 | } | |||
| 234 | ||||
| 235 | static int ilog_dump_assert_logs(struct libnvme_transport_handle *hdl, struct ilog *ilog) | |||
| 236 | { | |||
| 237 | __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096]; | |||
| 238 | __u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096]; | |||
| 239 | __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0); | |||
| 240 | char file_name[] = "AssertLog.bin"; | |||
| 241 | struct assert_dump_header *ad = (struct assert_dump_header *) head_buf; | |||
| 242 | struct libnvme_passthru_cmd cmd = { | |||
| 243 | .opcode = 0xd2, | |||
| 244 | .nsid = NVME_NSID_ALL, | |||
| 245 | .addr = (__u64)(void *)head_buf, | |||
| 246 | .cdw12 = ASSERTLOG, | |||
| 247 | .cdw13 = 0, | |||
| 248 | }; | |||
| 249 | int output, err; | |||
| 250 | ||||
| 251 | err = read_header(&cmd, hdl); | |||
| 252 | if (err) | |||
| 253 | return err; | |||
| 254 | ||||
| 255 | if (asprintf(&file_path, "%.*s/%s", | |||
| 256 | (int) (sizeof(file_path) - sizeof(file_name) - 1), | |||
| 257 | ilog->cfg->out_dir, file_name) < 0) | |||
| 258 | return -errno(*__errno_location ()); | |||
| 259 | output = nvme_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION)open((file_path), (01 | 0100 | 01000), 0644); | |||
| 260 | if (output < 0) | |||
| 261 | return -errno(*__errno_location ()); | |||
| 262 | err = write_header((__u8 *)ad, output, ad->header.header_size * DWORD_SIZE4); | |||
| 263 | if (err) { | |||
| 264 | perror("write failure"); | |||
| 265 | close(output); | |||
| 266 | return err; | |||
| 267 | } | |||
| 268 | cmd.addr = (__u64)(void *)buf; | |||
| 269 | ||||
| 270 | if (ilog->cfg->verbose) { | |||
| 271 | printf("Assert Log, cores: %d log size: %d header size: %d\n", ad->header.numcores, | |||
| 272 | ad->header.log_size * DWORD_SIZE4, ad->header.header_size * DWORD_SIZE4); | |||
| 273 | for (__u32 i = 0; i < ad->header.numcores; i++) | |||
| 274 | printf("core %d assert size: %d\n", i, ad->core[i].assertsize * DWORD_SIZE4); | |||
| 275 | } | |||
| 276 | ||||
| 277 | for (__u32 i = 0; i < ad->header.numcores; i++) { | |||
| 278 | if (!ad->core[i].assertvalid) | |||
| 279 | continue; | |||
| 280 | cmd.cdw13 = ad->core[i].coreoffset; | |||
| 281 | err = cmd_dump_repeat(&cmd, ad->core[i].assertsize, output, | |||
| 282 | hdl, false0); | |||
| 283 | if (err) { | |||
| 284 | close(output); | |||
| 285 | return err; | |||
| 286 | } | |||
| 287 | } | |||
| 288 | close(output); | |||
| 289 | printf("Successfully wrote Assert to %s\n", file_path); | |||
| 290 | return err; | |||
| 291 | } | |||
| 292 | ||||
| 293 | static int ilog_dump_event_logs(struct libnvme_transport_handle *hdl, struct ilog *ilog) | |||
| 294 | { | |||
| 295 | __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096]; | |||
| 296 | __u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096]; | |||
| 297 | __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0); | |||
| 298 | struct event_dump_header *ehdr = (struct event_dump_header *) head_buf; | |||
| 299 | struct libnvme_passthru_cmd cmd = { | |||
| 300 | .opcode = 0xd2, | |||
| 301 | .nsid = NVME_NSID_ALL, | |||
| 302 | .addr = (__u64)(void *)head_buf, | |||
| 303 | .cdw12 = EVENTLOG, | |||
| 304 | .cdw13 = 0, | |||
| 305 | }; | |||
| 306 | int output; | |||
| 307 | int core_num, err; | |||
| 308 | ||||
| 309 | err = read_header(&cmd, hdl); | |||
| 310 | if (err) | |||
| 311 | return err; | |||
| 312 | if (asprintf(&file_path, "%s/EventLog.bin", ilog->cfg->out_dir)) | |||
| 313 | return -errno(*__errno_location ()); | |||
| 314 | output = nvme_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION)open((file_path), (01 | 0100 | 01000), 0644); | |||
| 315 | if (output < 0) | |||
| 316 | return -errno(*__errno_location ()); | |||
| 317 | err = write_header(head_buf, output, INTERNAL_LOG_MAX_BYTE_TRANSFER4096); | |||
| 318 | ||||
| 319 | core_num = ehdr->header.numcores; | |||
| 320 | ||||
| 321 | if (err) { | |||
| 322 | close(output); | |||
| 323 | return err; | |||
| 324 | } | |||
| 325 | cmd.addr = (__u64)(void *)buf; | |||
| 326 | ||||
| 327 | if (ilog->cfg->verbose) | |||
| 328 | printf("Event Log, cores: %d log size: %d\n", core_num, ehdr->header.log_size * 4); | |||
| 329 | ||||
| 330 | for (__u32 j = 0; j < core_num; j++) { | |||
| 331 | if (ilog->cfg->verbose) { | |||
| 332 | for (int k = 0 ; k < 16; k++) { | |||
| 333 | printf("core: %d event: %d ", j, k); | |||
| 334 | printf("validity: %d ", ehdr->edumps[j].eventIdValidity[k]); | |||
| 335 | printf("offset: %d\n", ehdr->edumps[j].eventidoffset[k]); | |||
| 336 | } | |||
| 337 | } | |||
| 338 | cmd.cdw13 = ehdr->edumps[j].coreoffset; | |||
| 339 | err = cmd_dump_repeat(&cmd, ehdr->edumps[j].coresize, | |||
| 340 | output, hdl, false0); | |||
| 341 | if (err) { | |||
| 342 | close(output); | |||
| 343 | return err; | |||
| 344 | } | |||
| 345 | } | |||
| 346 | close(output); | |||
| 347 | printf("Successfully wrote Events to %s\n", file_path); | |||
| 348 | return err; | |||
| 349 | } | |||
| 350 | ||||
| 351 | static size_t get_nlog_header_size(struct nlog_dump_header_common *nlog_header) | |||
| 352 | { | |||
| 353 | switch (nlog_header->ver.major) { | |||
| 354 | case 3: | |||
| 355 | return sizeof(struct nlog_dump_header3_0); | |||
| 356 | case 4: | |||
| 357 | if (nlog_header->ver.minor == 0) | |||
| 358 | return sizeof(struct nlog_dump_header4_0); | |||
| 359 | return sizeof(struct nlog_dump_header4_1); | |||
| 360 | default: | |||
| 361 | return INTERNAL_LOG_MAX_BYTE_TRANSFER4096; | |||
| 362 | } | |||
| 363 | ||||
| 364 | } | |||
| 365 | ||||
| 366 | /* dumps nlogs from specified core or all cores when core = -1 */ | |||
| 367 | static int ilog_dump_nlogs(struct libnvme_transport_handle *hdl, struct ilog *ilog, int core) | |||
| 368 | { | |||
| 369 | int err = 0; | |||
| 370 | __u32 count, core_num; | |||
| 371 | __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER4096]; | |||
| 372 | __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0); | |||
| 373 | struct nlog_dump_header_common *nlog_header = (struct nlog_dump_header_common *)buf; | |||
| 374 | struct libnvme_passthru_cmd cmd = { | |||
| 375 | .opcode = 0xd2, | |||
| 376 | .nsid = NVME_NSID_ALL, | |||
| 377 | .addr = (__u64)(void *)buf | |||
| 378 | }; | |||
| 379 | ||||
| 380 | struct dump_select { | |||
| 381 | union { | |||
| 382 | struct { | |||
| 383 | __u32 selectLog : 3; | |||
| 384 | __u32 selectCore : 2; | |||
| 385 | __u32 selectNlog : 8; | |||
| 386 | }; | |||
| 387 | __u32 raw; | |||
| 388 | }; | |||
| 389 | } log_select; | |||
| 390 | int output; | |||
| 391 | bool_Bool is_open = false0; | |||
| 392 | size_t header_size = 0; | |||
| 393 | ||||
| 394 | log_select.selectCore = core < 0 ? 0 : core; | |||
| 395 | do { | |||
| 396 | log_select.selectNlog = 0; | |||
| 397 | do { | |||
| 398 | cmd.cdw13 = 0; | |||
| 399 | cmd.cdw12 = log_select.raw; | |||
| 400 | err = read_header(&cmd, hdl); | |||
| 401 | if (err) { | |||
| 402 | if (is_open) | |||
| 403 | close(output); | |||
| 404 | return err; | |||
| 405 | } | |||
| 406 | count = nlog_header->totalnlogs; | |||
| 407 | core_num = core < 0 ? nlog_header->corecount : 0; | |||
| 408 | if (!header_size) { | |||
| 409 | if (asprintf(&file_path, "%s/NLog.bin", ilog->cfg->out_dir) >= 0) { | |||
| 410 | output = nvme_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC,open((file_path), (01 | 0100 | 01000), 0644) | |||
| 411 | LOG_FILE_PERMISSION)open((file_path), (01 | 0100 | 01000), 0644); | |||
| 412 | if (output < 0) | |||
| 413 | return -errno(*__errno_location ()); | |||
| 414 | } else | |||
| 415 | return -errno(*__errno_location ()); | |||
| 416 | header_size = get_nlog_header_size(nlog_header); | |||
| 417 | is_open = true1; | |||
| 418 | } | |||
| 419 | err = write_header(buf, output, header_size); | |||
| 420 | if (err) | |||
| 421 | break; | |||
| 422 | if (ilog->cfg->verbose) | |||
| 423 | print_nlog_header(buf); | |||
| 424 | cmd.cdw13 = 0x400; | |||
| 425 | err = cmd_dump_repeat(&cmd, nlog_header->nlogbytesize / 4, | |||
| 426 | output, hdl, true1); | |||
| 427 | if (err) | |||
| 428 | break; | |||
| 429 | } while (++log_select.selectNlog < count); | |||
| 430 | if (err) | |||
| 431 | break; | |||
| 432 | } while (++log_select.selectCore < core_num); | |||
| 433 | if (is_open) { | |||
| 434 | close(output); | |||
| 435 | printf("Successfully wrote Nlog to %s\n", file_path); | |||
| 436 | } | |||
| 437 | return err; | |||
| 438 | } | |||
| 439 | ||||
| 440 | int ensure_dir(const char *parent_dir_name, const char *name) | |||
| 441 | { | |||
| 442 | __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0); | |||
| 443 | struct stat sb; | |||
| 444 | ||||
| 445 | if (asprintf(&file_path, "%s/%s", parent_dir_name, name) < 0) | |||
| 446 | return -errno(*__errno_location ()); | |||
| 447 | ||||
| 448 | if (!(stat(file_path, &sb) == 0 && S_ISDIR(sb.st_mode)((((sb.st_mode)) & 0170000) == (0040000)))) { | |||
| 449 | if (mkdir(file_path, 777) != 0) { | |||
| 450 | perror(file_path); | |||
| 451 | return -errno(*__errno_location ()); | |||
| 452 | } | |||
| 453 | } | |||
| 454 | ||||
| 455 | return 0; | |||
| 456 | } | |||
| 457 | ||||
| 458 | struct log { | |||
| 459 | __u8 id; | |||
| 460 | const char *desc; | |||
| 461 | size_t buffer_size; | |||
| 462 | __u8 *buffer; | |||
| 463 | }; | |||
| 464 | ||||
| 465 | static int log_save(struct log *log, const char *parent_dir_name, const char *subdir_name, | |||
| 466 | const char *file_name, __u8 *buffer, size_t buf_size) | |||
| 467 | { | |||
| 468 | __cleanup_fd__attribute__((cleanup(cleanup_fd))) int output = -1; | |||
| 469 | __cleanup_free__attribute__((cleanup(freep))) char *file_path = NULL((void*)0); | |||
| 470 | size_t bytes_remaining = 0; | |||
| 471 | ||||
| 472 | ensure_dir(parent_dir_name, subdir_name); | |||
| 473 | ||||
| 474 | if (asprintf(&file_path, "%s/%s/%s", parent_dir_name, subdir_name, file_name) < 0) | |||
| 475 | return -errno(*__errno_location ()); | |||
| 476 | ||||
| 477 | output = nvme_open_rawdata(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION)open((file_path), (01 | 0100 | 01000), 0644); | |||
| 478 | if (output < 0) | |||
| 479 | return -errno(*__errno_location ()); | |||
| 480 | ||||
| 481 | bytes_remaining = buf_size; | |||
| 482 | ||||
| 483 | while (bytes_remaining) { | |||
| 484 | ssize_t bytes_written = write(output, buffer, bytes_remaining); | |||
| 485 | ||||
| 486 | if (bytes_written < 0) | |||
| 487 | return -errno(*__errno_location ()); | |||
| 488 | ||||
| 489 | bytes_remaining -= bytes_written; | |||
| 490 | buffer += bytes_written; | |||
| 491 | } | |||
| 492 | printf("Successfully wrote %s to %s\n", log->desc, file_path); | |||
| 493 | return 0; | |||
| 494 | } | |||
| 495 | ||||
| 496 | static int ilog_dump_identify_page(struct libnvme_transport_handle *hdl, | |||
| 497 | struct ilog *ilog, struct log *cns, __u32 nsid) | |||
| 498 | { | |||
| 499 | __u8 data[NVME_IDENTIFY_DATA_SIZE]; | |||
| 500 | __u8 *buff = cns->buffer ? cns->buffer : data; | |||
| 501 | __cleanup_free__attribute__((cleanup(freep))) char *filename = NULL((void*)0); | |||
| 502 | int err; | |||
| 503 | ||||
| 504 | err = nvme_identify(hdl, nsid, NVME_CSI_NVM, cns->id, buff, | |||
| 505 | sizeof(data)); | |||
| 506 | if (err) | |||
| 507 | return err; | |||
| 508 | ||||
| 509 | if (asprintf(&filename, "cntid_0_cns_%d_nsid_%d_nvmsetid_0_csi_0.bin", | |||
| 510 | cns->id, nsid) < 0) | |||
| 511 | return -errno(*__errno_location ()); | |||
| 512 | ||||
| 513 | return log_save(cns, ilog->cfg->out_dir, "identify", filename, buff, | |||
| ||||
| 514 | sizeof(data)); | |||
| 515 | } | |||
| 516 | ||||
| 517 | static int ilog_ensure_dump_id_ctrl(struct libnvme_transport_handle *hdl, | |||
| 518 | struct ilog *ilog) | |||
| 519 | { | |||
| 520 | static bool_Bool first = true1; | |||
| 521 | static int err; | |||
| 522 | struct log idctrl = {NVME_IDENTIFY_CNS_CTRL, "Id Controller Data", sizeof(ilog->id_ctrl), | |||
| 523 | (__u8 *) &ilog->id_ctrl}; | |||
| 524 | ||||
| 525 | if (!first
| |||
| 526 | return err; | |||
| 527 | ||||
| 528 | first = false0; | |||
| 529 | err = ilog_dump_identify_page(hdl, ilog, &idctrl, 0); | |||
| 530 | ||||
| 531 | if (err == 0) | |||
| 532 | ilog->count++; | |||
| 533 | ||||
| 534 | return err; | |||
| 535 | } | |||
| 536 | ||||
| 537 | static bool_Bool is_atmos(struct libnvme_transport_handle *hdl, struct ilog *ilog) | |||
| 538 | { | |||
| 539 | ilog_ensure_dump_id_ctrl(hdl, ilog); | |||
| ||||
| 540 | return !strncmp(ilog->id_ctrl.mn, ATMOS_MODEL_PREFIX"SOLIDIGM SB5", sizeof(ATMOS_MODEL_PREFIX"SOLIDIGM SB5") - 1); | |||
| 541 | } | |||
| 542 | ||||
| 543 | static enum nvme_telemetry_da get_max_da(struct libnvme_transport_handle *hdl, | |||
| 544 | struct ilog *ilog, enum log_type ttype) | |||
| 545 | { | |||
| 546 | enum nvme_telemetry_da max_da = NVME_TELEMETRY_DA_1; | |||
| 547 | ||||
| 548 | ilog_ensure_dump_id_ctrl(hdl, ilog); | |||
| 549 | ||||
| 550 | if (is_atmos(hdl, ilog) && ttype != EXTENDED) | |||
| 551 | return NVME_TELEMETRY_DA_3; | |||
| 552 | ||||
| 553 | if (ilog->id_ctrl.lpa & 0x8) | |||
| 554 | max_da = NVME_TELEMETRY_DA_3; | |||
| 555 | if (ilog->id_ctrl.lpa & 0x40) | |||
| 556 | max_da = NVME_TELEMETRY_DA_4; | |||
| 557 | return max_da; | |||
| 558 | } | |||
| 559 | ||||
| 560 | static int ilog_dump_telemetry(struct libnvme_transport_handle *hdl, struct ilog *ilog, enum log_type ttype) | |||
| 561 | { | |||
| 562 | int err = 0; | |||
| 563 | enum nvme_telemetry_da da; | |||
| 564 | size_t mdts; | |||
| 565 | const char *file_name; | |||
| 566 | struct nvme_feat_host_behavior prev = {0}; | |||
| 567 | bool_Bool host_behavior_changed = false0; | |||
| 568 | struct libnvme_passthru_cmd cmd; | |||
| 569 | struct log log = {0}; | |||
| 570 | ||||
| 571 | err = ilog_ensure_dump_id_ctrl(hdl, ilog); | |||
| 572 | if (err) | |||
| 573 | return err; | |||
| 574 | ||||
| 575 | da = get_max_da(hdl, ilog, ttype); | |||
| 576 | mdts = ilog->id_ctrl.mdts; | |||
| 577 | ||||
| 578 | if (da == 4) { | |||
| 579 | nvme_init_get_features_host_behavior(&cmd, 0, &prev); | |||
| 580 | int err = libnvme_exec_admin_passthru(hdl, &cmd); | |||
| 581 | ||||
| 582 | if (!err && !prev.etdas) { | |||
| 583 | struct nvme_feat_host_behavior da4_enable = prev; | |||
| 584 | ||||
| 585 | da4_enable.etdas = 1; | |||
| 586 | nvme_init_set_features_host_behavior(&cmd, 0, &da4_enable); | |||
| 587 | libnvme_exec_admin_passthru(hdl, &cmd); | |||
| 588 | host_behavior_changed = true1; | |||
| 589 | } | |||
| 590 | } | |||
| 591 | ||||
| 592 | switch (ttype) { | |||
| 593 | case HIT: | |||
| 594 | case ALL: | |||
| 595 | case EXTENDED: | |||
| 596 | file_name = "lid_0x07_lsp_0x01_lsi_0x0000.bin"; | |||
| 597 | log.desc = "Host Initiated Telemetry"; | |||
| 598 | err = sldgm_dynamic_telemetry(hdl, true1, false0, false0, mdts, | |||
| 599 | da, (struct nvme_telemetry_log **) &log.buffer, | |||
| 600 | &log.buffer_size); | |||
| 601 | break; | |||
| 602 | case CIT: | |||
| 603 | file_name = "lid_0x08_lsp_0x00_lsi_0x0000.bin"; | |||
| 604 | log.desc = "Controller Initiated Telemetry"; | |||
| 605 | err = sldgm_dynamic_telemetry(hdl, false0, true1, true1, mdts, | |||
| 606 | da, (struct nvme_telemetry_log **) &log.buffer, | |||
| 607 | &log.buffer_size); | |||
| 608 | break; | |||
| 609 | default: | |||
| 610 | return -EINVAL22; | |||
| 611 | } | |||
| 612 | ||||
| 613 | if (host_behavior_changed) { | |||
| 614 | nvme_init_set_features_host_behavior(&cmd, 0, &prev); | |||
| 615 | libnvme_exec_admin_passthru(hdl, &cmd); | |||
| 616 | } | |||
| 617 | ||||
| 618 | if (err) | |||
| 619 | return err; | |||
| 620 | ||||
| 621 | err = log_save(&log, ilog->cfg->out_dir, "log_pages", file_name, log.buffer, | |||
| 622 | log.buffer_size); | |||
| 623 | return err; | |||
| 624 | } | |||
| 625 | ||||
| 626 | static int ilog_dump_identify_pages(struct libnvme_transport_handle *hdl, struct ilog *ilog) | |||
| 627 | { | |||
| 628 | struct nvme_ns_list ns_attached_list; | |||
| 629 | struct nvme_ns_list ns_allocated_list; | |||
| 630 | __u32 j = 0; | |||
| 631 | ||||
| 632 | struct log identify_base_list[] = { | |||
| 633 | {NVME_IDENTIFY_CNS_NS_ACTIVE_LIST, "Id Active Namespace ID list", | |||
| 634 | sizeof(ns_attached_list), (__u8 *) &ns_attached_list}, | |||
| 635 | {NVME_IDENTIFY_CNS_NVMSET_LIST, "Id NVM Set List"}, | |||
| 636 | {NVME_IDENTIFY_CNS_CSI_CTRL, "Id I/O Command Set specific"}, | |||
| 637 | {NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, "Id Allocated Namespace ID list", | |||
| 638 | sizeof(ns_allocated_list), (__u8 *) &ns_allocated_list}, | |||
| 639 | {NVME_IDENTIFY_CNS_CTRL_LIST, "Id Controller List"} | |||
| 640 | }; | |||
| 641 | struct log identify_ns_required_list[] = { | |||
| 642 | {NVME_IDENTIFY_CNS_NS, "Id Namespace data"}, | |||
| 643 | {NVME_IDENTIFY_CNS_NS_DESC_LIST, "Id Namespace Id Descriptor list"}, | |||
| 644 | {NVME_IDENTIFY_CNS_CSI_NS, "Id Namespace ID I/O Command Set specific"}, | |||
| 645 | {NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS, | |||
| 646 | "I/O Command Set Independent Identify Namespace Data"}, | |||
| 647 | {NVME_IDENTIFY_CNS_NS_CTRL_LIST, "Id Namespace Id Controller List"}, | |||
| 648 | }; | |||
| 649 | ||||
| 650 | struct log allocated = {NVME_IDENTIFY_CNS_ALLOCATED_NS, "Allocated Namespace Data", | |||
| 651 | NVME_IDENTIFY_DATA_SIZE, NULL((void*)0)}; | |||
| 652 | ||||
| 653 | ilog_ensure_dump_id_ctrl(hdl, ilog); | |||
| 654 | ||||
| 655 | for (int i = 0; i < ARRAY_SIZE(identify_base_list)(sizeof(identify_base_list) / sizeof((identify_base_list)[0]) ); i++) { | |||
| 656 | int err = ilog_dump_identify_page(hdl, ilog, &identify_base_list[i], 0); | |||
| 657 | ||||
| 658 | if (err == 0) | |||
| 659 | ilog->count++; | |||
| 660 | } | |||
| 661 | ||||
| 662 | while (ns_attached_list.ns[j]) { | |||
| 663 | for (int i = 0; i < ARRAY_SIZE(identify_ns_required_list)(sizeof(identify_ns_required_list) / sizeof((identify_ns_required_list )[0])); i++) { | |||
| 664 | int err = ilog_dump_identify_page(hdl, ilog, &identify_ns_required_list[i], | |||
| 665 | ns_attached_list.ns[j]); | |||
| 666 | ||||
| 667 | if (err == 0) | |||
| 668 | ilog->count++; | |||
| 669 | } | |||
| 670 | j++; | |||
| 671 | } | |||
| 672 | ||||
| 673 | j = 0; | |||
| 674 | while (ns_allocated_list.ns[j]) { | |||
| 675 | int err = ilog_dump_identify_page(hdl, ilog, &allocated, ns_allocated_list.ns[j]); | |||
| 676 | ||||
| 677 | if (err == 0) | |||
| 678 | ilog->count++; | |||
| 679 | j++; | |||
| 680 | } | |||
| 681 | ||||
| 682 | return 0; | |||
| 683 | } | |||
| 684 | ||||
| 685 | static int ilog_dump_log_page(struct libnvme_transport_handle *hdl, struct ilog *ilog, struct log *lp, __u32 nsid) | |||
| 686 | { | |||
| 687 | __u8 *buff = lp->buffer; | |||
| 688 | __cleanup_free__attribute__((cleanup(freep))) char *filename = NULL((void*)0); | |||
| 689 | ||||
| 690 | int err; | |||
| 691 | if (!lp->buffer_size) | |||
| 692 | return -EINVAL22; | |||
| 693 | if (!buff) { | |||
| 694 | buff = libnvme_alloc(lp->buffer_size); | |||
| 695 | if (!buff) | |||
| 696 | return -ENOMEM12; | |||
| 697 | } | |||
| 698 | err = nvme_get_nsid_log(hdl, 0, 0, lp->id, buff, lp->buffer_size); | |||
| 699 | if (err) | |||
| 700 | return err; | |||
| 701 | ||||
| 702 | if (asprintf(&filename, "lid_0x%02x_lsp_0x00_lsi_0x0000.bin", lp->id) < 0) | |||
| 703 | return -errno(*__errno_location ()); | |||
| 704 | ||||
| 705 | return log_save(lp, ilog->cfg->out_dir, "log_pages", filename, buff, lp->buffer_size); | |||
| 706 | } | |||
| 707 | ||||
| 708 | static int ilog_dump_no_lsp_log_pages(struct libnvme_transport_handle *hdl, struct ilog *ilog) | |||
| 709 | { | |||
| 710 | struct lba_status_info { | |||
| 711 | __u32 lslplen; | |||
| 712 | __u32 nlslne; | |||
| 713 | __u32 estulb; | |||
| 714 | __u16 rsvd; | |||
| 715 | __u16 lsgc; | |||
| 716 | } lba_status = {}; | |||
| 717 | __u64 num_entries = 0; | |||
| 718 | struct log log_page_dependent_list[] = { | |||
| 719 | {NVME_LOG_LID_LBA_STATUS}, | |||
| 720 | {NVME_LOG_LID_ENDURANCE_GRP_EVT}, | |||
| 721 | }; | |||
| 722 | struct log log_page_base_list[] = { | |||
| 723 | {NVME_LOG_LID_SUPPORTED_LOG_PAGES, NULL((void*)0), sizeof(struct nvme_supported_log_pages)}, | |||
| 724 | {NVME_LOG_LID_ERROR, NULL((void*)0), | |||
| 725 | (ilog->id_ctrl.elpe + 1) * sizeof(struct nvme_error_log_page)}, | |||
| 726 | {NVME_LOG_LID_SMART, NULL((void*)0), sizeof(struct nvme_smart_log)}, | |||
| 727 | {NVME_LOG_LID_FW_SLOT, NULL((void*)0), sizeof(struct nvme_firmware_slot)}, | |||
| 728 | {NVME_LOG_LID_CHANGED_NS, NULL((void*)0), sizeof(struct nvme_ns_list)}, | |||
| 729 | {NVME_LOG_LID_CMD_EFFECTS, NULL((void*)0), sizeof(struct nvme_cmd_effects_log)}, | |||
| 730 | {NVME_LOG_LID_DEVICE_SELF_TEST, NULL((void*)0), sizeof(struct nvme_self_test_log)}, | |||
| 731 | {NVME_LOG_LID_LBA_STATUS, NULL((void*)0), sizeof(lba_status), (__u8 *) &lba_status}, | |||
| 732 | {NVME_LOG_LID_ENDURANCE_GRP_EVT, NULL((void*)0), sizeof(num_entries), (__u8 *) &num_entries}, | |||
| 733 | {NVME_LOG_LID_FID_SUPPORTED_EFFECTS, NULL((void*)0), | |||
| 734 | sizeof(struct nvme_fid_supported_effects_log)}, | |||
| 735 | {NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS, NULL((void*)0), | |||
| 736 | sizeof(struct nvme_mi_cmd_supported_effects_log)}, | |||
| 737 | {NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN, NULL((void*)0), 512}, | |||
| 738 | {NVME_LOG_LID_PHY_RX_EOM, NULL((void*)0), 512}, | |||
| 739 | {NVME_LOG_LID_SANITIZE, NULL((void*)0), sizeof(struct nvme_sanitize_log_page)}, | |||
| 740 | {0xC0, "OCP or VU SMART / Health Information Extended", 512}, | |||
| 741 | {0xC1, "OCP Error Recovery or VU Latency Reads", 512}, | |||
| 742 | {0xC2, "OCP Firmware Activation History or VU Latency Writes", 4096}, | |||
| 743 | {0xC3, "OCP Latency Monitor", 512}, | |||
| 744 | {0xC4, "OCP Device Capabilities or VU Endurance Manager Statistics", 4096}, | |||
| 745 | {0xC5, "OCP Unsupported Requirements or VU Tempeture Statistics", 4096}, | |||
| 746 | {0xC7, "OCP TCG Configuration", 512}, | |||
| 747 | {0xCA, "SMART Attributes", 512}, | |||
| 748 | {0xd5, "Tempeture Statistics", 512}, | |||
| 749 | {0xfe, "Latency Outlier", 8192}, | |||
| 750 | }; | |||
| 751 | ||||
| 752 | for (int i = 0; i < ARRAY_SIZE(log_page_base_list)(sizeof(log_page_base_list) / sizeof((log_page_base_list)[0]) ); i++) { | |||
| 753 | log_page_base_list[i].desc = log_page_base_list[i].desc ? | |||
| 754 | log_page_base_list[i].desc : | |||
| 755 | nvme_log_to_string(log_page_base_list[i].id); | |||
| 756 | if (!ilog_dump_log_page(hdl, ilog, &log_page_base_list[i], 0)) | |||
| 757 | ilog->count++; | |||
| 758 | } | |||
| 759 | ||||
| 760 | /* if needed, patch logs based on retrieved log size */ | |||
| 761 | if (lba_status.lslplen > sizeof(lba_status)) | |||
| 762 | log_page_dependent_list[0].buffer_size = lba_status.lslplen; | |||
| 763 | if (num_entries) | |||
| 764 | log_page_dependent_list[1].buffer_size = sizeof(num_entries) + | |||
| 765 | (num_entries * sizeof(__u16)); | |||
| 766 | ||||
| 767 | for (int i = 0; i < ARRAY_SIZE(log_page_dependent_list)(sizeof(log_page_dependent_list) / sizeof((log_page_dependent_list )[0])); i++) { | |||
| 768 | log_page_dependent_list[i].desc = log_page_dependent_list[i].desc ? | |||
| 769 | log_page_dependent_list[i].desc : | |||
| 770 | nvme_log_to_string(log_page_dependent_list[i].id); | |||
| 771 | ilog_dump_log_page(hdl, ilog, &log_page_dependent_list[i], 0); | |||
| 772 | } | |||
| 773 | ||||
| 774 | return 0; | |||
| 775 | } | |||
| 776 | ||||
| 777 | static int ilog_dump_pel(struct libnvme_transport_handle *hdl, struct ilog *ilog) | |||
| 778 | { | |||
| 779 | __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_persistent_event_log *pevent = NULL((void*)0); | |||
| 780 | __cleanup_huge__attribute__((cleanup(libnvme_free_huge))) struct libnvme_mem_huge mh = {0}; | |||
| 781 | void *pevent_log_full; | |||
| 782 | struct log lp = { | |||
| 783 | NVME_LOG_LID_PERSISTENT_EVENT, | |||
| 784 | nvme_log_to_string(NVME_LOG_LID_PERSISTENT_EVENT) | |||
| 785 | }; | |||
| 786 | int err; | |||
| 787 | ||||
| 788 | err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_RELEASE_CTX, | |||
| 789 | pevent, sizeof(*pevent)); | |||
| 790 | if (err) | |||
| 791 | return err; | |||
| 792 | ||||
| 793 | ||||
| 794 | pevent = libnvme_alloc(sizeof(*pevent)); | |||
| 795 | if (!pevent) | |||
| 796 | return -ENOMEM12; | |||
| 797 | ||||
| 798 | err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_EST_CTX_AND_READ, | |||
| 799 | pevent, sizeof(*pevent)); | |||
| 800 | if (err) | |||
| 801 | return err; | |||
| 802 | ||||
| 803 | lp.buffer_size = le64_to_cpu(pevent->tll); | |||
| 804 | pevent_log_full = libnvme_alloc_huge(lp.buffer_size, &mh); | |||
| 805 | if (!pevent_log_full) | |||
| 806 | return -ENOMEM12; | |||
| 807 | ||||
| 808 | err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_READ, | |||
| 809 | pevent_log_full, lp.buffer_size); | |||
| 810 | if (err) | |||
| 811 | return err; | |||
| 812 | ||||
| 813 | ilog->count++; | |||
| 814 | ||||
| 815 | err = log_save(&lp, ilog->cfg->out_dir, "log_pages", "lid_0x0d_lsp_0x00_lsi_0x0000.bin", | |||
| 816 | pevent_log_full, lp.buffer_size); | |||
| 817 | ||||
| 818 | nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_RELEASE_CTX, | |||
| 819 | pevent, sizeof(*pevent)); | |||
| 820 | ||||
| 821 | return err; | |||
| 822 | } | |||
| 823 | ||||
| 824 | int solidigm_get_internal_log(int argc, char **argv, struct command *acmd, | |||
| 825 | struct plugin *plugin) | |||
| 826 | { | |||
| 827 | char sn_prefix[sizeof(((struct nvme_id_ctrl *)0)->sn)+1]; | |||
| 828 | char date_str[sizeof("-YYYYMMDDHHMMSS")]; | |||
| 829 | __cleanup_free__attribute__((cleanup(freep))) char *full_folder = NULL((void*)0); | |||
| 830 | __cleanup_free__attribute__((cleanup(freep))) char *unique_folder = NULL((void*)0); | |||
| 831 | __cleanup_free__attribute__((cleanup(freep))) char *zip_name = NULL((void*)0); | |||
| 832 | __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0); | |||
| 833 | __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0); | |||
| 834 | char *initial_folder; | |||
| 835 | char *output_path; | |||
| 836 | struct ilog ilog = {0}; | |||
| 837 | int err; | |||
| 838 | enum log_type log_type = ALL; | |||
| 839 | char type_ALL[] = "ALL"; | |||
| 840 | time_t current_time; | |||
| 841 | DIR *dir; | |||
| 842 | ||||
| 843 | const char *desc = "Get Debug Firmware Logs and save them."; | |||
| 844 | const char *type = "Log type; Defaults to ALL."; | |||
| 845 | const char *out_dir = "Output directory; defaults to current working directory."; | |||
| 846 | ||||
| 847 | struct config cfg = { | |||
| 848 | .out_dir = ".", | |||
| 849 | .type = type_ALL, | |||
| 850 | }; | |||
| 851 | ||||
| 852 | NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED" , CFG_STRING, &cfg.type, 1, type, 0, }, {"dir-name", 'd', "DIRECTORY", CFG_STRING, &cfg.out_dir, 1, out_dir, 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) } } | |||
| 853 | OPT_STRING("type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED", &cfg.type, type),struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED" , CFG_STRING, &cfg.type, 1, type, 0, }, {"dir-name", 'd', "DIRECTORY", CFG_STRING, &cfg.out_dir, 1, out_dir, 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) } } | |||
| 854 | OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.out_dir, out_dir))struct argconfig_commandline_options opts[] = { {"", 0, ((void *)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void *)0)}, {"type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT|EXTENDED" , CFG_STRING, &cfg.type, 1, type, 0, }, {"dir-name", 'd', "DIRECTORY", CFG_STRING, &cfg.out_dir, 1, out_dir, 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) } }; | |||
| 855 | ||||
| 856 | err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); | |||
| 857 | if (err) | |||
| 858 | return err; | |||
| 859 | ilog.cfg = &cfg; | |||
| 860 | ||||
| 861 | for (char *p = cfg.type; *p; ++p) | |||
| 862 | *p = toupper(*p)(__extension__ ({ int __res; if (sizeof (*p) > 1) { if (__builtin_constant_p (*p)) { int __c = (*p); __res = __c < -128 || __c > 255 ? __c : (*__ctype_toupper_loc ())[__c]; } else __res = toupper (*p); } else __res = (*__ctype_toupper_loc ())[(int) (*p)]; __res ; })); | |||
| 863 | ||||
| 864 | if (!strcmp(cfg.type, "ALL")) | |||
| 865 | log_type = ALL; | |||
| 866 | else if (!strcmp(cfg.type, "HIT")) | |||
| 867 | log_type = HIT; | |||
| 868 | else if (!strcmp(cfg.type, "CIT")) | |||
| 869 | log_type = CIT; | |||
| 870 | else if (!strcmp(cfg.type, "NLOG")) | |||
| 871 | log_type = NLOG; | |||
| 872 | else if (!strcmp(cfg.type, "ASSERT")) | |||
| 873 | log_type = ASSERTLOG; | |||
| 874 | else if (!strcmp(cfg.type, "EVENT")) | |||
| 875 | log_type = EVENTLOG; | |||
| 876 | else if (!strcmp(cfg.type, "EXTENDED")) | |||
| 877 | log_type = EXTENDED; | |||
| 878 | else { | |||
| 879 | fprintf(stderrstderr, "Invalid log type: %s\n", cfg.type); | |||
| 880 | return -EINVAL22; | |||
| 881 | } | |||
| 882 | ||||
| 883 | dir = opendir(cfg.out_dir); | |||
| 884 | if (dir) | |||
| 885 | closedir(dir); | |||
| 886 | else { | |||
| 887 | perror(cfg.out_dir); | |||
| 888 | return -errno(*__errno_location ()); | |||
| 889 | } | |||
| 890 | ||||
| 891 | initial_folder = cfg.out_dir; | |||
| 892 | ||||
| 893 | err = get_serial_number(sn_prefix, hdl); | |||
| 894 | if (err) | |||
| 895 | return err; | |||
| 896 | ||||
| 897 | current_time = time(NULL((void*)0)); | |||
| 898 | strftime(date_str, sizeof(date_str), "-%Y%m%d%H%M%S", localtime(¤t_time)); | |||
| 899 | if (asprintf(&unique_folder, "%s%s", sn_prefix, date_str) < 0) | |||
| 900 | return -errno(*__errno_location ()); | |||
| 901 | if (asprintf(&full_folder, "%s/%s", cfg.out_dir, unique_folder) < 0) | |||
| 902 | return -errno(*__errno_location ()); | |||
| 903 | ||||
| 904 | if (mkdir(full_folder, 0755) != 0) { | |||
| 905 | perror("mkdir"); | |||
| 906 | return -errno(*__errno_location ()); | |||
| 907 | } | |||
| 908 | ||||
| 909 | cfg.out_dir = full_folder; | |||
| 910 | output_path = full_folder; | |||
| 911 | ||||
| 912 | /* Retrieve first logs that records actions to retrieve other logs */ | |||
| 913 | if (log_type == ALL || log_type == HIT || log_type == EXTENDED) { | |||
| 914 | err = ilog_dump_telemetry(hdl, &ilog, log_type); | |||
| 915 | if (err == 0) | |||
| 916 | ilog.count++; | |||
| 917 | else if (err < 0) | |||
| 918 | perror("Error retrieving Host Initiated Telemetry"); | |||
| 919 | } | |||
| 920 | if (log_type == ALL || log_type == NLOG || log_type == EXTENDED) { | |||
| 921 | err = ilog_dump_nlogs(hdl, &ilog, -1); | |||
| 922 | if (err == 0) | |||
| 923 | ilog.count++; | |||
| 924 | else if (err < 0) | |||
| 925 | perror("Error retrieving Nlog"); | |||
| 926 | } | |||
| 927 | if (log_type == ALL || log_type == CIT || log_type == EXTENDED) { | |||
| 928 | err = ilog_dump_telemetry(hdl, &ilog, CIT); | |||
| 929 | if (err == 0) | |||
| 930 | ilog.count++; | |||
| 931 | else if (err < 0) | |||
| 932 | perror("Error retrieving Controller Initiated Telemetry"); | |||
| 933 | } | |||
| 934 | if (log_type == ALL || log_type == ASSERTLOG || log_type == EXTENDED) { | |||
| 935 | err = ilog_dump_assert_logs(hdl, &ilog); | |||
| 936 | if (err == 0) | |||
| 937 | ilog.count++; | |||
| 938 | else if (err < 0) | |||
| 939 | perror("Error retrieving Assert log"); | |||
| 940 | } | |||
| 941 | if (log_type == ALL || log_type == EVENTLOG || log_type == EXTENDED) { | |||
| 942 | err = ilog_dump_event_logs(hdl, &ilog); | |||
| 943 | if (err == 0) | |||
| 944 | ilog.count++; | |||
| 945 | else if (err < 0) | |||
| 946 | perror("Error retrieving Event log"); | |||
| 947 | } | |||
| 948 | if (log_type == ALL || log_type == EXTENDED) { | |||
| 949 | err = ilog_dump_identify_pages(hdl, &ilog); | |||
| 950 | if (err < 0) | |||
| 951 | perror("Error retrieving Identify pages"); | |||
| 952 | ||||
| 953 | err = ilog_dump_pel(hdl, &ilog); | |||
| 954 | if (err < 0) | |||
| 955 | perror("Error retrieving Persistent Event Log page"); | |||
| 956 | ||||
| 957 | err = ilog_dump_no_lsp_log_pages(hdl, &ilog); | |||
| 958 | if (err < 0) | |||
| 959 | perror("Error retrieving no LSP Log pages"); | |||
| 960 | } | |||
| 961 | ||||
| 962 | if (ilog.count > 0) { | |||
| 963 | int ret_cmd; | |||
| 964 | __cleanup_free__attribute__((cleanup(freep))) char *cmd = NULL((void*)0); | |||
| 965 | char *quiet = nvme_args.verbose ? "" : " -q"; | |||
| 966 | ||||
| 967 | if (asprintf(&zip_name, "%s.zip", unique_folder) < 0) | |||
| 968 | return -errno(*__errno_location ()); | |||
| 969 | ||||
| 970 | if (asprintf(&cmd, "cd \"%s\" && zip -MM -r \"../%s\" ./* %s", cfg.out_dir, | |||
| 971 | zip_name, quiet) < 0) { | |||
| 972 | err = errno(*__errno_location ()); | |||
| 973 | perror("Can't allocate string for zip command"); | |||
| 974 | goto out; | |||
| 975 | } | |||
| 976 | printf("Compressing logs to %s\n", zip_name); | |||
| 977 | ret_cmd = system(cmd); | |||
| 978 | if (ret_cmd) | |||
| 979 | perror(cmd); | |||
| 980 | else { | |||
| 981 | output_path = zip_name; | |||
| 982 | if (asprintf(&cmd, "rm -rf %s", cfg.out_dir) < 0) { | |||
| 983 | err = errno(*__errno_location ()); | |||
| 984 | perror("Can't allocate string for cleanup"); | |||
| 985 | goto out; | |||
| 986 | } | |||
| 987 | if (system(cmd) != 0) | |||
| 988 | perror("Failed removing logs folder"); | |||
| 989 | } | |||
| 990 | } | |||
| 991 | ||||
| 992 | out: | |||
| 993 | if (ilog.count == 0) { | |||
| 994 | if (err > 0) | |||
| 995 | nvme_show_status(err); | |||
| 996 | ||||
| 997 | } else if ((ilog.count > 0) || cfg.verbose) | |||
| 998 | printf("Total: %d log files in %s/%s\n", ilog.count, initial_folder, output_path); | |||
| 999 | ||||
| 1000 | return err; | |||
| 1001 | } |