aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2020-08-22 18:04:56 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2020-08-22 18:04:56 +0000
commite294cca49d78b88ed74f628353970ff907a77e05 (patch)
treeed2f495649bfdb4072c47e84b6cc9b455be4735c
parent936a3271c9f9d86c9592e1406bdd254341ff2849 (diff)
downloadsg3_utils-e294cca49d78b88ed74f628353970ff907a77e05.tar.gz
partial_clear_scsi_pt_obj() declared in sg_pt.h; testing directory work
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@861 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog2
-rw-r--r--lib/sg_lib_data.c1
-rw-r--r--lib/sg_pt_freebsd.c34
-rw-r--r--lib/sg_pt_linux.c22
-rw-r--r--testing/Makefile2
-rw-r--r--testing/sg_mrq_dd.cpp447
-rw-r--r--testing/sg_scat_gath.cpp15
-rw-r--r--testing/sg_scat_gath.h55
-rw-r--r--testing/sgh_dd.cpp79
-rw-r--r--testing/sgs_dd.c2
-rw-r--r--testing/uapi_sg.h3
11 files changed, 514 insertions, 148 deletions
diff --git a/ChangeLog b/ChangeLog
index 3ce51fe5..4f31cbb2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@ 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 [20200802] [svn: r860]
+Changelog for sg3_utils-1.46 [20200822] [svn: r861]
- sg_rep_pip: report new provisioning initialization pattern cmd
- sg_turs: estimated time-to-ready [20-061r2]
- add --delay=MS option
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index 0a8543af..b0a38049 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -1056,6 +1056,7 @@ struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] =
{0x38,0x04,"Esn - media class event"},
{0x38,0x06,"Esn - device busy class event"},
{0x38,0x07,"Thin provisioning soft threshold reached"},
+ {0x38,0x08,"Depopulation interrupted"}, /* > spc6r02 */
{0x39,0x00,"Saving parameters not supported"},
{0x3A,0x00,"Medium not present"},
{0x3A,0x01,"Medium not present - tray closed"},
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index 4fbe3434..e98a2bb7 100644
--- a/lib/sg_pt_freebsd.c
+++ b/lib/sg_pt_freebsd.c
@@ -251,7 +251,7 @@ scsi_pt_open_flags(const char * device_name, int oflags, int vb)
goto scsi_ata_try;
}
fdc_p->is_nvme = true;
- fdc_p->nvme_our_sntl = true; /* guess at this stage */
+ fdc_p->nvme_our_sntl = true; /* guess at this stage */
fdc_p->is_char = is_char;
fdc_p->nsid = (broadcast_nsid == nsid) ? 0 : nsid;
fdc_p->nv_ctrlid = nv_ctrlid;
@@ -456,26 +456,22 @@ void
partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
{
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+ struct freebsd_dev_channel *fdc_p;
if (NULL == ptp)
return;
ptp->in_err = 0;
ptp->os_err = 0;
ptp->transport_err = 0;
- if (ptp->nvme_our_sntl) {
- ptp->scsi_status = 0;
- ptp->dxfer_dir = CAM_DIR_NONE;
- ptp->dxferip = NULL;
- ptp->dxfer_ilen = 0;
- ptp->dxferop = NULL;
- ptp->dxfer_olen = 0;
- } else {
- struct freebsd_dev_channel *fdc_p;
-
- fdc_p = get_fdc_p(ptp);
- if (fdc_p)
- fdc_p->nvme_result = 0;
- }
+ ptp->scsi_status = 0;
+ ptp->dxfer_dir = CAM_DIR_NONE;
+ ptp->dxferip = NULL;
+ ptp->dxfer_ilen = 0;
+ ptp->dxferop = NULL;
+ ptp->dxfer_olen = 0;
+ fdc_p = get_fdc_p(ptp);
+ if (fdc_p)
+ fdc_p->nvme_result = 0;
}
/* Forget any previous dev_han and install the one given. May attempt to
@@ -871,8 +867,8 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp)
if (ptp) {
if (ptp->nvme_our_sntl)
- return ptp->scsi_status;
- else {
+ return ptp->scsi_status;
+ else {
const struct freebsd_dev_channel *fdc_p;
fdc_p = get_fdc_cp(ptp);
@@ -892,8 +888,8 @@ get_pt_result(const struct sg_pt_base * vp)
if (ptp) {
if (ptp->nvme_our_sntl)
- return (uint32_t)ptp->scsi_status;
- else {
+ return (uint32_t)ptp->scsi_status;
+ else {
const struct freebsd_dev_channel *fdc_p;
fdc_p = get_fdc_cp(ptp);
diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c
index 690abf82..d5e01e90 100644
--- a/lib/sg_pt_linux.c
+++ b/lib/sg_pt_linux.c
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_linux version 1.48 20200722 */
+/* sg_pt_linux version 1.49 20200820 */
#include <stdio.h>
@@ -486,16 +486,14 @@ partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
return;
ptp->in_err = 0;
ptp->os_err = 0;
- if (ptp->nvme_our_sntl) {
- 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;
- } else
- ptp->nvme_result = 0;
+ 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;
+ ptp->nvme_result = 0;
}
#ifndef SG_SET_GET_EXTENDED
@@ -856,7 +854,7 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp)
if (NULL == ptp)
return 0;
return (int)(ptp->nvme_our_sntl ? ptp->io_hdr.device_status :
- ptp->nvme_status);
+ ptp->nvme_status);
}
uint32_t
diff --git a/testing/Makefile b/testing/Makefile
index 16de2c39..0e1a8402 100644
--- a/testing/Makefile
+++ b/testing/Makefile
@@ -30,6 +30,8 @@ CXXLD = $(CXX)
CPPFLAGS = -iquote ../include -iquote .. -D_REENTRANT $(LARGE_FILE_FLAGS) -DHAVE_CONFIG_H -DHAVE_NVME
CXXFLAGS = -std=c++17 -pthread -ggdb -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+# CXXFLAGS = -std=c++2a -pthread -ggdb -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+# CXXFLAGS = -std=c++11 -pthread -ggdb -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
# CPPFLAGS = -iquote ../include -iquote .. -D_REENTRANT $(LARGE_FILE_FLAGS) -DHAVE_CONFIG_H -DHAVE_NVME -DDEBUG
# CFLAGS = -g -O2 -W -Wall
CFLAGS = -ggdb -O2 -W -Wall -DDEBUG
diff --git a/testing/sg_mrq_dd.cpp b/testing/sg_mrq_dd.cpp
index ea17e665..3a4c3306 100644
--- a/testing/sg_mrq_dd.cpp
+++ b/testing/sg_mrq_dd.cpp
@@ -30,7 +30,7 @@
*
*/
-static const char * version_str = "1.05 20200728";
+static const char * version_str = "1.08 20200820";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -100,6 +100,7 @@ static const char * version_str = "1.05 20200728";
// C++ local header
#include "sg_scat_gath.h"
+// C headers associated with sg3_utils library
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_io_linux.h"
@@ -221,7 +222,7 @@ struct global_collection /* one instance visible to all threads */
atomic<int64_t> in_rem_count; /* | count of remaining in blocks */
atomic<int> in_partial; /* | */
off_t in_st_size; /* Only for FT_OTHER (regular) file */
- int mrq_num; /* Number of multi-reqs for sg v4 */
+ int mrq_num; /* if user gives 0, set this to 1 */
int outfd;
int out_type;
int cdbsz_out;
@@ -231,9 +232,9 @@ struct global_collection /* one instance visible to all threads */
off_t out_st_size; /* Only for FT_OTHER (regular) file */
condition_variable infant_cv; /* after thread:0 does first segment */
mutex infant_mut;
- bool processed;
int bs;
int bpt;
+ int elem_sz;
int outregfd;
int outreg_type;
off_t outreg_st_size;
@@ -241,12 +242,14 @@ struct global_collection /* one instance visible to all threads */
atomic<int> sum_of_resids;
int verbose;
int dry_run;
+ bool mrq_eq_0; /* true when user gives mrq=0 */
+ bool processed;
bool cdbsz_given;
bool count_given;
bool flexible;
bool ofile_given;
bool unit_nanosec; /* default duration unit is millisecond */
- bool mrq_cmds; /* mrq=<NRQS>,C given */
+ bool no_waitq; /* if set use polling for response instead */
bool verify; /* don't copy, verify like Unix: cmp */
bool prefetch; /* for verify: do PF(b),RD(a),V(b)_a_data */
const char * infp;
@@ -335,6 +338,7 @@ static atomic<int> num_fin_eagain(0);
#if 0
static atomic<long> num_waiting_calls(0);
#endif
+static atomic<bool> vb_first_time(true);
static sigset_t signal_set;
@@ -348,6 +352,8 @@ static int do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
scat_gath_iter & o_sg_it, int seg_blks,
vector<cdb_arr_t> & a_cdb,
vector<struct sg_io_v4> & a_v4);
+static int do_both_sg_segment_mrq0(Rq_elem * rep, scat_gath_iter & i_sg_it,
+ scat_gath_iter & o_sg_it, int seg_blks);
static int do_normal_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
scat_gath_iter & o_sg_it, int seg_blks,
vector<cdb_arr_t> & a_cdb,
@@ -362,7 +368,7 @@ static mutex strerr_mut;
static bool have_sg_version = false;
static int sg_version = 0;
static bool sg_version_ge_40030 = false;
-static atomic<bool> shutting_down = false;
+static atomic<bool> shutting_down{false};
static bool do_sync = false;
static int do_time = 1;
static struct global_collection gcoll;
@@ -495,7 +501,7 @@ sg_flags_str(int flags, int b_len, char * b)
goto fini;
}
if (SGV4_FLAG_NO_WAITQ & flags) { /* 0x40 */
- n += sg_scnpr(b + n, b_len - n, "NWTQ|");
+ n += sg_scnpr(b + n, b_len - n, "NO_WTQ|");
if (n >= b_len)
goto fini;
}
@@ -505,7 +511,7 @@ sg_flags_str(int flags, int b_len, char * b)
goto fini;
}
if (SGV4_FLAG_COMPLETE_B4 & flags) { /* 0x100 */
- n += sg_scnpr(b + n, b_len - n, "NWTQ|");
+ n += sg_scnpr(b + n, b_len - n, "CPL_B4|");
if (n >= b_len)
goto fini;
}
@@ -858,12 +864,13 @@ usage(int pg_num)
"[seek=SEEK]\n"
" [skip=SKIP] [--help] [--version]\n\n");
pr2serr(" [bpt=BPT] [cdbsz=6|10|12|16] [dio=0|1] "
- "[fua=0|1|2|3]\n"
- " [mrq=MRQ] [ofreg=OFREG] [sync=0|1] [thr=THR] "
- "[time=0|1]\n"
- " [verbose=VERB] [--dry-run] [--verbose] "
- "[--verify]\n"
- " [--version]\n\n"
+ "[elemsz_kb=EKB]\n"
+ " [fua=0|1|2|3] [mrq=MRQ] [no_waitq=0|1] "
+ "[ofreg=OFREG]\n"
+ " [sync=0|1] [thr=THR] [time=0|1] "
+ "[verbose=VERB]\n"
+ " [--dry-run] [--verbose] [--verify] "
+ "[--version]\n\n"
" where the main options (shown in first group above) are:\n"
" bs must be device logical block size (default "
"512)\n"
@@ -875,6 +882,8 @@ usage(int pg_num)
" null,order,qtail,serial,wq_excl]\n"
" mrq number of cmds placed in each sg call "
"(def: 16)\n"
+ " if mrq=0 does single, blocking ioctl(SG_IO)s "
+ "for all IO\n"
" of file or device to write to (def: /dev/null "
"N.B. different\n"
" from dd it defaults to stdout). If 'of=.' "
@@ -891,10 +900,10 @@ usage(int pg_num)
"Copy IFILE to OFILE, similar to dd command. This utility is "
"specialized for\nSCSI devices and uses the 'multiple requests' "
"(mrq) in a single invocation\nfacility in version 4 of the sg "
- "driver. Usually one or both IFILE and\nOFILE will be sg "
- "devices. With the --verify option it does a\n"
+ "driver unless mrq=0. Usually one or both\nIFILE and OFILE will "
+ "be sg devices. With the --verify option it does a\n"
"verify/compare operation instead of a copy. This utility is "
- "Linux\n specific. Use '-hh', '-hhh' or '-hhhh' for more "
+ "Linux specific.\nUse '-hh', '-hhh' or '-hhhh' for more "
"information.\n"
);
return;
@@ -909,9 +918,15 @@ page2:
" cdbsz size of SCSI READ, WRITE or VERIFY cdb_s "
"(default is 10)\n"
" dio is direct IO, 1->attempt, 0->indirect IO (def)\n"
+ " elemsz_kb=EKB scatter gather list element size in "
+ "kibibytes;\n"
+ " must be power of two, >= page_size "
+ "(typically 4)\n"
" fua force unit access: 0->don't(def), 1->OFILE, "
"2->IFILE,\n"
" 3->OFILE+IFILE\n"
+ " no_waitq=0|1 poll for completion when 1; def: 0 (use "
+ "wait queue)\n"
" ofreg OFREG is regular file or pipe to send what is "
"read from\n"
" IFILE in the first half of each shared element\n"
@@ -1317,8 +1332,12 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
a_cdb.reserve(nn);
if (a_v4.capacity() < nn)
a_v4.reserve(nn);
- res = do_both_sg_segment(rep, i_sg_it, o_sg_it, seg_blks, a_cdb,
- a_v4);
+ if (clp->mrq_eq_0)
+ res = do_both_sg_segment_mrq0(rep, i_sg_it, o_sg_it,
+ seg_blks);
+ else
+ res = do_both_sg_segment(rep, i_sg_it, o_sg_it, seg_blks,
+ a_cdb, a_v4);
if (res < 0)
break;
} else if (only_one_sg) {
@@ -1371,7 +1390,7 @@ fini:
free(rep->alloc_bp);
if ((1 == rep->mmap_active) && (rep->mmap_len > 0)) {
if (munmap(rep->buffp, rep->mmap_len) < 0) {
- int err = errno;
+ err = errno;
char bb[64];
pr2serr_lk("thread=%d: munmap() failed: %s\n", rep->id,
@@ -1809,13 +1828,109 @@ fini:
/* Returns number of blocks successfully processed or a negative error
* number. */
static int
+sg_half_segment_mrq0(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr,
+ int seg_blks, uint8_t *dp)
+{
+ int k, res, fd, pack_id_base, id, rflags;
+ int num, kk, lin_blks, cdbsz, err;
+ uint32_t q_blks = 0;
+ struct global_collection * clp = rep->clp;
+ cdb_arr_t t_cdb = {};
+ struct sg_io_v4 t_v4;
+ struct sg_io_v4 * t_v4p = &t_v4;
+ struct flags_t * flagsp = is_wr ? &clp->out_flags : &clp->in_flags;
+ int vb = clp->verbose;
+
+ id = rep->id;
+ pack_id_base = id * PACK_ID_TID_MULTIPLIER;
+ rflags = 0;
+ fd = is_wr ? rep->outfd : rep->infd;
+ if (flagsp->mmap && (rep->outregfd >= 0))
+ rflags |= SGV4_FLAG_MMAP_IO;
+ if (flagsp->dio)
+ rflags |= SGV4_FLAG_DIRECT_IO;
+ if (flagsp->qhead)
+ rflags |= SGV4_FLAG_Q_AT_HEAD;
+ if (flagsp->qtail)
+ rflags |= SGV4_FLAG_Q_AT_TAIL;
+ if (clp->no_waitq)
+ rflags |= SGV4_FLAG_NO_WAITQ;
+
+ for (k = 0, num = 0; seg_blks > 0; ++k, seg_blks -= num) {
+ kk = min<int>(seg_blks, clp->bpt);
+ lin_blks = sg_it.linear_for_n_blks(kk);
+ num = lin_blks;
+ if (num <= 0) {
+ res = 0;
+ pr2serr_lk("[%d] %s: unexpected num=%d\n", id, __func__, num);
+ break;
+ }
+
+ /* First build the command/request for the read-side */
+ cdbsz = is_wr ? clp->cdbsz_out : clp->cdbsz_in;
+ res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num, sg_it.current_lba(),
+ false, is_wr, flagsp->fua, flagsp->dpo);
+ if (res) {
+ pr2serr_lk("[%d] %s: sg_build_scsi_cdb() failed\n", id, __func__);
+ break;
+ } else if (vb > 3)
+ lk_print_command_len("cdb: ", t_cdb.data(), cdbsz, true);
+
+ memset(t_v4p, 0, sizeof(*t_v4p));
+ t_v4p->guard = 'Q';
+ t_v4p->request = (uint64_t)t_cdb.data();
+ t_v4p->response = (uint64_t)rep->sb;
+ t_v4p->flags = rflags;
+ t_v4p->request_len = cdbsz;
+ if (is_wr) {
+ t_v4p->dout_xfer_len = num * clp->bs;
+ t_v4p->dout_xferp = (uint64_t)(dp + (q_blks * clp->bs));
+ } else {
+ t_v4p->din_xfer_len = num * clp->bs;
+ t_v4p->din_xferp = (uint64_t)(dp + (q_blks * clp->bs));
+ }
+ t_v4p->timeout = DEF_TIMEOUT;
+ t_v4p->usr_ptr = num; /* pass number blocks requested */
+ t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off;
+mrq0_again:
+ res = ioctl(fd, SG_IO, t_v4p);
+ err = errno;
+ if (vb > 5)
+ v4hdr_out_lk("sg_half_segment_mrq0: >> after ioctl(SG_IO)",
+ t_v4p, id, false);
+ if (res < 0) {
+ if (E2BIG == err)
+ sg_take_snap(fd, id, true);
+ else if (EBUSY == err) {
+ ++num_ebusy;
+ std::this_thread::yield();/* so other threads can progress */
+ goto mrq0_again;
+ }
+ pr2serr_lk("[%d] %s: ioctl(SG_IO)-->%d, errno=%d: %s\n", id,
+ __func__, res, err, strerror(err));
+ return -err;
+ }
+ if (t_v4p->device_status || t_v4p->transport_status ||
+ t_v4p->driver_status) {
+ pr2serr_lk("[%d] t_v4[%d]:\n", id, k);
+ lk_chk_n_print4(" ", t_v4p, vb > 4);
+ return q_blks;
+ }
+ q_blks += num;
+ sg_it.add_blks(num);
+ }
+ return q_blks;
+}
+
+/* Returns number of blocks successfully processed or a negative error
+ * number. */
+static int
sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr,
- int seg_blks, uint8_t *dp,
- vector<cdb_arr_t> & a_cdb,
- vector<struct sg_io_v4> & a_v4)
+ int seg_blks, uint8_t *dp, vector<cdb_arr_t> & a_cdb,
+ vector<struct sg_io_v4> & a_v4)
{
int num_mrq, k, res, fd, mrq_pack_id_base, id, b_len, rflags;
- int num, kk, lin_blks, cdbsz, num_good;
+ int num, kk, lin_blks, cdbsz, num_good, err;
int o_seg_blks = seg_blks;
uint32_t in_fin_blks, out_fin_blks;
uint32_t mrq_q_blks = 0;
@@ -1825,7 +1940,7 @@ sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr,
struct sg_io_v4 * a_v4p;
struct sg_io_v4 ctl_v4; /* MRQ control object */
struct global_collection * clp = rep->clp;
- const char * iosub_str = "SUBMIT(variable blocking)";
+ const char * iosub_str = "SG_IOSUBMIT(variable blocking)";
char b[80];
cdb_arr_t t_cdb = {};
struct sg_io_v4 t_v4;
@@ -1838,7 +1953,7 @@ sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr,
id = rep->id;
b_len = sizeof(b);
if (serial)
- iosub_str = "(ordered blocking)";
+ iosub_str = "SG_IO(ordered blocking)";
a_cdb.clear();
a_v4.clear();
@@ -1853,8 +1968,10 @@ sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr,
rflags |= SGV4_FLAG_Q_AT_HEAD;
if (flagsp->qtail)
rflags |= SGV4_FLAG_Q_AT_TAIL;
+ if (clp->no_waitq)
+ rflags |= SGV4_FLAG_NO_WAITQ;
- for (k = 0; seg_blks > 0; ++k, seg_blks -= num) {
+ for (k = 0, num = 0; seg_blks > 0; ++k, seg_blks -= num) {
kk = min<int>(seg_blks, clp->bpt);
lin_blks = sg_it.linear_for_n_blks(kk);
num = lin_blks;
@@ -1922,9 +2039,15 @@ sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr,
if (false /* allow_mrq_abort */)
ctl_v4.request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off;
+ if (vb && vb_first_time.load()) {
+ pr2serr_lk("First controlling object output by ioctl(%s), flags: "
+ "%s\n", iosub_str, sg_flags_str(ctl_v4.flags, b_len, b));
+ vb_first_time.store(false);
+ } else if (vb > 4) {
+ pr2serr_lk("[%d] %s: >> Control object _before_ ioctl(%s):\n", id,
+ __func__, iosub_str);
+ }
if (vb > 4) {
- pr2serr_lk("[%d] %s: >> Control object _before_ ioctl(SG_IO%s):\n",
- id, __func__, iosub_str);
if (vb > 5)
hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
v4hdr_out_lk(">> Control object before", &ctl_v4, id, false);
@@ -1940,16 +2063,15 @@ try_again:
else
res = ioctl(fd, SG_IOSUBMIT, &ctl_v4); /* overlapping commands */
if (res < 0) {
- int err = errno;
-
+ err = errno;
if (E2BIG == err)
- sg_take_snap(fd, id, true);
+ sg_take_snap(fd, id, true);
else if (EBUSY == err) {
++num_ebusy;
std::this_thread::yield();/* allow another thread to progress */
goto try_again;
}
- pr2serr_lk("[%d] %s: ioctl(SG_IO%s, %s)-->%d, errno=%d: %s\n", id,
+ pr2serr_lk("[%d] %s: ioctl(%s, %s)-->%d, errno=%d: %s\n", id,
__func__, iosub_str, sg_flags_str(ctl_v4.flags, b_len, b),
res, err, strerror(err));
return -err;
@@ -2128,8 +2250,12 @@ do_normal_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
++seg_blks;
rep->in_resid_bytes = 0;
}
- res = sg_half_segment(rep, o_sg_it, true /* is_wr */, seg_blks,
- rep->buffp, a_cdb, a_v4);
+ if (clp->mrq_eq_0)
+ res = sg_half_segment_mrq0(rep, o_sg_it, true /* is_wr */,
+ seg_blks, rep->buffp);
+ else
+ res = sg_half_segment(rep, o_sg_it, true /* is_wr */, seg_blks,
+ rep->buffp, a_cdb, a_v4);
if (res < seg_blks) {
if (res < 0) {
pr2serr_lk("[%d] %s: sg out failed d_off=%d, err=%d\n",
@@ -2142,8 +2268,12 @@ do_normal_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
out_fin_blks = seg_blks;
} else { /* in: sg --> out: normal */
- res = sg_half_segment(rep, i_sg_it, false, seg_blks, rep->buffp,
- a_cdb, a_v4);
+ if (clp->mrq_eq_0)
+ res = sg_half_segment_mrq0(rep, i_sg_it, false, seg_blks,
+ rep->buffp);
+ else
+ res = sg_half_segment(rep, i_sg_it, false, seg_blks, rep->buffp,
+ a_cdb, a_v4);
if (res < seg_blks) {
if (res < 0) {
pr2serr_lk("[%d] %s: sg in failed, err=%d\n", id, __func__,
@@ -2203,6 +2333,168 @@ fini:
* to the pass-through. Returns number of blocks processed (==seg_blks for
* all good) or a negative error number. */
static int
+do_both_sg_segment_mrq0(Rq_elem * rep, scat_gath_iter & i_sg_it,
+ scat_gath_iter & o_sg_it, int seg_blks)
+{
+ int k, kk, res, pack_id_base, id, iflags, oflags;
+ int num, i_lin_blks, o_lin_blks, cdbsz, err;
+ uint32_t in_fin_blks = 0;
+ uint32_t out_fin_blks = 0;
+ struct global_collection * clp = rep->clp;
+ cdb_arr_t t_cdb = {};
+ struct sg_io_v4 t_v4;
+ struct sg_io_v4 * t_v4p = &t_v4;
+ struct flags_t * iflagsp = &clp->in_flags;
+ struct flags_t * oflagsp = &clp->out_flags;
+ int vb = clp->verbose;
+
+ id = rep->id;
+ pack_id_base = id * PACK_ID_TID_MULTIPLIER;
+
+ iflags = SGV4_FLAG_SHARE;
+ if (iflagsp->mmap && (rep->outregfd >= 0))
+ iflags |= SGV4_FLAG_MMAP_IO;
+ else
+ iflags |= SGV4_FLAG_NO_DXFER;
+ if (iflagsp->dio)
+ iflags |= SGV4_FLAG_DIRECT_IO;
+ if (iflagsp->qhead)
+ iflags |= SGV4_FLAG_Q_AT_HEAD;
+ if (iflagsp->qtail)
+ iflags |= SGV4_FLAG_Q_AT_TAIL;
+ if (clp->no_waitq)
+ iflags |= SGV4_FLAG_NO_WAITQ;
+
+ oflags = SGV4_FLAG_SHARE | SGV4_FLAG_NO_DXFER;
+ if (oflagsp->dio)
+ oflags |= SGV4_FLAG_DIRECT_IO;
+ if (oflagsp->qhead)
+ oflags |= SGV4_FLAG_Q_AT_HEAD;
+ if (oflagsp->qtail)
+ oflags |= SGV4_FLAG_Q_AT_TAIL;
+ if (clp->no_waitq)
+ oflags |= SGV4_FLAG_NO_WAITQ;
+
+ for (k = 0; seg_blks > 0; ++k, seg_blks -= num) {
+ kk = min<int>(seg_blks, clp->bpt);
+ i_lin_blks = i_sg_it.linear_for_n_blks(kk);
+ o_lin_blks = o_sg_it.linear_for_n_blks(kk);
+ num = min<int>(i_lin_blks, o_lin_blks);
+ if (num <= 0) {
+ res = 0;
+ pr2serr_lk("[%d] %s: min(i_lin_blks=%d o_lin_blks=%d) < 1\n", id,
+ __func__, i_lin_blks, o_lin_blks);
+ break;
+ }
+
+ /* First build the command/request for the read-side*/
+ cdbsz = clp->cdbsz_in;
+ res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num,
+ i_sg_it.current_lba(), false, false,
+ iflagsp->fua, iflagsp->dpo);
+ if (res) {
+ pr2serr_lk("%s: t=%d: input sg_build_scsi_cdb() failed\n",
+ __func__, id);
+ break;
+ } else if (vb > 3)
+ lk_print_command_len("input cdb: ", t_cdb.data(), cdbsz, true);
+
+ memset(t_v4p, 0, sizeof(*t_v4p));
+ t_v4p->guard = 'Q';
+ t_v4p->request = (uint64_t)t_cdb.data();
+ t_v4p->response = (uint64_t)rep->sb;
+ t_v4p->flags = iflags;
+ t_v4p->request_len = cdbsz;
+ t_v4p->din_xfer_len = num * clp->bs;
+ t_v4p->timeout = DEF_TIMEOUT;
+ t_v4p->usr_ptr = num; /* pass number blocks requested */
+ t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off;
+mrq0_again:
+ res = ioctl(rep->infd, SG_IO, t_v4p);
+ err = errno;
+ if (vb > 5)
+ v4hdr_out_lk("do_both_sg_segment_mrq0: >> after ioctl(SG_IO)",
+ t_v4p, id, false);
+ if (res < 0) {
+ if (E2BIG == err)
+ sg_take_snap(rep->infd, id, true);
+ else if (EBUSY == err) {
+ ++num_ebusy;
+ std::this_thread::yield();/* so other threads can progress */
+ goto mrq0_again;
+ }
+ pr2serr_lk("[%d] %s: ioctl(SG_IO, read-side)-->%d, errno=%d: "
+ "%s\n", id, __func__, res, err, strerror(err));
+ return -err;
+ }
+ if (t_v4p->device_status || t_v4p->transport_status ||
+ t_v4p->driver_status) {
+ pr2serr_lk("[%d] t_v4[%d]:\n", id, k);
+ lk_chk_n_print4(" ", t_v4p, vb > 4);
+ return min<int>(in_fin_blks, out_fin_blks);
+ }
+ rep->in_local_count += num;
+ in_fin_blks += num;
+
+ /* Now build the command/request for write-side (WRITE or VERIFY) */
+ cdbsz = clp->cdbsz_out;
+ res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num,
+ o_sg_it.current_lba(), clp->verify, true,
+ oflagsp->fua, oflagsp->dpo);
+ if (res) {
+ pr2serr_lk("%s: t=%d: output sg_build_scsi_cdb() failed\n",
+ __func__, id);
+ break;
+ } else if (vb > 3)
+ lk_print_command_len("output cdb: ", t_cdb.data(), cdbsz, true);
+
+ memset(t_v4p, 0, sizeof(*t_v4p));
+ t_v4p->guard = 'Q';
+ t_v4p->request = (uint64_t)t_cdb.data();
+ t_v4p->response = (uint64_t)rep->sb;
+ t_v4p->flags = oflags;
+ t_v4p->request_len = cdbsz;
+ t_v4p->dout_xfer_len = num * clp->bs;
+ t_v4p->timeout = DEF_TIMEOUT;
+ t_v4p->usr_ptr = num; /* pass number blocks requested */
+ t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off;
+mrq0_again2:
+ res = ioctl(rep->outfd, SG_IO, t_v4p);
+ err = errno;
+ if (vb > 5)
+ v4hdr_out_lk("do_both_sg_segment_mrq0: >> after ioctl(SG_IO)",
+ t_v4p, id, false);
+ if (res < 0) {
+ if (E2BIG == err)
+ sg_take_snap(rep->outfd, id, true);
+ else if (EBUSY == err) {
+ ++num_ebusy;
+ std::this_thread::yield();/* so other threads can progress */
+ goto mrq0_again2;
+ }
+ pr2serr_lk("[%d] %s: ioctl(SG_IO, write-side)-->%d, errno=%d: "
+ "%s\n", id, __func__, res, err, strerror(err));
+ return -err;
+ }
+ if (t_v4p->device_status || t_v4p->transport_status ||
+ t_v4p->driver_status) {
+ pr2serr_lk("[%d] t_v4[%d]:\n", id, k);
+ lk_chk_n_print4(" ", t_v4p, vb > 4);
+ return min<int>(in_fin_blks, out_fin_blks);
+ }
+ rep->out_local_count += num;
+ out_fin_blks += num;
+
+ i_sg_it.add_blks(num);
+ o_sg_it.add_blks(num);
+ }
+ return min<int>(in_fin_blks, out_fin_blks);
+}
+
+/* This function sets up a multiple request (mrq) transaction and sends it
+ * to the pass-through. Returns number of blocks processed (==seg_blks for
+ * all good) or a negative error number. */
+static int
do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
scat_gath_iter & o_sg_it, int seg_blks,
vector<cdb_arr_t> & a_cdb,
@@ -2210,7 +2502,7 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
{
bool err_on_in = false;
int num_mrq, k, res, fd, mrq_pack_id_base, id, b_len, iflags, oflags;
- int num, kk, i_lin_blks, o_lin_blks, cdbsz, num_good;
+ int num, kk, i_lin_blks, o_lin_blks, cdbsz, num_good, err;
int o_seg_blks = seg_blks;
uint32_t in_fin_blks, out_fin_blks;
uint32_t in_mrq_q_blks = 0;
@@ -2219,7 +2511,7 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
struct sg_io_v4 * a_v4p;
struct sg_io_v4 ctl_v4; /* MRQ control object */
struct global_collection * clp = rep->clp;
- const char * iosub_str = "SUBMIT(svb)";
+ const char * iosub_str = "SG_IOSUBMIT(svb)";
char b[80];
cdb_arr_t t_cdb = {};
struct sg_io_v4 t_v4;
@@ -2246,6 +2538,8 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
iflags |= SGV4_FLAG_Q_AT_HEAD;
if (iflagsp->qtail)
iflags |= SGV4_FLAG_Q_AT_TAIL;
+ if (clp->no_waitq)
+ iflags |= SGV4_FLAG_NO_WAITQ;
oflags = SGV4_FLAG_SHARE | SGV4_FLAG_NO_DXFER;
if (oflagsp->dio)
@@ -2254,6 +2548,8 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
oflags |= SGV4_FLAG_Q_AT_HEAD;
if (oflagsp->qtail)
oflags |= SGV4_FLAG_Q_AT_TAIL;
+ if (clp->no_waitq)
+ oflags |= SGV4_FLAG_NO_WAITQ;
oflags |= SGV4_FLAG_DO_ON_OTHER;
for (k = 0; seg_blks > 0; ++k, seg_blks -= num) {
@@ -2263,7 +2559,8 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
num = min<int>(i_lin_blks, o_lin_blks);
if (num <= 0) {
res = 0;
- pr2serr_lk("[%d] %s: unexpected num=%d\n", id, __func__, num);
+ pr2serr_lk("[%d] %s: min(i_lin_blks=%d o_lin_blks=%d) < 1\n", id,
+ __func__, i_lin_blks, o_lin_blks);
break;
}
@@ -2325,12 +2622,9 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
}
if (rep->both_sg || rep->same_sg)
fd = rep->infd; /* assume share to rep->outfd */
- else if (rep->only_in_sg)
- fd = rep->infd;
- else if (rep->only_out_sg)
- fd = rep->outfd;
else {
- pr2serr_lk("[%d] %s: why am I here? No sg devices\n", id, __func__);
+ pr2serr_lk("[%d] %s: why am I here? Want 2 sg devices\n", id,
+ __func__);
res = -1;
goto fini;
}
@@ -2355,9 +2649,14 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
if (false /* allow_mrq_abort */)
ctl_v4.request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off;
+ if (vb && vb_first_time.load()) {
+ pr2serr_lk("First controlling object output by ioctl(%s), flags: "
+ "%s\n", iosub_str, sg_flags_str(ctl_v4.flags, b_len, b));
+ vb_first_time.store(false);
+ } else if (vb > 4)
+ pr2serr_lk("%s: >> Control object _before_ ioctl(%s):\n", __func__,
+ iosub_str);
if (vb > 4) {
- pr2serr_lk("%s: >> Control object _before_ ioctl(SG_IO%s):\n",
- __func__, iosub_str);
if (vb > 5)
hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
v4hdr_out_lk(">> Control object before", &ctl_v4, id, false);
@@ -2370,8 +2669,7 @@ try_again:
}
res = ioctl(fd, SG_IOSUBMIT, &ctl_v4);
if (res < 0) {
- int err = errno;
-
+ err = errno;
if (E2BIG == err)
sg_take_snap(fd, id, true);
else if (EBUSY == err) {
@@ -2379,7 +2677,7 @@ try_again:
std::this_thread::yield();/* allow another thread to progress */
goto try_again;
}
- pr2serr_lk("%s: ioctl(SG_IO%s, %s)-->%d, errno=%d: %s\n", __func__,
+ pr2serr_lk("%s: ioctl(%s, %s)-->%d, errno=%d: %s\n", __func__,
iosub_str, sg_flags_str(ctl_v4.flags, b_len, b), res, err,
strerror(err));
res = -err;
@@ -2424,7 +2722,8 @@ try_again:
resid_blks = out_mrq_q_blks - out_fin_blks;
if (resid_blks > 0) {
rep->out_rem_count += resid_blks;
- rep->stop_after_write = ! (! err_on_in && clp->out_flags.coe);
+ rep->stop_after_write = ! ((! err_on_in) &&
+ clp->out_flags.coe);
}
}
}
@@ -2441,7 +2740,8 @@ sg_prepare_resbuf(int fd, struct global_collection *clp, bool is_in,
bool no_dur = is_in ? clp->in_flags.no_dur : clp->out_flags.no_dur;
bool masync = is_in ? clp->in_flags.masync : clp->out_flags.masync;
bool wq_excl = is_in ? clp->in_flags.wq_excl : clp->out_flags.wq_excl;
- int res, t, num;
+ int elem_sz = clp->elem_sz;
+ int res, t, num, err;
uint8_t *mmp;
struct sg_extended_info sei;
struct sg_extended_info * seip;
@@ -2460,6 +2760,23 @@ sg_prepare_resbuf(int fd, struct global_collection *clp, bool is_in,
}
goto bypass;
}
+ if (elem_sz >= 4096) {
+ memset(seip, 0, sizeof(*seip));
+ seip->sei_rd_mask |= SG_SEIM_SGAT_ELEM_SZ;
+ res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
+ if (res < 0)
+ pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) rd "
+ "error: %s\n", __func__, strerror(errno));
+ if (elem_sz != (int)seip->sgat_elem_sz) {
+ memset(seip, 0, sizeof(*seip));
+ seip->sei_wr_mask |= SG_SEIM_SGAT_ELEM_SZ;
+ seip->sgat_elem_sz = elem_sz;
+ res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
+ if (res < 0)
+ pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) "
+ "wr error: %s\n", __func__, strerror(errno));
+ }
+ }
if (no_dur || masync) {
memset(seip, 0, sizeof(*seip));
seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
@@ -2503,8 +2820,7 @@ bypass:
mmp = (uint8_t *)mmap(NULL, num, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (MAP_FAILED == mmp) {
- int err = errno;
-
+ err = errno;
pr2serr_lk("sg_mrq_dd: %s: sz=%d, fd=%d, mmap() failed: %s\n",
__func__, num, fd, strerror(err));
return 0;
@@ -2830,6 +3146,17 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
} else if (0 == strcmp(key, "dio")) {
clp->in_flags.dio = !! sg_get_num(buf);
clp->out_flags.dio = clp->in_flags.dio;
+ } else if (0 == strcmp(key, "elemsz_kb")) {
+ n = sg_get_num(buf);
+ if (n < 1) {
+ pr2serr("elemsz_kb=EKB wants an integer > 0\n");
+ goto syn_err;
+ }
+ if (n & (n - 1)) {
+ pr2serr("elemsz_kb=EKB wants EKB to be power of 2\n");
+ goto syn_err;
+ }
+ clp->elem_sz = n * 1024;
} else if (0 == strcmp(key, "fua")) {
n = sg_get_num(buf);
if (n & 1)
@@ -2868,6 +3195,20 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
pr2serr("%sbad argument to 'mrq='\n", my_name);
goto syn_err;
}
+ if (0 == clp->mrq_num) {
+ clp->mrq_eq_0 = true;
+ clp->mrq_num = 1;
+ pr2serr("note: send single, non-mrq commands\n");
+ }
+ } else if ((0 == strcmp(key, "no_waitq")) ||
+ (0 == strcmp(key, "no-waitq"))) {
+ n = sg_get_num(buf);
+ if (-1 == n) {
+ pr2serr("%sbad argument to 'no_waitq=', expect 0 or 1\n",
+ my_name);
+ goto syn_err;
+ }
+ clp->no_waitq = !!n;
} else if (0 == strcmp(key, "obs")) {
obs = sg_get_num(buf);
if (-1 == obs) {
diff --git a/testing/sg_scat_gath.cpp b/testing/sg_scat_gath.cpp
index dc51f377..fcf45dfe 100644
--- a/testing/sg_scat_gath.cpp
+++ b/testing/sg_scat_gath.cpp
@@ -5,6 +5,8 @@
* license that can be found in the BSD_LICENSE file.
*
* SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Version 1.01 [20200820]
*/
// C headers
@@ -772,18 +774,19 @@ scat_gath_iter::add_blks(uint64_t blk_count)
if (0 == bc)
return true;
- for (first = true, k = it_el_ind; k < elems; ++k, first = false) {
+ for (first = true, k = it_el_ind; k < elems; ++k) {
num = ((k == last_ind) && extend_last) ? MAX_SGL_NUM_VAL :
sglist.sgl[k].num;
if (first) {
- if ((uint64_t)(num - it_blk_off) < bc)
+ first = false;
+ 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)
+ if ((uint64_t)num <= bc)
bc -= num;
else {
it_blk_off = (uint32_t)bc;
@@ -840,6 +843,7 @@ scat_gath_iter::sub_blks(uint64_t blk_count)
}
if (k < 0) {
blk_idx = 0;
+ it_blk_off = 0;
return false; /* bad situation */
}
if ((int64_t)orig_blk_count <= blk_idx)
@@ -935,6 +939,7 @@ scat_gath_iter::is_sgl_linear() const
return sglist.linearity == SGL_LINEAR;
}
+/* Should return 1 or more unless max_n<=0 or at_end() */
int
scat_gath_iter::linear_for_n_blks(int max_n) const
{
@@ -947,6 +952,10 @@ scat_gath_iter::linear_for_n_blks(int max_n) const
return 0;
sge = sglist.sgl[it_el_ind];
rem = (int)sge.num - it_blk_off;
+ if (rem <= 0) {
+ sge = sglist.sgl[it_el_ind + 1];
+ rem = (int)sge.num;
+ }
if (max_n <= rem)
return max_n;
prev_lba = sge.lba + sge.num;
diff --git a/testing/sg_scat_gath.h b/testing/sg_scat_gath.h
index 2b17fb3b..bda8aef7 100644
--- a/testing/sg_scat_gath.h
+++ b/testing/sg_scat_gath.h
@@ -7,13 +7,13 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-// C headers
+// C standard headers
#include <stdio.h>
#include <stdint.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
-// C++ headers
+// C++ standard headers
#include <vector>
// This file is a C++ header file
@@ -24,28 +24,28 @@
#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. */
+// 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 */
+ 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. */
+// 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 */
+ 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 */
+// 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),
@@ -76,18 +76,18 @@ public:
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 */
+ // 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' */
+ 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);
@@ -98,7 +98,7 @@ private:
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()) */
+ std::vector<scat_gath_elem> sgl; // an array on heap [0..num_elems())
};
@@ -113,11 +113,12 @@ public:
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 */
+ bool is_sgl_linear() const; // the whole list
+ // Should return 1 or more unless max_n<=0 or at_end()
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 */
+ // add/sub blocks return true if they reach EOL/start, else false
bool add_blks(uint64_t blk_count);
bool sub_blks(uint64_t blk_count);
@@ -133,9 +134,9 @@ public:
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) */
+ // 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/sgh_dd.cpp b/testing/sgh_dd.cpp
index 98b6fafe..de1ba9aa 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -36,7 +36,7 @@
* renamed [20181221]
*/
-static const char * version_str = "1.86 20200729";
+static const char * version_str = "1.89 20200818";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -358,6 +358,7 @@ static atomic<int> num_abort_req_success(0);
static atomic<int> num_mrq_abort_req(0);
static atomic<int> num_mrq_abort_req_success(0);
static atomic<long> num_waiting_calls(0);
+static atomic<bool> vb_first_time(true);
static sigset_t signal_set;
static pthread_t sig_listen_thread_id;
@@ -529,7 +530,7 @@ sg_flags_str(int flags, int b_len, char * b)
goto fini;
}
if (SGV4_FLAG_NO_WAITQ & flags) { /* 0x40 */
- n += sg_scnpr(b + n, b_len - n, "NWTQ|");
+ n += sg_scnpr(b + n, b_len - n, "NO_WTQ|");
if (n >= b_len)
goto fini;
}
@@ -539,7 +540,7 @@ sg_flags_str(int flags, int b_len, char * b)
goto fini;
}
if (SGV4_FLAG_COMPLETE_B4 & flags) { /* 0x100 */
- n += sg_scnpr(b + n, b_len - n, "NWTQ|");
+ n += sg_scnpr(b + n, b_len - n, "CPL_B4|");
if (n >= b_len)
goto fini;
}
@@ -598,6 +599,8 @@ sg_flags_str(int flags, int b_len, char * b)
if (n >= b_len)
goto fini;
}
+ if (0 == n)
+ n += sg_scnpr(b + n, b_len - n, "<none>");
fini:
if (n < b_len) { /* trim trailing '\' */
if ('|' == b[n - 1])
@@ -753,10 +756,14 @@ print_stats(const char * str)
pr2serr("%s%" PRId64 "+%d records in\n", str,
infull - gcoll.in_partial.load(), gcoll.in_partial.load());
- outfull = dd_count - gcoll.out_rem_count.load();
- pr2serr("%s%" PRId64 "+%d records %s\n", str,
- outfull - gcoll.out_partial.load(), gcoll.out_partial.load(),
- (gcoll.verify ? "verified" : "out"));
+ if (gcoll.out_type == FT_DEV_NULL)
+ pr2serr("%s0+0 records out\n", str);
+ else {
+ outfull = dd_count - gcoll.out_rem_count.load();
+ pr2serr("%s%" PRId64 "+%d records %s\n", str,
+ outfull - gcoll.out_partial.load(), gcoll.out_partial.load(),
+ (gcoll.verify ? "verified" : "out"));
+ }
}
static void
@@ -890,7 +897,7 @@ usage(int pg_num)
" [--help] [--version]\n\n");
pr2serr(" [ae=AEN[,MAEN]] [bpt=BPT] [cdbsz=6|10|12|16] "
"[coe=0|1]\n"
- " [dio=0|1] [elemsz_kb=ESK] [fail_mask=FM] "
+ " [dio=0|1] [elemsz_kb=EKB] [fail_mask=FM] "
"[fua=0|1|2|3]\n"
" [mrq=[I|O,]NRQS[,C]] [noshare=0|1] "
"[of2=OFILE2]\n"
@@ -1596,8 +1603,8 @@ skip_force_out_sequence:
if (stop_after_write)
clp->out_stop = true;
- clp->out_blk += blocks;
clp->out_count -= blocks;
+ clp->out_blk += blocks;
pthread_cleanup_push(cleanup_out, (void *)clp);
if (rep->outregfd >= 0) {
@@ -1614,21 +1621,21 @@ skip_force_out_sequence:
}
/* Output to OFILE */
wr_blks = rep->num_blks;
- if (out_is_sg)
+ if (out_is_sg) {
sg_out_wr_cmd(rep, deferred_arr, false, clp->prefetch);
- else if (FT_DEV_NULL == clp->out_type) {
+ ++rep->rep_count;
+ } else if (FT_DEV_NULL == clp->out_type) {
/* skip actual write operation */
wr_blks = 0;
clp->out_rem_count -= blocks;
status = pthread_mutex_unlock(&clp->out_mutex);
if (0 != status) err_exit(status, "unlock out_mutex");
- --rep->rep_count;
} else {
normal_out_wr(rep, blocks);
status = pthread_mutex_unlock(&clp->out_mutex);
if (0 != status) err_exit(status, "unlock out_mutex");
+ ++rep->rep_count;
}
- ++rep->rep_count;
pthread_cleanup_pop(0);
/* Output to OFILE2 if sg device */
@@ -2483,7 +2490,8 @@ sgh_do_async_mrq(Rq_elem * rep, mrq_arr_t & def_arr, int fd,
a_v4p = def_arr.first.data();
ctlop->flags = SGV4_FLAG_MULTIPLE_REQS;
if (clp->in_flags.no_waitq || clp->out_flags.no_waitq) {
- ctlop->flags |= SGV4_FLAG_NO_WAITQ; /* waitless non-blocking */
+ /* waitless non-blocking */
+ ctlop->flags |= (SGV4_FLAG_IMMED | SGV4_FLAG_NO_WAITQ);
if (!after1 && (clp->verbose > 1)) {
after1 = true;
pr2serr_lk("%s: %s\n", __func__, mrq_nw_nb_s);
@@ -2700,7 +2708,7 @@ sgh_do_deferred_mrq(Rq_elem * rep, mrq_arr_t & def_arr)
struct sg_io_v4 ctl_v4;
uint8_t * cmd_ap = NULL;
struct global_collection * clp = rep->clp;
- const char * iosub_str = "svb ?";
+ const char * iosub_str = "iosub_str";
char b[80];
id = rep->id;
@@ -2787,10 +2795,10 @@ sgh_do_deferred_mrq(Rq_elem * rep, mrq_arr_t & def_arr)
}
ctl_v4.request_extra = launch_mrq_abort ? mrq_pack_id : 0;
rep->mrq_id = mrq_pack_id;
+ if (clp->verbose && rep->both_sg && clp->mrq_async)
+ iosub_str = "SG_IOSUBMIT(variable)";
if (clp->verbose > 4) {
- if (rep->both_sg && clp->mrq_async)
- iosub_str = "SUBMIT(variable)";
- pr2serr_lk("%s: Controlling object _before_ ioctl(SG_IO%s):\n",
+ pr2serr_lk("%s: Controlling object _before_ ioctl(%s):\n",
__func__, iosub_str);
if (clp->verbose > 5)
hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
@@ -2870,6 +2878,7 @@ sgh_do_deferred_mrq(Rq_elem * rep, mrq_arr_t & def_arr)
try_again:
if (clp->unbalanced_mrq) {
+ iosub_str = "SG_IOSUBMIT(variable_blocking)";
if (!after1 && (clp->verbose > 1)) {
after1 = true;
pr2serr_lk("%s: unbalanced %s\n", __func__, mrq_vb_s);
@@ -2877,14 +2886,14 @@ try_again:
res = ioctl(fd, SG_IOSUBMIT, &ctl_v4);
} else {
if (clp->mrq_async) {
- iosub_str = "SUBMIT(variable_blocking)";
+ iosub_str = "SG_IOSUBMIT(variable_blocking)";
if (!after1 && (clp->verbose > 1)) {
after1 = true;
pr2serr_lk("%s: %s\n", __func__, mrq_vb_s);
}
res = ioctl(fd, SG_IOSUBMIT, &ctl_v4);
} else if (clp->in_flags.mrq_svb || clp->in_flags.mrq_svb) {
- iosub_str = "SUBMIT(shared_variable_blocking)";
+ iosub_str = "SG_IOSUBMIT(shared_variable_blocking)";
if (!after1 && (clp->verbose > 1)) {
after1 = true;
pr2serr_lk("%s: %s\n", __func__, mrq_svb_s);
@@ -2915,9 +2924,14 @@ try_again:
res = -1;
goto fini;
}
- if (clp->verbose > 4) {
+ if (clp->verbose && vb_first_time.load()) {
+ pr2serr_lk("First controlling object output by ioctl(%s), flags: "
+ "%s\n", iosub_str, sg_flags_str(ctl_v4.flags, b_len, b));
+ vb_first_time.store(false);
+ } else if (clp->verbose > 4)
pr2serr_lk("%s: Controlling object output by ioctl(%s):\n",
__func__, iosub_str);
+ if (clp->verbose > 4) {
if (clp->verbose > 5)
hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
v4hdr_out_lk("Controlling object after", &ctl_v4, id);
@@ -3073,6 +3087,11 @@ sg_start_io(Rq_elem * rep, mrq_arr_t & def_arr, int & pack_id,
blk_off = 0;
if (no_waitq)
flags |= SGV4_FLAG_NO_WAITQ;
+ if (clp->verbose && 0 == clp->nmrqs && vb_first_time.load()) {
+ vb_first_time.store(false);
+ pr2serr("First normal IO: %s, flags: %s\n", cp,
+ sg_flags_str(flags, b_len, b));
+ }
if (v4) {
memset(h4p, 0, sizeof(struct sg_io_v4));
if (clp->nmrqs > 0) {
@@ -3481,12 +3500,6 @@ sg_prepare_resbuf(int fd, int bs, int bpt, bool def_res, int elem_sz,
if (elem_sz >= 4096) {
memset(seip, 0, sizeof(*seip));
seip->sei_rd_mask |= SG_SEIM_SGAT_ELEM_SZ;
- if (no_dur) {
- seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
- seip->sei_rd_mask |= SG_SEIM_CTL_FLAGS;
- seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_DURATION;
- seip->ctl_flags |= SG_CTL_FLAGM_NO_DURATION;
- }
res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
if (res < 0)
pr2serr_lk("sgh_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) rd "
@@ -3840,12 +3853,16 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
clp->in_flags.dio = !! sg_get_num(buf);
clp->out_flags.dio = clp->in_flags.dio;
} else if (0 == strcmp(key, "elemsz_kb")) {
- clp->elem_sz = sg_get_num(buf) * 1024;
- if ((clp->elem_sz > 0) && (clp->elem_sz < 4096)) {
- pr2serr("elemsz_kb cannot be less than 4 (4 KB = 4096 "
- "bytes)\n");
+ n = sg_get_num(buf);
+ if (n < 1) {
+ pr2serr("elemsz_kb=EKB wants an integer > 0\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (n & (n - 1)) {
+ pr2serr("elemsz_kb=EKB wants EKB to be power of 2\n");
return SG_LIB_SYNTAX_ERROR;
}
+ clp->elem_sz = n * 1024;
} else if ((0 == strcmp(key, "fail_mask")) ||
(0 == strcmp(key, "fail-mask"))) {
clp->fail_mask = sg_get_num(buf);
diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c
index 8980864a..cca5330c 100644
--- a/testing/sgs_dd.c
+++ b/testing/sgs_dd.c
@@ -490,7 +490,7 @@ do_v4:
return res;
}
if (! (rep && (SGQ_IO_STARTED == rep->state))) {
- pr2serr("%s: bad usr_ptr=0x%p\n", __func__, rep);
+ pr2serr("%s: bad usr_ptr=0x%p\n", __func__, (void *)rep);
if (rep)
rep->state = SGQ_IO_ERR;
return -1;
diff --git a/testing/uapi_sg.h b/testing/uapi_sg.h
index 2f40f7e8..2a7e6d9f 100644
--- a/testing/uapi_sg.h
+++ b/testing/uapi_sg.h
@@ -14,7 +14,7 @@
* Later extensions (versions 2, 3 and 4) to driver:
* Copyright (C) 1998 - 2020 Douglas Gilbert
*
- * Version 4.0.44 (20200618)
+ * Version 4.0.44 (20200818)
* This version is for Linux 4 and 5 series kernels.
*
* Documentation
@@ -128,6 +128,7 @@ typedef struct sg_io_hdr {
#define SGV4_FLAG_MULTIPLE_REQS 0x20000 /* n sg_io_v4s in data-in */
#define SGV4_FLAG_EVENTFD 0x40000 /* signal completion on ... */
#define SGV4_FLAG_ORDERED_WR 0x80000 /* svb: issue in-order writes */
+#define SGV4_FLAG_REC_ORDER 0x100000 /* receive order in v4:request_priority */
/* Output (potentially OR-ed together) in v3::info or v4::info field */
#define SG_INFO_OK_MASK 0x1