From b057e79ee013a4898b7cfb68b88d32d0a1d798f6 Mon Sep 17 00:00:00 2001 From: Brandon Paupore Date: Fri, 30 Jan 2026 17:03:25 -0600 Subject: [PATCH 1/2] nvme-print: expose PEL print functions This allows for any plugin to more easily parse Persistent Event log data without requiring that the various event parsing functions be copied into the plugin. This then allows for more specific handling of Vendor Specific Information associated with any event or for any Vendor Specific Events. Signed-off-by: Brandon Paupore Reviewed-by: Jeffrey Lien --- nvme-print-json.c | 94 ++++++++++++++++++++++++--------------------- nvme-print-stdout.c | 74 ++++++++++++++++++++--------------- nvme-print.h | 50 ++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 75 deletions(-) diff --git a/nvme-print-json.c b/nvme-print-json.c index 81510c5684..fa8cd5e222 100644 --- a/nvme-print-json.c +++ b/nvme-print-json.c @@ -1560,8 +1560,8 @@ static void json_add_bitmap(int i, __u8 seb, struct json_object *r) } } -static void json_pevent_log_head(struct nvme_persistent_event_log *pevent_log_head, - struct json_object *r) +void nvme_json_pevent_log_head(struct nvme_persistent_event_log *pevent_log_head, + struct json_object *r) { int i; char sn[sizeof(pevent_log_head->sn) + 1]; @@ -1596,8 +1596,8 @@ static void json_pevent_log_head(struct nvme_persistent_event_log *pevent_log_he } } -static void json_pel_smart_health(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_smart_health(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { char key[128]; struct nvme_smart_log *smart_event = pevent_log_info + offset; @@ -1654,7 +1654,8 @@ static void json_pel_smart_health(void *pevent_log_info, __u32 offset, le32_to_cpu(smart_event->thm_temp2_total_time)); } -static void json_pel_fw_commit(void *pevent_log_info, __u32 offset, struct json_object *valid_attrs) +void nvme_json_pel_fw_commit(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { char fw_str[50]; struct nvme_fw_commit_event *fw_commit_event = pevent_log_info + offset; @@ -1673,7 +1674,8 @@ static void json_pel_fw_commit(void *pevent_log_info, __u32 offset, struct json_ le16_to_cpu(fw_commit_event->vndr_assign_fw_commit_rc)); } -static void json_pel_timestamp(void *pevent_log_info, __u32 offset, struct json_object *valid_attrs) +void nvme_json_pel_timestamp(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_time_stamp_change_event *ts_change_event = pevent_log_info + offset; @@ -1682,8 +1684,9 @@ static void json_pel_timestamp(void *pevent_log_info, __u32 offset, struct json_ le64_to_cpu(ts_change_event->ml_secs_since_reset)); } -static void json_pel_power_on_reset(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs, __le16 vsil, __le16 el) +void nvme_json_pel_power_on_reset(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs, + __le16 vsil, __le16 el) { __u64 *fw_rev; char fw_str[50]; @@ -1711,8 +1714,8 @@ static void json_pel_power_on_reset(void *pevent_log_info, __u32 offset, } } -static void json_pel_nss_hw_error(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_nss_hw_error(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_nss_hw_err_event *nss_hw_err_event = pevent_log_info + offset; @@ -1720,7 +1723,8 @@ static void json_pel_nss_hw_error(void *pevent_log_info, __u32 offset, le16_to_cpu(nss_hw_err_event->nss_hw_err_event_code)); } -static void json_pel_change_ns(void *pevent_log_info, __u32 offset, struct json_object *valid_attrs) +void nvme_json_pel_change_ns(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_change_ns_event *ns_event = pevent_log_info + offset; @@ -1735,8 +1739,8 @@ static void json_pel_change_ns(void *pevent_log_info, __u32 offset, struct json_ obj_add_uint(valid_attrs, "nsid", le32_to_cpu(ns_event->nsid)); } -static void json_pel_format_start(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_format_start(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_format_nvm_start_event *format_start_event = pevent_log_info + offset; @@ -1746,8 +1750,8 @@ static void json_pel_format_start(void *pevent_log_info, __u32 offset, le32_to_cpu(format_start_event->format_nvm_cdw10)); } -static void json_pel_format_completion(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_format_completion(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_format_nvm_compln_event *format_cmpln_event = pevent_log_info + offset; @@ -1757,8 +1761,8 @@ static void json_pel_format_completion(void *pevent_log_info, __u32 offset, obj_add_uint(valid_attrs, "compln_info", le16_to_cpu(format_cmpln_event->compln_info)); obj_add_uint(valid_attrs, "status_field", le32_to_cpu(format_cmpln_event->status_field)); } -static void json_pel_sanitize_start(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_sanitize_start(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_sanitize_start_event *sanitize_start_event = pevent_log_info + offset; @@ -1767,8 +1771,8 @@ static void json_pel_sanitize_start(void *pevent_log_info, __u32 offset, obj_add_uint(valid_attrs, "sani_cdw11", le32_to_cpu(sanitize_start_event->sani_cdw11)); } -static void json_pel_sanitize_completion(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_sanitize_completion(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_sanitize_compln_event *sanitize_cmpln_event = pevent_log_info + offset; @@ -1777,8 +1781,8 @@ static void json_pel_sanitize_completion(void *pevent_log_info, __u32 offset, obj_add_uint(valid_attrs, "cmpln_info", le16_to_cpu(sanitize_cmpln_event->cmpln_info)); } -static void json_pel_set_feature(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_set_feature(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_set_feature_event *set_feat_event = pevent_log_info + offset; int fid = NVME_GET(le32_to_cpu(set_feat_event->cdw_mem[0]), SET_FEATURES_CDW10_FID); @@ -1796,14 +1800,14 @@ static void json_pel_set_feature(void *pevent_log_info, __u32 offset, } } -static void json_pel_telemetry_crt(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_telemetry_crt(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { obj_d(valid_attrs, "create", pevent_log_info + offset, 512, 16, 1); } -static void json_pel_thermal_excursion(void *pevent_log_info, __u32 offset, - struct json_object *valid_attrs) +void nvme_json_pel_thermal_excursion(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) { struct nvme_thermal_exc_event *thermal_exc_event = pevent_log_info + offset; @@ -1850,8 +1854,9 @@ static void json_pel_vs_event_data(struct json_object *valid_attrs, void *vsed, 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) +void nvme_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; @@ -1925,48 +1930,49 @@ static void json_pevent_entry(void *pevent_log_info, __u8 action, __u32 size, co switch (pevent_entry_head->etype) { case NVME_PEL_SMART_HEALTH_EVENT: - json_pel_smart_health(pevent_log_info, offset, valid_attrs); + nvme_json_pel_smart_health(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_FW_COMMIT_EVENT: - json_pel_fw_commit(pevent_log_info, offset, valid_attrs); + nvme_json_pel_fw_commit(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_TIMESTAMP_EVENT: - json_pel_timestamp(pevent_log_info, offset, valid_attrs); + nvme_json_pel_timestamp(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_POWER_ON_RESET_EVENT: - json_pel_power_on_reset(pevent_log_info, offset, valid_attrs, - pevent_entry_head->vsil, pevent_entry_head->el); + nvme_json_pel_power_on_reset(pevent_log_info, offset, valid_attrs, + pevent_entry_head->vsil, + pevent_entry_head->el); break; case NVME_PEL_NSS_HW_ERROR_EVENT: - json_pel_nss_hw_error(pevent_log_info, offset, valid_attrs); + nvme_json_pel_nss_hw_error(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_CHANGE_NS_EVENT: - json_pel_change_ns(pevent_log_info, offset, valid_attrs); + nvme_json_pel_change_ns(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_FORMAT_START_EVENT: - json_pel_format_start(pevent_log_info, offset, valid_attrs); + nvme_json_pel_format_start(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_FORMAT_COMPLETION_EVENT: - json_pel_format_completion(pevent_log_info, offset, valid_attrs); + nvme_json_pel_format_completion(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_SANITIZE_START_EVENT: - json_pel_sanitize_start(pevent_log_info, offset, valid_attrs); + nvme_json_pel_sanitize_start(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_SANITIZE_COMPLETION_EVENT: - json_pel_sanitize_completion(pevent_log_info, offset, valid_attrs); + nvme_json_pel_sanitize_completion(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_SET_FEATURE_EVENT: - json_pel_set_feature(pevent_log_info, offset, valid_attrs); + nvme_json_pel_set_feature(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_TELEMETRY_CRT: - json_pel_telemetry_crt(pevent_log_info, offset, valid_attrs); + nvme_json_pel_telemetry_crt(pevent_log_info, offset, valid_attrs); break; case NVME_PEL_THERMAL_EXCURSION_EVENT: - json_pel_thermal_excursion(pevent_log_info, offset, valid_attrs); + nvme_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); + nvme_json_pel_vendor_specific_event(pevent_log_info, offset, el - vsil, + valid_attrs); break; default: break; @@ -1985,7 +1991,7 @@ static void json_persistent_event_log(void *pevent_log_info, __u8 action, __u32 offset = sizeof(struct nvme_persistent_event_log); if (size >= offset) { - json_pevent_log_head(pevent_log_info, r); + nvme_json_pevent_log_head(pevent_log_info, r); json_pevent_entry(pevent_log_info, action, size, devname, offset, valid); obj_add_array(r, "list_of_event_entries", valid); } else { diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index a920f79b39..94dd089c55 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -309,8 +309,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) +void nvme_show_pel_event_header(int i, struct nvme_persistent_event_entry *pevent_entry_head, + int human) { __u16 vsil = le16_to_cpu(pevent_entry_head->vsil); @@ -335,7 +335,8 @@ static void pel_event_header(int i, struct nvme_persistent_event_entry *pevent_e } } -static void pel_smart_health_event(void *pevent_log_info, __u32 offset, const char *devname) +void nvme_show_pel_smart_health_event(void *pevent_log_info, __u32 offset, + const char *devname) { struct nvme_smart_log *smart_event = pevent_log_info + offset; @@ -343,7 +344,7 @@ static void pel_smart_health_event(void *pevent_log_info, __u32 offset, const ch stdout_smart_log(smart_event, NVME_NSID_ALL, devname); } -static void pel_fw_commit_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_fw_commit_event(void *pevent_log_info, __u32 offset) { struct nvme_fw_commit_event *fw_commit_event = pevent_log_info + offset; @@ -360,7 +361,7 @@ static void pel_fw_commit_event(void *pevent_log_info, __u32 offset) le16_to_cpu(fw_commit_event->vndr_assign_fw_commit_rc)); } -static void pel_timestamp_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_timestamp_event(void *pevent_log_info, __u32 offset) { struct nvme_time_stamp_change_event *ts_change_event = pevent_log_info + offset; @@ -370,8 +371,8 @@ static void pel_timestamp_event(void *pevent_log_info, __u32 offset) le64_to_cpu(ts_change_event->ml_secs_since_reset)); } -static void pel_power_on_reset_event(void *pevent_log_info, __u32 offset, - struct nvme_persistent_event_entry *pevent_entry_head) +void nvme_show_pel_power_on_reset_event(void *pevent_log_info, __u32 offset, + struct nvme_persistent_event_entry *pevent_entry_head) { __u64 *fw_rev; __u32 por_info_len = le16_to_cpu(pevent_entry_head->el) - @@ -398,7 +399,7 @@ static void pel_power_on_reset_event(void *pevent_log_info, __u32 offset, } } -static void pel_nss_hw_error_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_nss_hw_error_event(void *pevent_log_info, __u32 offset) { struct nvme_nss_hw_err_event *nss_hw_err_event = pevent_log_info + offset; @@ -407,7 +408,7 @@ static void pel_nss_hw_error_event(void *pevent_log_info, __u32 offset) nvme_nss_hw_error_to_string(nss_hw_err_event->nss_hw_err_event_code)); } -static void pel_change_ns_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_change_ns_event(void *pevent_log_info, __u32 offset) { struct nvme_change_ns_event *ns_event = pevent_log_info + offset; @@ -423,7 +424,7 @@ static void pel_change_ns_event(void *pevent_log_info, __u32 offset) printf("Namespace ID: %u\n", le32_to_cpu(ns_event->nsid)); } -static void pel_format_start_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_format_start_event(void *pevent_log_info, __u32 offset) { struct nvme_format_nvm_start_event *format_start_event = pevent_log_info + offset; @@ -433,7 +434,7 @@ static void pel_format_start_event(void *pevent_log_info, __u32 offset) printf("Format NVM CDW10: %u\n", le32_to_cpu(format_start_event->format_nvm_cdw10)); } -static void pel_format_completion_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_format_completion_event(void *pevent_log_info, __u32 offset) { struct nvme_format_nvm_compln_event *format_cmpln_event = pevent_log_info + offset; @@ -445,7 +446,7 @@ static void pel_format_completion_event(void *pevent_log_info, __u32 offset) printf("Status Field: %u\n", le32_to_cpu(format_cmpln_event->status_field)); } -static void pel_sanitize_start_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_sanitize_start_event(void *pevent_log_info, __u32 offset) { struct nvme_sanitize_start_event *sanitize_start_event = pevent_log_info + offset; @@ -455,7 +456,7 @@ static void pel_sanitize_start_event(void *pevent_log_info, __u32 offset) printf("Sanitize CDW11: %u\n", le32_to_cpu(sanitize_start_event->sani_cdw11)); } -static void pel_sanitize_completion_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_sanitize_completion_event(void *pevent_log_info, __u32 offset) { struct nvme_sanitize_compln_event *sanitize_cmpln_event = pevent_log_info + offset; @@ -465,7 +466,7 @@ static void pel_sanitize_completion_event(void *pevent_log_info, __u32 offset) printf("Completion Information: %u\n", le16_to_cpu(sanitize_cmpln_event->cmpln_info)); } -static void pel_set_feature_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_set_feature_event(void *pevent_log_info, __u32 offset) { int fid, cdw11, cdw12, dword_cnt; unsigned char *mem_buf; @@ -491,7 +492,7 @@ static void pel_set_feature_event(void *pevent_log_info, __u32 offset) } } -static void pel_thermal_excursion_event(void *pevent_log_info, __u32 offset) +void nvme_show_pel_thermal_excursion_event(void *pevent_log_info, __u32 offset) { struct nvme_thermal_exc_event *thermal_exc_event = pevent_log_info + offset; @@ -525,8 +526,8 @@ static void pel_vs_event_data(void *vsed, __u8 vsedt, __u16 vsedl) } } -static void pel_vendor_specific_event(void *pevent_log_info, __u32 offset, - __u32 event_data_len) +void nvme_show_pel_vendor_specific_event(void *pevent_log_info, __u32 offset, + __u32 event_data_len) { __u32 progress = 0; __u16 vsedl; @@ -586,55 +587,66 @@ static void stdout_persistent_event_log(void *pevent_log_info, __u8 action, __u3 if ((offset + pevent_entry_head->ehl + 3 + el) >= size) break; - pel_event_header(i, pevent_entry_head, human); + nvme_show_pel_event_header(i, pevent_entry_head, human); offset += pevent_entry_head->ehl + vsil + 3; switch (pevent_entry_head->etype) { case NVME_PEL_SMART_HEALTH_EVENT: - pel_smart_health_event(pevent_log_info, offset, devname); + nvme_show_pel_smart_health_event(pevent_log_info, + offset, devname); break; case NVME_PEL_FW_COMMIT_EVENT: - pel_fw_commit_event(pevent_log_info, offset); + nvme_show_pel_fw_commit_event(pevent_log_info, offset); break; case NVME_PEL_TIMESTAMP_EVENT: - pel_timestamp_event(pevent_log_info, offset); + nvme_show_pel_timestamp_event(pevent_log_info, offset); break; case NVME_PEL_POWER_ON_RESET_EVENT: - pel_power_on_reset_event(pevent_log_info, offset, pevent_entry_head); + nvme_show_pel_power_on_reset_event(pevent_log_info, + offset, + pevent_entry_head); break; case NVME_PEL_NSS_HW_ERROR_EVENT: - pel_nss_hw_error_event(pevent_log_info, offset); + nvme_show_pel_nss_hw_error_event(pevent_log_info, + offset); break; case NVME_PEL_CHANGE_NS_EVENT: - pel_change_ns_event(pevent_log_info, offset); + nvme_show_pel_change_ns_event(pevent_log_info, offset); break; case NVME_PEL_FORMAT_START_EVENT: - pel_format_start_event(pevent_log_info, offset); + nvme_show_pel_format_start_event(pevent_log_info, + offset); break; case NVME_PEL_FORMAT_COMPLETION_EVENT: - pel_format_completion_event(pevent_log_info, offset); + nvme_show_pel_format_completion_event(pevent_log_info, + offset); break; case NVME_PEL_SANITIZE_START_EVENT: - pel_sanitize_start_event(pevent_log_info, offset); + nvme_show_pel_sanitize_start_event(pevent_log_info, + offset); break; case NVME_PEL_SANITIZE_COMPLETION_EVENT: - pel_sanitize_completion_event(pevent_log_info, offset); + nvme_show_pel_sanitize_completion_event(pevent_log_info, + offset); break; case NVME_PEL_SET_FEATURE_EVENT: - pel_set_feature_event(pevent_log_info, offset); + nvme_show_pel_set_feature_event(pevent_log_info, + offset); break; case NVME_PEL_TELEMETRY_CRT: d(pevent_log_info + offset, 512, 16, 1); break; case NVME_PEL_THERMAL_EXCURSION_EVENT: - pel_thermal_excursion_event(pevent_log_info, offset); + nvme_show_pel_thermal_excursion_event(pevent_log_info, + offset); break; 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); + nvme_show_pel_vendor_specific_event(pevent_log_info, + offset, el - vsil); break; default: printf("Reserved Event\n\n"); diff --git a/nvme-print.h b/nvme-print.h index 0f371d8502..45413e5ec5 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -375,6 +375,56 @@ void nvme_show_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log, nvme_print_flags_t flags); void nvme_show_log(const char *devname, struct nvme_get_log_args *args, nvme_print_flags_t flags); void nvme_show_pel_header(struct nvme_persistent_event_log *pevent_log_head, int human); +void nvme_show_pel_event_header(int i, struct nvme_persistent_event_entry *pevent_entry_head, + int human); +void nvme_show_pel_smart_health_event(void *pevent_log_info, __u32 offset, + const char *devname); +void nvme_show_pel_fw_commit_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_timestamp_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_power_on_reset_event(void *pevent_log_info, __u32 offset, + struct nvme_persistent_event_entry *pevent_entry_head); +void nvme_show_pel_nss_hw_error_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_change_ns_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_format_start_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_format_completion_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_sanitize_start_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_sanitize_completion_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_set_feature_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_thermal_excursion_event(void *pevent_log_info, __u32 offset); +void nvme_show_pel_vendor_specific_event(void *pevent_log_info, __u32 offset, + __u32 event_data_len); +void nvme_json_pevent_log_head(struct nvme_persistent_event_log *pevent_log_head, + struct json_object *r); +void nvme_json_pel_smart_health(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_fw_commit(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_timestamp(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_power_on_reset(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs, + __le16 vsil, __le16 el); +void nvme_json_pel_nss_hw_error(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_change_ns(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_format_start(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_format_completion(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_sanitize_start(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_sanitize_completion(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_set_feature(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_telemetry_crt(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_thermal_excursion(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs); +void nvme_json_pel_vendor_specific_event(void *pevent_log_info, __u32 offset, + __u32 event_data_len, + struct json_object *valid_attrs); extern char *alloc_error; #endif /* NVME_PRINT_H */ From f08f10cbef5279f1c7c71eedc9f048ddd040f209 Mon Sep 17 00:00:00 2001 From: Brandon Paupore Date: Fri, 30 Jan 2026 17:43:59 -0600 Subject: [PATCH 2/2] ocp: add Persistent Event log support This introduces a new OCP plugin command that mirrors the base nvme persistent-event-log command, but has additional parsing for the OCP 2.5 TCG Activity Event data. Signed-off-by: Brandon Paupore Reviewed-by: Jeffrey Lien --- .../nvme-ocp-persistent-event-log.txt | 79 ++++++ plugins/ocp/ocp-nvme.c | 123 +++++++++ plugins/ocp/ocp-nvme.h | 26 ++ plugins/ocp/ocp-print-binary.c | 7 + plugins/ocp/ocp-print-json.c | 242 ++++++++++++++++++ plugins/ocp/ocp-print-stdout.c | 148 +++++++++++ plugins/ocp/ocp-print.c | 8 + plugins/ocp/ocp-print.h | 4 + plugins/ocp/ocp-utils.c | 13 + plugins/ocp/ocp-utils.h | 11 + 10 files changed, 661 insertions(+) create mode 100644 Documentation/nvme-ocp-persistent-event-log.txt diff --git a/Documentation/nvme-ocp-persistent-event-log.txt b/Documentation/nvme-ocp-persistent-event-log.txt new file mode 100644 index 0000000000..4cb1f42ddb --- /dev/null +++ b/Documentation/nvme-ocp-persistent-event-log.txt @@ -0,0 +1,79 @@ +nvme-ocp-persistent-event-log(1) +================================ + +NAME +---- +nvme-ocp-persistent-event-log - Send NVMe persistent event log page request, +returns result and log additional OCP-specific data + +SYNOPSIS +-------- +[verse] +'nvme ocp persistent-event-log' [--action= | -a ] + [--log-len= | -l ] [--raw-binary | -b] + [--output-format= | -o ] [--verbose | -v] + +DESCRIPTION +----------- +Retrieves the NVMe persistent event log page from an NVMe device +and provides the returned structure. + +The parameter is mandatory and may be either the NVMe character +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). + +On success, the returned persistent event log structure may be returned +in one of several ways depending on the option flags; the structure may +parsed by the program and printed in a readable format or the raw buffer +may be printed to stdout for another program to parse. + +OPTIONS +------- +-a :: +--action=:: + While try to retrieve this log action the controller shall take + during processing this persistent log page command. This mandatory + field, based on the value issued it may Read Log Data, Establish + Context and Read Log Data or Release Context can occur. For More + details see NVM Express 1.4 Spec. Section 5.14.1.13 Persistent + Event Log (Log Identifier 0Dh) + +-l :: +--log-len=:: + Allocates a buffer of bytes size and requests this + many bytes be returned in the constructed NVMe command. This + param is mandatory. If given is 0 and action is 0, + it will read the Total Log Length(TLL) of the page. + +-b:: +--raw-binary:: + Print the raw persistent event log buffer to stdout. + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json' or 'binary'. Only one + output format can be used at a time. + +-v:: +--verbose:: + Increase the information detail in the output. + +EXAMPLES +-------- +* Print the persistent event log page in a human readable format: ++ +------------ +# nvme ocp persistent-event-log /dev/nvme0 +------------ ++ + +* Print the raw persistent event log to a file: ++ +------------ +# nvme ocp persistent-event-log /dev/nvme0 --raw-binary > persistent_log.raw +------------ ++ +It is probably a bad idea to not redirect stdout when using this mode. + +NVME +---- +Part of the nvme-user suite diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index d7efc2cdb0..288293d070 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -195,6 +195,7 @@ static const char *type = "Error injection type"; static const char *nrtdp = "Number of reads to trigger device panic"; static const char *save = "Specifies that the controller shall save the attribute"; static const char *enable_ieee1667_silo = "enable IEEE1667 silo"; +static const char *raw_use = "use binary output"; static int get_c3_log_page(struct nvme_transport_handle *hdl, char *format) { @@ -2974,3 +2975,125 @@ static int hwcomp_log(int argc, char **argv, struct command *acmd, struct plugin { return ocp_hwcomp_log(argc, argv, acmd, plugin); } + +static int ocp_get_persistent_event_log(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + const char *desc = "Retrieve Persistent Event log info for the given " \ + "device in either decoded format(default), json or binary."; + const char *action = "action the controller shall take during " \ + "processing this persistent log page command."; + const char *log_len = "number of bytes to retrieve"; + + _cleanup_free_ struct nvme_persistent_event_log *pevent = NULL; + struct nvme_persistent_event_log *pevent_collected = NULL; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; + _cleanup_nvme_global_ctx_ struct nvme_global_ctx *ctx = NULL; + _cleanup_nvme_transport_handle_ struct nvme_transport_handle *hdl = NULL; + + nvme_print_flags_t flags; + void *pevent_log_info; + int err; + + struct config { + __u8 action; + __u32 log_len; + bool raw_binary; + }; + + struct config cfg = { + .action = 0xff, + .log_len = 0, + .raw_binary = false, + }; + + NVME_ARGS(opts, + OPT_BYTE("action", 'a', &cfg.action, action), + OPT_UINT("log_len", 'l', &cfg.log_len, log_len), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_use)); + + err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); + if (err) + return err; + + err = validate_output_format(nvme_cfg.output_format, &flags); + if (err < 0) { + nvme_show_error("Invalid output format"); + return err; + } + + if (cfg.raw_binary) + flags = BINARY; + + pevent = nvme_alloc(sizeof(*pevent)); + if (!pevent) + return -ENOMEM; + + err = nvme_get_log_persistent_event(hdl, cfg.action, + pevent, sizeof(*pevent)); + if (err < 0) { + nvme_show_error("persistent event log: %s", nvme_strerror(err)); + return err; + } else if (err) { + nvme_show_status(err); + return err; + } + + if (cfg.action == NVME_PEVENT_LOG_RELEASE_CTX) { + printf("Releasing Persistent Event Log Context\n"); + return 0; + } + + if (!cfg.log_len && cfg.action != NVME_PEVENT_LOG_EST_CTX_AND_READ) { + cfg.log_len = le64_to_cpu(pevent->tll); + } else if (!cfg.log_len && cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) { + printf("Establishing Persistent Event Log Context\n"); + return 0; + } + + /* + * if header already read with context establish action 0x1, + * action shall not be 0x1 again in the subsequent request, + * until the current context is released by issuing action + * with 0x2, otherwise throws command sequence error, make + * it as zero to read the log page + */ + if (cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) + cfg.action = NVME_PEVENT_LOG_READ; + + pevent_log_info = nvme_alloc_huge(cfg.log_len, &mh); + if (!pevent_log_info) { + nvme_show_error("failed to allocate huge memory"); + return -ENOMEM; + } + + err = nvme_get_log_persistent_event(hdl, cfg.action, + pevent_log_info, cfg.log_len); + if (!err) { + err = nvme_get_log_persistent_event(hdl, cfg.action, + pevent, + sizeof(*pevent)); + if (err < 0) { + nvme_show_error("persistent event log: %s", nvme_strerror(err)); + return err; + } else if (err) { + nvme_show_status(err); + return err; + } + pevent_collected = pevent_log_info; + if (pevent_collected->gen_number != pevent->gen_number) { + printf("Collected Persistent Event Log may be invalid,\n" + "Re-read the log is required\n"); + return -EINVAL; + } + + ocp_show_persistent_event_log(pevent_log_info, cfg.action, + cfg.log_len, nvme_transport_handle_get_name(hdl), flags); + } else if (err > 0) { + nvme_show_status(err); + } else { + nvme_show_error("persistent event log: %s", nvme_strerror(err)); + } + + return err; +} diff --git a/plugins/ocp/ocp-nvme.h b/plugins/ocp/ocp-nvme.h index 6a447b049e..1f38353965 100644 --- a/plugins/ocp/ocp-nvme.h +++ b/plugins/ocp/ocp-nvme.h @@ -48,6 +48,8 @@ PLUGIN(NAME("ocp", "OCP cloud SSD extensions", OCP_PLUGIN_VERSION), get_clear_pcie_correctable_error_counters) ENTRY("get-telemetry-profile", "Get Telemetry Profile Feature", ocp_get_telemetry_profile_feature) + ENTRY("persistent-event-log", "Retrieve Persistent Event Log with OCP events", + ocp_get_persistent_event_log) ) ); @@ -280,4 +282,28 @@ enum ocp_dssd_feature_id { OCP_FID_TEL_CFG, /* Telemetry Profile */ OCP_FID_DAEC, /* DSSD Asynchronous Event Configuration */ }; + +/* + * struct tcg_activity_event_data - TCG Activity Event Data + * @rsvd: Reserved + * @tcg_command_count: TCG Command Count + * @invoking_id: Invoking ID + * @method_id: Method ID + * @com_id: Com ID + * @protocol_id: Protocol ID + * @status: Status of the TCG command + * @process_time: Time in milliseconds to process command + * @tcg_activity_specific_context: Additional context for the TCG activity + */ +struct __packed tcg_activity_event_data { + __u8 rsvd[2]; + __le32 tcg_command_count; + __le64 invoking_id; + __le64 method_id; + __le16 com_id; + __le16 protocol_id; + __u8 status; + __le16 process_time; + __u8 tcg_activity_specific_context[10]; +}; #endif /* OCP_NVME_H */ diff --git a/plugins/ocp/ocp-print-binary.c b/plugins/ocp/ocp-print-binary.c index 50604296bc..d98a062dde 100644 --- a/plugins/ocp/ocp-print-binary.c +++ b/plugins/ocp/ocp-print-binary.c @@ -15,6 +15,12 @@ static void binary_hwcomp_log(struct hwcomp_log *log, __u32 id, bool list) d_raw((unsigned char *)log, log_bytes); } +static void binary_persistent_event_log(void *pevent_log_info, + __u8 action, __u32 size, const char *devname) +{ + d_raw((unsigned char *)pevent_log_info, size); +} + static void binary_c5_log(struct nvme_transport_handle *hdl, struct unsupported_requirement_log *log_data) { d_raw((unsigned char *)log_data, sizeof(*log_data)); @@ -43,6 +49,7 @@ static void binary_c7_log(struct nvme_transport_handle *hdl, struct tcg_configur static struct ocp_print_ops binary_print_ops = { .hwcomp_log = binary_hwcomp_log, + .persistent_event_log = binary_persistent_event_log, .c5_log = binary_c5_log, .c1_log = binary_c1_log, .c4_log = binary_c4_log, diff --git a/plugins/ocp/ocp-print-json.c b/plugins/ocp/ocp-print-json.c index 4a88ff2b23..1d873d9ca8 100644 --- a/plugins/ocp/ocp-print-json.c +++ b/plugins/ocp/ocp-print-json.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include "util/json.h" #include "util/types.h" #include "common.h" #include "nvme-print.h" @@ -8,6 +9,71 @@ #include "ocp-smart-extended-log.h" #include "ocp-telemetry-decode.h" #include "ocp-nvme.h" +#include "ocp-utils.h" + +#define array_add_obj json_array_add_value_object +#define array_add_str json_array_add_value_string + +#define obj_add_array json_object_add_value_array +#define obj_add_int json_object_add_value_int +#define obj_add_obj json_object_add_value_object +#define obj_add_uint json_object_add_value_uint +#define obj_add_uint128 json_object_add_value_uint128 +#define obj_add_uint64 json_object_add_value_uint64 +#define obj_add_str json_object_add_value_string +#define obj_add_uint_02x json_object_add_uint_02x +#define obj_add_uint_0x json_object_add_uint_0x +#define obj_add_byte_array json_object_add_byte_array +#define obj_add_nprix64 json_object_add_nprix64 +#define obj_add_uint_0nx json_object_add_uint_0nx +#define obj_add_0nprix64 json_object_add_0nprix64 +#define obj_add_string json_object_add_string + +static void d_json(unsigned char *buf, int len, int width, int group, struct json_object *array) +{ + int i; + char ascii[32 + 1] = { 0 }; + + assert(width < sizeof(ascii)); + + for (i = 0; i < len; i++) { + ascii[i % width] = (buf[i] >= '!' && buf[i] <= '~') ? buf[i] : '.'; + if (!((i + 1) % width)) { + array_add_str(array, ascii); + memset(ascii, 0, sizeof(ascii)); + } + } + + if (strlen(ascii)) { + ascii[i % width + 1] = '\0'; + array_add_str(array, ascii); + } +} + +static void obj_d(struct json_object *o, const char *k, unsigned char *buf, int len, int width, + int group) +{ + struct json_object *data = json_create_array(); + + d_json(buf, len, width, group, data); + obj_add_array(o, k, data); +} + +static void obj_add_result(struct json_object *o, const char *v, ...) +{ + va_list ap; + + _cleanup_free_ char *value = NULL; + + va_start(ap, v); + + if (vasprintf(&value, v, ap) < 0) + value = alloc_error; + + obj_add_str(o, "Result", value); + + va_end(ap); +} static void print_hwcomp_desc_json(struct hwcomp_desc_entry *e, struct json_object *r) { @@ -1119,9 +1185,185 @@ static void json_c7_log(struct nvme_transport_handle *hdl, struct tcg_configurat json_free_object(root); } +static void pel_tcg_activity_event(void *pevent_log_info, __u32 offset, + struct json_object *valid_attrs) +{ + __u16 vsedl; + struct nvme_vs_event_desc *vs_desc; + struct tcg_activity_event_data *tcg_event_data; + struct json_object *tcg_event_obj = json_create_object(); + + vs_desc = pevent_log_info + offset; + tcg_event_data = (struct tcg_activity_event_data *)(vs_desc + 1); + vsedl = le16_to_cpu(vs_desc->vsedl); + + /* Only one descriptor (0) defined for the TCG Activity Event */ + json_object_add_value_uint(tcg_event_obj, "vendor_specific_event_descriptor", + 0); + json_object_add_value_uint(tcg_event_obj, "vendor_specific_event_code", + le16_to_cpu(vs_desc->vsec)); + json_object_add_value_uint(tcg_event_obj, "vendor_specific_event_data_type", + vs_desc->vsedt); + json_object_add_value_uint(tcg_event_obj, "vendor_specific_event_uindex", + vs_desc->uidx); + json_object_add_value_uint(tcg_event_obj, "vendor_specific_event_data_length", + vsedl); + json_object_add_value_uint(tcg_event_obj, "tcg_command_count", + le32_to_cpu(tcg_event_data->tcg_command_count)); + json_object_add_value_uint64(tcg_event_obj, "invoking_id", + le64_to_cpu(tcg_event_data->invoking_id)); + json_object_add_value_uint64(tcg_event_obj, "method_id", + le64_to_cpu(tcg_event_data->method_id)); + json_object_add_value_uint(tcg_event_obj, "com_id", + le16_to_cpu(tcg_event_data->com_id)); + json_object_add_value_uint(tcg_event_obj, "protocol_id", + tcg_event_data->protocol_id); + json_object_add_value_uint(tcg_event_obj, "status", + tcg_event_data->status); + json_object_add_value_uint(tcg_event_obj, "process_time", + le16_to_cpu(tcg_event_data->process_time)); + obj_d(tcg_event_obj, "tcg_activity_specific_context", + (void *)tcg_event_data->tcg_activity_specific_context, 10, 16, 1); + + json_object_add_value_object(valid_attrs, "tcg_activity_event_data", + tcg_event_obj); +} + +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; + + for (i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { + if (offset + sizeof(*pevent_entry_head) >= size) + 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 + el >= + size) + break; + + valid_attrs = json_create_object(); + + obj_add_uint(valid_attrs, "event_number", i); + obj_add_str(valid_attrs, "event_type", + nvme_pel_event_to_string(pevent_entry_head->etype)); + obj_add_uint(valid_attrs, "event_type_rev", pevent_entry_head->etype_rev); + obj_add_uint(valid_attrs, "event_header_len", pevent_entry_head->ehl); + obj_add_uint(valid_attrs, "event_header_additional_info", pevent_entry_head->ehai); + obj_add_uint(valid_attrs, "ctrl_id", le16_to_cpu(pevent_entry_head->cntlid)); + 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", 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 + vsil + 3; + + switch (pevent_entry_head->etype) { + case NVME_PEL_SMART_HEALTH_EVENT: + nvme_json_pel_smart_health(pevent_log_info, offset, + valid_attrs); + break; + case NVME_PEL_FW_COMMIT_EVENT: + nvme_json_pel_fw_commit(pevent_log_info, offset, + valid_attrs); + break; + case NVME_PEL_TIMESTAMP_EVENT: + nvme_json_pel_timestamp(pevent_log_info, offset, + valid_attrs); + break; + case NVME_PEL_POWER_ON_RESET_EVENT: + nvme_json_pel_power_on_reset(pevent_log_info, offset, + valid_attrs, + pevent_entry_head->vsil, + pevent_entry_head->el); + break; + case NVME_PEL_NSS_HW_ERROR_EVENT: + nvme_json_pel_nss_hw_error(pevent_log_info, offset, + valid_attrs); + break; + case NVME_PEL_CHANGE_NS_EVENT: + nvme_json_pel_change_ns(pevent_log_info, offset, valid_attrs); + break; + case NVME_PEL_FORMAT_START_EVENT: + nvme_json_pel_format_start(pevent_log_info, offset, valid_attrs); + break; + case NVME_PEL_FORMAT_COMPLETION_EVENT: + nvme_json_pel_format_completion(pevent_log_info, + offset, valid_attrs); + break; + case NVME_PEL_SANITIZE_START_EVENT: + nvme_json_pel_sanitize_start(pevent_log_info, offset, valid_attrs); + break; + case NVME_PEL_SANITIZE_COMPLETION_EVENT: + nvme_json_pel_sanitize_completion(pevent_log_info, + offset, valid_attrs); + break; + case NVME_PEL_SET_FEATURE_EVENT: + nvme_json_pel_set_feature(pevent_log_info, offset, valid_attrs); + break; + case NVME_PEL_TELEMETRY_CRT: + nvme_json_pel_telemetry_crt(pevent_log_info, offset, valid_attrs); + break; + case NVME_PEL_THERMAL_EXCURSION_EVENT: + nvme_json_pel_thermal_excursion(pevent_log_info, + offset, valid_attrs); + break; + case NVME_PEL_VENDOR_SPECIFIC_EVENT: + if (ocp_is_tcg_activity_event(pevent_entry_head, el, + vsil)) + pel_tcg_activity_event(pevent_log_info, offset, + valid_attrs); + else + nvme_json_pel_vendor_specific_event(pevent_log_info, + offset, el - vsil, + valid_attrs); + break; + default: + break; + } + + array_add_obj(valid, valid_attrs); + offset += le16_to_cpu(pevent_entry_head->el); + } +} + +static void json_persistent_event_log(void *pevent_log_info, __u8 action, + __u32 size, const char *devname) +{ + struct json_object *r = json_create_object(); + struct json_object *valid = json_create_array(); + __u32 offset = sizeof(struct nvme_persistent_event_log); + + if (size >= offset) { + nvme_json_pevent_log_head(pevent_log_info, r); + json_pevent_entry(pevent_log_info, action, size, devname, offset, valid); + obj_add_array(r, "list_of_event_entries", valid); + } else { + obj_add_result(r, "No log data can be shown with this log len at least " \ + "512 bytes is required or can be 0 to read the complete " \ + "log page after context established"); + } + + json_print(r); +} + static struct ocp_print_ops json_print_ops = { .hwcomp_log = json_hwcomp_log, .fw_act_history = json_fw_activation_history, + .persistent_event_log = json_persistent_event_log, .smart_extended_log = json_smart_extended_log, .telemetry_log = json_telemetry_log, .c3_log = json_c3_log, diff --git a/plugins/ocp/ocp-print-stdout.c b/plugins/ocp/ocp-print-stdout.c index 185b99a3ac..8110f17670 100644 --- a/plugins/ocp/ocp-print-stdout.c +++ b/plugins/ocp/ocp-print-stdout.c @@ -8,6 +8,9 @@ #include "ocp-smart-extended-log.h" #include "ocp-telemetry-decode.h" #include "ocp-nvme.h" +#include "ocp-utils.h" + +static struct ocp_print_ops stdout_print_ops; static void print_hwcomp_desc(struct hwcomp_desc_entry *e, bool list, int num) { @@ -765,9 +768,154 @@ static void stdout_c7_log(struct nvme_transport_handle *hdl, struct tcg_configur printf("\n"); } +static void pel_tcg_activity_event(void *pevent_log_info, __u32 offset) +{ + __u16 vsedl; + struct nvme_vs_event_desc *vs_desc; + struct tcg_activity_event_data *tcg_event_data; + + printf("TCG Activity Event Entry:\n"); + vs_desc = pevent_log_info + offset; + tcg_event_data = (struct tcg_activity_event_data *)(vs_desc + 1); + vsedl = le16_to_cpu(vs_desc->vsedl); + + printf("Vendor Specific Event Descriptor 0:\n"); + 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); + printf("TCG Activity Event Data:\n"); + printf("TCG Command Count: %u\n", + le32_to_cpu(tcg_event_data->tcg_command_count)); + printf("Invoking ID: %" PRIu64 "\n", + le64_to_cpu(tcg_event_data->invoking_id)); + printf("Method ID: %" PRIu64 "\n", + le64_to_cpu(tcg_event_data->method_id)); + printf("Com ID: %u\n", le16_to_cpu(tcg_event_data->com_id)); + printf("Protocol ID: %u\n", tcg_event_data->protocol_id); + printf("Status: %u\n", tcg_event_data->status); + printf("Process Time: %u\n", + le16_to_cpu(tcg_event_data->process_time)); + printf("TCG Activity Specific Context:\n"); + d((void *)tcg_event_data->tcg_activity_specific_context, 10, 16, 1); +} + +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; + + printf("Persistent Event Log for device: %s\n", devname); + printf("Action for Persistent Event Log: %u\n", action); + + if (size < offset) { + printf("No log data can be shown with this log len at least " \ + "512 bytes is required or can be 0 to read the complete " \ + "log page after context established\n"); + return; + } + + pevent_log_head = pevent_log_info; + + nvme_show_pel_header(pevent_log_head, human); + + printf("\n"); + printf("\nPersistent Event Entries:\n"); + for (int i = 0; i < le32_to_cpu(pevent_log_head->tnev); i++) { + if (offset + sizeof(*pevent_entry_head) >= size) + 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 + el) >= size) + break; + + nvme_show_pel_event_header(i, pevent_entry_head, human); + + offset += pevent_entry_head->ehl + vsil + 3; + + switch (pevent_entry_head->etype) { + case NVME_PEL_SMART_HEALTH_EVENT: + nvme_show_pel_smart_health_event(pevent_log_info, + offset, devname); + break; + case NVME_PEL_FW_COMMIT_EVENT: + nvme_show_pel_fw_commit_event(pevent_log_info, offset); + break; + case NVME_PEL_TIMESTAMP_EVENT: + nvme_show_pel_timestamp_event(pevent_log_info, offset); + break; + case NVME_PEL_POWER_ON_RESET_EVENT: + nvme_show_pel_power_on_reset_event(pevent_log_info, + offset, + pevent_entry_head); + break; + case NVME_PEL_NSS_HW_ERROR_EVENT: + nvme_show_pel_nss_hw_error_event(pevent_log_info, + offset); + break; + case NVME_PEL_CHANGE_NS_EVENT: + nvme_show_pel_change_ns_event(pevent_log_info, offset); + break; + case NVME_PEL_FORMAT_START_EVENT: + nvme_show_pel_format_start_event(pevent_log_info, + offset); + break; + case NVME_PEL_FORMAT_COMPLETION_EVENT: + nvme_show_pel_format_completion_event(pevent_log_info, + offset); + break; + case NVME_PEL_SANITIZE_START_EVENT: + nvme_show_pel_sanitize_start_event(pevent_log_info, + offset); + break; + case NVME_PEL_SANITIZE_COMPLETION_EVENT: + nvme_show_pel_sanitize_completion_event(pevent_log_info, + offset); + break; + case NVME_PEL_SET_FEATURE_EVENT: + nvme_show_pel_set_feature_event(pevent_log_info, + offset); + break; + case NVME_PEL_TELEMETRY_CRT: + d(pevent_log_info + offset, 512, 16, 1); + break; + case NVME_PEL_THERMAL_EXCURSION_EVENT: + nvme_show_pel_thermal_excursion_event(pevent_log_info, + offset); + break; + case NVME_PEL_SANITIZE_MEDIA_VERIF_EVENT: + printf("Sanitize Media Verification Event\n"); + break; + case NVME_PEL_VENDOR_SPECIFIC_EVENT: + if (ocp_is_tcg_activity_event(pevent_entry_head, el, + vsil)) + pel_tcg_activity_event(pevent_log_info, + offset); + else + nvme_show_pel_vendor_specific_event(pevent_log_info, + offset, + el - vsil); + break; + default: + printf("Reserved Event\n\n"); + break; + } + offset += el; + printf("\n"); + } +} + static struct ocp_print_ops stdout_print_ops = { .hwcomp_log = stdout_hwcomp_log, .fw_act_history = stdout_fw_activation_history, + .persistent_event_log = stdout_persistent_event_log, .smart_extended_log = stdout_smart_extended_log, .telemetry_log = stdout_telemetry_log, .c3_log = stdout_c3_log, diff --git a/plugins/ocp/ocp-print.c b/plugins/ocp/ocp-print.c index 43acf729c2..0dc5ba59d0 100644 --- a/plugins/ocp/ocp-print.c +++ b/plugins/ocp/ocp-print.c @@ -36,6 +36,14 @@ void ocp_fw_act_history(const struct fw_activation_history *fw_history, nvme_pri ocp_print(fw_act_history, flags, fw_history); } +void ocp_show_persistent_event_log(void *pevent_log_info, + __u8 action, __u32 size, const char *devname, + nvme_print_flags_t flags) +{ + ocp_print(persistent_event_log, flags, + pevent_log_info, action, size, devname); +} + void ocp_smart_extended_log(struct ocp_smart_extended_log *log, unsigned int version, nvme_print_flags_t flags) { diff --git a/plugins/ocp/ocp-print.h b/plugins/ocp/ocp-print.h index 35b9e10c1a..a1a72aa34f 100644 --- a/plugins/ocp/ocp-print.h +++ b/plugins/ocp/ocp-print.h @@ -11,6 +11,7 @@ struct ocp_print_ops { void (*hwcomp_log)(struct hwcomp_log *log, __u32 id, bool list); void (*fw_act_history)(const struct fw_activation_history *fw_history); + void (*persistent_event_log)(void *pevent_log_info, __u8 action, __u32 size, const char *devname); void (*smart_extended_log)(struct ocp_smart_extended_log *log, unsigned int version); void (*telemetry_log)(struct ocp_telemetry_parse_options *options); void (*c3_log)(struct nvme_transport_handle *hdl, struct ssd_latency_monitor_log *log_data); @@ -37,6 +38,9 @@ static inline struct ocp_print_ops *ocp_get_json_print_ops(nvme_print_flags_t fl void ocp_show_hwcomp_log(struct hwcomp_log *log, __u32 id, bool list, nvme_print_flags_t flags); void ocp_fw_act_history(const struct fw_activation_history *fw_history, nvme_print_flags_t flags); +void ocp_show_persistent_event_log(void *pevent_log_info, + __u8 action, __u32 size, const char *devname, + nvme_print_flags_t flags); void ocp_smart_extended_log(struct ocp_smart_extended_log *log, unsigned int version, nvme_print_flags_t flags); void ocp_show_telemetry_log(struct ocp_telemetry_parse_options *options, nvme_print_flags_t flags); diff --git a/plugins/ocp/ocp-utils.c b/plugins/ocp/ocp-utils.c index a8c09992bc..c9fad63be7 100644 --- a/plugins/ocp/ocp-utils.c +++ b/plugins/ocp/ocp-utils.c @@ -58,3 +58,16 @@ int ocp_get_log_simple(struct nvme_transport_handle *hdl, return nvme_get_log(hdl, &cmd, false, NVME_LOG_PAGE_PDU_SIZE); } + +bool ocp_is_tcg_activity_event(struct nvme_persistent_event_entry *pevent_entry_head, + __u16 el, __u16 vsil) +{ + struct nvme_vs_event_desc *vs_desc = + (struct nvme_vs_event_desc *)(pevent_entry_head + 1); + + return pevent_entry_head->etype == NVME_PEL_VENDOR_SPECIFIC_EVENT && + pevent_entry_head->ehl == 0x15 && vsil == 0x04 && el == 0x30 && + le16_to_cpu(vs_desc->vsec) == 0x01 && + le16_to_cpu(vs_desc->vsedl) == 0x26 && + vs_desc->vsedt == NVME_PEL_VSEDT_BINARY; +} diff --git a/plugins/ocp/ocp-utils.h b/plugins/ocp/ocp-utils.h index 58eef7b031..422ec64b85 100644 --- a/plugins/ocp/ocp-utils.h +++ b/plugins/ocp/ocp-utils.h @@ -31,3 +31,14 @@ int ocp_get_uuid_index(struct nvme_transport_handle *hdl, __u8 *index); int ocp_find_uuid_index(struct nvme_id_uuid_list *uuid_list, __u8 *index); int ocp_get_log_simple(struct nvme_transport_handle *hdl, enum ocp_dssd_log_id lid, __u32 len, void *log); + +/** + * ocp_is_tcg_activity_event() - Determine if persistent event is TCG activity event + * @pevent_entry_head: persistent event entry head pointer + * @el: Event Length + * @vsil: Vendor Specific Information Length + * + * Return: true if TCG activity event, false otherwise. + */ +bool ocp_is_tcg_activity_event(struct nvme_persistent_event_entry *pevent_entry_head, + __u16 el, __u16 vsil);