From dae7a5cc23e8d54049271253cd592ac089ebf155 Mon Sep 17 00:00:00 2001 From: Jeff Lien Date: Wed, 29 Sep 2021 09:50:47 -0500 Subject: [PATCH] ZNS report zones command fix for large number of zones When there are a large number of zones, the buffer required to contain the data becomes extremely large and the linux os is unable to handle that on all systems. This change will break up the transfer of the zone data into smaller chunks - 1024 zone entries at a time and 64k bytes total. Signed-off-by: Jeff Lien [dwagner: ported from nvme-cli-monolithic] Signed-off-by: Daniel Wagner --- nvme-print.c | 1 - plugins/zns/zns.c | 108 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 85 insertions(+), 24 deletions(-) diff --git a/nvme-print.c b/nvme-print.c index 5e867257..1c059827 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -4546,7 +4546,6 @@ void nvme_show_zns_report_zones(void *report, __u32 descs, return json_nvme_zns_report_zones(report, descs, ext_size, report_size, nr_zones); - printf("nr_zones: %"PRIu64"\n", (uint64_t)le64_to_cpu(r->nr_zones)); for (i = 0; i < descs; i++) { desc = (struct nvme_zns_desc *) (report + sizeof(*r) + i * (sizeof(*desc) + ext_size)); diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index de576099..dc5f319c 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -670,6 +670,18 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi __u32 report_size; void *report; bool huge = false; + struct nvme_zone_report *buff; + + unsigned int nr_zones_chunks = 1024, /* 1024 entries * 64 bytes per entry = 64k byte transfer */ + nr_zones_retrieved = 0, + nr_zones, + offset, + log_len; + int total_nr_zones = 0; + struct nvme_zns_id_ns id_zns; + struct nvme_id_ns id_ns; + uint8_t lbaf; + __le64 zsze; struct config { char *output_format; @@ -725,23 +737,53 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi } } - if (cfg.num_descs == -1) { - struct nvme_zone_report r; + err = nvme_identify_ns(fd, cfg.namespace_id, &id_ns); + if (err) { + nvme_show_status(err); + goto close_fd; + } - err = nvme_zns_report_zones(fd, cfg.namespace_id, 0, - 0, cfg.state, 0, sizeof(r), &r); - if (err > 0) { - nvme_show_status(err); - goto close_fd; - } else if (err < 0) { - perror("zns report-zones"); - goto close_fd; - } - cfg.num_descs = le64_to_cpu(r.nr_zones); + err = nvme_zns_identify_ns(fd, cfg.namespace_id, &id_zns); + if (!err) { + /* get zsze field from zns id ns data - needed for offset calculation */ + lbaf = id_ns.flbas & NVME_NS_FLBAS_LBA_MASK; + zsze = le64_to_cpu(id_zns.lbafe[lbaf].zsze); + } + else { + nvme_show_status(err); + goto close_fd; } - report_size = sizeof(struct nvme_zone_report) + cfg.num_descs * - (sizeof(struct nvme_zns_desc) + zdes); + log_len = sizeof(struct nvme_zone_report); + buff = calloc(1, log_len); + if (!buff) { + err = -ENOMEM; + goto close_fd; + } + + err = nvme_zns_report_zones(fd, cfg.namespace_id, 0, + 0, cfg.state, 0, log_len, buff); + if (err > 0) { + nvme_show_status(err); + goto free_buff; + } + else if (err < 0) { + perror("zns report-zones"); + goto free_buff; + } + + total_nr_zones = le64_to_cpu(buff->nr_zones); + + if (cfg.num_descs == -1) { + cfg.num_descs = total_nr_zones; + } + + nr_zones = cfg.num_descs; + if (nr_zones < nr_zones_chunks) + nr_zones_chunks = nr_zones; + + log_len = sizeof(struct nvme_zone_report) + ((sizeof(struct nvme_zns_desc) * nr_zones_chunks) + (nr_zones_chunks * zdes)); + report_size = log_len; report = nvme_alloc(report_size, &huge); if (!report) { @@ -750,17 +792,37 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi goto close_fd; } - err = nvme_zns_report_zones(fd, cfg.namespace_id, cfg.zslba, - cfg.extended, cfg.state, cfg.partial, report_size, report); - if (!err) - nvme_show_zns_report_zones(report, cfg.num_descs, zdes, - report_size, flags); - else if (err > 0) - nvme_show_status(err); - else - perror("zns report-zones"); + offset = cfg.zslba; + printf("nr_zones: %"PRIu64"\n", (uint64_t)le64_to_cpu(total_nr_zones)); + + while (nr_zones_retrieved < nr_zones) { + if (nr_zones_retrieved >= nr_zones) + break; + + if (nr_zones_retrieved + nr_zones_chunks > nr_zones) { + nr_zones_chunks = nr_zones - nr_zones_retrieved; + log_len = sizeof(struct nvme_zone_report) + ((sizeof(struct nvme_zns_desc) * nr_zones_chunks) + (nr_zones_chunks * zdes)); + } + + err = nvme_zns_report_zones(fd, cfg.namespace_id, offset, + cfg.extended, cfg.state, cfg.partial, log_len, report); + if (err > 0) { + nvme_show_status(err); + break; + } + + if (!err) + nvme_show_zns_report_zones(report, nr_zones_chunks, zdes, + log_len, flags); + + nr_zones_retrieved += nr_zones_chunks; + offset = (nr_zones_retrieved * zsze); + } nvme_free(report, huge); + +free_buff: + free(buff); close_fd: close(fd); return nvme_status_to_errno(err, false); -- 2.50.1