aboutsummaryrefslogtreecommitdiff
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
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
-rw-r--r--ChangeLog7
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg_turs.822
-rw-r--r--include/sg_cmds_basic.h10
-rw-r--r--include/sg_pt.h17
-rw-r--r--lib/sg_cmds_basic.c219
-rw-r--r--lib/sg_cmds_basic2.c81
-rw-r--r--lib/sg_cmds_extra.c142
-rw-r--r--lib/sg_pt_common.c2
-rw-r--r--lib/sg_pt_freebsd.c55
-rw-r--r--lib/sg_pt_linux.c53
-rw-r--r--lib/sg_pt_osf1.c43
-rw-r--r--lib/sg_pt_solaris.c49
-rw-r--r--lib/sg_pt_win32.c63
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_requests.c171
-rw-r--r--src/sg_stream_ctl.c6
-rw-r--r--src/sg_turs.c126
-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
24 files changed, 2066 insertions, 1508 deletions
diff --git a/ChangeLog b/ChangeLog
index f78cd94c..5a87f875 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,8 +2,11 @@ Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
directory have their own "man" pages. There is also a sg3_utils man page.
-Changelog for sg3_utils-1.46 [20200716] [svn: r856]
+Changelog for sg3_utils-1.46 [20200724] [svn: r857]
- sg_rep_pip: report new provisioning initialization pattern cmd
+ - sg_turs: estimated time-to-ready [20-061r2]
+ - add --delay=MS option
+ - sg_requests: substantial cleanup
- sg_dd: separate category for miscompare errors
- sg_get_elem_status: add ralwd bit sbc4r20a
- sg_write_x: add dld bits to write(32) [sbc4r19a]
@@ -13,6 +16,8 @@ Changelog for sg3_utils-1.46 [20200716] [svn: r856]
- sg_pt_solaris+sg_pt_osf1: fix problem with clear_scsi_pt_obj()
which needs to remember is_nvme and dev_fd values
- sg_lib: restore elements and rebuild command added
+ - sg_lib,sg_pt: add partial_clear_scsi_pt_obj(),
+ get_scsi_pt_cdb_len() and get_scsi_pt_cdb_buf()
- sg_lib: Linux NVMe SNTL: add read, write and verify;
synchronize cache and write same translations
- add dummy start stop unit and test unit ready commands
diff --git a/debian/changelog b/debian/changelog
index eae95a69..26aeef15 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.46-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Mon, 01 Jun 2020 12:00:00 -0400
+ -- Douglas Gilbert <dgilbert@interlog.com> Fri, 24 Jul 2020 12:00:00 -0400
sg3-utils (1.45-0.1) unstable; urgency=low
diff --git a/doc/sg_turs.8 b/doc/sg_turs.8
index 6bf8d9cd..bc6b4fde 100644
--- a/doc/sg_turs.8
+++ b/doc/sg_turs.8
@@ -1,14 +1,14 @@
-.TH SG_TURS "8" "September 2019" "sg3_utils\-1.45" SG3_UTILS
+.TH SG_TURS "8" "July 2020" "sg3_utils\-1.46" SG3_UTILS
.SH NAME
sg_turs \- send one or more SCSI TEST UNIT READY commands
.SH SYNOPSIS
.B sg_turs
-[\fI\-\-help\fR] [\fI\-\-low\fR] [\fI\-\-num=NUM\fR] [\fI\-\-number=NUM\fR]
-[\fI\-\-progress\fR] [\fI\-\-time\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
-\fIDEVICE\fR
+[\fI\-\-delay=MS\fR] [\fI\-\-help\fR] [\fI\-\-low\fR] [\fI\-\-num=NUM\fR]
+[\fI\-\-number=NUM\fR] [\fI\-\-progress\fR] [\fI\-\-time\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
.PP
.B sg_turs
-[\fI\-n=NUM\fR] [\fI\-p\fR] [\fI\-t\fR] [\fI\-v\fR] [\fI\-V\fR]
+[\fI\-d=MS\fR] [\fI\-n=NUM\fR] [\fI\-p\fR] [\fI\-t\fR] [\fI\-v\fR] [\fI\-V\fR]
\fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
@@ -24,6 +24,10 @@ on the old command line syntax outlines the second group of options.
.SH OPTIONS
Arguments to long options are mandatory for short options as well.
.TP
+\fB\-d\fR, \fB\-\-delay\fR=\fIMS\fR
+this option causes a delay of \fIMS\fR milliseconds to occur before each
+TEST UNIT READY command is issued.
+.TP
\fB\-h\fR, \fB\-\-help\fR
print out the usage message then exit.
.TP
@@ -55,6 +59,8 @@ Switch to older style options. Please use as first option.
show progress indication (a percentage) if available. If \fI\-\-num=NUM\fR
is given, \fINUM\fR is greater than 1 and an initial progress indication
was detected then this utility waits 30 seconds before subsequent checks.
+If the \fI\-\-delay=MS\fR option is given then it will wait for that number
+of milliseconds instead of 30 seconds.
Exits when \fINUM\fR is reached or there are no more progress indications.
Ignores \fI\-\-time\fR option. See NOTES section below.
.TP
@@ -101,6 +107,10 @@ options which can be overridden by using \fI\-\-old\fR (or \fI\-O\fR) as the
first option. See the ENVIRONMENT VARIABLES section for another way to
force the use of these older command line options.
.TP
+\fB\-d\fR, \fB\-\-delay\fR=\fIMS\fR
+this option causes a delay of \fIMS\fR milliseconds to occur before each
+TEST UNIT READY command is issued.
+.TP
\fB\-n\fR=\fINUM\fR
performs TEST UNIT READY \fINUM\fR times. If not given defaults to 1.
Equivalent to \fI\-\-num=NUM\fR in the main description.
@@ -130,7 +140,7 @@ using \fI\-\-old\fR (or \fI\-O\fR) as the first command line option.
.SH AUTHORS
Written by D. Gilbert
.SH COPYRIGHT
-Copyright \(co 2000\-2019 Douglas Gilbert
+Copyright \(co 2000\-2020 Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/include/sg_cmds_basic.h b/include/sg_cmds_basic.h
index e40d1011..48aabc9a 100644
--- a/include/sg_cmds_basic.h
+++ b/include/sg_cmds_basic.h
@@ -2,7 +2,7 @@
#define SG_CMDS_BASIC_H
/*
- * Copyright (c) 2004-2019 Douglas Gilbert.
+ * Copyright (c) 2004-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.
@@ -33,7 +33,13 @@ extern "C" {
* sg_pt_base rather than an open file descriptor as their first argument.
* That object is assumed to be constructed and have a device file descriptor
* associated with it. clear_scsi_pt_obj() is called at the start of each
- * "_pt" function. Caller is responsible for lifetime of ptp. */
+ * "_pt" function. Caller is responsible for lifetime of ptp.
+ * If the sense buffer is accessed outside the "_pt" function then the caller
+ * must invoke set_scsi_pt_sense() _prior_ to the "_pt" function. Otherwise
+ * a sense buffer local to "_pt" function is used.
+ * Usually the cdb pointer will be NULL going into the "_pt" functions but
+ * could be given by the caller in which case it will used rather than a
+ * locally generated one. */
struct sg_pt_base;
diff --git a/include/sg_pt.h b/include/sg_pt.h
index 2be9cbf9..8f55fc11 100644
--- a/include/sg_pt.h
+++ b/include/sg_pt.h
@@ -2,7 +2,7 @@
#define SG_PT_H
/*
- * Copyright (c) 2005-2019 Douglas Gilbert.
+ * Copyright (c) 2005-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.
@@ -96,18 +96,23 @@ int get_pt_file_handle(const struct sg_pt_base * objp);
* Use set_pt_file_handle() to change dev_fd. */
void clear_scsi_pt_obj(struct sg_pt_base * objp);
+/* Partially clear state information held in *objp . Any error settings and
+ * the data-in and data-out settings are cleared. So dev_fd, cdb and sense
+ * settings are kept. */
+void partial_clear_scsi_pt_obj(struct sg_pt_base * objp);
+
/* Set the CDB (command descriptor block). May also be a NVMe Admin command
* which will be 64 bytes long.
*
* Note that the sg_cmds_is_nvme() function found in sg_cmds_basic.h can be
* called after this function to "guess" which command set the given command
- * belongs to. */
+ * belongs to. It is valid to supply a cdb value of NULL. */
void set_scsi_pt_cdb(struct sg_pt_base * objp, const uint8_t * cdb,
int cdb_len);
/* Set the sense buffer and the maximum length of that buffer. For NVMe
* commands this "sense" buffer will receive the 4 DWORDs of from the
- * completion queue. */
+ * completion queue. It is valid to supply a sense value of NULL. */
void set_scsi_pt_sense(struct sg_pt_base * objp, uint8_t * sense,
int max_sense_len);
@@ -190,6 +195,12 @@ int get_scsi_pt_status_response(const struct sg_pt_base * objp);
* 'objp' is NULL then returns 0xffffffff. */
uint32_t get_pt_result(const struct sg_pt_base * objp);
+/* These two get functions should just echo what has been given to
+ * set_scsi_pt_cdb(). If it has not been called or clear_scsi_pt_obj()
+ * has been called then return 0 and NULL respectively. */
+int get_scsi_pt_cdb_len(const struct sg_pt_base * objp);
+uint8_t * get_scsi_pt_cdb_buf(const struct sg_pt_base * objp);
+
/* Actual sense length returned. If sense data is present but
actual sense length is not known, return 'max_sense_len' */
int get_scsi_pt_sense_len(const struct sg_pt_base * objp);
diff --git a/lib/sg_cmds_basic.c b/lib/sg_cmds_basic.c
index 323df25d..4521b803 100644
--- a/lib/sg_cmds_basic.c
+++ b/lib/sg_cmds_basic.c
@@ -42,7 +42,7 @@
#endif
-static const char * const version_str = "1.96 20200503";
+static const char * const version_str = "1.97 20200722";
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
@@ -367,10 +367,13 @@ static const char * const inquiry_s = "inquiry";
/* Returns 0 on success, while positive values are SG_LIB_CAT_* errors
* (e.g. SG_LIB_CAT_MALFORMED). If OS error, returns negated errno or -1. */
static int
-sg_ll_inquiry_com(struct sg_pt_base * ptvp, bool cmddt, bool evpd, int pg_op,
- void * resp, int mx_resp_len, int timeout_secs,
+sg_ll_inquiry_com(struct sg_pt_base * ptvp, int sg_fd, bool cmddt, bool evpd,
+ int pg_op, void * resp, int mx_resp_len, int timeout_secs,
int * residp, bool noisy, int verbose)
{
+ bool ptvp_given = false;
+ bool local_sense = true;
+ bool local_cdb = true;
int res, ret, sense_cat, resid;
uint8_t inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
uint8_t sense_b[SENSE_BUFF_LEN];
@@ -403,8 +406,24 @@ sg_ll_inquiry_com(struct sg_pt_base * ptvp, bool cmddt, bool evpd, int pg_op,
}
if (timeout_secs <= 0)
timeout_secs = DEF_PT_TIMEOUT;
- set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
- set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ if (ptvp) {
+ ptvp_given = true;
+ partial_clear_scsi_pt_obj(ptvp);
+ if (get_scsi_pt_cdb_buf(ptvp))
+ local_cdb = false; /* N.B. Ignores locally built cdb */
+ else
+ set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
+ if (get_scsi_pt_sense_buf(ptvp))
+ local_sense = false;
+ else
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ } else {
+ ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
+ if (NULL == ptvp)
+ return sg_convert_errno(ENOMEM);
+ set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ }
set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, -1, timeout_secs, verbose);
ret = sg_cmds_process_resp(ptvp, inquiry_s, res, noisy, verbose,
@@ -435,11 +454,23 @@ sg_ll_inquiry_com(struct sg_pt_base * ptvp, bool cmddt, bool evpd, int pg_op,
if (resid > mx_resp_len) {
pr2ws("%s resid (%d) should never exceed requested "
"len=%d\n", inquiry_s, resid, mx_resp_len);
- return ret ? ret : SG_LIB_CAT_MALFORMED;
+ if (0 == ret)
+ ret = SG_LIB_CAT_MALFORMED;
+ goto fini;
}
/* zero unfilled section of response buffer, based on resid */
memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid);
}
+fini:
+ if (ptvp_given) {
+ if (local_sense) /* stop caller trying to access local sense */
+ set_scsi_pt_sense(ptvp, NULL, 0);
+ if (local_cdb)
+ set_scsi_pt_cdb(ptvp, NULL, 0);
+ } else {
+ if (ptvp)
+ destruct_scsi_pt_obj(ptvp);
+ }
return ret;
}
@@ -450,16 +481,9 @@ int
sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
int mx_resp_len, bool noisy, int verbose)
{
- int ret;
- struct sg_pt_base * ptvp;
-
- ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
- if (NULL == ptvp)
- return sg_convert_errno(ENOMEM);
- ret = sg_ll_inquiry_com(ptvp, cmddt, evpd, pg_op, resp, mx_resp_len,
- 0 /* timeout_sec */, NULL, noisy, verbose);
- destruct_scsi_pt_obj(ptvp);
- return ret;
+ return sg_ll_inquiry_com(NULL, sg_fd, cmddt, evpd, pg_op, resp,
+ mx_resp_len, 0 /* timeout_sec */, NULL, noisy,
+ verbose);
}
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
@@ -478,16 +502,9 @@ sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
int mx_resp_len, int timeout_secs, int * residp,
bool noisy, int verbose)
{
- int ret;
- struct sg_pt_base * ptvp;
-
- ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
- if (NULL == ptvp)
- return sg_convert_errno(ENOMEM);
- ret = sg_ll_inquiry_com(ptvp, false, evpd, pg_op, resp, mx_resp_len,
- timeout_secs, residp, noisy, verbose);
- destruct_scsi_pt_obj(ptvp);
- return ret;
+ return sg_ll_inquiry_com(NULL, sg_fd, false, evpd, pg_op, resp,
+ mx_resp_len, timeout_secs, residp, noisy,
+ verbose);
}
/* Similar to _v2 but takes a pointer to an object (derived from) sg_pt_base.
@@ -498,10 +515,8 @@ sg_ll_inquiry_pt(struct sg_pt_base * ptvp, bool evpd, int pg_op, void * resp,
int mx_resp_len, int timeout_secs, int * residp, bool noisy,
int verbose)
{
- clear_scsi_pt_obj(ptvp);
- return sg_ll_inquiry_com(ptvp, false, evpd, pg_op, resp, mx_resp_len,
+ return sg_ll_inquiry_com(ptvp, -1, false, evpd, pg_op, resp, mx_resp_len,
timeout_secs, residp, noisy, verbose);
-
}
/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
@@ -525,8 +540,8 @@ sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
pr2ws("%s: out of memory\n", __func__);
return sg_convert_errno(ENOMEM);
}
- ret = sg_ll_inquiry_v2(sg_fd, false, 0, inq_resp, SAFE_STD_INQ_RESP_LEN,
- 0, NULL, noisy, verbose);
+ ret = sg_ll_inquiry_com(NULL, sg_fd, false, false, 0, inq_resp,
+ SAFE_STD_INQ_RESP_LEN, 0, NULL, noisy, verbose);
if (inq_data && (0 == ret)) {
inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
@@ -567,8 +582,8 @@ sg_simple_inquiry_pt(struct sg_pt_base * ptvp,
pr2ws("%s: out of memory\n", __func__);
return sg_convert_errno(ENOMEM);
}
- ret = sg_ll_inquiry_pt(ptvp, false, 0, inq_resp, SAFE_STD_INQ_RESP_LEN,
- 0, NULL, noisy, verbose);
+ ret = sg_ll_inquiry_com(ptvp, -1, false, false, 0, inq_resp,
+ SAFE_STD_INQ_RESP_LEN, 0, NULL, noisy, verbose);
if (inq_data && (0 == ret)) {
inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
@@ -589,16 +604,21 @@ sg_simple_inquiry_pt(struct sg_pt_base * ptvp,
}
/* Invokes a SCSI TEST UNIT READY command.
+ * N.B. To access the sense buffer outside this routine then one be
+ * provided by the caller.
* 'pack_id' is just for diagnostics, safe to set to 0.
* Looks for progress indicator if 'progress' non-NULL;
* if found writes value [0..65535] else write -1.
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
* -1 -> other errors */
-int
-sg_ll_test_unit_ready_progress_pt(struct sg_pt_base * ptvp, int pack_id,
- int * progress, bool noisy, int verbose)
+static int
+sg_ll_test_unit_ready_com(struct sg_pt_base * ptvp, int sg_fd, int pack_id,
+ int * progress, bool noisy, int verbose)
{
static const char * const tur_s = "test unit ready";
+ bool ptvp_given = false;
+ bool local_sense = true;
+ bool local_cdb = true;
int res, ret, sense_cat;
uint8_t tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
uint8_t sense_b[SENSE_BUFF_LEN];
@@ -609,10 +629,24 @@ sg_ll_test_unit_ready_progress_pt(struct sg_pt_base * ptvp, int pack_id,
pr2ws(" %s cdb: %s\n", tur_s,
sg_get_command_str(tur_cdb, TUR_CMDLEN, false, sizeof(b), b));
}
-
- clear_scsi_pt_obj(ptvp);
- set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
- set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ if (ptvp) {
+ ptvp_given = true;
+ partial_clear_scsi_pt_obj(ptvp);
+ if (get_scsi_pt_cdb_buf(ptvp))
+ local_cdb = false; /* N.B. Ignores locally built cdb */
+ else
+ set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
+ if (get_scsi_pt_sense_buf(ptvp))
+ local_sense = false;
+ else
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ } else {
+ ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
+ if (NULL == ptvp)
+ return sg_convert_errno(ENOMEM);
+ set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ }
set_scsi_pt_packet_id(ptvp, pack_id);
res = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, tur_s, res, noisy, verbose, &sense_cat);
@@ -636,23 +670,32 @@ sg_ll_test_unit_ready_progress_pt(struct sg_pt_base * ptvp, int pack_id,
}
} else
ret = 0;
+ if (ptvp_given) {
+ if (local_sense) /* stop caller trying to access local sense */
+ set_scsi_pt_sense(ptvp, NULL, 0);
+ if (local_cdb)
+ set_scsi_pt_cdb(ptvp, NULL, 0);
+ } else {
+ if (ptvp)
+ destruct_scsi_pt_obj(ptvp);
+ }
return ret;
}
int
+sg_ll_test_unit_ready_progress_pt(struct sg_pt_base * ptvp, int pack_id,
+ int * progress, bool noisy, int verbose)
+{
+ return sg_ll_test_unit_ready_com(ptvp, -1, pack_id, progress, noisy,
+ verbose);
+}
+
+int
sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
bool noisy, int verbose)
{
- int ret;
- struct sg_pt_base * ptvp;
-
- ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
- if (NULL == ptvp)
- return sg_convert_errno(ENOMEM);
- ret = sg_ll_test_unit_ready_progress_pt(ptvp, pack_id, progress, noisy,
- verbose);
- destruct_scsi_pt_obj(ptvp);
- return ret;
+ return sg_ll_test_unit_ready_com(NULL, sg_fd, pack_id, progress, noisy,
+ verbose);
}
/* Invokes a SCSI TEST UNIT READY command.
@@ -662,24 +705,15 @@ sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
int
sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose)
{
- int ret;
- struct sg_pt_base * ptvp;
-
- ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
- if (NULL == ptvp)
- return sg_convert_errno(ENOMEM);
- ret = sg_ll_test_unit_ready_progress_pt(ptvp, pack_id, NULL, noisy,
- verbose);
- destruct_scsi_pt_obj(ptvp);
- return ret;
+ return sg_ll_test_unit_ready_com(NULL, sg_fd, pack_id, NULL, noisy,
+ verbose);
}
int
sg_ll_test_unit_ready_pt(struct sg_pt_base * ptvp, int pack_id, bool noisy,
int verbose)
{
- return sg_ll_test_unit_ready_progress_pt(ptvp, pack_id, NULL, noisy,
- verbose);
+ return sg_ll_test_unit_ready_com(ptvp, -1, pack_id, NULL, noisy, verbose);
}
/* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various
@@ -689,6 +723,8 @@ sg_ll_request_sense_com(struct sg_pt_base * ptvp, int sg_fd, bool desc,
void * resp, int mx_resp_len, bool noisy, int verbose)
{
bool ptvp_given = false;
+ bool local_cdb = true;
+ bool local_sense = true;
int ret, res, sense_cat;
static const char * const rq_s = "request sense";
uint8_t rs_cdb[REQUEST_SENSE_CMDLEN] =
@@ -709,15 +745,23 @@ sg_ll_request_sense_com(struct sg_pt_base * ptvp, int sg_fd, bool desc,
sg_get_command_str(rs_cdb, REQUEST_SENSE_CMDLEN, false,
sizeof(b), b));
}
- if (ptvp)
+ if (ptvp) {
ptvp_given = true;
- else {
+ if (get_scsi_pt_sense_buf(ptvp))
+ local_cdb = false;
+ else
+ set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
+ if (get_scsi_pt_sense_buf(ptvp))
+ local_sense = false;
+ else
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ } else {
ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
if (NULL == ptvp)
return sg_convert_errno(ENOMEM);
+ set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
}
- set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
- set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, rq_s, res, noisy, verbose, &sense_cat);
@@ -742,7 +786,12 @@ sg_ll_request_sense_com(struct sg_pt_base * ptvp, int sg_fd, bool desc,
} else
ret = 0;
}
- if ((! ptvp_given) && ptvp)
+ if (ptvp_given) {
+ if (local_sense) /* stop caller accessing local sense */
+ set_scsi_pt_sense(ptvp, NULL, 0);
+ if (local_cdb) /* stop caller accessing local sense */
+ set_scsi_pt_cdb(ptvp, NULL, 0);
+ } else if (ptvp)
destruct_scsi_pt_obj(ptvp);
return ret;
}
@@ -759,7 +808,6 @@ int
sg_ll_request_sense_pt(struct sg_pt_base * ptvp, bool desc, void * resp,
int mx_resp_len, bool noisy, int verbose)
{
- clear_scsi_pt_obj(ptvp);
return sg_ll_request_sense_com(ptvp, -1, desc, resp, mx_resp_len,
noisy, verbose);
}
@@ -772,6 +820,8 @@ sg_ll_report_luns_com(struct sg_pt_base * ptvp, int sg_fd, int select_report,
{
static const char * const report_luns_s = "report luns";
bool ptvp_given = false;
+ bool local_cdb = true;
+ bool local_sense = true;
int ret, res, sense_cat;
uint8_t rl_cdb[REPORT_LUNS_CMDLEN] =
{REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -786,12 +836,23 @@ sg_ll_report_luns_com(struct sg_pt_base * ptvp, int sg_fd, int select_report,
sg_get_command_str(rl_cdb, REPORT_LUNS_CMDLEN, false,
sizeof(b), b));
}
- if (ptvp)
+ if (ptvp) {
ptvp_given = true;
- else if (NULL == ((ptvp = create_pt_obj(report_luns_s))))
- return sg_convert_errno(ENOMEM);
- set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
- set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ partial_clear_scsi_pt_obj(ptvp);
+ if (get_scsi_pt_cdb_buf(ptvp))
+ local_cdb = false;
+ else
+ set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
+ if (get_scsi_pt_sense_buf(ptvp))
+ local_sense = false;
+ else
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ } else {
+ if (NULL == ((ptvp = create_pt_obj(report_luns_s))))
+ return sg_convert_errno(ENOMEM);
+ set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ }
set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, report_luns_s, res, noisy, verbose,
@@ -810,8 +871,15 @@ sg_ll_report_luns_com(struct sg_pt_base * ptvp, int sg_fd, int select_report,
}
} else
ret = 0;
- if ((! ptvp_given) && ptvp)
- destruct_scsi_pt_obj(ptvp);
+ if (ptvp_given) {
+ if (local_sense) /* stop caller accessing local sense */
+ set_scsi_pt_sense(ptvp, NULL, 0);
+ if (local_cdb)
+ set_scsi_pt_cdb(ptvp, NULL, 0);
+ } else {
+ if (ptvp)
+ destruct_scsi_pt_obj(ptvp);
+ }
return ret;
}
@@ -834,7 +902,6 @@ int
sg_ll_report_luns_pt(struct sg_pt_base * ptvp, int select_report,
void * resp, int mx_resp_len, bool noisy, int verbose)
{
- clear_scsi_pt_obj(ptvp);
return sg_ll_report_luns_com(ptvp, -1, select_report, resp,
mx_resp_len, noisy, verbose);
}
diff --git a/lib/sg_cmds_basic2.c b/lib/sg_cmds_basic2.c
index c6c8c0bf..ba06920e 100644
--- a/lib/sg_cmds_basic2.c
+++ b/lib/sg_cmds_basic2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999-2019 Douglas Gilbert.
+ * Copyright (c) 1999-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.
@@ -927,23 +927,6 @@ sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code,
return ret;
}
-int
-sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num,
- int power_cond, bool noflush__fl, bool loej, bool start,
- bool noisy, int verbose)
-{
- int ret;
- struct sg_pt_base * ptvp;
-
- ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
- if (NULL == ptvp)
- return sg_convert_errno(ENOMEM);
- ret = sg_ll_start_stop_unit_pt(ptvp, immed, pc_mod__fl_num, power_cond,
- noflush__fl, loej, start, noisy, verbose);
- destruct_scsi_pt_obj(ptvp);
- return ret;
-}
-
/* Invokes a SCSI START STOP UNIT command (SBC + MMC).
* Return of 0 -> success,
* various SG_LIB_CAT_* positive values or -1 -> other errors.
@@ -952,12 +935,15 @@ sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num,
* and fl(mmc) one bit field. This is the cause of the awkardly named
* pc_mod__fl_num and noflush__fl arguments to this function.
* */
-int
-sg_ll_start_stop_unit_pt(struct sg_pt_base * ptvp, bool immed,
- int pc_mod__fl_num, int power_cond, bool noflush__fl,
- bool loej, bool start, bool noisy, int verbose)
+static int
+sg_ll_start_stop_unit_com(struct sg_pt_base * ptvp, int sg_fd, bool immed,
+ int pc_mod__fl_num, int power_cond, bool noflush__fl,
+ bool loej, bool start, bool noisy, int verbose)
{
static const char * const cdb_s = "start stop unit";
+ bool ptvp_given = false;
+ bool local_sense = true;
+ bool local_cdb = true;
int res, ret, sense_cat;
uint8_t ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0};
uint8_t sense_b[SENSE_BUFF_LEN];
@@ -979,10 +965,24 @@ sg_ll_start_stop_unit_pt(struct sg_pt_base * ptvp, bool immed,
sg_get_command_str(ssuBlk, sizeof(ssuBlk), false,
sizeof(b), b));
}
-
- clear_scsi_pt_obj(ptvp);
- set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk));
- set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ if (ptvp) {
+ ptvp_given = true;
+ partial_clear_scsi_pt_obj(ptvp);
+ if (get_scsi_pt_cdb_buf(ptvp))
+ local_cdb = false; /* N.B. Ignores locally built cdb */
+ else
+ set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk));
+ if (get_scsi_pt_sense_buf(ptvp))
+ local_sense = false;
+ else
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ } else {
+ ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
+ if (NULL == ptvp)
+ return sg_convert_errno(ENOMEM);
+ set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ }
res = do_scsi_pt(ptvp, -1, START_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat);
if (-1 == ret)
@@ -999,9 +999,38 @@ sg_ll_start_stop_unit_pt(struct sg_pt_base * ptvp, bool immed,
}
} else
ret = 0;
+ if (ptvp_given) {
+ if (local_sense) /* stop caller trying to access local sense */
+ set_scsi_pt_sense(ptvp, NULL, 0);
+ if (local_cdb)
+ set_scsi_pt_cdb(ptvp, NULL, 0);
+ } else {
+ if (ptvp)
+ destruct_scsi_pt_obj(ptvp);
+ }
return ret;
}
+int
+sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num,
+ int power_cond, bool noflush__fl, bool loej, bool start,
+ bool noisy, int verbose)
+{
+ return sg_ll_start_stop_unit_com(NULL, sg_fd, immed, pc_mod__fl_num,
+ power_cond, noflush__fl, loej, start,
+ noisy, verbose);
+}
+
+int
+sg_ll_start_stop_unit_pt(struct sg_pt_base * ptvp, bool immed,
+ int pc_mod__fl_num, int power_cond, bool noflush__fl,
+ bool loej, bool start, bool noisy, int verbose)
+{
+ return sg_ll_start_stop_unit_com(ptvp, -1, immed, pc_mod__fl_num,
+ power_cond, noflush__fl, loej, start,
+ noisy, verbose);
+}
+
/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command
* [was in SPC-3 but displaced from SPC-4 into SBC-3, MMC-5, SSC-3]
* prevent==0 allows removal, prevent==1 prevents removal ...
diff --git a/lib/sg_cmds_extra.c b/lib/sg_cmds_extra.c
index 71af0358..7874dd16 100644
--- a/lib/sg_cmds_extra.c
+++ b/lib/sg_cmds_extra.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999-2019 Douglas Gilbert.
+ * Copyright (c) 1999-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.
@@ -448,12 +448,15 @@ sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg,
* value is taken as the timeout value in seconds. Return of 0 -> success,
* various SG_LIB_CAT_* positive values or -1 -> other errors */
int
-sg_ll_send_diag_pt(struct sg_pt_base * ptvp, int st_code, bool pf_bit,
- bool st_bit, bool devofl_bit, bool unitofl_bit,
- int long_duration, void * paramp, int param_len,
- bool noisy, int vb)
+sg_ll_send_diag_com(struct sg_pt_base * ptvp, int sg_fd, int st_code,
+ bool pf_bit, bool st_bit, bool devofl_bit,
+ bool unitofl_bit, int long_duration, void * paramp,
+ int param_len, bool noisy, int vb)
{
static const char * const cdb_s = "Send diagnostic";
+ bool ptvp_given = false;
+ bool local_sense = true;
+ bool local_cdb = true;
int res, ret, s_cat, tmout;
uint8_t senddiag_cdb[SEND_DIAGNOSTIC_CMDLEN] =
{SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0};
@@ -488,9 +491,24 @@ sg_ll_send_diag_pt(struct sg_pt_base * ptvp, int st_code, bool pf_bit,
pr2ws(" %s timeout: %d seconds\n", cdb_s, tmout);
}
}
-
- set_scsi_pt_cdb(ptvp, senddiag_cdb, sizeof(senddiag_cdb));
- set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ if (ptvp) {
+ ptvp_given = true;
+ partial_clear_scsi_pt_obj(ptvp);
+ if (get_scsi_pt_cdb_buf(ptvp))
+ local_cdb = false; /* N.B. Ignores locally built cdb */
+ else
+ set_scsi_pt_cdb(ptvp, senddiag_cdb, sizeof(senddiag_cdb));
+ if (get_scsi_pt_sense_buf(ptvp))
+ local_sense = false;
+ else
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ } else {
+ ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb);
+ if (NULL == ptvp)
+ return sg_convert_errno(ENOMEM);
+ set_scsi_pt_cdb(ptvp, senddiag_cdb, sizeof(senddiag_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ }
set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len);
res = do_scsi_pt(ptvp, -1, tmout, vb);
ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat);
@@ -509,34 +527,49 @@ sg_ll_send_diag_pt(struct sg_pt_base * ptvp, int st_code, bool pf_bit,
} else
ret = 0;
+ if (ptvp_given) {
+ if (local_sense) /* stop caller trying to access local sense */
+ set_scsi_pt_sense(ptvp, NULL, 0);
+ if (local_cdb)
+ set_scsi_pt_cdb(ptvp, NULL, 0);
+ } else {
+ if (ptvp)
+ destruct_scsi_pt_obj(ptvp);
+ }
return ret;
}
int
+sg_ll_send_diag_pt(struct sg_pt_base * ptvp, int st_code, bool pf_bit,
+ bool st_bit, bool devofl_bit, bool unitofl_bit,
+ int long_duration, void * paramp, int param_len,
+ bool noisy, int vb)
+{
+ return sg_ll_send_diag_com(ptvp, -1, st_code, pf_bit, st_bit, devofl_bit,
+ unitofl_bit, long_duration, paramp,
+ param_len, noisy, vb);
+}
+
+int
sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit,
bool devofl_bit, bool unitofl_bit, int long_duration,
void * paramp, int param_len, bool noisy, int vb)
{
- int ret;
- struct sg_pt_base * ptvp;
-
- ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb);
- if (NULL == ptvp)
- return sg_convert_errno(ENOMEM);
- ret = sg_ll_send_diag_pt(ptvp, st_code, pf_bit, st_bit, devofl_bit,
- unitofl_bit, long_duration, paramp, param_len,
- noisy, vb);
- destruct_scsi_pt_obj(ptvp);
- return ret;
+ return sg_ll_send_diag_com(NULL, sg_fd, st_code, pf_bit, st_bit,
+ devofl_bit, unitofl_bit, long_duration, paramp,
+ param_len, noisy, vb);
}
/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
* various SG_LIB_CAT_* positive values or -1 -> other errors */
-int
-sg_ll_receive_diag_pt(struct sg_pt_base * ptvp, bool pcv, int pg_code,
- void * resp, int mx_resp_len, int timeout_secs,
- int * residp, bool noisy, int vb)
+static int
+sg_ll_receive_diag_com(struct sg_pt_base * ptvp, int sg_fd, bool pcv,
+ int pg_code, void * resp, int mx_resp_len,
+ int timeout_secs, int * residp, bool noisy, int vb)
{
+ bool ptvp_given = false;
+ bool local_sense = true;
+ bool local_cdb = true;
int resid = 0;
int res, ret, s_cat;
static const char * const cdb_s = "Receive diagnostic results";
@@ -559,8 +592,24 @@ sg_ll_receive_diag_pt(struct sg_pt_base * ptvp, bool pcv, int pg_code,
if (timeout_secs <= 0)
timeout_secs = DEF_PT_TIMEOUT;
- set_scsi_pt_cdb(ptvp, rcvdiag_cdb, sizeof(rcvdiag_cdb));
- set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ if (ptvp) {
+ ptvp_given = true;
+ partial_clear_scsi_pt_obj(ptvp);
+ if (get_scsi_pt_cdb_buf(ptvp))
+ local_cdb = false; /* N.B. Ignores locally built cdb */
+ else
+ set_scsi_pt_cdb(ptvp, rcvdiag_cdb, sizeof(rcvdiag_cdb));
+ if (get_scsi_pt_sense_buf(ptvp))
+ local_sense = false;
+ else
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ } else {
+ ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb);
+ if (NULL == ptvp)
+ return sg_convert_errno(ENOMEM);
+ set_scsi_pt_cdb(ptvp, rcvdiag_cdb, sizeof(rcvdiag_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ }
set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, -1, timeout_secs, vb);
ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, vb, &s_cat);
@@ -593,9 +642,27 @@ sg_ll_receive_diag_pt(struct sg_pt_base * ptvp, bool pcv, int pg_code,
}
ret = 0;
}
+
+ if (ptvp_given) {
+ if (local_sense) /* stop caller trying to access local sense */
+ set_scsi_pt_sense(ptvp, NULL, 0);
+ if (local_cdb)
+ set_scsi_pt_cdb(ptvp, NULL, 0);
+ } else {
+ if (ptvp)
+ destruct_scsi_pt_obj(ptvp);
+ }
return ret;
}
+int
+sg_ll_receive_diag_pt(struct sg_pt_base * ptvp, bool pcv, int pg_code,
+ void * resp, int mx_resp_len, int timeout_secs,
+ int * residp, bool noisy, int vb)
+{
+ return sg_ll_receive_diag_com(ptvp, -1, pcv, pg_code, resp, mx_resp_len,
+ timeout_secs, residp, noisy, vb);
+}
/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
* various SG_LIB_CAT_* positive values or -1 -> other errors */
@@ -603,16 +670,8 @@ int
sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp,
int mx_resp_len, bool noisy, int vb)
{
- int ret;
- struct sg_pt_base * ptvp;
-
- ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb);
- if (NULL == ptvp)
- return sg_convert_errno(ENOMEM);
- ret = sg_ll_receive_diag_pt(ptvp, pcv, pg_code, resp, mx_resp_len, 0,
- NULL, noisy, vb);
- destruct_scsi_pt_obj(ptvp);
- return ret;
+ return sg_ll_receive_diag_com(NULL, sg_fd, pcv, pg_code, resp,
+ mx_resp_len, 0, NULL, noisy, vb);
}
int
@@ -620,16 +679,9 @@ sg_ll_receive_diag_v2(int sg_fd, bool pcv, int pg_code, void * resp,
int mx_resp_len, int timeout_secs, int * residp,
bool noisy, int vb)
{
- int ret;
- struct sg_pt_base * ptvp;
-
- ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb);
- if (NULL == ptvp)
- return sg_convert_errno(ENOMEM);
- ret = sg_ll_receive_diag_pt(ptvp, pcv, pg_code, resp, mx_resp_len,
- timeout_secs, residp, noisy, vb);
- destruct_scsi_pt_obj(ptvp);
- return ret;
+ return sg_ll_receive_diag_com(NULL, sg_fd, pcv, pg_code, resp,
+ mx_resp_len, timeout_secs, residp, noisy,
+ vb);
}
/* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 -> success
diff --git a/lib/sg_pt_common.c b/lib/sg_pt_common.c
index 4d803207..d469ef6a 100644
--- a/lib/sg_pt_common.c
+++ b/lib/sg_pt_common.c
@@ -31,7 +31,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * scsi_pt_version_str = "3.15 20200412";
+static const char * scsi_pt_version_str = "3.16 20200722";
const char *
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index a36b45b5..4d2e5887 100644
--- a/lib/sg_pt_freebsd.c
+++ b/lib/sg_pt_freebsd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005-2019 Douglas Gilbert.
+ * Copyright (c) 2005-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.
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_freebsd version 1.35 20190210 */
+/* sg_pt_freebsd version 1.36 20200724 */
#include <stdio.h>
#include <stdlib.h>
@@ -452,6 +452,32 @@ clear_scsi_pt_obj(struct sg_pt_base * vp)
}
}
+void
+partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+ struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+ if (NULL == ptp)
+ return;
+ ptp->in_err = 0;
+ ptp->os_err = 0;
+ ptp->transport_err = 0;
+ if (ptp->nvme_direct) {
+ struct freebsd_dev_channel *fdc_p;
+
+ fdc_p = get_fdc_p(ptp);
+ if (fdc_p)
+ fdc_p->nvme_result = 0;
+ } else {
+ ptp->scsi_status = 0;
+ ptp->dxfer_dir = CAM_DIR_NONE;
+ ptp->dxferip = NULL;
+ ptp->dxfer_ilen = 0;
+ ptp->dxferop = NULL;
+ ptp->dxfer_olen = 0;
+ }
+}
+
/* Forget any previous dev_han and install the one given. May attempt to
* find file type (e.g. if pass-though) from OS so there could be an error.
* Returns 0 for success or the same value as get_scsi_pt_os_err()
@@ -511,21 +537,36 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb, int cdb_len)
{
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- if (ptp->cdb)
- ++ptp->in_err;
ptp->cdb = (uint8_t *)cdb;
ptp->cdb_len = cdb_len;
}
+int
+get_scsi_pt_cdb_len(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+ return ptp->cdb_len;
+}
+
+uint8_t *
+get_scsi_pt_cdb_buf(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+ return ptp->cdb;
+}
+
void
set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense,
int max_sense_len)
{
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- if (ptp->sense)
- ++ptp->in_err;
- memset(sense, 0, max_sense_len);
+ if (sense) {
+ if (max_sense_len > 0)
+ memset(sense, 0, max_sense_len);
+ }
ptp->sense = sense;
ptp->sense_len = max_sense_len;
}
diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c
index c96c1d1b..9e8a571e 100644
--- a/lib/sg_pt_linux.c
+++ b/lib/sg_pt_linux.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005-2019 Douglas Gilbert.
+ * Copyright (c) 2005-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.
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_linux version 1.47 20190612 */
+/* sg_pt_linux version 1.48 20200722 */
#include <stdio.h>
@@ -477,6 +477,28 @@ clear_scsi_pt_obj(struct sg_pt_base * vp)
}
}
+void
+partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+ struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+ if (NULL == ptp)
+ return;
+ ptp->in_err = 0;
+ ptp->os_err = 0;
+ if (ptp->nvme_direct)
+ ptp->nvme_result = 0;
+ else {
+ ptp->io_hdr.device_status = 0;
+ ptp->io_hdr.transport_status = 0;
+ ptp->io_hdr.driver_status = 0;
+ ptp->io_hdr.din_xferp = 0;
+ ptp->io_hdr.din_xfer_len = 0;
+ ptp->io_hdr.dout_xferp = 0;
+ ptp->io_hdr.dout_xfer_len = 0;
+ }
+}
+
#ifndef SG_SET_GET_EXTENDED
/* If both sei_wr_mask and sei_rd_mask are 0, this ioctl does nothing */
@@ -619,21 +641,36 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb,
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
- if (ptp->io_hdr.request)
- ++ptp->in_err;
ptp->io_hdr.request = (__u64)(sg_uintptr_t)cdb;
ptp->io_hdr.request_len = cdb_len;
}
+int
+get_scsi_pt_cdb_len(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+ return ptp->io_hdr.request_len;
+}
+
+uint8_t *
+get_scsi_pt_cdb_buf(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+ return (uint8_t *)ptp->io_hdr.request;
+}
+
void
set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense,
int max_sense_len)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
- if (ptp->io_hdr.response)
- ++ptp->in_err;
- memset(sense, 0, max_sense_len);
+ if (sense) {
+ if (max_sense_len > 0)
+ memset(sense, 0, max_sense_len);
+ }
ptp->io_hdr.response = (__u64)(sg_uintptr_t)sense;
ptp->io_hdr.max_response_len = max_sense_len;
}
@@ -686,7 +723,7 @@ set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
- ptp->io_hdr.request_extra = pack_id; /* was place in spare_in */
+ ptp->io_hdr.request_extra = pack_id; /* was placed in spare_in */
}
void
diff --git a/lib/sg_pt_osf1.c b/lib/sg_pt_osf1.c
index edf79d11..5901b16b 100644
--- a/lib/sg_pt_osf1.c
+++ b/lib/sg_pt_osf1.c
@@ -30,7 +30,7 @@
#include "sg_lib.h"
#include "sg_pr2serr.h"
-/* Version 2.02 20200713 */
+/* Version 2.03 20200722 */
#define OSF1_MAXDEV 64
@@ -219,26 +219,57 @@ clear_scsi_pt_obj(struct sg_pt_base * vp)
}
void
+partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+ struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+ if (NULL == ptp)
+ return;
+ ptp->in_err = 0;
+ ptp->os_err = 0;
+ ptp->transport_err = 0;
+ ptp->scsi_status = 0;
+ ptp->dxfer_dir = CAM_DIR_NONE;
+ ptp->dxferp = NULL;
+ ptp->dxfer_len = 0;
+}
+
+void
set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb,
int cdb_len)
{
struct sg_pt_osf1_scsi * ptp = &vp->impl;
- if (ptp->cdb)
- ++ptp->in_err;
ptp->cdb = (uint8_t *)cdb;
ptp->cdb_len = cdb_len;
}
+int
+get_scsi_pt_cdb_len(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+ return ptp->cdb_len;
+}
+
+uint8_t *
+get_scsi_pt_cdb_buf(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+ return ptp->cdb;
+}
+
void
set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense,
int max_sense_len)
{
struct sg_pt_osf1_scsi * ptp = &vp->impl;
- if (ptp->sense)
- ++ptp->in_err;
- bzero(sense, max_sense_len);
+ if (sense) {
+ if (max_sense_len > 0)
+ bzero(sense, max_sense_len);
+ }
ptp->sense = sense;
ptp->sense_len = max_sense_len;
}
diff --git a/lib/sg_pt_solaris.c b/lib/sg_pt_solaris.c
index f1956249..c2444997 100644
--- a/lib/sg_pt_solaris.c
+++ b/lib/sg_pt_solaris.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2019 Douglas Gilbert.
+ * Copyright (c) 2007-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.
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_solaris version 1.13 20200713 */
+/* sg_pt_solaris version 1.14 20200724 */
#include <stdio.h>
#include <stdlib.h>
@@ -148,26 +148,55 @@ clear_scsi_pt_obj(struct sg_pt_base * vp)
}
void
+partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+ struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+ if (ptp) {
+ ptp->in_err = 0;
+ ptp->os_err = 0;
+ ptp->uscsi.uscsi_status = 0;
+ ptp->uscsi.uscsi_bufaddr = NULL;
+ ptp->uscsi.uscsi_buflen = 0;
+ ptp->uscsi.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE |
+ USCSI_RQENABLE;
+ }
+}
+
+void
set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb,
int cdb_len)
{
struct sg_pt_solaris_scsi * ptp = &vp->impl;
- if (ptp->uscsi.uscsi_cdb)
- ++ptp->in_err;
ptp->uscsi.uscsi_cdb = (char *)cdb;
ptp->uscsi.uscsi_cdblen = cdb_len;
}
+int
+get_scsi_pt_cdb_len(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+ return ptp->uscsi.uscsi_cdblen;
+}
+
+uint8_t *
+get_scsi_pt_cdb_buf(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+ return (uint8_t *)ptp->uscsi.uscsi_cdb;
+}
+
void
set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense,
int max_sense_len)
{
struct sg_pt_solaris_scsi * ptp = &vp->impl;
- if (ptp->uscsi.uscsi_rqbuf)
- ++ptp->in_err;
- memset(sense, 0, max_sense_len);
+ if (sense && (max_sense_len > 0))
+ memset(sense, 0, max_sense_len);
ptp->uscsi.uscsi_rqbuf = (char *)sense;
ptp->uscsi.uscsi_rqlen = max_sense_len;
ptp->max_sense_len = max_sense_len;
@@ -185,7 +214,8 @@ set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp,
if (dxfer_len > 0) {
ptp->uscsi.uscsi_bufaddr = (char *)dxferp;
ptp->uscsi.uscsi_buflen = dxfer_len;
- ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
+ ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_DIAGNOSE |
+ USCSI_RQENABLE;
}
}
@@ -201,7 +231,8 @@ set_scsi_pt_data_out(struct sg_pt_base * vp, const uint8_t * dxferp,
if (dxfer_len > 0) {
ptp->uscsi.uscsi_bufaddr = (char *)dxferp;
ptp->uscsi.uscsi_buflen = dxfer_len;
- ptp->uscsi.uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | USCSI_RQENABLE;
+ ptp->uscsi.uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | USCSI_DIAGNOSE |
+ USCSI_RQENABLE;
}
}
diff --git a/lib/sg_pt_win32.c b/lib/sg_pt_win32.c
index 41295fc6..d01f7a81 100644
--- a/lib/sg_pt_win32.c
+++ b/lib/sg_pt_win32.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006-2019 Douglas Gilbert.
+ * Copyright (c) 2006-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.
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_win32 version 1.30 20190210 */
+/* sg_pt_win32 version 1.31 20200723 */
#include <stdio.h>
#include <stdlib.h>
@@ -864,6 +864,32 @@ clear_scsi_pt_obj(struct sg_pt_base * vp)
}
void
+partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+ struct sg_pt_win32_scsi * psp = &vp->impl;
+
+ if (NULL == psp)
+ return;
+ psp->in_err = 0;
+ psp->os_err = 0;
+ psp->transport_err = 0;
+ psp->scsi_status = 0;
+ if (spt_direct) {
+ psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+ psp->swb_d.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN;
+ psp->swb_d.spt.SenseInfoOffset =
+ offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+ psp->swb_d.spt.TimeOutValue = DEF_TIMEOUT;
+ } else {
+ psp->swb_i.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+ psp->swb_i.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN;
+ psp->swb_i.spt.SenseInfoOffset =
+ offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+ psp->swb_i.spt.TimeOutValue = DEF_TIMEOUT;
+ }
+}
+
+void
set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb,
int cdb_len)
{
@@ -871,14 +897,9 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb,
struct sg_pt_win32_scsi * psp = vp->implp;
if (! scsi_cdb) {
- if (psp->have_nvme_cmd)
- ++psp->in_err;
- else
- psp->have_nvme_cmd = true;
+ psp->have_nvme_cmd = true;
memcpy(psp->nvme_cmd, cdb, cdb_len);
} else if (spt_direct) {
- if (psp->swb_d.spt.CdbLength > 0)
- ++psp->in_err;
if (cdb_len > (int)sizeof(psp->swb_d.spt.Cdb)) {
++psp->in_err;
return;
@@ -886,8 +907,6 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb,
memcpy(psp->swb_d.spt.Cdb, cdb, cdb_len);
psp->swb_d.spt.CdbLength = cdb_len;
} else {
- if (psp->swb_i.spt.CdbLength > 0)
- ++psp->in_err;
if (cdb_len > (int)sizeof(psp->swb_i.spt.Cdb)) {
++psp->in_err;
return;
@@ -897,15 +916,29 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb,
}
}
+int
+get_scsi_pt_cdb_len(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_win32_scsi * psp = &vp->impl;
+
+ return spt_direct ? psp->swb_d.spt.CdbLength : psp->swb_i.spt.CdbLength;
+}
+
+uint8_t *
+get_scsi_pt_cdb_buf(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_win32_scsi * psp = &vp->impl;
+
+ return (uint8_t *)(spt_direct ? psp->swb_d.spt.Cdb : psp->swb_i.spt.Cdb);
+}
+
void
-set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense,
- int sense_len)
+set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense, int sense_len)
{
struct sg_pt_win32_scsi * psp = vp->implp;
- if (psp->sensep)
- ++psp->in_err;
- memset(sense, 0, sense_len);
+ if (sense && (sense_len > 0))
+ memset(sense, 0, sense_len);
psp->sensep = sense;
psp->sense_len = sense_len;
}
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 09ccd98b..0dccb8e2 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -84,7 +84,7 @@ fi
%{_libdir}/*.la
%changelog
-* Mon Jun 01 2020 - dgilbert at interlog dot com
+* Fri Jul 24 2020 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.46
diff --git a/src/sg_requests.c b/src/sg_requests.c
index 331c84d8..37ce1668 100644
--- a/src/sg_requests.c
+++ b/src/sg_requests.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2019 Douglas Gilbert.
+ * Copyright (c) 2004-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.
@@ -34,7 +34,7 @@
* This program issues the SCSI command REQUEST SENSE to the given SCSI device.
*/
-static const char * version_str = "1.34 20190706";
+static const char * version_str = "1.35 20200723";
#define MAX_REQS_RESP_LEN 255
#define DEF_REQS_RESP_LEN 252
@@ -126,19 +126,22 @@ dStrRaw(const uint8_t * str, int len)
int
main(int argc, char * argv[])
{
- int c, n, resp_len, k, progress, rs, sense_cat;
+ int c, n, k, progress, rs, sense_cat, act_din_len;
int do_error = 0;
int err = 0;
int num_errs = 0;
+ int num_din_errs = 0;
+ int most_recent_skey = 0;
int sg_fd = -1;
int res = 0;
- uint8_t requestSenseBuff[MAX_REQS_RESP_LEN + 1];
+ uint8_t rsBuff[MAX_REQS_RESP_LEN + 1];
bool desc = false;
bool do_progress = false;
bool do_raw = false;
bool do_status = false;
bool verbose_given = false;
bool version_given = false;
+ bool not_raw_hex;
int num_rs = 1;
int do_hex = 0;
int maxlen = 0;
@@ -146,7 +149,7 @@ main(int argc, char * argv[])
const char * device_name = NULL;
int ret = 0;
struct sg_pt_base * ptvp = NULL;
- char b[80];
+ char b[256];
uint8_t rs_cdb[REQUEST_SENSE_CMDLEN] =
{REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
uint8_t sense_b[SENSE_BUFF_LEN];
@@ -265,10 +268,20 @@ main(int argc, char * argv[])
return SG_LIB_FILE_ERROR;
}
}
+ if (do_raw || do_hex) {
+ not_raw_hex = false;
+ if (do_progress || do_time) {
+ pr2serr("With either --raw or --hex, --progress and --time "
+ "contradict\n");
+ ret = SG_LIB_CONTRADICT;
+ goto finish;
+ }
+ } else
+ not_raw_hex = true;
sg_fd = sg_cmds_open_device(device_name, true /* ro */, verbose);
if (sg_fd < 0) {
- if (verbose)
+ if (not_raw_hex && verbose)
pr2serr(ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
ret = sg_convert_errno(-sg_fd);
@@ -276,7 +289,8 @@ main(int argc, char * argv[])
}
ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
if ((NULL == ptvp) || ((err = get_scsi_pt_os_err(ptvp)))) {
- pr2serr("%s: unable to construct pt object\n", __func__);
+ if (not_raw_hex)
+ pr2serr("%s: unable to construct pt object\n", __func__);
ret = sg_convert_errno(err ? err : ENOMEM);
goto finish;
}
@@ -287,18 +301,25 @@ main(int argc, char * argv[])
rs_cdb[4] = maxlen;
if (do_progress) {
for (k = 0; k < num_rs; ++k) {
+ act_din_len = 0;
if (k > 0)
sleep_for(30);
set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
- memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
- set_scsi_pt_data_in(ptvp, requestSenseBuff,
- sizeof(requestSenseBuff));
+ memset(rsBuff, 0x0, sizeof(rsBuff));
+ set_scsi_pt_data_in(ptvp, rsBuff, sizeof(rsBuff));
set_scsi_pt_packet_id(ptvp, k + 1);
if (do_error > 1) {
++num_errs;
n = 0;
} else {
+ if (verbose && (0 == k)) {
+ char b[128];
+
+ pr2serr(" cdb: %s\n",
+ sg_get_command_str(rs_cdb, REQUEST_SENSE_CMDLEN,
+ true, sizeof(b), b));
+ }
rs = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose);
n = sg_cmds_process_resp(ptvp, "Request sense", rs, (0 == k),
verbose, &sense_cat);
@@ -336,36 +357,17 @@ main(int argc, char * argv[])
break;
}
}
- clear_scsi_pt_obj(ptvp);
+ if (n >= 0)
+ act_din_len = n;
if (ret)
goto finish;
-#if 0
- res = sg_ll_request_sense(sg_fd, desc, requestSenseBuff, maxlen,
- true, verbose);
- if (res) {
- ret = res;
- if (SG_LIB_CAT_INVALID_OP == res)
- pr2serr("Request Sense command not supported\n");
- else if (SG_LIB_CAT_ILLEGAL_REQ == res)
- pr2serr("bad field in Request Sense cdb\n");
- else if (SG_LIB_CAT_ABORTED_COMMAND == res)
- pr2serr("Request Sense, aborted command\n");
- else {
- sg_get_category_sense_str(res, sizeof(b), b, verbose);
- pr2serr("Request Sense command: %s\n", b);
- }
- break;
- }
-#endif
- /* "Additional sense length" same in descriptor and fixed */
- resp_len = requestSenseBuff[7] + 8;
if (verbose > 1) {
pr2serr("Parameter data in hex\n");
- hex2stderr(requestSenseBuff, resp_len, 1);
+ hex2stderr(rsBuff, act_din_len, 1);
}
progress = -1;
- sg_get_sense_progress_fld(requestSenseBuff, resp_len, &progress);
+ sg_get_sense_progress_fld(rsBuff, act_din_len, &progress);
if (progress < 0) {
ret = res;
if (verbose > 1)
@@ -377,32 +379,40 @@ main(int argc, char * argv[])
printf("Progress indication: %d.%02d%% done\n",
(progress * 100) / 65536,
((progress * 100) % 65536) / 656);
- }
+ partial_clear_scsi_pt_obj(ptvp);
+ } /* >>>>> end of for(num_rs) loop */
goto finish;
}
#ifndef SG_LIB_MINGW
- if (do_time) {
+ if (not_raw_hex && do_time) {
start_tm.tv_sec = 0;
start_tm.tv_usec = 0;
gettimeofday(&start_tm, NULL);
}
#endif
- requestSenseBuff[0] = '\0';
- requestSenseBuff[7] = '\0';
+ rsBuff[0] = '\0';
+ rsBuff[7] = '\0';
for (k = 0; k < num_rs; ++k) {
- memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
+ act_din_len = 0;
+ ret = 0;
set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
- memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
- set_scsi_pt_data_in(ptvp, requestSenseBuff,
- sizeof(requestSenseBuff));
+ memset(rsBuff, 0x0, sizeof(rsBuff));
+ set_scsi_pt_data_in(ptvp, rsBuff, sizeof(rsBuff));
set_scsi_pt_packet_id(ptvp, k + 1);
if (do_error > 1) {
++num_errs;
n = 0;
} else {
+ if (verbose && (0 == k)) {
+ char b[128];
+
+ pr2serr(" cdb: %s\n",
+ sg_get_command_str(rs_cdb, REQUEST_SENSE_CMDLEN,
+ true, sizeof(b), b));
+ }
rs = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose);
n = sg_cmds_process_resp(ptvp, "Request sense", rs, (0 == k),
verbose, &sense_cat);
@@ -440,55 +450,50 @@ main(int argc, char * argv[])
break;
}
}
- clear_scsi_pt_obj(ptvp);
- if (ret)
- goto finish;
+ if (n >= 0)
+ act_din_len = n;
- // res = sg_ll_request_sense(sg_fd, desc, requestSenseBuff, maxlen,
- // true, verbose);
- // ret = res;
- resp_len = requestSenseBuff[7] + 8;
- if (do_raw)
- dStrRaw(requestSenseBuff, resp_len);
- else if (do_hex)
- hex2stdout(requestSenseBuff, resp_len, 1);
- else if (1 == num_rs) {
- pr2serr("Decode parameter data as sense data:\n");
- sg_print_sense(NULL, requestSenseBuff, resp_len, 0);
- if (verbose > 1) {
- pr2serr("\nParameter data in hex\n");
- hex2stderr(requestSenseBuff, resp_len, 1);
+ if (act_din_len > 7) {
+ struct sg_scsi_sense_hdr ssh;
+
+ if (sg_scsi_normalize_sense(rsBuff, act_din_len, &ssh)) {
+ if (ssh.sense_key > 0) {
+ ++num_din_errs;
+ most_recent_skey = ssh.sense_key;
+ }
+ if (not_raw_hex && ((1 == num_rs) || verbose)) {
+ char b[144];
+
+ sg_get_sense_str(NULL, rsBuff, act_din_len,
+ false, sizeof(b), b);
+ pr2serr("data-in decoded as sense:\n%s\n", b);
+ }
}
}
-#if 0
- continue;
- else if (SG_LIB_CAT_INVALID_OP == res)
- pr2serr("Request Sense command not supported\n");
- else if (SG_LIB_CAT_ILLEGAL_REQ == res)
- pr2serr("bad field in Request Sense cdb\n");
- else if (SG_LIB_CAT_ABORTED_COMMAND == res)
- pr2serr("Request Sense, aborted command\n");
- else {
- sg_get_category_sense_str(res, sizeof(b), b, verbose);
- pr2serr("Request Sense command: %s\n", b);
+ partial_clear_scsi_pt_obj(ptvp);
+ if (ret)
+ goto finish;
+
+ if (act_din_len > 0) {
+ if (do_raw)
+ dStrRaw(rsBuff, act_din_len);
+ else if (do_hex)
+ hex2stdout(rsBuff, act_din_len, 1);
}
- break;
-#endif
- }
+ } /* <<<<< end of for(num_rs) loop */
if ((0 == ret) && do_status) {
- resp_len = requestSenseBuff[7] + 8;
- ret = sg_err_category_sense(requestSenseBuff, resp_len);
+ ret = sg_err_category_sense(rsBuff, act_din_len);
if (SG_LIB_CAT_NO_SENSE == ret) {
struct sg_scsi_sense_hdr ssh;
- if (sg_scsi_normalize_sense(requestSenseBuff, resp_len, &ssh)) {
+ if (sg_scsi_normalize_sense(rsBuff, act_din_len, &ssh)) {
if ((0 == ssh.asc) && (0 == ssh.ascq))
ret = 0;
}
}
}
#ifndef SG_LIB_MINGW
- if (do_time && (start_tm.tv_sec || start_tm.tv_usec)) {
+ if (not_raw_hex && do_time && (start_tm.tv_sec || start_tm.tv_usec)) {
struct timeval res_tm;
double den, num;
@@ -512,17 +517,23 @@ main(int argc, char * argv[])
#endif
finish:
- if (num_errs > 0)
- printf("Number of errors detected: %d\n", num_errs);
+ if (not_raw_hex) {
+ if (num_errs > 0)
+ printf("Number of command errors detected: %d\n", num_errs);
+ if (num_din_errs > 0)
+ printf("Number of data-in errors detected: %d, most recent "
+ "sense_key=%d\n", num_din_errs, most_recent_skey);
+ }
if (sg_fd >= 0) {
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
- pr2serr("close error: %s\n", safe_strerror(-res));
+ if (not_raw_hex)
+ pr2serr("close error: %s\n", safe_strerror(-res));
if (0 == ret)
ret = sg_convert_errno(-res);
}
}
- if (0 == verbose) {
+ if (not_raw_hex && (0 == verbose)) {
if (! sg_if_can2stderr("sg_requests failed: ", ret))
pr2serr("Some error occurred, try again with '-v' "
"or '-vv' for more information\n");
diff --git a/src/sg_stream_ctl.c b/src/sg_stream_ctl.c
index 4179aa7f..61bb40f8 100644
--- a/src/sg_stream_ctl.c
+++ b/src/sg_stream_ctl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 Douglas Gilbert.
+ * Copyright (c) 2018-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.
@@ -35,7 +35,7 @@
* to the given SCSI device. Based on sbc4r15.pdf .
*/
-static const char * version_str = "1.08 20191220";
+static const char * version_str = "1.09 20200724";
#define STREAM_CONTROL_SA 0x14
#define GET_STREAM_STATUS_SA 0x16
@@ -294,7 +294,7 @@ main(int argc, char * argv[])
return 0;
case 'i':
k = sg_get_num(optarg);
- if ((k < 0) || (k > UINT16_MAX)) {
+ if ((k < 0) || (k > (int)UINT16_MAX)) {
pr2serr("--id= expects a number from 0 to 65535\n");
return SG_LIB_SYNTAX_ERROR;
}
diff --git a/src/sg_turs.c b/src/sg_turs.c
index d31d9dad..88075a4f 100644
--- a/src/sg_turs.c
+++ b/src/sg_turs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2000-2019 D. Gilbert
+ * Copyright (C) 2000-2020 D. Gilbert
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
@@ -22,8 +22,11 @@
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
+#include <time.h>
#include <errno.h>
#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -42,23 +45,13 @@
#include "sg_pr2serr.h"
-static const char * version_str = "3.46 20190618";
-
-#if defined(MSC_VER) || defined(__MINGW32__)
-#define HAVE_MS_SLEEP
-#endif
-
-#ifdef HAVE_MS_SLEEP
-#include <windows.h>
-#define sleep_for(seconds) Sleep( (seconds) * 1000)
-#else
-#define sleep_for(seconds) sleep(seconds)
-#endif
+static const char * version_str = "3.47 20200722";
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
static struct option long_options[] = {
+ {"delay", required_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"low", no_argument, 0, 'l'},
{"new", no_argument, 0, 'N'},
@@ -74,12 +67,14 @@ static struct option long_options[] = {
};
struct opts_t {
+ bool delay_given;
bool do_low;
bool do_progress;
bool do_time;
bool opts_new;
bool verbose_given;
bool version_given;
+ int delay;
int do_help;
int do_number;
int verbose;
@@ -96,10 +91,13 @@ struct loop_res_t {
static void
usage()
{
- printf("Usage: sg_turs [--help] [--low] [--number=NUM] [--num=NUM] "
- "[--progress]\n"
- " [--time] [--verbose] [--version] DEVICE\n"
+ printf("Usage: sg_turs [--delay=MS] [--help] [--low] [--number=NUM] "
+ "[--num=NUM]\n"
+ " [--progress] [--time] [--verbose] [--version] "
+ "DEVICE\n"
" where:\n"
+ " --delay=MS|-d MS delay MS miiliseconds before sending "
+ "each tur\n"
" --help|-h print usage message then exit\n"
" --low|-l use low level (sg_pt) interface for "
"speed\n"
@@ -109,19 +107,23 @@ usage()
" --old|-O use old interface (use as first option)\n"
" --progress|-p outputs progress indication (percentage) "
"if available\n"
+ " waits 30 seconds before TUR unless "
+ "--delay=MS given\n"
" --time|-t outputs total duration and commands per "
"second\n"
" --verbose|-v increase verbosity\n"
" --version|-V print version string then exit\n\n"
- "Performs a SCSI TEST UNIT READY command (or many of them).\n");
+ "Performs a SCSI TEST UNIT READY command (or many of them).\n"
+ "This SCSI command is often known by its abbreviation: TUR .\n");
}
static void
usage_old()
{
- printf("Usage: sg_turs [-l] [-n=NUM] [-p] [-t] [-v] [-V] "
+ printf("Usage: sg_turs [-d=MS] [-l] [-n=NUM] [-p] [-t] [-v] [-V] "
"DEVICE\n"
" where:\n"
+ " -d=MS same as --delay=MS in new interface\n"
" -l use low level interface (sg_pt) for speed\n"
" -n=NUM number of test_unit_ready commands "
"(def: 1)\n"
@@ -152,12 +154,22 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hln:NOptvV", long_options,
+ c = getopt_long(argc, argv, "d:hln:NOptvV", long_options,
&option_index);
if (c == -1)
break;
switch (c) {
+ case 'd':
+ n = sg_get_num(optarg);
+ if (n < 0) {
+ pr2serr("bad argument to '--delay='\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->delay = n;
+ op->delay_given = true;
+ break;
case 'h':
case '?':
++op->do_help;
@@ -263,7 +275,15 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
}
if (plen <= 0)
continue;
- if (0 == strncmp("n=", cp, 2)) {
+ if (0 == strncmp("d=", cp, 2)) {
+ op->delay = sg_get_num(cp + 2);
+ if (op->delay < 0) {
+ printf("Couldn't decode number after 'd=' option\n");
+ usage_old();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->delay_given = true;
+ } else if (0 == strncmp("n=", cp, 2)) {
op->do_number = sg_get_num(cp + 2);
if (op->do_number <= 0) {
printf("Couldn't decode number after 'n=' option\n");
@@ -310,6 +330,38 @@ parse_cmd_line(struct opts_t * op, int argc, char * argv[])
return res;
}
+static void
+wait_millisecs(int millisecs)
+{
+ struct timespec wait_period, rem;
+
+ wait_period.tv_sec = millisecs / 1000;
+ wait_period.tv_nsec = (millisecs % 1000) * 1000000;
+ while ((nanosleep(&wait_period, &rem) < 0) && (EINTR == errno))
+ wait_period = rem;
+}
+
+/* Returns true if prints estimate of duration to ready */
+bool
+check_for_lu_becoming(struct sg_pt_base * ptvp)
+{
+ int s_len = get_scsi_pt_sense_len(ptvp);
+ uint64_t info;
+ uint8_t * sense_b = get_scsi_pt_sense_buf(ptvp);
+ struct sg_scsi_sense_hdr ssh;
+
+ /* Check for "LU is in process of becoming ready" with a non-zero INFO
+ * field that isn't too big. As per 20-061r2 it means the following: */
+ if (sg_scsi_normalize_sense(sense_b, s_len, &ssh) && (ssh.asc == 0x4) &&
+ (ssh.ascq == 0x1) && sg_get_sense_info_fld(sense_b, s_len, &info) &&
+ (info > 0x0) && (info < 0x1000000)) {
+ printf("device not ready, estimated to be ready in %" PRIu64
+ " milliseconds\n", info);
+ return true;
+ }
+ return false;
+}
+
/* Returns number of TURs performed */
static int
loop_turs(struct sg_pt_base * ptvp, struct loop_res_t * resp,
@@ -319,13 +371,15 @@ loop_turs(struct sg_pt_base * ptvp, struct loop_res_t * resp,
int packet_id = 0;
int vb = op->verbose;
char b[80];
+ uint8_t sense_b[32];
if (op->do_low) {
int rs, n, sense_cat;
uint8_t cdb[6];
- uint8_t sense_b[32];
for (k = 0; k < op->do_number; ++k) {
+ if (op->delay > 0)
+ wait_millisecs(op->delay);
/* Might get Unit Attention on first invocation */
memset(cdb, 0, sizeof(cdb)); /* TUR's cdb is 6 zeros */
set_scsi_pt_cdb(ptvp, cdb, sizeof(cdb));
@@ -344,9 +398,10 @@ loop_turs(struct sg_pt_base * ptvp, struct loop_res_t * resp,
break;
case SG_LIB_CAT_NOT_READY:
++resp->num_errs;
- if (1 == op->do_number) {
+ if ((1 == op->do_number) || (op->delay > 0)) {
+ if (! check_for_lu_becoming(ptvp))
+ printf("device not ready\n");
resp->ret = sense_cat;
- printf("device not ready\n");
resp->reported = true;
}
break;
@@ -369,20 +424,25 @@ loop_turs(struct sg_pt_base * ptvp, struct loop_res_t * resp,
break;
}
}
- clear_scsi_pt_obj(ptvp);
+ partial_clear_scsi_pt_obj(ptvp);
}
return k;
} else {
for (k = 0; k < op->do_number; ++k) {
+ if (op->delay > 0)
+ wait_millisecs(op->delay);
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
/* Might get Unit Attention on first invocation */
res = sg_ll_test_unit_ready_pt(ptvp, k, (0 == k), vb);
if (res) {
++resp->num_errs;
resp->ret = res;
- if (1 == op->do_number) {
- if (SG_LIB_CAT_NOT_READY == res)
- printf("device not ready\n");
- else {
+ if ((1 == op->do_number) || (op->delay > 0)) {
+ if (SG_LIB_CAT_NOT_READY == res) {
+ if (! check_for_lu_becoming(ptvp))
+ printf("device not ready\n");
+ continue;
+ } else {
sg_get_category_sense_str(res, sizeof(b), b, vb);
printf("%s\n", b);
}
@@ -447,6 +507,8 @@ main(int argc, char * argv[])
pr2serr("Version string: %s\n", version_str);
return 0;
}
+ if (op->do_progress && (! op->delay_given))
+ op->delay = 30 * 1000; /* progress has 30 second default delay */
if (NULL == op->device_name) {
pr2serr("No DEVICE argument given\n");
@@ -469,8 +531,12 @@ main(int argc, char * argv[])
}
if (op->do_progress) {
for (k = 0; k < op->do_number; ++k) {
- if (k > 0)
- sleep_for(30);
+ if (op->delay > 0) {
+ if (op->delay_given)
+ wait_millisecs(op->delay);
+ else if (k > 0)
+ wait_millisecs(op->delay);
+ }
progress = -1;
res = sg_ll_test_unit_ready_progress_pt(ptvp, k, &progress,
(1 == op->do_number), op->verbose);
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;