summaryrefslogtreecommitdiffstats
path: root/plugins/solidigm
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/solidigm')
-rw-r--r--plugins/solidigm/solidigm-internal-logs.c70
-rw-r--r--plugins/solidigm/solidigm-nvme.h2
-rw-r--r--plugins/solidigm/solidigm-telemetry.c13
-rw-r--r--plugins/solidigm/solidigm-util.c16
-rw-r--r--plugins/solidigm/solidigm-util.h5
-rw-r--r--plugins/solidigm/solidigm-workload-tracker.c365
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);