aboutsummaryrefslogtreecommitdiff
path: root/testing
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2020-07-24 18:56:21 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2020-07-24 18:56:21 +0000
commit985b5cda4750a8a033ebb4162cbb65f1eaed93ba (patch)
tree0f0d306533dee63d5d7b9fb498a394fff3dcb0eb /testing
parent5d21382d1a81a0aa3da2c27235171a8b88357c00 (diff)
downloadsg3_utils-985b5cda4750a8a033ebb4162cbb65f1eaed93ba.tar.gz
sg_turs: estimated time-to-ready, add --delay=MS option; sg_requests: cleanup; sg_pt: add partial_clear_scsi_pt_obj, get_scsi_pt_cdb_len and get_scsi_pt_cdb_buf
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@857 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'testing')
-rw-r--r--testing/Makefile2
-rw-r--r--testing/sg_mrq_dd.cpp1175
-rw-r--r--testing/sg_scat_gath.cpp1029
-rw-r--r--testing/sg_scat_gath.h141
-rw-r--r--testing/sg_tst_ioctl.c153
-rw-r--r--testing/sgh_dd.cpp4
6 files changed, 1316 insertions, 1188 deletions
diff --git a/testing/Makefile b/testing/Makefile
index dedf23a6..16de2c39 100644
--- a/testing/Makefile
+++ b/testing/Makefile
@@ -109,7 +109,7 @@ sg_tst_async: sg_tst_async.o $(LIBFILESNEW)
sgh_dd: sgh_dd.o $(LIBFILESNEW)
$(CXXLD) -o $@ $(LDFLAGS) -pthread -latomic $^
-sg_mrq_dd: sg_mrq_dd.o $(LIBFILESNEW)
+sg_mrq_dd: sg_mrq_dd.o sg_scat_gath.o $(LIBFILESNEW)
$(CXXLD) -o $@ $(LDFLAGS) -pthread -latomic $^
diff --git a/testing/sg_mrq_dd.cpp b/testing/sg_mrq_dd.cpp
index 900f45dc..83d921a2 100644
--- a/testing/sg_mrq_dd.cpp
+++ b/testing/sg_mrq_dd.cpp
@@ -25,18 +25,12 @@
*
* This version is designed for the linux kernel 4 and 5 series.
*
- * sgp_dd is a Posix threads specialization of the sg_dd utility. Both
- * sgp_dd and sg_dd only perform special tasks when one or both of the given
- * devices belong to the Linux sg driver.
+ * sg_mrq_dd uses C++ threads and MRQ (multiple requests (in one invocation))
+ * facilities in the sg version 4 driver to do "dd" type copies and verifies.
*
- * sgh_dd further extends sgp_dd to use the experimental kernel buffer
- * sharing feature added in 3.9.02 .
- * N.B. This utility was previously called sgs_dd but there was already an
- * archived version of a dd variant called sgs_dd so this utility name was
- * renamed [20181221]
*/
-static const char * version_str = "1.03 20200716";
+static const char * version_str = "1.04 20200720";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -103,6 +97,9 @@ static const char * version_str = "1.03 20200716";
#define __user
#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
+// C++ local header
+#include "sg_scat_gath.h"
+
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_io_linux.h"
@@ -112,11 +109,11 @@ static const char * version_str = "1.03 20200716";
using namespace std;
-#ifdef __GNUC__
-#ifndef __clang__
-#pragma GCC diagnostic ignored "-Wclobbered"
-#endif
-#endif
+// #ifdef __GNUC__
+// #ifndef __clang__
+// #pragma GCC diagnostic ignored "-Wclobbered"
+// #endif
+// #endif
#define MAX_SGL_NUM_VAL (INT32_MAX - 1) /* should reduce for testing */
@@ -169,127 +166,6 @@ using namespace std;
#define PROC_SCSI_SG_VERSION "/proc/scsi/sg/version"
#define SYS_SCSI_SG_VERSION "/sys/module/sg/version"
-#define SG_SGL_MAX_ELEMENTS 16384
-
-#define SG_COUNT_INDEFINITE (-1)
-#define SG_LBA_INVALID SG_COUNT_INDEFINITE
-
-/* Sizing matches largest SCSI READ and WRITE commands plus those of Unix
- * read(2)s and write(2)s. User can give larger than 31 bit 'num's but they
- * are split into several consecutive elements. */
-struct scat_gath_elem {
- uint64_t lba; /* of start block */
- uint32_t num; /* number of blocks from and including start block */
-
- void make_bad() { lba = UINT64_MAX; num = UINT32_MAX; }
- bool is_bad() const { return (lba == UINT64_MAX && num == UINT32_MAX); }
-};
-
-/* Consider "linearity" as a scatter gather list property. Elements of this
- * of from the strongest form to the weakest. */
-enum sgl_linearity_e {
- SGL_LINEAR = 0, /* empty list and 0,0 considered linear */
- SGL_MONOTONIC, /* since not linear, implies holes */
- SGL_MONO_OVERLAP, /* monotonic but same LBA in two or more elements */
- SGL_NON_MONOTONIC /* weakest */
-};
-
-
-/* Holds one scatter gather list and its associated metadata */
-class scat_gath_list {
-public:
- scat_gath_list() : linearity(SGL_LINEAR), sum_hard(false), m_errno(0),
- high_lba_p1(0), lowest_lba(0), sum(0) { }
-
- scat_gath_list(const scat_gath_list &) = default;
- scat_gath_list & operator=(const scat_gath_list &) = default;
- ~scat_gath_list() = default;
-
- bool empty() const;
- bool empty_or_00() const;
- int num_elems() const;
- int64_t get_lowest_lba(bool ignore_degen, bool always_last) const;
- int64_t get_low_lba_from_linear() const;
- bool is_pipe_suitable() const;
-
- friend bool sgls_eq_off(const scat_gath_list &left, int l_e_ind,
- int l_blk_off,
- const scat_gath_list &right, int r_e_ind,
- int r_blk_off, bool allow_partial);
-
- bool load_from_cli(const char * cl_p, bool b_vb);
- bool load_from_file(const char * file_name, bool def_hex, bool flexible,
- bool b_vb);
- int append_1or(int64_t extra_blks, int64_t start_lba);
- int append_1or(int64_t extra_blks);
-
- void dbg_print(bool skip_meta, const char * id_str, bool to_stdout,
- bool show_sgl, bool lock = true) const;
-
- /* calculates and sets following bool-s and int64_t-s */
- void sum_scan(const char * id_str, bool show_sgl, bool b_verbose);
-
- void set_weaker_linearity(enum sgl_linearity_e lin);
- enum sgl_linearity_e linearity;
- const char * linearity_as_str() const;
-
- bool sum_hard; /* 'num' in last element of 'sgl' is > 0 */
- int m_errno; /* OS failure errno */
- int64_t high_lba_p1; /* highest LBA plus 1, next write from and above */
- int64_t lowest_lba; /* initialized to 0 */
- int64_t sum; /* of all 'num' elements in 'sgl' */
-
- friend int diff_between_iters(const struct scat_gath_iter & left,
- const struct scat_gath_iter & right);
-
-private:
- friend class scat_gath_iter;
-
- bool file2sgl_helper(FILE * fp, const char * fnp, bool def_hex,
- bool flexible, bool b_vb);
-
- vector<scat_gath_elem> sgl; /* an array on heap [0..num_elems()) */
-};
-
-
-class scat_gath_iter {
-public:
- scat_gath_iter(const scat_gath_list & my_scat_gath_list);
- scat_gath_iter(const scat_gath_iter & src) = default;
- scat_gath_iter& operator=(const scat_gath_iter&) = delete;
- ~scat_gath_iter() = default;
-
- int64_t current_lba() const;
- int64_t current_lba_rem_num(int & rem_num) const;
- struct scat_gath_elem current_elem() const;
- bool at_end() const;
- bool is_sgl_linear() const; /* the whole list */
- int linear_for_n_blks(int max_n) const;
-
- bool set_by_blk_idx(int64_t _blk_idx);
- /* add/sub blocks return true if they reach EOL, else false */
- bool add_blks(uint64_t blk_count);
- bool sub_blks(uint64_t blk_count);
-
- void dbg_print(const char * id_str, bool to_stdout, int verbose) const;
-
- friend int diff_between_iters(const struct scat_gath_iter & left,
- const struct scat_gath_iter & right);
-
- friend bool sgls_eq_from_iters(const struct scat_gath_iter & left,
- const struct scat_gath_iter & right,
- bool allow_partial);
-
-private:
- const scat_gath_list &sglist;
-
- /* dual representation: either it_el_ind,it_blk_off or blk_idx */
- int it_el_ind; /* refers to sge==sglist[it_el_ind] */
- int it_blk_off; /* refers to LBA==(sge.lba + it_blk_off) */
- int64_t blk_idx; /* in range: [0 .. sglist.sum) */
- bool extend_last;
-};
-
struct flags_t {
bool append;
@@ -846,10 +722,14 @@ print_stats(const char * str)
pr2serr("%s%" PRId64 "+%d records in\n", str,
infull, gcoll.in_partial.load());
- outfull = gcoll.dd_count - gcoll.out_rem_count.load();
- pr2serr("%s%" PRId64 "+%d records %s\n", str,
- outfull, gcoll.out_partial.load(),
- (gcoll.verify ? "verified" : "out"));
+ if (gcoll.out_type == FT_DEV_NULL)
+ pr2serr("%s0+0 records out\n", str);
+ else {
+ outfull = gcoll.dd_count - gcoll.out_rem_count.load();
+ pr2serr("%s%" PRId64 "+%d records %s\n", str,
+ outfull, gcoll.out_partial.load(),
+ (gcoll.verify ? "verified" : "out"));
+ }
}
static void
@@ -1134,1009 +1014,6 @@ page4:
}
-bool
-scat_gath_list::empty() const
-{
- return sgl.empty();
-}
-
-bool
-scat_gath_list::empty_or_00() const
-{
- if (sgl.empty())
- return true;
- return ((sgl.size() == 1) && (sgl[0].lba == 0) && (sgl[0].num == 0));
-}
-
-int
-scat_gath_list::num_elems() const
-{
- return sgl.size();
-}
-
-
-/* Read numbers (up to 64 bits in size) from command line (comma (or
- * (single) space **) separated list). Assumed decimal unless prefixed
- * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
- * Returns 0 if ok, or 1 if error. Assumed to be LBA (64 bit) and
- * number_of_block (32 bit) pairs. ** Space on command line needs to
- * be escaped, otherwise it is an operand/option separator. */
-bool
-scat_gath_list::load_from_cli(const char * cl_p, bool b_vb)
-{
- bool split, full_pair;
- int in_len, k, j;
- const int max_nbs = MAX_SGL_NUM_VAL;
- int64_t ll, large_num;
- uint64_t prev_lba;
- char * cp;
- char * c2p;
- const char * lcp;
- struct scat_gath_elem sge;
-
- if (NULL == cl_p) {
- pr2serr("%s: bad arguments\n", __func__);
- goto err_out;
- }
- lcp = cl_p;
- in_len = strlen(cl_p);
- if ('-' == cl_p[0]) { /* read from stdin */
- pr2serr("%s: logic error: no stdin here\n", __func__);
- goto err_out;
- } else { /* list of numbers (default decimal) on command line */
- k = strspn(cl_p, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP, ");
- if (in_len != k) {
- if (b_vb)
- pr2serr("%s: error at pos %d\n", __func__, k + 1);
- goto err_out;
- }
- j = 0;
- full_pair = true;
- for (k = 0, split = false; ; ++k) {
- if (split) {
- /* splitting given elem with large number_of_blocks into
- * multiple elems within array being built */
- ++j;
- sge.lba = prev_lba + (uint64_t)max_nbs;
- if (large_num > max_nbs) {
- sge.num = (uint32_t)max_nbs;
- prev_lba = sge.lba;
- large_num -= max_nbs;
- sgl.push_back(sge);
- } else {
- sge.num = (uint32_t)large_num;
- split = false;
- if (b_vb)
- pr2serr("%s: split large sg elem into %d element%s\n",
- __func__, j, (j == 1 ? "" : "s"));
- sgl.push_back(sge);
- goto check_for_next;
- }
- continue;
- }
- full_pair = false;
- ll = sg_get_llnum(lcp);
- if (-1 != ll) {
- sge.lba = (uint64_t)ll;
- cp = (char *)strchr(lcp, ',');
- c2p = (char *)strchr(lcp, ' ');
- if (NULL == cp) {
- cp = c2p;
- if (NULL == cp)
- break;
- }
- if (c2p && (c2p < cp))
- cp = c2p;
- lcp = cp + 1;
- } else {
- if (b_vb)
- pr2serr("%s: error at pos %d\n", __func__,
- (int)(lcp - cl_p + 1));
- goto err_out;
- }
- ll = sg_get_llnum(lcp);
- if (ll >= 0) {
- full_pair = true;
- if (ll > max_nbs) {
- sge.num = (uint32_t)max_nbs;
- prev_lba = sge.lba;
- large_num = ll - max_nbs;
- split = true;
- j = 1;
- continue;
- }
- sge.num = (uint32_t)ll;
- } else { /* bad or negative number as number_of_blocks */
- if (b_vb)
- pr2serr("%s: bad number at pos %d\n", __func__,
- (int)(lcp - cl_p + 1));
- goto err_out;
- }
- sgl.push_back(sge);
-check_for_next:
- cp = (char *)strchr(lcp, ',');
- c2p = (char *)strchr(lcp, ' ');
- if (NULL == cp) {
- cp = c2p;
- if (NULL == cp)
- break;
- }
- if (c2p && (c2p < cp))
- cp = c2p;
- lcp = cp + 1;
- } /* end of for loop over items in operand */
- /* other than first pair, expect even number of items */
- if ((k > 0) && (! full_pair)) {
- if (b_vb)
- pr2serr("%s: expected even number of items: "
- "LBA0,NUM0,LBA1,NUM1...\n", __func__);
- goto err_out;
- }
- }
- return true;
-err_out:
- if (0 == m_errno)
- m_errno = SG_LIB_SYNTAX_ERROR;
- return false;
-}
-
-bool
-scat_gath_list::file2sgl_helper(FILE * fp, const char * fnp, bool def_hex,
- bool flexible, bool b_vb)
-{
- bool bit0;
- bool pre_addr1 = true;
- bool pre_hex_seen = false;
- int in_len, k, j, m, ind;
- const int max_nbs = MAX_SGL_NUM_VAL;
- int off = 0;
- int64_t ll;
- uint64_t ull, prev_lba;
- char * lcp;
- struct scat_gath_elem sge;
- char line[1024];
-
- for (j = 0 ; ; ++j) {
- if (NULL == fgets(line, sizeof(line), fp))
- break;
- // could improve with carry_over logic if sizeof(line) too small
- in_len = strlen(line);
- if (in_len > 0) {
- if ('\n' == line[in_len - 1]) {
- --in_len;
- line[in_len] = '\0';
- } else {
- m_errno = SG_LIB_SYNTAX_ERROR;
- if (b_vb)
- pr2serr("%s: %s: line too long, max %d bytes\n",
- __func__, fnp, (int)(sizeof(line) - 1));
- goto err_out;
- }
- }
- if (in_len < 1)
- continue;
- lcp = line;
- m = strspn(lcp, " \t");
- if (m == in_len)
- continue;
- lcp += m;
- in_len -= m;
- if ('#' == *lcp)
- continue;
- if (pre_addr1 || pre_hex_seen) {
- /* Accept lines with leading 'HEX' and ignore as long as there
- * is one _before_ any LBA,NUM lines in the file. This allows
- * HEX marked sgls to be concaternated together. */
- if (('H' == toupper(lcp[0])) && ('E' == toupper(lcp[1])) &&
- ('X' == toupper(lcp[2]))) {
- pre_hex_seen = true;
- if (def_hex)
- continue; /* bypass 'HEX' marker line if expecting hex */
- else {
- if (flexible) {
- def_hex = true; /* okay, switch to hex parse */
- continue;
- } else {
- pr2serr("%s: %s: 'hex' string detected on line %d, "
- "expecting decimal\n", __func__, fnp, j + 1);
- m_errno = EINVAL;
- goto err_out;
- }
- }
- }
- }
- k = strspn(lcp, "0123456789aAbBcCdDeEfFhHxXbBdDiIkKmMgGtTpP, \t");
- if ((k < in_len) && ('#' != lcp[k])) {
- m_errno = EINVAL;
- if (b_vb)
- pr2serr("%s: %s: syntax error at line %d, pos %d\n",
- __func__, fnp, j + 1, m + k + 1);
- goto err_out;
- }
- for (k = 0; k < 256; ++k) {
- /* limit parseable items on one line to 256 */
- if (def_hex) { /* don't accept negatives or multipliers */
- if (1 == sscanf(lcp, "%" SCNx64, &ull))
- ll = (int64_t)ull;
- else
- ll = -1; /* use (2**64 - 1) as error flag */
- } else
- ll = sg_get_llnum(lcp);
- if (-1 != ll) {
- ind = ((off + k) >> 1);
- bit0 = !! (0x1 & (off + k));
- if (ind >= SG_SGL_MAX_ELEMENTS) {
- m_errno = EINVAL;
- if (b_vb)
- pr2serr("%s: %s: array length exceeded\n", __func__,
- fnp);
- goto err_out;
- }
- if (bit0) { /* bit0 set when decoding a NUM */
- if (ll < 0) {
- m_errno = EINVAL;
- if (b_vb)
- pr2serr("%s: %s: bad number in line %d, at pos "
- "%d\n", __func__, fnp, j + 1,
- (int)(lcp - line + 1));
- goto err_out;
- }
- if (ll > max_nbs) {
- int h = 1;
-
- /* split up this elem into multiple, smaller elems */
- do {
- sge.num = (uint32_t)max_nbs;
- prev_lba = sge.lba;
- sgl.push_back(sge);
- sge.lba = prev_lba + (uint64_t)max_nbs;
- ++h;
- off += 2;
- ll -= max_nbs;
- } while (ll > max_nbs);
- if (b_vb)
- pr2serr("%s: split large sg elem into %d "
- "elements\n", __func__, h);
- }
- sge.num = (uint32_t)ll;
- sgl.push_back(sge);
- } else { /* bit0 clear when decoding a LBA */
- if (pre_addr1)
- pre_addr1 = false;
- sge.lba = (uint64_t)ll;
- }
- } else { /* failed to decode number on line */
- if ('#' == *lcp) { /* numbers before #, rest of line comment */
- --k;
- break; /* goes to next line */
- }
- m_errno = EINVAL;
- if (b_vb)
- pr2serr("%s: %s: error in line %d, at pos %d\n",
- __func__, fnp, j + 1, (int)(lcp - line + 1));
- goto err_out;
- }
- lcp = strpbrk(lcp, " ,\t#");
- if ((NULL == lcp) || ('#' == *lcp))
- break;
- lcp += strspn(lcp, " ,\t");
- if ('\0' == *lcp)
- break;
- } /* <<< end of for(k < 256) loop */
- off += (k + 1);
- } /* <<< end of for loop, one iteration per line */
- /* allow one items, but not higher odd number of items */
- if ((off > 1) && (0x1 & off)) {
- m_errno = EINVAL;
- if (b_vb)
- pr2serr("%s: %s: expect even number of items: "
- "LBA0,NUM0,LBA1,NUM1...\n", __func__, fnp);
- goto err_out;
- }
- clearerr(fp); /* even EOF on first pass needs this before rescan */
- return true;
-err_out:
- clearerr(fp);
- return false;
-}
-
-/* Read numbers from filename (or stdin), line by line (comma (or (single)
- * space) separated list); places starting_LBA,number_of_block pairs in an
- * array of scat_gath_elem elements pointed to by the returned value. If
- * this fails NULL is returned and an error number is written to errp (if it
- * is non-NULL). Assumed decimal (and may have suffix multipliers) when
- * def_hex==false; if a number is prefixed by '0x', '0X' or contains trailing
- * 'h' or 'H' that denotes a hex number. When def_hex==true all numbers are
- * assumed to be hex (ignored '0x' prefixes and 'h' suffixes) and multiplers
- * are not permitted. Heap allocates an array just big enough to hold all
- * elements if the file is countable. Pipes and stdin are not considered
- * countable. In the non-countable case an array of MAX_FIXED_SGL_ELEMS
- * elements is pre-allocated; if it is exceeded sg_convert_errno(EDOM) is
- * placed in *errp (if it is non-NULL). One of the first actions is to write
- * 0 to *errp (if it is non-NULL) so the caller does not need to zero it
- * before calling. */
-bool
-scat_gath_list::load_from_file(const char * file_name, bool def_hex,
- bool flexible, bool b_vb)
-{
- bool have_stdin;
- bool have_err = false;
- FILE * fp;
- const char * fnp;
-
- have_stdin = ((1 == strlen(file_name)) && ('-' == file_name[0]));
- if (have_stdin) {
- fp = stdin;
- fnp = "<stdin>";
- } else {
- fnp = file_name;
- fp = fopen(fnp, "r");
- if (NULL == fp) {
- m_errno = errno;
- if (b_vb)
- pr2serr("%s: opening %s: %s\n", __func__, fnp,
- safe_strerror(m_errno));
- return false;
- }
- }
- if (! file2sgl_helper(fp, fnp, def_hex, flexible, b_vb))
- have_err = true;
- if (! have_stdin)
- fclose(fp);
- return have_err ? false : true;
-}
-
-const char *
-scat_gath_list::linearity_as_str() const
-{
- switch (linearity) {
- case SGL_LINEAR:
- return "linear";
- case SGL_MONOTONIC:
- return "monotonic";
- case SGL_MONO_OVERLAP:
- return "monotonic, overlapping";
- case SGL_NON_MONOTONIC:
- return "non-monotonic";
- default:
- return "unknown";
- }
-}
-
-void
-scat_gath_list::set_weaker_linearity(enum sgl_linearity_e lin)
-{
- int i_lin = (int)lin;
-
- if (i_lin > (int)linearity)
- linearity = lin;
-}
-
-/* id_str may be NULL (if so replace by "unknown"), present to enhance verbose
- * output. */
-void
-scat_gath_list::dbg_print(bool skip_meta, const char * id_str, bool to_stdout,
- bool show_sgl, bool lock) const
-{
- int k;
- if (lock)
- strerr_mut.lock();
- int num = sgl.size();
- const char * caller = id_str ? id_str : "unknown";
- FILE * fp = to_stdout ? stdout : stderr;
-
- if (! skip_meta) {
- fprintf(fp, "%s: elems=%d, sgl %spresent, linearity=%s\n",
- caller, num, (sgl.empty() ? "not " : ""),
- linearity_as_str());
- fprintf(fp, " sum=%" PRId64 ", sum_hard=%s lowest=0x%" PRIx64
- ", high_lba_p1=", sum, (sum_hard ? "true" : "false"),
- lowest_lba);
- fprintf(fp, "0x%" PRIx64 "\n", high_lba_p1);
- }
- fprintf(fp, " >> %s scatter gather list (%d element%s):\n", caller, num,
- (num == 1 ? "" : "s"));
- if (show_sgl) {
- for (k = 0; k < num; ++k) {
- const struct scat_gath_elem & sge = sgl[k];
-
- fprintf(fp, " lba: 0x%" PRIx64 ", number: 0x%" PRIx32,
- sge.lba, sge.num);
- if (sge.lba > 0)
- fprintf(fp, " [next lba: 0x%" PRIx64 "]", sge.lba + sge.num);
- fprintf(fp, "\n");
- }
- }
- if (lock)
- strerr_mut.unlock();
-}
-
-/* Assumes sgl array (vector) is setup. The other fields in this object are
- * set by analyzing sgl in a single pass. The fields that are set are:
- * fragmented, lowest_lba, high_lba_p1, monotonic, overlapping, sum and
- * sum_hard. Degenerate elements (i.e. those with 0 blocks) are ignored apart
- * from when one is last which makes sum_hard false and its LBA becomes
- * high_lba_p1 if it is the highest in the list. An empty sgl is equivalent
- * to a 1 element list with [0, 0], so sum_hard==false, monit==true,
- * fragmented==false and overlapping==false . id_str may be NULL, present
- * to enhance verbose output. */
-void
-scat_gath_list::sum_scan(const char * id_str, bool show_sgl, bool b_vb)
-{
- bool degen = false;
- bool first = true;
- bool regular = true; /* no overlapping segments detected */
- int k;
- int elems = sgl.size();
- uint32_t prev_num, t_num;
- uint64_t prev_lba, t_lba, low, high, end;
-
- sum = 0;
- for (k = 0, low = 0, high = 0; k < elems; ++k) {
- const struct scat_gath_elem & sge = sgl[k];
-
- degen = false;
- t_num = sge.num;
- if (0 == t_num) {
- degen = true;
- if (! first)
- continue; /* ignore degen element that not first */
- }
- if (first) {
- low = sge.lba;
- sum = t_num;
- high = sge.lba + sge.num;
- first = false;
- } else {
- t_lba = sge.lba;
- if ((prev_lba + prev_num) != t_lba)
- set_weaker_linearity(SGL_MONOTONIC);
- sum += t_num;
- end = t_lba + t_num;
- if (end > high)
- high = end; /* high is one plus highest LBA */
- if (prev_lba < t_lba)
- ;
- else if (prev_lba == t_lba) {
- if (prev_num > 0) {
- set_weaker_linearity(SGL_MONO_OVERLAP);
- break;
- }
- } else {
- low = t_lba;
- set_weaker_linearity(SGL_NON_MONOTONIC);
- break;
- }
- if (regular) {
- if ((prev_lba + prev_num) > t_lba)
- regular = false;
- }
- }
- prev_lba = sge.lba;
- prev_num = sge.num;
- } /* end of for loop while still elements and monot true */
-
- if (k < elems) { /* only here if above breaks are taken */
- prev_lba = t_lba;
- ++k;
- for ( ; k < elems; ++k) {
- const struct scat_gath_elem & sge = sgl[k];
-
- degen = false;
- t_lba = sge.lba;
- t_num = sge.num;
- if (0 == t_num) {
- degen = true;
- continue;
- }
- sum += t_num;
- end = t_lba + t_num;
- if (end > high)
- high = end;
- if (prev_lba > t_lba) {
- if (t_lba < low)
- low = t_lba;
- }
- prev_lba = t_lba;
- }
- } else
- if (! regular)
- set_weaker_linearity(SGL_MONO_OVERLAP);
-
- lowest_lba = low;
- if (degen && (elems > 0)) { /* last element always impacts high_lba_p1 */
- t_lba = sgl[elems - 1].lba;
- high_lba_p1 = (t_lba > high) ? t_lba : high;
- } else
- high_lba_p1 = high;
- sum_hard = (elems > 0) ? ! degen : false;
- if (b_vb)
- dbg_print(false, id_str, false, show_sgl);
-}
-
-/* Usually will append (or add to start if empty) sge unless 'extra_blks'
- * exceeds MAX_SGL_NUM_VAL. In that case multiple sge_s are added with
- * sge.num = MAX_SGL_NUM_VAL or less (for final sge) until extra_blks is
- * exhausted. Returns new size of scatter gather list. */
-int
-scat_gath_list::append_1or(int64_t extra_blks, int64_t start_lba)
-{
- int o_num = sgl.size();
- const int max_nbs = MAX_SGL_NUM_VAL;
- int64_t cnt = 0;
- struct scat_gath_elem sge;
-
- if ((extra_blks <= 0) || (start_lba < 0))
- return o_num; /* nothing to do */
- if ((o_num > 0) && (! sum_hard)) {
- sge = sgl[o_num - 1]; /* assume sge.num==0 */
- if (sge.lba == (uint64_t)start_lba) {
- if (extra_blks <= max_nbs)
- sge.num = extra_blks;
- else
- sge.num = max_nbs;
- sgl[o_num - 1] = sge;
- cnt = sge.num;
- sum += cnt;
- sum_hard = true;
- if (cnt <= extra_blks) {
- high_lba_p1 = sge.lba + cnt;
- return o_num;
- }
- }
- } else if (0 == o_num)
- lowest_lba = start_lba;
-
- for ( ; cnt < extra_blks; cnt += max_nbs) {
- sge.lba = start_lba + cnt;
- if ((extra_blks - cnt) <= max_nbs)
- sge.num = extra_blks - cnt;
- else
- sge.num = max_nbs;
- sgl.push_back(sge);
- sum += sge.num;
- } /* always loops at least once */
- sum_hard = true;
- high_lba_p1 = sge.lba + sge.num;
- return sgl.size();
-}
-
-int
-scat_gath_list::append_1or(int64_t extra_blks)
-{
- int o_num = sgl.size();
- if (o_num < 1)
- return append_1or(extra_blks, 0);
-
- struct scat_gath_elem sge = sgl[o_num - 1];
- return append_1or(extra_blks, sge.lba + sge.num);
-}
-
-bool
-sgls_eq_off(const scat_gath_list & left, int l_e_ind, int l_blk_off,
- const scat_gath_list & right, int r_e_ind, int r_blk_off,
- bool allow_partial)
-{
- int lrem, rrem;
- int lelems = left.sgl.size();
- int relems = right.sgl.size();
-
- while ((l_e_ind < lelems) && (r_e_ind < relems)) {
- if ((left.sgl[l_e_ind].lba + l_blk_off) !=
- (right.sgl[r_e_ind].lba + r_blk_off))
- return false;
- lrem = left.sgl[l_e_ind].num - l_blk_off;
- rrem = right.sgl[r_e_ind].num - r_blk_off;
- if (lrem == rrem) {
- ++l_e_ind;
- l_blk_off = 0;
- ++r_e_ind;
- r_blk_off = 0;
- } else if (lrem < rrem) {
- ++l_e_ind;
- l_blk_off = 0;
- r_blk_off += lrem;
- } else {
- ++r_e_ind;
- r_blk_off = 0;
- l_blk_off += rrem;
- }
- }
- if ((l_e_ind >= lelems) && (r_e_ind >= relems))
- return true;
- return allow_partial;
-}
-
-/* If bad arguments returns -1, otherwise returns the lowest LBA in *sglp .
- * If no elements considered returns 0. If ignore_degen is true than
- * ignores all elements with sge.num zero unless always_last is also
- * true in which case the last element is always considered. */
-int64_t
-scat_gath_list::get_lowest_lba(bool ignore_degen, bool always_last) const
-{
- int k;
- const int num_elems = sgl.size();
- bool some = (num_elems > 0);
- int64_t res = INT64_MAX;
-
- for (k = 0; k < num_elems; ++k) {
- if ((0 == sgl[k].num) && ignore_degen)
- continue;
- if ((int64_t)sgl[k].lba < res)
- res = sgl[k].lba;
- }
- if (always_last && some) {
- if ((int64_t)sgl[k - 1].lba < res)
- res = sgl[k - 1].lba;
- }
- return (INT64_MAX == res) ? 0 : res;
-}
-
-/* Returns >= 0 if sgl can be simplified to a single LBA. So an empty sgl
- * will return 0; a one element sgl will return its LBA. A multiple element
- * sgl only returns the first element's LBA (that is not degenerate) if the
- * sgl is monotonic and not fragmented. In the extreme case takes last
- * element's LBA if all prior elements are degenerate. Else returns -1 .
- * Assumes sgl_sum_scan() has been called. */
-int64_t
-scat_gath_list::get_low_lba_from_linear() const
-{
- const int num_elems = sgl.size();
- int k;
-
- if (num_elems <= 1)
- return (1 == num_elems) ? sgl[0].lba : 0;
- else {
- if (linearity == SGL_LINEAR) {
- for (k = 0; k < (num_elems - 1); ++k) {
- if (sgl[k].num > 0)
- return sgl[k].lba;
- }
- /* take last element's LBA if all earlier are degenerate */
- return sgl[k].lba;
- } else
- return -1;
- }
-}
-
-bool
-scat_gath_list::is_pipe_suitable() const
-{
- return (lowest_lba == 0) && (linearity == SGL_LINEAR);
-}
-
-scat_gath_iter::scat_gath_iter(const scat_gath_list & parent)
- : sglist(parent), it_el_ind(0), it_blk_off(0), blk_idx(0)
-{
- int elems = sglist.num_elems();
-
- if (elems > 0)
- extend_last = (0 == sglist.sgl[elems - 1].num);
-}
-
-bool
-scat_gath_iter::set_by_blk_idx(int64_t _blk_idx)
-{
- bool first;
- int k;
- const int elems = sglist.sgl.size();
- const int last_ind = elems - 1;
- uint32_t num;
- int64_t bc = _blk_idx;
-
- if (bc < 0)
- return false;
-
- if (bc == blk_idx)
- return true;
- else if (bc > blk_idx) {
- k = it_el_ind;
- bc -= blk_idx;
- } else
- k = 0;
- for (first = true; k < elems; ++k, first = false) {
- num = ((k == last_ind) && extend_last) ? MAX_SGL_NUM_VAL :
- sglist.sgl[k].num;
- if (first) {
- if ((int64_t)(num - it_blk_off) < bc)
- bc -= (num - it_blk_off);
- else {
- it_blk_off = bc + it_blk_off;
- break;
- }
- } else {
- if ((int64_t)num < bc)
- bc -= num;
- else {
- it_blk_off = (uint32_t)bc;
- break;
- }
- }
- }
- it_el_ind = k;
- blk_idx = _blk_idx;
-
- if (k < elems)
- return true;
- else if ((k == elems) && (0 == it_blk_off))
- return true; /* EOL */
- else
- return false;
-}
-
-/* Given a blk_count, the iterator (*iter_p) is moved toward the EOL.
- * Returns true unless blk_count takes iterator two or more past the last
- * element. So if blk_count takes the iterator to the EOL, this function
- * returns true. Takes into account iterator's extend_last flag. */
-bool
-scat_gath_iter::add_blks(uint64_t blk_count)
-{
- bool first;
- int k;
- const int elems = sglist.sgl.size();
- const int last_ind = elems - 1;
- uint32_t num;
- uint64_t bc = blk_count;
-
- if (0 == bc)
- return true;
- for (first = true, k = it_el_ind; k < elems; ++k, first = false) {
- num = ((k == last_ind) && extend_last) ? MAX_SGL_NUM_VAL :
- sglist.sgl[k].num;
- if (first) {
- if ((uint64_t)(num - it_blk_off) < bc)
- bc -= (num - it_blk_off);
- else {
- it_blk_off = bc + it_blk_off;
- break;
- }
- } else {
- if ((uint64_t)num < bc)
- bc -= num;
- else {
- it_blk_off = (uint32_t)bc;
- break;
- }
- }
- }
- it_el_ind = k;
- blk_idx += blk_count;
-
- if (k < elems)
- return true;
- else if ((k == elems) && (0 == it_blk_off))
- return true; /* EOL */
- else
- return false;
-}
-
-/* Move the iterator from its current position (which may be to EOL) towards
- * the start of the sgl (i.e. backwards) for blk_count blocks. Returns true
- * if iterator is valid after the move, else returns false. N.B. if false is
- * returned, then the iterator is invalid and may need to set it to a valid
- * value. */
-bool
-scat_gath_iter::sub_blks(uint64_t blk_count)
-{
- bool first;
- int k = it_el_ind;
- uint64_t bc = 0;
- const uint64_t orig_blk_count = blk_count;
-
- if (0 == blk_count)
- return true;
- for (first = true; k >= 0; --k) {
- if (first) {
- if (blk_count > (uint64_t)it_blk_off)
- blk_count -= it_blk_off;
- else {
- it_blk_off -= blk_count;
- break;
- }
- first = false;
- } else {
- uint32_t off = sglist.sgl[k].num;
-
- bc = blk_count;
- if (bc > (uint64_t)off)
- blk_count -= off;
- else {
- bc = off - bc;
- break;
- }
- }
- }
- if (k < 0) {
- blk_idx = 0;
- return false; /* bad situation */
- }
- if ((int64_t)orig_blk_count <= blk_idx)
- blk_idx -= orig_blk_count;
- else
- blk_idx = 0;
- it_el_ind = k;
- if (! first)
- it_blk_off = (uint32_t)bc;
- return true;
-}
-
-/* Returns LBA referred to by iterator if valid or returns SG_LBA_INVALID
- * (-1) if at end or invalid. */
-int64_t
-scat_gath_iter::current_lba() const
-{
- const int elems = sglist.sgl.size();
- int64_t res = SG_LBA_INVALID; /* for at end or invalid (-1) */
-
- if (it_el_ind < elems) {
- struct scat_gath_elem sge = sglist.sgl[it_el_ind];
-
- if ((uint32_t)it_blk_off < sge.num)
- return sge.lba + it_blk_off;
- else if (((uint32_t)it_blk_off == sge.num) &&
- ((it_el_ind + 1) < elems)) {
- class scat_gath_iter iter(*this);
-
- ++iter.it_el_ind;
- iter.it_blk_off = 0;
- /* worst case recursion will stop at end of sgl */
- return iter.current_lba();
- }
- }
- return res;
-}
-
-int64_t
-scat_gath_iter::current_lba_rem_num(int & rem_num) const
-{
- const int elems = sglist.sgl.size();
- int64_t res = SG_LBA_INVALID; /* for at end or invalid (-1) */
-
- if (it_el_ind < elems) {
- struct scat_gath_elem sge = sglist.sgl[it_el_ind];
-
- if ((uint32_t)it_blk_off < sge.num) {
- rem_num = sge.num - it_blk_off;
- return sge.lba + it_blk_off;
- } else if (((uint32_t)it_blk_off == sge.num) &&
- ((it_el_ind + 1) < elems)) {
- class scat_gath_iter iter(*this);
-
- ++iter.it_el_ind;
- iter.it_blk_off = 0;
- /* worst case recursion will stop at end of sgl */
- return iter.current_lba_rem_num(rem_num);
- }
- }
- rem_num = -1;
- return res;
-}
-
-struct scat_gath_elem
-scat_gath_iter::current_elem() const
-{
- const int elems = sglist.sgl.size();
- struct scat_gath_elem sge;
-
- sge.make_bad();
- if (it_el_ind < elems)
- return sglist.sgl[it_el_ind];
- return sge;
-}
-
-/* Returns true of no sgl or sgl is at the end [elems, 0], otherwise it
- * returns false. */
-bool
-scat_gath_iter::at_end() const
-{
- const int elems = sglist.sgl.size();
-
- return ((0 == elems) || ((it_el_ind == elems) && (0 == it_blk_off)));
-}
-
-/* Returns true if associated iterator is monotonic (increasing) and not
- * fragmented. Empty sgl and single element degenerate considered linear.
- * Assumes sgl_sum_scan() has been called on sgl. */
-bool
-scat_gath_iter::is_sgl_linear() const
-{
- return sglist.linearity == SGL_LINEAR;
-}
-
-int
-scat_gath_iter::linear_for_n_blks(int max_n) const
-{
- int k, rem;
- const int elems = sglist.sgl.size();
- uint64_t prev_lba;
- struct scat_gath_elem sge;
-
- if (at_end() || (max_n <= 0))
- return 0;
- sge = sglist.sgl[it_el_ind];
- rem = (int)sge.num - it_blk_off;
- if (max_n <= rem)
- return max_n;
- prev_lba = sge.lba + sge.num;
- for (k = it_el_ind + 1; k < elems; ++k) {
- sge = sglist.sgl[k];
- if (sge.lba != prev_lba)
- return rem;
- rem += sge.num;
- if (max_n <= rem)
- return max_n;
- prev_lba = sge.lba + sge.num;
- }
- return rem;
-}
-
-/* id_str may be NULL (if so replace by "unknown"), present to enhance verbose
- * output. */
-void
-scat_gath_iter::dbg_print(const char * id_str, bool to_stdout,
- int verbose) const
-{
- const char * caller = id_str ? id_str : "unknown";
- FILE * fp = to_stdout ? stdout : stderr;
- lock_guard<mutex> lk(strerr_mut);
-
- fprintf(fp, "%s: it_el_ind=%d, it_blk_off=%d, blk_idx=%" PRId64 "\n",
- caller, it_el_ind, it_blk_off, blk_idx);
- fprintf(fp, " extend_last=%d\n", extend_last);
- if (verbose)
- sglist.dbg_print(false, " iterator's", to_stdout, verbose > 1, false);
-}
-
-/* Calculates difference between iterators, logically: res <-- lhs - rhs
- * Checks that lhsp and rhsp have same underlying sgl, if not returns
- * INT_MIN. Assumes iterators close enough for result to lie in range
- * from (-INT_MAX) to INT_MAX (inclusive). */
-int
-diff_between_iters(const struct scat_gath_iter & left,
- const struct scat_gath_iter & right)
-{
- int res, k, r_e_ind, l_e_ind;
-
- if (&left.sglist != &right.sglist) {
- pr2serr("%s: bad args\n", __func__);
- return INT_MIN;
- }
- r_e_ind = right.it_el_ind;
- l_e_ind = left.it_el_ind;
- if (l_e_ind < r_e_ind) { /* so difference will be negative */
- res = diff_between_iters(right, left); /* cheat */
- if (INT_MIN == res)
- return res;
- return -res;
- } else if (l_e_ind == r_e_ind)
- return (int)left.it_blk_off - (int)right.it_blk_off;
- /* (l_e_ind > r_e_ind) so (lhs > rhs) */
- res = (int)right.sglist.sgl[r_e_ind].num - right.it_blk_off;
- for (k = 1; (r_e_ind + k) < l_e_ind; ++k) {
- // pr2serr("%s: k=%d, res=%d, num=%d\n", __func__, k, res,
- // (int)right.sglist.sgl[r_e_ind + k].num);
- res += (int)right.sglist.sgl[r_e_ind + k].num;
- }
- res += left.it_blk_off;
- // pr2serr("%s: at exit res=%d\n", __func__, res);
- return res;
-}
-
-/* Compares from the current iterator positions of left and left until
- * the shorter list is exhausted. Returns false on the first inequality.
- * If no inequality and both remaining lists are same length then returns
- * true. If no inequality but remaining lists differ in length then returns
- * allow_partial. */
-bool
-sgls_eq_from_iters(const struct scat_gath_iter & left,
- const struct scat_gath_iter & right,
- bool allow_partial)
-{
- return sgls_eq_off(left.sglist, left.it_el_ind, left.it_blk_off,
- right.sglist, right.it_el_ind, right.it_blk_off,
- allow_partial);
-}
-
get_next_res
global_collection::get_next(int desired_num_blks)
{
@@ -2421,6 +1298,8 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
break;
}
if (! i_sg_it.set_by_blk_idx(gnr.first)) {
+ lock_guard<mutex> lk(strerr_mut);
+
pr2serr_lk("[%d]: input set_by_blk_idx() failed\n", id);
i_sg_it.dbg_print("input after set_by_blk_idx", false, vb > 5);
res = 2;
@@ -2811,7 +1690,7 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
int hole_count = 0;
int vb = clp->verbose;
int k, j, f1, slen, sstatus, blen;
- char b[80];
+ char b[160];
blen = sizeof(b);
good_inblks = 0;
@@ -3276,8 +2155,10 @@ do_normal_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
seg_blks = res;
in_fin_blks = seg_blks;
- if (FT_DEV_NULL == clp->out_type)
+ if (FT_DEV_NULL == clp->out_type) {
+ out_fin_blks = seg_blks;/* so finish logic doesn't suspect ... */
goto bypass;
+ }
d_off = 0;
for (k = 0; seg_blks > 0; ++k, seg_blks -= num, d_off += num) {
kk = min<int>(seg_blks, clp->bpt);
@@ -3732,6 +2613,8 @@ skip_seek(struct global_collection *clp, const char * key, const char * buf,
either_list.sum_scan(key, vb > 3 /* bool show_sgl */, vb > 1);
#if 0
if (vb > 3) {
+ lock_guard<mutex> lk(strerr_mut);
+
pr2serr("%s: scatter gathet list:\n", is_skip ? ("skip" : "seek"));
either_list.dbg_print(false, is_skip ? ("skip" : "seek"), false,
bool show_sgl)
@@ -4590,6 +3473,10 @@ main(int argc, char * argv[])
}
clp->outfp = outf;
}
+ if (clp->verify && (clp->out_type == FT_DEV_NULL)) {
+ pr2serr("Can't do verify when OFILE not given\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
if ((FT_SG == clp->in_type ) && (FT_SG == clp->out_type)) {
;
diff --git a/testing/sg_scat_gath.cpp b/testing/sg_scat_gath.cpp
new file mode 100644
index 00000000..dc51f377
--- /dev/null
+++ b/testing/sg_scat_gath.cpp
@@ -0,0 +1,1029 @@
+/*
+ * Copyright (c) 2014-2020 Douglas Gilbert.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+// C headers
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+// C++ headers
+#include <array>
+
+#include "sg_scat_gath.h"
+#include "sg_lib.h"
+#include "sg_pr2serr.h"
+
+using namespace std;
+
+#define MAX_SGL_NUM_VAL (INT32_MAX - 1) /* should reduce for testing */
+// #define MAX_SGL_NUM_VAL 7 /* should reduce for testing */
+#if MAX_SGL_NUM_VAL > INT32_MAX
+#error "MAX_SGL_NUM_VAL cannot exceed 2^31 - 1"
+#endif
+
+bool
+scat_gath_list::empty() const
+{
+ return sgl.empty();
+}
+
+bool
+scat_gath_list::empty_or_00() const
+{
+ if (sgl.empty())
+ return true;
+ return ((sgl.size() == 1) && (sgl[0].lba == 0) && (sgl[0].num == 0));
+}
+
+int
+scat_gath_list::num_elems() const
+{
+ return sgl.size();
+}
+
+
+/* Read numbers (up to 64 bits in size) from command line (comma (or
+ * (single) space **) separated list). Assumed decimal unless prefixed
+ * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
+ * Returns 0 if ok, or 1 if error. Assumed to be LBA (64 bit) and
+ * number_of_block (32 bit) pairs. ** Space on command line needs to
+ * be escaped, otherwise it is an operand/option separator. */
+bool
+scat_gath_list::load_from_cli(const char * cl_p, bool b_vb)
+{
+ bool split, full_pair;
+ int in_len, k, j;
+ const int max_nbs = MAX_SGL_NUM_VAL;
+ int64_t ll, large_num;
+ uint64_t prev_lba;
+ char * cp;
+ char * c2p;
+ const char * lcp;
+ struct scat_gath_elem sge;
+
+ if (NULL == cl_p) {
+ pr2serr("%s: bad arguments\n", __func__);
+ goto err_out;
+ }
+ lcp = cl_p;
+ in_len = strlen(cl_p);
+ if ('-' == cl_p[0]) { /* read from stdin */
+ pr2serr("%s: logic error: no stdin here\n", __func__);
+ goto err_out;
+ } else { /* list of numbers (default decimal) on command line */
+ k = strspn(cl_p, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP, ");
+ if (in_len != k) {
+ if (b_vb)
+ pr2serr("%s: error at pos %d\n", __func__, k + 1);
+ goto err_out;
+ }
+ j = 0;
+ full_pair = true;
+ for (k = 0, split = false; ; ++k) {
+ if (split) {
+ /* splitting given elem with large number_of_blocks into
+ * multiple elems within array being built */
+ ++j;
+ sge.lba = prev_lba + (uint64_t)max_nbs;
+ if (large_num > max_nbs) {
+ sge.num = (uint32_t)max_nbs;
+ prev_lba = sge.lba;
+ large_num -= max_nbs;
+ sgl.push_back(sge);
+ } else {
+ sge.num = (uint32_t)large_num;
+ split = false;
+ if (b_vb)
+ pr2serr("%s: split large sg elem into %d element%s\n",
+ __func__, j, (j == 1 ? "" : "s"));
+ sgl.push_back(sge);
+ goto check_for_next;
+ }
+ continue;
+ }
+ full_pair = false;
+ ll = sg_get_llnum(lcp);
+ if (-1 != ll) {
+ sge.lba = (uint64_t)ll;
+ cp = (char *)strchr(lcp, ',');
+ c2p = (char *)strchr(lcp, ' ');
+ if (NULL == cp) {
+ cp = c2p;
+ if (NULL == cp)
+ break;
+ }
+ if (c2p && (c2p < cp))
+ cp = c2p;
+ lcp = cp + 1;
+ } else {
+ if (b_vb)
+ pr2serr("%s: error at pos %d\n", __func__,
+ (int)(lcp - cl_p + 1));
+ goto err_out;
+ }
+ ll = sg_get_llnum(lcp);
+ if (ll >= 0) {
+ full_pair = true;
+ if (ll > max_nbs) {
+ sge.num = (uint32_t)max_nbs;
+ prev_lba = sge.lba;
+ large_num = ll - max_nbs;
+ split = true;
+ j = 1;
+ continue;
+ }
+ sge.num = (uint32_t)ll;
+ } else { /* bad or negative number as number_of_blocks */
+ if (b_vb)
+ pr2serr("%s: bad number at pos %d\n", __func__,
+ (int)(lcp - cl_p + 1));
+ goto err_out;
+ }
+ sgl.push_back(sge);
+check_for_next:
+ cp = (char *)strchr(lcp, ',');
+ c2p = (char *)strchr(lcp, ' ');
+ if (NULL == cp) {
+ cp = c2p;
+ if (NULL == cp)
+ break;
+ }
+ if (c2p && (c2p < cp))
+ cp = c2p;
+ lcp = cp + 1;
+ } /* end of for loop over items in operand */
+ /* other than first pair, expect even number of items */
+ if ((k > 0) && (! full_pair)) {
+ if (b_vb)
+ pr2serr("%s: expected even number of items: "
+ "LBA0,NUM0,LBA1,NUM1...\n", __func__);
+ goto err_out;
+ }
+ }
+ return true;
+err_out:
+ if (0 == m_errno)
+ m_errno = SG_LIB_SYNTAX_ERROR;
+ return false;
+}
+
+bool
+scat_gath_list::file2sgl_helper(FILE * fp, const char * fnp, bool def_hex,
+ bool flexible, bool b_vb)
+{
+ bool bit0;
+ bool pre_addr1 = true;
+ bool pre_hex_seen = false;
+ int in_len, k, j, m, ind;
+ const int max_nbs = MAX_SGL_NUM_VAL;
+ int off = 0;
+ int64_t ll;
+ uint64_t ull, prev_lba;
+ char * lcp;
+ struct scat_gath_elem sge;
+ char line[1024];
+
+ for (j = 0 ; ; ++j) {
+ if (NULL == fgets(line, sizeof(line), fp))
+ break;
+ // could improve with carry_over logic if sizeof(line) too small
+ in_len = strlen(line);
+ if (in_len > 0) {
+ if ('\n' == line[in_len - 1]) {
+ --in_len;
+ line[in_len] = '\0';
+ } else {
+ m_errno = SG_LIB_SYNTAX_ERROR;
+ if (b_vb)
+ pr2serr("%s: %s: line too long, max %d bytes\n",
+ __func__, fnp, (int)(sizeof(line) - 1));
+ goto err_out;
+ }
+ }
+ if (in_len < 1)
+ continue;
+ lcp = line;
+ m = strspn(lcp, " \t");
+ if (m == in_len)
+ continue;
+ lcp += m;
+ in_len -= m;
+ if ('#' == *lcp)
+ continue;
+ if (pre_addr1 || pre_hex_seen) {
+ /* Accept lines with leading 'HEX' and ignore as long as there
+ * is one _before_ any LBA,NUM lines in the file. This allows
+ * HEX marked sgls to be concaternated together. */
+ if (('H' == toupper(lcp[0])) && ('E' == toupper(lcp[1])) &&
+ ('X' == toupper(lcp[2]))) {
+ pre_hex_seen = true;
+ if (def_hex)
+ continue; /* bypass 'HEX' marker line if expecting hex */
+ else {
+ if (flexible) {
+ def_hex = true; /* okay, switch to hex parse */
+ continue;
+ } else {
+ pr2serr("%s: %s: 'hex' string detected on line %d, "
+ "expecting decimal\n", __func__, fnp, j + 1);
+ m_errno = EINVAL;
+ goto err_out;
+ }
+ }
+ }
+ }
+ k = strspn(lcp, "0123456789aAbBcCdDeEfFhHxXbBdDiIkKmMgGtTpP, \t");
+ if ((k < in_len) && ('#' != lcp[k])) {
+ m_errno = EINVAL;
+ if (b_vb)
+ pr2serr("%s: %s: syntax error at line %d, pos %d\n",
+ __func__, fnp, j + 1, m + k + 1);
+ goto err_out;
+ }
+ for (k = 0; k < 256; ++k) {
+ /* limit parseable items on one line to 256 */
+ if (def_hex) { /* don't accept negatives or multipliers */
+ if (1 == sscanf(lcp, "%" SCNx64, &ull))
+ ll = (int64_t)ull;
+ else
+ ll = -1; /* use (2**64 - 1) as error flag */
+ } else
+ ll = sg_get_llnum(lcp);
+ if (-1 != ll) {
+ ind = ((off + k) >> 1);
+ bit0 = !! (0x1 & (off + k));
+ if (ind >= SG_SGL_MAX_ELEMENTS) {
+ m_errno = EINVAL;
+ if (b_vb)
+ pr2serr("%s: %s: array length exceeded\n", __func__,
+ fnp);
+ goto err_out;
+ }
+ if (bit0) { /* bit0 set when decoding a NUM */
+ if (ll < 0) {
+ m_errno = EINVAL;
+ if (b_vb)
+ pr2serr("%s: %s: bad number in line %d, at pos "
+ "%d\n", __func__, fnp, j + 1,
+ (int)(lcp - line + 1));
+ goto err_out;
+ }
+ if (ll > max_nbs) {
+ int h = 1;
+
+ /* split up this elem into multiple, smaller elems */
+ do {
+ sge.num = (uint32_t)max_nbs;
+ prev_lba = sge.lba;
+ sgl.push_back(sge);
+ sge.lba = prev_lba + (uint64_t)max_nbs;
+ ++h;
+ off += 2;
+ ll -= max_nbs;
+ } while (ll > max_nbs);
+ if (b_vb)
+ pr2serr("%s: split large sg elem into %d "
+ "elements\n", __func__, h);
+ }
+ sge.num = (uint32_t)ll;
+ sgl.push_back(sge);
+ } else { /* bit0 clear when decoding a LBA */
+ if (pre_addr1)
+ pre_addr1 = false;
+ sge.lba = (uint64_t)ll;
+ }
+ } else { /* failed to decode number on line */
+ if ('#' == *lcp) { /* numbers before #, rest of line comment */
+ --k;
+ break; /* goes to next line */
+ }
+ m_errno = EINVAL;
+ if (b_vb)
+ pr2serr("%s: %s: error in line %d, at pos %d\n",
+ __func__, fnp, j + 1, (int)(lcp - line + 1));
+ goto err_out;
+ }
+ lcp = strpbrk(lcp, " ,\t#");
+ if ((NULL == lcp) || ('#' == *lcp))
+ break;
+ lcp += strspn(lcp, " ,\t");
+ if ('\0' == *lcp)
+ break;
+ } /* <<< end of for(k < 256) loop */
+ off += (k + 1);
+ } /* <<< end of for loop, one iteration per line */
+ /* allow one items, but not higher odd number of items */
+ if ((off > 1) && (0x1 & off)) {
+ m_errno = EINVAL;
+ if (b_vb)
+ pr2serr("%s: %s: expect even number of items: "
+ "LBA0,NUM0,LBA1,NUM1...\n", __func__, fnp);
+ goto err_out;
+ }
+ clearerr(fp); /* even EOF on first pass needs this before rescan */
+ return true;
+err_out:
+ clearerr(fp);
+ return false;
+}
+
+/* Read numbers from filename (or stdin), line by line (comma (or (single)
+ * space) separated list); places starting_LBA,number_of_block pairs in an
+ * array of scat_gath_elem elements pointed to by the returned value. If
+ * this fails NULL is returned and an error number is written to errp (if it
+ * is non-NULL). Assumed decimal (and may have suffix multipliers) when
+ * def_hex==false; if a number is prefixed by '0x', '0X' or contains trailing
+ * 'h' or 'H' that denotes a hex number. When def_hex==true all numbers are
+ * assumed to be hex (ignored '0x' prefixes and 'h' suffixes) and multiplers
+ * are not permitted. Heap allocates an array just big enough to hold all
+ * elements if the file is countable. Pipes and stdin are not considered
+ * countable. In the non-countable case an array of MAX_FIXED_SGL_ELEMS
+ * elements is pre-allocated; if it is exceeded sg_convert_errno(EDOM) is
+ * placed in *errp (if it is non-NULL). One of the first actions is to write
+ * 0 to *errp (if it is non-NULL) so the caller does not need to zero it
+ * before calling. */
+bool
+scat_gath_list::load_from_file(const char * file_name, bool def_hex,
+ bool flexible, bool b_vb)
+{
+ bool have_stdin;
+ bool have_err = false;
+ FILE * fp;
+ const char * fnp;
+
+ have_stdin = ((1 == strlen(file_name)) && ('-' == file_name[0]));
+ if (have_stdin) {
+ fp = stdin;
+ fnp = "<stdin>";
+ } else {
+ fnp = file_name;
+ fp = fopen(fnp, "r");
+ if (NULL == fp) {
+ m_errno = errno;
+ if (b_vb)
+ pr2serr("%s: opening %s: %s\n", __func__, fnp,
+ safe_strerror(m_errno));
+ return false;
+ }
+ }
+ if (! file2sgl_helper(fp, fnp, def_hex, flexible, b_vb))
+ have_err = true;
+ if (! have_stdin)
+ fclose(fp);
+ return have_err ? false : true;
+}
+
+const char *
+scat_gath_list::linearity_as_str() const
+{
+ switch (linearity) {
+ case SGL_LINEAR:
+ return "linear";
+ case SGL_MONOTONIC:
+ return "monotonic";
+ case SGL_MONO_OVERLAP:
+ return "monotonic, overlapping";
+ case SGL_NON_MONOTONIC:
+ return "non-monotonic";
+ default:
+ return "unknown";
+ }
+}
+
+void
+scat_gath_list::set_weaker_linearity(enum sgl_linearity_e lin)
+{
+ int i_lin = (int)lin;
+
+ if (i_lin > (int)linearity)
+ linearity = lin;
+}
+
+/* id_str may be NULL (if so replace by "unknown"), present to enhance verbose
+ * output. */
+void
+scat_gath_list::dbg_print(bool skip_meta, const char * id_str, bool to_stdout,
+ bool show_sgl) const
+{
+ int k;
+ int num = sgl.size();
+ const char * caller = id_str ? id_str : "unknown";
+ FILE * fp = to_stdout ? stdout : stderr;
+
+ if (! skip_meta) {
+ fprintf(fp, "%s: elems=%d, sgl %spresent, linearity=%s\n",
+ caller, num, (sgl.empty() ? "not " : ""),
+ linearity_as_str());
+ fprintf(fp, " sum=%" PRId64 ", sum_hard=%s lowest=0x%" PRIx64
+ ", high_lba_p1=", sum, (sum_hard ? "true" : "false"),
+ lowest_lba);
+ fprintf(fp, "0x%" PRIx64 "\n", high_lba_p1);
+ }
+ fprintf(fp, " >> %s scatter gather list (%d element%s):\n", caller, num,
+ (num == 1 ? "" : "s"));
+ if (show_sgl) {
+ for (k = 0; k < num; ++k) {
+ const struct scat_gath_elem & sge = sgl[k];
+
+ fprintf(fp, " lba: 0x%" PRIx64 ", number: 0x%" PRIx32,
+ sge.lba, sge.num);
+ if (sge.lba > 0)
+ fprintf(fp, " [next lba: 0x%" PRIx64 "]", sge.lba + sge.num);
+ fprintf(fp, "\n");
+ }
+ }
+}
+
+/* Assumes sgl array (vector) is setup. The other fields in this object are
+ * set by analyzing sgl in a single pass. The fields that are set are:
+ * fragmented, lowest_lba, high_lba_p1, monotonic, overlapping, sum and
+ * sum_hard. Degenerate elements (i.e. those with 0 blocks) are ignored apart
+ * from when one is last which makes sum_hard false and its LBA becomes
+ * high_lba_p1 if it is the highest in the list. An empty sgl is equivalent
+ * to a 1 element list with [0, 0], so sum_hard==false, monit==true,
+ * fragmented==false and overlapping==false . id_str may be NULL, present
+ * to enhance verbose output. */
+void
+scat_gath_list::sum_scan(const char * id_str, bool show_sgl, bool b_vb)
+{
+ bool degen = false;
+ bool first = true;
+ bool regular = true; /* no overlapping segments detected */
+ int k;
+ int elems = sgl.size();
+ uint32_t prev_num, t_num;
+ uint64_t prev_lba, t_lba, low, high, end;
+
+ sum = 0;
+ for (k = 0, low = 0, high = 0; k < elems; ++k) {
+ const struct scat_gath_elem & sge = sgl[k];
+
+ degen = false;
+ t_num = sge.num;
+ if (0 == t_num) {
+ degen = true;
+ if (! first)
+ continue; /* ignore degen element that not first */
+ }
+ if (first) {
+ low = sge.lba;
+ sum = t_num;
+ high = sge.lba + sge.num;
+ first = false;
+ } else {
+ t_lba = sge.lba;
+ if ((prev_lba + prev_num) != t_lba)
+ set_weaker_linearity(SGL_MONOTONIC);
+ sum += t_num;
+ end = t_lba + t_num;
+ if (end > high)
+ high = end; /* high is one plus highest LBA */
+ if (prev_lba < t_lba)
+ ;
+ else if (prev_lba == t_lba) {
+ if (prev_num > 0) {
+ set_weaker_linearity(SGL_MONO_OVERLAP);
+ break;
+ }
+ } else {
+ low = t_lba;
+ set_weaker_linearity(SGL_NON_MONOTONIC);
+ break;
+ }
+ if (regular) {
+ if ((prev_lba + prev_num) > t_lba)
+ regular = false;
+ }
+ }
+ prev_lba = sge.lba;
+ prev_num = sge.num;
+ } /* end of for loop while still elements and monot true */
+
+ if (k < elems) { /* only here if above breaks are taken */
+ prev_lba = t_lba;
+ ++k;
+ for ( ; k < elems; ++k) {
+ const struct scat_gath_elem & sge = sgl[k];
+
+ degen = false;
+ t_lba = sge.lba;
+ t_num = sge.num;
+ if (0 == t_num) {
+ degen = true;
+ continue;
+ }
+ sum += t_num;
+ end = t_lba + t_num;
+ if (end > high)
+ high = end;
+ if (prev_lba > t_lba) {
+ if (t_lba < low)
+ low = t_lba;
+ }
+ prev_lba = t_lba;
+ }
+ } else
+ if (! regular)
+ set_weaker_linearity(SGL_MONO_OVERLAP);
+
+ lowest_lba = low;
+ if (degen && (elems > 0)) { /* last element always impacts high_lba_p1 */
+ t_lba = sgl[elems - 1].lba;
+ high_lba_p1 = (t_lba > high) ? t_lba : high;
+ } else
+ high_lba_p1 = high;
+ sum_hard = (elems > 0) ? ! degen : false;
+ if (b_vb)
+ dbg_print(false, id_str, false, show_sgl);
+}
+
+/* Usually will append (or add to start if empty) sge unless 'extra_blks'
+ * exceeds MAX_SGL_NUM_VAL. In that case multiple sge_s are added with
+ * sge.num = MAX_SGL_NUM_VAL or less (for final sge) until extra_blks is
+ * exhausted. Returns new size of scatter gather list. */
+int
+scat_gath_list::append_1or(int64_t extra_blks, int64_t start_lba)
+{
+ int o_num = sgl.size();
+ const int max_nbs = MAX_SGL_NUM_VAL;
+ int64_t cnt = 0;
+ struct scat_gath_elem sge;
+
+ if ((extra_blks <= 0) || (start_lba < 0))
+ return o_num; /* nothing to do */
+ if ((o_num > 0) && (! sum_hard)) {
+ sge = sgl[o_num - 1]; /* assume sge.num==0 */
+ if (sge.lba == (uint64_t)start_lba) {
+ if (extra_blks <= max_nbs)
+ sge.num = extra_blks;
+ else
+ sge.num = max_nbs;
+ sgl[o_num - 1] = sge;
+ cnt = sge.num;
+ sum += cnt;
+ sum_hard = true;
+ if (cnt <= extra_blks) {
+ high_lba_p1 = sge.lba + cnt;
+ return o_num;
+ }
+ }
+ } else if (0 == o_num)
+ lowest_lba = start_lba;
+
+ for ( ; cnt < extra_blks; cnt += max_nbs) {
+ sge.lba = start_lba + cnt;
+ if ((extra_blks - cnt) <= max_nbs)
+ sge.num = extra_blks - cnt;
+ else
+ sge.num = max_nbs;
+ sgl.push_back(sge);
+ sum += sge.num;
+ } /* always loops at least once */
+ sum_hard = true;
+ high_lba_p1 = sge.lba + sge.num;
+ return sgl.size();
+}
+
+int
+scat_gath_list::append_1or(int64_t extra_blks)
+{
+ int o_num = sgl.size();
+ if (o_num < 1)
+ return append_1or(extra_blks, 0);
+
+ struct scat_gath_elem sge = sgl[o_num - 1];
+ return append_1or(extra_blks, sge.lba + sge.num);
+}
+
+bool
+sgls_eq_off(const scat_gath_list & left, int l_e_ind, int l_blk_off,
+ const scat_gath_list & right, int r_e_ind, int r_blk_off,
+ bool allow_partial)
+{
+ int lrem, rrem;
+ int lelems = left.sgl.size();
+ int relems = right.sgl.size();
+
+ while ((l_e_ind < lelems) && (r_e_ind < relems)) {
+ if ((left.sgl[l_e_ind].lba + l_blk_off) !=
+ (right.sgl[r_e_ind].lba + r_blk_off))
+ return false;
+ lrem = left.sgl[l_e_ind].num - l_blk_off;
+ rrem = right.sgl[r_e_ind].num - r_blk_off;
+ if (lrem == rrem) {
+ ++l_e_ind;
+ l_blk_off = 0;
+ ++r_e_ind;
+ r_blk_off = 0;
+ } else if (lrem < rrem) {
+ ++l_e_ind;
+ l_blk_off = 0;
+ r_blk_off += lrem;
+ } else {
+ ++r_e_ind;
+ r_blk_off = 0;
+ l_blk_off += rrem;
+ }
+ }
+ if ((l_e_ind >= lelems) && (r_e_ind >= relems))
+ return true;
+ return allow_partial;
+}
+
+/* If bad arguments returns -1, otherwise returns the lowest LBA in *sglp .
+ * If no elements considered returns 0. If ignore_degen is true than
+ * ignores all elements with sge.num zero unless always_last is also
+ * true in which case the last element is always considered. */
+int64_t
+scat_gath_list::get_lowest_lba(bool ignore_degen, bool always_last) const
+{
+ int k;
+ const int num_elems = sgl.size();
+ bool some = (num_elems > 0);
+ int64_t res = INT64_MAX;
+
+ for (k = 0; k < num_elems; ++k) {
+ if ((0 == sgl[k].num) && ignore_degen)
+ continue;
+ if ((int64_t)sgl[k].lba < res)
+ res = sgl[k].lba;
+ }
+ if (always_last && some) {
+ if ((int64_t)sgl[k - 1].lba < res)
+ res = sgl[k - 1].lba;
+ }
+ return (INT64_MAX == res) ? 0 : res;
+}
+
+/* Returns >= 0 if sgl can be simplified to a single LBA. So an empty sgl
+ * will return 0; a one element sgl will return its LBA. A multiple element
+ * sgl only returns the first element's LBA (that is not degenerate) if the
+ * sgl is monotonic and not fragmented. In the extreme case takes last
+ * element's LBA if all prior elements are degenerate. Else returns -1 .
+ * Assumes sgl_sum_scan() has been called. */
+int64_t
+scat_gath_list::get_low_lba_from_linear() const
+{
+ const int num_elems = sgl.size();
+ int k;
+
+ if (num_elems <= 1)
+ return (1 == num_elems) ? sgl[0].lba : 0;
+ else {
+ if (linearity == SGL_LINEAR) {
+ for (k = 0; k < (num_elems - 1); ++k) {
+ if (sgl[k].num > 0)
+ return sgl[k].lba;
+ }
+ /* take last element's LBA if all earlier are degenerate */
+ return sgl[k].lba;
+ } else
+ return -1;
+ }
+}
+
+bool
+scat_gath_list::is_pipe_suitable() const
+{
+ return (lowest_lba == 0) && (linearity == SGL_LINEAR);
+}
+
+scat_gath_iter::scat_gath_iter(const scat_gath_list & parent)
+ : sglist(parent), it_el_ind(0), it_blk_off(0), blk_idx(0)
+{
+ int elems = sglist.num_elems();
+
+ if (elems > 0)
+ extend_last = (0 == sglist.sgl[elems - 1].num);
+}
+
+bool
+scat_gath_iter::set_by_blk_idx(int64_t _blk_idx)
+{
+ bool first;
+ int k;
+ const int elems = sglist.sgl.size();
+ const int last_ind = elems - 1;
+ uint32_t num;
+ int64_t bc = _blk_idx;
+
+ if (bc < 0)
+ return false;
+
+ if (bc == blk_idx)
+ return true;
+ else if (bc > blk_idx) {
+ k = it_el_ind;
+ bc -= blk_idx;
+ } else
+ k = 0;
+ for (first = true; k < elems; ++k, first = false) {
+ num = ((k == last_ind) && extend_last) ? MAX_SGL_NUM_VAL :
+ sglist.sgl[k].num;
+ if (first) {
+ if ((int64_t)(num - it_blk_off) < bc)
+ bc -= (num - it_blk_off);
+ else {
+ it_blk_off = bc + it_blk_off;
+ break;
+ }
+ } else {
+ if ((int64_t)num < bc)
+ bc -= num;
+ else {
+ it_blk_off = (uint32_t)bc;
+ break;
+ }
+ }
+ }
+ it_el_ind = k;
+ blk_idx = _blk_idx;
+
+ if (k < elems)
+ return true;
+ else if ((k == elems) && (0 == it_blk_off))
+ return true; /* EOL */
+ else
+ return false;
+}
+
+/* Given a blk_count, the iterator (*iter_p) is moved toward the EOL.
+ * Returns true unless blk_count takes iterator two or more past the last
+ * element. So if blk_count takes the iterator to the EOL, this function
+ * returns true. Takes into account iterator's extend_last flag. */
+bool
+scat_gath_iter::add_blks(uint64_t blk_count)
+{
+ bool first;
+ int k;
+ const int elems = sglist.sgl.size();
+ const int last_ind = elems - 1;
+ uint32_t num;
+ uint64_t bc = blk_count;
+
+ if (0 == bc)
+ return true;
+ for (first = true, k = it_el_ind; k < elems; ++k, first = false) {
+ num = ((k == last_ind) && extend_last) ? MAX_SGL_NUM_VAL :
+ sglist.sgl[k].num;
+ if (first) {
+ if ((uint64_t)(num - it_blk_off) < bc)
+ bc -= (num - it_blk_off);
+ else {
+ it_blk_off = bc + it_blk_off;
+ break;
+ }
+ } else {
+ if ((uint64_t)num < bc)
+ bc -= num;
+ else {
+ it_blk_off = (uint32_t)bc;
+ break;
+ }
+ }
+ }
+ it_el_ind = k;
+ blk_idx += blk_count;
+
+ if (k < elems)
+ return true;
+ else if ((k == elems) && (0 == it_blk_off))
+ return true; /* EOL */
+ else
+ return false;
+}
+
+/* Move the iterator from its current position (which may be to EOL) towards
+ * the start of the sgl (i.e. backwards) for blk_count blocks. Returns true
+ * if iterator is valid after the move, else returns false. N.B. if false is
+ * returned, then the iterator is invalid and may need to set it to a valid
+ * value. */
+bool
+scat_gath_iter::sub_blks(uint64_t blk_count)
+{
+ bool first;
+ int k = it_el_ind;
+ uint64_t bc = 0;
+ const uint64_t orig_blk_count = blk_count;
+
+ if (0 == blk_count)
+ return true;
+ for (first = true; k >= 0; --k) {
+ if (first) {
+ if (blk_count > (uint64_t)it_blk_off)
+ blk_count -= it_blk_off;
+ else {
+ it_blk_off -= blk_count;
+ break;
+ }
+ first = false;
+ } else {
+ uint32_t off = sglist.sgl[k].num;
+
+ bc = blk_count;
+ if (bc > (uint64_t)off)
+ blk_count -= off;
+ else {
+ bc = off - bc;
+ break;
+ }
+ }
+ }
+ if (k < 0) {
+ blk_idx = 0;
+ return false; /* bad situation */
+ }
+ if ((int64_t)orig_blk_count <= blk_idx)
+ blk_idx -= orig_blk_count;
+ else
+ blk_idx = 0;
+ it_el_ind = k;
+ if (! first)
+ it_blk_off = (uint32_t)bc;
+ return true;
+}
+
+/* Returns LBA referred to by iterator if valid or returns SG_LBA_INVALID
+ * (-1) if at end or invalid. */
+int64_t
+scat_gath_iter::current_lba() const
+{
+ const int elems = sglist.sgl.size();
+ int64_t res = SG_LBA_INVALID; /* for at end or invalid (-1) */
+
+ if (it_el_ind < elems) {
+ struct scat_gath_elem sge = sglist.sgl[it_el_ind];
+
+ if ((uint32_t)it_blk_off < sge.num)
+ return sge.lba + it_blk_off;
+ else if (((uint32_t)it_blk_off == sge.num) &&
+ ((it_el_ind + 1) < elems)) {
+ class scat_gath_iter iter(*this);
+
+ ++iter.it_el_ind;
+ iter.it_blk_off = 0;
+ /* worst case recursion will stop at end of sgl */
+ return iter.current_lba();
+ }
+ }
+ return res;
+}
+
+int64_t
+scat_gath_iter::current_lba_rem_num(int & rem_num) const
+{
+ const int elems = sglist.sgl.size();
+ int64_t res = SG_LBA_INVALID; /* for at end or invalid (-1) */
+
+ if (it_el_ind < elems) {
+ struct scat_gath_elem sge = sglist.sgl[it_el_ind];
+
+ if ((uint32_t)it_blk_off < sge.num) {
+ rem_num = sge.num - it_blk_off;
+ return sge.lba + it_blk_off;
+ } else if (((uint32_t)it_blk_off == sge.num) &&
+ ((it_el_ind + 1) < elems)) {
+ class scat_gath_iter iter(*this);
+
+ ++iter.it_el_ind;
+ iter.it_blk_off = 0;
+ /* worst case recursion will stop at end of sgl */
+ return iter.current_lba_rem_num(rem_num);
+ }
+ }
+ rem_num = -1;
+ return res;
+}
+
+struct scat_gath_elem
+scat_gath_iter::current_elem() const
+{
+ const int elems = sglist.sgl.size();
+ struct scat_gath_elem sge;
+
+ sge.make_bad();
+ if (it_el_ind < elems)
+ return sglist.sgl[it_el_ind];
+ return sge;
+}
+
+/* Returns true of no sgl or sgl is at the end [elems, 0], otherwise it
+ * returns false. */
+bool
+scat_gath_iter::at_end() const
+{
+ const int elems = sglist.sgl.size();
+
+ return ((0 == elems) || ((it_el_ind == elems) && (0 == it_blk_off)));
+}
+
+/* Returns true if associated iterator is monotonic (increasing) and not
+ * fragmented. Empty sgl and single element degenerate considered linear.
+ * Assumes sgl_sum_scan() has been called on sgl. */
+bool
+scat_gath_iter::is_sgl_linear() const
+{
+ return sglist.linearity == SGL_LINEAR;
+}
+
+int
+scat_gath_iter::linear_for_n_blks(int max_n) const
+{
+ int k, rem;
+ const int elems = sglist.sgl.size();
+ uint64_t prev_lba;
+ struct scat_gath_elem sge;
+
+ if (at_end() || (max_n <= 0))
+ return 0;
+ sge = sglist.sgl[it_el_ind];
+ rem = (int)sge.num - it_blk_off;
+ if (max_n <= rem)
+ return max_n;
+ prev_lba = sge.lba + sge.num;
+ for (k = it_el_ind + 1; k < elems; ++k) {
+ sge = sglist.sgl[k];
+ if (sge.lba != prev_lba)
+ return rem;
+ rem += sge.num;
+ if (max_n <= rem)
+ return max_n;
+ prev_lba = sge.lba + sge.num;
+ }
+ return rem;
+}
+
+/* id_str may be NULL (if so replace by "unknown"), present to enhance verbose
+ * output. */
+void
+scat_gath_iter::dbg_print(const char * id_str, bool to_stdout,
+ int verbose) const
+{
+ const char * caller = id_str ? id_str : "unknown";
+ FILE * fp = to_stdout ? stdout : stderr;
+
+ fprintf(fp, "%s: it_el_ind=%d, it_blk_off=%d, blk_idx=%" PRId64 "\n",
+ caller, it_el_ind, it_blk_off, blk_idx);
+ fprintf(fp, " extend_last=%d\n", extend_last);
+ if (verbose)
+ sglist.dbg_print(false, " iterator's", to_stdout, verbose > 1);
+}
+
+/* Calculates difference between iterators, logically: res <-- lhs - rhs
+ * Checks that lhsp and rhsp have same underlying sgl, if not returns
+ * INT_MIN. Assumes iterators close enough for result to lie in range
+ * from (-INT_MAX) to INT_MAX (inclusive). */
+int
+diff_between_iters(const struct scat_gath_iter & left,
+ const struct scat_gath_iter & right)
+{
+ int res, k, r_e_ind, l_e_ind;
+
+ if (&left.sglist != &right.sglist) {
+ pr2serr("%s: bad args\n", __func__);
+ return INT_MIN;
+ }
+ r_e_ind = right.it_el_ind;
+ l_e_ind = left.it_el_ind;
+ if (l_e_ind < r_e_ind) { /* so difference will be negative */
+ res = diff_between_iters(right, left); /* cheat */
+ if (INT_MIN == res)
+ return res;
+ return -res;
+ } else if (l_e_ind == r_e_ind)
+ return (int)left.it_blk_off - (int)right.it_blk_off;
+ /* (l_e_ind > r_e_ind) so (lhs > rhs) */
+ res = (int)right.sglist.sgl[r_e_ind].num - right.it_blk_off;
+ for (k = 1; (r_e_ind + k) < l_e_ind; ++k) {
+ // pr2serr("%s: k=%d, res=%d, num=%d\n", __func__, k, res,
+ // (int)right.sglist.sgl[r_e_ind + k].num);
+ res += (int)right.sglist.sgl[r_e_ind + k].num;
+ }
+ res += left.it_blk_off;
+ // pr2serr("%s: at exit res=%d\n", __func__, res);
+ return res;
+}
+
+/* Compares from the current iterator positions of left and left until
+ * the shorter list is exhausted. Returns false on the first inequality.
+ * If no inequality and both remaining lists are same length then returns
+ * true. If no inequality but remaining lists differ in length then returns
+ * allow_partial. */
+bool
+sgls_eq_from_iters(const struct scat_gath_iter & left,
+ const struct scat_gath_iter & right,
+ bool allow_partial)
+{
+ return sgls_eq_off(left.sglist, left.it_el_ind, left.it_blk_off,
+ right.sglist, right.it_el_ind, right.it_blk_off,
+ allow_partial);
+}
diff --git a/testing/sg_scat_gath.h b/testing/sg_scat_gath.h
new file mode 100644
index 00000000..2b17fb3b
--- /dev/null
+++ b/testing/sg_scat_gath.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2014-2020 Douglas Gilbert.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+// C headers
+#include <stdio.h>
+#include <stdint.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+// C++ headers
+#include <vector>
+
+// This file is a C++ header file
+
+
+#define SG_SGL_MAX_ELEMENTS 16384
+
+#define SG_COUNT_INDEFINITE (-1)
+#define SG_LBA_INVALID SG_COUNT_INDEFINITE
+
+/* Sizing matches largest SCSI READ and WRITE commands plus those of Unix
+ * read(2)s and write(2)s. User can give larger than 31 bit 'num's but they
+ * are split into several consecutive elements. */
+struct scat_gath_elem {
+ uint64_t lba; /* of start block */
+ uint32_t num; /* number of blocks from and including start block */
+
+ void make_bad() { lba = UINT64_MAX; num = UINT32_MAX; }
+ bool is_bad() const { return (lba == UINT64_MAX && num == UINT32_MAX); }
+};
+
+/* Consider "linearity" as a scatter gather list property. Elements of this
+ * of from the strongest form to the weakest. */
+enum sgl_linearity_e {
+ SGL_LINEAR = 0, /* empty list and 0,0 considered linear */
+ SGL_MONOTONIC, /* since not linear, implies holes */
+ SGL_MONO_OVERLAP, /* monotonic but same LBA in two or more elements */
+ SGL_NON_MONOTONIC /* weakest */
+};
+
+
+/* Holds one scatter gather list and its associated metadata */
+class scat_gath_list {
+public:
+ scat_gath_list() : linearity(SGL_LINEAR), sum_hard(false), m_errno(0),
+ high_lba_p1(0), lowest_lba(0), sum(0) { }
+
+ scat_gath_list(const scat_gath_list &) = default;
+ scat_gath_list & operator=(const scat_gath_list &) = default;
+ ~scat_gath_list() = default;
+
+ bool empty() const;
+ bool empty_or_00() const;
+ int num_elems() const;
+ int64_t get_lowest_lba(bool ignore_degen, bool always_last) const;
+ int64_t get_low_lba_from_linear() const;
+ bool is_pipe_suitable() const;
+
+ friend bool sgls_eq_off(const scat_gath_list &left, int l_e_ind,
+ int l_blk_off,
+ const scat_gath_list &right, int r_e_ind,
+ int r_blk_off, bool allow_partial);
+
+ bool load_from_cli(const char * cl_p, bool b_vb);
+ bool load_from_file(const char * file_name, bool def_hex, bool flexible,
+ bool b_vb);
+ int append_1or(int64_t extra_blks, int64_t start_lba);
+ int append_1or(int64_t extra_blks);
+
+ void dbg_print(bool skip_meta, const char * id_str, bool to_stdout,
+ bool show_sgl) const;
+
+ /* calculates and sets following bool-s and int64_t-s */
+ void sum_scan(const char * id_str, bool show_sgl, bool b_verbose);
+
+ void set_weaker_linearity(enum sgl_linearity_e lin);
+ enum sgl_linearity_e linearity;
+ const char * linearity_as_str() const;
+
+ bool sum_hard; /* 'num' in last element of 'sgl' is > 0 */
+ int m_errno; /* OS failure errno */
+ int64_t high_lba_p1; /* highest LBA plus 1, next write from and above */
+ int64_t lowest_lba; /* initialized to 0 */
+ int64_t sum; /* of all 'num' elements in 'sgl' */
+
+ friend int diff_between_iters(const struct scat_gath_iter & left,
+ const struct scat_gath_iter & right);
+
+private:
+ friend class scat_gath_iter;
+
+ bool file2sgl_helper(FILE * fp, const char * fnp, bool def_hex,
+ bool flexible, bool b_vb);
+
+ std::vector<scat_gath_elem> sgl; /* an array on heap [0..num_elems()) */
+};
+
+
+class scat_gath_iter {
+public:
+ scat_gath_iter(const scat_gath_list & my_scat_gath_list);
+ scat_gath_iter(const scat_gath_iter & src) = default;
+ scat_gath_iter& operator=(const scat_gath_iter&) = delete;
+ ~scat_gath_iter() = default;
+
+ int64_t current_lba() const;
+ int64_t current_lba_rem_num(int & rem_num) const;
+ struct scat_gath_elem current_elem() const;
+ bool at_end() const;
+ bool is_sgl_linear() const; /* the whole list */
+ int linear_for_n_blks(int max_n) const;
+
+ bool set_by_blk_idx(int64_t _blk_idx);
+ /* add/sub blocks return true if they reach EOL, else false */
+ bool add_blks(uint64_t blk_count);
+ bool sub_blks(uint64_t blk_count);
+
+ void dbg_print(const char * id_str, bool to_stdout, int verbose) const;
+
+ friend int diff_between_iters(const struct scat_gath_iter & left,
+ const struct scat_gath_iter & right);
+
+ friend bool sgls_eq_from_iters(const struct scat_gath_iter & left,
+ const struct scat_gath_iter & right,
+ bool allow_partial);
+
+private:
+ const scat_gath_list &sglist;
+
+ /* dual representation: either it_el_ind,it_blk_off or blk_idx */
+ int it_el_ind; /* refers to sge==sglist[it_el_ind] */
+ int it_blk_off; /* refers to LBA==(sge.lba + it_blk_off) */
+ int64_t blk_idx; /* in range: [0 .. sglist.sum) */
+ bool extend_last;
+};
diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c
index 54e0d23a..1fd91339 100644
--- a/testing/sg_tst_ioctl.c
+++ b/testing/sg_tst_ioctl.c
@@ -60,14 +60,14 @@
* later of the Linux sg driver. */
-static const char * version_str = "Version: 1.18 20200716";
+static const char * version_str = "Version: 1.18 20200719";
#define INQ_REPLY_LEN 128
#define INQ_CMD_LEN 6
#define SDIAG_CMD_LEN 6
#define SENSE_BUFFER_LEN 96
-#define EBUFF_SZ 256
+#define EBUFF_SZ 512
#ifndef SG_FLAG_Q_AT_TAIL
#define SG_FLAG_Q_AT_TAIL 0x10
@@ -105,6 +105,9 @@ static int sleep_secs = 0;
static int reserve_buff_sz = DEF_RESERVE_BUFF_SZ;
static int num_mrqs = 0;
static int num_sgnw = 0;
+static int dname_current = 0;
+static int dname_last = 0;
+static int dname_pos = 0;
static int verbose = 0;
static const char * relative_cp = NULL;
@@ -119,16 +122,18 @@ usage(void)
" [-m=MRQS[,I|S]] [-M] [-n] [-o] [-r=SZ] "
"[-s=SEC]\n"
" [-S] [-t] [-T=NUM] [-v] [-V] [-w]\n"
- " <sg_device> [<sg_device2>]\n"
+ " <sg_device>[-<num>] [<sg_device2>]\n"
" where:\n"
+ " -3 use sg v3 interface (def: sg v4 if available)\n"
" -c timestamp when sg driver created <sg_device>\n"
" -f fork and test share between processes\n"
" -h help: print usage message then exit\n"
" -I=0|1 iterator test of mid-level; 0: unlocked, 1: "
"locked\n"
" does test -T=NUM times, outputs duration\n"
- " -J=0|1 object walk (to root); 0: without ptr; 1: with "
- "ptr\n"
+ " -J=0|1 object walk up then 2 lookups; 0: no logging; "
+ "1: log\n"
+ " up-scan once per 1000 iterations\n"
" -l=Q_LEN queue length, between 1 and 511 (def: 16)\n"
" -m=MRQS[,I|S] test multi-req, MRQS number to do; if "
"the letter\n"
@@ -151,7 +156,14 @@ usage(void)
" ioctl(SG_GET_NUM_WAITING); then exit\n"
" -v increase verbosity of output\n"
" -V print version string then exit\n"
- " -w write (submit) only then exit\n");
+ " -w write (submit) only then exit\n\n");
+ printf("There are various groups of options for different tests. The "
+ "get_num_waiting\ngroup needs '-T=NUM' given. When '-I=0|1' is "
+ "also given then an object tree\niterator test is done NUM "
+ "times. If instead '-J=0|1' is given then an\nobject tree "
+ "traversal (up/down) is done 10,000 times (and NUM is\n"
+ "ignored).\n"
+ );
}
static void
@@ -756,10 +768,13 @@ fini:
int
main(int argc, char * argv[])
{
- bool done;
+ bool done, is_first;
bool nw_given = false;
- int sg_fd, k, ok, pack_id, num_waiting;
+ bool has_dname_range = false;
+ int k, ok, pack_id, num_waiting;
int res = 0;
+ int sum_nw = 0;
+ int sg_fd = -1;
int sg_fd2 = -1;
int sock = -1;
uint8_t inq_cdb[INQ_CMD_LEN] =
@@ -770,9 +785,11 @@ main(int argc, char * argv[])
sg_io_hdr_t io_hdr[MAX_Q_LEN];
sg_io_hdr_t rio_hdr;
char ebuff[EBUFF_SZ];
+ char dname[256];
uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
const char * second_fname = NULL;
const char * cp;
+ char * chp;
struct sg_scsi_id ssi;
@@ -900,7 +917,7 @@ main(int argc, char * argv[])
break;
}
}
- if (iterator_test >= 0)
+ if ((iterator_test >= 0) || (object_walk_test >= 0))
nw_given = false;
if (show_size_value) {
@@ -953,58 +970,87 @@ main(int argc, char * argv[])
usage();
return 1;
}
+ memset(dname, 0, sizeof(dname));
+ if (strlen(file_name) > 255) {
+ fprintf(stderr, "file_name too long\n");
+ goto out;
+ }
+ strncpy(dname, file_name, sizeof(dname) - 1);
+ if ((chp = strchr(dname, '-'))) {
+ if (1 != sscanf(chp + 1, "%d", &dname_last)) {
+ fprintf(stderr, "can't code number after '-' in file_name\n");
+ goto out;
+ }
+ *chp = '\0';
+ --chp;
+ while (isdigit(*chp))
+ --chp;
+ ++chp;
+ if (1 != sscanf(chp, "%d", &dname_current)) {
+ fprintf(stderr, "can't code number before '-' in file_name\n");
+ goto out;
+ }
+ *chp = '\0';
+ has_dname_range = true;
+ dname_pos = strlen(dname);
+ }
+ is_first = true;
+
+dname_range_loop:
+ if (has_dname_range)
+ sprintf(dname + dname_pos, "%d", dname_current);
/* An access mode of O_RDWR is required for write()/read() interface */
- if ((sg_fd = open(file_name, O_RDWR)) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- "error opening file: %s", file_name);
+ if ((sg_fd = open(dname, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ, "error opening file: %s", dname);
perror(ebuff);
return 1;
}
if (verbose)
fprintf(stderr, "opened given file: %s successfully, fd=%d\n",
- file_name, sg_fd);
+ dname, sg_fd);
if (ioctl(sg_fd, SG_GET_VERSION_NUM, &sg_drv_ver_num) < 0) {
pr2serr("ioctl(SG_GET_VERSION_NUM) failed, errno=%d %s\n", errno,
strerror(errno));
goto out;
}
- printf("Linux sg driver version: %d\n", sg_drv_ver_num);
-
- if (object_walk_test >= 0) {
- k = (object_walk_test == 0) ? -999 : 999;
- if (ioctl(sg_fd, SG_SET_DEBUG, &k) < 0) {
- res = errno;
- fprintf(stderr, "%s%d: ioctl(SG_SET_DEBUG) failed errno=%d\n",
- relative_cp, k, res);
- }
- goto out;
- }
+ if (is_first)
+ printf("Linux sg driver version: %d\n", sg_drv_ver_num);
if (create_time && (sg_drv_ver_num > 40030)) {
- pr_create_dev_time(sg_fd, file_name);
+ pr_create_dev_time(sg_fd, dname);
goto out;
}
- if (nw_given || (iterator_test >= 0)) { /* -T=NUM and/or -I=0|1 */
+ if (nw_given || (iterator_test >= 0) || (object_walk_test >= 0)) {
+ /* -T=NUM and/or -I=0|1 or -j=0|1 */
/* time ioctl(SG_GET_NUM_WAITING) or do iterator_test */
- int nw, sum_nw;
+ int nw;
struct timespec start_tm, fin_tm, res_tm;
- if (nw_given)
- printf("Timing %d calls to ioctl(SG_GET_NUM_WAITING)\n",
- num_sgnw);
- else
- printf("Timing calls to ioctl(SG_SET_DEBUG, %d)\n",
- num_sgnw);
- if (0 != clock_gettime(CLOCK_MONOTONIC, &start_tm)) {
- res = errno;
- perror("start clock_gettime() failed:");
- goto out;
+ if (is_first) {
+ int rang = has_dname_range ? (1 + dname_last - dname_current) : 1;
+
+ is_first = false;
+ if (nw_given)
+ printf("Timing %dx%d calls to ioctl(SG_GET_NUM_WAITING)\n",
+ rang, num_sgnw);
+ else if (iterator_test >= 0) {
+ k = num_sgnw + 1000;
+ printf("Timing %d calls to ioctl(SG_SET_DEBUG, %d)\n",
+ rang, ((0 == iterator_test) ? -k : k));
+ } else
+ printf("Timing %d calls to ioctl(SG_SET_DEBUG, %d)\n",
+ rang, (object_walk_test == 0) ? 999 : -999);
+ if (0 != clock_gettime(CLOCK_MONOTONIC, &start_tm)) {
+ res = errno;
+ perror("start clock_gettime() failed:");
+ goto out;
+ }
}
if (nw_given) {
- for (k = 0, sum_nw = 0; k < num_sgnw; ++k, sum_nw += nw) {
+ for (k = 0; k < num_sgnw; ++k, sum_nw += nw) {
if (ioctl(sg_fd, SG_GET_NUM_WAITING, &nw) < 0) {
res = errno;
fprintf(stderr, "%d: ioctl(SG_GET_NUM_WAITING) failed "
@@ -1012,7 +1058,7 @@ main(int argc, char * argv[])
goto out;
}
}
- } else {
+ } else if (iterator_test >= 0) {
int fd, pid;
k = num_sgnw + 1000;
@@ -1049,6 +1095,31 @@ main(int argc, char * argv[])
fprintf(stderr, "%s%d: ioctl(SG_SET_DEBUG) failed errno=%d\n",
relative_cp, k, res);
goto out;
+ } else if (verbose)
+ fprintf(stderr, "%siterator_test good ioctl(SG_SET_DEBUG, "
+ "%d)\n", relative_cp, k);
+ sum_nw += num_sgnw;
+ } else if (object_walk_test >= 0) {
+ const char * ccp = "object_walk_test";
+
+ relative_cp = "";
+ k = (object_walk_test == 0) ? 999 : -999;
+ if (ioctl(sg_fd, SG_SET_DEBUG, &k) < 0) {
+ res = errno;
+ fprintf(stderr, "%s: ioctl(SG_SET_DEBUG, %d) failed "
+ "errno=%d\n", ccp, k, res);
+ } else if (verbose)
+ fprintf(stderr, "%s: good call to ioctl(SG_SET_DEBUG, %d)\n",
+ ccp, k);
+ sum_nw += 10000; /* (1_up-scan + 2_lookups) * 10,000 times */
+ }
+
+ if (has_dname_range) {
+ ++dname_current;
+ if (dname_current <= dname_last) {
+ if (sg_fd >= 0)
+ close(sg_fd);
+ goto dname_range_loop;
}
}
if (0 != clock_gettime(CLOCK_MONOTONIC, &fin_tm)) {
@@ -1076,7 +1147,7 @@ main(int argc, char * argv[])
if (m > 0.000001)
printf("%sCalls per second: %.2f\n", relative_cp,
- (double)num_sgnw / m);
+ (double)sum_nw / m);
}
res = 0;
goto out;
@@ -1133,7 +1204,7 @@ main(int argc, char * argv[])
cp = do_fork ? relative_cp : "";
if (! do_v3_only) {
- if (tst_extended_ioctl(file_name, sg_fd, second_fname, sg_fd2, sock,
+ if (tst_extended_ioctl(dname, sg_fd, second_fname, sg_fd2, sock,
cp))
goto out;
}
diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp
index 75fc5364..519f1f82 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -36,7 +36,7 @@
* renamed [20181221]
*/
-static const char * version_str = "1.84 20200716";
+static const char * version_str = "1.85 20200720";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -2267,7 +2267,7 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
int hole_count = 0;
int vb = clp->verbose;
int k, j, f1, slen, sstatus, blen;
- char b[80];
+ char b[160];
blen = sizeof(b);
good_inblks = 0;