diff options
Diffstat (limited to '')
-rw-r--r-- | plugins/solidigm/solidigm-internal-logs.c | 70 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-nvme.h | 2 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry.c | 13 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-util.c | 16 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-util.h | 5 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-workload-tracker.c | 365 |
6 files changed, 330 insertions, 141 deletions
diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c index f5b57f3..d493216 100644 --- a/plugins/solidigm/solidigm-internal-logs.c +++ b/plugins/solidigm/solidigm-internal-logs.c @@ -4,6 +4,7 @@ * * Authors: leonardo.da.cunha@solidigm.com * shankaralingegowda.singonahalli@solidigm.com + * haro.panosyan@solidigm.com */ #include <fcntl.h> @@ -136,7 +137,6 @@ struct ilog { int count; struct nvme_id_ctrl id_ctrl; enum nvme_telemetry_da max_da; - __u32 max_tx; }; static void print_nlog_header(__u8 *buffer) @@ -458,7 +458,6 @@ static int log_save(struct log *log, const char *parent_dir_name, const char *su _cleanup_fd_ int output = -1; char file_path[PATH_MAX] = {0}; size_t bytes_remaining = 0; - int err = 0; ensure_dir(parent_dir_name, subdir_name); @@ -473,19 +472,14 @@ static int log_save(struct log *log, const char *parent_dir_name, const char *su while (bytes_remaining) { ssize_t bytes_written = write(output, buffer, bytes_remaining); - if (bytes_written < 0) { - err = -errno; - goto log_save_close_output; - } + if (bytes_written < 0) + return -errno; bytes_remaining -= bytes_written; buffer += bytes_written; } printf("Successfully wrote %s to %s\n", log->desc, file_path); - -log_save_close_output: - close(output); - return err; + return 0; } static int ilog_dump_identify_page(struct ilog *ilog, struct log *cns, __u32 nsid) @@ -527,11 +521,6 @@ static int ilog_ensure_dump_id_ctrl(struct ilog *ilog) if (ilog->id_ctrl.lpa & 0x40) ilog->max_da = NVME_TELEMETRY_DA_4; - /* assuming CAP.MPSMIN is zero minimum Memory Page Size is at least 4096 bytes */ - ilog->max_tx = (1 << ilog->id_ctrl.mdts) * NVME_LOG_PAGE_PDU_SIZE; - if (ilog->max_tx > DRIVER_MAX_TX_256K) - ilog->max_tx = DRIVER_MAX_TX_256K; - return err; } @@ -539,7 +528,7 @@ static int ilog_dump_telemetry(struct ilog *ilog, enum log_type ttype) { int err = 0; enum nvme_telemetry_da da; - size_t max_data_tx; + size_t mdts; const char *file_name; struct nvme_feat_host_behavior prev = {0}; bool host_behavior_changed = false; @@ -550,7 +539,7 @@ static int ilog_dump_telemetry(struct ilog *ilog, enum log_type ttype) return err; da = ilog->max_da; - max_data_tx = ilog->max_tx; + mdts = ilog->id_ctrl.mdts; if (da == 4) { __u32 result; @@ -569,16 +558,16 @@ static int ilog_dump_telemetry(struct ilog *ilog, enum log_type ttype) case HIT: file_name = "lid_0x07_lsp_0x01_lsi_0x0000.bin"; log.desc = "Host Initiated Telemetry"; - err = nvme_get_telemetry_log(dev_fd(ilog->dev), true, false, false, max_data_tx, da, - (struct nvme_telemetry_log **) &log.buffer, - &log.buffer_size); + err = sldgm_dynamic_telemetry(dev_fd(ilog->dev), true, false, false, mdts, + da, (struct nvme_telemetry_log **) &log.buffer, + &log.buffer_size); break; case CIT: file_name = "lid_0x08_lsp_0x00_lsi_0x0000.bin"; log.desc = "Controller Initiated Telemetry"; - err = nvme_get_telemetry_log(dev_fd(ilog->dev), false, true, true, max_data_tx, da, - (struct nvme_telemetry_log **) &log.buffer, - &log.buffer_size); + err = sldgm_dynamic_telemetry(dev_fd(ilog->dev), false, true, true, mdts, + da, (struct nvme_telemetry_log **) &log.buffer, + &log.buffer_size); break; default: return -EINVAL; @@ -597,14 +586,17 @@ static int ilog_dump_telemetry(struct ilog *ilog, enum log_type ttype) static int ilog_dump_identify_pages(struct ilog *ilog) { - struct nvme_ns_list ns_list; + struct nvme_ns_list ns_attached_list; + struct nvme_ns_list ns_allocated_list; __u32 j = 0; + struct log identify_base_list[] = { {NVME_IDENTIFY_CNS_NS_ACTIVE_LIST, "Id Active Namespace ID list", - sizeof(ns_list), (__u8 *) &ns_list}, + sizeof(ns_attached_list), (__u8 *) &ns_attached_list}, {NVME_IDENTIFY_CNS_NVMSET_LIST, "Id NVM Set List"}, {NVME_IDENTIFY_CNS_CSI_CTRL, "Id I/O Command Set specific"}, - {NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, "Id Allocated Namespace ID list"}, + {NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, "Id Allocated Namespace ID list", + sizeof(ns_allocated_list), (__u8 *) &ns_allocated_list}, {NVME_IDENTIFY_CNS_CTRL_LIST, "Id Controller List"} }; struct log identify_ns_required_list[] = { @@ -613,10 +605,12 @@ static int ilog_dump_identify_pages(struct ilog *ilog) {NVME_IDENTIFY_CNS_CSI_NS, "Id Namespace ID I/O Command Set specific"}, {NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS, "I/O Command Set Independent Identify Namespace Data"}, - {NVME_IDENTIFY_CNS_ALLOCATED_NS, "Id Namespace data "}, {NVME_IDENTIFY_CNS_NS_CTRL_LIST, "Id Namespace Id Controller List"}, }; + struct log allocated = {NVME_IDENTIFY_CNS_ALLOCATED_NS, "Allocated Namespace Data", + NVME_IDENTIFY_DATA_SIZE, NULL}; + ilog_ensure_dump_id_ctrl(ilog); for (int i = 0; i < ARRAY_SIZE(identify_base_list); i++) { @@ -626,10 +620,10 @@ static int ilog_dump_identify_pages(struct ilog *ilog) ilog->count++; } - while (ns_list.ns[j]) { + while (ns_attached_list.ns[j]) { for (int i = 0; i < ARRAY_SIZE(identify_ns_required_list); i++) { int err = ilog_dump_identify_page(ilog, &identify_ns_required_list[i], - ns_list.ns[j]); + ns_attached_list.ns[j]); if (err == 0) ilog->count++; @@ -637,6 +631,15 @@ static int ilog_dump_identify_pages(struct ilog *ilog) j++; } + j = 0; + while (ns_allocated_list.ns[j]) { + int err = ilog_dump_identify_page(ilog, &allocated, ns_allocated_list.ns[j]); + + if (err == 0) + ilog->count++; + j++; + } + return 0; } @@ -740,6 +743,7 @@ static int ilog_dump_pel(struct ilog *ilog) void *pevent_log_full; int err; struct nvme_get_log_args args; + size_t max_data_tx; _cleanup_free_ struct nvme_persistent_event_log *pevent = NULL; @@ -785,7 +789,13 @@ static int ilog_dump_pel(struct ilog *ilog) .rae = false, .ot = false, }; - err = nvme_get_log_page(dev_fd(ilog->dev), ilog->max_tx, &args); + + max_data_tx = (1 << ilog->id_ctrl.mdts) * NVME_LOG_PAGE_PDU_SIZE; + do { + err = nvme_get_log_page(dev_fd(ilog->dev), max_data_tx, &args); + max_data_tx /= 2; + } while (err == -EPERM && max_data_tx >= NVME_LOG_PAGE_PDU_SIZE); + if (err) return err; diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h index 2b74a02..cb32ed0 100644 --- a/plugins/solidigm/solidigm-nvme.h +++ b/plugins/solidigm/solidigm-nvme.h @@ -13,7 +13,7 @@ #include "cmd.h" -#define SOLIDIGM_PLUGIN_VERSION "1.6" +#define SOLIDIGM_PLUGIN_VERSION "1.8" PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION), COMMAND_LIST( diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index 2bebccc..12cb6c6 100644 --- a/plugins/solidigm/solidigm-telemetry.c +++ b/plugins/solidigm/solidigm-telemetry.c @@ -144,6 +144,8 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc if (!cfg.is_input_file) { size_t max_data_tx; + size_t power2; + __u8 mdts = 0; err = nvme_get_telemetry_max(dev_fd(dev), NULL, &max_data_tx); if (err < 0) { @@ -155,11 +157,14 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc SOLIDIGM_LOG_WARNING("Failed to acquire identify ctrl %d!", err); goto close_fd; } - if (max_data_tx > DRIVER_MAX_TX_256K) - max_data_tx = DRIVER_MAX_TX_256K; + power2 = max_data_tx / NVME_LOG_PAGE_PDU_SIZE; + while (power2 && !(1 & power2)) { + power2 >>= 1; + mdts++; + } - err = nvme_get_telemetry_log(dev_fd(dev), cfg.host_gen, cfg.ctrl_init, true, - max_data_tx, cfg.data_area, &tl.log, &tl.log_size); + err = sldgm_dynamic_telemetry(dev_fd(dev), cfg.host_gen, cfg.ctrl_init, true, + mdts, cfg.data_area, &tl.log, &tl.log_size); if (err < 0) { SOLIDIGM_LOG_WARNING("get-telemetry-log: %s", nvme_strerror(errno)); diff --git a/plugins/solidigm/solidigm-util.c b/plugins/solidigm/solidigm-util.c index 05d1537..8206ef8 100644 --- a/plugins/solidigm/solidigm-util.c +++ b/plugins/solidigm/solidigm-util.c @@ -37,3 +37,19 @@ int sldgm_get_uuid_index(struct nvme_dev *dev, __u8 *index) return sldgm_find_uuid_index(&uuid_list, index); } + +int sldgm_dynamic_telemetry(int dev_fd, bool create, bool ctrl, bool log_page, __u8 mtds, + enum nvme_telemetry_da da, struct nvme_telemetry_log **log_buffer, + size_t *log_buffer_size) +{ + int err; + size_t max_data_tx = (1 << mtds) * NVME_LOG_PAGE_PDU_SIZE; + + do { + err = nvme_get_telemetry_log(dev_fd, create, ctrl, log_page, max_data_tx, da, + log_buffer, log_buffer_size); + max_data_tx /= 2; + create = false; + } while (err == -EPERM && max_data_tx >= NVME_LOG_PAGE_PDU_SIZE); + return err; +} diff --git a/plugins/solidigm/solidigm-util.h b/plugins/solidigm/solidigm-util.h index ed7bf0f..85adbf9 100644 --- a/plugins/solidigm/solidigm-util.h +++ b/plugins/solidigm/solidigm-util.h @@ -7,7 +7,8 @@ #include "nvme.h" -#define DRIVER_MAX_TX_256K (256 * 1024) - int sldgm_find_uuid_index(struct nvme_id_uuid_list *uuid_list, __u8 *index); int sldgm_get_uuid_index(struct nvme_dev *dev, __u8 *index); +int sldgm_dynamic_telemetry(int dev_fd, bool create, bool ctrl, bool log_page, __u8 mtds, + enum nvme_telemetry_da da, struct nvme_telemetry_log **log_buffer, + size_t *log_buffer_size); diff --git a/plugins/solidigm/solidigm-workload-tracker.c b/plugins/solidigm/solidigm-workload-tracker.c index 73bb3c3..14d8458 100644 --- a/plugins/solidigm/solidigm-workload-tracker.c +++ b/plugins/solidigm/solidigm-workload-tracker.c @@ -12,7 +12,8 @@ #define LID 0xf9 #define FID 0xf1 -#define WLT2MS 25000 +#define WLT2US 25 +#define WLT2MS (WLT2US * 1000) #define MAX_WORKLOAD_LOG_ENTRIES 126 #define MAX_WORKLOAD_LOG_ENTRY_SIZE 32 #define MAX_FIELDS 15 @@ -174,11 +175,11 @@ union WorkloadLogEnable { __u32 contentGroup : 4; // content group select __u32 stopCount : 12;// event limit,if<>0,stop tracker after stopCount events __u32 eventDumpEnable : 1; // trigger event dump enable - } field; + }; __u32 dword; }; -struct workloadLogHeader { +struct workloadLog { // Full WL Log Structure __u16 majorVersion; // Major Version __u16 minorVersion; // Minor Version __u32 workloadLogCount; // Number of Entries in the Workload Log @@ -187,14 +188,9 @@ struct workloadLogHeader { __u32 samplePeriodInMilliseconds; // Sample Period In Milliseconds __u64 timestamp_lastEntry; // Timestamp for the last full entry __u64 timestamp_triggered; // Timestamp at the point of trigger - __u32 trackerEnable; // Workload trigger and enable settings + union WorkloadLogEnable config; // Workload trigger and enable settings __u32 triggerthreshold; // Trigger threshold __u32 triggeredValue; // Actual value fired the trigger -}; - - -struct workloadLog { // Full WL Log Structure - struct workloadLogHeader header; __u8 entry[MAX_WORKLOAD_LOG_ENTRIES][MAX_WORKLOAD_LOG_ENTRY_SIZE]; }; #pragma pack(pop) @@ -202,24 +198,26 @@ struct workloadLog { // Full WL Log Structure struct wltracker { int fd; struct workloadLog workload_log; - size_t entry_count; + size_t poll_count; + bool show_wall_timestamp; + __u64 us_epoch_ssd_delta; unsigned int verbose; + __u64 start_time_us; + __u64 run_time_us; + bool disable; }; static void wltracker_print_field_names(struct wltracker *wlt) { struct workloadLog *log = &wlt->workload_log; - __u8 cnt = log->header.workloadLogCount; - union WorkloadLogEnable workloadEnable = (union WorkloadLogEnable)log->header.trackerEnable; - __u8 content_group = workloadEnable.field.contentGroup; - if (cnt == 0) + if (log->workloadLogCount == 0) return; printf("%-16s", "timestamp"); for (int i = 0 ; i < MAX_FIELDS; i++) { - struct field f = group_fields[content_group][i]; + struct field f = group_fields[log->config.contentGroup][i]; if (f.size == 0) break; @@ -228,8 +226,11 @@ static void wltracker_print_field_names(struct wltracker *wlt) printf("%s ", f.name); } + if (wlt->show_wall_timestamp) + printf("%-*s", (int)sizeof("YYYY-MM-DD-hh:mm:ss.uuuuuu"), "wall-time"); + if (wlt->verbose > 1) - printf("%s", "entry#"); + printf("%s", "entry# "); printf("\n"); } @@ -237,35 +238,59 @@ static void wltracker_print_field_names(struct wltracker *wlt) static void wltracker_print_header(struct wltracker *wlt) { struct workloadLog *log = &wlt->workload_log; - __u8 cnt = log->header.workloadLogCount; - union WorkloadLogEnable workloadEnable = (union WorkloadLogEnable)log->header.trackerEnable; - __u8 content_group = workloadEnable.field.contentGroup; - - printf("%-20s %u.%u\n", "Log page version:", le16_to_cpu(log->header.majorVersion), - le16_to_cpu(log->header.minorVersion)); - printf("%-20s %u\n", "Sample period(ms):", - le32_to_cpu(log->header.samplePeriodInMilliseconds)); - printf("%-20s %lu\n", "timestamp_lastEntry:", - le64_to_cpu(log->header.timestamp_lastEntry) / WLT2MS); - printf("%-20s %lu\n", "timestamp_triggered:", - le64_to_cpu(log->header.timestamp_triggered/1000)); - printf("%-20s 0x%x\n", "trackerEnable:", le32_to_cpu(log->header.trackerEnable)); - printf("%-20s %u\n", "Triggerthreshold:", - le32_to_cpu(log->header.triggerthreshold)); - printf("%-20s %u\n", "ValueTriggered:", le32_to_cpu(log->header.triggeredValue)); - printf("%-20s %s\n", "Tracker Type:", trk_types[content_group]); - printf("%-30s %u\n", "Total workload log entries:", le16_to_cpu(cnt)); - printf("%-20s %ld\n\n", "Sample count:", wlt->entry_count); - if (wlt->entry_count != 0) + + printf("%-24s %u.%u\n", "Log page version:", le16_to_cpu(log->majorVersion), + le16_to_cpu(log->minorVersion)); + printf("%-24s %u\n", "Sample period(ms):", le32_to_cpu(log->samplePeriodInMilliseconds)); + printf("%-24s %lu\n", "timestamp_lastChange:", le64_to_cpu(log->timestamp_lastEntry)); + printf("%-24s %lu\n", "timestamp_triggered:", le64_to_cpu(log->timestamp_triggered)); + printf("%-24s 0x%x\n", "config:", le32_to_cpu(log->config.dword)); + printf("%-24s %u\n", "Triggerthreshold:", le32_to_cpu(log->triggerthreshold)); + printf("%-24s %u\n", "ValueTriggered:", le32_to_cpu(log->triggeredValue)); + printf("%-24s %s\n", "Tracker Type:", trk_types[log->config.contentGroup]); + printf("%-24s %u\n", "Total log page entries:", le32_to_cpu(log->workloadLogCount)); + printf("%-24s %u\n", "Trigger count:", log->triggeredEvents); + if (wlt->verbose > 1) + printf("%-24s %ld\n", "Poll count:", wlt->poll_count); + if (wlt->poll_count != 0) wltracker_print_field_names(wlt); } +__u64 micros_id(clockid_t clk_id) +{ + struct timespec ts; + __u64 us; + + clock_gettime(clk_id, &ts); + us = (((__u64)ts.tv_sec)*1000000) + (((__u64)ts.tv_nsec)/1000); + return us; +} + +__u64 micros(void) +{ + return micros_id(CLOCK_REALTIME); +} + +int wltracker_config(struct wltracker *wlt, union WorkloadLogEnable *we) +{ + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = wlt->fd, + .fid = FID, + .cdw11 = we->dword, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + }; + + return nvme_set_features(&args); +} + static int wltracker_show_newer_entries(struct wltracker *wlt) { struct workloadLog *log = &wlt->workload_log; __u8 cnt; __u8 content_group; - static __u64 last_timestamp_ms; + static __u64 last_timestamp_us; + __u64 timestamp_us = 0; __u64 timestamp = 0; union WorkloadLogEnable workloadEnable; @@ -281,29 +306,75 @@ static int wltracker_show_newer_entries(struct wltracker *wlt) if (wlt->verbose) wltracker_print_header(wlt); - cnt = log->header.workloadLogCount; - workloadEnable = (union WorkloadLogEnable)log->header.trackerEnable; - content_group = workloadEnable.field.contentGroup; + cnt = log->workloadLogCount; + workloadEnable = log->config; + content_group = workloadEnable.contentGroup; if (cnt == 0) { nvme_show_error("Warning : No valid workload log data\n"); return 0; } - timestamp = (le64_to_cpu(log->header.timestamp_lastEntry) / WLT2MS) - - (log->header.samplePeriodInMilliseconds * (cnt - 1)); + timestamp_us = (le64_to_cpu(log->timestamp_lastEntry) / WLT2US) - + (log->samplePeriodInMilliseconds * 1000 * (cnt - 1)); + timestamp = le64_to_cpu(log->timestamp_lastEntry) - + (log->samplePeriodInMilliseconds * WLT2MS * (cnt - 1)); + if (wlt->poll_count++ == 0) { + __u64 tle = log->timestamp_lastEntry; + __u8 tracker_enable_bit = workloadEnable.trackerEnable; - if (wlt->entry_count == 0) wltracker_print_field_names(wlt); + if (wlt->show_wall_timestamp && + ((log->triggeredEvents && wlt->disable) || !tracker_enable_bit)) { + // retrieve fresh timestamp to reconstruct wall time + union WorkloadLogEnable we = log->config; + + if (wlt->verbose > 1) { + printf("Temporarily enabling tracker to find current timestamp\n"); + printf("Original config value: 0x%08x\n", we.dword); + } + we.trackerEnable = true; + we.triggerEnable = false; + we.sampleTime = 1; + + if (wlt->verbose > 1) + printf("Modified config value: 0x%08x\n", we.dword); + + err = wltracker_config(wlt, &we); + usleep(1000); + if (!err) { + struct workloadLog tl; + + err = nvme_get_log_simple(wlt->fd, LID, sizeof(tl), &tl); + tle = tl.timestamp_lastEntry; + } + if (err) { + nvme_show_error("Failed to retrieve latest SSD timestamp"); + } else { + // Restore original config , but don't reenable trigger + we = log->config; + we.triggerEnable = false; + err = wltracker_config(wlt, &we); + if (wlt->verbose > 1) + printf("Restored config value: 0x%08x\n", + we.dword); + } + } + wlt->us_epoch_ssd_delta = (micros() - le64_to_cpu(tle) / WLT2US); + } + for (int i = cnt - 1; i >= 0; i--) { int offset = 0; __u8 *entry = (__u8 *) &log->entry[i]; - bool is_old = timestamp <= last_timestamp_ms; + // allow 10% sample skew + bool is_old = timestamp_us <= last_timestamp_us + + (log->samplePeriodInMilliseconds * 100); if (is_old) { - timestamp += log->header.samplePeriodInMilliseconds; + timestamp_us += (log->samplePeriodInMilliseconds * 1000); + timestamp += log->samplePeriodInMilliseconds * WLT2MS; continue; } printf("%-16llu", timestamp); @@ -312,8 +383,21 @@ static int wltracker_show_newer_entries(struct wltracker *wlt) struct field f = group_fields[content_group][j]; if (f.size == 0) { + if (wlt->show_wall_timestamp) { + time_t epoch_ts_us = timestamp_us + + wlt->us_epoch_ssd_delta; + time_t ts_s = epoch_ts_us / 1000000; + struct tm ts = *localtime(&ts_s); + char buf[80]; + + strftime(buf, sizeof(buf), "%Y-%m-%d-%H:%M:%S", &ts); + printf("%s.%06" PRIu64 " ", buf, + (uint64_t)(epoch_ts_us % 1000000ULL)); + } + if (wlt->verbose > 1) printf("%-*i", (int)sizeof("entry#"), i); + printf("\n"); break; } @@ -337,28 +421,24 @@ static int wltracker_show_newer_entries(struct wltracker *wlt) printf("%-*u ", (int)strlen(f.name), val); } - wlt->entry_count++; - timestamp += log->header.samplePeriodInMilliseconds; + timestamp_us += (log->samplePeriodInMilliseconds * 1000); + timestamp += log->samplePeriodInMilliseconds * WLT2MS; } - last_timestamp_ms = log->header.timestamp_lastEntry / WLT2MS; + last_timestamp_us = log->timestamp_lastEntry / WLT2US; return 0; } -int wltracker_config(struct wltracker *wlt, union WorkloadLogEnable *we) +void wltracker_run_time_update(struct wltracker *wlt) { - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = wlt->fd, - .fid = FID, - .cdw11 = we->dword, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - }; - - return nvme_set_features(&args); + wlt->run_time_us = micros() - wlt->start_time_us; + if (wlt->verbose > 0) + printf("run_time: %lluus\n", wlt->run_time_us); } static int stricmp(char const *a, char const *b) { + if (!a || !b) + return 1; for (; *a || *b; a++, b++) if (tolower((unsigned char)*a) != tolower((unsigned char)*b)) return 1; @@ -367,14 +447,14 @@ static int stricmp(char const *a, char const *b) static int find_option(char const *list[], int size, const char *val) { - for (int i = 0; i < size; i++) { - if (!stricmp(val, list[i])) - return i; - } - return -EINVAL; + for (int i = 0; i < size; i++) { + if (!stricmp(val, list[i])) + return i; + } + return -EINVAL; } -static void join(char *dest, char const *list[], size_t list_size) +static void join_options(char *dest, char const *list[], size_t list_size) { strcat(dest, list[0]); for (int i = 1; i < list_size; i++) { @@ -383,14 +463,26 @@ static void join(char *dest, char const *list[], size_t list_size) } } -__u64 micros(void) +static int find_field(struct field *fields, const char *val) { - struct timespec ts; - __u64 us; + for (int i = 0; i < MAX_FIELDS; i++) { + if (!stricmp(val, fields[i].name)) + return i; + } + return -EINVAL; +} - clock_gettime(CLOCK_MONOTONIC_RAW, &ts); - us = (((__u64)ts.tv_sec)*1000000) + (((__u64)ts.tv_nsec)/1000); - return us; +static void join_fields(char *dest, struct field *fields) +{ + strcat(dest, fields[0].name); + for (int i = 1; i < MAX_FIELDS; i++) { + char *name = fields[i].name; + + if (name) { + strcat(dest, "|"); + strcat(dest, name); + } + } } int sldgm_get_workload_tracker(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -406,9 +498,7 @@ int sldgm_get_workload_tracker(int argc, char **argv, struct command *cmd, struc "Samples (1 to 126) to wait for extracting data. Default 100 samples"; char type_options[80] = {0}; char sample_options[80] = {0}; - __u64 us_start; - __u64 run_time_us; - __u64 elapsed_run_time_us = 0; + __u64 stop_time_us; __u64 next_sample_us = 0; int opt; int err; @@ -416,29 +506,43 @@ int sldgm_get_workload_tracker(int argc, char **argv, struct command *cmd, struc struct config { bool enable; bool disable; + bool trigger_on_delta; + bool trigger_on_latency; const char *tracker_type; const char *sample_time; - int run_time_s; + __u32 run_time_s; int flush_frequency; + char *trigger_field; + __u32 trigger_treshold; }; struct config cfg = { .sample_time = samplet[0], .flush_frequency = 100, .tracker_type = trk_types[0], + .trigger_field = "", }; - join(type_options, trk_types, ARRAY_SIZE(trk_types)); - join(sample_options, samplet, ARRAY_SIZE(samplet)); + join_options(type_options, trk_types, ARRAY_SIZE(trk_types)); + join_options(sample_options, samplet, ARRAY_SIZE(samplet)); OPT_ARGS(opts) = { OPT_FLAG("enable", 'e', &cfg.enable, "tracker enable"), OPT_FLAG("disable", 'd', &cfg.disable, "tracker disable"), OPT_STRING("sample-time", 's', sample_options, &cfg.sample_time, sample_interval), OPT_STRING("type", 't', type_options, &cfg.tracker_type, "Tracker type"), - OPT_INT("run-time", 'r', &cfg.run_time_s, run_time), + OPT_UINT("run-time", 'r', &cfg.run_time_s, run_time), OPT_INT("flush-freq", 'f', &cfg.flush_frequency, flush_frequency), - OPT_INCR("verbose", 'v', &wlt.verbose, "Increase logging verbosity"), + OPT_FLAG("wall-clock", 'w', &wlt.show_wall_timestamp, + "Logs current wall timestamp when entry was retrieved"), + OPT_STR("trigger-field", 'T', &cfg.trigger_field, "Field name to stop trigger on"), + OPT_UINT("trigger-threshold", 'V', &cfg.trigger_treshold, + "Field value to trigger stop sampling"), + OPT_FLAG("trigger-on-delta", 'D', &cfg.trigger_on_delta, + "Trigger on delta to stop sampling"), + OPT_FLAG("trigger-on-latency", 'L', &cfg.trigger_on_latency, + "Use latency tracker to trigger stop sampling"), + OPT_INCR("verbose", 'v', &wlt.verbose, "Increase logging verbosity"), OPT_END() }; @@ -460,23 +564,59 @@ int sldgm_get_workload_tracker(int argc, char **argv, struct command *cmd, struc cfg.sample_time, sample_options); return -EINVAL; } - we.field.sampleTime = opt; + we.sampleTime = opt; opt = find_option(trk_types, ARRAY_SIZE(trk_types), cfg.tracker_type); if (opt < 0) { - nvme_show_error("Invalid tracker type: %s. Valid types: %s", + nvme_show_error("Invalid tracker type: \"%s\". Valid types: %s", cfg.tracker_type, type_options); return -EINVAL; } - we.field.contentGroup = opt; + we.contentGroup = opt; + if (argconfig_parse_seen(opts, "trigger-field")) { + int field_pos = find_field(group_fields[opt], cfg.trigger_field); + int field_offset = 0; + + if (field_pos < 0) { + char field_options[256]; + + join_fields(field_options, group_fields[opt]); + nvme_show_error( + "Invalid field name: %s. For type: \"%s\", valid fields are: %s", + cfg.trigger_field, cfg.tracker_type, field_options); + return -EINVAL; + } + for (int i = 0; i < field_pos; i++) + field_offset += group_fields[opt][i].size; + + we.triggerDwordIndex += field_offset / 4; + we.triggerSize = + group_fields[opt][field_pos].size == + 4 ? 3 : group_fields[opt][field_pos].size; + we.triggerByteWordIndex = field_offset % 4 / + group_fields[opt][field_pos].size; + we.triggerEnable = true; + we.triggerDelta = cfg.trigger_on_delta; + we.triggerSynchronous = !cfg.trigger_on_latency; + err = nvme_set_features_data(wlt.fd, 0xf5, 0, cfg.trigger_treshold, 0, 0, NULL, + NULL); + if (err < 0) { + nvme_show_error("Trigger Threshold set-feature: %s", nvme_strerror(errno)); + return err; + } else if (err > 0) { + nvme_show_status(err); + return err; + } + } if (cfg.enable && cfg.disable) { nvme_show_error("Can't enable disable simultaneously"); return -EINVAL; } + wlt.disable = cfg.disable; - if (cfg.enable || cfg.disable) { - we.field.trackerEnable = cfg.enable; + if (cfg.enable) { + we.trackerEnable = true; err = wltracker_config(&wlt, &we); if (err < 0) { nvme_show_error("tracker set-feature: %s", nvme_strerror(errno)); @@ -487,17 +627,12 @@ int sldgm_get_workload_tracker(int argc, char **argv, struct command *cmd, struc } } - if (cfg.disable && !cfg.enable) { - printf("Tracker disabled\n"); - return 0; - } - - us_start = micros(); - run_time_us = cfg.run_time_s * 1000000; - while (elapsed_run_time_us < run_time_us) { + wlt.start_time_us = micros(); + stop_time_us = cfg.run_time_s * 1000000; + while (wlt.run_time_us < stop_time_us) { __u64 interval; __u64 elapsed; - __u64 prev_elapsed_run_time_us = elapsed_run_time_us; + __u64 prev_run_time_us = wlt.run_time_us; err = wltracker_show_newer_entries(&wlt); @@ -505,28 +640,50 @@ int sldgm_get_workload_tracker(int argc, char **argv, struct command *cmd, struc nvme_show_status(err); return err; } - interval = ((__u64)wlt.workload_log.header.samplePeriodInMilliseconds) * 1000 * + interval = ((__u64)wlt.workload_log.samplePeriodInMilliseconds) * 1000 * cfg.flush_frequency; next_sample_us += interval; - elapsed_run_time_us = micros() - us_start; - elapsed = elapsed_run_time_us - prev_elapsed_run_time_us; - if (wlt.verbose > 1) - printf("elapsed_run_time: %lluus\n", elapsed_run_time_us); + wltracker_run_time_update(&wlt); + + if (wlt.workload_log.triggeredEvents) + break; + elapsed = wlt.run_time_us - prev_run_time_us; if (interval > elapsed) { - __u64 period_us = min(next_sample_us - elapsed_run_time_us, - run_time_us - elapsed_run_time_us); + __u64 period_us = min(next_sample_us - wlt.run_time_us, + stop_time_us - wlt.run_time_us); if (wlt.verbose > 1) printf("Sleeping %lluus..\n", period_us); usleep(period_us); + wltracker_run_time_update(&wlt); } - elapsed_run_time_us = micros() - us_start; } - err = wltracker_show_newer_entries(&wlt); + if (!wlt.workload_log.triggeredEvents) { + err = wltracker_show_newer_entries(&wlt); + wltracker_run_time_update(&wlt); + } + + if (cfg.disable) { + union WorkloadLogEnable we2 = wlt.workload_log.config; - elapsed_run_time_us = micros() - us_start; - if (wlt.verbose > 0) - printf("elapsed_run_time: %lluus\n", elapsed_run_time_us); + if (wlt.verbose > 1) + printf("Original config value: 0x%08x\n", we2.dword); + + we2.trackerEnable = false; + we2.triggerEnable = false; + err = wltracker_config(&wlt, &we2); + if (err < 0) { + nvme_show_error("tracker set-feature: %s", nvme_strerror(errno)); + return err; + } else if (err > 0) { + nvme_show_status(err); + return err; + } + if (wlt.verbose > 1) + printf("Modified config value: 0x%08x\n", we2.dword); + printf("Tracker disabled\n"); + return 0; + } if (err > 0) { nvme_show_status(err); |