diff options
Diffstat (limited to 'src/sg_rep_zones.c')
-rw-r--r-- | src/sg_rep_zones.c | 412 |
1 files changed, 373 insertions, 39 deletions
diff --git a/src/sg_rep_zones.c b/src/sg_rep_zones.c index 90d744a4..5329c20e 100644 --- a/src/sg_rep_zones.c +++ b/src/sg_rep_zones.c @@ -14,6 +14,7 @@ #include <stdarg.h> #include <stdbool.h> #include <string.h> +#include <limits.h> #include <errno.h> #include <ctype.h> #include <getopt.h> @@ -39,22 +40,25 @@ * Based on zbc2r10.pdf */ -static const char * version_str = "1.32 20220218"; +static const char * version_str = "1.33 20220224"; #define WILD_RZONES_BUFF_LEN (1 << 28) #define MAX_RZONES_BUFF_LEN (2 * 1024 * 1024) #define DEF_RZONES_BUFF_LEN (1024 * 16) +#define RCAP16_REPLY_LEN 32 #define SG_ZONING_IN_CMDLEN 16 - -#define REPORT_ZONES_SA 0x0 -#define REPORT_ZONE_DOMAINS_SA 0x7 -#define REPORT_REALMS_SA 0x6 - #define REPORT_ZONES_DESC_LEN 64 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ #define DEF_PT_TIMEOUT 60 /* 60 seconds */ +/* Three zone service actions supported by this utility */ +enum zone_report_sa_e { + REPORT_ZONES_SA = 0x0, + REPORT_REALMS_SA = 0x6, + REPORT_ZONE_DOMAINS_SA = 0x7 +}; + struct opts_t { bool do_brief; bool do_force; @@ -64,9 +68,11 @@ struct opts_t { bool do_zdomains; bool maxlen_given; bool o_readonly; + bool statistics; bool verbose_given; bool version_given; bool wp_only; + enum zone_report_sa_e serv_act; int do_help; int do_hex; int do_num; @@ -103,6 +109,8 @@ static struct option long_options[] = { {"realms", no_argument, 0, 'e'}, {"report", required_argument, 0, 'o'}, {"start", required_argument, 0, 's'}, + {"statistics", no_argument, 0, 'S'}, + {"stats", no_argument, 0, 'S'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"wp", no_argument, 0, 'w'}, @@ -188,6 +196,7 @@ usage(int h) "zones)\n" " --start=LBA|-s LBA report zones from the LBA (def: 0)\n" " need not be a zone starting LBA\n" + " --statistics gather statistics by reviewing zones\n" " --verbose|-v increase verbosity\n" " --version|-V print version string and exit\n" " --wp|-w output write pointer only\n\n" @@ -237,8 +246,8 @@ h_twoormore: * (see ZBC and ZBC-2). Return of 0 -> success, various SG_LIB_CAT_* positive * values or -1 -> other errors */ static int -sg_ll_report_zzz(int sg_fd, int serv_act, uint64_t zs_lba, bool partial, - int report_opts, void * resp, int mx_resp_len, +sg_ll_report_zzz(int sg_fd, enum zone_report_sa_e serv_act, uint64_t zs_lba, + bool partial, int report_opts, void * resp, int mx_resp_len, int * residp, bool noisy, int vb) { int ret, res, sense_cat; @@ -248,7 +257,7 @@ sg_ll_report_zzz(int sg_fd, int serv_act, uint64_t zs_lba, bool partial, uint8_t sense_b[SENSE_BUFF_LEN] = {0}; struct sg_pt_base * ptvp; - rz_cdb[1] = serv_act; + rz_cdb[1] = (uint8_t)serv_act; sg_put_unaligned_be64(zs_lba, rz_cdb + 2); sg_put_unaligned_be32((uint32_t)mx_resp_len, rz_cdb + 10); rz_cdb[14] = report_opts & 0x3f; @@ -607,35 +616,40 @@ find_report_zones(int sg_fd, uint8_t * rzBuff, const char * cmd_name, { bool found = false; uint8_t zt; - int k, res, resid, rlen, num_zd; + int k, res, resid, rlen, num_zd, num_rem; uint32_t zn_dnum = 0; uint64_t slba = op->st_lba; uint64_t mx_lba = 0; const uint8_t * bp = rzBuff; char b[96]; - while (true) { + num_rem = op->do_num ? op->do_num : INT_MAX; + for ( ; num_rem > 0; num_rem -= num_zd) { resid = 0; - res = sg_ll_report_zzz(sg_fd, REPORT_ZONES_SA, slba, - true /* set partial */, op->reporting_opt, - rzBuff, op->maxlen, &resid, true, op->vb); - if (res) { - if (SG_LIB_CAT_INVALID_OP == res) - pr2serr("%s: %s%u, %s command not supported\n", __func__, - zn_dnum_s, zn_dnum, cmd_name); - else { - sg_get_category_sense_str(res, sizeof(b), b, op->vb); - pr2serr("%s: %s%u, %s command: %s\n", __func__, - zn_dnum_s, zn_dnum, cmd_name, b); + if (sg_fd >= 0) { + res = sg_ll_report_zzz(sg_fd, REPORT_ZONES_SA, slba, + true /* set partial */, op->reporting_opt, + rzBuff, op->maxlen, &resid, true, op->vb); + if (res) { + if (SG_LIB_CAT_INVALID_OP == res) + pr2serr("%s: %s%u, %s command not supported\n", __func__, + zn_dnum_s, zn_dnum, cmd_name); + else { + sg_get_category_sense_str(res, sizeof(b), b, op->vb); + pr2serr("%s: %s%u, %s command: %s\n", __func__, + zn_dnum_s, zn_dnum, cmd_name, b); + } + break; } - break; - } + } else + res = 0; rlen = op->maxlen - resid; - if (rlen <= 64) { + if (rlen <= 64) break; - } mx_lba = sg_get_unaligned_be64(rzBuff + 8); num_zd = (rlen - 64) / REPORT_ZONES_DESC_LEN; + if (num_zd > num_rem) + num_zd = num_rem; for (k = 0, bp = rzBuff + 64; k < num_zd; ++k, bp += REPORT_ZONES_DESC_LEN, ++zn_dnum) { zt = 0xf & bp[0]; @@ -652,9 +666,9 @@ find_report_zones(int sg_fd, uint8_t * rzBuff, const char * cmd_name, if (k < num_zd) { found = true; break; - } else if (slba > mx_lba) + } else if ((slba > mx_lba) || (sg_fd < 0)) break; - } + } /* end of outer for loop */ if (res == 0) { if (found) { if (op->do_hex) { @@ -670,13 +684,328 @@ find_report_zones(int sg_fd, uint8_t * rzBuff, const char * cmd_name, if (op->do_hex) { memset(b, 0xff, 64); hex2stdout((const uint8_t *)b, 64, -1); - } else + } else if (num_rem < 1) + printf("Condition NOT met, checked %d zones; next %s%u\n", + op->do_num, zn_dnum_s, zn_dnum); + else printf("Condition NOT met; next %s%u\n", zn_dnum_s, zn_dnum); } } return res; } +struct statistics_t { + uint32_t zt_conv_num; + uint32_t zt_swr_num; + uint32_t zt_swp_num; + uint32_t zt_sob_num; + uint32_t zt_gap_num; + uint32_t zt_unk_num; + + uint32_t zc_nwp_num; + uint32_t zc_mt_num; + uint32_t zc_iop_num; + uint32_t zc_eop_num; + uint32_t zc_cl_num; + uint32_t zc_ina_num; + uint32_t zc_ro_num; + uint32_t zc_full_num; + uint32_t zc_off_num; + uint32_t zc_unk_num; + + /* The following LBAs have 1 added to them, initialized to 0 */ + uint64_t zt_swr_1st_lba1; + uint64_t zt_swp_1st_lba1; + uint64_t zt_sob_1st_lba1; + uint64_t zt_gap_1st_lba1; + + uint64_t zc_nwp_1st_lba1; + uint64_t zc_mt_1st_lba1; + uint64_t zc_iop_1st_lba1; + uint64_t zc_eop_1st_lba1; + uint64_t zc_cl_1st_lba1; + uint64_t zc_ina_1st_lba1; + uint64_t zc_ro_1st_lba1; + uint64_t zc_full_1st_lba1; + uint64_t zc_off_1st_lba1; + + uint64_t wp_max_lba1; /* ... that isn't Zone start LBA */ + uint64_t wp_blk_num; /* sum of (zwp - zs_lba) */ +}; + +static int +gather_statistics(int sg_fd, uint8_t * rzBuff, const char * cmd_name, + struct opts_t * op) +{ + uint8_t zt, zc; + int k, res, resid, rlen, num_zd, num_rem; + uint32_t zn_dnum = 0; + uint64_t slba = op->st_lba; + uint64_t mx_lba = 0; + uint64_t zs_lba, zwp, z_blks; + const uint8_t * bp = rzBuff; + struct statistics_t st = {0}; + char b[96]; + + if (op->serv_act != REPORT_ZONES_SA) { + pr2serr("%s: do not support statistics for %s yet\n", __func__, + cmd_name); + return SG_LIB_SYNTAX_ERROR; + } + + num_rem = op->do_num ? op->do_num : INT_MAX; + for ( ; num_rem > 0; num_rem -= num_zd) { + resid = 0; + zs_lba = slba; + if (sg_fd >= 0) { + res = sg_ll_report_zzz(sg_fd, REPORT_ZONES_SA, slba, + true /* set partial */, op->reporting_opt, + rzBuff, op->maxlen, &resid, true, op->vb); + if (res) { + if (SG_LIB_CAT_INVALID_OP == res) + pr2serr("%s: %s%u, %s command not supported\n", __func__, + zn_dnum_s, zn_dnum, cmd_name); + else { + sg_get_category_sense_str(res, sizeof(b), b, op->vb); + pr2serr("%s: %s%u, %s command: %s\n", __func__, + zn_dnum_s, zn_dnum, cmd_name, b); + } + break; + } + } else + res = 0; + rlen = op->maxlen - resid; + if (rlen <= 64) { + break; + } + mx_lba = sg_get_unaligned_be64(rzBuff + 8); + num_zd = (rlen - 64) / REPORT_ZONES_DESC_LEN; + if (num_zd > num_rem) + num_zd = num_rem; + for (k = 0, bp = rzBuff + 64; k < num_zd; + ++k, bp += REPORT_ZONES_DESC_LEN, ++zn_dnum) { + z_blks = sg_get_unaligned_be64(bp + 8); + zs_lba = sg_get_unaligned_be64(bp + 16); + zwp = sg_get_unaligned_be64(bp + 24); + zt = 0xf & bp[0]; + switch (zt) { + case 1: + ++st.zt_conv_num; + break; + case 2: + ++st.zt_swr_num; + if (0 == st.zt_swr_1st_lba1) + st.zt_swr_1st_lba1 = zs_lba + 1; + break; + case 3: + ++st.zt_swp_num; + if (0 == st.zt_swp_1st_lba1) + st.zt_swp_1st_lba1 = zs_lba + 1; + break; + case 4: + ++st.zt_sob_num; + if (0 == st.zt_sob_1st_lba1) + st.zt_sob_1st_lba1 = zs_lba + 1; + break; + case 5: + ++st.zt_gap_num; + if (0 == st.zt_gap_1st_lba1) + st.zt_gap_1st_lba1 = zs_lba + 1; + break; + default: + ++st.zt_unk_num; + break; + } + zc = (bp[1] >> 4) & 0xf; + switch (zc) { + case 0: /* not write pointer (zone) */ + ++st.zc_nwp_num; + if (0 == st.zc_nwp_1st_lba1) + st.zc_nwp_1st_lba1 = zs_lba + 1; + break; + case 1: /* empty */ + ++st.zc_mt_num; + if (0 == st.zc_mt_1st_lba1) + st.zc_mt_1st_lba1 = zs_lba + 1; + break; + case 2: /* implicitly opened */ + ++st.zc_iop_num; + if (0 == st.zc_iop_1st_lba1) + st.zc_iop_1st_lba1 = zs_lba + 1; + if (zwp > zs_lba) { + st.wp_max_lba1 = zwp + 1; + st.wp_blk_num += zwp - zs_lba; + } + break; + case 3: /* explicitly opened */ + ++st.zc_eop_num; + if (0 == st.zc_eop_1st_lba1) + st.zc_eop_1st_lba1 = zs_lba + 1; + if (zwp > zs_lba) { + st.wp_max_lba1 = zwp + 1; + st.wp_blk_num += zwp - zs_lba; + } + break; + case 4: /* closed */ + ++st.zc_cl_num; + if (0 == st.zc_cl_1st_lba1) + st.zc_cl_1st_lba1 = zs_lba + 1; + if (zwp > zs_lba) { + st.wp_max_lba1 = zwp + 1; + st.wp_blk_num += zwp - zs_lba; + } + break; + case 5: + ++st.zc_ina_num; + if (0 == st.zc_ina_1st_lba1) + st.zc_ina_1st_lba1 = zs_lba + 1; + break; + case 0xd: + ++st.zc_ro_num; + if (0 == st.zc_ro_1st_lba1) + st.zc_ro_1st_lba1 = zs_lba + 1; + break; + case 0xe: + ++st.zc_full_num; + if (0 == st.zc_full_1st_lba1) + st.zc_full_1st_lba1 = zs_lba + 1; + st.wp_blk_num += z_blks; + break; + case 0xf: + ++st.zc_off_num; + if (0 == st.zc_off_1st_lba1) + st.zc_off_1st_lba1 = zs_lba + 1; + break; + default: + ++st.zc_unk_num; + break; + } + slba = zs_lba + z_blks; + } /* end of inner for loop */ + if ((slba > mx_lba) || (sg_fd < 0)) + break; + } /* end of outer for loop */ + printf("Number of conventional type zones: %u\n", st.zt_conv_num); + if (st.zt_swr_num > 0) + printf("Number of sequential write required type zones: %u\n", + st.zt_swr_num); + if (st.zt_swr_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zt_swr_1st_lba1 - 1); + if (st.zt_swp_num > 0) + printf("Number of sequential write preferred type zones: %u\n", + st.zt_swp_num); + if (st.zt_swp_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zt_swp_1st_lba1 - 1); + if (st.zt_sob_num > 0) + printf("Number of sequential or before type zones: %u\n", + st.zt_sob_num); + if (st.zt_sob_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zt_sob_1st_lba1 - 1); + if (st.zt_gap_num > 0) + printf("Number of gap type zones: %u\n", st.zt_gap_num); + if (st.zt_gap_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zt_gap_1st_lba1 - 1); + if (st.zt_unk_num > 0) + printf("Number of unknown type zones: %u\n", st.zt_unk_num); + + printf("Number of 'not write pointer' condition zones: %u\n", + st.zc_nwp_num); + if (st.zc_nwp_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_nwp_1st_lba1 - 1); + printf("Number of empty condition zones: %u\n", st.zc_mt_num); + if (st.zc_mt_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_mt_1st_lba1 - 1); + if (st.zc_iop_num > 0) + printf("Number of implicitly open condition zones: %u\n", + st.zc_iop_num); + if (st.zc_iop_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_iop_1st_lba1 - 1); + if (st.zc_eop_num) + printf("Number of explicitly open condition zones: %u\n", + st.zc_eop_num); + if (st.zc_eop_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_eop_1st_lba1 - 1); + if (st.zc_cl_num) + printf("Number of closed condition zones: %u\n", st.zc_cl_num); + if (st.zc_cl_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_cl_1st_lba1 - 1); + if (st.zc_ina_num) + printf("Number of inactive condition zones: %u\n", st.zc_ina_num); + if (st.zc_ina_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_ina_1st_lba1 - 1); + if (st.zc_ro_num) + printf("Number of inactive condition zones: %u\n", st.zc_ro_num); + if (st.zc_ro_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_ro_1st_lba1 - 1); + if (st.zc_full_num) + printf("Number of full condition zones: %u\n", st.zc_full_num); + if (st.zc_full_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_full_1st_lba1 - 1); + if (st.zc_off_num) + printf("Number of offline condition zones: %u\n", st.zc_off_num); + if (st.zc_off_1st_lba1 > 0) + printf(" Lowest starting LBA: 0x%" PRIx64 "\n", + st.zc_off_1st_lba1 - 1); + if (st.zc_unk_num > 0) + printf("Number of unknown condition zones: %u\n", st.zc_unk_num); + + if (st.wp_max_lba1 > 0) + printf("Highest active write pointer LBA: 0x%" PRIx64 "\n", + st.wp_max_lba1 - 1); + printf("Number of used blocks in write pointer zones: 0x%" PRIx64 "\n", + st.wp_blk_num); + + if ((sg_fd >= 0) && (op->maxlen >= RCAP16_REPLY_LEN) && + (st.wp_blk_num > 0)) { + res = sg_ll_readcap_16(sg_fd, false, 0, rzBuff, + RCAP16_REPLY_LEN, true, op->vb); + if (SG_LIB_CAT_INVALID_OP == res) { + pr2serr("READ CAPACITY (16) cdb not supported\n"); + } else if (SG_LIB_CAT_ILLEGAL_REQ == res) + pr2serr("bad field in READ CAPACITY (16) cdb including " + "unsupported service action\n"); + else if (res) { + sg_get_category_sense_str(res, sizeof(b), b, op->vb); + pr2serr("READ CAPACITY (16) failed: %s\n", b); + } else { + uint32_t block_size = sg_get_unaligned_be32(rzBuff + 8); + uint64_t total_sz = st.wp_blk_num * block_size; + double sz_mb, sz_gb; + + sz_mb = (double)(total_sz) / (double)(1048576); + sz_gb = (double)(total_sz) / (double)(1000000000L); +#ifdef SG_LIB_MINGW + printf(" associated size: %" PRIu64 " bytes, %g MiB, %g GB", + total_sz, sz_mb, sz_gb); +#else + printf(" associated size: %" PRIu64 " bytes, %.1f MiB, %.2f " + "GB", total_sz, sz_mb, sz_gb); +#endif + if (sz_gb > 2000) { +#ifdef SG_LIB_MINGW + printf(", %g TB", sz_gb / 1000); +#else + printf(", %.2f TB", sz_gb / 1000); +#endif + } + printf("\n"); + } + } + return res; +} + int main(int argc, char * argv[]) @@ -686,7 +1015,6 @@ main(int argc, char * argv[]) int sg_fd = -1; int resid = 0; int ret = 0; - int serv_act = REPORT_ZONES_SA; uint32_t decod_len; int64_t ll; const char * device_name = NULL; @@ -694,14 +1022,14 @@ main(int argc, char * argv[]) uint8_t * free_rzbp = NULL; const char * cmd_name = "Report zones"; char b[80]; - struct opts_t opts; + struct opts_t opts = {0}; struct opts_t * op = &opts; - memset(&opts, 0, sizeof(opts)); + op->serv_act = REPORT_ZONES_SA; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "bdefF:hHi:l:m:n:o:prRs:vVw", + c = getopt_long(argc, argv, "bdefF:hHi:l:m:n:o:prRs:SvVw", long_options, &option_index); if (c == -1) break; @@ -712,11 +1040,11 @@ main(int argc, char * argv[]) break; case 'd': op->do_zdomains = true; - serv_act = REPORT_ZONE_DOMAINS_SA; + op->serv_act = REPORT_ZONE_DOMAINS_SA; break; case 'e': op->do_realms = true; - serv_act = REPORT_REALMS_SA; + op->serv_act = REPORT_REALMS_SA; break; case 'f': op->do_force = true; @@ -803,6 +1131,9 @@ main(int argc, char * argv[]) } op->st_lba = (uint64_t)ll; break; + case 'S': + op->statistics = true; + break; case 'v': op->verbose_given = true; ++op->vb; @@ -863,7 +1194,7 @@ main(int argc, char * argv[]) cmd_name = "Report zone domains"; else if (op->do_realms) cmd_name = "Report realms"; - if ((serv_act != REPORT_ZONES_SA) && op->do_partial) { + if ((op->serv_act != REPORT_ZONES_SA) && op->do_partial) { pr2serr("Can only use --partial with REPORT ZONES\n"); return SG_LIB_SYNTAX_ERROR; } @@ -932,8 +1263,11 @@ main(int argc, char * argv[]) if (op->find_zt) { /* so '-F none' will drop through */ ret = find_report_zones(sg_fd, rzBuff, cmd_name, op); goto the_end; + } else if (op->statistics) { + ret = gather_statistics(sg_fd, rzBuff, cmd_name, op); + goto the_end; } - res = sg_ll_report_zzz(sg_fd, serv_act, op->st_lba, op->do_partial, + res = sg_ll_report_zzz(sg_fd, op->serv_act, op->st_lba, op->do_partial, op->reporting_opt, rzBuff, op->maxlen, &resid, true, op->vb); ret = res; @@ -954,7 +1288,7 @@ start_response: } } if (decod_len > (uint32_t)rlen) { - if ((REPORT_ZONES_SA == serv_act) && (! op->do_partial)) { + if ((REPORT_ZONES_SA == op->serv_act) && (! op->do_partial)) { printf("%u zones starting from LBA 0x%" PRIx64 " available " "but only %d zones returned\n", (decod_len - 64) / REPORT_ZONES_DESC_LEN, op->st_lba, @@ -990,7 +1324,7 @@ start_response: ret = SG_LIB_CAT_MALFORMED; goto the_end; } - if (REPORT_ZONES_SA == serv_act) + if (REPORT_ZONES_SA == op->serv_act) ret = decode_rep_zones(rzBuff, act_len, decod_len, op); else if (op->do_realms) ret = decode_rep_realms(rzBuff, act_len, op); |