aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--doc/sg_xcopy.849
-rw-r--r--src/sg_vpd.c88
-rw-r--r--src/sg_xcopy.c17
-rw-r--r--testing/sg_tst_bidi.c176
-rw-r--r--testing/sg_tst_ioctl.c9
6 files changed, 249 insertions, 94 deletions
diff --git a/ChangeLog b/ChangeLog
index c67ea78f..c36443da 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 [20190116] [svn: r807]
+Changelog for sg3_utils-1.45 [20190123] [svn: r808]
- sg_ses: bug: --page= being overridden when --control
and --data= also given; fix
- sg_opcodes: expand MLU (spc5r20)
@@ -11,6 +11,8 @@ Changelog for sg3_utils-1.45 [20190116] [svn: r807]
- sg_format: add --dcrt used twice (FOV=1 DCRT=0)
- sg_raw: fix --send bug when using stdin
- sg_vpd: 3pc VPD page add copy group descriptor
+ - sg_xcopy: add --fco (fast copy only) (spc5r20)
+ - implement --app=1 (append) on regular OFILE type
- sg_scan (win32): expand limits for big arrays
- sg_lib: add sg_t10_uuid_desig2str()
- tweak sg_pt interface to better handle bidi
diff --git a/doc/sg_xcopy.8 b/doc/sg_xcopy.8
index 437bad08..ccd470ec 100644
--- a/doc/sg_xcopy.8
+++ b/doc/sg_xcopy.8
@@ -1,4 +1,4 @@
-.TH SG_XCOPY "8" "August 2018" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_XCOPY "8" "January 2019" "sg3_utils\-1.45" SG3_UTILS
.SH NAME
sg_xcopy \- copy data to and from files and devices using SCSI EXTENDED
COPY (XCOPY)
@@ -9,7 +9,7 @@ COPY (XCOPY)
[\fIoflag=FLAGS\fR] [\fIseek=SEEK\fR] [\fIskip=SKIP\fR] [\fI\-\-help\fR]
[\fI\-\-version\fR]
.PP
-[\fIbpt=BPT\fR] [\fIcat=\fR0|1] [\fIdc=\fR0|1]
+[\fIapp=\fR0|1] [\fIbpt=BPT\fR] [\fIcat=\fR0|1] [\fIdc=\fR0|1] [\fIfco=\fR0|1]
[\fIid_usage=\fR{hold|discard|disable}] [\fIlist_id=ID\fR] [\fIprio=PRIO\fR]
[\fItime=\fR0|1] [\fIverbose=VERB\fR] [\fI\-\-on_dst|\-\-on_src\fR]
[\fI\-\-verbose\fR]
@@ -19,14 +19,6 @@ COPY (XCOPY)
Copy data to and from any files. Specialized for "files" that are Linux SCSI
devices that support the SCSI EXTENDED COPY (XCOPY) command.
.PP
-During the draft stages of SPC\-4 the T10 committee has expanded the XCOPY
-command so that it now has two variants: "LID1" (for a List Identifier
-length of 1 byte) and "LID4" (for a List Identifier length of 4 bytes).
-This utility supports the older, LID1 variant which is also found in SPC\-3
-and earlier. While the LID1 variant in SPC\-4 is command level (binary)
-compatible with XCOPY as defined in SPC\-3, some of the command naming has
-changed. This utility uses the older, SPC\-3 XCOPY names.
-.PP
This utility
has similar syntax and semantics to
.B dd(1)
@@ -42,12 +34,26 @@ with the \fI\-\-on_src\fR or \fIiflag=xflag\fR options which cause the XCOPY
command to be sent to \fIIFILE\fR instead. Also see the section on
ENVIRONMENT VARIABLES.
.PP
+In the SPC\-4 standard the T10 committee has expanded the XCOPY command so
+that it now has two variants: "LID1" (for a List Identifier length of 1 byte)
+and "LID4" (for a List Identifier length of 4 bytes). This utility supports
+the older, LID1 variant which is also found in SPC\-3 and earlier. While the
+LID1 variant in SPC\-4 is command level (binary) compatible with XCOPY as
+defined in SPC\-3, some of the command naming has changed. This utility uses
+the older, SPC\-3 XCOPY names.
+.PP
The ddpt utility supports the same xcopy(LID1) functionality as this utility
with the same options and flags. Additionally ddpt supports a subset of
xcopy(LID4) functionality variously called "xcopy version 2, lite" or ODX.
ODX is a market name and stands for Offloaded Data Xfer (i.e. transfer).
.SH OPTIONS
.TP
+\fBapp\fR={0|1}
+if 1 start the destination of the copy at the end of OFILE. This assumes
+that OFILE is a regular file. The default is 0 in which case the destination
+of the copy starts at the beginning of OFILE (possibly offset be SEEK). This
+option cannot be used with the \fIseek=SEEK\fR option.
+.TP
\fBbpt\fR=\fIBPT\fR
each IO transaction will be made using \fIBPT\fR blocks (or less if near
the end of the copy). Default is 128 for logical block sizes less that 2048
@@ -73,17 +79,9 @@ the handling of residual data. See section
.B HANDLING OF RESIDUAL DATA
for details.
.TP
-\fBdc\fR={0|1}
-sets the SCSI EXTENDED COPY command segment descriptor DC bit to 0 or
-1 (default: 0). The DC bit controls whether \fICOUNT\fR
-refers to the source (\fIdc=0\fR) or the target (\fIdc=1\fR) descriptor.
-.TP
\fBconv\fR=\fBCONV\fR
all \fBCONV\fR arguments are ignored.
.TP
-\fBapp\fR=\fBAPPEND\fR
-all \fBAPPEND\fR arguments are ignored.
-.TP
\fBcount\fR=\fICOUNT\fR
copy \fICOUNT\fR blocks from \fIIFILE\fR to \fIOFILE\fR. Default is the
minimum (\fIIFILE\fR if \fIdc=0\fR or \fIOFILE\fR if \fIdc=1\fR)
@@ -98,6 +96,19 @@ rather than the size of the whole device is used. If \fICOUNT\fR is
not given (or \fIcount=\-1\fR) and cannot be derived then an error
message is issued and no copy takes place.
.TP
+\fBdc\fR={0|1}
+sets the SCSI EXTENDED COPY command segment descriptor DC bit to 0 or
+1 (default: 0). The DC bit controls whether \fICOUNT\fR
+refers to the source (\fIdc=0\fR) or the target (\fIdc=1\fR) descriptor.
+.TP
+\fBfco\fR={0|1}
+sets the SCSI EXTENDED COPY command segment descriptor FCO bit to 0 or
+1 (default: 0). The Fast Copy Only (FCO) bit set will result in the
+copy being done but a technique faster than SCSI READ and WRITE commands.
+If the copy cannot but done in a faster manner then a sense key of "Copy
+aborted" with and additional sense of "Fast copy not possible" is
+returned.
+.TP
\fBibs\fR=\fIBS\fR
if given must be the same as \fIBS\fR given to 'bs=' option.
.TP
@@ -343,7 +354,7 @@ Written by Hannes Reinecke and Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2000\-2018 Hannes Reinecke and Douglas Gilbert
+Copyright \(co 2000\-2019 Hannes Reinecke and Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/src/sg_vpd.c b/src/sg_vpd.c
index 04d5110d..ed55d90f 100644
--- a/src/sg_vpd.c
+++ b/src/sg_vpd.c
@@ -40,7 +40,7 @@
*/
-static const char * version_str = "1.49 20190109"; /* spc5r20 + sbc4r15 */
+static const char * version_str = "1.50 20190119"; /* spc5r20 + sbc4r15 */
/* standard VPD pages, in ascending page number order */
#define VPD_SUPPORTED_VPDS 0x0
@@ -152,9 +152,10 @@ static int svpd_decode_t10(int sg_fd, struct opts_t * op, int subvalue,
static int svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue,
int off);
-static int decode_dev_ids(const char * print_if_found, uint8_t * buff,
- int len, int m_assoc, int m_desig_type,
- int m_code_set, const struct opts_t * op);
+static int decode_dev_ids(const char * print_if_found, int num_leading,
+ uint8_t * buff, int len, int m_assoc,
+ int m_desig_type, int m_code_set,
+ const struct opts_t * op);
uint8_t * rsp_buff;
const int rsp_buff_sz = MX_ALLOC_LEN + 2;
@@ -746,23 +747,23 @@ decode_id_vpd(uint8_t * buff, int len, int subvalue,
m_d = -1;
m_cs = -1;
if (0 == subvalue) {
- decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), b, blen,
+ decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
VPD_ASSOC_LU, m_d, m_cs, op);
- decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), b, blen,
+ decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b, blen,
VPD_ASSOC_TPORT, m_d, m_cs, op);
- decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), b, blen,
+ decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b, blen,
VPD_ASSOC_TDEVICE, m_d, m_cs, op);
} else if (VPD_DI_SEL_AS_IS == subvalue)
- decode_dev_ids(NULL, b, blen, m_a, m_d, m_cs, op);
+ decode_dev_ids(NULL, 0, b, blen, m_a, m_d, m_cs, op);
else {
if (VPD_DI_SEL_LU & subvalue)
- decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), b, blen,
+ decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
VPD_ASSOC_LU, m_d, m_cs, op);
if (VPD_DI_SEL_TPORT & subvalue)
- decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), b, blen,
- VPD_ASSOC_TPORT, m_d, m_cs, op);
+ decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b,
+ blen, VPD_ASSOC_TPORT, m_d, m_cs, op);
if (VPD_DI_SEL_TARGET & subvalue)
- decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE),
+ decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0,
b, blen, VPD_ASSOC_TDEVICE, m_d, m_cs, op);
}
}
@@ -925,8 +926,8 @@ decode_scsi_ports_vpd(uint8_t * buff, int len, const struct opts_t * op)
} else {
if ((0 == op->do_quiet) || (ip_tid_len > 0))
printf(" Target port descriptor(s):\n");
- decode_dev_ids("SCSI Ports", bp + bump + 4, tpd_len,
- VPD_ASSOC_TPORT, -1, -1, op);
+ decode_dev_ids("", 2 /* leading spaces */, bp + bump + 4,
+ tpd_len, VPD_ASSOC_TPORT, -1, -1, op);
}
}
bump += tpd_len + 4;
@@ -1133,18 +1134,25 @@ decode_dev_ids_quiet(uint8_t * buff, int len, int m_assoc,
/* Prints outs designation descriptors (dd_s)selected by association,
designator type and/or code set. */
static int
-decode_dev_ids(const char * print_if_found, uint8_t * buff, int len,
- int m_assoc, int m_desig_type, int m_code_set,
+decode_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff,
+ int len, int m_assoc, int m_desig_type, int m_code_set,
const struct opts_t * op)
{
int assoc, off, u, i_len;
bool printed;
const uint8_t * bp;
char b[1024];
+ char sp[82];
if (op->do_quiet)
return decode_dev_ids_quiet(buff, len, m_assoc, m_desig_type,
m_code_set);
+ if (num_leading > (int)(sizeof(sp) - 2))
+ num_leading = sizeof(sp) - 2;
+ if (num_leading > 0)
+ snprintf(sp, sizeof(sp), "%*c", num_leading, ' ');
+ else
+ sp[0] = '\0';
if (buff[2] != 0) { /* all valid dd_s should have 0 in this byte */
if (op->verbose)
pr2serr("%s: designation descriptors byte 2 should be 0\n"
@@ -1166,11 +1174,12 @@ decode_dev_ids(const char * print_if_found, uint8_t * buff, int len,
assoc = ((bp[1] >> 4) & 0x3);
if (print_if_found && (! printed)) {
printed = true;
- printf(" %s:\n", print_if_found);
+ if (strlen(print_if_found) > 0)
+ printf(" %s:\n", print_if_found);
}
if (NULL == print_if_found)
- printf(" %s:\n", sg_get_desig_assoc_str(assoc));
- sg_get_designation_descriptor_str("", bp, i_len + 4, false,
+ printf(" %s%s:\n", sp, sg_get_desig_assoc_str(assoc));
+ sg_get_designation_descriptor_str(sp, bp, i_len + 4, false,
op->do_long, sizeof(b), b);
printf("%s", b);
}
@@ -1603,6 +1612,7 @@ decode_rod_descriptor(const uint8_t * buff, int len)
{
const uint8_t * bp = buff;
int k, bump;
+ uint64_t ul;
for (k = 0; k < len; k += bump, bp += bump) {
bump = sg_get_unaligned_be16(bp + 2) + 4;
@@ -1613,26 +1623,46 @@ decode_rod_descriptor(const uint8_t * buff, int len)
sg_get_unaligned_be16(bp + 6));
printf(" Maximum Bytes in block ROD: %" PRIu64 "\n",
sg_get_unaligned_be64(bp + 8));
- printf(" Optimal Bytes in block ROD transfer: %" PRIu64 "\n",
- sg_get_unaligned_be64(bp + 16));
- printf(" Optimal Bytes to token per segment: %" PRIu64 "\n",
- sg_get_unaligned_be64(bp + 24));
- printf(" Optimal Bytes from token per segment:"
- " %" PRIu64 "\n", sg_get_unaligned_be64(bp + 32));
+ ul = sg_get_unaligned_be64(bp + 16);
+ printf(" Optimal Bytes in block ROD transfer: ");
+ if (SG_LIB_UNBOUNDED_64BIT == ul)
+ printf("-1 [no limit]\n");
+ else
+ printf("%" PRIu64 "\n", ul);
+ ul = sg_get_unaligned_be64(bp + 24);
+ printf(" Optimal Bytes to token per segment: ");
+ if (SG_LIB_UNBOUNDED_64BIT == ul)
+ printf("-1 [no limit]\n");
+ else
+ printf("%" PRIu64 "\n", ul);
+ ul = sg_get_unaligned_be64(bp + 32);
+ printf(" Optimal Bytes from token per segment: ");
+ if (SG_LIB_UNBOUNDED_64BIT == ul)
+ printf("-1 [no limit]\n");
+ else
+ printf("%" PRIu64 "\n", ul);
break;
case 1:
/* Stream ROD device type specific descriptor */
printf(" Maximum Bytes in stream ROD: %" PRIu64 "\n",
sg_get_unaligned_be64(bp + 8));
- printf(" Optimal Bytes in stream ROD transfer:"
- " %" PRIu64 "\n", sg_get_unaligned_be64(bp + 16));
+ ul = sg_get_unaligned_be64(bp + 16);
+ printf(" Optimal Bytes in stream ROD transfer: ");
+ if (SG_LIB_UNBOUNDED_64BIT == ul)
+ printf("-1 [no limit]\n");
+ else
+ printf("%" PRIu64 "\n", ul);
break;
case 3:
/* Copy manager ROD device type specific descriptor */
printf(" Maximum Bytes in processor ROD: %" PRIu64 "\n",
sg_get_unaligned_be64(bp + 8));
- printf(" Optimal Bytes in processor ROD transfer:"
- " %" PRIu64 "\n", sg_get_unaligned_be64(bp + 16));
+ ul = sg_get_unaligned_be64(bp + 16);
+ printf(" Optimal Bytes in processor ROD transfer: ");
+ if (SG_LIB_UNBOUNDED_64BIT == ul)
+ printf("-1 [no limit]\n");
+ else
+ printf("%" PRIu64 "\n", ul);
break;
default:
printf(" Unhandled descriptor (format %d, device type %d)\n",
diff --git a/src/sg_xcopy.c b/src/sg_xcopy.c
index 6bfd1dba..c0b53a88 100644
--- a/src/sg_xcopy.c
+++ b/src/sg_xcopy.c
@@ -1,7 +1,7 @@
/* A utility program for copying files. Similar to 'dd' but using
* the 'Extended Copy' command.
*
- * Copyright (c) 2011-2018 Hannes Reinecke, SUSE Labs
+ * Copyright (c) 2011-2019 Hannes Reinecke, SUSE Labs
*
* Largely taken from 'sg_dd', which has the
*
@@ -69,7 +69,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "0.68 20180811";
+static const char * version_str = "0.69 20190120";
#define ME "sg_xcopy: "
@@ -172,6 +172,7 @@ static bool do_time = false;
static bool start_tm_valid = false;
static bool xcopy_flag_cat = false;
static bool xcopy_flag_dc = false;
+static bool xcopy_flag_fco = false; /* fast copy only, spc5r20 */
static int blk_sz = 0;
static int list_id_usage = -1;
static int priority = 1;
@@ -533,6 +534,7 @@ primary_help:
" conv ignored\n"
" count number of blocks to copy (def: device size)\n"
" dc xcopy segment descriptor DC bit (default: 0)\n"
+ " fco xcopy segment descriptor FCO bit (default: 0)\n"
" ibs input block size (if given must be same as "
"'bs=')\n"
" id_usage sets list_id_usage field to hold (0), "
@@ -596,6 +598,8 @@ scsi_encode_seg_desc(uint8_t *seg_desc, int seg_desc_type,
seg_desc[1] |= 0x1;
if (xcopy_flag_dc)
seg_desc[1] |= 0x2;
+ if (xcopy_flag_fco)
+ seg_desc[1] |= 0x4;
if (seg_desc_type == 0x02) {
seg_desc_len = 0x18;
seg_desc[4] = 0;
@@ -1260,6 +1264,8 @@ open_of(struct xcopy_fp_t * ofp, int vb)
flags = O_RDWR | O_NONBLOCK;
if (ofp->excl)
flags |= O_EXCL;
+ if (ofp->append)
+ flags |= O_APPEND;
if ((outfd = open(ofp->fname, flags)) < 0) {
err = errno;
snprintf(ebuff, EBUFF_SZ,
@@ -1409,6 +1415,13 @@ main(int argc, char * argv[])
return SG_LIB_SYNTAX_ERROR;
}
xcopy_flag_dc = !! n;
+ } else if (0 == strcmp(key, "fco")) {
+ n = sg_get_num(buf);
+ if (n < 0 || n > 1) {
+ pr2serr(ME "bad argument to 'fco='\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ xcopy_flag_fco = !! n;
} else if (0 == strcmp(key, "ibs")) {
ibs = sg_get_num(buf);
} else if (strcmp(key, "if") == 0) {
diff --git a/testing/sg_tst_bidi.c b/testing/sg_tst_bidi.c
index a0b14588..fd5d00c0 100644
--- a/testing/sg_tst_bidi.c
+++ b/testing/sg_tst_bidi.c
@@ -53,7 +53,7 @@
is implemented by the scsi_debug driver is used. */
-static const char * version_str = "Version: 1.02 20190115";
+static const char * version_str = "Version: 1.03 20190122";
#define INQ_REPLY_LEN 96
#define INQ_CMD_OP 0x12
@@ -90,8 +90,9 @@ usage(void)
{
printf("Usage: sg_tst_bidi [-b=LB_SZ] [-d=DIO_BLKS] [-D] [-h] -l=LBA [-N] "
"[-q=Q_LEN]\n"
- " [-Q] [-r=SZ] [-s=SEC] [-t] [-v] [-V] [-w] "
- "<sg_device>\n"
+ " [-Q] [-r=SZ] [-R=RC] [-s=SEC] [-t] [-v] [-V] "
+ "[-w]\n"
+ " <sg_or_bsg_device>\n"
" where:\n"
" -b=LB_SZ logical block size (def: 512 bytes)\n"
" -d=DIO_BLKS data in and out length (unit: logical "
@@ -102,17 +103,23 @@ usage(void)
" -l=LBA logical block address (LDA) of first modded "
"block\n"
" -N durations in nanoseconds (def: milliseconds)\n"
- " -q=Q_LEN queue length, between 1 and 511 (def: 16)\n"
+ " -q=Q_LEN queue length, between 1 and 511 (def: 16). "
+ " Calls\n"
+ " ioctl(SG_IO) when -q=1 else SG_IOSUBMIT "
+ "(async)\n"
" -Q quiet, suppress usual output\n"
" -r=SZ reserve buffer size in KB (def: 256 --> 256 "
"KB)\n"
+ " -R=RC repetition count (def: 0)\n"
" -s=SEC sleep between writes and reads (def: 0)\n"
" -t queue_at_tail (def: q_at_head)\n"
" -v increase verbosity of output\n"
" -V print version string then exit\n"
" -w sets DISABLE WRITE bit on cdb to 0 (def: 1)\n\n"
"Warning: this test utility writes to location LBA and Q_LEN "
- "following\nblocks using the XDWRITEREAD(10) SBC-3 command\n");
+ "following\nblocks using the XDWRITEREAD(10) SBC-3 command. "
+ "When -q=1 does\nioctl(SG_IO) and that is only case when a "
+ "bsg device can be given.\n");
}
static int
@@ -153,9 +160,10 @@ main(int argc, char * argv[])
bool nanosecs = false;
bool quiet = false;
bool disable_write = true;
- int k, j, ok, ver_num, pack_id, num_waiting, access_count, din_len;
+ int k, j, ok, ver_num, pack_id, num_waiting, din_len;
int dout_len, cat;
int ret = 0;
+ int rep_count = 0;
int sg_fd = -1;
int lb_sz = 512;
int dio_blks = 1;
@@ -175,7 +183,6 @@ main(int argc, char * argv[])
uint8_t * free_doutp = NULL;
char ebuff[EBUFF_SZ];
uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
- struct sg_scsi_id ssi;
for (k = 1; k < argc; ++k) {
if (0 == memcmp("-b=", argv[k], 3)) {
@@ -230,6 +237,13 @@ main(int argc, char * argv[])
file_name = 0;
break;
}
+ } else if (0 == memcmp("-R=", argv[k], 3)) {
+ rep_count = atoi(argv[k] + 3);
+ if (rep_count < 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) {
@@ -312,14 +326,99 @@ main(int argc, char * argv[])
strerror(errno));
goto out;
}
- printf("Linux sg driver version: %d\n", ver_num);
- if (ext_ioctl(sg_fd, nanosecs))
+ if (! quiet)
+ printf("Linux sg driver version: %d\n", ver_num);
+ if ((q_len > 1) && ext_ioctl(sg_fd, nanosecs))
goto out;
- printf("start write() calls\n");
+
+ if (1 == q_len) { /* do sync ioct(SG_IO) */
+ io_v4p = &io_v4[k];
+rep_sg_io:
+ memset(io_v4p, 0, sizeof(*io_v4p));
+ io_v4p->guard = 'Q';
+ if (direct_io)
+ io_v4p->flags |= SG_FLAG_DIRECT_IO;
+ if (disable_write)
+ xdwrrd10_cdb[2] |= 0x4;
+ sg_put_unaligned_be16(dio_blks, xdwrrd10_cdb + 7);
+ sg_put_unaligned_be32(lba, xdwrrd10_cdb + 2);
+ if (verbose > 2) {
+ pr2serr(" %s cdb: ", "XDWRITE(10)");
+ for (j = 0; j < XDWRITEREAD_10_LEN; ++j)
+ pr2serr("%02x ", xdwrrd10_cdb[j]);
+ pr2serr("\n");
+ }
+ io_v4p->request_len = XDWRITEREAD_10_LEN;
+ io_v4p->request = (uint64_t)xdwrrd10_cdb;
+ io_v4p->din_xfer_len = din_len;
+ io_v4p->din_xferp = (uint64_t)(dinp + (k * din_len));
+ io_v4p->dout_xfer_len = dout_len;
+ io_v4p->dout_xferp = (uint64_t)(doutp + (k * dout_len));
+ io_v4p->response = (uint64_t)sense_buffer[k];
+ io_v4p->max_response_len = SENSE_BUFFER_LEN;
+ io_v4p->timeout = 20000; /* 20000 millisecs == 20 seconds */
+ io_v4p->request_extra = 99; /* so pack_id doesn't start at 0 */
+ /* default is to queue at head (in SCSI mid level) */
+ if (q_at_tail)
+ io_v4p->flags |= SG_FLAG_Q_AT_TAIL;
+ else
+ io_v4p->flags |= SG_FLAG_Q_AT_HEAD;
+ /* io_v4p->usr_ptr = NULL; */
+
+ if (ioctl(sg_fd, SG_IO, io_v4p) < 0) {
+ pr2serr("sg ioctl(SG_IO) errno=%d [%s]\n", errno,
+ strerror(errno));
+ close(sg_fd);
+ return 1;
+ }
+ /* now for the error processing */
+ ok = 0;
+ rio_v4 = *io_v4p;
+ cat = sg_err_category_new(rio_v4.device_status,
+ rio_v4.transport_status,
+ rio_v4.driver_status,
+ (const uint8_t *)rio_v4.response,
+ rio_v4.response_len);
+ switch (cat) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ printf("Recovered error, continuing\n");
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ sg_linux_sense_print(NULL, rio_v4.device_status,
+ rio_v4.transport_status,
+ rio_v4.driver_status,
+ (const uint8_t *)rio_v4.response,
+ rio_v4.response_len, true);
+ break;
+ }
+ if ((rio_v4.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO)
+ ++dirio_count;
+ if (verbose > 3) {
+ pr2serr(">> din_resid=%d, dout_resid=%d, info=0x%x\n",
+ rio_v4.din_resid, rio_v4.dout_resid, rio_v4.info);
+ if (rio_v4.response_len > 0) {
+ pr2serr("sense buffer: ");
+ hex2stderr(sense_buffer[k], rio_v4.response_len, -1);
+ }
+ }
+ if ((! quiet) && ok) /* output result if it is available */
+ printf("XDWRITEREAD(10) using ioctl(SG_IO) duration=%u\n",
+ rio_v4.duration);
+ if (rep_count-- > 0)
+ goto rep_sg_io;
+ goto out;
+ }
+
+rep_async:
+ if (! quiet)
+ printf("start write() calls\n");
for (k = 0; k < q_len; ++k) {
io_v4p = &io_v4[k];
- /* Prepare INQUIRY command */
memset(io_v4p, 0, sizeof(*io_v4p));
io_v4p->guard = 'Q';
if (direct_io)
@@ -373,28 +472,34 @@ main(int argc, char * argv[])
}
}
- memset(&ssi, 0, sizeof(ssi));
- if (ioctl(sg_fd, SG_GET_SCSI_ID, &ssi) < 0)
- pr2serr("ioctl(SG_GET_SCSI_ID) failed, errno=%d %s\n",
- errno, strerror(errno));
- else {
- printf("host_no: %d\n", ssi.host_no);
- printf(" channel: %d\n", ssi.channel);
- printf(" scsi_id: %d\n", ssi.scsi_id);
- printf(" lun: %d\n", ssi.lun);
- printf(" pdt: %d\n", ssi.scsi_type);
- printf(" h_cmd_per_lun: %d\n", ssi.h_cmd_per_lun);
- printf(" d_queue_depth: %d\n", ssi.d_queue_depth);
+#if 0
+ {
+ struct sg_scsi_id ssi;
+
+ memset(&ssi, 0, sizeof(ssi));
+ if (ioctl(sg_fd, SG_GET_SCSI_ID, &ssi) < 0)
+ pr2serr("ioctl(SG_GET_SCSI_ID) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ else {
+ printf("host_no: %d\n", ssi.host_no);
+ printf(" channel: %d\n", ssi.channel);
+ printf(" scsi_id: %d\n", ssi.scsi_id);
+ printf(" lun: %d\n", ssi.lun);
+ printf(" pdt: %d\n", ssi.scsi_type);
+ printf(" h_cmd_per_lun: %d\n", ssi.h_cmd_per_lun);
+ printf(" d_queue_depth: %d\n", ssi.d_queue_depth);
+ }
}
+#endif
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));
- else
+ else if (! quiet)
printf("first available pack_id: %d\n", pack_id);
if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting) < 0)
pr2serr("ioctl(SG_GET_NUM_WAITING) failed, errno=%d %s\n",
errno, strerror(errno));
- else
+ else if (! quiet)
printf("num_waiting: %d\n", num_waiting);
if (sleep_secs > 0)
@@ -403,34 +508,31 @@ main(int argc, char * argv[])
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));
- else
+ else if (! quiet)
printf("first available pack_id: %d\n", pack_id);
if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting) < 0)
pr2serr("ioctl(SG_GET_NUM_WAITING) failed, errno=%d %s\n",
errno, strerror(errno));
- else
+ else if (! quiet)
printf("num_waiting: %d\n", num_waiting);
- printf("\nstart read() calls\n");
+ if (! quiet)
+ printf("\nstart read() calls\n");
for (k = 0, done = false; k < q_len; ++k) {
if ((! done) && (k == q_len / 2)) {
done = true;
- printf("\n>>> half way through read\n");
+ if (! quiet)
+ printf("\n>>> half way through read\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));
- else
+ else if (! quiet)
printf("first available pack_id: %d\n", pack_id);
if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting) < 0)
pr2serr("ioctl(SG_GET_NUM_WAITING) failed, errno=%d %s\n",
errno, strerror(errno));
- else
+ else if (! quiet)
printf("num_waiting: %d\n", num_waiting);
- if (ioctl(sg_fd, SG_GET_ACCESS_COUNT, &access_count) < 0)
- pr2serr("ioctl(SG_GET_ACCESS_COUNT) failed, errno=%d %s\n",
- errno, strerror(errno));
- else
- printf("access_count: %d\n", access_count);
}
memset(&rio_v4, 0, sizeof(struct sg_io_v4));
rio_v4.guard = 'Q';
@@ -486,6 +588,8 @@ main(int argc, char * argv[])
pr2serr("Direct IO requested %d times, done %d times\nMaybe need "
"'echo 1 > /proc/scsi/sg/allow_dio'\n", q_len, dirio_count);
}
+ if (rep_count-- > 0)
+ goto rep_async;
ret = 0;
out:
diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c
index cbbd423a..c030889e 100644
--- a/testing/sg_tst_ioctl.c
+++ b/testing/sg_tst_ioctl.c
@@ -53,7 +53,7 @@
* later of the Linux sg driver. */
-static const char * version_str = "Version: 1.02 20190113";
+static const char * version_str = "Version: 1.03 20190123";
#define INQ_REPLY_LEN 96
#define INQ_CMD_LEN 6
@@ -445,7 +445,7 @@ int
main(int argc, char * argv[])
{
bool done;
- int sg_fd, k, ok, ver_num, pack_id, num_waiting, access_count;
+ int sg_fd, k, ok, ver_num, pack_id, num_waiting;
int sg_fd2 = -1;
int sock = -1;
uint8_t inq_cdb[INQ_CMD_LEN] =
@@ -689,11 +689,6 @@ main(int argc, char * argv[])
errno, strerror(errno));
else
printf("num_waiting: %d\n", num_waiting);
- if (ioctl(sg_fd, SG_GET_ACCESS_COUNT, &access_count) < 0)
- pr2serr("ioctl(SG_GET_ACCESS_COUNT) failed, errno=%d %s\n",
- errno, strerror(errno));
- else
- printf("access_count: %d\n", access_count);
}
memset(&rio_hdr, 0, sizeof(sg_io_hdr_t));
rio_hdr.interface_id = 'S';