| File: | .build-ci/../plugins/ocp/ocp-telemetry-decode.c |
| Warning: | line 1354, column 3 Potential leak of memory pointed to by 'description' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | /* Copyright (c) 2024 Western Digital Corporation or its affiliates. | |||
| 3 | * | |||
| 4 | * Authors: Jeff Lien <jeff.lien@wdc.com>, | |||
| 5 | */ | |||
| 6 | #include <libnvme.h> | |||
| 7 | ||||
| 8 | #include <ccan/array_size/array_size.h> | |||
| 9 | #include <ccan/endian/endian.h> | |||
| 10 | #include <shared/hex-util.h> | |||
| 11 | #include <shared/string-util.h> | |||
| 12 | ||||
| 13 | #include "nvme-print.h" | |||
| 14 | #include "ocp-telemetry-decode.h" | |||
| 15 | ||||
| 16 | static const struct statistic_entry statistic_identifiers_map[] = { | |||
| 17 | { 0x00, "Error, this entry does not exist." }, | |||
| 18 | { 0x01, "Outstanding Admin Commands" }, | |||
| 19 | { 0x02, "Host Write Bandwidth"}, | |||
| 20 | { 0x03, "GC Write Bandwidth"}, | |||
| 21 | { 0x04, "Active Namespaces"}, | |||
| 22 | { 0x05, "Internal Write Workload"}, | |||
| 23 | { 0x06, "Internal Read Workload"}, | |||
| 24 | { 0x07, "Internal Write Queue Depth"}, | |||
| 25 | { 0x08, "Internal Read Queue Depth"}, | |||
| 26 | { 0x09, "Pending Trim LBA Count"}, | |||
| 27 | { 0x0A, "Host Trim LBA Request Count"}, | |||
| 28 | { 0x0B, "Current NVMe Power State"}, | |||
| 29 | { 0x0C, "Current DSSD Power State"}, | |||
| 30 | { 0x0D, "Program Fail Count"}, | |||
| 31 | { 0x0E, "Erase Fail Count"}, | |||
| 32 | { 0x0F, "Read Disturb Writes"}, | |||
| 33 | { 0x10, "Retention Writes"}, | |||
| 34 | { 0x11, "Wear Leveling Writes"}, | |||
| 35 | { 0x12, "Read Recovery Writes"}, | |||
| 36 | { 0x13, "GC Writes"}, | |||
| 37 | { 0x14, "SRAM Correctable Count"}, | |||
| 38 | { 0x15, "DRAM Correctable Count"}, | |||
| 39 | { 0x16, "SRAM Uncorrectable Count"}, | |||
| 40 | { 0x17, "DRAM Uncorrectable Count"}, | |||
| 41 | { 0x18, "Data Integrity Error Count"}, | |||
| 42 | { 0x19, "Read Retry Error Count"}, | |||
| 43 | { 0x1A, "PERST Events Count"}, | |||
| 44 | { 0x1B, "Max Die Bad Block"}, | |||
| 45 | { 0x1C, "Max NAND Channel Bad Block"}, | |||
| 46 | { 0x1D, "Minimum NAND Channel Bad Block"}, | |||
| 47 | { 0x1E, "Physical Media Units Written"}, | |||
| 48 | { 0x1F, "Physical Media Units Read"}, | |||
| 49 | { 0x20, "Bad User NAND Blocks"}, | |||
| 50 | { 0x21, "Bad System NAND Blocks"}, | |||
| 51 | { 0x22, "XOR Recovery Count"}, | |||
| 52 | { 0x23, "Uncorrectable Read Error Count"}, | |||
| 53 | { 0x24, "Soft ECC Error Count"}, | |||
| 54 | { 0x25, "End to End Correction Counts"}, | |||
| 55 | { 0x26, "System Data % Used"}, | |||
| 56 | { 0x27, "Refresh Counts"}, | |||
| 57 | { 0x28, "User Data Erase Counts"}, | |||
| 58 | { 0x29, "Thermal Throttling Status and Count"}, | |||
| 59 | { 0x2A, "DSSD Specification Version"}, | |||
| 60 | { 0x2B, "PCIe Correctable Error Count"}, | |||
| 61 | { 0x2C, "Incomplete Shutdowns"}, | |||
| 62 | { 0x2D, "% Free Blocks"}, | |||
| 63 | { 0x2E, "Capacitor Health"}, | |||
| 64 | { 0x2F, "NVM Express Base Errata Version"}, | |||
| 65 | { 0x30, "NVM Command Set Errata Version"}, | |||
| 66 | { 0x31, "NVM Express Management Interface Errata Version"}, | |||
| 67 | { 0x32, "Unaligned I/O"}, | |||
| 68 | { 0x33, "Security Version Number"}, | |||
| 69 | { 0x34, "Total NUSE"}, | |||
| 70 | { 0x35, "PLP Start Count"}, | |||
| 71 | { 0x36, "Endurance Estimate"}, | |||
| 72 | { 0x37, "PCIe Link Retraining Count"}, | |||
| 73 | { 0x38, "Power State Change Count"}, | |||
| 74 | { 0x39, "Lowest Permitted Firmware Revision"}, | |||
| 75 | { 0x3A, "Log Page Version"}, | |||
| 76 | { 0x3B, "Media Dies Offline"}, | |||
| 77 | { 0x3C, "Max Temperature Recorded"}, | |||
| 78 | { 0x3D, "NAND Avg. Erase Count"}, | |||
| 79 | { 0x3E, "Command Timeouts"}, | |||
| 80 | { 0x3F, "System Area Program Fail Count"}, | |||
| 81 | { 0x40, "System Area Read Fail Count"}, | |||
| 82 | { 0x41, "System Area Erase Fail Count"}, | |||
| 83 | { 0x42, "Max Peak Power Capability"}, | |||
| 84 | { 0x43, "Current Average Power"}, | |||
| 85 | { 0x44, "Lifetime Power Consumed"}, | |||
| 86 | { 0x45, "Error / Assert Count"}, | |||
| 87 | { 0x46, "Device Busy Time"}, | |||
| 88 | { 0x47, "Critical Warning"}, | |||
| 89 | { 0x48, "Composite Temperature"}, | |||
| 90 | { 0x49, "Available Spare"}, | |||
| 91 | { 0x4A, "Available Spare Threshold"}, | |||
| 92 | { 0x4B, "Percentage Used"}, | |||
| 93 | { 0x4C, "Endurance Group Critical Warning Summary"}, | |||
| 94 | { 0x4D, "Data Units Read"}, | |||
| 95 | { 0x4E, "Data Units Written"}, | |||
| 96 | { 0x4F, "Host Read Commands"}, | |||
| 97 | { 0x50, "Host Write Commands"}, | |||
| 98 | { 0x51, "Controller Busy Time"}, | |||
| 99 | { 0x52, "Power Cycles"}, | |||
| 100 | { 0x53, "Power On Hours"}, | |||
| 101 | { 0x54, "Unsafe Shutdowns"}, | |||
| 102 | { 0x55, "Media and Data Integrity Errors"}, | |||
| 103 | { 0x56, "Number of Error Information Log Entries"}, | |||
| 104 | { 0x57, "Warning Composite Temperature Time"}, | |||
| 105 | { 0x58, "Critical Composite Temperature Time"}, | |||
| 106 | { 0x59, "Temperature Sensor 1"}, | |||
| 107 | { 0x5A, "Temperature Sensor 2"}, | |||
| 108 | { 0x5B, "Temperature Sensor 3"}, | |||
| 109 | { 0x5C, "Temperature Sensor 4"}, | |||
| 110 | { 0x5D, "Temperature Sensor 5"}, | |||
| 111 | { 0x5E, "Temperature Sensor 6"}, | |||
| 112 | { 0x5F, "Temperature Sensor 7"}, | |||
| 113 | { 0x60, "Temperature Sensor 8"}, | |||
| 114 | { 0x61, "Thermal Management Temperature 1 Transition Count"}, | |||
| 115 | { 0x62, "Thermal Management Temperature 2 Transition Count"}, | |||
| 116 | { 0x63, "Total Time For Thermal Management Temperature 1"}, | |||
| 117 | { 0x64, "Total Time For Thermal Management Temperature 2"}, | |||
| 118 | { 0x65, "Endurance Estimate"}, | |||
| 119 | { 0x66, "Data Units Read"}, | |||
| 120 | { 0x67, "Data Units Written"}, | |||
| 121 | { 0x68, "Media Units Written"}, | |||
| 122 | { 0x69, "Number of Error Information Log Entries"}, | |||
| 123 | { 0x6A, "Form Factor"}, | |||
| 124 | { 0x6B, "Dies In Use Bad NAND Blocks"}, | |||
| 125 | { 0x6C, "Proactive Bad Die Retirement"}, | |||
| 126 | { 0x6D, "Namespace ID Context Statistic Descriptor"}, | |||
| 127 | { 0x6E, "Controller ID Context Statistic Descriptor"}, | |||
| 128 | { 0x6F, "Queue ID Context Statistic Descriptor"} | |||
| 129 | }; | |||
| 130 | ||||
| 131 | /* OCP 2.7 Section 4.9.15.2 Event IDs; reserved and obsolete IDs are NULL. */ | |||
| 132 | static const char * const timestamp_event_id_str[] = { | |||
| 133 | [0x0000] = "Host issued a Timestamp Set Feature Command (Feature ID Eh)", | |||
| 134 | [0x0001] = "Firmware initiated Timestamp Snapshot", | |||
| 135 | }; | |||
| 136 | ||||
| 137 | static const char * const pcie_event_id_str[] = { | |||
| 138 | [0x0000] = "Link Up", | |||
| 139 | [0x0001] = "Link Down", | |||
| 140 | [0x0002] = "PCIe Error Detected", | |||
| 141 | [0x0003] = "PERST# Asserted", | |||
| 142 | [0x0004] = "PERST# De-asserted", | |||
| 143 | [0x0005] = "Refclk Stable", | |||
| 144 | [0x0006] = "Vmain Stable", | |||
| 145 | [0x0007] = "Link Speed and Width Negotiated", | |||
| 146 | }; | |||
| 147 | ||||
| 148 | static const char * const nvme_event_id_str[] = { | |||
| 149 | [0x0000] = "CC.EN transitions from 0b to 1b", | |||
| 150 | [0x0001] = "CC.EN transitions from 1b to 0b", | |||
| 151 | [0x0002] = "CSTS.RDY transitions from 0b to 1b", | |||
| 152 | [0x0003] = "CSTS.RDY transitions from 1b to 0b", | |||
| 153 | [0x0005] = "Create I/O Submission Queue Command or Create I/O Completion Queue Command Processed successfully", | |||
| 154 | [0x0006] = "In-band Admin Command Processed other than Create I/O Submission Queue Command or Create I/O Completion Queue Command Processed successfully", | |||
| 155 | [0x0007] = "An Admin command returned a non-zero Status Code", | |||
| 156 | [0x0008] = "An I/O Command Returned a Non-zero Status Code", | |||
| 157 | [0x0009] = "CSTS.CFS transitioned from 0b to 1b", | |||
| 158 | [0x000A] = "Admin Submission Queue Base Address Written (AQA) or Admin Completion Queue Based Address (ACQ) written", | |||
| 159 | [0x000B] = "Controller Configuration Register (CC) Changed except for the cases that are covered in 0000h and 0001h", | |||
| 160 | [0x000C] = "Controller Status Register (CSTS) Changed except for the cases that are covered in 0002h, 0003h, and 0009h", | |||
| 161 | [0x000D] = "Delete I/O Completion Queue Command or Delete I/O Submission Queue Command Processed", | |||
| 162 | [0x000E] = "Out-of-band Command Processed", | |||
| 163 | [0x000F] = "Out-of-band Asynchronous Event Message Transmitted", | |||
| 164 | [0x0010] = "Error Reporting AEN was sent (see EREC-5)", | |||
| 165 | }; | |||
| 166 | ||||
| 167 | static const char * const reset_event_id_str[] = { | |||
| 168 | [0x0000] = "PCIe Conventional Hot Reset", | |||
| 169 | [0x0001] = "Main Power Cycle", | |||
| 170 | [0x0002] = "PERST#", | |||
| 171 | [0x0003] = "PCIe Function Level Reset", | |||
| 172 | [0x0004] = "NVM Subsystem Reset", | |||
| 173 | }; | |||
| 174 | ||||
| 175 | static const char * const boot_sequence_event_id_str[] = { | |||
| 176 | [0x0000] = "Main Firmware Boot Complete", | |||
| 177 | [0x0001] = "FTL Load from NVM Complete", | |||
| 178 | [0x0002] = "FTL Rebuild Started", | |||
| 179 | [0x0003] = "FTL Rebuild Complete", | |||
| 180 | }; | |||
| 181 | ||||
| 182 | static const char * const firmware_assert_event_id_str[] = { | |||
| 183 | [0x0000] = "Assert in NVMe Processing Code", | |||
| 184 | [0x0001] = "Assert in Media Code", | |||
| 185 | [0x0002] = "Assert in Security Code", | |||
| 186 | [0x0003] = "Assert in Background Services Code", | |||
| 187 | [0x0004] = "FTL Rebuild Failed", | |||
| 188 | [0x0005] = "FTL Data Mismatch", | |||
| 189 | [0x0006] = "Assert in Other Code", | |||
| 190 | }; | |||
| 191 | ||||
| 192 | static const char * const temperature_event_id_str[] = { | |||
| 193 | [0x0000] = "Composite Temperature decreases to (WCTEMP - 2)", | |||
| 194 | [0x0001] = "Composite Temperature increases to WCTEMP", | |||
| 195 | [0x0002] = "Composite Temperature increases to reach CCTEMP", | |||
| 196 | }; | |||
| 197 | ||||
| 198 | static const char * const media_event_id_str[] = { | |||
| 199 | [0x0000] = "XOR (or equivalent) Recovery Invoked", | |||
| 200 | [0x0001] = "Uncorrectable Media Error", | |||
| 201 | [0x0002] = "Block Marked Bad Due to Program Error", | |||
| 202 | [0x0003] = "Block Marked Bad Due to Erase Error", | |||
| 203 | [0x0004] = "Block Marked Bad Due to Read Error", | |||
| 204 | [0x0005] = "Plane Failure Event", | |||
| 205 | }; | |||
| 206 | ||||
| 207 | static const char * const media_wear_event_id_str[] = { | |||
| 208 | [0x0000] = "Media Wear", | |||
| 209 | }; | |||
| 210 | ||||
| 211 | static const char * const virtual_fifo_event_id_str[] = { | |||
| 212 | [0x0000] = "Virtual FIFO Start", | |||
| 213 | [0x0001] = "Virtual FIFO End", | |||
| 214 | }; | |||
| 215 | ||||
| 216 | static const char * const smbus_event_id_str[] = { | |||
| 217 | [0x0000] = "Timeout Errors", | |||
| 218 | [0x0001] = "PEC Errors", | |||
| 219 | [0x0002] = "Arbitration loss", | |||
| 220 | [0x0003] = "NACK error", | |||
| 221 | }; | |||
| 222 | ||||
| 223 | static const char * const mctp_event_id_str[] = { | |||
| 224 | [0x0000] = "Dropped Packet", | |||
| 225 | [0x0001] = "Dropped Message", | |||
| 226 | [0x0002] = "Discovery and addressing errors", | |||
| 227 | [0x0003] = "Error Status", | |||
| 228 | }; | |||
| 229 | ||||
| 230 | #define EVENT_IDS(t){ t, (sizeof(t) / sizeof((t)[0]) + 0) } { t, ARRAY_SIZE(t)(sizeof(t) / sizeof((t)[0]) + 0) } | |||
| 231 | ||||
| 232 | static const struct { | |||
| 233 | const char * const *str; | |||
| 234 | size_t count; | |||
| 235 | } spec_event_id_str[] = { | |||
| 236 | [TIME_STAMP_CLASS_TYPE] = EVENT_IDS(timestamp_event_id_str){ timestamp_event_id_str, (sizeof(timestamp_event_id_str) / sizeof ((timestamp_event_id_str)[0]) + 0) }, | |||
| 237 | [PCIE_CLASS_TYPE] = EVENT_IDS(pcie_event_id_str){ pcie_event_id_str, (sizeof(pcie_event_id_str) / sizeof((pcie_event_id_str )[0]) + 0) }, | |||
| 238 | [NVME_CLASS_TYPE] = EVENT_IDS(nvme_event_id_str){ nvme_event_id_str, (sizeof(nvme_event_id_str) / sizeof((nvme_event_id_str )[0]) + 0) }, | |||
| 239 | [RESET_CLASS_TYPE] = EVENT_IDS(reset_event_id_str){ reset_event_id_str, (sizeof(reset_event_id_str) / sizeof((reset_event_id_str )[0]) + 0) }, | |||
| 240 | [BOOT_SEQUENCE_CLASS_TYPE] = EVENT_IDS(boot_sequence_event_id_str){ boot_sequence_event_id_str, (sizeof(boot_sequence_event_id_str ) / sizeof((boot_sequence_event_id_str)[0]) + 0) }, | |||
| 241 | [FIRMWARE_ASSERT_CLASS_TYPE] = EVENT_IDS(firmware_assert_event_id_str){ firmware_assert_event_id_str, (sizeof(firmware_assert_event_id_str ) / sizeof((firmware_assert_event_id_str)[0]) + 0) }, | |||
| 242 | [TEMPERATURE_CLASS_TYPE] = EVENT_IDS(temperature_event_id_str){ temperature_event_id_str, (sizeof(temperature_event_id_str) / sizeof((temperature_event_id_str)[0]) + 0) }, | |||
| 243 | [MEDIA_CLASS_TYPE] = EVENT_IDS(media_event_id_str){ media_event_id_str, (sizeof(media_event_id_str) / sizeof((media_event_id_str )[0]) + 0) }, | |||
| 244 | [MEDIA_WEAR_CLASS_TYPE] = EVENT_IDS(media_wear_event_id_str){ media_wear_event_id_str, (sizeof(media_wear_event_id_str) / sizeof((media_wear_event_id_str)[0]) + 0) }, | |||
| 245 | [VIRTUAL_FIFO_EVENT_CLASS_TYPE] = EVENT_IDS(virtual_fifo_event_id_str){ virtual_fifo_event_id_str, (sizeof(virtual_fifo_event_id_str ) / sizeof((virtual_fifo_event_id_str)[0]) + 0) }, | |||
| 246 | [SMBUS_I2C_I3C_EVENT_CLASS_TYPE] = EVENT_IDS(smbus_event_id_str){ smbus_event_id_str, (sizeof(smbus_event_id_str) / sizeof((smbus_event_id_str )[0]) + 0) }, | |||
| 247 | [MCTP_EVENT_CLASS_TYPE] = EVENT_IDS(mctp_event_id_str){ mctp_event_id_str, (sizeof(mctp_event_id_str) / sizeof((mctp_event_id_str )[0]) + 0) }, | |||
| 248 | }; | |||
| 249 | ||||
| 250 | static const struct request_data host_log_page_header[] = { | |||
| 251 | { "LogIdentifier", 1 }, | |||
| 252 | { "Reserved1", 4 }, | |||
| 253 | { "IEEE OUI Identifier", 3 }, | |||
| 254 | { "Telemetry Host-Initiated Data Area 1 Last Block", 2 }, | |||
| 255 | { "Telemetry Host-Initiated Data Area 2 Last Block", 2 }, | |||
| 256 | { "Telemetry Host-Initiated Data Area 3 Last Block", 2 }, | |||
| 257 | { "Reserved2", 2 }, | |||
| 258 | { "Telemetry Host-Initiated Data Area 4 Last Block", 4 }, | |||
| 259 | { "Reserved3", 360 }, | |||
| 260 | { "Telemetry Host-Initiated Scope", 1 }, | |||
| 261 | { "Telemetry Host-Initiated Data Generation Number", 1 }, | |||
| 262 | { "Telemetry Controller-Initiated Data Available", 1 }, | |||
| 263 | { "Telemetry Controller-Initiated Data Generation Number", 1 } | |||
| 264 | }; | |||
| 265 | ||||
| 266 | static const struct request_data controller_log_page_header[] = { | |||
| 267 | { "LogIdentifier", 1 }, | |||
| 268 | { "Reserved1", 4 }, | |||
| 269 | { "IEEE OUI Identifier", 3 }, | |||
| 270 | { "Telemetry Controller-Initiated Data Area 1 Last Block", 2 }, | |||
| 271 | { "Telemetry Controller-Initiated Data Area 2 Last Block", 2 }, | |||
| 272 | { "Telemetry Controller-Initiated Data Area 3 Last Block", 2 }, | |||
| 273 | { "Reserved2", 2 }, | |||
| 274 | { "Telemetry Controller-Initiated Data Area 4 Last Block", 4 }, | |||
| 275 | { "Reserved3", 361 }, | |||
| 276 | { "Telemetry Controller-Initiated Scope", 1 }, | |||
| 277 | { "Telemetry Controller-Initiated Data Available", 1 }, | |||
| 278 | { "Telemetry Controller-Initiated Data Generation Number", 1 } | |||
| 279 | }; | |||
| 280 | ||||
| 281 | static const struct request_data reason_identifier[] = { | |||
| 282 | { "Error ID", 64 }, | |||
| 283 | { "File ID", 8 }, | |||
| 284 | { "Line Number", 2 }, | |||
| 285 | { "Valid Flags", 1 }, | |||
| 286 | { "Reserved", 21 }, | |||
| 287 | { "VU Reason Extension", 32 } | |||
| 288 | }; | |||
| 289 | ||||
| 290 | static const struct request_data ocp_header_in_da1[] = { | |||
| 291 | { "Major Version", 2 }, | |||
| 292 | { "Minor Version", 2 }, | |||
| 293 | { "Reserved1", 4 }, | |||
| 294 | { "Timestamp", 8 }, | |||
| 295 | { "Log page GUID", GUID_LEN16 }, | |||
| 296 | { "Number Telemetry Profiles Supported", 1 }, | |||
| 297 | { "Telemetry Profile Selected", 1 }, | |||
| 298 | { "Reserved2", 6 }, | |||
| 299 | { "Telemetry String Log Size", 8 }, | |||
| 300 | { "Reserved3", 8 }, | |||
| 301 | { "Firmware Revision", 8 }, | |||
| 302 | { "Reserved4", 32 }, | |||
| 303 | { "Data Area 1 Statistic Start", 8 }, | |||
| 304 | { "Data Area 1 Statistic Size", 8 }, | |||
| 305 | { "Data Area 2 Statistic Start", 8 }, | |||
| 306 | { "Data Area 2 Statistic Size", 8 }, | |||
| 307 | { "Reserved5", 32 }, | |||
| 308 | { "Event FIFO 1 Data Area", 1 }, | |||
| 309 | { "Event FIFO 2 Data Area", 1 }, | |||
| 310 | { "Event FIFO 3 Data Area", 1 }, | |||
| 311 | { "Event FIFO 4 Data Area", 1 }, | |||
| 312 | { "Event FIFO 5 Data Area", 1 }, | |||
| 313 | { "Event FIFO 6 Data Area", 1 }, | |||
| 314 | { "Event FIFO 7 Data Area", 1 }, | |||
| 315 | { "Event FIFO 8 Data Area", 1 }, | |||
| 316 | { "Event FIFO 9 Data Area", 1 }, | |||
| 317 | { "Event FIFO 10 Data Area", 1 }, | |||
| 318 | { "Event FIFO 11 Data Area", 1 }, | |||
| 319 | { "Event FIFO 12 Data Area", 1 }, | |||
| 320 | { "Event FIFO 13 Data Area", 1 }, | |||
| 321 | { "Event FIFO 14 Data Area", 1 }, | |||
| 322 | { "Event FIFO 15 Data Area", 1 }, | |||
| 323 | { "Event FIFO 16 Data Area", 1 }, | |||
| 324 | { "Event FIFO 1 Start", 8 }, | |||
| 325 | { "Event FIFO 1 Size", 8 }, | |||
| 326 | { "Event FIFO 2 Start", 8 }, | |||
| 327 | { "Event FIFO 2 Size", 8 }, | |||
| 328 | { "Event FIFO 3 Start", 8 }, | |||
| 329 | { "Event FIFO 3 Size", 8 }, | |||
| 330 | { "Event FIFO 4 Start", 8 }, | |||
| 331 | { "Event FIFO 4 Size", 8 }, | |||
| 332 | { "Event FIFO 5 Start", 8 }, | |||
| 333 | { "Event FIFO 5 Size", 8 }, | |||
| 334 | { "Event FIFO 6 Start", 8 }, | |||
| 335 | { "Event FIFO 6 Size", 8 }, | |||
| 336 | { "Event FIFO 7 Start", 8 }, | |||
| 337 | { "Event FIFO 7 Size", 8 }, | |||
| 338 | { "Event FIFO 8 Start", 8 }, | |||
| 339 | { "Event FIFO 8 Size", 8 }, | |||
| 340 | { "Event FIFO 9 Start", 8 }, | |||
| 341 | { "Event FIFO 9 Size", 8 }, | |||
| 342 | { "Event FIFO 10 Start", 8 }, | |||
| 343 | { "Event FIFO 10 Size", 8 }, | |||
| 344 | { "Event FIFO 11 Start", 8 }, | |||
| 345 | { "Event FIFO 11 Size", 8 }, | |||
| 346 | { "Event FIFO 12 Start", 8 }, | |||
| 347 | { "Event FIFO 12 Size", 8 }, | |||
| 348 | { "Event FIFO 13 Start", 8 }, | |||
| 349 | { "Event FIFO 13 Size", 8 }, | |||
| 350 | { "Event FIFO 14 Start", 8 }, | |||
| 351 | { "Event FIFO 14 Size", 8 }, | |||
| 352 | { "Event FIFO 15 Start", 8 }, | |||
| 353 | { "Event FIFO 15 Size", 8 }, | |||
| 354 | { "Event FIFO 16 Start", 8 }, | |||
| 355 | { "Event FIFO 16 Size", 8 }, | |||
| 356 | { "Reserved6", 80 } | |||
| 357 | }; | |||
| 358 | ||||
| 359 | static const struct request_data smart[] = { | |||
| 360 | { "Critical Warning", 1 }, | |||
| 361 | { "Composite Temperature", 2 }, | |||
| 362 | { "Available Spare", 1 }, | |||
| 363 | { "Available Spare Threshold", 1 }, | |||
| 364 | { "Percentage Used", 1 }, | |||
| 365 | { "Reserved1", 26 }, | |||
| 366 | { "Data Units Read", 16 }, | |||
| 367 | { "Data Units Written", 16 }, | |||
| 368 | { "Host Read Commands", 16 }, | |||
| 369 | { "Host Write Commands", 16 }, | |||
| 370 | { "Controller Busy Time", 16 }, | |||
| 371 | { "Power Cycles", 16 }, | |||
| 372 | { "Power On Hours", 16 }, | |||
| 373 | { "Unsafe Shutdowns", 16 }, | |||
| 374 | { "Media and Data Integrity Errors", 16 }, | |||
| 375 | { "Number of Error Information Log Entries", 16 }, | |||
| 376 | { "Warning Composite Temperature Time", 4 }, | |||
| 377 | { "Critical Composite Temperature Time", 4 }, | |||
| 378 | { "Temperature Sensor 1", 2 }, | |||
| 379 | { "Temperature Sensor 2", 2 }, | |||
| 380 | { "Temperature Sensor 3", 2 }, | |||
| 381 | { "Temperature Sensor 4", 2 }, | |||
| 382 | { "Temperature Sensor 5", 2 }, | |||
| 383 | { "Temperature Sensor 6", 2 }, | |||
| 384 | { "Temperature Sensor 7", 2 }, | |||
| 385 | { "Temperature Sensor 8", 2 }, | |||
| 386 | { "Thermal Management Temperature 1 Transition Count", 4 }, | |||
| 387 | { "Thermal Management Temperature 2 Transition Count", 4 }, | |||
| 388 | { "Total Time for Thermal Management Temperature 1", 4 }, | |||
| 389 | { "Total Time for Thermal Management Temperature 2", 4 }, | |||
| 390 | { "Reserved2", 280 } | |||
| 391 | }; | |||
| 392 | ||||
| 393 | static const struct request_data smart_extended[] = { | |||
| 394 | { "Physical Media Units Written", 16 }, | |||
| 395 | { "Physical Media Units Read", 16 }, | |||
| 396 | { "Bad User NAND Blocks Raw Count", 6 }, | |||
| 397 | { "Bad User NAND Blocks Normalized Value", 2 }, | |||
| 398 | { "Bad System NAND Blocks Raw Count", 6 }, | |||
| 399 | { "Bad System NAND Blocks Normalized Value", 2 }, | |||
| 400 | { "XOR Recovery Count", 8 }, | |||
| 401 | { "Uncorrectable Read Error Count", 8 }, | |||
| 402 | { "Soft ECC Error Count", 8 }, | |||
| 403 | { "End to End Correction Counts Detected Errors", 4 }, | |||
| 404 | { "End to End Correction Counts Corrected Errors", 4 }, | |||
| 405 | { "System Data Percent Used", 1 }, | |||
| 406 | { "Refresh Counts", 7 }, | |||
| 407 | { "Maximum User Data Erase Count", 4 }, | |||
| 408 | { "Minimum User Data Erase Count", 4 }, | |||
| 409 | { "Number of thermal throttling events", 1 }, | |||
| 410 | { "Current Throttling Status", 1 }, | |||
| 411 | { "Errata Version Field", 1 }, | |||
| 412 | { "Point Version Field", 2 }, | |||
| 413 | { "Minor Version Field", 2 }, | |||
| 414 | { "Major Version Field", 1 }, | |||
| 415 | { "PCIe Correctable Error Count", 8 }, | |||
| 416 | { "Incomplete Shutdowns", 4 }, | |||
| 417 | { "Reserved1", 4 }, | |||
| 418 | { "Percent Free Blocks", 1 }, | |||
| 419 | { "Reserved2", 7 }, | |||
| 420 | { "Capacitor Health", 2 }, | |||
| 421 | { "NVMe Base Errata Version", 1 }, | |||
| 422 | { "NVMe Command Set Errata Version", 1 }, | |||
| 423 | { "Reserved3", 4 }, | |||
| 424 | { "Unaligned IO", 8 }, | |||
| 425 | { "Security Version Number", 8 }, | |||
| 426 | { "Total NUSE", 8 }, | |||
| 427 | { "PLP Start Count", 16 }, | |||
| 428 | { "Endurance Estimate", 16 }, | |||
| 429 | { "PCIe Link Retraining Count", 8 }, | |||
| 430 | { "Power State Change Count", 8 }, | |||
| 431 | { "Lowest Permitted Firmware Revision", 8 }, | |||
| 432 | { "Reserved4", 278 }, | |||
| 433 | { "Log Page Version", 2 }, | |||
| 434 | { "Log page GUID", GUID_LEN16 } | |||
| 435 | }; | |||
| 436 | ||||
| 437 | void json_add_formatted_u32_str(struct json_object *pobject, const char *msg, unsigned int pdata) | |||
| 438 | { | |||
| 439 | char data_str[70] = { 0 }; | |||
| 440 | ||||
| 441 | snprintf(data_str, sizeof(data_str), "0x%x", pdata); | |||
| 442 | json_object_add_value_string(pobject, msg, data_str); | |||
| 443 | } | |||
| 444 | ||||
| 445 | void json_add_formatted_var_size_str(struct json_object *pobject, const char *msg, __u8 *pdata, | |||
| 446 | unsigned int data_size) | |||
| 447 | { | |||
| 448 | char *description_str = NULL((void*)0); | |||
| 449 | size_t size = data_size * 2 + 1; | |||
| 450 | ||||
| 451 | /* 2 chars for each value in the data and the null terminator */ | |||
| 452 | description_str = (char *) calloc(1, size); | |||
| 453 | if (!description_str) { | |||
| 454 | nvme_show_error("Failed to allocate description buffer")nvme_show_message(1, "Failed to allocate description buffer"); | |||
| 455 | return; | |||
| 456 | } | |||
| 457 | ||||
| 458 | shr_bin_to_hex(description_str, size, pdata, data_size, SHR_HEX_UPPER(1 << 0)); | |||
| 459 | ||||
| 460 | json_object_add_value_string(pobject, msg, description_str); | |||
| 461 | free(description_str); | |||
| 462 | } | |||
| 463 | ||||
| 464 | /* | |||
| 465 | * A log can hold fewer data areas than its header describes, so each data | |||
| 466 | * area is cut to the bytes actually read. | |||
| 467 | */ | |||
| 468 | static __u64 telemetry_da_size(__u64 start, __u64 end, size_t buf_len) | |||
| 469 | { | |||
| 470 | if (end > buf_len) | |||
| 471 | end = buf_len; | |||
| 472 | ||||
| 473 | return end > start ? end - start : 0; | |||
| 474 | } | |||
| 475 | ||||
| 476 | int get_telemetry_das_offset_and_size( | |||
| 477 | struct nvme_ocp_telemetry_common_header *ptelemetry_common_header, | |||
| 478 | size_t buf_len, struct nvme_ocp_telemetry_offsets *ptelemetry_das_offset) | |||
| 479 | { | |||
| 480 | if (NULL((void*)0) == ptelemetry_common_header || NULL((void*)0) == ptelemetry_das_offset) { | |||
| 481 | nvme_show_error("Invalid input arguments.")nvme_show_message(1, "Invalid input arguments."); | |||
| 482 | return -1; | |||
| 483 | } | |||
| 484 | ||||
| 485 | if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_HOST) | |||
| 486 | ptelemetry_das_offset->header_size = | |||
| 487 | sizeof(struct nvme_ocp_telemetry_host_initiated_header); | |||
| 488 | else if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_CTRL) | |||
| 489 | ptelemetry_das_offset->header_size = | |||
| 490 | sizeof(struct nvme_ocp_telemetry_controller_initiated_header); | |||
| 491 | else | |||
| 492 | return -1; | |||
| 493 | ||||
| 494 | __u64 header_size = ptelemetry_das_offset->header_size; | |||
| 495 | __u64 da1_end = header_size + (__u64)le16_to_cpu(ptelemetry_common_header->da1_last_block) * | |||
| 496 | OCP_TELEMETRY_DATA_BLOCK_SIZE512; | |||
| 497 | __u64 da2_end = header_size + (__u64)le16_to_cpu(ptelemetry_common_header->da2_last_block) * | |||
| 498 | OCP_TELEMETRY_DATA_BLOCK_SIZE512; | |||
| 499 | __u64 da3_end = header_size + (__u64)le16_to_cpu(ptelemetry_common_header->da3_last_block) * | |||
| 500 | OCP_TELEMETRY_DATA_BLOCK_SIZE512; | |||
| 501 | __u64 da4_end = header_size + (__u64)le32_to_cpu(ptelemetry_common_header->da4_last_block) * | |||
| 502 | OCP_TELEMETRY_DATA_BLOCK_SIZE512; | |||
| 503 | ||||
| 504 | ptelemetry_das_offset->da1_start_offset = header_size; | |||
| 505 | ptelemetry_das_offset->da1_size = | |||
| 506 | telemetry_da_size(ptelemetry_das_offset->da1_start_offset, da1_end, buf_len); | |||
| 507 | ||||
| 508 | ptelemetry_das_offset->da2_start_offset = ptelemetry_das_offset->da1_start_offset + | |||
| 509 | ptelemetry_das_offset->da1_size; | |||
| 510 | ptelemetry_das_offset->da2_size = | |||
| 511 | telemetry_da_size(ptelemetry_das_offset->da2_start_offset, da2_end, buf_len); | |||
| 512 | ||||
| 513 | ptelemetry_das_offset->da3_start_offset = ptelemetry_das_offset->da2_start_offset + | |||
| 514 | ptelemetry_das_offset->da2_size; | |||
| 515 | ptelemetry_das_offset->da3_size = | |||
| 516 | telemetry_da_size(ptelemetry_das_offset->da3_start_offset, da3_end, buf_len); | |||
| 517 | ||||
| 518 | ptelemetry_das_offset->da4_start_offset = ptelemetry_das_offset->da3_start_offset + | |||
| 519 | ptelemetry_das_offset->da3_size; | |||
| 520 | ptelemetry_das_offset->da4_size = | |||
| 521 | telemetry_da_size(ptelemetry_das_offset->da4_start_offset, da4_end, buf_len); | |||
| 522 | ||||
| 523 | return 0; | |||
| 524 | } | |||
| 525 | ||||
| 526 | /* | |||
| 527 | * Device tables store ascii_id_length as the zero-based index of the last | |||
| 528 | * character, so the byte count is length + 1. Keep the copy bounded to the | |||
| 529 | * fixed caller buffers used throughout the telemetry parser. | |||
| 530 | */ | |||
| 531 | static size_t ocp_ascii_id_copy_len(__u8 ascii_id_length) | |||
| 532 | { | |||
| 533 | size_t copy_len = (size_t)ascii_id_length + 1; | |||
| 534 | ||||
| 535 | if (copy_len >= OCP_TELEMETRY_DESCRIPTION_MAX256) | |||
| 536 | copy_len = OCP_TELEMETRY_DESCRIPTION_MAX256 - 1; | |||
| 537 | ||||
| 538 | return copy_len; | |||
| 539 | } | |||
| 540 | ||||
| 541 | static int ocp_copy_ascii_id(const struct ocp_telemetry_logs *logs, | |||
| 542 | __le64 ascii_id_offset, __u8 ascii_id_length, | |||
| 543 | char *description) | |||
| 544 | { | |||
| 545 | struct nvme_ocp_telemetry_string_header *pocp_ts_header = | |||
| 546 | (struct nvme_ocp_telemetry_string_header *)logs->string; | |||
| 547 | __u64 ascts = le64_to_cpu(pocp_ts_header->ascts); | |||
| 548 | __u64 asctsz = le64_to_cpu(pocp_ts_header->asctsz); | |||
| 549 | __u64 offset = le64_to_cpu(ascii_id_offset); | |||
| 550 | size_t copy_len = ocp_ascii_id_copy_len(ascii_id_length); | |||
| 551 | ||||
| 552 | /* With the ASCII table inside the log, neither product can overflow. */ | |||
| 553 | if (!ocp_c9_table_fits(ascts, asctsz, logs->string_size) || offset > asctsz || | |||
| 554 | copy_len > (asctsz - offset) * SIZE_OF_DWORD4) | |||
| 555 | return -1; | |||
| 556 | ||||
| 557 | memcpy(description, logs->string + (ascts + offset) * SIZE_OF_DWORD4, copy_len); | |||
| 558 | description[copy_len] = '\0'; | |||
| 559 | ||||
| 560 | return 0; | |||
| 561 | } | |||
| 562 | ||||
| 563 | int get_statistic_id_ascii_string(const struct ocp_telemetry_logs *logs, | |||
| 564 | int identifier, char *description) | |||
| 565 | { | |||
| 566 | struct nvme_ocp_telemetry_string_header *pocp_ts_header; | |||
| 567 | struct nvme_ocp_statistics_identifier_string_table *entry; | |||
| 568 | __u64 entries = 0, i; | |||
| 569 | __u8 *table; | |||
| 570 | ||||
| 571 | if (!logs->string || !description) | |||
| 572 | return -1; | |||
| 573 | ||||
| 574 | pocp_ts_header = (struct nvme_ocp_telemetry_string_header *)logs->string; | |||
| 575 | table = ocp_string_table(logs->string, logs->string_size, | |||
| 576 | le64_to_cpu(pocp_ts_header->sits), | |||
| 577 | le64_to_cpu(pocp_ts_header->sitsz), sizeof(*entry), &entries); | |||
| 578 | ||||
| 579 | for (i = 0; table && i < entries; i++) { | |||
| 580 | entry = (struct nvme_ocp_statistics_identifier_string_table *) | |||
| 581 | (table + i * sizeof(*entry)); | |||
| 582 | ||||
| 583 | if (identifier == (int)le16_to_cpu(entry->vs_statistic_identifier) && | |||
| 584 | !ocp_copy_ascii_id(logs, entry->ascii_id_offset, | |||
| 585 | entry->ascii_id_length, description)) | |||
| 586 | return 0; | |||
| 587 | } | |||
| 588 | ||||
| 589 | // If ASCII string isn't found, see in our internal Map | |||
| 590 | // for 2.5 Spec defined strings | |||
| 591 | if (identifier <= 0x6F) { | |||
| 592 | shr_strscpy(description, | |||
| 593 | statistic_identifiers_map[identifier].description, | |||
| 594 | OCP_TELEMETRY_DESCRIPTION_MAX256); | |||
| 595 | return 0; | |||
| 596 | } | |||
| 597 | ||||
| 598 | return -1; | |||
| 599 | } | |||
| 600 | ||||
| 601 | /* Event and VU event string table entries share one layout. */ | |||
| 602 | static int ocp_event_ascii_string(const struct ocp_telemetry_logs *logs, | |||
| 603 | __le64 start, __le64 size, int identifier, | |||
| 604 | int debug_event_class, char *description) | |||
| 605 | { | |||
| 606 | struct nvme_ocp_event_string_table *entry; | |||
| 607 | __u64 entries = 0, i; | |||
| 608 | __u8 *table; | |||
| 609 | ||||
| 610 | table = ocp_string_table(logs->string, logs->string_size, le64_to_cpu(start), | |||
| 611 | le64_to_cpu(size), sizeof(*entry), &entries); | |||
| 612 | ||||
| 613 | for (i = 0; table && i < entries; i++) { | |||
| 614 | entry = (struct nvme_ocp_event_string_table *)(table + i * sizeof(*entry)); | |||
| 615 | ||||
| 616 | if (identifier == (int)le16_to_cpu(entry->event_identifier) && | |||
| 617 | debug_event_class == (int)entry->debug_event_class && | |||
| 618 | !ocp_copy_ascii_id(logs, entry->ascii_id_offset, | |||
| 619 | entry->ascii_id_length, description)) | |||
| 620 | return 0; | |||
| 621 | } | |||
| 622 | ||||
| 623 | return -1; | |||
| 624 | } | |||
| 625 | ||||
| 626 | static int ocp_spec_event_id_string(int identifier, int debug_event_class, | |||
| 627 | char *description) | |||
| 628 | { | |||
| 629 | const char *str; | |||
| 630 | ||||
| 631 | if (debug_event_class < 0 || | |||
| 632 | debug_event_class >= (int)ARRAY_SIZE(spec_event_id_str)(sizeof(spec_event_id_str) / sizeof((spec_event_id_str)[0]) + 0) || | |||
| 633 | identifier < 0 || | |||
| 634 | identifier >= (int)spec_event_id_str[debug_event_class].count) | |||
| 635 | return -1; | |||
| 636 | ||||
| 637 | str = spec_event_id_str[debug_event_class].str[identifier]; | |||
| 638 | if (!str) | |||
| 639 | return -1; | |||
| 640 | ||||
| 641 | shr_strscpy(description, str, OCP_TELEMETRY_DESCRIPTION_MAX256); | |||
| 642 | return 0; | |||
| 643 | } | |||
| 644 | ||||
| 645 | int get_event_id_ascii_string(const struct ocp_telemetry_logs *logs, | |||
| 646 | int identifier, int debug_event_class, | |||
| 647 | char *description) | |||
| 648 | { | |||
| 649 | struct nvme_ocp_telemetry_string_header *pocp_ts_header = | |||
| 650 | (struct nvme_ocp_telemetry_string_header *)logs->string; | |||
| 651 | ||||
| 652 | if (!logs->string) | |||
| 653 | return -1; | |||
| 654 | ||||
| 655 | if (!ocp_event_ascii_string(logs, pocp_ts_header->ests, | |||
| 656 | pocp_ts_header->estsz, identifier, | |||
| 657 | debug_event_class, description)) | |||
| 658 | return 0; | |||
| 659 | ||||
| 660 | /* TELSLG-14: the String Log may omit Event IDs the spec defines. */ | |||
| 661 | return ocp_spec_event_id_string(identifier, debug_event_class, | |||
| 662 | description); | |||
| 663 | } | |||
| 664 | ||||
| 665 | int get_vu_event_id_ascii_string(const struct ocp_telemetry_logs *logs, | |||
| 666 | int identifier, int debug_event_class, | |||
| 667 | char *description) | |||
| 668 | { | |||
| 669 | struct nvme_ocp_telemetry_string_header *pocp_ts_header = | |||
| 670 | (struct nvme_ocp_telemetry_string_header *)logs->string; | |||
| 671 | ||||
| 672 | if (!logs->string) | |||
| 673 | return -1; | |||
| 674 | ||||
| 675 | return ocp_event_ascii_string(logs, pocp_ts_header->vu_ests, | |||
| 676 | pocp_ts_header->vu_estsz, identifier, | |||
| 677 | debug_event_class, description); | |||
| 678 | } | |||
| 679 | ||||
| 680 | int parse_ocp_telemetry_string_log(const struct ocp_telemetry_logs *logs, | |||
| 681 | int event_fifo_num, int identifier, int debug_event_class, | |||
| 682 | enum ocp_telemetry_string_tables string_table, char *description) | |||
| 683 | { | |||
| 684 | if (logs->string == NULL((void*)0)) | |||
| 685 | return -1; | |||
| 686 | ||||
| 687 | if (event_fifo_num < 0 || event_fifo_num > MAX_NUM_FIFOS16) | |||
| 688 | return -1; | |||
| 689 | ||||
| 690 | if (event_fifo_num != 0) { | |||
| 691 | struct nvme_ocp_telemetry_string_header *pocp_ts_header = | |||
| 692 | (struct nvme_ocp_telemetry_string_header *)logs->string; | |||
| 693 | ||||
| 694 | /* A name that fills all 16 bytes carries no terminator. */ | |||
| 695 | memcpy(description, pocp_ts_header->fifo_ascii_string[event_fifo_num - 1], | |||
| 696 | sizeof(pocp_ts_header->fifo_ascii_string[0])); | |||
| 697 | description[sizeof(pocp_ts_header->fifo_ascii_string[0])] = '\0'; | |||
| 698 | ||||
| 699 | return 0; | |||
| 700 | } | |||
| 701 | ||||
| 702 | if (string_table == STATISTICS_IDENTIFIER_STRING) | |||
| 703 | get_statistic_id_ascii_string(logs, identifier, description); | |||
| 704 | else if (string_table == EVENT_STRING && debug_event_class < 0x80) | |||
| 705 | get_event_id_ascii_string(logs, identifier, debug_event_class, | |||
| 706 | description); | |||
| 707 | else if (string_table == VU_EVENT_STRING || debug_event_class >= 0x80) | |||
| 708 | get_vu_event_id_ascii_string(logs, identifier, | |||
| 709 | debug_event_class, description); | |||
| 710 | ||||
| 711 | return 0; | |||
| 712 | } | |||
| 713 | ||||
| 714 | int parse_time_stamp_event( | |||
| 715 | const struct ocp_telemetry_logs *logs, | |||
| 716 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 717 | struct json_object *pevent_descriptor_obj, | |||
| 718 | __u8 *pevent_specific_data, | |||
| 719 | struct json_object *pevent_fifos_object, | |||
| 720 | FILE *fp) | |||
| 721 | { | |||
| 722 | struct nvme_ocp_time_stamp_dbg_evt_class_format *ptime_stamp_event = | |||
| 723 | (struct nvme_ocp_time_stamp_dbg_evt_class_format *) pevent_specific_data; | |||
| 724 | struct nvme_ocp_common_dbg_evt_class_vu_data *ptime_stamp_event_vu_data = NULL((void*)0); | |||
| 725 | __u16 vu_event_id = 0; | |||
| 726 | __u8 *pdata = NULL((void*)0); | |||
| 727 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 728 | unsigned int vu_data_size = 0; | |||
| 729 | bool_Bool vu_data_present = false0; | |||
| 730 | ||||
| 731 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) > | |||
| 732 | sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format)) { | |||
| 733 | vu_data_present = true1; | |||
| 734 | vu_data_size = | |||
| 735 | ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) - | |||
| 736 | (sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format) + | |||
| 737 | SIZE_OF_VU_EVENT_ID2)); | |||
| 738 | ||||
| 739 | ptime_stamp_event_vu_data = | |||
| 740 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)((char *)ptime_stamp_event + | |||
| 741 | sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format)); | |||
| 742 | vu_event_id = le16_to_cpu(ptime_stamp_event_vu_data->vu_event_identifier); | |||
| 743 | pdata = (__u8 *)&(ptime_stamp_event_vu_data->data); | |||
| 744 | ||||
| 745 | parse_ocp_telemetry_string_log(logs, 0, vu_event_id, | |||
| 746 | pevent_descriptor->debug_event_class_type, | |||
| 747 | VU_EVENT_STRING, description_str); | |||
| 748 | } else if (pevent_descriptor->event_data_size < 2) | |||
| 749 | return -1; | |||
| 750 | ||||
| 751 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 752 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 753 | ptime_stamp_event->time_stamp, DATA_SIZE_88); | |||
| 754 | if (vu_data_present) { | |||
| 755 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 756 | vu_event_id); | |||
| 757 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 758 | description_str); | |||
| 759 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 760 | vu_data_size); | |||
| 761 | } | |||
| 762 | } else { | |||
| 763 | if (fp) { | |||
| 764 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 765 | ptime_stamp_event->time_stamp, DATA_SIZE_88, fp); | |||
| 766 | if (vu_data_present) { | |||
| 767 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 768 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 769 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 770 | } | |||
| 771 | } else { | |||
| 772 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 773 | ptime_stamp_event->time_stamp, DATA_SIZE_88, fp); | |||
| 774 | if (vu_data_present) { | |||
| 775 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 776 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 777 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 778 | } | |||
| 779 | } | |||
| 780 | } | |||
| 781 | ||||
| 782 | return 0; | |||
| 783 | } | |||
| 784 | ||||
| 785 | int parse_pcie_event( | |||
| 786 | const struct ocp_telemetry_logs *logs, | |||
| 787 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 788 | struct json_object *pevent_descriptor_obj, | |||
| 789 | __u8 *pevent_specific_data, | |||
| 790 | struct json_object *pevent_fifos_object, | |||
| 791 | FILE *fp) | |||
| 792 | { | |||
| 793 | struct nvme_ocp_pcie_dbg_evt_class_format *ppcie_event = | |||
| 794 | (struct nvme_ocp_pcie_dbg_evt_class_format *) pevent_specific_data; | |||
| 795 | struct nvme_ocp_common_dbg_evt_class_vu_data *ppcie_event_vu_data = NULL((void*)0); | |||
| 796 | __u16 vu_event_id = 0; | |||
| 797 | __u8 *pdata = NULL((void*)0); | |||
| 798 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 799 | unsigned int vu_data_size = 0; | |||
| 800 | bool_Bool vu_data_present = false0; | |||
| 801 | ||||
| 802 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) > | |||
| 803 | sizeof(struct nvme_ocp_pcie_dbg_evt_class_format)) { | |||
| 804 | vu_data_present = true1; | |||
| 805 | vu_data_size = | |||
| 806 | ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) - | |||
| 807 | (sizeof(struct nvme_ocp_pcie_dbg_evt_class_format) + | |||
| 808 | SIZE_OF_VU_EVENT_ID2)); | |||
| 809 | ||||
| 810 | ppcie_event_vu_data = | |||
| 811 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)((char *)ppcie_event + | |||
| 812 | sizeof(struct nvme_ocp_pcie_dbg_evt_class_format)); | |||
| 813 | vu_event_id = le16_to_cpu(ppcie_event_vu_data->vu_event_identifier); | |||
| 814 | pdata = (__u8 *)&(ppcie_event_vu_data->data); | |||
| 815 | ||||
| 816 | parse_ocp_telemetry_string_log(logs, 0, vu_event_id, | |||
| 817 | pevent_descriptor->debug_event_class_type, | |||
| 818 | VU_EVENT_STRING, description_str); | |||
| 819 | } else if (pevent_descriptor->event_data_size < 1) | |||
| 820 | return -1; | |||
| 821 | ||||
| 822 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 823 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 824 | ppcie_event->pCIeDebugEventData, DATA_SIZE_44); | |||
| 825 | if (vu_data_present) { | |||
| 826 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 827 | vu_event_id); | |||
| 828 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 829 | description_str); | |||
| 830 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 831 | vu_data_size); | |||
| 832 | } | |||
| 833 | } else { | |||
| 834 | if (fp) { | |||
| 835 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 836 | ppcie_event->pCIeDebugEventData, DATA_SIZE_44, fp); | |||
| 837 | if (vu_data_present) { | |||
| 838 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 839 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 840 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 841 | } | |||
| 842 | } else { | |||
| 843 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 844 | ppcie_event->pCIeDebugEventData, DATA_SIZE_44, fp); | |||
| 845 | if (vu_data_present) { | |||
| 846 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 847 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 848 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 849 | } | |||
| 850 | } | |||
| 851 | } | |||
| 852 | ||||
| 853 | return 0; | |||
| 854 | } | |||
| 855 | ||||
| 856 | int parse_nvme_event( | |||
| 857 | const struct ocp_telemetry_logs *logs, | |||
| 858 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 859 | struct json_object *pevent_descriptor_obj, | |||
| 860 | __u8 *pevent_specific_data, | |||
| 861 | struct json_object *pevent_fifos_object, | |||
| 862 | FILE *fp) | |||
| 863 | { | |||
| 864 | struct nvme_ocp_nvme_dbg_evt_class_format *pnvme_event = | |||
| 865 | (struct nvme_ocp_nvme_dbg_evt_class_format *) pevent_specific_data; | |||
| 866 | struct nvme_ocp_common_dbg_evt_class_vu_data *pnvme_event_vu_data = NULL((void*)0); | |||
| 867 | __u16 vu_event_id = 0; | |||
| 868 | __u8 *pdata = NULL((void*)0); | |||
| 869 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 870 | unsigned int vu_data_size = 0; | |||
| 871 | bool_Bool vu_data_present = false0; | |||
| 872 | ||||
| 873 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) > | |||
| 874 | sizeof(struct nvme_ocp_nvme_dbg_evt_class_format)) { | |||
| 875 | vu_data_present = true1; | |||
| 876 | vu_data_size = | |||
| 877 | ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) - | |||
| 878 | (sizeof(struct nvme_ocp_nvme_dbg_evt_class_format) + | |||
| 879 | SIZE_OF_VU_EVENT_ID2)); | |||
| 880 | pnvme_event_vu_data = | |||
| 881 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)((char *)pnvme_event + | |||
| 882 | sizeof(struct nvme_ocp_nvme_dbg_evt_class_format)); | |||
| 883 | ||||
| 884 | vu_event_id = le16_to_cpu(pnvme_event_vu_data->vu_event_identifier); | |||
| 885 | pdata = (__u8 *)&(pnvme_event_vu_data->data); | |||
| 886 | ||||
| 887 | parse_ocp_telemetry_string_log(logs, 0, vu_event_id, | |||
| 888 | pevent_descriptor->debug_event_class_type, | |||
| 889 | VU_EVENT_STRING, | |||
| 890 | description_str); | |||
| 891 | } else if (pevent_descriptor->event_data_size < 2) | |||
| 892 | return -1; | |||
| 893 | ||||
| 894 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 895 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 896 | pnvme_event->nvmeDebugEventData, DATA_SIZE_88); | |||
| 897 | if (vu_data_present) { | |||
| 898 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 899 | vu_event_id); | |||
| 900 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 901 | description_str); | |||
| 902 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 903 | vu_data_size); | |||
| 904 | } | |||
| 905 | } else { | |||
| 906 | if (fp) { | |||
| 907 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 908 | pnvme_event->nvmeDebugEventData, DATA_SIZE_88, fp); | |||
| 909 | if (vu_data_present) { | |||
| 910 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 911 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 912 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 913 | } | |||
| 914 | } else { | |||
| 915 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 916 | pnvme_event->nvmeDebugEventData, DATA_SIZE_88, fp); | |||
| 917 | if (vu_data_present) { | |||
| 918 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 919 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 920 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 921 | } | |||
| 922 | } | |||
| 923 | } | |||
| 924 | ||||
| 925 | return 0; | |||
| 926 | } | |||
| 927 | ||||
| 928 | void parse_common_event( | |||
| 929 | const struct ocp_telemetry_logs *logs, | |||
| 930 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 931 | struct json_object *pevent_descriptor_obj, | |||
| 932 | __u8 *pevent_specific_data, | |||
| 933 | struct json_object *pevent_fifos_object, | |||
| 934 | FILE *fp) | |||
| 935 | { | |||
| 936 | if (pevent_specific_data) { | |||
| 937 | struct nvme_ocp_common_dbg_evt_class_vu_data *pcommon_debug_event_vu_data = | |||
| 938 | (struct nvme_ocp_common_dbg_evt_class_vu_data *) pevent_specific_data; | |||
| 939 | ||||
| 940 | __u16 vu_event_id = le16_to_cpu(pcommon_debug_event_vu_data->vu_event_identifier); | |||
| 941 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 942 | __u8 *pdata = (__u8 *)&(pcommon_debug_event_vu_data->data); | |||
| 943 | ||||
| 944 | unsigned int vu_data_size = ((pevent_descriptor->event_data_size * | |||
| 945 | SIZE_OF_DWORD4) - SIZE_OF_VU_EVENT_ID2); | |||
| 946 | ||||
| 947 | parse_ocp_telemetry_string_log(logs, 0, vu_event_id, | |||
| 948 | pevent_descriptor->debug_event_class_type, | |||
| 949 | VU_EVENT_STRING, description_str); | |||
| 950 | ||||
| 951 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 952 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 953 | vu_event_id); | |||
| 954 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 955 | description_str); | |||
| 956 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 957 | vu_data_size); | |||
| 958 | } else { | |||
| 959 | if (fp) { | |||
| 960 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 961 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 962 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 963 | } else { | |||
| 964 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 965 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 966 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 967 | } | |||
| 968 | } | |||
| 969 | } | |||
| 970 | } | |||
| 971 | ||||
| 972 | int parse_media_wear_event( | |||
| 973 | const struct ocp_telemetry_logs *logs, | |||
| 974 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 975 | struct json_object *pevent_descriptor_obj, | |||
| 976 | __u8 *pevent_specific_data, | |||
| 977 | struct json_object *pevent_fifos_object, | |||
| 978 | FILE *fp) | |||
| 979 | { | |||
| 980 | struct nvme_ocp_media_wear_dbg_evt_class_format *pmedia_wear_event = | |||
| 981 | (struct nvme_ocp_media_wear_dbg_evt_class_format *) pevent_specific_data; | |||
| 982 | struct nvme_ocp_common_dbg_evt_class_vu_data *pmedia_wear_event_vu_data = NULL((void*)0); | |||
| 983 | ||||
| 984 | __u16 vu_event_id = 0; | |||
| 985 | __u8 *pdata = NULL((void*)0); | |||
| 986 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 987 | unsigned int vu_data_size = 0; | |||
| 988 | bool_Bool vu_data_present = false0; | |||
| 989 | ||||
| 990 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) > | |||
| 991 | sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format)) { | |||
| 992 | vu_data_present = true1; | |||
| 993 | vu_data_size = | |||
| 994 | ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) - | |||
| 995 | (sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format) + | |||
| 996 | SIZE_OF_VU_EVENT_ID2)); | |||
| 997 | ||||
| 998 | pmedia_wear_event_vu_data = | |||
| 999 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)((char *)pmedia_wear_event + | |||
| 1000 | sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format)); | |||
| 1001 | vu_event_id = le16_to_cpu(pmedia_wear_event_vu_data->vu_event_identifier); | |||
| 1002 | pdata = (__u8 *)&(pmedia_wear_event_vu_data->data); | |||
| 1003 | ||||
| 1004 | parse_ocp_telemetry_string_log(logs, 0, vu_event_id, | |||
| 1005 | pevent_descriptor->debug_event_class_type, | |||
| 1006 | VU_EVENT_STRING, | |||
| 1007 | description_str); | |||
| 1008 | } else if (pevent_descriptor->event_data_size < 3) | |||
| 1009 | return -1; | |||
| 1010 | ||||
| 1011 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1012 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1013 | pmedia_wear_event->currentMediaWear, DATA_SIZE_1212); | |||
| 1014 | if (vu_data_present) { | |||
| 1015 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 1016 | vu_event_id); | |||
| 1017 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 1018 | description_str); | |||
| 1019 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 1020 | vu_data_size); | |||
| 1021 | } | |||
| 1022 | } else { | |||
| 1023 | if (fp) { | |||
| 1024 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1025 | pmedia_wear_event->currentMediaWear, DATA_SIZE_1212, fp); | |||
| 1026 | if (vu_data_present) { | |||
| 1027 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1028 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 1029 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 1030 | } | |||
| 1031 | } else { | |||
| 1032 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1033 | pmedia_wear_event->currentMediaWear, DATA_SIZE_1212, NULL((void*)0)); | |||
| 1034 | if (vu_data_present) { | |||
| 1035 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1036 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 1037 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 1038 | } | |||
| 1039 | } | |||
| 1040 | } | |||
| 1041 | ||||
| 1042 | return 0; | |||
| 1043 | } | |||
| 1044 | ||||
| 1045 | /* | |||
| 1046 | * The Virtual FIFO Event class (0Bh) carries a single Dword of class specific | |||
| 1047 | * data and no VU data: a VU Virtual FIFO Identifier and a reserved half-word. | |||
| 1048 | * The identifier names a virtual FIFO in the String Log's VU event table, and | |||
| 1049 | * splits into the enclosing physical Event FIFO number and the virtual FIFO | |||
| 1050 | * number within that physical FIFO. | |||
| 1051 | */ | |||
| 1052 | int parse_virtual_fifo_event( | |||
| 1053 | const struct ocp_telemetry_logs *logs, | |||
| 1054 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 1055 | struct json_object *pevent_descriptor_obj, | |||
| 1056 | __u8 *pevent_specific_data, | |||
| 1057 | struct json_object *pevent_fifos_object, | |||
| 1058 | FILE *fp) | |||
| 1059 | { | |||
| 1060 | struct nvme_ocp_virtual_fifo_dbg_evt_class_format *pvirtual_fifo_event = | |||
| 1061 | (struct nvme_ocp_virtual_fifo_dbg_evt_class_format *) | |||
| 1062 | pevent_specific_data; | |||
| 1063 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1064 | char fifo_name_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1065 | __u16 vu_virtual_fifo_id = 0; | |||
| 1066 | __u8 physical_fifo_num = 0; | |||
| 1067 | __u16 virtual_fifo_num = 0; | |||
| 1068 | ||||
| 1069 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) < | |||
| 1070 | sizeof(*pvirtual_fifo_event)) | |||
| 1071 | return -1; | |||
| 1072 | ||||
| 1073 | vu_virtual_fifo_id = | |||
| 1074 | le16_to_cpu(pvirtual_fifo_event->vu_virtual_fifo_identifier); | |||
| 1075 | physical_fifo_num = vu_virtual_fifo_id >> VU_VIRTUAL_FIFO_PHY_NUM_SHIFT11; | |||
| 1076 | virtual_fifo_num = vu_virtual_fifo_id & VU_VIRTUAL_FIFO_NUM_MASK0x07ff; | |||
| 1077 | ||||
| 1078 | parse_ocp_telemetry_string_log(logs, 0, vu_virtual_fifo_id, | |||
| 1079 | pevent_descriptor->debug_event_class_type, | |||
| 1080 | VU_EVENT_STRING, description_str); | |||
| 1081 | ||||
| 1082 | /* | |||
| 1083 | * A physical FIFO number of 0h is invalid and the field is wide enough | |||
| 1084 | * to hold values past the 10h maximum, so range-check it before using | |||
| 1085 | * it to index the string log's FIFO name array. | |||
| 1086 | */ | |||
| 1087 | if (physical_fifo_num >= 1 && physical_fifo_num <= MAX_NUM_FIFOS16) | |||
| 1088 | parse_ocp_telemetry_string_log(logs, physical_fifo_num, 0, 0, | |||
| 1089 | EVENT_STRING, fifo_name_str); | |||
| 1090 | ||||
| 1091 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1092 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1093 | STR_VU_VIRTUAL_FIFO_ID"VU Virtual FIFO Identifier", | |||
| 1094 | vu_virtual_fifo_id); | |||
| 1095 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1096 | STR_VU_VIRTUAL_FIFO_STRING"VU Virtual FIFO String", | |||
| 1097 | description_str); | |||
| 1098 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1099 | STR_PHYSICAL_EVENT_FIFO_NUM"Physical Event FIFO Number", | |||
| 1100 | physical_fifo_num); | |||
| 1101 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1102 | STR_PHYSICAL_EVENT_FIFO_STRING"Physical Event FIFO String", | |||
| 1103 | fifo_name_str); | |||
| 1104 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1105 | STR_VIRTUAL_FIFO_NUM"Virtual FIFO Number", | |||
| 1106 | virtual_fifo_num); | |||
| 1107 | } else if (fp) { | |||
| 1108 | fprintf(fp, "%s: 0x%x\n", STR_VU_VIRTUAL_FIFO_ID"VU Virtual FIFO Identifier", | |||
| 1109 | vu_virtual_fifo_id); | |||
| 1110 | fprintf(fp, "%s: %s\n", STR_VU_VIRTUAL_FIFO_STRING"VU Virtual FIFO String", | |||
| 1111 | description_str); | |||
| 1112 | fprintf(fp, "%s: 0x%x\n", STR_PHYSICAL_EVENT_FIFO_NUM"Physical Event FIFO Number", | |||
| 1113 | physical_fifo_num); | |||
| 1114 | fprintf(fp, "%s: %s\n", STR_PHYSICAL_EVENT_FIFO_STRING"Physical Event FIFO String", | |||
| 1115 | fifo_name_str); | |||
| 1116 | fprintf(fp, "%s: 0x%x\n", STR_VIRTUAL_FIFO_NUM"Virtual FIFO Number", | |||
| 1117 | virtual_fifo_num); | |||
| 1118 | } else { | |||
| 1119 | printf("%s: 0x%x\n", STR_VU_VIRTUAL_FIFO_ID"VU Virtual FIFO Identifier", | |||
| 1120 | vu_virtual_fifo_id); | |||
| 1121 | printf("%s: %s\n", STR_VU_VIRTUAL_FIFO_STRING"VU Virtual FIFO String", | |||
| 1122 | description_str); | |||
| 1123 | printf("%s: 0x%x\n", STR_PHYSICAL_EVENT_FIFO_NUM"Physical Event FIFO Number", | |||
| 1124 | physical_fifo_num); | |||
| 1125 | printf("%s: %s\n", STR_PHYSICAL_EVENT_FIFO_STRING"Physical Event FIFO String", | |||
| 1126 | fifo_name_str); | |||
| 1127 | printf("%s: 0x%x\n", STR_VIRTUAL_FIFO_NUM"Virtual FIFO Number", | |||
| 1128 | virtual_fifo_num); | |||
| 1129 | } | |||
| 1130 | ||||
| 1131 | return 0; | |||
| 1132 | } | |||
| 1133 | ||||
| 1134 | /* | |||
| 1135 | * A VU Event Identifier and VU Data follow a class's fixed record when its | |||
| 1136 | * Event Data Size reaches past it. @vu_data_size excludes the identifier. | |||
| 1137 | */ | |||
| 1138 | static void parse_class_vu_fields( | |||
| 1139 | const struct ocp_telemetry_logs *logs, | |||
| 1140 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 1141 | struct json_object *pevent_descriptor_obj, | |||
| 1142 | __u8 *pvu_fields, unsigned int vu_data_size, | |||
| 1143 | struct json_object *pevent_fifos_object, | |||
| 1144 | FILE *fp) | |||
| 1145 | { | |||
| 1146 | struct nvme_ocp_common_dbg_evt_class_vu_data *pvu_data = | |||
| 1147 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)pvu_fields; | |||
| 1148 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1149 | __u16 vu_event_id = le16_to_cpu(pvu_data->vu_event_identifier); | |||
| 1150 | ||||
| 1151 | parse_ocp_telemetry_string_log(logs, 0, vu_event_id, | |||
| 1152 | pevent_descriptor->debug_event_class_type, | |||
| 1153 | VU_EVENT_STRING, description_str); | |||
| 1154 | ||||
| 1155 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1156 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1157 | STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1158 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1159 | STR_VU_EVENT_STRING"VU Event String", | |||
| 1160 | description_str); | |||
| 1161 | json_add_formatted_var_size_str(pevent_descriptor_obj, | |||
| 1162 | STR_VU_DATA"VU Data", pvu_data->data, | |||
| 1163 | vu_data_size); | |||
| 1164 | } else if (fp) { | |||
| 1165 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1166 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 1167 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pvu_data->data, | |||
| 1168 | vu_data_size, fp); | |||
| 1169 | } else { | |||
| 1170 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1171 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 1172 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pvu_data->data, | |||
| 1173 | vu_data_size, NULL((void*)0)); | |||
| 1174 | } | |||
| 1175 | } | |||
| 1176 | ||||
| 1177 | /* | |||
| 1178 | * The SMBUS/I2C/I3C Debug Event class (0Ch) carries one Dword of class | |||
| 1179 | * specific data: the SMBUS Debug Event Data and a reserved half-word. The | |||
| 1180 | * Event Data values are defined only for the NACK error Event ID. | |||
| 1181 | */ | |||
| 1182 | int parse_smbus_event( | |||
| 1183 | const struct ocp_telemetry_logs *logs, | |||
| 1184 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 1185 | struct json_object *pevent_descriptor_obj, | |||
| 1186 | __u8 *pevent_specific_data, | |||
| 1187 | struct json_object *pevent_fifos_object, | |||
| 1188 | FILE *fp) | |||
| 1189 | { | |||
| 1190 | struct nvme_ocp_smbus_dbg_evt_class_format *psmbus_event = | |||
| 1191 | (struct nvme_ocp_smbus_dbg_evt_class_format *) | |||
| 1192 | pevent_specific_data; | |||
| 1193 | unsigned int event_size = | |||
| 1194 | pevent_descriptor->event_data_size * SIZE_OF_DWORD4; | |||
| 1195 | __u16 event_id = le16_to_cpu(pevent_descriptor->event_id); | |||
| 1196 | const char *event_data_str = NULL((void*)0); | |||
| 1197 | __u16 event_data = 0; | |||
| 1198 | ||||
| 1199 | if (event_size < sizeof(*psmbus_event)) | |||
| 1200 | return -1; | |||
| 1201 | ||||
| 1202 | event_data = le16_to_cpu(psmbus_event->smbus_debug_event_data); | |||
| 1203 | event_data_str = telemetry_smbus_event_data_to_string(event_id, | |||
| 1204 | event_data); | |||
| 1205 | ||||
| 1206 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1207 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1208 | STR_SMBUS_DEBUG_EVENT_DATA"SMBUS Debug Event Data", | |||
| 1209 | event_data); | |||
| 1210 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1211 | STR_SMBUS_DEBUG_EVENT_DATA_STRING"SMBUS Debug Event Data String", | |||
| 1212 | event_data_str); | |||
| 1213 | } else if (fp) { | |||
| 1214 | fprintf(fp, "%s: 0x%x\n", STR_SMBUS_DEBUG_EVENT_DATA"SMBUS Debug Event Data", | |||
| 1215 | event_data); | |||
| 1216 | fprintf(fp, "%s: %s\n", STR_SMBUS_DEBUG_EVENT_DATA_STRING"SMBUS Debug Event Data String", | |||
| 1217 | event_data_str); | |||
| 1218 | } else { | |||
| 1219 | printf("%s: 0x%x\n", STR_SMBUS_DEBUG_EVENT_DATA"SMBUS Debug Event Data", event_data); | |||
| 1220 | printf("%s: %s\n", STR_SMBUS_DEBUG_EVENT_DATA_STRING"SMBUS Debug Event Data String", | |||
| 1221 | event_data_str); | |||
| 1222 | } | |||
| 1223 | ||||
| 1224 | if (event_size > sizeof(*psmbus_event)) | |||
| 1225 | parse_class_vu_fields(logs, pevent_descriptor, | |||
| 1226 | pevent_descriptor_obj, | |||
| 1227 | pevent_specific_data + sizeof(*psmbus_event), | |||
| 1228 | event_size - sizeof(*psmbus_event) - SIZE_OF_VU_EVENT_ID2, | |||
| 1229 | pevent_fifos_object, fp); | |||
| 1230 | ||||
| 1231 | return 0; | |||
| 1232 | } | |||
| 1233 | ||||
| 1234 | /* | |||
| 1235 | * The MCTP Debug Event class (0Dh) carries two Dwords of class specific | |||
| 1236 | * data: the MCTP Debug Event Data, whose values depend on the Event ID, the | |||
| 1237 | * Transport Protocol Information, the Event Flags and the MCTP Transport | |||
| 1238 | * Header. The header holds captured packet bytes only when the Transport | |||
| 1239 | * Header Valid flag is set, so it is left out otherwise. | |||
| 1240 | */ | |||
| 1241 | int parse_mctp_event( | |||
| 1242 | const struct ocp_telemetry_logs *logs, | |||
| 1243 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 1244 | struct json_object *pevent_descriptor_obj, | |||
| 1245 | __u8 *pevent_specific_data, | |||
| 1246 | struct json_object *pevent_fifos_object, | |||
| 1247 | FILE *fp) | |||
| 1248 | { | |||
| 1249 | struct nvme_ocp_mctp_dbg_evt_class_format *pmctp_event = | |||
| 1250 | (struct nvme_ocp_mctp_dbg_evt_class_format *) | |||
| 1251 | pevent_specific_data; | |||
| 1252 | unsigned int event_size = | |||
| 1253 | pevent_descriptor->event_data_size * SIZE_OF_DWORD4; | |||
| 1254 | __u16 event_id = le16_to_cpu(pevent_descriptor->event_id); | |||
| 1255 | const char *event_data_str = NULL((void*)0); | |||
| 1256 | const char *protocol_str = NULL((void*)0); | |||
| 1257 | bool_Bool header_valid = false0; | |||
| 1258 | __u16 event_data = 0; | |||
| 1259 | ||||
| 1260 | if (event_size < sizeof(*pmctp_event)) | |||
| 1261 | return -1; | |||
| 1262 | ||||
| 1263 | event_data = le16_to_cpu(pmctp_event->mctp_debug_event_data); | |||
| 1264 | event_data_str = telemetry_mctp_event_data_to_string(event_id, | |||
| 1265 | event_data); | |||
| 1266 | protocol_str = telemetry_mctp_transport_protocol_to_string( | |||
| 1267 | pmctp_event->transport_protocol); | |||
| 1268 | header_valid = pmctp_event->event_flags & | |||
| 1269 | MCTP_EVENT_FLAG_TRANSPORT_HEADER_VALID0x80; | |||
| 1270 | ||||
| 1271 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1272 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1273 | STR_MCTP_DEBUG_EVENT_DATA"MCTP Debug Event Data", | |||
| 1274 | event_data); | |||
| 1275 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1276 | STR_MCTP_DEBUG_EVENT_DATA_STRING"MCTP Debug Event Data String", | |||
| 1277 | event_data_str); | |||
| 1278 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1279 | STR_MCTP_TRANSPORT_PROTOCOL"MCTP Transport Protocol Information", | |||
| 1280 | pmctp_event->transport_protocol); | |||
| 1281 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1282 | STR_MCTP_TRANSPORT_PROTOCOL_STRING"MCTP Transport Protocol String", | |||
| 1283 | protocol_str); | |||
| 1284 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1285 | STR_MCTP_TRANSPORT_HEADER_VALID"MCTP Transport Header Valid", | |||
| 1286 | header_valid); | |||
| 1287 | if (header_valid) | |||
| 1288 | json_add_formatted_var_size_str(pevent_descriptor_obj, | |||
| 1289 | STR_MCTP_TRANSPORT_HEADER"MCTP Transport Header", | |||
| 1290 | pmctp_event->transport_header, DATA_SIZE_44); | |||
| 1291 | } else if (fp) { | |||
| 1292 | fprintf(fp, "%s: 0x%x\n", STR_MCTP_DEBUG_EVENT_DATA"MCTP Debug Event Data", | |||
| 1293 | event_data); | |||
| 1294 | fprintf(fp, "%s: %s\n", STR_MCTP_DEBUG_EVENT_DATA_STRING"MCTP Debug Event Data String", | |||
| 1295 | event_data_str); | |||
| 1296 | fprintf(fp, "%s: 0x%x\n", STR_MCTP_TRANSPORT_PROTOCOL"MCTP Transport Protocol Information", | |||
| 1297 | pmctp_event->transport_protocol); | |||
| 1298 | fprintf(fp, "%s: %s\n", STR_MCTP_TRANSPORT_PROTOCOL_STRING"MCTP Transport Protocol String", | |||
| 1299 | protocol_str); | |||
| 1300 | fprintf(fp, "%s: 0x%x\n", STR_MCTP_TRANSPORT_HEADER_VALID"MCTP Transport Header Valid", | |||
| 1301 | header_valid); | |||
| 1302 | if (header_valid) | |||
| 1303 | print_formatted_var_size_str(STR_MCTP_TRANSPORT_HEADER"MCTP Transport Header", | |||
| 1304 | pmctp_event->transport_header, DATA_SIZE_44, fp); | |||
| 1305 | } else { | |||
| 1306 | printf("%s: 0x%x\n", STR_MCTP_DEBUG_EVENT_DATA"MCTP Debug Event Data", event_data); | |||
| 1307 | printf("%s: %s\n", STR_MCTP_DEBUG_EVENT_DATA_STRING"MCTP Debug Event Data String", | |||
| 1308 | event_data_str); | |||
| 1309 | printf("%s: 0x%x\n", STR_MCTP_TRANSPORT_PROTOCOL"MCTP Transport Protocol Information", | |||
| 1310 | pmctp_event->transport_protocol); | |||
| 1311 | printf("%s: %s\n", STR_MCTP_TRANSPORT_PROTOCOL_STRING"MCTP Transport Protocol String", | |||
| 1312 | protocol_str); | |||
| 1313 | printf("%s: 0x%x\n", STR_MCTP_TRANSPORT_HEADER_VALID"MCTP Transport Header Valid", | |||
| 1314 | header_valid); | |||
| 1315 | if (header_valid) | |||
| 1316 | print_formatted_var_size_str(STR_MCTP_TRANSPORT_HEADER"MCTP Transport Header", | |||
| 1317 | pmctp_event->transport_header, DATA_SIZE_44, NULL((void*)0)); | |||
| 1318 | } | |||
| 1319 | ||||
| 1320 | if (event_size > sizeof(*pmctp_event)) | |||
| 1321 | parse_class_vu_fields(logs, pevent_descriptor, | |||
| 1322 | pevent_descriptor_obj, | |||
| 1323 | pevent_specific_data + sizeof(*pmctp_event), | |||
| 1324 | event_size - sizeof(*pmctp_event) - SIZE_OF_VU_EVENT_ID2, | |||
| 1325 | pevent_fifos_object, fp); | |||
| 1326 | ||||
| 1327 | return 0; | |||
| 1328 | } | |||
| 1329 | ||||
| 1330 | int parse_event_fifo(const struct ocp_telemetry_logs *logs, | |||
| 1331 | unsigned int fifo_num, unsigned char *pfifo_start, | |||
| 1332 | struct json_object *pevent_fifos_object, | |||
| 1333 | struct nvme_ocp_telemetry_offsets *poffsets, __u64 fifo_size, FILE *fp) | |||
| 1334 | { | |||
| 1335 | if (NULL((void*)0) == pfifo_start || NULL((void*)0) == poffsets) { | |||
| ||||
| 1336 | nvme_show_error("Input buffer was NULL")nvme_show_message(1, "Input buffer was NULL"); | |||
| 1337 | return -1; | |||
| 1338 | } | |||
| 1339 | ||||
| 1340 | int status = 0, ret = 0; | |||
| 1341 | unsigned int event_fifo_number = fifo_num + 1; | |||
| 1342 | __cleanup_free__attribute__((cleanup(shr_freep))) char *description = calloc(1, 40 + 1); | |||
| 1343 | ||||
| 1344 | if (!description) { | |||
| 1345 | nvme_show_error("Failed to allocate description buffer")nvme_show_message(1, "Failed to allocate description buffer"); | |||
| 1346 | return -1; | |||
| 1347 | } | |||
| 1348 | ||||
| 1349 | status = | |||
| 1350 | parse_ocp_telemetry_string_log(logs, event_fifo_number, 0, 0, | |||
| 1351 | EVENT_STRING, description); | |||
| 1352 | ||||
| 1353 | if (status
| |||
| 1354 | nvme_show_error("Failed to get C9 String. status: %d", status)nvme_show_message(1, "Failed to get C9 String. status: %d", status ); | |||
| ||||
| 1355 | return -1; | |||
| 1356 | } | |||
| 1357 | ||||
| 1358 | char event_fifo_name[100] = {0}; | |||
| 1359 | ||||
| 1360 | snprintf(event_fifo_name, sizeof(event_fifo_name), "%s%d%s%s", "EVENT FIFO ", | |||
| 1361 | event_fifo_number, " - ", description); | |||
| 1362 | ||||
| 1363 | __cleanup_json_object__attribute__((cleanup(json_free_objectp))) struct json_object *pevent_fifo_array = NULL((void*)0); | |||
| 1364 | ||||
| 1365 | if (pevent_fifos_object != NULL((void*)0)) | |||
| 1366 | pevent_fifo_array = json_create_array()json_object_new_array(); | |||
| 1367 | else { | |||
| 1368 | char buffer[1024] = {0}; | |||
| 1369 | ||||
| 1370 | snprintf(buffer, sizeof(buffer), "%s%s\n%s", STR_LINE"==============================================================================\n", | |||
| 1371 | event_fifo_name, STR_LINE"==============================================================================\n"); | |||
| 1372 | if (fp) | |||
| 1373 | fprintf(fp, "%s", buffer); | |||
| 1374 | else | |||
| 1375 | printf("%s", buffer); | |||
| 1376 | } | |||
| 1377 | ||||
| 1378 | int offset_to_move = 0; | |||
| 1379 | unsigned int event_des_size = sizeof(struct nvme_ocp_telemetry_event_descriptor); | |||
| 1380 | ||||
| 1381 | while ((fifo_size > 0) && (offset_to_move < fifo_size)) { | |||
| 1382 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor = | |||
| 1383 | (struct nvme_ocp_telemetry_event_descriptor *) | |||
| 1384 | (pfifo_start + offset_to_move); | |||
| 1385 | ||||
| 1386 | /* check if at the end of the list */ | |||
| 1387 | if (pevent_descriptor->debug_event_class_type == RESERVED_CLASS_TYPE) | |||
| 1388 | break; | |||
| 1389 | ||||
| 1390 | __u8 *pevent_specific_data = NULL((void*)0); | |||
| 1391 | __u16 event_id = 0; | |||
| 1392 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1393 | unsigned int data_size = 0; | |||
| 1394 | __u64 remaining = fifo_size - offset_to_move; | |||
| 1395 | bool_Bool is_snapshot = pevent_descriptor->debug_event_class_type == | |||
| 1396 | STATISTIC_SNAPSHOT_CLASS_TYPE; | |||
| 1397 | struct nvme_ocp_statistic_snapshot_evt_class_format *psnapshot = | |||
| 1398 | (struct nvme_ocp_statistic_snapshot_evt_class_format *) | |||
| 1399 | pevent_descriptor; | |||
| 1400 | ||||
| 1401 | /* | |||
| 1402 | * Bound the whole entry by what is left of the FIFO before its | |||
| 1403 | * size field or any class specific data is read. A snapshot's | |||
| 1404 | * Event ID and Event Data Size bytes are reserved, so its | |||
| 1405 | * statistic's identifier and size are reported instead. | |||
| 1406 | */ | |||
| 1407 | event_des_size = is_snapshot ? sizeof(*psnapshot) : | |||
| 1408 | sizeof(struct nvme_ocp_telemetry_event_descriptor); | |||
| 1409 | if (remaining < event_des_size) { | |||
| 1410 | nvme_show_error(nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1411 | "Invalid entry at offset 0x%x of Event FIFO %u: "nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1412 | "class 0x%x needs a %u-byte header, %llu bytes left in FIFO",nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1413 | offset_to_move, event_fifo_number,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1414 | pevent_descriptor->debug_event_class_type,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1415 | event_des_size, (unsigned long long)remaining)nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining); | |||
| 1416 | return -1; | |||
| 1417 | } | |||
| 1418 | ||||
| 1419 | /* Data sizes are in Dwords */ | |||
| 1420 | if (is_snapshot) | |||
| 1421 | data_size = le16_to_cpu(psnapshot->stat_data_size) * | |||
| 1422 | SIZE_OF_DWORD4; | |||
| 1423 | else | |||
| 1424 | data_size = pevent_descriptor->event_data_size * | |||
| 1425 | SIZE_OF_DWORD4; | |||
| 1426 | if (remaining - event_des_size < data_size) { | |||
| 1427 | nvme_show_error(nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1428 | "Invalid entry at offset 0x%x of Event FIFO %u: "nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1429 | "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO",nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1430 | offset_to_move, event_fifo_number,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1431 | pevent_descriptor->debug_event_class_type,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1432 | is_snapshot ? "Statistic ID" : "Event ID",nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1433 | le16_to_cpu(is_snapshot ? psnapshot->stat_id :nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1434 | pevent_descriptor->event_id),nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1435 | data_size,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1436 | (unsigned long long)(remaining - event_des_size))nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)); | |||
| 1437 | return -1; | |||
| 1438 | } | |||
| 1439 | ||||
| 1440 | if (!is_snapshot) { | |||
| 1441 | if (pevent_descriptor->event_data_size > 0) | |||
| 1442 | pevent_specific_data = (__u8 *)pevent_descriptor + event_des_size; | |||
| 1443 | ||||
| 1444 | event_id = le16_to_cpu(pevent_descriptor->event_id); | |||
| 1445 | ||||
| 1446 | parse_ocp_telemetry_string_log(logs, 0, event_id, | |||
| 1447 | pevent_descriptor->debug_event_class_type, EVENT_STRING, | |||
| 1448 | description_str); | |||
| 1449 | ||||
| 1450 | __cleanup_json_object__attribute__((cleanup(json_free_objectp))) | |||
| 1451 | struct json_object *pevent_descriptor_obj = | |||
| 1452 | ((pevent_fifos_object != NULL((void*)0))?json_create_object()json_object_new_object():NULL((void*)0)); | |||
| 1453 | ||||
| 1454 | if (pevent_descriptor_obj != NULL((void*)0)) { | |||
| 1455 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1456 | STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1457 | pevent_descriptor->debug_event_class_type); | |||
| 1458 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1459 | STR_EVENT_IDENTIFIER"Event Identifier", event_id); | |||
| 1460 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1461 | STR_EVENT_STRING"Event String", description_str); | |||
| 1462 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1463 | STR_EVENT_DATA_SIZE"Event Data Size", pevent_descriptor->event_data_size); | |||
| 1464 | ||||
| 1465 | if (pevent_descriptor->debug_event_class_type >= 0x80) | |||
| 1466 | json_add_formatted_var_size_str(pevent_descriptor_obj, | |||
| 1467 | STR_VU_DATA"VU Data", pevent_specific_data, data_size); | |||
| 1468 | } else { | |||
| 1469 | if (fp) { | |||
| 1470 | fprintf(fp, "%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1471 | pevent_descriptor->debug_event_class_type); | |||
| 1472 | fprintf(fp, "%s: 0x%x\n", STR_EVENT_IDENTIFIER"Event Identifier", | |||
| 1473 | event_id); | |||
| 1474 | fprintf(fp, "%s: %s\n", STR_EVENT_STRING"Event String", description_str); | |||
| 1475 | fprintf(fp, "%s: 0x%x\n", STR_EVENT_DATA_SIZE"Event Data Size", | |||
| 1476 | pevent_descriptor->event_data_size); | |||
| 1477 | } else { | |||
| 1478 | printf("%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1479 | pevent_descriptor->debug_event_class_type); | |||
| 1480 | printf("%s: 0x%x\n", STR_EVENT_IDENTIFIER"Event Identifier", | |||
| 1481 | event_id); | |||
| 1482 | printf("%s: %s\n", STR_EVENT_STRING"Event String", description_str); | |||
| 1483 | printf("%s: 0x%x\n", STR_EVENT_DATA_SIZE"Event Data Size", | |||
| 1484 | pevent_descriptor->event_data_size); | |||
| 1485 | } | |||
| 1486 | ||||
| 1487 | if (pevent_descriptor->debug_event_class_type >= 0x80) | |||
| 1488 | print_formatted_var_size_str(STR_VU_DATA"VU Data", | |||
| 1489 | pevent_specific_data, data_size, fp); | |||
| 1490 | } | |||
| 1491 | ||||
| 1492 | switch (pevent_descriptor->debug_event_class_type) { | |||
| 1493 | case TIME_STAMP_CLASS_TYPE: | |||
| 1494 | ret = parse_time_stamp_event(logs, | |||
| 1495 | pevent_descriptor, | |||
| 1496 | pevent_descriptor_obj, | |||
| 1497 | pevent_specific_data, | |||
| 1498 | pevent_fifos_object, | |||
| 1499 | fp); | |||
| 1500 | break; | |||
| 1501 | case PCIE_CLASS_TYPE: | |||
| 1502 | ret = parse_pcie_event(logs, | |||
| 1503 | pevent_descriptor, | |||
| 1504 | pevent_descriptor_obj, | |||
| 1505 | pevent_specific_data, | |||
| 1506 | pevent_fifos_object, | |||
| 1507 | fp); | |||
| 1508 | break; | |||
| 1509 | case NVME_CLASS_TYPE: | |||
| 1510 | ret = parse_nvme_event(logs, | |||
| 1511 | pevent_descriptor, | |||
| 1512 | pevent_descriptor_obj, | |||
| 1513 | pevent_specific_data, | |||
| 1514 | pevent_fifos_object, | |||
| 1515 | fp); | |||
| 1516 | break; | |||
| 1517 | case RESET_CLASS_TYPE: | |||
| 1518 | case BOOT_SEQUENCE_CLASS_TYPE: | |||
| 1519 | case FIRMWARE_ASSERT_CLASS_TYPE: | |||
| 1520 | case TEMPERATURE_CLASS_TYPE: | |||
| 1521 | case MEDIA_CLASS_TYPE: | |||
| 1522 | parse_common_event(logs, | |||
| 1523 | pevent_descriptor, | |||
| 1524 | pevent_descriptor_obj, | |||
| 1525 | pevent_specific_data, | |||
| 1526 | pevent_fifos_object, | |||
| 1527 | fp); | |||
| 1528 | ret = 0; | |||
| 1529 | break; | |||
| 1530 | case MEDIA_WEAR_CLASS_TYPE: | |||
| 1531 | ret = parse_media_wear_event(logs, | |||
| 1532 | pevent_descriptor, | |||
| 1533 | pevent_descriptor_obj, | |||
| 1534 | pevent_specific_data, | |||
| 1535 | pevent_fifos_object, | |||
| 1536 | fp); | |||
| 1537 | break; | |||
| 1538 | case VIRTUAL_FIFO_EVENT_CLASS_TYPE: | |||
| 1539 | ret = parse_virtual_fifo_event( | |||
| 1540 | logs, | |||
| 1541 | pevent_descriptor, | |||
| 1542 | pevent_descriptor_obj, | |||
| 1543 | pevent_specific_data, | |||
| 1544 | pevent_fifos_object, | |||
| 1545 | fp); | |||
| 1546 | break; | |||
| 1547 | case SMBUS_I2C_I3C_EVENT_CLASS_TYPE: | |||
| 1548 | ret = parse_smbus_event(logs, | |||
| 1549 | pevent_descriptor, | |||
| 1550 | pevent_descriptor_obj, | |||
| 1551 | pevent_specific_data, | |||
| 1552 | pevent_fifos_object, | |||
| 1553 | fp); | |||
| 1554 | break; | |||
| 1555 | case MCTP_EVENT_CLASS_TYPE: | |||
| 1556 | ret = parse_mctp_event(logs, | |||
| 1557 | pevent_descriptor, | |||
| 1558 | pevent_descriptor_obj, | |||
| 1559 | pevent_specific_data, | |||
| 1560 | pevent_fifos_object, | |||
| 1561 | fp); | |||
| 1562 | break; | |||
| 1563 | case RESERVED_CLASS_TYPE: | |||
| 1564 | break; | |||
| 1565 | default: | |||
| 1566 | /* | |||
| 1567 | * Keep the payload of a class with no decoder | |||
| 1568 | * visible; vendor unique classes already print | |||
| 1569 | * theirs as VU Data. | |||
| 1570 | */ | |||
| 1571 | if (pevent_descriptor->debug_event_class_type >= 0x80) | |||
| 1572 | break; | |||
| 1573 | if (pevent_descriptor_obj != NULL((void*)0)) | |||
| 1574 | json_add_formatted_var_size_str( | |||
| 1575 | pevent_descriptor_obj, | |||
| 1576 | STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1577 | pevent_specific_data, data_size); | |||
| 1578 | else | |||
| 1579 | print_formatted_var_size_str( | |||
| 1580 | STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1581 | pevent_specific_data, data_size, | |||
| 1582 | fp); | |||
| 1583 | break; | |||
| 1584 | } | |||
| 1585 | ||||
| 1586 | if (ret) { | |||
| 1587 | nvme_show_error(nvme_show_message(1, "ERROR : OCP : Invalid NVMe Event FIFO entry\n" ) | |||
| 1588 | "ERROR : OCP : Invalid NVMe Event FIFO entry\n")nvme_show_message(1, "ERROR : OCP : Invalid NVMe Event FIFO entry\n" ); | |||
| 1589 | nvme_show_error(nvme_show_message(1, "FIFO: %d, offset: 0x%x\n", event_fifo_number , offset_to_move) | |||
| 1590 | "FIFO: %d, offset: 0x%x\n",nvme_show_message(1, "FIFO: %d, offset: 0x%x\n", event_fifo_number , offset_to_move) | |||
| 1591 | event_fifo_number, offset_to_move)nvme_show_message(1, "FIFO: %d, offset: 0x%x\n", event_fifo_number , offset_to_move); | |||
| 1592 | nvme_show_error(nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size) | |||
| 1593 | "Type: 0x%x, ID: 0x%x, Size: 0x%x\n",nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size) | |||
| 1594 | pevent_descriptor->debug_event_class_type,nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size) | |||
| 1595 | event_id,nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size) | |||
| 1596 | pevent_descriptor->event_data_size)nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size); | |||
| 1597 | return ret; | |||
| 1598 | } | |||
| 1599 | ||||
| 1600 | if (pevent_descriptor_obj != NULL((void*)0) && | |||
| 1601 | pevent_fifo_array != NULL((void*)0)) { | |||
| 1602 | json_array_add_value_object(pevent_fifo_array,json_object_array_add(pevent_fifo_array, pevent_descriptor_obj ) | |||
| 1603 | pevent_descriptor_obj)json_object_array_add(pevent_fifo_array, pevent_descriptor_obj ); | |||
| 1604 | pevent_descriptor_obj = NULL((void*)0); | |||
| 1605 | } else { | |||
| 1606 | if (fp) | |||
| 1607 | fprintf(fp, STR_LINE2"-----------------------------------------------------------------------------\n"); | |||
| 1608 | else | |||
| 1609 | printf(STR_LINE2"-----------------------------------------------------------------------------\n"); | |||
| 1610 | } | |||
| 1611 | } else { | |||
| 1612 | parse_ocp_telemetry_string_log(logs, 0, event_id, | |||
| 1613 | pevent_descriptor->debug_event_class_type, EVENT_STRING, | |||
| 1614 | description_str); | |||
| 1615 | ||||
| 1616 | __cleanup_json_object__attribute__((cleanup(json_free_objectp))) | |||
| 1617 | struct json_object *pevent_descriptor_obj = | |||
| 1618 | ((pevent_fifos_object != NULL((void*)0)) ? json_create_object()json_object_new_object() : NULL((void*)0)); | |||
| 1619 | ||||
| 1620 | if (pevent_descriptor_obj != NULL((void*)0)) { | |||
| 1621 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1622 | STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1623 | pevent_descriptor->debug_event_class_type); | |||
| 1624 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1625 | STR_EVENT_STRING"Event String", description_str); | |||
| 1626 | } else { | |||
| 1627 | if (fp) { | |||
| 1628 | fprintf(fp, "%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1629 | pevent_descriptor->debug_event_class_type); | |||
| 1630 | fprintf(fp, "%s: %s\n", STR_EVENT_STRING"Event String", description_str); | |||
| 1631 | } else { | |||
| 1632 | printf("%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1633 | pevent_descriptor->debug_event_class_type); | |||
| 1634 | printf("%s: %s\n", STR_EVENT_STRING"Event String", description_str); | |||
| 1635 | } | |||
| 1636 | } | |||
| 1637 | ||||
| 1638 | struct nvme_ocp_statistic_snapshot_evt_class_format | |||
| 1639 | *pStaticSnapshotEvent = | |||
| 1640 | (struct nvme_ocp_statistic_snapshot_evt_class_format *) | |||
| 1641 | pevent_descriptor; | |||
| 1642 | ||||
| 1643 | if (pStaticSnapshotEvent != NULL((void*)0) && | |||
| 1644 | pStaticSnapshotEvent->stat_data_size > 0) { | |||
| 1645 | __u8 *pstatistic_entry; | |||
| 1646 | ||||
| 1647 | pstatistic_entry = | |||
| 1648 | (__u8 *)pStaticSnapshotEvent + | |||
| 1649 | sizeof(struct nvme_ocp_telemetry_event_descriptor); | |||
| 1650 | ||||
| 1651 | parse_statistic(logs, | |||
| 1652 | (struct nvme_ocp_telemetry_statistic_descriptor *) | |||
| 1653 | pstatistic_entry, | |||
| 1654 | pevent_descriptor_obj, | |||
| 1655 | fp); | |||
| 1656 | } | |||
| 1657 | ||||
| 1658 | if (pevent_descriptor_obj != NULL((void*)0) && | |||
| 1659 | pevent_fifo_array != NULL((void*)0)) { | |||
| 1660 | json_array_add_value_object(pevent_fifo_array,json_object_array_add(pevent_fifo_array, pevent_descriptor_obj ) | |||
| 1661 | pevent_descriptor_obj)json_object_array_add(pevent_fifo_array, pevent_descriptor_obj ); | |||
| 1662 | pevent_descriptor_obj = NULL((void*)0); | |||
| 1663 | } | |||
| 1664 | } | |||
| 1665 | offset_to_move += (data_size + event_des_size); | |||
| 1666 | } | |||
| 1667 | ||||
| 1668 | if (pevent_fifos_object != NULL((void*)0) && pevent_fifo_array != NULL((void*)0)) { | |||
| 1669 | json_object_add_value_array(pevent_fifos_object, event_fifo_name,json_object_object_add(pevent_fifos_object, event_fifo_name, pevent_fifo_array ) | |||
| 1670 | pevent_fifo_array)json_object_object_add(pevent_fifos_object, event_fifo_name, pevent_fifo_array ); | |||
| 1671 | pevent_fifo_array = NULL((void*)0); | |||
| 1672 | } | |||
| 1673 | ||||
| 1674 | return ret; | |||
| 1675 | } | |||
| 1676 | ||||
| 1677 | /* | |||
| 1678 | * Data Area 1 always begins with the mandatory OCP DA1 header (which in | |||
| 1679 | * turn carries the DA1/DA2 statistics and event FIFO offsets/sizes). A | |||
| 1680 | * controller that has never captured a telemetry log for this type | |||
| 1681 | * reports Data Area 1 as empty (da1_size == 0); reading the DA1 header, | |||
| 1682 | * SMART blocks, statistics, or event FIFOs out of the telemetry buffer in | |||
| 1683 | * that case would run past the end of what was actually fetched/read. | |||
| 1684 | */ | |||
| 1685 | static bool_Bool telemetry_da1_header_present( | |||
| 1686 | struct nvme_ocp_telemetry_offsets *poffsets) | |||
| 1687 | { | |||
| 1688 | return poffsets->da1_size >= sizeof(struct nvme_ocp_header_in_da1); | |||
| 1689 | } | |||
| 1690 | ||||
| 1691 | int parse_event_fifos(const struct ocp_telemetry_logs *logs, | |||
| 1692 | struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets, | |||
| 1693 | FILE *fp) | |||
| 1694 | { | |||
| 1695 | if (poffsets == NULL((void*)0)) { | |||
| 1696 | nvme_show_error("Input buffer was NULL")nvme_show_message(1, "Input buffer was NULL"); | |||
| 1697 | return -1; | |||
| 1698 | } | |||
| 1699 | ||||
| 1700 | if (!telemetry_da1_header_present(poffsets)) | |||
| 1701 | return 0; | |||
| 1702 | ||||
| 1703 | __cleanup_json_object__attribute__((cleanup(json_free_objectp))) struct json_object *pevent_fifos_object = NULL((void*)0); | |||
| 1704 | ||||
| 1705 | if (root != NULL((void*)0)) | |||
| 1706 | pevent_fifos_object = json_create_object()json_object_new_object(); | |||
| 1707 | ||||
| 1708 | __u8 *pda1_header_offset = logs->telemetry + poffsets->da1_start_offset;//512 | |||
| 1709 | __u8 *pda2_offset = logs->telemetry + poffsets->da2_start_offset; | |||
| 1710 | struct nvme_ocp_header_in_da1 *pda1_header = (struct nvme_ocp_header_in_da1 *) | |||
| 1711 | pda1_header_offset; | |||
| 1712 | struct nvme_ocp_event_fifo_data event_fifo[MAX_NUM_FIFOS16]; | |||
| 1713 | ||||
| 1714 | for (int fifo_num = 0; fifo_num < MAX_NUM_FIFOS16; fifo_num++) { | |||
| 1715 | event_fifo[fifo_num].event_fifo_num = fifo_num; | |||
| 1716 | event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_da[fifo_num]; | |||
| 1717 | event_fifo[fifo_num].event_fifo_start = | |||
| 1718 | le64_to_cpu(pda1_header->fifo_offsets[fifo_num].event_fifo_start); | |||
| 1719 | event_fifo[fifo_num].event_fifo_size = | |||
| 1720 | le64_to_cpu(pda1_header->fifo_offsets[fifo_num].event_fifo_size); | |||
| 1721 | } | |||
| 1722 | ||||
| 1723 | //Parse all the FIFOs DA wise | |||
| 1724 | for (int fifo_no = 0; fifo_no < MAX_NUM_FIFOS16; fifo_no++) { | |||
| 1725 | if (event_fifo[fifo_no].event_fifo_da == poffsets->data_area) { | |||
| 1726 | __u64 start_dw = event_fifo[fifo_no].event_fifo_start; | |||
| 1727 | __u64 size_dw = event_fifo[fifo_no].event_fifo_size; | |||
| 1728 | __u64 da_dw = ((poffsets->data_area == 1) ? | |||
| 1729 | poffsets->da1_size : poffsets->da2_size) / SIZE_OF_DWORD4; | |||
| 1730 | __u64 fifo_offset, fifo_size; | |||
| 1731 | __u8 *pfifo_start = NULL((void*)0); | |||
| 1732 | ||||
| 1733 | if (!size_dw || start_dw > da_dw || size_dw > da_dw - start_dw) { | |||
| 1734 | nvme_show_error(nvme_show_message(1, "Invalid FIFO bounds for FIFO %d", fifo_no ) | |||
| 1735 | "Invalid FIFO bounds for FIFO %d",nvme_show_message(1, "Invalid FIFO bounds for FIFO %d", fifo_no ) | |||
| 1736 | fifo_no)nvme_show_message(1, "Invalid FIFO bounds for FIFO %d", fifo_no ); | |||
| 1737 | return -1; | |||
| 1738 | } | |||
| 1739 | fifo_offset = start_dw * SIZE_OF_DWORD4; | |||
| 1740 | fifo_size = size_dw * SIZE_OF_DWORD4; | |||
| 1741 | ||||
| 1742 | if (event_fifo[fifo_no].event_fifo_da == 1) | |||
| 1743 | pfifo_start = pda1_header_offset + fifo_offset; | |||
| 1744 | else if (event_fifo[fifo_no].event_fifo_da == 2) | |||
| 1745 | pfifo_start = pda2_offset + fifo_offset; | |||
| 1746 | else { | |||
| 1747 | nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area)nvme_show_message(1, "Unsupported Data Area:[%d]", poffsets-> data_area); | |||
| 1748 | return -1; | |||
| 1749 | } | |||
| 1750 | ||||
| 1751 | int status = parse_event_fifo(logs, fifo_no, | |||
| 1752 | pfifo_start, | |||
| 1753 | pevent_fifos_object, | |||
| 1754 | poffsets, fifo_size, fp); | |||
| 1755 | ||||
| 1756 | if (status != 0) { | |||
| 1757 | nvme_show_error("Failed to parse Event FIFO. status:%d", status)nvme_show_message(1, "Failed to parse Event FIFO. status:%d", status); | |||
| 1758 | return -1; | |||
| 1759 | } | |||
| 1760 | } | |||
| 1761 | } | |||
| 1762 | ||||
| 1763 | if (pevent_fifos_object != NULL((void*)0) && root != NULL((void*)0)) { | |||
| 1764 | json_object_add_value_array(root,json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Event FIFO info" : "Data Area 2 Event FIFO info", pevent_fifos_object) | |||
| 1765 | poffsets->data_area == 1 ? STR_DA_1_EVENT_FIFO_INFO :json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Event FIFO info" : "Data Area 2 Event FIFO info", pevent_fifos_object) | |||
| 1766 | STR_DA_2_EVENT_FIFO_INFO,json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Event FIFO info" : "Data Area 2 Event FIFO info", pevent_fifos_object) | |||
| 1767 | pevent_fifos_object)json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Event FIFO info" : "Data Area 2 Event FIFO info", pevent_fifos_object); | |||
| 1768 | pevent_fifos_object = NULL((void*)0); | |||
| 1769 | } | |||
| 1770 | ||||
| 1771 | return 0; | |||
| 1772 | } | |||
| 1773 | ||||
| 1774 | #define STAT_NESTED_INDENT" " " " | |||
| 1775 | ||||
| 1776 | /* Where a statistic is printed: a JSON object, or text lines to fp */ | |||
| 1777 | struct stat_sink { | |||
| 1778 | struct json_object *obj; | |||
| 1779 | FILE *fp; | |||
| 1780 | const char *indent; | |||
| 1781 | }; | |||
| 1782 | ||||
| 1783 | static FILE *stat_stream(struct stat_sink *s) | |||
| 1784 | { | |||
| 1785 | return s->fp ? s->fp : stdoutstdout; | |||
| 1786 | } | |||
| 1787 | ||||
| 1788 | static void stat_add_hex(struct stat_sink *s, const char *key, __u32 value, int width) | |||
| 1789 | { | |||
| 1790 | if (s->obj) | |||
| 1791 | json_add_formatted_u32_str(s->obj, key, value); | |||
| 1792 | else | |||
| 1793 | fprintf(stat_stream(s), "%s%s: 0x%0*x\n", s->indent, key, width, value); | |||
| 1794 | } | |||
| 1795 | ||||
| 1796 | static void stat_add_str(struct stat_sink *s, const char *key, const char *value) | |||
| 1797 | { | |||
| 1798 | if (s->obj) | |||
| 1799 | json_object_add_value_string(s->obj, key, value); | |||
| 1800 | else | |||
| 1801 | fprintf(stat_stream(s), "%s%s: %s\n", s->indent, key, value); | |||
| 1802 | } | |||
| 1803 | ||||
| 1804 | static void stat_add_data(struct stat_sink *s, const char *key, __u8 *data, unsigned int size) | |||
| 1805 | { | |||
| 1806 | if (s->obj) { | |||
| 1807 | json_add_formatted_var_size_str(s->obj, key, data, size); | |||
| 1808 | } else { | |||
| 1809 | fputs(s->indent, stat_stream(s)); | |||
| 1810 | print_formatted_var_size_str(key, data, size, stat_stream(s)); | |||
| 1811 | } | |||
| 1812 | } | |||
| 1813 | ||||
| 1814 | /* Returns the JSON array to add items to, NULL in text mode */ | |||
| 1815 | static struct json_object *stat_list_begin(struct stat_sink *s, const char *key) | |||
| 1816 | { | |||
| 1817 | struct json_object *array = NULL((void*)0); | |||
| 1818 | ||||
| 1819 | if (s->obj) { | |||
| 1820 | array = json_create_array()json_object_new_array(); | |||
| 1821 | json_object_add_value_array(s->obj, key, array)json_object_object_add(s->obj, key, array); | |||
| 1822 | } else { | |||
| 1823 | fprintf(stat_stream(s), "%s%s:\n", s->indent, key); | |||
| 1824 | } | |||
| 1825 | return array; | |||
| 1826 | } | |||
| 1827 | ||||
| 1828 | static void stat_item_begin(struct stat_sink *item, struct json_object *array, FILE *fp, | |||
| 1829 | const char *indent) | |||
| 1830 | { | |||
| 1831 | item->obj = array ? json_create_object()json_object_new_object() : NULL((void*)0); | |||
| 1832 | item->fp = fp; | |||
| 1833 | item->indent = indent; | |||
| 1834 | } | |||
| 1835 | ||||
| 1836 | static void stat_item_end(struct stat_sink *item, struct json_object *array) | |||
| 1837 | { | |||
| 1838 | if (array) | |||
| 1839 | json_array_add_value_object(array, item->obj)json_object_array_add(array, item->obj); | |||
| 1840 | else | |||
| 1841 | fprintf(stat_stream(item), "%s%s", item->indent, STR_LINE2"-----------------------------------------------------------------------------\n"); | |||
| 1842 | } | |||
| 1843 | ||||
| 1844 | static __u32 get_le_field(const __u8 *p, unsigned int size) | |||
| 1845 | { | |||
| 1846 | __u32 value = 0; | |||
| 1847 | ||||
| 1848 | while (size--) | |||
| 1849 | value = (value << 8) | p[size]; | |||
| 1850 | return value; | |||
| 1851 | } | |||
| 1852 | ||||
| 1853 | /* Scope fields by offset into the context data (OCP 2.7 section 4.9.12) */ | |||
| 1854 | static const struct { | |||
| 1855 | __u16 stat_id; | |||
| 1856 | const char *name; | |||
| 1857 | __u8 offset; | |||
| 1858 | __u8 size; | |||
| 1859 | } context_scope_fields[] = { | |||
| 1860 | { NAMESPACE_ID_CONTEXT_ID, STR_CONTEXT_NAMESPACE_ID"Namespace ID", 4, 4 }, | |||
| 1861 | { CONTROLLER_ID_CONTEXT_ID, STR_CONTEXT_CONTROLLER_ID"Controller ID", 6, 2 }, | |||
| 1862 | { QUEUE_ID_CONTEXT_ID, STR_CONTEXT_CONTROLLER_ID"Controller ID", 4, 2 }, | |||
| 1863 | { QUEUE_ID_CONTEXT_ID, STR_CONTEXT_QUEUE_ID"Queue ID", 6, 2 }, | |||
| 1864 | }; | |||
| 1865 | ||||
| 1866 | static const struct { | |||
| 1867 | __u16 stat_id; | |||
| 1868 | const char *percent; | |||
| 1869 | const char *raw; | |||
| 1870 | } bad_block_statistics[] = { | |||
| 1871 | { MAX_DIE_BAD_BLOCK_ID, STR_STATISTICS_WORST_DIE_PERCENT"Worst die % of bad blocks", | |||
| 1872 | STR_STATISTICS_WORST_DIE_RAW"Worst die raw number of bad blocks" }, | |||
| 1873 | { MAX_NAND_CHANNEL_BAD_BLOCK_ID, STR_STATISTICS_WORST_NAND_CHANNEL_PERCENT"Worst NAND channel % of bad blocks", | |||
| 1874 | STR_STATISTICS_WORST_NAND_CHANNEL_RAW"Worst NAND channel number of bad blocks" }, | |||
| 1875 | { MIN_NAND_CHANNEL_BAD_BLOCK_ID, STR_STATISTICS_BEST_NAND_CHANNEL_PERCENT"Best NAND channel % of bad blocks", | |||
| 1876 | STR_STATISTICS_BEST_NAND_CHANNEL_RAW"Best NAND channel number of bad blocks" }, | |||
| 1877 | }; | |||
| 1878 | ||||
| 1879 | static bool_Bool is_context_scope_id(__u16 id) | |||
| 1880 | { | |||
| 1881 | return id >= NAMESPACE_ID_CONTEXT_ID && id <= QUEUE_ID_CONTEXT_ID; | |||
| 1882 | } | |||
| 1883 | ||||
| 1884 | static bool_Bool is_context_statistic(struct nvme_ocp_telemetry_statistic_descriptor *d) | |||
| 1885 | { | |||
| 1886 | return d->statistic_info_context_index || | |||
| 1887 | is_context_scope_id(le16_to_cpu(d->statistic_id)); | |||
| 1888 | } | |||
| 1889 | ||||
| 1890 | static void print_statistics(const struct ocp_telemetry_logs *logs, | |||
| 1891 | __u8 *pstats, unsigned int size, | |||
| 1892 | struct json_object *array, | |||
| 1893 | FILE *fp, bool_Bool encapsulated, const char *where); | |||
| 1894 | ||||
| 1895 | static void parse_context_statistic(const struct ocp_telemetry_logs *logs, | |||
| 1896 | __u16 id, __u8 *pdata, | |||
| 1897 | unsigned int data_size, struct stat_sink *s) | |||
| 1898 | { | |||
| 1899 | struct nvme_ocp_statistic_context_data *context = | |||
| 1900 | (struct nvme_ocp_statistic_context_data *)pdata; | |||
| 1901 | __u16 context_size = le16_to_cpu(context->context_data_size); | |||
| 1902 | char where[64]; | |||
| 1903 | struct json_object *array; | |||
| 1904 | struct stat_sink item; | |||
| 1905 | size_t i; | |||
| 1906 | ||||
| 1907 | /* | |||
| 1908 | * The encapsulated descriptors follow the context data whatever its | |||
| 1909 | * declared size, so an invalid Context Data Size is only reported. | |||
| 1910 | */ | |||
| 1911 | if (!context_size || context_size > data_size / SIZE_OF_DWORD4) | |||
| 1912 | nvme_show_error("Context Statistic 0x%x: "nvme_show_message(1, "Context Statistic 0x%x: " "Context Data Size 0x%x is outside 1 to 0x%x" , id, context_size, data_size / 4) | |||
| 1913 | "Context Data Size 0x%x is outside 1 to 0x%x",nvme_show_message(1, "Context Statistic 0x%x: " "Context Data Size 0x%x is outside 1 to 0x%x" , id, context_size, data_size / 4) | |||
| 1914 | id, context_size, data_size / SIZE_OF_DWORD)nvme_show_message(1, "Context Statistic 0x%x: " "Context Data Size 0x%x is outside 1 to 0x%x" , id, context_size, data_size / 4); | |||
| 1915 | ||||
| 1916 | stat_add_hex(s, STR_CONTEXT_DATA_SIZE"Context Data Size", context_size, 0); | |||
| 1917 | stat_add_hex(s, STR_CONTEXT_DATA_RESERVED"Context Data Reserved", le16_to_cpu(context->reserved), 0); | |||
| 1918 | stat_add_data(s, STR_CONTEXT_SCOPE"Context Scope", context->scope, sizeof(context->scope)); | |||
| 1919 | ||||
| 1920 | if (is_context_scope_id(id)) { | |||
| 1921 | array = stat_list_begin(s, STR_CONTEXT_SCOPE_FIELDS"Context Scope Fields"); | |||
| 1922 | for (i = 0; i < ARRAY_SIZE(context_scope_fields)(sizeof(context_scope_fields) / sizeof((context_scope_fields) [0]) + 0); i++) { | |||
| 1923 | __u8 offset = context_scope_fields[i].offset; | |||
| 1924 | __u8 size = context_scope_fields[i].size; | |||
| 1925 | ||||
| 1926 | if (context_scope_fields[i].stat_id != id) | |||
| 1927 | continue; | |||
| 1928 | stat_item_begin(&item, array, s->fp, STAT_NESTED_INDENT" "); | |||
| 1929 | stat_add_str(&item, STR_SCOPE_FIELD_STRING"Scope Field String", context_scope_fields[i].name); | |||
| 1930 | stat_add_hex(&item, STR_SCOPE_FIELD_OFFSET"Scope Field Offset", offset, 0); | |||
| 1931 | stat_add_hex(&item, STR_SCOPE_FIELD_SIZE"Scope Field Size", size, 0); | |||
| 1932 | stat_add_hex(&item, STR_SCOPE_FIELD_VALUE"Scope Field Value", | |||
| 1933 | get_le_field(pdata + offset, size), 0); | |||
| 1934 | stat_item_end(&item, array); | |||
| 1935 | } | |||
| 1936 | } | |||
| 1937 | ||||
| 1938 | snprintf(where, sizeof(where), | |||
| 1939 | "Encapsulated Statistic Descriptors of Context Statistic 0x%x", id); | |||
| 1940 | array = stat_list_begin(s, STR_ENCAPSULATED_STATISTICS"Encapsulated Statistic Descriptors"); | |||
| 1941 | print_statistics(logs, pdata + sizeof(*context), | |||
| 1942 | data_size - sizeof(*context), array, s->fp, | |||
| 1943 | true1, where); | |||
| 1944 | } | |||
| 1945 | ||||
| 1946 | /* | |||
| 1947 | * Prints a statistic as a new object of @pstats_array, into the existing | |||
| 1948 | * object @pstat_obj, or as text. | |||
| 1949 | */ | |||
| 1950 | static void print_statistic(const struct ocp_telemetry_logs *logs, | |||
| 1951 | struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry, | |||
| 1952 | struct json_object *pstats_array, | |||
| 1953 | struct json_object *pstat_obj, FILE *fp, | |||
| 1954 | bool_Bool encapsulated) | |||
| 1955 | { | |||
| 1956 | __u16 id = le16_to_cpu(pstatistic_entry->statistic_id); | |||
| 1957 | __u16 data_dwords = le16_to_cpu(pstatistic_entry->statistic_data_size); | |||
| 1958 | unsigned int data_size = data_dwords * SIZE_OF_DWORD4; | |||
| 1959 | __u8 *pdata = (__u8 *)pstatistic_entry + sizeof(*pstatistic_entry); | |||
| 1960 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1961 | bool_Bool context = is_context_statistic(pstatistic_entry); | |||
| 1962 | struct stat_sink s; | |||
| 1963 | size_t i; | |||
| 1964 | ||||
| 1965 | parse_ocp_telemetry_string_log(logs, 0, id, 0, | |||
| 1966 | STATISTICS_IDENTIFIER_STRING, | |||
| 1967 | description_str); | |||
| 1968 | ||||
| 1969 | stat_item_begin(&s, pstats_array, fp, encapsulated ? STAT_NESTED_INDENT" " : ""); | |||
| 1970 | if (pstat_obj) | |||
| 1971 | s.obj = pstat_obj; | |||
| 1972 | stat_add_hex(&s, STR_STATISTICS_IDENTIFIER"Statistics Identifier", id, 0); | |||
| 1973 | stat_add_str(&s, STR_STATISTICS_IDENTIFIER_STR"Statistic Identifier String", description_str); | |||
| 1974 | stat_add_hex(&s, STR_STATISTICS_INFO_BEHAVIOUR_TYPE"Statistics Info Behavior Type", | |||
| 1975 | pstatistic_entry->statistic_info_behaviour_type, 0); | |||
| 1976 | stat_add_hex(&s, STR_STATISTICS_INFO_CONTEXT_INDEX"Statistics Info Context Index", | |||
| 1977 | pstatistic_entry->statistic_info_context_index, 0); | |||
| 1978 | stat_add_hex(&s, STR_STATISTICS_INFO_HOST_HINT_TYPE"Statistics Info Host Hint Type", | |||
| 1979 | pstatistic_entry->statistic_info_host_hint_type, 0); | |||
| 1980 | stat_add_hex(&s, STR_STATISTICS_INFO_RESERVED"Statistics Info Reserved", | |||
| 1981 | pstatistic_entry->statistic_info_reserved, 0); | |||
| 1982 | stat_add_hex(&s, STR_NAMESPACE_IDENTIFIER"Namespace Identifier", pstatistic_entry->ns_info_nsid, 0); | |||
| 1983 | stat_add_hex(&s, STR_NAMESPACE_INFO_VALID"Namespace Information Valid", pstatistic_entry->ns_info_ns_info_valid, 0); | |||
| 1984 | stat_add_hex(&s, STR_STATISTICS_DATA_SIZE"Statistic Data Size", data_dwords, 0); | |||
| 1985 | stat_add_hex(&s, STR_NAMESPACE_IDENTIFIER_15_0"Namespace Identifier[15:0]", | |||
| 1986 | le16_to_cpu(pstatistic_entry->ns_identifier_15_0), 0); | |||
| 1987 | ||||
| 1988 | /* Context Statistic Descriptors do not nest; the walk reports one that does */ | |||
| 1989 | if (context && !encapsulated) { | |||
| 1990 | if (data_dwords >= CONTEXT_DATA_DWORDS2) { | |||
| 1991 | parse_context_statistic(logs, id, pdata, data_size, &s); | |||
| 1992 | goto out; | |||
| 1993 | } | |||
| 1994 | nvme_show_error("Context Statistic 0x%x: %u data bytes cannot hold "nvme_show_message(1, "Context Statistic 0x%x: %u data bytes cannot hold " "its context data", id, data_size) | |||
| 1995 | "its context data", id, data_size)nvme_show_message(1, "Context Statistic 0x%x: %u data bytes cannot hold " "its context data", id, data_size); | |||
| 1996 | } | |||
| 1997 | ||||
| 1998 | for (i = 0; !context && data_size >= 4 && i < ARRAY_SIZE(bad_block_statistics)(sizeof(bad_block_statistics) / sizeof((bad_block_statistics) [0]) + 0); i++) { | |||
| 1999 | if (bad_block_statistics[i].stat_id != id) | |||
| 2000 | continue; | |||
| 2001 | stat_add_hex(&s, bad_block_statistics[i].percent, pdata[0], 2); | |||
| 2002 | stat_add_hex(&s, bad_block_statistics[i].raw, get_le_field(pdata + 2, 2), 4); | |||
| 2003 | goto out; | |||
| 2004 | } | |||
| 2005 | ||||
| 2006 | stat_add_data(&s, STR_STATISTICS_SPECIFIC_DATA"Statistic Specific Data", pdata, data_size); | |||
| 2007 | out: | |||
| 2008 | if (!pstat_obj) | |||
| 2009 | stat_item_end(&s, pstats_array); | |||
| 2010 | } | |||
| 2011 | ||||
| 2012 | /* | |||
| 2013 | * Prints the statistic descriptors in the @size bytes at @pstats, up to the | |||
| 2014 | * first reserved identifier or the first descriptor that does not fit. | |||
| 2015 | */ | |||
| 2016 | static void print_statistics(const struct ocp_telemetry_logs *logs, | |||
| 2017 | __u8 *pstats, unsigned int size, | |||
| 2018 | struct json_object *array, | |||
| 2019 | FILE *fp, bool_Bool encapsulated, const char *where) | |||
| 2020 | { | |||
| 2021 | struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry; | |||
| 2022 | unsigned int offset = 0, left, data_size; | |||
| 2023 | __u16 id; | |||
| 2024 | ||||
| 2025 | /* Sizes are in Dwords, so the identifier always fits */ | |||
| 2026 | while (offset < size) { | |||
| 2027 | pstatistic_entry = (struct nvme_ocp_telemetry_statistic_descriptor *) | |||
| 2028 | (pstats + offset); | |||
| 2029 | id = le16_to_cpu(pstatistic_entry->statistic_id); | |||
| 2030 | left = size - offset; | |||
| 2031 | ||||
| 2032 | if (id == STATISTICS_RESERVED_ID) | |||
| 2033 | break; | |||
| 2034 | if (left < sizeof(*pstatistic_entry)) { | |||
| 2035 | nvme_show_error("Invalid statistic at offset 0x%x of %s: "nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "descriptor needs %zu bytes, %u left", offset, where, sizeof (*pstatistic_entry), left) | |||
| 2036 | "descriptor needs %zu bytes, %u left",nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "descriptor needs %zu bytes, %u left", offset, where, sizeof (*pstatistic_entry), left) | |||
| 2037 | offset, where, sizeof(*pstatistic_entry), left)nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "descriptor needs %zu bytes, %u left", offset, where, sizeof (*pstatistic_entry), left); | |||
| 2038 | break; | |||
| 2039 | } | |||
| 2040 | data_size = le16_to_cpu(pstatistic_entry->statistic_data_size) * SIZE_OF_DWORD4; | |||
| 2041 | if (left - sizeof(*pstatistic_entry) < data_size) { | |||
| 2042 | nvme_show_error("Invalid statistic at offset 0x%x of %s: "nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Statistic ID 0x%x declares %u data bytes, %zu left", offset , where, id, data_size, left - sizeof(*pstatistic_entry)) | |||
| 2043 | "Statistic ID 0x%x declares %u data bytes, %zu left",nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Statistic ID 0x%x declares %u data bytes, %zu left", offset , where, id, data_size, left - sizeof(*pstatistic_entry)) | |||
| 2044 | offset, where, id, data_size,nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Statistic ID 0x%x declares %u data bytes, %zu left", offset , where, id, data_size, left - sizeof(*pstatistic_entry)) | |||
| 2045 | left - sizeof(*pstatistic_entry))nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Statistic ID 0x%x declares %u data bytes, %zu left", offset , where, id, data_size, left - sizeof(*pstatistic_entry)); | |||
| 2046 | break; | |||
| 2047 | } | |||
| 2048 | if (encapsulated && is_context_statistic(pstatistic_entry)) | |||
| 2049 | nvme_show_error("Invalid statistic at offset 0x%x of %s: "nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Context Statistic Descriptor 0x%x does not nest", offset, where , id) | |||
| 2050 | "Context Statistic Descriptor 0x%x does not nest",nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Context Statistic Descriptor 0x%x does not nest", offset, where , id) | |||
| 2051 | offset, where, id)nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Context Statistic Descriptor 0x%x does not nest", offset, where , id); | |||
| 2052 | ||||
| 2053 | print_statistic(logs, pstatistic_entry, array, NULL((void*)0), fp, | |||
| 2054 | encapsulated); | |||
| 2055 | ||||
| 2056 | /* | |||
| 2057 | * A Context Statistic Descriptor's data size spans its context | |||
| 2058 | * data and its encapsulated descriptors. | |||
| 2059 | */ | |||
| 2060 | offset += sizeof(*pstatistic_entry) + data_size; | |||
| 2061 | } | |||
| 2062 | } | |||
| 2063 | ||||
| 2064 | int parse_statistic(const struct ocp_telemetry_logs *logs, | |||
| 2065 | struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry, | |||
| 2066 | struct json_object *pstat_obj, FILE *fp) | |||
| 2067 | { | |||
| 2068 | if (pstatistic_entry == NULL((void*)0)) { | |||
| 2069 | nvme_show_error("Statistics Input buffer was NULL")nvme_show_message(1, "Statistics Input buffer was NULL"); | |||
| 2070 | return -1; | |||
| 2071 | } | |||
| 2072 | ||||
| 2073 | if (le16_to_cpu(pstatistic_entry->statistic_id) == STATISTICS_RESERVED_ID) | |||
| 2074 | /* End of statistics entries, return -1 to stop processing the buffer */ | |||
| 2075 | return -1; | |||
| 2076 | ||||
| 2077 | print_statistic(logs, pstatistic_entry, NULL((void*)0), pstat_obj, fp, false0); | |||
| 2078 | return 0; | |||
| 2079 | } | |||
| 2080 | ||||
| 2081 | int parse_statistics(const struct ocp_telemetry_logs *logs, | |||
| 2082 | struct json_object *root, | |||
| 2083 | struct nvme_ocp_telemetry_offsets *poffsets, | |||
| 2084 | FILE *fp) | |||
| 2085 | { | |||
| 2086 | if (poffsets == NULL((void*)0)) { | |||
| 2087 | nvme_show_error("Input buffer was NULL")nvme_show_message(1, "Input buffer was NULL"); | |||
| 2088 | return -1; | |||
| 2089 | } | |||
| 2090 | ||||
| 2091 | if (!telemetry_da1_header_present(poffsets)) | |||
| 2092 | return 0; | |||
| 2093 | ||||
| 2094 | __u8 *pda1_ocp_header_offset = logs->telemetry + poffsets->header_size;//512 | |||
| 2095 | struct nvme_ocp_header_in_da1 *pda1_header = | |||
| 2096 | (struct nvme_ocp_header_in_da1 *)pda1_ocp_header_offset; | |||
| 2097 | __u64 start_dw, size_dw, da_dw; | |||
| 2098 | __u8 *pda_offset = NULL((void*)0); | |||
| 2099 | char where[16]; | |||
| 2100 | ||||
| 2101 | //Data is present in the form of DWORDS, So multiplying with sizeof(DWORD) | |||
| 2102 | if (poffsets->data_area == 1) { | |||
| 2103 | start_dw = le64_to_cpu(pda1_header->da1_statistic_start); | |||
| 2104 | size_dw = le64_to_cpu(pda1_header->da1_statistic_size); | |||
| 2105 | da_dw = poffsets->da1_size / SIZE_OF_DWORD4; | |||
| 2106 | pda_offset = pda1_ocp_header_offset; | |||
| 2107 | } else if (poffsets->data_area == 2) { | |||
| 2108 | start_dw = le64_to_cpu(pda1_header->da2_statistic_start); | |||
| 2109 | size_dw = le64_to_cpu(pda1_header->da2_statistic_size); | |||
| 2110 | da_dw = poffsets->da2_size / SIZE_OF_DWORD4; | |||
| 2111 | pda_offset = logs->telemetry + poffsets->da2_start_offset; | |||
| 2112 | } else { | |||
| 2113 | nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area)nvme_show_message(1, "Unsupported Data Area:[%d]", poffsets-> data_area); | |||
| 2114 | return -1; | |||
| 2115 | } | |||
| 2116 | ||||
| 2117 | /* With no statistics, the start is never used */ | |||
| 2118 | if (!size_dw) { | |||
| 2119 | start_dw = 0; | |||
| 2120 | } else if (start_dw > da_dw || size_dw > da_dw - start_dw) { | |||
| 2121 | nvme_show_error("Invalid statistics bounds for Data Area %d",nvme_show_message(1, "Invalid statistics bounds for Data Area %d" , poffsets->data_area) | |||
| 2122 | poffsets->data_area)nvme_show_message(1, "Invalid statistics bounds for Data Area %d" , poffsets->data_area); | |||
| 2123 | return -1; | |||
| 2124 | } | |||
| 2125 | ||||
| 2126 | struct json_object *pstats_array = ((root != NULL((void*)0)) ? json_create_array()json_object_new_array() : NULL((void*)0)); | |||
| 2127 | ||||
| 2128 | snprintf(where, sizeof(where), "Data Area %d", poffsets->data_area); | |||
| 2129 | print_statistics(logs, pda_offset + start_dw * SIZE_OF_DWORD4, size_dw * SIZE_OF_DWORD4, | |||
| 2130 | pstats_array, fp, false0, where); | |||
| 2131 | ||||
| 2132 | if (root != NULL((void*)0) && pstats_array != NULL((void*)0)) | |||
| 2133 | json_object_add_value_array(root,json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Statistics" : "Data Area 2 Statistics", pstats_array) | |||
| 2134 | poffsets->data_area == 1 ? STR_DA_1_STATS : STR_DA_2_STATS,json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Statistics" : "Data Area 2 Statistics", pstats_array) | |||
| 2135 | pstats_array)json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Statistics" : "Data Area 2 Statistics", pstats_array); | |||
| 2136 | ||||
| 2137 | return 0; | |||
| 2138 | } | |||
| 2139 | ||||
| 2140 | static void parse_log_page_header(const struct ocp_telemetry_logs *logs, | |||
| 2141 | struct json_object *pobject, FILE *fp) | |||
| 2142 | { | |||
| 2143 | if (logs->telemetry[0] == NVME_LOG_LID_TELEMETRY_CTRL) | |||
| 2144 | generic_structure_parser(logs->telemetry, | |||
| 2145 | controller_log_page_header, | |||
| 2146 | ARRAY_SIZE(controller_log_page_header)(sizeof(controller_log_page_header) / sizeof((controller_log_page_header )[0]) + 0), pobject, 0, fp); | |||
| 2147 | else | |||
| 2148 | generic_structure_parser(logs->telemetry, | |||
| 2149 | host_log_page_header, | |||
| 2150 | ARRAY_SIZE(host_log_page_header)(sizeof(host_log_page_header) / sizeof((host_log_page_header) [0]) + 0), pobject, 0, fp); | |||
| 2151 | } | |||
| 2152 | ||||
| 2153 | int print_ocp_telemetry_normal(const struct ocp_telemetry_logs *logs, | |||
| 2154 | struct ocp_telemetry_parse_options *options) | |||
| 2155 | { | |||
| 2156 | int status = 0; | |||
| 2157 | char file_path[PATH_MAX4096]; | |||
| 2158 | FILE *fp = stdoutstdout; | |||
| 2159 | ||||
| 2160 | if (logs->telemetry == NULL((void*)0)) { | |||
| 2161 | nvme_show_error("No telemetry data to parse.")nvme_show_message(1, "No telemetry data to parse."); | |||
| 2162 | return -1; | |||
| 2163 | } | |||
| 2164 | ||||
| 2165 | if (options->output_file != NULL((void*)0)) { | |||
| 2166 | snprintf(file_path, sizeof(file_path), "%s.%s", | |||
| 2167 | options->output_file, "txt"); | |||
| 2168 | fp = fopen(file_path, "w"); | |||
| 2169 | if (!fp) { | |||
| 2170 | nvme_show_error("Failed to open %s file.", file_path)nvme_show_message(1, "Failed to open %s file.", file_path); | |||
| 2171 | return -1; | |||
| 2172 | } | |||
| 2173 | } | |||
| 2174 | ||||
| 2175 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2176 | fprintf(fp, "%s\n", STR_LOG_PAGE_HEADER"Log Page Header"); | |||
| 2177 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2178 | parse_log_page_header(logs, NULL((void*)0), fp); | |||
| 2179 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2180 | fprintf(fp, "%s\n", STR_REASON_IDENTIFIER"Reason Identifier"); | |||
| 2181 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2182 | __u8 *preason_identifier_offset = logs->telemetry + | |||
| 2183 | offsetof(struct nvme_ocp_telemetry_host_initiated_header,__builtin_offsetof(struct nvme_ocp_telemetry_host_initiated_header , reason_id) | |||
| 2184 | reason_id)__builtin_offsetof(struct nvme_ocp_telemetry_host_initiated_header , reason_id); | |||
| 2185 | ||||
| 2186 | generic_structure_parser(preason_identifier_offset, reason_identifier, | |||
| 2187 | ARRAY_SIZE(reason_identifier)(sizeof(reason_identifier) / sizeof((reason_identifier)[0]) + 0), NULL((void*)0), 0, fp); | |||
| 2188 | ||||
| 2189 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2190 | fprintf(fp, "%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1"Telemetry Host-Initiated Data Block 1"); | |||
| 2191 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2192 | ||||
| 2193 | //Set DA to 1 and get offsets | |||
| 2194 | struct nvme_ocp_telemetry_offsets offsets = { 0 }; | |||
| 2195 | ||||
| 2196 | offsets.data_area = 1;// Default DA - DA1 | |||
| 2197 | ||||
| 2198 | struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = | |||
| 2199 | (struct nvme_ocp_telemetry_common_header *) logs->telemetry; | |||
| 2200 | ||||
| 2201 | get_telemetry_das_offset_and_size(ptelemetry_common_header, logs->telemetry_size, | |||
| 2202 | &offsets); | |||
| 2203 | ||||
| 2204 | __u8 *pda1_header_offset = logs->telemetry + | |||
| 2205 | offsets.da1_start_offset;//512 | |||
| 2206 | ||||
| 2207 | if (telemetry_da1_header_present(&offsets)) { | |||
| 2208 | generic_structure_parser( | |||
| 2209 | pda1_header_offset, ocp_header_in_da1, | |||
| 2210 | ARRAY_SIZE(ocp_header_in_da1)(sizeof(ocp_header_in_da1) / sizeof((ocp_header_in_da1)[0]) + 0), NULL((void*)0), 0, fp); | |||
| 2211 | ||||
| 2212 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2213 | fprintf(fp, "%s\n", STR_SMART_HEALTH_INFO"SMART / Health Information Log(LID-02h)"); | |||
| 2214 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2215 | __u8 *pda1_smart_offset = pda1_header_offset + | |||
| 2216 | offsetof(struct nvme_ocp_header_in_da1,__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info ) | |||
| 2217 | smart_health_info)__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info ); | |||
| 2218 | //512+512 =1024 | |||
| 2219 | ||||
| 2220 | generic_structure_parser(pda1_smart_offset, smart, | |||
| 2221 | ARRAY_SIZE(smart)(sizeof(smart) / sizeof((smart)[0]) + 0), NULL((void*)0), 0, fp); | |||
| 2222 | ||||
| 2223 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2224 | fprintf(fp, "%s\n", STR_SMART_HEALTH_INTO_EXTENDED"SMART / Health Information Extended(LID-C0h)"); | |||
| 2225 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2226 | __u8 *pda1_smart_ext_offset = pda1_header_offset + | |||
| 2227 | offsetof(struct nvme_ocp_header_in_da1,__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended ) | |||
| 2228 | smart_health_info_extended)__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended ); | |||
| 2229 | ||||
| 2230 | generic_structure_parser( | |||
| 2231 | pda1_smart_ext_offset, smart_extended, | |||
| 2232 | ARRAY_SIZE(smart_extended)(sizeof(smart_extended) / sizeof((smart_extended)[0]) + 0), NULL((void*)0), 0, fp); | |||
| 2233 | } | |||
| 2234 | ||||
| 2235 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2236 | fprintf(fp, "%s\n", STR_DA_1_STATS"Data Area 1 Statistics"); | |||
| 2237 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2238 | ||||
| 2239 | status = parse_statistics(logs, NULL((void*)0), &offsets, fp); | |||
| 2240 | if (status != 0) { | |||
| 2241 | nvme_show_error("status: %d", status)nvme_show_message(1, "status: %d", status); | |||
| 2242 | status = -1; | |||
| 2243 | goto out; | |||
| 2244 | } | |||
| 2245 | ||||
| 2246 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2247 | fprintf(fp, "%s\n", STR_DA_1_EVENT_FIFO_INFO"Data Area 1 Event FIFO info"); | |||
| 2248 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2249 | status = parse_event_fifos(logs, NULL((void*)0), &offsets, fp); | |||
| 2250 | if (status != 0) { | |||
| 2251 | status = -1; | |||
| 2252 | goto out; | |||
| 2253 | } | |||
| 2254 | ||||
| 2255 | //Set the DA to 2 | |||
| 2256 | if (options->data_area >= DATA_AREA_2) { | |||
| 2257 | offsets.data_area = 2; | |||
| 2258 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2259 | fprintf(fp, "%s\n", STR_DA_2_STATS"Data Area 2 Statistics"); | |||
| 2260 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2261 | status = parse_statistics(logs, NULL((void*)0), &offsets, fp); | |||
| 2262 | if (status != 0) { | |||
| 2263 | nvme_show_error("status: %d", status)nvme_show_message(1, "status: %d", status); | |||
| 2264 | status = -1; | |||
| 2265 | goto out; | |||
| 2266 | } | |||
| 2267 | ||||
| 2268 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2269 | fprintf(fp, "%s\n", STR_DA_2_EVENT_FIFO_INFO"Data Area 2 Event FIFO info"); | |||
| 2270 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2271 | status = parse_event_fifos(logs, NULL((void*)0), &offsets, fp); | |||
| 2272 | if (status != 0) { | |||
| 2273 | status = -1; | |||
| 2274 | goto out; | |||
| 2275 | } | |||
| 2276 | } | |||
| 2277 | ||||
| 2278 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2279 | out: | |||
| 2280 | if (fp != stdoutstdout) | |||
| 2281 | fclose(fp); | |||
| 2282 | ||||
| 2283 | return status; | |||
| 2284 | } | |||
| 2285 | ||||
| 2286 | #ifdef CONFIG_JSONC | |||
| 2287 | int print_ocp_telemetry_json(const struct ocp_telemetry_logs *logs, | |||
| 2288 | struct ocp_telemetry_parse_options *options) | |||
| 2289 | { | |||
| 2290 | int status = 0; | |||
| 2291 | char file_path[PATH_MAX4096]; | |||
| 2292 | ||||
| 2293 | //create json objects | |||
| 2294 | struct json_object *root, *pheader, *preason_identifier, *da1_header, *smart_obj, | |||
| 2295 | *ext_smart_obj; | |||
| 2296 | ||||
| 2297 | root = json_create_object()json_object_new_object(); | |||
| 2298 | ||||
| 2299 | //Add data to root json object | |||
| 2300 | ||||
| 2301 | //"Log Page Header" | |||
| 2302 | pheader = json_create_object()json_object_new_object(); | |||
| 2303 | ||||
| 2304 | parse_log_page_header(logs, pheader, NULL((void*)0)); | |||
| 2305 | json_object_add_value_object(root, STR_LOG_PAGE_HEADER, pheader)json_object_object_add(root, "Log Page Header", pheader); | |||
| 2306 | ||||
| 2307 | //"Reason Identifier" | |||
| 2308 | preason_identifier = json_create_object()json_object_new_object(); | |||
| 2309 | ||||
| 2310 | __u8 *preason_identifier_offset = logs->telemetry + | |||
| 2311 | offsetof(struct nvme_ocp_telemetry_host_initiated_header, reason_id)__builtin_offsetof(struct nvme_ocp_telemetry_host_initiated_header , reason_id); | |||
| 2312 | ||||
| 2313 | generic_structure_parser(preason_identifier_offset, reason_identifier, | |||
| 2314 | ARRAY_SIZE(reason_identifier)(sizeof(reason_identifier) / sizeof((reason_identifier)[0]) + 0), preason_identifier, 0, NULL((void*)0)); | |||
| 2315 | json_object_add_value_object(pheader, STR_REASON_IDENTIFIER, preason_identifier)json_object_object_add(pheader, "Reason Identifier", preason_identifier ); | |||
| 2316 | ||||
| 2317 | struct nvme_ocp_telemetry_offsets offsets = { 0 }; | |||
| 2318 | ||||
| 2319 | //Set DA to 1 and get offsets | |||
| 2320 | offsets.data_area = 1; | |||
| 2321 | struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = | |||
| 2322 | (struct nvme_ocp_telemetry_common_header *) logs->telemetry; | |||
| 2323 | ||||
| 2324 | get_telemetry_das_offset_and_size(ptelemetry_common_header, logs->telemetry_size, | |||
| 2325 | &offsets); | |||
| 2326 | ||||
| 2327 | //"Telemetry Host-Initiated Data Block 1" | |||
| 2328 | __u8 *pda1_header_offset = logs->telemetry + offsets.da1_start_offset;//512 | |||
| 2329 | ||||
| 2330 | if (telemetry_da1_header_present(&offsets)) { | |||
| 2331 | da1_header = json_create_object()json_object_new_object(); | |||
| 2332 | ||||
| 2333 | generic_structure_parser( | |||
| 2334 | pda1_header_offset, ocp_header_in_da1, | |||
| 2335 | ARRAY_SIZE(ocp_header_in_da1)(sizeof(ocp_header_in_da1) / sizeof((ocp_header_in_da1)[0]) + 0), da1_header, 0, NULL((void*)0)); | |||
| 2336 | json_object_add_value_object(root,json_object_object_add(root, "Telemetry Host-Initiated Data Block 1" , da1_header) | |||
| 2337 | STR_TELEMETRY_HOST_DATA_BLOCK_1,json_object_object_add(root, "Telemetry Host-Initiated Data Block 1" , da1_header) | |||
| 2338 | da1_header)json_object_object_add(root, "Telemetry Host-Initiated Data Block 1" , da1_header); | |||
| 2339 | ||||
| 2340 | //"SMART / Health Information Log(LID-02h)" | |||
| 2341 | __u8 *pda1_smart_offset = pda1_header_offset + | |||
| 2342 | offsetof(struct nvme_ocp_header_in_da1,__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info ) | |||
| 2343 | smart_health_info)__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info ); | |||
| 2344 | smart_obj = json_create_object()json_object_new_object(); | |||
| 2345 | ||||
| 2346 | generic_structure_parser(pda1_smart_offset, smart, | |||
| 2347 | ARRAY_SIZE(smart)(sizeof(smart) / sizeof((smart)[0]) + 0), smart_obj, 0, | |||
| 2348 | NULL((void*)0)); | |||
| 2349 | json_object_add_value_object(da1_header, STR_SMART_HEALTH_INFO,json_object_object_add(da1_header, "SMART / Health Information Log(LID-02h)" , smart_obj) | |||
| 2350 | smart_obj)json_object_object_add(da1_header, "SMART / Health Information Log(LID-02h)" , smart_obj); | |||
| 2351 | ||||
| 2352 | //"SMART / Health Information Extended(LID-C0h)" | |||
| 2353 | __u8 *pda1_smart_ext_offset = pda1_header_offset + | |||
| 2354 | offsetof(struct nvme_ocp_header_in_da1,__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended ) | |||
| 2355 | smart_health_info_extended)__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended ); | |||
| 2356 | ext_smart_obj = json_create_object()json_object_new_object(); | |||
| 2357 | ||||
| 2358 | generic_structure_parser( | |||
| 2359 | pda1_smart_ext_offset, smart_extended, | |||
| 2360 | ARRAY_SIZE(smart_extended)(sizeof(smart_extended) / sizeof((smart_extended)[0]) + 0), ext_smart_obj, 0, NULL((void*)0)); | |||
| 2361 | json_object_add_value_object(da1_header,json_object_object_add(da1_header, "SMART / Health Information Extended(LID-C0h)" , ext_smart_obj) | |||
| 2362 | STR_SMART_HEALTH_INTO_EXTENDED,json_object_object_add(da1_header, "SMART / Health Information Extended(LID-C0h)" , ext_smart_obj) | |||
| 2363 | ext_smart_obj)json_object_object_add(da1_header, "SMART / Health Information Extended(LID-C0h)" , ext_smart_obj); | |||
| 2364 | } | |||
| 2365 | ||||
| 2366 | //Data Area 1 Statistics | |||
| 2367 | status = parse_statistics(logs, root, &offsets, NULL((void*)0)); | |||
| 2368 | if (status != 0) { | |||
| 2369 | nvme_show_error("status: %d", status)nvme_show_message(1, "status: %d", status); | |||
| 2370 | status = -1; | |||
| 2371 | goto out; | |||
| 2372 | } | |||
| 2373 | ||||
| 2374 | //Data Area 1 Event FIFOs | |||
| 2375 | status = parse_event_fifos(logs, root, &offsets, NULL((void*)0)); | |||
| 2376 | if (status != 0) { | |||
| 2377 | status = -1; | |||
| 2378 | goto out; | |||
| 2379 | } | |||
| 2380 | ||||
| 2381 | if (options->data_area >= DATA_AREA_2) { | |||
| 2382 | //Set the DA to 2 | |||
| 2383 | offsets.data_area = 2; | |||
| 2384 | //Data Area 2 Statistics | |||
| 2385 | status = parse_statistics(logs, root, &offsets, NULL((void*)0)); | |||
| 2386 | if (status != 0) { | |||
| 2387 | nvme_show_error("status: %d", status)nvme_show_message(1, "status: %d", status); | |||
| 2388 | status = -1; | |||
| 2389 | goto out; | |||
| 2390 | } | |||
| 2391 | ||||
| 2392 | //Data Area 2 Event FIFOs | |||
| 2393 | status = parse_event_fifos(logs, root, &offsets, NULL((void*)0)); | |||
| 2394 | if (status != 0) { | |||
| 2395 | status = -1; | |||
| 2396 | goto out; | |||
| 2397 | } | |||
| 2398 | } | |||
| 2399 | ||||
| 2400 | if (options->output_file != NULL((void*)0)) { | |||
| 2401 | const char *json_string = json_object_to_json_string(root); | |||
| 2402 | snprintf(file_path, sizeof(file_path), "%s.%s", | |||
| 2403 | options->output_file, "json"); | |||
| 2404 | FILE *fp = fopen(file_path, "w"); | |||
| 2405 | ||||
| 2406 | if (fp) { | |||
| 2407 | fputs(json_string, fp); | |||
| 2408 | fclose(fp); | |||
| 2409 | } else { | |||
| 2410 | nvme_show_error("Failed to open %s file.", file_path)nvme_show_message(1, "Failed to open %s file.", file_path); | |||
| 2411 | status = -1; | |||
| 2412 | } | |||
| 2413 | } else { | |||
| 2414 | //Print root json object | |||
| 2415 | json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 << 1) | (1 << 4))); | |||
| 2416 | nvme_show_result("\n")nvme_show_message(0, "\n"); | |||
| 2417 | } | |||
| 2418 | ||||
| 2419 | out: | |||
| 2420 | json_free_object(root)json_object_put(root); | |||
| 2421 | ||||
| 2422 | return status; | |||
| 2423 | } | |||
| 2424 | #endif /* CONFIG_JSONC */ |