aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2018-12-17 21:16:36 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2018-12-17 21:16:36 +0000
commit18c9852db91032afe4bedd85857d558402b360e2 (patch)
treee1f3ef6efab1d17665cb19e85d43439888291a6e
parentd906e8cb000e95d9260d1833d3692c1c763e489a (diff)
downloadsg3_utils-18c9852db91032afe4bedd85857d558402b360e2.tar.gz
testing: improve sgs_dd, sg_tst_async and sg_tst_ioctl sg v4 test utilities
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@799 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog2
-rw-r--r--src/sg_dd.c43
-rw-r--r--src/sgm_dd.c20
-rw-r--r--src/sgp_dd.c20
-rw-r--r--testing/sg_tst_async.cpp338
-rw-r--r--testing/sg_tst_ioctl.c519
-rw-r--r--testing/sgs_dd.c24
-rw-r--r--testing/uapi_sg.h16
8 files changed, 725 insertions, 257 deletions
diff --git a/ChangeLog b/ChangeLog
index 3cbdca10..61559027 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.45 [20181213] [svn: r797]
+Changelog for sg3_utils-1.45 [20181217] [svn: r798]
- sg_ses: bug: --page= being overridden when --control
and --data= also given; fix
- sg_opcodes: expand MLU (18-102r0)
diff --git a/src/sg_dd.c b/src/sg_dd.c
index 28e6be92..0dadc6c0 100644
--- a/src/sg_dd.c
+++ b/src/sg_dd.c
@@ -11,8 +11,8 @@
*
* This program is a specialisation of the Unix "dd" command in which
* either the input or the output file is a scsi generic device, raw
- * device, a block device or a normal file. The block size ('bs') is
- * assumed to be 512 if not given. This program complains if 'ibs' or
+ * device, a block device or a normal file. The logical block size ('bs')
+ * is assumed to be 512 if not given. This program complains if 'ibs' or
* 'obs' are given with a value that differs from 'bs' (or the default 512).
* If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
* not given or 'of=-' then stdout assumed.
@@ -66,7 +66,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "6.04 20180811";
+static const char * version_str = "6.05 20181213";
#define ME "sg_dd: "
@@ -366,7 +366,7 @@ usage()
"SG_IO\n"
" bpt is blocks_per_transfer (default is 128 or 32 "
"when BS>=2048)\n"
- " bs block size (default is 512)\n");
+ " bs logical block size (default is 512)\n");
pr2serr(" cdbsz size of SCSI READ or WRITE cdb (default is "
"10)\n"
" coe 0->exit on error (def), 1->continue on sg "
@@ -378,15 +378,16 @@ usage()
"times\n"
" when COE>1 (default: 0 which is no limit)\n"
" count number of blocks to copy (def: device size)\n"
- " dio for direct IO, 1->attempt, 0->indirect IO (def)\n"
- " ibs input block size (if given must be same as "
- "'bs=')\n"
+ " dio for direct IO, 1->attempt, 0->indirect IO "
+ "(def)\n"
+ " ibs input logical block size (if given must be same "
+ "as 'bs=')\n"
" if file or device to read from (def: stdin)\n"
" iflag comma separated list from: [coe,dio,direct,"
"dpo,dsync,excl,\n"
" flock,fua,nocache,null,sgio]\n"
- " obs output block size (if given must be same as "
- "'bs=')\n"
+ " obs output logical block size (if given must be "
+ "same as 'bs=')\n"
" odir 1->use O_DIRECT when opening block dev, "
"0->don't(def)\n"
" of file or device to write to (def: stdout), "
@@ -451,7 +452,7 @@ scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
}
if (verbose)
pr2serr(" number of blocks=%" PRId64 " [0x%" PRIx64 "], "
- "block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ "logical block size=%d\n", *num_sect, *num_sect, *sect_sz);
return 0;
}
@@ -477,7 +478,8 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
*num_sect = ((int64_t)ull / (int64_t)*sect_sz);
if (verbose)
pr2serr(" [bgs64] number of blocks=%" PRId64 " [0x%" PRIx64
- "], block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ "], logical block size=%d\n", *num_sect, *num_sect,
+ *sect_sz);
#else
unsigned long ul;
@@ -488,7 +490,8 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
*num_sect = (int64_t)ul;
if (verbose)
pr2serr(" [bgs] number of blocks=%" PRId64 " [0x%" PRIx64
- "], block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ "], logical block size=%d\n", *num_sect, *num_sect,
+ *sect_sz);
#endif
}
return 0;
@@ -1729,7 +1732,8 @@ main(int argc, char * argv[])
}
if (blk_sz <= 0) {
blk_sz = DEF_BLOCK_SIZE;
- pr2serr("Assume default 'bs' (block size) of %d bytes\n", blk_sz);
+ pr2serr("Assume default 'bs' ((logical) block size) of %d bytes\n",
+ blk_sz);
}
if ((ibs && (ibs != blk_sz)) || (obs && (obs != blk_sz))) {
pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
@@ -1826,15 +1830,15 @@ main(int argc, char * argv[])
pr2serr("Unable to read capacity on %s\n", inf);
in_num_sect = -1;
} else if (in_sect_sz != blk_sz)
- pr2serr(">> warning: block size on %s confusion: bs=%d, "
- "device claims=%d\n", inf, blk_sz, in_sect_sz);
+ pr2serr(">> warning: logical block size on %s confusion: "
+ "bs=%d, device claims=%d\n", inf, blk_sz, in_sect_sz);
} else if (FT_BLOCK & in_type) {
if (0 != read_blkdev_capacity(infd, &in_num_sect, &in_sect_sz)) {
pr2serr("Unable to read block capacity on %s\n", inf);
in_num_sect = -1;
}
if (blk_sz != in_sect_sz) {
- pr2serr("block size on %s confusion: bs=%d, device "
+ pr2serr("logical block size on %s confusion: bs=%d, device "
"claims=%d\n", inf, blk_sz, in_sect_sz);
in_num_sect = -1;
}
@@ -1860,15 +1864,16 @@ main(int argc, char * argv[])
pr2serr("Unable to read capacity on %s\n", outf);
out_num_sect = -1;
} else if (blk_sz != out_sect_sz)
- pr2serr(">> warning: block size on %s confusion: bs=%d, "
- "device claims=%d\n", outf, blk_sz, out_sect_sz);
+ pr2serr(">> warning: logical block size on %s confusion: "
+ "bs=%d, device claims=%d\n", outf, blk_sz,
+ out_sect_sz);
} else if (FT_BLOCK & out_type) {
if (0 != read_blkdev_capacity(outfd, &out_num_sect,
&out_sect_sz)) {
pr2serr("Unable to read block capacity on %s\n", outf);
out_num_sect = -1;
} else if (blk_sz != out_sect_sz) {
- pr2serr("block size on %s confusion: bs=%d, device "
+ pr2serr("logical block size on %s confusion: bs=%d, device "
"claims=%d\n", outf, blk_sz, out_sect_sz);
out_num_sect = -1;
}
diff --git a/src/sgm_dd.c b/src/sgm_dd.c
index 0b6cf044..76bba41d 100644
--- a/src/sgm_dd.c
+++ b/src/sgm_dd.c
@@ -273,7 +273,8 @@ usage()
"[--dry-run] [--verbose]\n\n"
" where:\n"
" bpt is blocks_per_transfer (default is 128)\n"
- " bs must be device block size (default 512)\n"
+ " bs must be device logical block size (default "
+ "512)\n"
" cdbsz size of SCSI READ or WRITE cdb (default is 10)\n"
" count number of blocks to copy (def: device size)\n"
" dio 0->indirect IO on write, 1->direct IO on write\n"
@@ -363,7 +364,8 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
*num_sect = ((int64_t)ull / (int64_t)*sect_sz);
if (verbose)
pr2serr(" [bgs64] number of blocks=%" PRId64 " [0x%" PRIx64
- "], block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ "], logical block size=%d\n", *num_sect, *num_sect,
+ *sect_sz);
#else
unsigned long ul;
@@ -374,7 +376,8 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
*num_sect = (int64_t)ul;
if (verbose)
pr2serr(" [bgs] number of blocks=%" PRId64 " [0x%" PRIx64
- "], block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ "], logical block size=%d\n", *num_sect, *num_sect,
+ *sect_sz);
#endif
}
return 0;
@@ -913,7 +916,8 @@ main(int argc, char * argv[])
if (blk_sz <= 0) {
blk_sz = DEF_BLOCK_SIZE;
- pr2serr("Assume default 'bs' (block size) of %d bytes\n", blk_sz);
+ pr2serr("Assume default 'bs' ((logical) block size) of %d bytes\n",
+ blk_sz);
}
if ((ibs && (ibs != blk_sz)) || (obs && (obs != blk_sz))) {
pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
@@ -1172,8 +1176,8 @@ main(int argc, char * argv[])
in_num_sect = -1;
}
if (blk_sz != in_sect_sz) {
- pr2serr("block size on %s confusion; bs=%d, from device=%d\n",
- inf, blk_sz, in_sect_sz);
+ pr2serr("logical block size on %s confusion; bs=%d, from "
+ "device=%d\n", inf, blk_sz, in_sect_sz);
in_num_sect = -1;
}
}
@@ -1202,8 +1206,8 @@ main(int argc, char * argv[])
out_num_sect = -1;
}
if (blk_sz != out_sect_sz) {
- pr2serr("block size on %s confusion: bs=%d, from device=%d\n",
- outf, blk_sz, out_sect_sz);
+ pr2serr("logical block size on %s confusion: bs=%d, from "
+ "device=%d\n", outf, blk_sz, out_sect_sz);
out_num_sect = -1;
}
}
diff --git a/src/sgp_dd.c b/src/sgp_dd.c
index b129d575..e6e06355 100644
--- a/src/sgp_dd.c
+++ b/src/sgp_dd.c
@@ -11,8 +11,8 @@
*
* This program is a specialisation of the Unix "dd" command in which
* one or both of the given files is a scsi generic device or a raw
- * device. A block size ('bs') is assumed to be 512 if not given. This
- * program complains if 'ibs' or 'obs' are given with some other value
+ * device. A logical block size ('bs') is assumed to be 512 if not given.
+ * This program complains if 'ibs' or 'obs' are given with some other value
* than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
* 'of' is not given or 'of=-' then stdout assumed.
*
@@ -68,7 +68,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "5.69 20180811";
+static const char * version_str = "5.70 20181213";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -356,7 +356,8 @@ usage()
" [--dry-run] [--verbose]\n"
" where:\n"
" bpt is blocks_per_transfer (default is 128)\n"
- " bs must be device block size (default 512)\n"
+ " bs must be device logical block size (default "
+ "512)\n"
" cdbsz size of SCSI READ or WRITE cdb (default is 10)\n"
" coe continue on error, 0->exit (def), "
"1->zero + continue\n"
@@ -1369,7 +1370,8 @@ main(int argc, char * argv[])
if (rcoll.bs <= 0) {
rcoll.bs = DEF_BLOCK_SIZE;
- pr2serr("Assume default 'bs' (block size) of %d bytes\n", rcoll.bs);
+ pr2serr("Assume default 'bs' ((logical) block size) of %d bytes\n",
+ rcoll.bs);
}
if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) {
pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
@@ -1569,8 +1571,8 @@ main(int argc, char * argv[])
in_num_sect = -1;
}
if (rcoll.bs != in_sect_sz) {
- pr2serr("block size on %s confusion; bs=%d, from device=%d\n",
- inf, rcoll.bs, in_sect_sz);
+ pr2serr("logical block size on %s confusion; bs=%d, from "
+ "device=%d\n", inf, rcoll.bs, in_sect_sz);
in_num_sect = -1;
}
}
@@ -1601,8 +1603,8 @@ main(int argc, char * argv[])
out_num_sect = -1;
}
if (rcoll.bs != out_sect_sz) {
- pr2serr("block size on %s confusion: bs=%d, from device=%d\n",
- outf, rcoll.bs, out_sect_sz);
+ pr2serr("logical block size on %s confusion: bs=%d, from "
+ "device=%d\n", outf, rcoll.bs, out_sect_sz);
out_num_sect = -1;
}
}
diff --git a/testing/sg_tst_async.cpp b/testing/sg_tst_async.cpp
index a6ac65ee..d16b22fc 100644
--- a/testing/sg_tst_async.cpp
+++ b/testing/sg_tst_async.cpp
@@ -82,7 +82,7 @@
#include "sg_pt.h"
#include "sg_cmds.h"
-static const char * version_str = "1.22 20181207";
+static const char * version_str = "1.23 20181216";
static const char * util_name = "sg_tst_async";
/* This is a test program for checking the async usage of the Linux sg
@@ -185,7 +185,9 @@ struct opts_t {
bool no_xfer;
bool pack_id_force;
bool sg_vn_ge_30901;
+ bool submit;
bool verbose_given;
+ bool v4;
bool version_given;
int maxq_per_thread;
int num_per_thread;
@@ -245,6 +247,7 @@ private:
};
static struct option long_options[] = {
+ {"v4", no_argument, 0, '4'},
{"cmd-time", no_argument, 0, 'c'},
{"cmd_time", no_argument, 0, 'c'},
{"direct", no_argument, 0, 'd'},
@@ -265,8 +268,9 @@ static struct option long_options[] = {
{"qat", required_argument, 0, 'q'},
{"qfav", required_argument, 0, 'Q'},
{"read", no_argument, 0, 'R'},
- {"szlb", required_argument, 0, 's'},
{"stats", no_argument, 0, 'S'},
+ {"submit", no_argument, 0, 'u'},
+ {"szlb", required_argument, 0, 's'},
{"tnum", required_argument, 0, 't'},
{"tur", no_argument, 0, 'T'},
{"verbose", no_argument, 0, 'v'},
@@ -285,11 +289,12 @@ usage(void)
"[--mmap-io]\n"
" [--numpt=NPT] [--noxfer] [--pack-id] "
"[--qat=AT]\n"
- " [-qfav=FAV] [--read] [--szlb=LB[,NLBS]] "
- "[--stats]\n"
- " [--tnum=NT] [--tur] [--verbose] [--version] "
- "[--wait=MS]\n"
- " [--write] <sg_disk_device>*\n",
+ " [-qfav=FAV] [--read] [--stats] [--submit]\n"
+ " [--szlb=LB[,NLBS]] [--tnum=NT] [--tur] "
+ "[--v4]\n"
+ " [--verbose] [--version] [--wait=MS] "
+ "[--write]\n"
+ " <sg_disk_device>*\n",
util_name);
printf(" where\n");
printf(" --cmd-time|-c calculate per command average time (ns)\n");
@@ -324,13 +329,17 @@ usage(void)
" FAV=2: favour submissions (larger q, "
"default)\n");
printf(" --read|-R do READs (def: TUR)\n");
+ printf(" --stats|-S show more statistics on completion\n");
+ printf(" --submit|-u use SG_IOSUBMIT+SG_IORECEIVE instead of "
+ "write+read\n");
printf(" --szlb=LB[,NLBS]| LB is logical block size (def: 512)\n");
printf(" -s LB[,NLBS] NLBS is number of logical blocks (def: "
"1)\n");
- printf(" --stats|-S show more statistics on completion\n");
printf(" --tnum=NT|-t NT number of threads (def: %d)\n",
DEF_NUM_THREADS);
printf(" --tur|-T do TEST UNIT READYs (default is TURs)\n");
+ printf(" --v4|-4 use sg v4 interface (def: v3). If given "
+ "sets --submit\n");
printf(" --verbose|-v increase verbosity\n");
printf(" --version|-V print version number then exit\n");
printf(" --wait=MS|-w MS >0: poll(<wait_ms>); =0: poll(0); (def: "
@@ -410,11 +419,12 @@ get_urandom_uint(void)
* not done due to queue data size limit struck. */
static int
start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
- uint8_t * lbp, int xfer_bytes, int flags,
+ uint8_t * lbp, int xfer_bytes, int flags, bool submit,
unsigned int & eagains, unsigned int & ebusy,
unsigned int & e2big, unsigned int & edom)
{
struct sg_io_hdr pt;
+ struct sg_io_v4 p4t;
uint8_t turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0};
uint8_t r16CmdBlk[READ16_CMD_LEN] =
{0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
@@ -422,46 +432,56 @@ start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
{0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
uint8_t sense_buffer[64];
const char * np = NULL;
-
- memset(&pt, 0, sizeof(pt));
+ struct sg_io_hdr * ptp;
+
+ if (submit) { /* nest a v3 interface inside a store for v4 */
+ memset(&p4t, 0, sizeof(p4t));
+ ptp = (struct sg_io_hdr *)&p4t; /* p4t is larger than pt */
+ } else {
+ ptp = &pt;
+ memset(ptp, 0, sizeof(*ptp));
+ }
switch (cmd2exe) {
case SCSI_TUR:
np = "TEST UNIT READY";
- pt.cmdp = turCmdBlk;
- pt.cmd_len = sizeof(turCmdBlk);
- pt.dxfer_direction = SG_DXFER_NONE;
+ ptp->cmdp = turCmdBlk;
+ ptp->cmd_len = sizeof(turCmdBlk);
+ ptp->dxfer_direction = SG_DXFER_NONE;
break;
case SCSI_READ16:
np = "READ(16)";
if (lba > 0xffffffff)
sg_put_unaligned_be32(lba >> 32, &r16CmdBlk[2]);
sg_put_unaligned_be32(lba & 0xffffffff, &r16CmdBlk[6]);
- pt.cmdp = r16CmdBlk;
- pt.cmd_len = sizeof(r16CmdBlk);
- pt.dxfer_direction = SG_DXFER_FROM_DEV;
- pt.dxferp = lbp;
- pt.dxfer_len = xfer_bytes;
+ ptp->cmdp = r16CmdBlk;
+ ptp->cmd_len = sizeof(r16CmdBlk);
+ ptp->dxfer_direction = SG_DXFER_FROM_DEV;
+ ptp->dxferp = lbp;
+ ptp->dxfer_len = xfer_bytes;
break;
case SCSI_WRITE16:
np = "WRITE(16)";
if (lba > 0xffffffff)
sg_put_unaligned_be32(lba >> 32, &w16CmdBlk[2]);
sg_put_unaligned_be32(lba & 0xffffffff, &w16CmdBlk[6]);
- pt.cmdp = w16CmdBlk;
- pt.cmd_len = sizeof(w16CmdBlk);
- pt.dxfer_direction = SG_DXFER_TO_DEV;
- pt.dxferp = lbp;
- pt.dxfer_len = xfer_bytes;
+ ptp->cmdp = w16CmdBlk;
+ ptp->cmd_len = sizeof(w16CmdBlk);
+ ptp->dxfer_direction = SG_DXFER_TO_DEV;
+ ptp->dxferp = lbp;
+ ptp->dxfer_len = xfer_bytes;
break;
}
- pt.interface_id = 'S';
- pt.mx_sb_len = sizeof(sense_buffer);
- pt.sbp = sense_buffer; /* ignored .... */
- pt.timeout = DEF_TIMEOUT_MS;
- pt.pack_id = pack_id;
- pt.flags = flags;
-
- for (int k = 0; write(sg_fd, &pt, sizeof(pt)) < 0; ++k) {
+ ptp->interface_id = 'S';
+ ptp->mx_sb_len = sizeof(sense_buffer);
+ ptp->sbp = sense_buffer; /* ignored .... */
+ ptp->timeout = DEF_TIMEOUT_MS;
+ ptp->pack_id = pack_id;
+ ptp->flags = flags;
+
+ for (int k = 0;
+ (submit ? ioctl(sg_fd, SG_IOSUBMIT, ptp) :
+ write(sg_fd, ptp, sizeof(*ptp)) < 0);
+ ++k) {
if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) {
this_thread::yield();
continue;
@@ -486,39 +506,212 @@ start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
static int
finish_sg3_cmd(int sg_fd, command2execute cmd2exe, bool pack_id_force,
- int & pack_id, int wait_ms, unsigned int & eagains,
- unsigned int & nanosecs)
+ int & pack_id, bool receive, int wait_ms,
+ unsigned int & eagains, unsigned int & nanosecs)
{
bool ok;
int res, k;
+ uint8_t sense_buffer[64];
+ const char * np = NULL;
struct sg_io_hdr pt;
+ struct sg_io_hdr * ptp;
+ struct sg_io_v4 p4t;
+
+ if (receive) { /* nest a v3 interface inside a store for v4 */
+ memset(&p4t, 0, sizeof(p4t));
+ ptp = (struct sg_io_hdr *)&p4t; /* p4t is larger than pt */
+ } else {
+ ptp = &pt;
+ memset(ptp, 0, sizeof(*ptp));
+ }
+ switch (cmd2exe) {
+ case SCSI_TUR:
+ np = "TEST UNIT READY";
+ ptp->dxfer_direction = SG_DXFER_NONE;
+ break;
+ case SCSI_READ16:
+ np = "READ(16)";
+ ptp->dxfer_direction = SG_DXFER_FROM_DEV;
+ break;
+ case SCSI_WRITE16:
+ np = "WRITE(16)";
+ ptp->dxfer_direction = SG_DXFER_TO_DEV;
+ break;
+ }
+ ptp->interface_id = 'S';
+ ptp->mx_sb_len = sizeof(sense_buffer);
+ ptp->sbp = sense_buffer;
+ ptp->timeout = DEF_TIMEOUT_MS;
+ /* if SG_SET_FORCE_PACK_ID, then need to set ptp->dxfer_direction */
+ ptp->pack_id = pack_id;
+
+ k = 0;
+ while ((((res = receive ? ioctl(sg_fd, SG_IORECEIVE, ptp) :
+ read(sg_fd, ptp, sizeof(*ptp)))) < 0) &&
+ (EAGAIN == errno)) {
+ ++eagains;
+ if (pack_id_force) {
+ ++k;
+ if (k > 10000) {
+ pr2serr_lk("%s: unable to find pack_id=%d\n", __func__,
+ pack_id);
+ return -1; /* crash out */
+ }
+ }
+ if (wait_ms > 0)
+ this_thread::sleep_for(milliseconds{wait_ms});
+ else if (0 == wait_ms)
+ this_thread::yield();
+ else if (-2 == wait_ms)
+ sleep(0); // process yield ??
+ }
+ if (res < 0) {
+ pr_errno_lk(errno, "%s: %s", __func__, np);
+ return -1;
+ }
+ /* now for the error processing */
+ pack_id = ptp->pack_id;
+ ok = false;
+ switch (sg_err_category3(ptp)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = true;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ pr2serr_lk("%s: Recovered error on %s, continuing\n", __func__, np);
+ ok = true;
+ break;
+ default: /* won't bother decoding other categories */
+ {
+ lock_guard<mutex> lg(console_mutex);
+ sg_chk_n_print3(np, ptp, 1);
+ }
+ break;
+ }
+ if (ok)
+ nanosecs = ptp->duration;
+ return ok ? 0 : -1;
+}
+
+/* Returns 0 if command injected okay, return -1 for error and 2 for
+ * not done due to queue data size limit struck. */
+static int
+start_sg4_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
+ uint8_t * lbp, int xfer_bytes, int flags, bool submit,
+ unsigned int & eagains, unsigned int & ebusy,
+ unsigned int & e2big, unsigned int & edom)
+{
+ struct sg_io_v4 p4t;
+ uint8_t turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0};
+ uint8_t r16CmdBlk[READ16_CMD_LEN] =
+ {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+ uint8_t w16CmdBlk[WRITE16_CMD_LEN] =
+ {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
uint8_t sense_buffer[64];
const char * np = NULL;
+ struct sg_io_v4 * ptp;
- memset(&pt, 0, sizeof(pt));
+ if (! submit) {
+ pr2serr_lk("%s: logic error, submit must be true, isn't\n", __func__);
+ return -1;
+ }
+ ptp = &p4t;
+ memset(ptp, 0, sizeof(*ptp));
switch (cmd2exe) {
case SCSI_TUR:
np = "TEST UNIT READY";
- pt.dxfer_direction = SG_DXFER_NONE;
+ ptp->request = (uint64_t)turCmdBlk;
+ ptp->request_len = sizeof(turCmdBlk);
break;
case SCSI_READ16:
np = "READ(16)";
- pt.dxfer_direction = SG_DXFER_FROM_DEV;
+ if (lba > 0xffffffff)
+ sg_put_unaligned_be32(lba >> 32, &r16CmdBlk[2]);
+ sg_put_unaligned_be32(lba & 0xffffffff, &r16CmdBlk[6]);
+ ptp->request = (uint64_t)r16CmdBlk;
+ ptp->request_len = sizeof(r16CmdBlk);
+ ptp->din_xferp = (uint64_t)lbp;
+ ptp->din_xfer_len = xfer_bytes;
break;
case SCSI_WRITE16:
np = "WRITE(16)";
- pt.dxfer_direction = SG_DXFER_TO_DEV;
+ if (lba > 0xffffffff)
+ sg_put_unaligned_be32(lba >> 32, &w16CmdBlk[2]);
+ sg_put_unaligned_be32(lba & 0xffffffff, &w16CmdBlk[6]);
+ ptp->request = (uint64_t)w16CmdBlk;
+ ptp->request_len = sizeof(w16CmdBlk);
+ ptp->dout_xferp = (uint64_t)lbp;
+ ptp->dout_xfer_len = xfer_bytes;
break;
}
- pt.interface_id = 'S';
- pt.mx_sb_len = sizeof(sense_buffer);
- pt.sbp = sense_buffer;
- pt.timeout = DEF_TIMEOUT_MS;
- /* if SG_SET_FORCE_PACK_ID, then need to set pt.dxfer_direction */
- pt.pack_id = pack_id;
+ ptp->guard = 'Q';
+ ptp->max_response_len = sizeof(sense_buffer);
+ ptp->response = (uint64_t)sense_buffer; /* ignored .... */
+ ptp->timeout = DEF_TIMEOUT_MS;
+ ptp->request_extra = pack_id;
+ ptp->flags = flags;
+
+ for (int k = 0; ioctl(sg_fd, SG_IOSUBMIT, ptp) < 0; ++k) {
+ if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) {
+ this_thread::yield();
+ continue;
+ } else if (EAGAIN == errno) {
+ ++eagains;
+ this_thread::yield();
+ continue;
+ } else if (EBUSY == errno) {
+ ++ebusy;
+ this_thread::yield();
+ continue;
+ } else if (E2BIG == errno) {
+ ++e2big;
+ return 2;
+ } else if (EDOM == errno)
+ ++edom;
+ pr_errno_lk(errno, "%s: %s, pack_id=%d", __func__, np, pack_id);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+finish_sg4_cmd(int sg_fd, command2execute cmd2exe, bool pack_id_force,
+ int & pack_id, bool receive, int wait_ms,
+ unsigned int & eagains, unsigned int & nanosecs)
+{
+ bool ok;
+ int res, k;
+ uint8_t sense_buffer[64];
+ const char * np = NULL;
+ struct sg_io_v4 * ptp;
+ struct sg_io_v4 p4t;
+
+ if (! receive) {
+ pr2serr_lk("%s: logic error, receive must be true, isn't\n",
+ __func__);
+ return -1;
+ }
+ ptp = &p4t;
+ memset(ptp, 0, sizeof(*ptp));
+ switch (cmd2exe) {
+ case SCSI_TUR:
+ np = "TEST UNIT READY";
+ break;
+ case SCSI_READ16:
+ np = "READ(16)";
+ break;
+ case SCSI_WRITE16:
+ np = "WRITE(16)";
+ break;
+ }
+ ptp->guard = 'S';
+ ptp->max_response_len = sizeof(sense_buffer);
+ ptp->response = (uint64_t)sense_buffer;
+ ptp->timeout = DEF_TIMEOUT_MS;
+ /* if SG_SET_FORCE_PACK_ID, then need to set ptp->dxfer_direction */
+ ptp->request_extra = pack_id;
k = 0;
- while (((res = read(sg_fd, &pt, sizeof(pt))) < 0) &&
+ while ((((res = ioctl(sg_fd, SG_IORECEIVE, ptp))) < 0) &&
(EAGAIN == errno)) {
++eagains;
if (pack_id_force) {
@@ -541,9 +734,13 @@ finish_sg3_cmd(int sg_fd, command2execute cmd2exe, bool pack_id_force,
return -1;
}
/* now for the error processing */
- pack_id = pt.pack_id;
+ pack_id = ptp->request_extra;
ok = false;
- switch (sg_err_category3(&pt)) {
+ res = sg_err_category_new(ptp->device_status, ptp->transport_status,
+ ptp->driver_status,
+ (const uint8_t *)ptp->response,
+ ptp->response_len);
+ switch (res) {
case SG_LIB_CAT_CLEAN:
ok = true;
break;
@@ -554,12 +751,17 @@ finish_sg3_cmd(int sg_fd, command2execute cmd2exe, bool pack_id_force,
default: /* won't bother decoding other categories */
{
lock_guard<mutex> lg(console_mutex);
- sg_chk_n_print3(np, &pt, 1);
+
+ sg_linux_sense_print(np, ptp->device_status,
+ ptp->transport_status,
+ ptp->driver_status,
+ (const uint8_t *)ptp->response,
+ ptp->response_len, true);
}
break;
}
if (ok)
- nanosecs = pt.duration;
+ nanosecs = ptp->duration;
return ok ? 0 : -1;
}
@@ -870,10 +1072,15 @@ work_thread(int id, struct opts_t * op)
lba = 0;
if (vb > 4)
pr2serr_lk("t_id=%d: starting pack_id=%d\n", id, pack_id);
- res = start_sg3_cmd(sg_fd, op->c2e, pack_id, lba, lbp,
- op->lb_sz * op->num_lbs, sg_flags,
- thr_start_eagain_count, thr_start_ebusy_count,
- thr_start_e2big_count, thr_start_edom_count);
+ res = (op->v4) ?
+ start_sg4_cmd(sg_fd, op->c2e, pack_id, lba, lbp,
+ op->lb_sz * op->num_lbs, sg_flags, op->submit,
+ thr_start_eagain_count, thr_start_ebusy_count,
+ thr_start_e2big_count, thr_start_edom_count) :
+ start_sg3_cmd(sg_fd, op->c2e, pack_id, lba, lbp,
+ op->lb_sz * op->num_lbs, sg_flags, op->submit,
+ thr_start_eagain_count, thr_start_ebusy_count,
+ thr_start_e2big_count, thr_start_edom_count);
if (res) {
if (res > 1) { /* here if E2BIG, start not done, try finish */
do_inc = 0;
@@ -989,8 +1196,14 @@ work_thread(int id, struct opts_t * op)
} else
pack_id = -1;
ask = pack_id;
- if (finish_sg3_cmd(sg_fd, op->c2e, op->pack_id_force, pack_id,
- op->wait_ms, thr_fin_eagain_count, nanosecs)) {
+ res = (op->v4) ?
+ finish_sg4_cmd(sg_fd, op->c2e, op->pack_id_force, pack_id,
+ op->submit, op->wait_ms,
+ thr_fin_eagain_count, nanosecs) :
+ finish_sg3_cmd(sg_fd, op->c2e, op->pack_id_force, pack_id,
+ op->submit, op->wait_ms,
+ thr_fin_eagain_count, nanosecs);
+ if (res) {
err = "finish_sg3_cmd()";
if (ruip && (pack_id > 0)) {
auto q = pi_2_lba.find(pack_id);
@@ -1260,12 +1473,15 @@ main(int argc, char * argv[])
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "cdfghl:mM:n:Npq:Q:Rs:St:TvVw:W",
+ c = getopt_long(argc, argv, "4cdfghl:mM:n:Npq:Q:Rs:St:TuvVw:W",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
+ case '4':
+ op->v4 = true;
+ break;
case 'c':
op->cmd_time = true;
break;
@@ -1405,6 +1621,9 @@ main(int argc, char * argv[])
case 'T':
op->c2e = SCSI_TUR;
break;
+ case 'u':
+ op->submit = true;
+ break;
case 'v':
op->verbose_given = true;
++op->verbose;
@@ -1481,6 +1700,13 @@ main(int argc, char * argv[])
cerr << "lba,hi_lba range is illegal" << endl;
return 1;
}
+ if (op->v4) {
+ if (! op->submit) {
+ op->submit = true;
+ if (op->verbose > 1)
+ cerr << "when --v4 is given, --submit will be set" << endl;
+ }
+ }
try {
int sg_ver_num;
diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c
index d7dc227b..d0d7d153 100644
--- a/testing/sg_tst_ioctl.c
+++ b/testing/sg_tst_ioctl.c
@@ -21,6 +21,8 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/socket.h> /* For passing fd_s via Unix sockets */
+
/* Kernel uapi header contain __user decorations on user space pointers
* to indicate they are unsafe in the kernel space. However glibc takes
* all those __user decorations out from headers in /usr/include/linux .
@@ -45,7 +47,7 @@
* later of the Linux sg driver. */
-static const char * version_str = "Version: 0.98 20181207";
+static const char * version_str = "Version: 1.01 20181216";
#define INQ_REPLY_LEN 96
#define INQ_CMD_LEN 6
@@ -67,14 +69,29 @@ static const char * version_str = "Version: 0.98 20181207";
#define DEF_RESERVE_BUFF_SZ (256 * 1024)
+static bool is_parent = false;
+static bool do_fork = false;
+static bool ioctl_only = false;
+static bool q_at_tail = false;
+static bool write_only = false;
+
+static int childs_pid = 0;
+static int q_len = DEF_Q_LEN;
+static int sleep_secs = 0;
+static int reserve_buff_sz = DEF_RESERVE_BUFF_SZ;
+static int verbose = 0;
+
+static const char * relative_cp = NULL;
+
static void
usage(void)
{
- printf("Usage: 'sg_tst_ioctl [-h] [-l=Q_LEN] [-o] [-r=SZ] [-s=SEC] "
+ printf("Usage: 'sg_tst_ioctl [-f] [-h] [-l=Q_LEN] [-o] [-r=SZ] [-s=SEC] "
"[-t]\n"
- " [-v] [-V] [-w] <sg_device>'\n"
+ " [-v] [-V] [-w] <sg_device> [<sg_device2>]'\n"
" where:\n"
+ " -f fork and test share between processes\n"
" -h help: print usage message then exit\n"
" -l=Q_LEN queue length, between 1 and 511 (def: 16)\n"
" -o ioctls only, then exit\n"
@@ -87,125 +104,114 @@ usage(void)
" -w write (submit) only then exit\n");
}
-
-int
-main(int argc, char * argv[])
+/* This function taken from Keith Parkard's blog dated 2101205 */
+static ssize_t
+sock_fd_write(int sock, const void *buf, ssize_t buflen, int fd)
{
- bool done;
- bool q_at_tail = false;
- bool ioctl_only = false;
- bool write_only = false;
- int sg_fd, k, ok, ver_num, pack_id, num_waiting, access_count;
- int sg_fd2 = -1;
- uint8_t inq_cdb[INQ_CMD_LEN] =
- {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
- uint8_t sdiag_cdb[SDIAG_CMD_LEN] =
- {0x1d, 0, 0, 0, 0, 0};
- uint8_t inqBuff[MAX_Q_LEN][INQ_REPLY_LEN];
- sg_io_hdr_t io_hdr[MAX_Q_LEN];
- sg_io_hdr_t rio_hdr;
- char * file_name = 0;
- char ebuff[EBUFF_SZ];
- uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
- int q_len = DEF_Q_LEN;
- int sleep_secs = 0;
- int reserve_buff_sz = DEF_RESERVE_BUFF_SZ;
- int verbose = 0;
- uint32_t cflags;
- struct sg_extended_info sei;
- struct sg_extended_info * seip;
- const char * second_fname = "/dev/sg2";
- struct sg_scsi_id ssi;
+ ssize_t size;
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ struct cmsghdr cmsghdr;
+ char control[CMSG_SPACE(sizeof (int))];
+ } cmsgu;
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = (void *)buf; /* OS shouldn't write back in this */
+ iov.iov_len = buflen;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (fd != -1) {
+ msg.msg_control = cmsgu.control;
+ msg.msg_controllen = sizeof(cmsgu.control);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof (int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ printf ("passing fd %d\n", fd);
+ *((int *) CMSG_DATA(cmsg)) = fd;
+ } else {
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ printf ("not passing fd\n");
+ }
- for (k = 1; k < argc; ++k) {
- if (0 == memcmp("-t", argv[k], 2))
- q_at_tail = true;
- else if (0 == memcmp("-h", argv[k], 2)) {
- file_name = 0;
- break;
- } else if (0 == memcmp("-l=", argv[k], 3)) {
- q_len = atoi(argv[k] + 3);
- if ((q_len > 511) || (q_len < 1)) {
- printf("Expect -l= to take a number (q length) between 1 "
- "and 511\n");
- file_name = 0;
- break;
- }
- } else if (0 == memcmp("-o", argv[k], 2))
- ioctl_only = true;
- else if (0 == memcmp("-r=", argv[k], 3)) {
- reserve_buff_sz = atoi(argv[k] + 3);
- if (reserve_buff_sz < 0) {
- printf("Expect -r= to take a number 0 or higher\n");
- file_name = 0;
- break;
+ size = sendmsg(sock, &msg, 0);
+
+ if (size < 0)
+ perror ("sendmsg");
+ return size;
+}
+
+/* This function taken from Keith Parkard's blog dated 2101205 */
+static ssize_t
+sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd)
+{
+ ssize_t size;
+
+ if (fd) {
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ struct cmsghdr cmsghdr;
+ char control[CMSG_SPACE(sizeof (int))];
+ } cmsgu;
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = buf;
+ iov.iov_len = bufsize;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgu.control;
+ msg.msg_controllen = sizeof(cmsgu.control);
+ size = recvmsg (sock, &msg, 0);
+ if (size < 0) {
+ perror ("recvmsg");
+ exit(1);
+ }
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ if (cmsg->cmsg_level != SOL_SOCKET) {
+ fprintf (stderr, "invalid cmsg_level %d\n",
+ cmsg->cmsg_level);
+ exit(1);
}
- } else if (0 == memcmp("-s=", argv[k], 3)) {
- sleep_secs = atoi(argv[k] + 3);
- if (sleep_secs < 0) {
- printf("Expect -s= to take a number 0 or higher\n");
- file_name = 0;
- break;
+ if (cmsg->cmsg_type != SCM_RIGHTS) {
+ fprintf (stderr, "invalid cmsg_type %d\n",
+ cmsg->cmsg_type);
+ exit(1);
}
- } else if (0 == memcmp("-vvvv", argv[k], 5))
- verbose += 4;
- else if (0 == memcmp("-vvv", argv[k], 4))
- verbose += 3;
- else if (0 == memcmp("-vv", argv[k], 3))
- verbose += 2;
- else if (0 == memcmp("-v", argv[k], 2))
- verbose += 1;
- else if (0 == memcmp("-V", argv[k], 2)) {
- printf("%s\n", version_str);
- file_name = 0;
- break;
- } else if (0 == memcmp("-w", argv[k], 2))
- write_only = true;
- else if (*argv[k] == '-') {
- printf("Unrecognized switch: %s\n", argv[k]);
- file_name = 0;
- break;
- }
- else if (0 == file_name)
- file_name = argv[k];
- else {
- printf("too many arguments\n");
- file_name = 0;
- break;
- }
- }
- if (0 == file_name) {
- usage();
- return 1;
- }
-
- /* 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,
- "sg_queue_tst: error opening file: %s", file_name);
- perror(ebuff);
- return 1;
- }
- if (verbose)
- fprintf(stderr, "opened given file: %s successfully, fd=%d\n",
- file_name, sg_fd);
- if (ioctl(sg_fd, SG_GET_VERSION_NUM, &ver_num) < 0) {
- pr2serr("ioctl(SG_GET_VERSION_NUM) failed, errno=%d %s\n", errno,
- strerror(errno));
- goto out;
+ *fd = *((int *) CMSG_DATA(cmsg));
+ printf ("received fd %d\n", *fd);
+ } else
+ *fd = -1;
+ } else {
+ size = read (sock, buf, bufsize);
+ if (size < 0) {
+ perror("read");
+ exit(1);
+ }
}
- printf("Linux sg driver version: %d\n", ver_num);
+ return size;
+}
- if ((sg_fd2 = open(second_fname, O_RDWR)) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- "%s: error opening file: %s", __func__, second_fname);
- perror(ebuff);
- return 1;
- }
- if (verbose)
- fprintf(stderr, "opened second file: %s successfully, fd=%d\n",
- second_fname, sg_fd2);
+static int
+tst_ioctl(const char * fnp, int sg_fd, const char * fn2p, int sg_fd2,
+ int sock, const char * cp)
+{
+ uint32_t cflags;
+ struct sg_extended_info sei;
+ struct sg_extended_info * seip;
seip = &sei;
memset(seip, 0, sizeof(*seip));
@@ -224,6 +230,8 @@ main(int argc, char * argv[])
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_OTHER_OPENS;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_ORPHANS;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_Q_TAIL;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_IS_SHARE;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_IS_MASTER;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_UNSHARE;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_MASTER_FINI;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_MASTER_ERR;
@@ -233,48 +241,54 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
#if 1
- printf("SG_SET_GET_EXTENDED ioctl ok\n");
+ printf("%sSG_SET_GET_EXTENDED ioctl ok\n", cp);
if (SG_SEIM_RESERVED_SIZE & seip->valid_rd_mask)
- printf(" reserved size: %u\n", seip->reserved_sz);
+ printf(" %sreserved size: %u\n", cp, seip->reserved_sz);
if (SG_SEIM_MINOR_INDEX & seip->valid_rd_mask)
- printf(" minor index: %u\n", seip->minor_index);
+ printf(" %sminor index: %u\n", cp, seip->minor_index);
if (SG_SEIM_RQ_REM_THRESH & seip->valid_rd_mask)
- printf(" rq_rem_sgat_thresh: %u\n", seip->rq_rem_sgat_thresh);
+ printf(" %srq_rem_sgat_thresh: %u\n", cp, seip->rq_rem_sgat_thresh);
if (SG_SEIM_TOT_FD_THRESH & seip->valid_rd_mask)
- printf(" tot_fd_thresh: %u\n", seip->tot_fd_thresh);
+ printf(" %stot_fd_thresh: %u\n", cp, seip->tot_fd_thresh);
if ((SG_SEIM_CTL_FLAGS & seip->valid_rd_mask) ||
(SG_SEIM_CTL_FLAGS & seip->valid_wr_mask)) {
cflags = seip->ctl_flags;
if (SG_CTL_FLAGM_TIME_IN_NS & seip->ctl_flags_rd_mask)
- printf(" TIME_IN_NS: %s\n",
+ printf(" %sTIME_IN_NS: %s\n", cp,
(SG_CTL_FLAGM_TIME_IN_NS & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_OTHER_OPENS & seip->ctl_flags_rd_mask)
- printf(" OTHER_OPENS: %s\n",
+ printf(" %sOTHER_OPENS: %s\n", cp,
(SG_CTL_FLAGM_OTHER_OPENS & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_ORPHANS & seip->ctl_flags_rd_mask)
- printf(" ORPHANS: %s\n",
+ printf(" %sORPHANS: %s\n", cp,
(SG_CTL_FLAGM_ORPHANS & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_Q_TAIL & seip->ctl_flags_rd_mask)
- printf(" Q_TAIL: %s\n",
+ printf(" %sQ_TAIL: %s\n", cp,
(SG_CTL_FLAGM_Q_TAIL & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_IS_SHARE & seip->ctl_flags_rd_mask)
+ printf(" %sIS_SHARE: %s\n", cp,
+ (SG_CTL_FLAGM_IS_SHARE & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_IS_MASTER & seip->ctl_flags_rd_mask)
+ printf(" %sIS_MASTER: %s\n", cp,
+ (SG_CTL_FLAGM_IS_MASTER & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_UNSHARE & seip->ctl_flags_rd_mask)
- printf(" UNSHARE: %s\n",
+ printf(" %sUNSHARE: %s\n", cp,
(SG_CTL_FLAGM_UNSHARE & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_MASTER_FINI & seip->ctl_flags_rd_mask)
- printf(" MASTER_FINI: %s\n",
+ printf(" %sMASTER_FINI: %s\n", cp,
(SG_CTL_FLAGM_MASTER_FINI & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_MASTER_ERR & seip->ctl_flags_rd_mask)
- printf(" MASTER_ERR: %s\n",
+ printf(" %sMASTER_ERR: %s\n", cp,
(SG_CTL_FLAGM_MASTER_ERR & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_CHECK_FOR_MORE & seip->ctl_flags_rd_mask)
- printf(" CHECK_FOR_MORE: %s\n",
+ printf(" %sCHECK_FOR_MORE: %s\n", cp,
(SG_CTL_FLAGM_CHECK_FOR_MORE & cflags) ? "true" : "false");
}
if (SG_SEIM_MINOR_INDEX & seip->valid_rd_mask)
- printf(" minor_index: %u\n", seip->minor_index);
+ printf(" %sminor_index: %u\n", cp, seip->minor_index);
printf("\n");
#endif
@@ -285,10 +299,20 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_INT_MASK]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_BOOL_MASK;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
}
- printf("SG_SET_GET_EXTENDED ioctl ok\n");
- printf(" read_value[SG_SEIRV_INT_MASK]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_BOOL_MASK]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -297,9 +321,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_VERS_NUM]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_VERS_NUM]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -308,9 +332,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_FL_RQS]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_FL_RQS]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -319,9 +343,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_DEV_FL_RQS]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_DEV_FL_RQS]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -330,9 +354,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_TRC_SZ]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_TRC_SZ]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -341,9 +365,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_TRC_MAX_SZ]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_TRC_MAX_SZ]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_SHARE_FD;
@@ -353,10 +377,15 @@ main(int argc, char * argv[])
#else
seip->share_fd = sg_fd;
#endif
- if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0)
- pr2serr("ioctl(SG_SET_GET_EXTENDED) shared_fd=%d, failed errno=%d "
- "%s\n", sg_fd2, errno, strerror(errno));
- printf(" read back shared_fd= %u\n", seip->share_fd);
+ if (do_fork && is_parent)
+ goto bypass_share;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("%sioctl(SG_SET_GET_EXTENDED) shared_fd=%d, failed errno=%d "
+ "%s\n", cp, sg_fd2, errno, strerror(errno));
+ }
+ printf(" %sshare successful, read back shared_fd= %d\n", cp,
+ (int)seip->share_fd);
+bypass_share:
// printf("SG_IOSUBMIT value=0x%lx\n", SG_IOSUBMIT);
// printf("SG_IORECEIVE value=0x%lx\n", SG_IORECEIVE);
@@ -364,17 +393,203 @@ main(int argc, char * argv[])
pr2serr("ioctl(SG_GET_TRANSFORM) fail expected, errno=%d %s\n",
errno, strerror(errno));
else
- printf("SG_GET_TRANSFORM okay (does nothing)\n");
+ printf("%sSG_GET_TRANSFORM okay (does nothing)\n", cp);
if (ioctl(sg_fd, SG_SET_TRANSFORM, NULL) < 0)
pr2serr("ioctl(SG_SET_TRANSFORM) fail expected, errno=%d %s\n",
errno, strerror(errno));
else
- printf("SG_SET_TRANSFORM okay (does nothing)\n");
+ printf("%sSG_SET_TRANSFORM okay (does nothing)\n", cp);
printf("\n");
+ /* test sending a sg file descriptor between 2 processes using UNIX
+ * sockets */
+ if (do_fork && is_parent && fnp && (sock >= 0)) { /* master/READ side */
+ int res;
+ int fd_ma = open(fnp, O_RDWR);
+
+ if (fd_ma < 0) {
+ pr2serr("%s: opening %s failed: %s\n", __func__, fnp,
+ strerror(errno));
+ return 1;
+ }
+ res = sock_fd_write(sock, "boo", 4, fd_ma);
+ if (res < 0)
+ pr2serr("%s: sock_fd_write() failed\n", __func__);
+ else
+ printf("%s: sock_fd_write() returned: %d\n", __func__, res);
+ } else if (do_fork && !is_parent && fn2p && (sock >= 0)) {
+ int res, fd_ma;
+ /* int fd_sl = open(fn2p, O_RDWR); not needed */
+ uint8_t b[32];
+
+ fd_ma = -1;
+ res = sock_fd_read(sock, b, sizeof(b), &fd_ma);
+ if (res < 0)
+ pr2serr("%s: sock_fd_read() failed\n", __func__);
+ else
+ printf("%s: sock_fd_read() returned: %d, fd_ma=%d\n", __func__,
+ res, fd_ma);
+ /* yes it works! */
+ }
+ return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool done;
+ int sg_fd, k, ok, ver_num, pack_id, num_waiting, access_count;
+ int sg_fd2 = -1;
+ int sock = -1;
+ uint8_t inq_cdb[INQ_CMD_LEN] =
+ {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+ uint8_t sdiag_cdb[SDIAG_CMD_LEN] =
+ {0x1d, 0, 0, 0, 0, 0};
+ uint8_t inqBuff[MAX_Q_LEN][INQ_REPLY_LEN];
+ sg_io_hdr_t io_hdr[MAX_Q_LEN];
+ sg_io_hdr_t rio_hdr;
+ char * file_name = 0;
+ char ebuff[EBUFF_SZ];
+ uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
+ const char * second_fname = NULL;
+ const char * cp;
+ struct sg_scsi_id ssi;
+
+ for (k = 1; k < argc; ++k) {
+ if (0 == memcmp("-f", argv[k], 2))
+ do_fork = true;
+ else if (0 == memcmp("-h", argv[k], 2)) {
+ file_name = 0;
+ break;
+ } else if (0 == memcmp("-l=", argv[k], 3)) {
+ q_len = atoi(argv[k] + 3);
+ if ((q_len > 511) || (q_len < 1)) {
+ printf("Expect -l= to take a number (q length) between 1 "
+ "and 511\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-o", argv[k], 2))
+ ioctl_only = true;
+ else if (0 == memcmp("-r=", argv[k], 3)) {
+ reserve_buff_sz = atoi(argv[k] + 3);
+ if (reserve_buff_sz < 0) {
+ printf("Expect -r= to take a number 0 or higher\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-s=", argv[k], 3)) {
+ sleep_secs = atoi(argv[k] + 3);
+ if (sleep_secs < 0) {
+ printf("Expect -s= to take a number 0 or higher\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-t", argv[k], 2))
+ q_at_tail = true;
+ else if (0 == memcmp("-vvvv", argv[k], 5))
+ verbose += 4;
+ else if (0 == memcmp("-vvv", argv[k], 4))
+ verbose += 3;
+ else if (0 == memcmp("-vv", argv[k], 3))
+ verbose += 2;
+ else if (0 == memcmp("-v", argv[k], 2))
+ verbose += 1;
+ else if (0 == memcmp("-V", argv[k], 2)) {
+ printf("%s\n", version_str);
+ file_name = 0;
+ break;
+ } else if (0 == memcmp("-w", argv[k], 2))
+ write_only = true;
+ else if (*argv[k] == '-') {
+ printf("Unrecognized switch: %s\n", argv[k]);
+ file_name = 0;
+ break;
+ }
+ else if (0 == file_name)
+ file_name = argv[k];
+ else if (NULL == second_fname)
+ second_fname = argv[k];
+ else {
+ printf("too many arguments\n");
+ file_name = 0;
+ break;
+ }
+ }
+ if (0 == file_name) {
+ printf("No filename (sg device) given\n\n");
+ usage();
+ return 1;
+ }
+
+ /* 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);
+ perror(ebuff);
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, "opened given file: %s successfully, fd=%d\n",
+ file_name, sg_fd);
+
+ if (ioctl(sg_fd, SG_GET_VERSION_NUM, &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", ver_num);
+
+ if (second_fname) {
+ if ((sg_fd2 = open(second_fname, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "%s: error opening file: %s", __func__, second_fname);
+ perror(ebuff);
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, "opened second file: %s successfully, fd=%d\n",
+ second_fname, sg_fd2);
+ }
+
+ if (do_fork) {
+ int pid;
+ int sv[2];
+
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+ printf("socketpair: sv[0]=%d, sv[1]=%d sg_fd=%d\n", sv[0], sv[1],
+ sg_fd);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ goto out;
+ } else if (0 == pid) {
+ relative_cp = "child ";
+ is_parent = false;
+ close(sv[0]);
+ sock = sv[1];
+ } else {
+ relative_cp = "parent ";
+ is_parent = true;
+ childs_pid = pid;
+ close(sv[1]);
+ sock = sv[0];
+ }
+ }
+
+ cp = do_fork ? relative_cp : "";
+ if (tst_ioctl(file_name, sg_fd, second_fname, sg_fd2, sock, cp))
+ goto out;
if (ioctl_only)
goto out;
+ if (do_fork && !is_parent)
+ return 0;
+
printf("start write() calls\n");
for (k = 0; k < q_len; ++k) {
/* Prepare INQUIRY command */
@@ -405,7 +620,7 @@ main(int argc, char * argv[])
/* io_hdr[k].usr_ptr = NULL; */
if (write(sg_fd, &io_hdr[k], sizeof(sg_io_hdr_t)) < 0) {
- perror("sg_queue_tst: sg write error");
+ pr2serr("%ssg write errno=%d [%s]\n", cp, errno, strerror(errno));
close(sg_fd);
return 1;
}
@@ -440,6 +655,8 @@ main(int argc, char * argv[])
if (write_only)
goto out;
+ if (do_fork)
+ printf("\n\nFollowing starting with get_pack_id are all CHILD\n");
if (ioctl(sg_fd, SG_GET_PACK_ID, &pack_id) < 0)
pr2serr("ioctl(SG_GET_PACK_ID) failed, errno=%d %s\n",
errno, strerror(errno));
@@ -475,7 +692,7 @@ main(int argc, char * argv[])
memset(&rio_hdr, 0, sizeof(sg_io_hdr_t));
rio_hdr.interface_id = 'S';
if (read(sg_fd, &rio_hdr, sizeof(sg_io_hdr_t)) < 0) {
- perror("sg_queue_tst: sg read error");
+ perror("sg read error");
close(sg_fd);
return 1;
}
diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c
index 16aab4fa..3d1a1076 100644
--- a/testing/sgs_dd.c
+++ b/testing/sgs_dd.c
@@ -11,7 +11,7 @@
*
* This program is a specialisation of the Unix "dd" command in which
* one or both of the given files is a scsi generic device.
- * A block size ('bs') is assumed to be 512 if not given. This
+ * A logical block size ('bs') is assumed to be 512 if not given. This
* program complains if 'ibs' or 'obs' are given with some other value
* than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
* 'of' is not given or 'of=-' then stdout assumed.
@@ -88,7 +88,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "1.05 20181213";
+static const char * version_str = "1.06 20181215";
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wclobbered"
@@ -483,7 +483,8 @@ usage(int pg_num)
"[time=0|1]\n"
" [verbose=VERB] [--dry-run] [--verbose]\n\n"
" where the main options (shown in first group above) are:\n"
- " bs must be device block size (default 512)\n"
+ " bs must be device logical block size (default "
+ "512)\n"
" count number of blocks to copy (def: device size)\n"
" if file or device to read from (def: stdin)\n"
" iflag comma separated list from: [2fds,coe,defres,dio,"
@@ -846,6 +847,11 @@ read_write_thread(void * v_tip)
rep->wr = true;
status = pthread_mutex_lock(&clp->out_mutex);
if (0 != status) err_exit(status, "lock out_mutex");
+
+ /* Make sure the OFILE (+ OFILE2) are in same sequence as IFILE */
+ if ((rep->out2fd < 0) && (FT_SG == clp->in_type) &&
+ (FT_SG == clp->out_type))
+ goto skip_force_out_sequence;
if (share_and_of2 || (FT_DEV_NULL != clp->out_type)) {
while ((! clp->out_stop) &&
((rep->blk + seek_skip) != clp->out_blk)) {
@@ -856,6 +862,7 @@ read_write_thread(void * v_tip)
pthread_cleanup_pop(0);
}
}
+skip_force_out_sequence:
if (clp->out_stop || (clp->out_count <= 0)) {
if (! clp->out_stop)
@@ -1875,7 +1882,8 @@ main(int argc, char * argv[])
}
if (clp->bs <= 0) {
clp->bs = DEF_BLOCK_SIZE;
- pr2serr("Assume default 'bs' (block size) of %d bytes\n", clp->bs);
+ pr2serr("Assume default 'bs' ((logical) block size) of %d bytes\n",
+ clp->bs);
}
if ((ibs && (ibs != clp->bs)) || (obs && (obs != clp->bs))) {
pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
@@ -2103,8 +2111,8 @@ main(int argc, char * argv[])
in_num_sect = -1;
}
if (clp->bs != in_sect_sz) {
- pr2serr("block size on %s confusion; bs=%d, from device=%d\n",
- inf, clp->bs, in_sect_sz);
+ pr2serr("logical block size on %s confusion; bs=%d, from "
+ "device=%d\n", inf, clp->bs, in_sect_sz);
in_num_sect = -1;
}
}
@@ -2135,8 +2143,8 @@ main(int argc, char * argv[])
out_num_sect = -1;
}
if (clp->bs != out_sect_sz) {
- pr2serr("block size on %s confusion: bs=%d, from device=%d\n",
- outf, clp->bs, out_sect_sz);
+ pr2serr("logical block size on %s confusion: bs=%d, from "
+ "device=%d\n", outf, clp->bs, out_sect_sz);
out_num_sect = -1;
}
}
diff --git a/testing/uapi_sg.h b/testing/uapi_sg.h
index 4985fd9f..4d3ccba6 100644
--- a/testing/uapi_sg.h
+++ b/testing/uapi_sg.h
@@ -13,7 +13,7 @@
* Version 2 and 3 extensions to driver:
* Copyright (C) 1998 - 2018 Douglas Gilbert
*
- * Version: 3.9.02 (20181203)
+ * Version: 4.0.02 (20181216)
* This version is for Linux 2.6, 3 and 4 series kernels.
*
* Documentation
@@ -105,11 +105,10 @@ typedef struct sg_io_hdr {
*/
#define SGV4_FLAG_DIRECT_IO SG_FLAG_DIRECT_IO
#define SGV4_FLAG_MMAP_IO SG_FLAG_MMAP_IO
-#define SGV4_FLAG_YIELD_TAG 0x8 /* sg_io_v4::request_attr set after SG_IOS */
+#define SGV4_FLAG_YIELD_TAG 0x8 /* sg_io_v4::request_tag set after SG_IOS */
#define SGV4_FLAG_Q_AT_TAIL SG_FLAG_Q_AT_TAIL
#define SGV4_FLAG_Q_AT_HEAD SG_FLAG_Q_AT_HEAD
#define SGV4_FLAG_FIND_BY_TAG 0x100 /* in SG_IOR, def: find by pack_id */
-/* Flag value 0x200 not currently used */
#define SGV4_FLAG_IMMED 0x400 /* for polling with SG_IOR else ignored */
/* Flag value 0x800 not currently used */
#define SGV4_FLAG_DEV_SCOPE 0x1000 /* permit SG_IOABORT to have wider scope */
@@ -124,7 +123,7 @@ typedef struct sg_io_hdr {
#define SG_INFO_DIRECT_IO_MASK 0x6
#define SG_INFO_INDIRECT_IO 0x0 /* data xfer via kernel buffers (or no xfer) */
#define SG_INFO_DIRECT_IO 0x2 /* direct IO requested and performed */
-#define SG_INFO_MIXED_IO 0x4 /* part direct, part indirect IO */
+#define SG_INFO_MIXED_IO 0x4 /* not used, always 0 */
#define SG_INFO_DEVICE_DETACHING 0x8 /* completed successfully but ... */
#define SG_INFO_ANOTHER_WAITING 0x10 /* needs SG_CTL_FLAGM_CHECK_FOR_MORE */
@@ -194,7 +193,7 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
#define SG_SEIRV_BOOL_MASK 0x1 /* get SG_CTL_FLAGM_ALL_BITS */
#define SG_SEIRV_VERS_NUM 0x2 /* get driver version number as int */
#define SG_SEIRV_FL_RQS 0x3 /* number of requests in free list */
-#define SG_SEIRV_DEV_FL_RQS 0x4 /* sum of rqs on all fds on this dev */
+#define SG_SEIRV_DEV_FL_RQS 0x4 /* sum(fl rqs) on all of dev's fds */
#define SG_SEIRV_TRC_SZ 0x5 /* current size of trace buffer */
#define SG_SEIRV_TRC_MAX_SZ 0x6 /* maximum size of trace buffer */
@@ -413,11 +412,18 @@ struct sg_header {
* the sg_v3 interface (structure defined above) and the sg_v4 interface
* (structure defined in <include/uapi/linux/bsg.h> ). Following "read" and
* "write" terms are from the driver's POV, the _IO macros from users' POV.
+ *
+ * SG_IOSUBMIT and SG_IORECEIVE can take a pointer to a v4 or v3 interface
+ * which have different lengths (v4 is longer) but their definitions below
+ * encode v4's length into the ioctl number. So users of the v3 interface
+ * would be best to place a v3 structure inside (overwriting and at the start
+ * of) a v4 structure and pass a pointer to that v4 structure to the ioctl.
*/
/* via pointer reads sg v3 or v4 object, optionally writes tag, so _IOWR */
#define SG_IOSUBMIT _IOWR(SG_IOCTL_MAGIC_NUM, 0x41, struct sg_io_v4)
/* via pointer optionally reads tag, writes sg v3 or v4 object, so _IOWR */
#define SG_IORECEIVE _IOWR(SG_IOCTL_MAGIC_NUM, 0x42, struct sg_io_v4)
+
/* via pointer reads v4 object (including tag), writes nothing, so _IOW */
#define SG_IOABORT _IOW(SG_IOCTL_MAGIC_NUM, 0x43, struct sg_io_v4)