diff --git a/libnvme/src/nvme/cmds.h b/libnvme/src/nvme/cmds.h index 6396703d9b..cbd778513a 100644 --- a/libnvme/src/nvme/cmds.h +++ b/libnvme/src/nvme/cmds.h @@ -1520,6 +1520,25 @@ nvme_init_get_log_mgmt_addr_list(struct nvme_passthru_cmd *cmd, log, len); } +/** + * nvme_init_get_log_power_measurement() - Initialize passthru command for + * Power Measurement + * @cmd: Passthru command to use + * @log: User address to store the log page + * @len: The allocated length of the log page + * + * Initializes the passthru command buffer for the Get Log command with + * LID value %NVME_LOG_LID_POWER_MEASUREMENT + */ +static inline void +nvme_init_get_log_power_measurement(struct nvme_passthru_cmd *cmd, + struct nvme_power_meas_log *log, __u32 len) +{ + nvme_init_get_log(cmd, NVME_NSID_ALL, + NVME_LOG_LID_POWER_MEASUREMENT, NVME_CSI_NVM, + log, len); +} + /** * nvme_init_get_log_phy_rx_eom() - Initialize passthru command for * Physical Interface Receiver Eye Opening Measurement diff --git a/libnvme/src/nvme/types.h b/libnvme/src/nvme/types.h index ff478d0f71..cc9652ccda 100644 --- a/libnvme/src/nvme/types.h +++ b/libnvme/src/nvme/types.h @@ -3773,7 +3773,26 @@ enum nvme_err_status_field { * reached. A value of %0h, indicates that this transition * has never occurred or this field is not implemented. * @thm_temp2_total_time: Total Time For Thermal Management Temperature 2 - * @rsvd232: Reserved + * @op_lifetime_energy_consumed: Operational Lifetime Energy Consumed: Contains + * the cumulative operational energy consumed by the NVM + * subsystem in watt-hours calculated from all interval + * power measurements collected from the time of + * manufacture to the point that this log page is read. + * This value is rounded up (e.g., two indicates the + * number of watt-hours consumed is greater than 1 and + * less than or equal to 2). This field shall not wrap + * once the value %FFFFFFFFFFFFFFFFh is reached. A value + * of %0h indicates that the cumulative operational energy + * consumed is not reported. + * @interval_power_measurement: Interval Power Measurement: Contains the average + * of power measurement samples over the most recent one + * second interval at the time of processing the Get Log + * Page command. The power in Watts is equal to the + * Interval Power Measurement Value (bits 15:0) multiplied + * by the scale indicated in the Interval Power Measurement + * Scale field (bits 17:16). A value of %0h indicates that + * the interval power measurement is not reported. + * @rsvd244: Reserved */ struct nvme_smart_log { __u8 critical_warning; @@ -3800,7 +3819,9 @@ struct nvme_smart_log { __le32 thm_temp2_trans_count; __le32 thm_temp1_total_time; __le32 thm_temp2_total_time; - __u8 rsvd232[280]; + __le64 op_lifetime_energy_consumed; + __le32 interval_power_measurement; + __u8 rsvd244[268]; }; /** @@ -4669,6 +4690,23 @@ struct nvme_timestamp { __u8 rsvd; }; +/** + * enum nvme_timestamp_attr - Timestamp Attribute field + * @NVME_TIMESTAMP_ATTR_SYNC_SHIFT: Shift amount to get the timestamp synch + * @NVME_TIMESTAMP_ATTR_TO_SHIFT: Shift amount to get the timestamp origin + * @NVME_TIMESTAMP_ATTR_SYNC_MASK: Mask to get the timestamp synch + * @NVME_TIMESTAMP_ATTR_TO_MASK: Mask to get the timestamp origin + */ +enum nvme_timestamp_attr { + NVME_TIMESTAMP_ATTR_SYNC_SHIFT = 0, + NVME_TIMESTAMP_ATTR_TO_SHIFT = 1, + NVME_TIMESTAMP_ATTR_SYNC_MASK = 0x1, + NVME_TIMESTAMP_ATTR_TO_MASK = 0x7, +}; + +#define NVME_TIMESTAMP_ATTR_SYNC(attr) NVME_GET(attr, TIMESTAMP_ATTR_SYNC) +#define NVME_TIMESTAMP_ATTR_TO(attr) NVME_GET(attr, TIMESTAMP_ATTR_TO) + /** * struct nvme_time_stamp_change_event - Timestamp Change Event * @previous_timestamp: Previous Timestamp @@ -6102,6 +6140,113 @@ struct nvme_fdp_ruh_status { struct nvme_fdp_ruh_status_desc ruhss[]; }; +/** + * enum nvme_pma - Power Measurement Attributes (PMA) field + * @NVME_PMA_PME_SHIFT: Shift amount to get the power measurement enable + * @NVME_PMA_NCPDF_SHIFT: Shift amount to get the non-contiguous power data flag + * @NVME_PMA_EPF_SHIFT: Shift amount to get the estimated power flag + * @NVME_PMA_MIPWRTS_SHIFT: Shift amount to get the maximum interval power timestamp support + * @NVME_PMA_PHDO_SHIFT: Shift amount to get the power histogram descriptor overflow + * @NVME_PMA_PMT_SHIFT: Shift amount to get the power measurement type + * @NVME_PMA_PME_MASK: Mask to get the power measurement enable + * @NVME_PMA_NCPDF_MASK: Mask to get the non-contiguous power data flag + * @NVME_PMA_EPF_MASK: Mask to get the estimated power flag + * @NVME_PMA_MIPWRTS_MASK: Mask to get the maximum interval power timestamp support + * @NVME_PMA_PHDO_MASK: Mask to get the power histogram descriptor overflow + * @NVME_PMA_PMT_MASK: Mask to get the power measurement type + */ +enum nvme_pma { + NVME_PMA_PME_SHIFT = 0, + NVME_PMA_NCPDF_SHIFT = 1, + NVME_PMA_EPF_SHIFT = 2, + NVME_PMA_MIPWRTS_SHIFT = 3, + NVME_PMA_PHDO_SHIFT = 4, + NVME_PMA_PMT_SHIFT = 12, + NVME_PMA_PME_MASK = 0x1, + NVME_PMA_NCPDF_MASK = 0x1, + NVME_PMA_EPF_MASK = 0x1, + NVME_PMA_MIPWRTS_MASK = 0x1, + NVME_PMA_PHDO_MASK = 0x1, + NVME_PMA_PMT_MASK = 0xf, +}; + +/** + * struct nvme_power_histogram_desc - Power Histogram Descriptor + * @phbc: Power Histogram Bin Count. Does not wrap after %FFFFFFFFh. + * Cleared to %0h if PMC is %0h. + * @phblt: Power Histogram Bin Lower Threshold. Bits 17:16 are PWRS + * (see &enum nvme_psd_ps), bits 15:0 are PWRV. + */ +struct nvme_power_histogram_desc { + __le32 phbc; + __le32 phblt; +}; + +/** + * struct nvme_power_meas_log - Power Measurement Log Page (Log Identifier 25h) + * @ver: Version. Shall be cleared to %0h. + * @pmgn: Power Measurement Generation Number. Incremented each time a + * Set Features command with Action = %1h (Start Power Measurements) + * is successfully completed. Rolls over from %FFh to %0h. + * @pma: Power Measurement Attributes. See &enum nvme_pma. + * @sze: Size. Indicates the size of this log page in bytes. + * @pmc: Power Measurement Count. Number of interval power measurements + * collected. Does not wrap after %FFFFFFFFh. Cleared to %0h when + * Start Power Measurements succeeds or if no measurement has occurred. + * @nphd: Number of Power Histogram Descriptors in this log page. + * @smtr: Stop Measurement Time Remaining. Time remaining in minutes until + * controller stops collecting interval power measurements. + * %0h means not specified. + * @smts: Stop Measurement Timestamp. Timestamp of when interval power + * measurements stopped being collected. See &struct nvme_timestamp. + * @phds: Power Histogram Descriptor Size. Size in bytes of each descriptor. + * @phbs: Power Histogram Bin Size. Size in milliwatts of each bin. + * Shall be set to 250 milliwatts. + * @nphds: Number of Power Histogram Descriptors Supported. Maximum number + * of descriptors that can be reported. If %0h, NPHD shall be + * cleared to %0h and no descriptors are reported. + * @vss: Vendor Specific Size. Size in bytes of the Vendor Specific field. + * @phdoc: Power Histogram Descriptor Overflow Count. Number of interval + * power measurements greater than the maximum power indicated by + * the last Power Histogram Descriptor. Does not wrap after %FFFFFFFFh. + * @rsvd36: Reserved. + * @aipwr: Average Interval Power. Bits 31:18 are reserved. Bits 17:16 + * contain the Power Scale (PWRS); see &enum nvme_psd_ps. Bits 15:0 + * contain the Power Value (PWRV). + * @mipwr: Maximum Interval Power. Bits 31:18 are reserved. Bits 17:16 + * contain the Power Scale (PWRS); see &enum nvme_psd_ps. Bits 15:0 + * contain the Power Value (PWRV). + * @mipwrt: Maximum Interval Power Timestamp. Timestamp of when the maximum + * interval power was collected. See &struct nvme_timestamp. + * @ipwrpe: Interval Power Percent Error. Maximum percent error (0-100%). + * Values 101-254 reserved. 255 indicates not reported. + * @rsvd57: Reserved. + * @descs: Power Histogram Descriptors (&struct nvme_power_histogram_desc). + */ +struct nvme_power_meas_log { + __u8 ver; + __u8 pmgn; + __le16 pma; + __le32 sze; + __le32 pmc; + __le16 nphd; + __le16 smtr; + struct nvme_timestamp smts; + __le16 phds; + __le16 phbs; + __le16 nphds; + __le16 vss; + __le32 phdoc; + __u8 rsvd36[4]; + __le32 aipwr; + __le32 mipwr; + struct nvme_timestamp mipwrt; + __u8 ipwrpe; + __u8 rsvd57[7]; + struct nvme_power_histogram_desc descs[]; +}; + + /** * struct nvme_lba_status_desc - LBA Status Descriptor Entry * @dslba: Descriptor Starting LBA diff --git a/nvme-builtin.h b/nvme-builtin.h index 3f3d82a497..2ebf37fc2a 100644 --- a/nvme-builtin.h +++ b/nvme-builtin.h @@ -67,6 +67,7 @@ COMMAND_LIST( ENTRY("host-discovery-log", "Retrieve Host Discovery Log, show it", get_host_discovery_log) ENTRY("ave-discovery-log", "Retrieve AVE Discovery Log, show it", get_ave_discovery_log) ENTRY("pull-model-ddc-req-log", "Retrieve Pull Model DDC Request Log, show it", get_pull_model_ddc_req_log) + ENTRY("power-measurement-log", "Retrieve Power Measurement Log, show it", get_power_measurement_log) ENTRY("set-feature", "Set a feature and show the resulting value", set_feature) ENTRY("set-property", "Set a property and show the resulting value", set_property) ENTRY("get-property", "Get a property and show the resulting value", get_property) diff --git a/nvme-cmds.h b/nvme-cmds.h index 97cd8ba578..35d30f1620 100644 --- a/nvme-cmds.h +++ b/nvme-cmds.h @@ -1100,6 +1100,33 @@ nvme_get_log_mgmt_addr_list(struct nvme_transport_handle *hdl, return nvme_get_log(hdl, &cmd, false, len); } +/** + * nvme_get_log_power_measurement() - Retrieve the Power Measurement Log Page + * @hdl: Transport handle for the controller. + * @log: Pointer to the buffer (@struct nvme_power_meas_log) where + * the log page data will be stored. + * @len: Length of the buffer provided in @log. + * + * Submits the Get Log Page command specifically for the Power Measurement Log. + * + * It automatically sets the Log Identifier (LID) to + * NVME_LOG_LID_POWER_MEASUREMENT, Retain Asynchronous Event (RAE) to false, + * and uses NVME_NSID_ALL. + * + * Return: 0 on success, the NVMe command status on error, or a negative + * errno otherwise. + */ +static inline int +nvme_get_log_power_measurement(struct nvme_transport_handle *hdl, + struct nvme_power_meas_log *log, __u32 len) +{ + struct nvme_passthru_cmd cmd; + + nvme_init_get_log_power_measurement(&cmd, log, len); + + return nvme_get_log(hdl, &cmd, false, NVME_LOG_PAGE_PDU_SIZE); +} + /** * nvme_get_log_phy_rx_eom() - Retrieve the Physical Interface Receiver Eye * Opening Measurement Log Page diff --git a/nvme-print-binary.c b/nvme-print-binary.c index ceb7323e4e..75592f6d24 100644 --- a/nvme-print-binary.c +++ b/nvme-print-binary.c @@ -347,6 +347,11 @@ static void binary_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *lo d_raw((unsigned char *)log, le32_to_cpu(log->tpdrpl)); } +static void binary_power_meas_log(struct nvme_power_meas_log *log, __u32 size) +{ + d_raw((unsigned char *)log, size); +} + static struct print_ops binary_print_ops = { /* libnvme types.h print functions */ .ana_log = binary_ana_log, @@ -421,6 +426,7 @@ static struct print_ops binary_print_ops = { .host_discovery_log = binary_host_discovery_log, .ave_discovery_log = binary_ave_discovery_log, .pull_model_ddc_req_log = binary_pull_model_ddc_req_log, + .power_meas_log = binary_power_meas_log, /* libnvme tree print functions */ .list_item = NULL, diff --git a/nvme-print-json.c b/nvme-print-json.c index 15ca6f4dd0..17b1c37183 100644 --- a/nvme-print-json.c +++ b/nvme-print-json.c @@ -776,6 +776,8 @@ static void json_smart_log(struct nvme_smart_log *smart, unsigned int nsid, obj_add_uint(r, "thm_temp2_trans_count", le32_to_cpu(smart->thm_temp2_trans_count)); obj_add_uint(r, "thm_temp1_total_time", le32_to_cpu(smart->thm_temp1_total_time)); obj_add_uint(r, "thm_temp2_total_time", le32_to_cpu(smart->thm_temp2_total_time)); + obj_add_uint64(r, "op_lifetime_energy_consumed", le64_to_cpu(smart->op_lifetime_energy_consumed)); + obj_add_uint(r, "interval_power_measurement", le32_to_cpu(smart->interval_power_measurement)); json_print(r); } @@ -3768,24 +3770,10 @@ static void json_feature_show_fields_host_mem_buf(struct json_object *r, unsigne static void json_timestamp(struct json_object *r, struct nvme_timestamp *ts) { - char buffer[BUF_LEN]; - time_t timestamp = int48_to_long(ts->timestamp) / 1000; - struct tm *tm = localtime(×tamp); - obj_add_uint64(r, "timestamp", int48_to_long(ts->timestamp)); - - if (!strftime(buffer, sizeof(buffer), "%c %Z", tm)) - sprintf(buffer, "%s", "-"); - - obj_add_str(r, "timestamp string", buffer); - - obj_add_str(r, "timestamp origin", ts->attr & 2 ? - "The Timestamp field was initialized with a Timestamp value using a Set Features command." : - "The Timestamp field was initialized to 0h by a Controller Level Reset."); - - obj_add_str(r, "synch", ts->attr & 1 ? - "The controller may have stopped counting during vendor specific intervals after the Timestamp value was initialized." : - "The controller counted time in milliseconds continuously since the Timestamp value was initialized."); + obj_add_str(r, "timestamp string", nvme_format_timestamp(ts->timestamp)); + obj_add_str(r, "timestamp origin", nvme_format_timestamp_origin(ts->attr)); + obj_add_str(r, "synch", nvme_format_timestamp_sync(ts->attr)); } static void json_feature_show_fields_timestamp(struct json_object *r, unsigned char *buf) @@ -5716,6 +5704,63 @@ static void json_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log) obj_d(r, "osp", (unsigned char *)log->osp, osp_len, 16, 1); } +static void json_power_meas_log(struct nvme_power_meas_log *log, __u32 size UNUSED) +{ + struct json_object *r = json_create_object(); + struct json_object *descs; + struct json_object *smts_obj; + struct json_object *mipwrt_obj; + __u16 nphd = le16_to_cpu(log->nphd); + __u16 pma = le16_to_cpu(log->pma); + __u8 pmt = NVME_GET(pma, PMA_PMT); + __u16 i; + + obj_add_uint(r, "ver", log->ver); + obj_add_uint(r, "pmgn", log->pmgn); + obj_add_uint(r, "pma", pma); + obj_add_uint(r, "pme", NVME_GET(pma, PMA_PME)); + obj_add_uint(r, "ncpdf", NVME_GET(pma, PMA_NCPDF)); + obj_add_uint(r, "epf", NVME_GET(pma, PMA_EPF)); + obj_add_uint(r, "mipwrts", NVME_GET(pma, PMA_MIPWRTS)); + obj_add_uint(r, "phdo", NVME_GET(pma, PMA_PHDO)); + obj_add_uint(r, "pmt", pmt); + obj_add_str(r, "pmt_str", nvme_power_measurement_type_to_string(pmt)); + obj_add_uint(r, "sze", le32_to_cpu(log->sze)); + obj_add_uint(r, "pmc", le32_to_cpu(log->pmc)); + obj_add_uint(r, "nphd", nphd); + obj_add_uint(r, "smtr", le16_to_cpu(log->smtr)); + + smts_obj = json_create_object(); + json_timestamp(smts_obj, &log->smts); + obj_add_obj(r, "smts", smts_obj); + + obj_add_uint(r, "phds", le16_to_cpu(log->phds)); + obj_add_uint(r, "phbs", le16_to_cpu(log->phbs)); + obj_add_uint(r, "nphds", le16_to_cpu(log->nphds)); + obj_add_uint(r, "vss", le16_to_cpu(log->vss)); + obj_add_uint(r, "phdoc", le32_to_cpu(log->phdoc)); + obj_add_uint(r, "aipwr", le32_to_cpu(log->aipwr)); + obj_add_uint(r, "mipwr", le32_to_cpu(log->mipwr)); + + mipwrt_obj = json_create_object(); + json_timestamp(mipwrt_obj, &log->mipwrt); + obj_add_obj(r, "mipwrt", mipwrt_obj); + + obj_add_uint(r, "ipwrpe", log->ipwrpe); + + descs = json_create_array(); + for (i = 0; i < nphd; i++) { + struct json_object *desc = json_create_object(); + + obj_add_uint(desc, "phbc", le32_to_cpu(log->descs[i].phbc)); + obj_add_uint(desc, "phblt", le32_to_cpu(log->descs[i].phblt)); + json_object_array_add(descs, desc); + } + obj_add_obj(r, "descs", descs); + + json_print(r); +} + static struct print_ops json_print_ops = { /* libnvme types.h print functions */ .ana_log = json_ana_log, @@ -5791,6 +5836,7 @@ static struct print_ops json_print_ops = { .host_discovery_log = json_host_discovery_log, .ave_discovery_log = json_ave_discovery_log, .pull_model_ddc_req_log = json_pull_model_ddc_req_log, + .power_meas_log = json_power_meas_log, /* libnvme tree print functions */ .list_item = json_list_item, diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index 3188dafaf3..810f216e5e 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -3271,6 +3271,11 @@ static void print_ps_power_and_scale(__le16 ctr_power, __u8 scale) print_power_and_scale(le16_to_cpu(ctr_power), scale); } +static void print_power_field(__u32 pwr) +{ + print_power_and_scale(pwr & 0xffff, (pwr >> 16) & 0x3); +} + static void print_psd_time(const char *desc, __u8 time, __u8 ts) { int width = 12 + strlen(desc); @@ -4545,6 +4550,7 @@ static void stdout_endurance_log(struct nvme_endurance_group_log *endurance_log, static void stdout_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char *devname) { __u16 temperature = smart->temperature[1] << 8 | smart->temperature[0]; + __u32 ipm = le32_to_cpu(smart->interval_power_measurement); int i; bool human = stdout_print_ops.flags & VERBOSE; @@ -4617,6 +4623,13 @@ static void stdout_smart_log(struct nvme_smart_log *smart, unsigned int nsid, co le32_to_cpu(smart->thm_temp1_total_time)); printf("Thermal Management T2 Total Time : %u\n", le32_to_cpu(smart->thm_temp2_total_time)); + printf("Operational Lifetime Energy Consumed : %"PRIu64"\n", + le64_to_cpu(smart->op_lifetime_energy_consumed)); + printf("Interval Power Measurement Type : %s\n", + nvme_power_measurement_type_to_string((ipm >> 20) & 0x3f)); + printf("Interval Power Measurement : "); + print_power_field(ipm); + printf("\n"); } static void stdout_ana_log(struct nvme_ana_log *ana_log, const char *devname, @@ -4904,27 +4917,22 @@ static void stdout_auto_pst(struct nvme_feat_auto_pst *apst) } } -static void stdout_timestamp(struct nvme_timestamp *ts) +static const char *stdout_format_timestamp(__u8 *timestamp_bytes) { - struct tm *tm; - char buffer[320]; - time_t timestamp = int48_to_long(ts->timestamp) / 1000; + static char buf[STR_LEN]; + uint64_t ts_ms = int48_to_long(timestamp_bytes); - tm = localtime(×tamp); + snprintf(buf, sizeof(buf), "%"PRIu64" (%s)", ts_ms, + nvme_format_timestamp(timestamp_bytes)); - printf("\tThe timestamp is : %'"PRIu64" (%s)\n", - int48_to_long(ts->timestamp), - strftime(buffer, sizeof(buffer), "%c %Z", tm) ? buffer : "-"); - printf("\t%s\n", (ts->attr & 2) ? - "The Timestamp field was initialized with a "\ - "Timestamp value using a Set Features command." : - "The Timestamp field was initialized "\ - "to ‘0’ by a Controller Level Reset."); - printf("\t%s\n", (ts->attr & 1) ? - "The controller may have stopped counting during vendor specific "\ - "intervals after the Timestamp value was initialized" : - "The controller counted time in milliseconds "\ - "continuously since the Timestamp value was initialized."); + return buf; +} + +static void stdout_timestamp(struct nvme_timestamp *ts) +{ + printf("\tThe timestamp is : %s\n", stdout_format_timestamp(ts->timestamp)); + printf("\t%s\n", nvme_format_timestamp_origin(ts->attr)); + printf("\t%s\n", nvme_format_timestamp_sync(ts->attr)); } static void stdout_host_mem_buffer(struct nvme_host_mem_buf_attrs *hmb) @@ -6622,6 +6630,83 @@ static void stdout_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *lo d((unsigned char *)log->osp, osp_len, 16, 1); } +static void stdout_power_meas_log(struct nvme_power_meas_log *log, __u32 size) +{ + __u16 nphd = le16_to_cpu(log->nphd); + __u16 pma = le16_to_cpu(log->pma); + __u8 pmt = NVME_GET(pma, PMA_PMT); + __u32 aipwr = le32_to_cpu(log->aipwr); + __u32 mipwr = le32_to_cpu(log->mipwr); + __u16 i; + bool verbose = stdout_print_ops.flags & VERBOSE; + + printf("Power Measurement Log\n"); + printf("%-47s : %u\n", "Version", log->ver); + printf("%-47s : %u\n", "Power Measurement Generation Number", log->pmgn); + printf("%-47s : %#06x\n", "Power Measurement Attributes", pma); + + if (verbose) { + printf(" %-43s : %u\n", "Power Measurement Enable", NVME_GET(pma, PMA_PME)); + printf(" %-43s : %u\n", "Non-Contiguous Power Data Flag", NVME_GET(pma, PMA_NCPDF)); + printf(" %-43s : %u\n", "Estimated Power Flag", NVME_GET(pma, PMA_EPF)); + printf(" %-43s : %u\n", "Maximum Interval Power Timestamp Support", NVME_GET(pma, PMA_MIPWRTS)); + printf(" %-43s : %u\n", "Power Histogram Descriptor Overflow", NVME_GET(pma, PMA_PHDO)); + printf(" %-43s : %u (%s)\n", "Power Measurement Type", pmt, + nvme_power_measurement_type_to_string(pmt)); + } + + printf("%-47s : %u\n", "Size (bytes)", le32_to_cpu(log->sze)); + printf("%-47s : %u\n", "Power Measurement Count", le32_to_cpu(log->pmc)); + printf("%-47s : %u\n", "Number of Power Histogram Descriptors", nphd); + printf("%-47s : %u\n", "Stop Measurement Time Remaining (minutes)", le16_to_cpu(log->smtr)); + printf("%-47s : %s\n", "Stop Measurement Timestamp", stdout_format_timestamp(log->smts.timestamp)); + + if (verbose) { + printf(" %-43s : %u (%s)\n", "Timestamp Origin", + NVME_TIMESTAMP_ATTR_TO(log->smts.attr), + nvme_format_timestamp_origin(log->smts.attr)); + printf(" %-43s : %u (%s)\n", "Sync", + NVME_TIMESTAMP_ATTR_SYNC(log->smts.attr), + nvme_format_timestamp_sync(log->smts.attr)); + } + + printf("%-47s : %u\n", "Power Histogram Descriptor Size (bytes)", le16_to_cpu(log->phds)); + printf("%-47s : %u\n", "Power Histogram Bin Size (mW)", le16_to_cpu(log->phbs)); + printf("%-47s : %u\n", "Number of Power Histogram Descriptors Supported", le16_to_cpu(log->nphds)); + printf("%-47s : %u\n", "Vendor Specific Size (bytes)", le16_to_cpu(log->vss)); + printf("%-47s : %u\n", "Power Histogram Descriptor Overflow Count", le32_to_cpu(log->phdoc)); + printf("%-47s : ", "Average Interval Power"); + print_power_field(aipwr); + printf("\n"); + printf("%-47s : ", "Maximum Interval Power"); + print_power_field(mipwr); + printf("\n"); + printf("%-47s : %s\n", "Maximum Interval Power Timestamp", stdout_format_timestamp(log->mipwrt.timestamp)); + + if (verbose) { + printf(" %-43s : %u (%s)\n", "Timestamp Origin", + NVME_TIMESTAMP_ATTR_TO(log->mipwrt.attr), + nvme_format_timestamp_origin(log->mipwrt.attr)); + printf(" %-43s : %u (%s)\n", "Sync", + NVME_TIMESTAMP_ATTR_SYNC(log->mipwrt.attr), + nvme_format_timestamp_sync(log->mipwrt.attr)); + } + + printf("%-47s : %u\n", "Interval Power Percent Error", log->ipwrpe); + + if (verbose) { + for (i = 0; i < nphd; i++) { + __u32 phblt = le32_to_cpu(log->descs[i].phblt); + + printf("Power Histogram Descriptor [%u]:\n", i); + printf(" %-43s : %u\n", "Power Histogram Bin Count", le32_to_cpu(log->descs[i].phbc)); + printf(" %-43s : ", "Power Histogram Bin Lower Threshold"); + print_power_field(phblt); + printf("\n"); + } + } +} + static void stdout_relatives(struct nvme_global_ctx *ctx, const char *name) { struct nvme_resources res; @@ -6808,6 +6893,9 @@ static void stdout_log(const char *devname, struct nvme_get_log_args *args) case NVME_LOG_LID_PULL_MODEL_DDC_REQ: stdout_pull_model_ddc_req_log((struct nvme_pull_model_ddc_req_log *)args->log); break; + case NVME_LOG_LID_POWER_MEASUREMENT: + stdout_power_meas_log((struct nvme_power_meas_log *)args->log, args->len); + break; case NVME_LOG_LID_RESERVATION: stdout_resv_notif_log((struct nvme_resv_notification_log *)args->log, devname); break; @@ -6898,6 +6986,7 @@ static struct print_ops stdout_print_ops = { .host_discovery_log = stdout_host_discovery_log, .ave_discovery_log = stdout_ave_discovery_log, .pull_model_ddc_req_log = stdout_pull_model_ddc_req_log, + .power_meas_log = stdout_power_meas_log, .log = stdout_log, /* libnvme tree print functions */ diff --git a/nvme-print.c b/nvme-print.c index 92de312aec..140ee9321c 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -890,6 +890,44 @@ const char *nvme_degrees_fahrenheit_string(long t) return str; } +const char *nvme_format_timestamp(__u8 *timestamp_bytes) +{ + static char buf[STR_LEN]; + struct tm *tm; + char timebuf[STR_LEN]; + uint64_t ts_ms = int48_to_long(timestamp_bytes); + time_t ts_sec = ts_ms / 1000; + + tm = localtime(&ts_sec); + if (!tm) { + snprintf(buf, sizeof(buf), "%s", "-"); + } else { + snprintf(buf, sizeof(buf), "%s", + strftime(timebuf, sizeof(timebuf), "%c %Z", tm) ? timebuf : "-"); + } + + return buf; +} + +const char *nvme_format_timestamp_origin(__u8 attr) +{ + switch (NVME_TIMESTAMP_ATTR_TO(attr)) { + case 0: + return "The Timestamp field was initialized to 0h by a Controller Level Reset."; + case 1: + return "The Timestamp field was initialized with a Timestamp value using a Set Features command."; + default: + return "Reserved"; + } +} + +const char *nvme_format_timestamp_sync(__u8 attr) +{ + return NVME_TIMESTAMP_ATTR_SYNC(attr) ? + "The controller may have stopped counting during vendor specific intervals after the Timestamp value was initialized." : + "The controller counted time in milliseconds continuously since the Timestamp value was initialized."; +} + void nvme_show_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char *devname, nvme_print_flags_t flags) { @@ -1703,6 +1741,12 @@ void nvme_show_mgmt_addr_list_log(struct nvme_mgmt_addr_list_log *ma_list, nvme_ nvme_print(mgmt_addr_list_log, flags, ma_list); } +void nvme_show_power_meas_log(struct nvme_power_meas_log *log, __u32 size, + nvme_print_flags_t flags) +{ + nvme_print(power_meas_log, flags, log, size); +} + void nvme_show_rotational_media_info_log(struct nvme_rotational_media_info_log *info, nvme_print_flags_t flags) { diff --git a/nvme-print.h b/nvme-print.h index 0d976215d1..cca59ff1f9 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -64,6 +64,7 @@ struct print_ops { void (*ns_list_log)(struct nvme_ns_list *log, const char *devname, bool alloc); void (*nvm_id_ns)(struct nvme_nvm_id_ns *nvm_ns, unsigned int nsid, struct nvme_id_ns *ns, unsigned int lba_index, bool cap_only); void (*persistent_event_log)(void *pevent_log_info, __u8 action, __u32 size, const char *devname); + void (*power_meas_log)(struct nvme_power_meas_log *log, __u32 size); void (*predictable_latency_event_agg_log)(struct nvme_aggregate_predictable_lat_event *pea_log, __u64 log_entries, __u32 size, const char *devname); void (*predictable_latency_per_nvmset)(struct nvme_nvmset_predictable_lat_log *plpns_log, __u16 nvmset_id, const char *devname); void (*primary_ctrl_cap)(const struct nvme_primary_ctrl_cap *caps); @@ -360,11 +361,16 @@ bool nvme_registers_cmbloc_support(__u32 cmbsz); bool nvme_registers_pmrctl_ready(__u32 pmrctl); const char *nvme_degrees_string(long t); const char *nvme_degrees_fahrenheit_string(long t); +const char *nvme_format_timestamp(__u8 *timestamp_bytes); +const char *nvme_format_timestamp_origin(__u8 attr); +const char *nvme_format_timestamp_sync(__u8 attr); void print_array(char *name, __u8 *data, int size); void json_print(struct json_object *r); struct json_object *obj_create_array_obj(struct json_object *o, const char *k); void nvme_show_mgmt_addr_list_log(struct nvme_mgmt_addr_list_log *ma_list, nvme_print_flags_t flags); +void nvme_show_power_meas_log(struct nvme_power_meas_log *log, __u32 size, + nvme_print_flags_t flags); void nvme_show_rotational_media_info_log(struct nvme_rotational_media_info_log *info, nvme_print_flags_t flags); void nvme_show_dispersed_ns_psub_log(struct nvme_dispersed_ns_participating_nss_log *log, diff --git a/nvme.c b/nvme.c index 08fb9f8363..b9d0a9f440 100644 --- a/nvme.c +++ b/nvme.c @@ -10789,6 +10789,83 @@ static int get_dispersed_ns_participating_nss_log(int argc, char **argv, struct return err; } +static int get_power_measurement_log(int argc, char **argv, struct command *acmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve Power Measurement Log (Log ID 0x25) " + "for the given device in either decoded format (default), " + "json, or binary."; + + _cleanup_nvme_global_ctx_ struct nvme_global_ctx *ctx = NULL; + _cleanup_nvme_transport_handle_ struct nvme_transport_handle *hdl = NULL; + _cleanup_free_ struct nvme_power_meas_log *log = NULL; + nvme_print_flags_t flags; + __u32 min_log_size = sizeof(struct nvme_power_meas_log); + __u32 log_size; + int err = -1; + + struct config { + bool raw_binary; + }; + + struct config cfg = { + .raw_binary = false, + }; + + NVME_ARGS(opts, + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_output)); + + err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts); + if (err) + return err; + + err = validate_output_format(nvme_args.output_format, &flags); + if (err < 0) { + nvme_show_error("Invalid output format"); + return err; + } + + if (cfg.raw_binary) + flags = BINARY; + + if (argconfig_parse_seen(opts, "verbose")) + flags |= VERBOSE; + + /* First read minimum size to discover the full log size */ + log = nvme_alloc(min_log_size); + if (!log) + return -ENOMEM; + + err = nvme_get_log_power_measurement(hdl, log, min_log_size); + if (err) { + nvme_show_err("power-measurement-log", err); + return err; + } + + log_size = le32_to_cpu(log->sze); + + /* If sze is 0 or smaller than the minimum, just use minimum */ + if (log_size < min_log_size) + log_size = min_log_size; + + /* If the log is larger, re-read with full size */ + if (log_size > min_log_size) { + log = nvme_realloc(log, log_size); + if (!log) + return -ENOMEM; + + err = nvme_get_log_power_measurement(hdl, log, log_size); + if (err) { + nvme_show_err("power-measurement-log", err); + return err; + } + } + + nvme_show_power_meas_log(log, log_size, flags); + + return err; +} + static int get_log_offset(struct nvme_transport_handle *hdl, struct nvme_get_log_args *args, __u64 *offset, __u32 len, void **log) diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 6a88c827f1..655d85fc91 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -4189,21 +4189,14 @@ struct micron_smart_log_ext { /* Access vendor-specific fields via rsvd232 overlay */ }; -/* - * OLEC: bytes 232-239 (rsvd232[0:7]) - * IPM: bytes 240-243 (rsvd232[8:11]) - */ -#define SMART_OLEC_OFFSET 0 -#define SMART_IPM_OFFSET 8 - static inline __u64 get_smart_olec(struct nvme_smart_log *smart) { - return le64_to_cpu(*(__le64 *)&smart->rsvd232[SMART_OLEC_OFFSET]); + return le64_to_cpu(smart->op_lifetime_energy_consumed); } static inline __u32 get_smart_ipm(struct nvme_smart_log *smart) { - return le32_to_cpu(*(__le32 *)&smart->rsvd232[SMART_IPM_OFFSET]); + return le32_to_cpu(smart->interval_power_measurement); } static void print_micron_health_log_normal(struct nvme_smart_log *smart,