From: Swapnil Dinkar Date: Thu, 7 Nov 2024 03:32:30 +0000 (+0000) Subject: plugins/amzn: add stats support X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=5df679d03106dd793b0d777425696a8b8724b389;p=users%2Fsagi%2Fnvme-cli.git plugins/amzn: add stats support this patch adds support to pull stats from amzn ebs nvme devices. Signed-off-by: Swapnil Dinkar --- 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) ) );