From 92621d588aeb4b15408ef321dfab7cd78f89600a Mon Sep 17 00:00:00 2001 From: Brandon Paupore Date: Wed, 28 Jan 2026 20:07:30 -0600 Subject: [PATCH] nvme-print: support PEL VS Events and Info This adds support for reporting and parsing Vendor Specific events within a Persistent Event Log, and accounts for any Vendor Specific Information for parsing Event Data. Without this change, the presence of any VS Information associated with an event would result in the incorrect data offset being used. Signed-off-by: Brandon Paupore Reviewed-by: Jeffrey Lien --- libnvme/src/nvme/types.h | 30 ++++++++++++++ nvme-print-json.c | 89 ++++++++++++++++++++++++++++++++++++++-- nvme-print-stdout.c | 72 +++++++++++++++++++++++++++++--- 3 files changed, 182 insertions(+), 9 deletions(-) diff --git a/libnvme/src/nvme/types.h b/libnvme/src/nvme/types.h index 311bf78b23..1a479a6a0b 100644 --- a/libnvme/src/nvme/types.h +++ b/libnvme/src/nvme/types.h @@ -4554,6 +4554,36 @@ enum nvme_pel_ehai_pit { NVME_PEL_EHAI_PIT_NOT_ASSOCIATED = 3, }; +/** + * enum nvme_pel_vsedt_code - Persistent Event Log - Vendor Specific Event Data Type Code + * @NVME_PEL_VSEDT_RSVD: Reserved + * @NVME_PEL_VSEDT_EVENT_NAME: Event Name + * @NVME_PEL_VSEDT_ASCII_STRING: ASCII String + * @NVME_PEL_VSEDT_BINARY: Binary + * @NVME_PEL_VSEDT_SIGNED_INT: Signed Integer + */ +enum nvme_pel_vsedt_code { + NVME_PEL_VSEDT_RSVD = 0, + NVME_PEL_VSEDT_EVENT_NAME = 1, + NVME_PEL_VSEDT_ASCII_STRING = 2, + NVME_PEL_VSEDT_BINARY = 3, + NVME_PEL_VSEDT_SIGNED_INT = 4, +}; + +/** + * struct nvme_vs_event_desc - Vendor Specific Event Descriptor + * @vsec: Vendor Specific Event Code + * @vsedt: Vendor Specific Event Data Type + * @uidx: UUID Index + * @vsedl: Vendor Specific Event Data Length + */ +struct nvme_vs_event_desc { + __le16 vsec; + __u8 vsedt; + __u8 uidx; + __le16 vsedl; +}; + /** * struct nvme_fw_commit_event - Firmware Commit Event Data * @old_fw_rev: Old Firmware Revision diff --git a/nvme-print-json.c b/nvme-print-json.c index ca5d774830..b51485666f 100644 --- a/nvme-print-json.c +++ b/nvme-print-json.c @@ -1811,10 +1811,81 @@ static void json_pel_thermal_excursion(void *pevent_log_info, __u32 offset, obj_add_uint(valid_attrs, "threshold", thermal_exc_event->threshold); } +static void json_pel_vs_event_data(struct json_object *valid_attrs, void *vsed, + __u8 vsedt, __u16 vsedl) +{ + struct json_object *vs_data = json_create_object(); + char *str; + + switch (vsedt) { + case NVME_PEL_VSEDT_EVENT_NAME: + str = malloc(vsedl + 1); + if (str) { + memcpy(str, vsed, vsedl); + str[vsedl] = '\0'; + obj_add_str(vs_data, "event_name", str); + free(str); + } + break; + case NVME_PEL_VSEDT_ASCII_STRING: + str = malloc(vsedl + 1); + if (str) { + memcpy(str, vsed, vsedl); + str[vsedl] = '\0'; + obj_add_str(vs_data, "ascii_string_data", str); + free(str); + } + break; + case NVME_PEL_VSEDT_BINARY: + obj_d(vs_data, "binary_data", (unsigned char *)vsed, vsedl, 16, 1); + break; + case NVME_PEL_VSEDT_SIGNED_INT: + obj_add_int(vs_data, "signed_integer_data", *(int64_t *)vsed); + break; + default: + obj_d(vs_data, "reserved_data_type_bin", (unsigned char *)vsed, vsedl, 16, 1); + break; + } + + obj_add_obj(valid_attrs, "vs_event_data", vs_data); +} + +static void json_pel_vendor_specific_event(void *pevent_log_info, __u32 offset, + __u32 event_data_len, struct json_object *valid_attrs) +{ + __u32 progress = 0; + __u16 vsedl; + uint i; + struct nvme_vs_event_desc *vs_desc; + struct json_object *vs_events = json_create_array(); + + for (i = 0; progress < event_data_len; i++) { + struct json_object *vs_event = json_create_object(); + + vs_desc = pevent_log_info + offset + progress; + vsedl = le16_to_cpu(vs_desc->vsedl); + + obj_add_uint(vs_event, "vs_event_descriptor_number", i); + obj_add_uint(vs_event, "vs_event_code", le16_to_cpu(vs_desc->vsec)); + obj_add_uint(vs_event, "vs_event_data_type", vs_desc->vsedt); + obj_add_uint(vs_event, "vs_event_uuid_index", vs_desc->uidx); + obj_add_uint(vs_event, "vs_event_data_len", vsedl); + + if (vsedl) + json_pel_vs_event_data(vs_event, vs_desc + 1, vs_desc->vsedt, vsedl); + + array_add_obj(vs_events, vs_event); + progress += sizeof(*vs_desc) + vsedl; + } + + obj_add_array(valid_attrs, "vs_event_entry", vs_events); +} + static void json_pevent_entry(void *pevent_log_info, __u8 action, __u32 size, const char *devname, __u32 offset, struct json_object *valid) { int i; + __u16 vsil, el; struct nvme_persistent_event_log *pevent_log_head = pevent_log_info; struct nvme_persistent_event_entry *pevent_entry_head; struct json_object *valid_attrs; @@ -1824,8 +1895,10 @@ static void json_pevent_entry(void *pevent_log_info, __u8 action, __u32 size, co break; pevent_entry_head = pevent_log_info + offset; + vsil = le16_to_cpu(pevent_entry_head->vsil); + el = le16_to_cpu(pevent_entry_head->el); - if (offset + pevent_entry_head->ehl + 3 + le16_to_cpu(pevent_entry_head->el) >= + if (offset + pevent_entry_head->ehl + 3 + el >= size) break; @@ -1841,10 +1914,14 @@ static void json_pevent_entry(void *pevent_log_info, __u8 action, __u32 size, co obj_add_uint64(valid_attrs, "event_time_stamp", le64_to_cpu(pevent_entry_head->ets)); obj_add_uint(valid_attrs, "port_id", le16_to_cpu(pevent_entry_head->pelpid)); - obj_add_uint(valid_attrs, "vu_info_len", le16_to_cpu(pevent_entry_head->vsil)); - obj_add_uint(valid_attrs, "event_len", le16_to_cpu(pevent_entry_head->el)); + obj_add_uint(valid_attrs, "vu_info_len", vsil); + obj_add_uint(valid_attrs, "event_len", el); + + if (vsil) + obj_d(valid_attrs, "vs_info_bin", + (void *)pevent_entry_head + 1, vsil, 16, 1); - offset += pevent_entry_head->ehl + 3; + offset += pevent_entry_head->ehl + vsil + 3; switch (pevent_entry_head->etype) { case NVME_PEL_SMART_HEALTH_EVENT: @@ -1887,6 +1964,10 @@ static void json_pevent_entry(void *pevent_log_info, __u8 action, __u32 size, co case NVME_PEL_THERMAL_EXCURSION_EVENT: json_pel_thermal_excursion(pevent_log_info, offset, valid_attrs); break; + case NVME_PEL_VENDOR_SPECIFIC_EVENT: + json_pel_vendor_specific_event(pevent_log_info, offset, el - vsil, + valid_attrs); + break; default: break; } diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index 7995052a3e..d1333f2d95 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -312,6 +312,8 @@ void nvme_show_pel_header(struct nvme_persistent_event_log *pevent_log_head, int static void pel_event_header(int i, struct nvme_persistent_event_entry *pevent_entry_head, int human) { + __u16 vsil = le16_to_cpu(pevent_entry_head->vsil); + printf("Event Number: %u\n", i); printf("Event Type: %s\n", nvme_pel_event_to_string(pevent_entry_head->etype)); printf("Event Type Revision: %u\n", pevent_entry_head->etype_rev); @@ -324,8 +326,13 @@ static void pel_event_header(int i, struct nvme_persistent_event_entry *pevent_e printf("Controller Identifier: %u\n", le16_to_cpu(pevent_entry_head->cntlid)); printf("Event Timestamp: %"PRIu64"\n", le64_to_cpu(pevent_entry_head->ets)); printf("Port Identifier: %u\n", le16_to_cpu(pevent_entry_head->pelpid)); - printf("Vendor Specific Information Length: %u\n", le16_to_cpu(pevent_entry_head->vsil)); + printf("Vendor Specific Information Length: %u\n", vsil); printf("Event Length: %u\n", le16_to_cpu(pevent_entry_head->el)); + + if (vsil) { + printf("Vendor Specific Information:\n"); + d((void *)pevent_entry_head + 1, vsil, 16, 1); + } } static void pel_smart_health_event(void *pevent_log_info, __u32 offset, const char *devname) @@ -493,11 +500,62 @@ static void pel_thermal_excursion_event(void *pevent_log_info, __u32 offset) printf("Threshold: %u\n", thermal_exc_event->threshold); } +static void pel_vs_event_data(void *vsed, __u8 vsedt, __u16 vsedl) +{ + printf("Vendor Specific Event Data:\n"); + switch (vsedt) { + case NVME_PEL_VSEDT_EVENT_NAME: + printf("Event Name for Vendor Specific Event Code:\n"); + printf("%.*s\n", vsedl, (char *)vsed); + break; + case NVME_PEL_VSEDT_ASCII_STRING: + printf("ASCII String Data:\n"); + printf("%.*s\n", vsedl, (char *)vsed); + break; + case NVME_PEL_VSEDT_BINARY: + printf("Binary Data:\n"); + d(vsed, vsedl, 16, 1); + break; + case NVME_PEL_VSEDT_SIGNED_INT: + printf("Signed Integer Data: %" PRId64 "\n", (int64_t)vsedt); + break; + default: + printf("Reserved data type. As Binary:\n"); + d(vsed, vsedl, 16, 1); + } +} + +static void pel_vendor_specific_event(void *pevent_log_info, __u32 offset, + __u32 event_data_len) +{ + __u32 progress = 0; + __u16 vsedl; + int i; + struct nvme_vs_event_desc *vs_desc; + + printf("Vendor Specific Event Entry:\n"); + for (i = 0; progress < event_data_len; i++) { + vs_desc = pevent_log_info + offset + progress; + vsedl = le16_to_cpu(vs_desc->vsedl); + + printf("Vendor Specific Event Descriptor %u:\n", i); + printf("Vendor Specific Event Code: %u\n", le16_to_cpu(vs_desc->vsec)); + printf("Vendor Specific Event Data Type: %u\n", vs_desc->vsedt); + printf("Vendor Specific Event UIndex: %u\n", vs_desc->uidx); + printf("Vendor Specific Event Data Length: %u\n", vsedl); + if (vsedl) + pel_vs_event_data(vs_desc + 1, vs_desc->vsedt, + vsedl); + progress += sizeof(*vs_desc) + vsedl; + } +} + static void stdout_persistent_event_log(void *pevent_log_info, __u8 action, __u32 size, const char *devname) { struct nvme_persistent_event_log *pevent_log_head; __u32 offset = sizeof(*pevent_log_head); + __u16 vsil, el; struct nvme_persistent_event_entry *pevent_entry_head; int human = stdout_print_ops.flags & VERBOSE; @@ -522,14 +580,15 @@ static void stdout_persistent_event_log(void *pevent_log_info, __u8 action, __u3 break; pevent_entry_head = pevent_log_info + offset; + vsil = le16_to_cpu(pevent_entry_head->vsil); + el = le16_to_cpu(pevent_entry_head->el); - if ((offset + pevent_entry_head->ehl + 3 + - le16_to_cpu(pevent_entry_head->el)) >= size) + if ((offset + pevent_entry_head->ehl + 3 + el) >= size) break; pel_event_header(i, pevent_entry_head, human); - offset += pevent_entry_head->ehl + 3; + offset += pevent_entry_head->ehl + vsil + 3; switch (pevent_entry_head->etype) { case NVME_PEL_SMART_HEALTH_EVENT: @@ -574,11 +633,14 @@ static void stdout_persistent_event_log(void *pevent_log_info, __u8 action, __u3 case NVME_PEL_SANITIZE_MEDIA_VERIF_EVENT: printf("Sanitize Media Verification Event\n"); break; + case NVME_PEL_VENDOR_SPECIFIC_EVENT: + pel_vendor_specific_event(pevent_log_info, offset, el - vsil); + break; default: printf("Reserved Event\n\n"); break; } - offset += le16_to_cpu(pevent_entry_head->el); + offset += el; printf("\n"); } }