From c940f6abead2ed35504ff0e295c75349276a4b2b Mon Sep 17 00:00:00 2001 From: Ben Cressey Date: Fri, 15 Nov 2024 03:04:38 +0000 Subject: [PATCH] nvme-cli: add patch for EBS stats Signed-off-by: Ben Cressey --- .../0001-plugins-amzn-add-stats-support.patch | 256 ++++++++++++++++++ packages/nvme-cli/nvme-cli.spec | 1 + 2 files changed, 257 insertions(+) create mode 100644 packages/nvme-cli/0001-plugins-amzn-add-stats-support.patch diff --git a/packages/nvme-cli/0001-plugins-amzn-add-stats-support.patch b/packages/nvme-cli/0001-plugins-amzn-add-stats-support.patch new file mode 100644 index 000000000..865c4eab8 --- /dev/null +++ b/packages/nvme-cli/0001-plugins-amzn-add-stats-support.patch @@ -0,0 +1,256 @@ +From 5df679d03106dd793b0d777425696a8b8724b389 Mon Sep 17 00:00:00 2001 +From: Swapnil Dinkar +Date: Thu, 7 Nov 2024 03:32:30 +0000 +Subject: [PATCH] plugins/amzn: add stats support + +this patch adds support to pull stats from amzn ebs nvme devices. + +Signed-off-by: Swapnil Dinkar +--- + plugins/amzn/amzn-nvme.c | 208 +++++++++++++++++++++++++++++++++++++++ + plugins/amzn/amzn-nvme.h | 1 + + 2 files changed, 209 insertions(+) + +diff --git a/plugins/amzn/amzn-nvme.c b/plugins/amzn/amzn-nvme.c +index d359cc34..54afb0dd 100644 +--- a/plugins/amzn/amzn-nvme.c ++++ b/plugins/amzn/amzn-nvme.c +@@ -14,11 +14,54 @@ + #define CREATE_CMD + #include "amzn-nvme.h" + ++#define AMZN_NVME_STATS_LOGPAGE_ID 0xD0 ++#define AMZN_NVME_STATS_MAGIC 0x3C23B510 ++ ++#define array_add_obj json_array_add_value_object ++#define obj_add_array json_object_add_value_array ++#define obj_add_obj json_object_add_value_object ++#define obj_add_uint json_object_add_value_uint ++#define obj_add_uint64 json_object_add_value_uint64 ++ + struct nvme_vu_id_ctrl_field { + __u8 bdev[32]; + __u8 reserved0[992]; + }; + ++struct amzn_latency_histogram_bin { ++ __u64 lower; ++ __u64 upper; ++ __u32 count; ++ __u32 reserved; ++} __packed; ++ ++struct amzn_latency_histogram { ++ __u64 num_bins; ++ struct amzn_latency_histogram_bin bins[64]; ++} __packed; ++ ++struct amzn_latency_log_page { ++ __u32 magic; ++ __u32 reserved0; ++ __u64 total_read_ops; ++ __u64 total_write_ops; ++ __u64 total_read_bytes; ++ __u64 total_write_bytes; ++ __u64 total_read_time; ++ __u64 total_write_time; ++ __u64 ebs_volume_performance_exceeded_iops; ++ __u64 ebs_volume_performance_exceeded_tp; ++ __u64 ec2_instance_ebs_performance_exceeded_iops; ++ __u64 ec2_instance_ebs_performance_exceeded_tp; ++ __u64 volume_queue_length; ++ __u8 reserved1[416]; ++ ++ struct amzn_latency_histogram read_io_latency_histogram; ++ struct amzn_latency_histogram write_io_latency_histogram; ++ ++ __u8 reserved2[496]; ++} __packed; ++ + static void json_amzn_id_ctrl(struct nvme_vu_id_ctrl_field *id, + char *bdev, + struct json_object *root) +@@ -52,3 +95,168 @@ static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *pl + { + return __id_ctrl(argc, argv, cmd, plugin, amzn_id_ctrl); + } ++ ++static void amzn_print_latency_histogram(struct amzn_latency_histogram *hist) ++{ ++ printf("=================================\n"); ++ printf("Lower Upper IO Count\n"); ++ printf("=================================\n"); ++ ++ for (int b = 0; b < hist->num_bins && b < 64; b++) { ++ struct amzn_latency_histogram_bin *bin = &hist->bins[b]; ++ ++ printf("[%-8llu - %-8llu] => %-8u\n", ++ bin->lower, bin->upper, bin->count); ++ } ++ ++ printf("=================================\n\n"); ++} ++ ++static void amzn_json_add_histogram(struct json_object *root, ++ struct amzn_latency_histogram *hist) ++{ ++ struct json_object *bins = json_create_array(); ++ ++ obj_add_uint64(root, "num_bins", hist->num_bins); ++ obj_add_array(root, "bins", bins); ++ ++ for (int b = 0; b < hist->num_bins && b < 64; b++) { ++ struct amzn_latency_histogram_bin *bin = &hist->bins[b]; ++ struct json_object *json_bin = json_create_object(); ++ ++ obj_add_uint64(json_bin, "lower", bin->lower); ++ obj_add_uint64(json_bin, "upper", bin->upper); ++ obj_add_uint(json_bin, "count", bin->count); ++ ++ array_add_obj(bins, json_bin); ++ } ++} ++ ++static void amzn_print_json_stats(struct amzn_latency_log_page *log) ++{ ++ struct json_object *root = json_create_object(); ++ struct json_object *r_hist = json_create_object(); ++ struct json_object *w_hist = json_create_object(); ++ ++ obj_add_uint64(root, "total_read_ops", log->total_read_ops); ++ obj_add_uint64(root, "total_write_ops", log->total_write_ops); ++ obj_add_uint64(root, "total_read_bytes", log->total_read_bytes); ++ obj_add_uint64(root, "total_write_bytes", log->total_write_bytes); ++ obj_add_uint64(root, "total_read_time", log->total_read_time); ++ obj_add_uint64(root, "total_write_time", log->total_write_time); ++ obj_add_uint64(root, "ebs_volume_performance_exceeded_iops", ++ log->ebs_volume_performance_exceeded_iops); ++ obj_add_uint64(root, "ebs_volume_performance_exceeded_tp", ++ log->ebs_volume_performance_exceeded_tp); ++ obj_add_uint64(root, ++ "ec2_instance_ebs_performance_exceeded_iops", ++ log->ec2_instance_ebs_performance_exceeded_iops); ++ obj_add_uint64(root, "ec2_instance_ebs_performance_exceeded_tp", ++ log->ec2_instance_ebs_performance_exceeded_tp); ++ obj_add_uint64(root, "volume_queue_length", log->volume_queue_length); ++ ++ amzn_json_add_histogram(r_hist, &log->read_io_latency_histogram); ++ obj_add_obj(root, "read_io_latency_histogram", r_hist); ++ amzn_json_add_histogram(w_hist, &log->write_io_latency_histogram); ++ obj_add_obj(root, "write_io_latency_histogram", w_hist); ++ ++ json_print_object(root, NULL); ++ printf("\n"); ++ ++ json_free_object(root); ++} ++ ++static void amzn_print_normal_stats(struct amzn_latency_log_page *log) ++{ ++ printf("Total Ops:\n"); ++ printf(" Read: %llu\n", log->total_read_ops); ++ printf(" Write: %llu\n", log->total_write_ops); ++ printf("Total Bytes:\n"); ++ printf(" Read: %llu\n", log->total_read_bytes); ++ printf(" Write: %llu\n", log->total_write_bytes); ++ printf("Total Time (us):\n"); ++ printf(" Read: %llu\n", log->total_read_time); ++ printf(" Write: %llu\n\n", log->total_write_time); ++ ++ printf("EBS Volume Performance Exceeded (us):\n"); ++ printf(" IOPS: %llu\n", log->ebs_volume_performance_exceeded_iops); ++ printf(" Throughput: %llu\n\n", ++ log->ebs_volume_performance_exceeded_tp); ++ printf("EC2 Instance EBS Performance Exceeded (us):\n"); ++ printf(" IOPS: %llu\n", ++ log->ec2_instance_ebs_performance_exceeded_iops); ++ printf(" Throughput: %llu\n\n", ++ log->ec2_instance_ebs_performance_exceeded_tp); ++ ++ printf("Queue Length (point in time): %llu\n\n", ++ log->volume_queue_length); ++ ++ printf("Read IO Latency Histogram\n"); ++ amzn_print_latency_histogram(&log->read_io_latency_histogram); ++ ++ printf("Write IO Latency Histogram\n"); ++ amzn_print_latency_histogram(&log->write_io_latency_histogram); ++} ++ ++static int get_stats(int argc, char **argv, struct command *cmd, ++ struct plugin *plugin) ++{ ++ const char *desc = "display command latency statistics"; ++ struct nvme_dev *dev; ++ struct amzn_latency_log_page log = { 0 }; ++ int rc; ++ ++ struct config { ++ char *output_format; ++ }; ++ ++ struct config cfg = { ++ .output_format = "normal", ++ }; ++ ++ OPT_ARGS(opts) = { ++ OPT_FMT("output-format", 'o', &cfg.output_format, ++ "Output Format: normal|json"), ++ OPT_END()}; ++ ++ rc = parse_and_open(&dev, argc, argv, desc, opts); ++ if (rc) ++ return rc; ++ ++ struct nvme_get_log_args args = { ++ .args_size = sizeof(args), ++ .fd = dev_fd(dev), ++ .lid = AMZN_NVME_STATS_LOGPAGE_ID, ++ .nsid = 1, ++ .lpo = 0, ++ .lsp = NVME_LOG_LSP_NONE, ++ .lsi = 0, ++ .rae = false, ++ .uuidx = 0, ++ .csi = NVME_CSI_NVM, ++ .ot = false, ++ .len = sizeof(log), ++ .log = &log, ++ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, ++ .result = NULL, ++ }; ++ ++ rc = nvme_get_log(&args); ++ if (rc != 0) { ++ fprintf(stderr, "[ERROR] %s: Failed to get log page, rc = %d", ++ __func__, rc); ++ return rc; ++ } ++ ++ if (log.magic != AMZN_NVME_STATS_MAGIC) { ++ fprintf(stderr, "[ERROR] %s: Not an EBS device", __func__); ++ return -ENOTSUP; ++ } ++ ++ if (!strcmp(cfg.output_format, "json")) ++ amzn_print_json_stats(&log); ++ else ++ amzn_print_normal_stats(&log); ++ ++ return 0; ++} +diff --git a/plugins/amzn/amzn-nvme.h b/plugins/amzn/amzn-nvme.h +index f6c4f8bb..19f209bd 100644 +--- a/plugins/amzn/amzn-nvme.h ++++ b/plugins/amzn/amzn-nvme.h +@@ -10,6 +10,7 @@ + PLUGIN(NAME("amzn", "Amazon vendor specific extensions", NVME_VERSION), + COMMAND_LIST( + ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) ++ ENTRY("stats", "Get EBS volume stats", get_stats) + ) + ); + +-- +2.47.0 + diff --git a/packages/nvme-cli/nvme-cli.spec b/packages/nvme-cli/nvme-cli.spec index f32bf03f8..0c0794e4d 100644 --- a/packages/nvme-cli/nvme-cli.spec +++ b/packages/nvme-cli/nvme-cli.spec @@ -6,6 +6,7 @@ Summary: CLI to interact with NVMe devices License: LGPL-2.1-only AND GPL-2.0-only AND CC0-1.0 AND MIT URL: /~https://github.com/linux-nvme/nvme-cli Source0: /~https://github.com/linux-nvme/nvme-cli/archive/v%{version}/nvme-cli-%{version}.tar.gz +Patch1: 0001-plugins-amzn-add-stats-support.patch BuildRequires: meson BuildRequires: %{_cross_os}glibc-devel