aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2020-11-10 01:46:05 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2020-11-10 01:46:05 +0000
commit6276daf9b89e1d27b91438ff1e92482a1d2e7179 (patch)
tree4803611223ef48163d2a0fb6cbf717639d992271
parent4ca8449c2826be469bc9558f2a037236c6703d64 (diff)
downloadsg3_utils-6276daf9b89e1d27b91438ff1e92482a1d2e7179.tar.gz
tweak transport error handling in Linux; sg_compare_and_write: add examples section to its manpage; testing
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@866 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog4
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg3_utils.82
-rw-r--r--doc/sg_compare_and_write.8126
-rw-r--r--include/sg_io_linux.h32
-rw-r--r--lib/sg_cmds_basic.c9
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_dd.c84
-rw-r--r--src/sg_verify.c8
-rw-r--r--testing/Makefile12
-rw-r--r--testing/sg_iovec_tst.c271
-rw-r--r--testing/sg_iovec_tst.cpp594
-rw-r--r--testing/sg_mrq_dd.cpp20
-rw-r--r--testing/sgh_dd.cpp4
14 files changed, 789 insertions, 381 deletions
diff --git a/ChangeLog b/ChangeLog
index 2a4337c1..81d71ba2 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 [20201013] [svn: r865]
+Changelog for sg3_utils-1.46 [20201109] [svn: r866]
- sg_rep_pip: report new provisioning initialization pattern cmd
- sg_turs: estimated time-to-ready [20-061r2]
- add --delay=MS option
@@ -16,6 +16,7 @@ Changelog for sg3_utils-1.46 [20201013] [svn: r865]
- sg_rep_zones: print invalid write pointer LBA as -1 rather
than 16 "f"s
- sg_ses: use fan speed factor field for calculation [ses4r04]
+ - sg_compare_and_write: add examples section to its manpage
- sg_modes: document '-s' option (same as '-6')
- sg_raw: increase maximum data-in and data-out buffer size
from 64 KB to 1 MB
@@ -27,6 +28,7 @@ Changelog for sg3_utils-1.46 [20201013] [svn: r865]
- sg_lib,sg_pt: add partial_clear_scsi_pt_obj(),
get_scsi_pt_cdb_len() and get_scsi_pt_cdb_buf()
- add do_nvm_pt() for the NVM (sub-)command set
+ - tweak transport error handling in Linux
- sg_lib: Linux NVMe SNTL: add read, write and verify;
synchronize cache and write same translations
- add dummy start stop unit and test unit ready commands
diff --git a/debian/changelog b/debian/changelog
index 99589f79..4a9f3e4a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.46-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Tue, 13 Oct 2020 01:00:00 -0400
+ -- Douglas Gilbert <dgilbert@interlog.com> Mon, 02 Nov 2020 22:00:00 -0500
sg3-utils (1.45-0.1) unstable; urgency=low
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index 1f0874a0..a2411120 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -1,4 +1,4 @@
-.TH SG3_UTILS "8" "October 2020" "sg3_utils\-1.46" SG3_UTILS
+.TH SG3_UTILS "8" "November 2020" "sg3_utils\-1.46" SG3_UTILS
.SH NAME
sg3_utils \- a package of utilities for sending SCSI commands
.SH SYNOPSIS
diff --git a/doc/sg_compare_and_write.8 b/doc/sg_compare_and_write.8
index 54a6f091..43926626 100644
--- a/doc/sg_compare_and_write.8
+++ b/doc/sg_compare_and_write.8
@@ -1,4 +1,4 @@
-.TH "COMPARE AND WRITE" "8" "August 2018" "sg3_utils\-1.43" SG3_UTILS
+.TH "COMPARE AND WRITE" "8" "November 2020" "sg3_utils\-1.46" SG3_UTILS
.SH NAME
sg_compare_and_write \- send the SCSI COMPARE AND WRITE command
.SH SYNOPSIS
@@ -10,36 +10,36 @@ sg_compare_and_write \- send the SCSI COMPARE AND WRITE command
[\fI\-\-xferlen=LEN\fR] \fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
-Send the SCSI COMPARE AND WRITE command to \fIDEVICE\fR. This utility
-reads a compare buffer and a write buffer from either one or two files. If
-the \fI\-\-inw=WF\fR option is not given then the concatenated compare
-and write buffers are read from the file indicated by the \fI\-\-in=IF\fR
-option. If the \fI\-\-inw=WF\fR option is given then the compare buffer
-is read from the file indicated by the \fI\-\-in=IF\fR while the write
-buffer is read from the file indicated by the \fI\-\-inw=WF\fR.
+Send the SCSI COMPARE AND WRITE command to \fIDEVICE\fR. This utility fetches
+a compare buffer and a write buffer from either one or two files. If the
+\fI\-\-inw=WF\fR option is given then the compare buffer is fetched from the
+file indicated by the \fI\-\-in=IF\fR while the write buffer is fetched from
+the file indicated by the \fI\-\-inw=WF\fR. If the \fI\-\-inw=WF\fR option is
+not given then the concatenated compare and write buffers are fetched from the
+file indicated by the \fI\-\-in=IF\fR option.
.PP
Those buffers are expected to each contain \fINUM\fR blocks of data. The
-compare starts at logical block address \fILBA\fR on the \fIDEVICE\fR
-and if the comparison fails (i.e. the provided compare buffer does not
-equal at \fILBA\fR on the \fIDEVICE\fR) then the COMPARE AND WRITE command
-finishes with a sense key of MISCOMPARE. In this case this utility will
-completes and set an exit status of 14 (which happens to be the sense key
-value of MISCOMPARE).
-.PP
-If the comparison succeeds then the provided write buffer is written to
-starting at \fILBA\fR for \fINUM\fR blocks on the \fIDEVICE\fR.
-.PP
-The actual number of bytes transferred in the data\-out buffer of the
-COMPARE AND WRITE command may need to be given by the user with the
+compare starts at logical block address \fILBA\fR on the \fIDEVICE\fR and if
+the comparison fails (i.e. the provided compare buffer does not equal the data
+at \fILBA\fR on the \fIDEVICE\fR) then the COMPARE AND WRITE command finishes
+with a sense key of MISCOMPARE. In this case this utility will complete and
+set an exit status of 14 (which happens to be the sense key value of
+MISCOMPARE).
+.PP
+If the comparison succeeds then the provided write buffer is stored starting
+at \fILBA\fR for \fINUM\fR blocks on the \fIDEVICE\fR.
+.PP
+The actual number of bytes transferred in the data\-out buffer of the COMPARE
+AND WRITE command may need to be given by the user with the
\fI\-\-xferlen=LEN\fR option. \fILEN\fR defaults to (2 * \fINUM\fR * 512)
-which is 1024 for the default \fINUM\fR of 1. If the block size is
-other than 512 then the user will need to use \fI\-\-xferlen=LEN\fR option.
-If protection information is given (indicated by a value of \fIWP\fR
-other than 0 (the default)) then for a \fINUM\fR of 1 \fILEN\fR should
-be 1040 . Note that the SCSI READ CAPACITY command is not checked by
-this utility (e.g. to find the block size).
-.PP
-The definition of the SCSI COMPARE AND WRITE command requires that the
+which is 1024 for the default \fINUM\fR of 1. If the block size is other than
+512 then the user will need to use \fI\-\-xferlen=LEN\fR option. If
+protection information is given (indicated by a value of \fIWP\fR other than
+0 (the default)) then for a \fINUM\fR of 1 \fILEN\fR should be 1040 . Note
+that the SCSI READ CAPACITY command is not performed by this utility (e.g.
+to find the block size).
+.PP
+The T10 definition of the SCSI COMPARE AND WRITE command requires that the
\fIDEVICE\fR implement the compare and optional write as an uninterrupted
series of actions. Depending on some other \fIDEVICE\fR settings a
verify operation may occur prior to the compare.
@@ -131,6 +131,74 @@ for \fILEN\fR
Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
in the sg3_utils(8) man page.
+.SH EXAMPLES
+Before overwriting the first two blocks of whatever (SCSI) storage device
+that is chosen, take a small backup. The logical block size is assumed to
+be 512 bytes. Take a copy (in backup01.bin) of the first two blocks::
+.PP
+ # sg_dd if=/dev/sg1 bs=512 of=backup01.bin count=2
+ 2+0 records in
+ 2+0 records out
+.PP
+.B WARNING:
+if /dev/sg1 corresponds to a disk on your system that contains currently
+mounted file systems, do _not_ continue. If you can, unmount all file
+systems on that disk. If that is not possible, use another disk with no
+mounted file systems on it. In Linux the scsi_debug driver is a good
+candidate for experimentation.
+.PP
+Now fill the first block with 0xff bytes:
+.PP
+ # sg_dd iflag=ff bs=512 of=/dev/sg1 count=1
+ 1+0 records in
+ 1+0 records out
+.PP
+and the second block with 0x0 bytes:
+.PP
+ # sg_dd iflag=00 bs=512 seek=1 of=/dev/sg1 count=1
+ 1+0 records in
+ 1+0 records out
+.PP
+Now copy those two blocks into a file:
+.PP
+ # sg_dd if=/dev/sg1 bs=512 of=ff00.bin count=2
+ 2+0 records in
+ 2+0 records out
+.PP
+Now we can do a compare and write command. It is told to compare the first
+block (i.e. LBA 0) with the first block in the given file (i.e. ff00.bin).
+If they are equal (they should be both full of 0xff bytes). Since the
+compare succeeds, it will write the second block in ff00.bin over LBA 0:
+.PP
+ # sg_compare_and_write \-\-in=ff00.bin \-\-lba=0 \-\-num=1 /dev/sg1
+
+.PP
+No news is good news. Now if we do that command again:
+.PP
+ # sg_compare_and_write \-\-in=ff00.bin \-\-lba=0 \-\-num=1 /dev/sg1
+ Miscompare at byte offset: 0 [0x0]
+ sg_compare_and_write failed: Miscompare
+.PP
+This is expected. The first sg_compare_and_write ended up writing 0x0 bytes
+over LBA 0x0. The second sg_compare_and_write command compares LBA 0x0 with
+0xff bytes and fails immediately (i.e. at byte offset: 0). Now we will
+overwrite the first 3 bytes of ff00.bin with 0x0:
+.PP
+ # sg_dd bs=1 iflag=00 of=ff00.bin count=3
+ 3+0 records in
+ 3+0 records out
+.PP
+Notice the 'bs=1' operand. The dd utility (and thus sg_dd) is very useful for
+doing small binary edits on a file. Now if we do that sg_compare_and_write
+again, it still fails but with a small difference:
+.PP
+ # sg_compare_and_write \-\-in=ff00.bin \-\-lba=0 \-\-num=1 /dev/sg1
+ Miscompare at byte offset: 3 [0x3]
+ sg_compare_and_write failed: Miscompare
+.PP
+So the bytes at offset 0, 1, and 2 compared equal but not the byte at
+offset 3. The SCSI COMPARE AND WRITE will stop on the first micompared
+byte.
.SH EXIT STATUS
The exit status of sg_compare_and_write is 0 when it is successful. If the
compare step fails then the exit status is 14. For other exit status values
@@ -144,7 +212,7 @@ Eric Seppanen.
.SH "REPORTING BUGS"
Report bugs to shahar.salzman@kaminario.com or dgilbert@interlog.com
.SH COPYRIGHT
-Copyright \(co 2012\-2018 Kaminario Technologies LTD
+Copyright \(co 2012\-2020 Kaminario Technologies LTD
.br
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
diff --git a/include/sg_io_linux.h b/include/sg_io_linux.h
index 4766e2ae..7b3567cd 100644
--- a/include/sg_io_linux.h
+++ b/include/sg_io_linux.h
@@ -2,7 +2,7 @@
#define SG_IO_LINUX_H
/*
- * Copyright (c) 2004-2018 Douglas Gilbert.
+ * Copyright (c) 2004-2020 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -11,7 +11,7 @@
*/
/*
- * Version 1.07 [20181211]
+ * Version 1.08 [20201102]
*/
/*
@@ -145,18 +145,20 @@ void sg_print_host_status(int host_status);
void sg_print_driver_status(int driver_status);
/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings
- else it prints errors/warnings (prefixed by 'leadin') to
- 'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the
- raw sense buffer (in ASCII hex) should be printed. */
+ * else it prints errors/warnings (prefixed by 'leadin') to
+ * 'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the
+ * raw sense buffer (in ASCII hex) should be printed. */
int sg_chk_n_print(const char * leadin, int masked_status, int host_status,
int driver_status, const uint8_t * sense_buffer,
int sb_len, bool raw_sinfo);
/* The following function declaration is for the sg version 3 driver. */
struct sg_io_hdr;
+
/* sg_chk_n_print3() returns 1 quietly if there are no errors/warnings;
- else it prints errors/warnings (prefixed by 'leadin') to
- 'sg_warnings_fd' and returns 0. */
+ * else it prints errors/warnings (prefixed by 'leadin') to
+ * 'sg_warnings_fd' and returns 0. For sg_io_v4 interface use
+ * sg_linux_sense_print() instead. */
int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp,
bool raw_sinfo);
@@ -169,25 +171,29 @@ int sg_linux_sense_print(const char * leadin, int scsi_status,
bool raw_sinfo);
/* Calls sg_scsi_normalize_sense() after obtaining the sense buffer and
- its length from the struct sg_io_hdr pointer. If these cannot be
- obtained, false is returned. */
+ * its length from the struct sg_io_hdr pointer. If these cannot be
+ * obtained, false is returned. For sg_io_v4 interface use
+ * sg_scsi_normalize_sense() function instead [see sg_lib.h]. */
bool sg_normalize_sense(const struct sg_io_hdr * hp,
struct sg_scsi_sense_hdr * sshp);
+/* Returns SG_LIB_CAT_* value. */
int sg_err_category(int masked_status, int host_status, int driver_status,
const uint8_t * sense_buffer, int sb_len);
+/* Returns SG_LIB_CAT_* value. */
int sg_err_category_new(int scsi_status, int host_status, int driver_status,
const uint8_t * sense_buffer, int sb_len);
-/* The following function declaration is for the sg version 3 driver. */
+/* The following function declaration is for the sg version 3 driver. for
+ * sg_io_v4 interface use sg_err_category_new() function instead */
int sg_err_category3(struct sg_io_hdr * hp);
/* Note about SCSI status codes found in older versions of Linux.
- Linux has traditionally used a 1 bit right shifted and masked
- version of SCSI standard status codes. Now CHECK_CONDITION
- and friends (in <scsi/scsi.h>) are deprecated. */
+ * Linux has traditionally used a 1 bit right shifted and masked
+ * version of SCSI standard status codes. Now CHECK_CONDITION
+ * and friends (in <scsi/scsi.h>) are deprecated. */
#ifdef __cplusplus
}
diff --git a/lib/sg_cmds_basic.c b/lib/sg_cmds_basic.c
index 4521b803..0b862821 100644
--- a/lib/sg_cmds_basic.c
+++ b/lib/sg_cmds_basic.c
@@ -191,7 +191,7 @@ int
sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
int pt_res, bool noisy, int verbose, int * o_sense_cat)
{
- bool transport_sense;
+ bool favour_sense;
int cat, slen, resp_code, sstat, req_din_x, req_dout_x;
int act_din_x, act_dout_x;
const uint8_t * sbp;
@@ -321,13 +321,14 @@ sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
pr2ws("%s: transport: %s\n", leadin, b);
}
+ /* Shall we favour sense data over a transport error (given both) */
#ifdef SG_LIB_LINUX
- transport_sense = (slen > 0);
+ favour_sense = false; /* DRIVER_SENSE is not passed through */
#else
- transport_sense = ((SAM_STAT_CHECK_CONDITION ==
+ favour_sense = ((SAM_STAT_CHECK_CONDITION ==
get_scsi_pt_status_response(ptvp)) && (slen > 0));
#endif
- if (transport_sense)
+ if (favour_sense)
return sg_cmds_process_helper(leadin, req_din_x, act_din_x,
req_dout_x, act_dout_x, sbp, slen,
noisy, verbose, o_sense_cat);
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 7bf1afd0..e2146319 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -84,7 +84,7 @@ fi
%{_libdir}/*.la
%changelog
-* Tue Oct 13 2020 - dgilbert at interlog dot com
+* Mon Nov 02 2020 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.46
diff --git a/src/sg_dd.c b/src/sg_dd.c
index b20c6682..fb88ac7e 100644
--- a/src/sg_dd.c
+++ b/src/sg_dd.c
@@ -67,7 +67,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "6.20 20201011";
+static const char * version_str = "6.22 20201031";
#define ME "sg_dd: "
@@ -427,11 +427,11 @@ static void
usage()
{
pr2serr("Usage: sg_dd [bs=BS] [conv=CONV] [count=COUNT] [ibs=BS] "
- "[if=IFILE]\n"
+ "[if=IFILE]\n"
" [iflag=FLAGS] [obs=BS] [of=OFILE] [oflag=FLAGS] "
"[seek=SEEK]\n"
- " [skip=SKIP] [--dry-run] [--help] [--verbose] "
- "[--version]\n\n"
+ " [skip=SKIP] [--dry-run] [--help] [--verbose] "
+ "[--version]\n\n"
" [blk_sgio=0|1] [bpt=BPT] [cdbsz=6|10|12|16] "
"[coe=0|1|2|3]\n"
" [coe_limit=CL] [dio=0|1] [odir=0|1] "
@@ -454,9 +454,9 @@ usage()
" coe_limit limit consecutive 'bad' blocks on reads to CL "
"times\n"
" when COE>1 (default: 0 which is no limit)\n"
- " conv comma separated list from: [nocreat,noerror,"
- "notrunc,\n"
- " null,sparse,sync]\n"
+ " conv comma separated list from: [nocreat,noerror,"
+ "notrunc,\n"
+ " null,sparse,sync]\n"
" count number of blocks to copy (def: device size)\n"
" dio for direct IO, 1->attempt, 0->indirect IO "
"(def)\n"
@@ -1486,6 +1486,7 @@ static int
open_of(const char * outf, int64_t seek, int bpt, struct flags_t * ofp,
int * out_typep, int vb)
{
+ bool not_found;
int outfd = -1;
int flags, t, res;
char ebuff[EBUFF_SZ];
@@ -1495,6 +1496,7 @@ open_of(const char * outf, int64_t seek, int bpt, struct flags_t * ofp,
if (vb)
pr2serr(" >> Output file type: %s\n",
dd_filetype_str(*out_typep, ebuff));
+ not_found = (FT_ERROR == *out_typep); /* assume error was not found */
if ((FT_BLOCK & *out_typep) && ofp->sgio)
*out_typep |= FT_SG;
@@ -1542,43 +1544,41 @@ open_of(const char * outf, int64_t seek, int bpt, struct flags_t * ofp,
goto file_err;
} else if (FT_DEV_NULL & *out_typep)
outfd = -1; /* don't bother opening */
- else {
- if (! (FT_RAW & *out_typep)) {
- flags = O_WRONLY;
- if (! ofp->nocreat)
- flags = O_CREAT;
- if (ofp->direct)
- flags |= O_DIRECT;
- if (ofp->excl)
- flags |= O_EXCL;
- if (ofp->dsync)
- flags |= O_SYNC;
- if (ofp->append)
- flags |= O_APPEND;
- if ((outfd = open(outf, flags, 0666)) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- ME "could not open %s for writing", outf);
- perror(ebuff);
- goto file_err;
- }
- } else {
- flags = O_WRONLY;
- if (ofp->direct)
- flags |= O_DIRECT;
- if (ofp->excl)
- flags |= O_EXCL;
- if (ofp->dsync)
- flags |= O_SYNC;
- if ((outfd = open(outf, flags)) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- ME "could not open %s for raw writing", outf);
- perror(ebuff);
- goto file_err;
- }
+ else if (FT_RAW & *out_typep) {
+ flags = O_WRONLY;
+ if (ofp->direct)
+ flags |= O_DIRECT;
+ if (ofp->excl)
+ flags |= O_EXCL;
+ if (ofp->dsync)
+ flags |= O_SYNC;
+ if ((outfd = open(outf, flags)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for raw writing", outf);
+ perror(ebuff);
+ goto file_err;
+ }
+ } else { /* FT_OTHER or FT_ERROR (not found so create) */
+ flags = O_WRONLY;
+ if (! ofp->nocreat)
+ flags |= O_CREAT;
+ if (ofp->direct)
+ flags |= O_DIRECT;
+ if (ofp->excl)
+ flags |= O_EXCL;
+ if (ofp->dsync)
+ flags |= O_SYNC;
+ if (ofp->append)
+ flags |= O_APPEND;
+ if ((outfd = open(outf, flags, 0666)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for writing", outf);
+ perror(ebuff);
+ goto file_err;
}
if (vb)
pr2serr(" %s output, flags=0x%x\n",
- ((O_CREAT & flags) ? "create" : "open"), flags);
+ (not_found ? "create" : "open"), flags);
if (seek > 0) {
off64_t offset = seek;
@@ -2067,7 +2067,7 @@ main(int argc, char * argv[])
ccp = "<random>";
cc2p = "random";
- ssz = getrandom(&seed, sizeof(seed), 0);
+ ssz = getrandom(&seed, sizeof(seed), GRND_NONBLOCK);
if (ssz < (ssize_t)sizeof(seed))
pr2serr("getrandom() failed, ret=%d\n", (int)ssz);
if (verbose > 1)
diff --git a/src/sg_verify.c b/src/sg_verify.c
index 642d10ae..ddd71c8e 100644
--- a/src/sg_verify.c
+++ b/src/sg_verify.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2019 Douglas Gilbert.
+ * Copyright (c) 2004-2020 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -39,7 +39,7 @@
* the possibility of protection data (DIF).
*/
-static const char * version_str = "1.26 20191216"; /* sbc4r17 */
+static const char * version_str = "1.27 20201029"; /* sbc4r17 */
#define ME "sg_verify: "
@@ -123,7 +123,7 @@ usage()
" --verbose|-v increase verbosity\n"
" --version|-V print version string and exit\n"
" --vrprotect=VRP|-P VRP set vrprotect field to VRP "
- "(def: 0)\n"
+ "(def: 0)\n\n"
"Performs one or more SCSI VERIFY(10) or SCSI VERIFY(16) "
"commands. sbc3r34\nmade the BYTCHK field two bits wide "
"(it was a single bit).\n");
@@ -388,7 +388,7 @@ main(int argc, char * argv[])
}
skip:
if (NULL == device_name) {
- pr2serr("missing device name!\n");
+ pr2serr("missing device name!\n\n");
usage();
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
diff --git a/testing/Makefile b/testing/Makefile
index 0a34ddec..8383cc5b 100644
--- a/testing/Makefile
+++ b/testing/Makefile
@@ -4,10 +4,10 @@ PREFIX=/usr/local
INSTDIR=$(DESTDIR)/$(PREFIX)/bin
MANDIR=$(DESTDIR)/$(PREFIX)/man
-EXECS = sg_iovec_tst sg_sense_test sg_queue_tst bsg_queue_tst sg_chk_asc \
- sg_tst_nvme sg_tst_ioctl sg_tst_bidi tst_sg_lib sgs_dd sg_tst_excl \
+EXECS = sg_sense_test sg_queue_tst bsg_queue_tst sg_chk_asc sg_tst_nvme \
+ sg_tst_ioctl sg_tst_bidi tst_sg_lib sgs_dd sg_tst_excl \
sg_tst_excl2 sg_tst_excl3 sg_tst_context sg_tst_async sgh_dd \
- sg_mrq_dd
+ sg_mrq_dd sg_iovec_tst
EXTRAS =
@@ -62,9 +62,6 @@ depend dep:
clean:
/bin/rm -f *.o $(EXECS) $(EXTRAS) $(BSG_EXTRAS) core .depend
-sg_iovec_tst: sg_iovec_tst.o $(LIBFILESOLD)
- $(LD) -o $@ $(LDFLAGS) $^
-
sg_sense_test: sg_sense_test.o $(LIBFILESOLD)
$(LD) -o $@ $(LDFLAGS) $^
@@ -114,6 +111,9 @@ sgh_dd: sgh_dd.o $(LIBFILESNEW)
sg_mrq_dd: sg_mrq_dd.o sg_scat_gath.o $(LIBFILESNEW)
$(CXXLD) -o $@ $(LDFLAGS) -pthread -latomic $^
+sg_iovec_tst: sg_iovec_tst.o sg_scat_gath.o $(LIBFILESNEW)
+ $(CXXLD) -o $@ $(LDFLAGS) -pthread -latomic $^
+
install: $(EXECS)
install -d $(INSTDIR)
diff --git a/testing/sg_iovec_tst.c b/testing/sg_iovec_tst.c
deleted file mode 100644
index f95dc67f..00000000
--- a/testing/sg_iovec_tst.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2003-2019 D. Gilbert
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- *
- * Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
- * device driver.
- * This program will read a certain number of blocks of a given block size
- * from a given sg device node and write what is retrieved out to a
- * normal file. The purpose is to test the sg_iovec mechanism within the
- * sg_io_hdr structure.
- *
- * Version 0.17 (20181207)
- */
-
-#include <unistd.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "sg_lib.h"
-#include "sg_io_linux.h"
-#include "sg_unaligned.h"
-
-
-
-#define ME "sg_iovec_tst: "
-
-#define A_PRIME 509
-#define IOVEC_ELEMS 2048
-
-#define SENSE_BUFF_LEN 32
-#define DEF_TIMEOUT 40000 /* 40,000 milliseconds */
-
-struct sg_iovec iovec[IOVEC_ELEMS];
-
-
-static void
-usage(void)
-{
- printf("Usage: sg_iovec_tst [-a] [-b=bs] -c=num [-e=es] [-h]\n"
- " <generic_device> <output_filename>\n");
- printf(" where: -a async sg use (def: use ioctl(SGIO) )\n");
- printf(" -b=bs block size (default 512 Bytes)\n");
- printf(" -c=num count of blocks to transfer\n");
- printf(" -e=es iovec element size (def: 509)\n");
- printf(" -h this usage message\n");
- printf(" reads from <generic_device> and sends to "
- "<output_filename>\nUses iovec (a scatter list) in linear "
- "mode\n");
-}
-
-/* Returns 0 if everything ok */
-static int sg_read(int sg_fd, uint8_t * buff, int num_blocks,
- int from_block, int bs, int elem_size, int async)
-{
- uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- uint8_t senseBuff[SENSE_BUFF_LEN];
- struct sg_io_hdr io_hdr;
- struct pollfd a_poll;
- int dxfer_len = bs * num_blocks;
- int k, pos, rem, res;
-
- sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
- sg_put_unaligned_be16((uint16_t)from_block, rdCmd + 7);
-
- for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
- iovec[k].iov_base = buff + pos;
- iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
- if (rem <= elem_size)
- break;
- pos += elem_size;
- rem -= elem_size;
- }
- if (k >= IOVEC_ELEMS) {
- fprintf(stderr, "Can't fit dxfer_len=%d bytes in iovec\n", dxfer_len);
- return -1;
- }
- memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
- io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(rdCmd);
- io_hdr.cmdp = rdCmd;
- io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr.dxfer_len = dxfer_len;
- io_hdr.iovec_count = k + 1;
- io_hdr.dxferp = iovec;
- io_hdr.mx_sb_len = SENSE_BUFF_LEN;
- io_hdr.sbp = senseBuff;
- io_hdr.timeout = DEF_TIMEOUT;
- io_hdr.pack_id = from_block;
-
- if (async) {
- res = write(sg_fd, &io_hdr, sizeof(io_hdr));
- if (res < 0) {
- perror("write(<sg_device>), error");
- return -1;
- } else if (res < (int)sizeof(io_hdr)) {
- fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n",
- res, (int)sizeof(io_hdr));
- return -1;
- }
- a_poll.fd = sg_fd;
- a_poll.events = POLLIN;
- a_poll.revents = 0;
- res = poll(&a_poll, 1, 2000 /* millisecs */ );
- if (res < 0) {
- perror("poll error on <sg_device>");
- return -1;
- }
- if (0 == (POLLIN & a_poll.revents)) {
- fprintf(stderr, "strange, poll() completed without data to "
- "read\n");
- return -1;
- }
- res = read(sg_fd, &io_hdr, sizeof(io_hdr));
- if (res < 0) {
- perror("read(<sg_device>), error");
- return -1;
- } else if (res < (int)sizeof(io_hdr)) {
- fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n",
- res, (int)sizeof(io_hdr));
- return -1;
- }
- } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
- perror("reading (SG_IO) on sg device, error");
- return -1;
- }
- switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
- case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
- from_block, num_blocks);
- break;
- case SG_LIB_CAT_UNIT_ATTENTION:
- fprintf(stderr, "Unit attention\n");
- return -1;
- default:
- sg_chk_n_print3("reading", &io_hdr, 1);
- return -1;
- }
- return 0;
-}
-
-
-int main(int argc, char * argv[])
-{
- int sg_fd, fd, res, j, m, dxfer_len;
- unsigned int k, num;
- int do_async = 0;
- int do_help = 0;
- int blk_size = 512;
- int elem_size = A_PRIME;
- int count = 0;
- char * sg_file_name = 0;
- char * out_file_name = 0;
- uint8_t * buffp;
-
- for (j = 1; j < argc; ++j) {
- if (0 == strcmp("-a", argv[j]))
- do_async = 1;
- else if (0 == strncmp("-b=", argv[j], 3)) {
- m = 3;
- num = sscanf(argv[j] + m, "%d", &blk_size);
- if ((1 != num) || (blk_size <= 0)) {
- printf("Couldn't decode number after '-b' switch\n");
- sg_file_name = 0;
- break;
- }
- } else if (0 == strncmp("-c=", argv[j], 3)) {
- m = 3;
- num = sscanf(argv[j] + m, "%d", &count);
- if (1 != num) {
- printf("Couldn't decode number after '-c' switch\n");
- sg_file_name = 0;
- break;
- }
- } else if (0 == strncmp("-e=", argv[j], 3)) {
- m = 3;
- num = sscanf(argv[j] + m, "%d", &elem_size);
- if (1 != num) {
- printf("Couldn't decode number after '-e' switch\n");
- sg_file_name = 0;
- break;
- }
- } else if (0 == strcmp("-h", argv[j]))
- do_help = 1;
- else if (*argv[j] == '-') {
- printf("Unrecognized switch: %s\n", argv[j]);
- sg_file_name = 0;
- break;
- } else if (NULL == sg_file_name)
- sg_file_name = argv[j];
- else
- out_file_name = argv[j];
- }
- if (do_help) {
- usage();
- return 0;
- }
- if (NULL == sg_file_name) {
- printf(">>> need sg node name (e.g. /dev/sg3)\n\n");
- usage();
- return 1;
- }
- if (NULL == out_file_name) {
- printf(">>> need out filename (to place what is fetched by READ\n\n");
- usage();
- return 1;
- }
- if (0 == count) {
- printf(">>> need count of blocks to READ\n\n");
- usage();
- return 1;
- }
-
- if (do_async)
- sg_fd = open(sg_file_name, O_RDWR);
- else
- sg_fd = open(sg_file_name, O_RDONLY);
- if (sg_fd < 0) {
- perror(ME "sg device node open error");
- return 1;
- }
- /* Don't worry, being very careful not to write to a none-sg file ... */
- res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
- if ((res < 0) || (k < 30000)) {
- printf(ME "not a sg device, or driver prior to 3.x\n");
- return 1;
- }
- fd = open(out_file_name, O_WRONLY | O_CREAT, 0666);
- if (fd < 0) {
- perror(ME "output file open error");
- return 1;
- }
- dxfer_len = count * blk_size;
- buffp = (uint8_t *)calloc(count, blk_size);
- if (buffp) {
- if (0 == sg_read(sg_fd, buffp, count, 0, blk_size, elem_size,
- do_async)) {
- if (write(fd, buffp, dxfer_len) < 0)
- perror(ME "output write failed");
- }
- free(buffp);
- } else
- fprintf(stderr, "user space calloc for %d bytes failed\n",
- dxfer_len);
- res = close(fd);
- if (res < 0) {
- perror(ME "output file close error");
- close(sg_fd);
- return 1;
- }
- res = close(sg_fd);
- if (res < 0) {
- perror(ME "sg device close error");
- return 1;
- }
- return 0;
-}
diff --git a/testing/sg_iovec_tst.cpp b/testing/sg_iovec_tst.cpp
new file mode 100644
index 00000000..3199dc8d
--- /dev/null
+++ b/testing/sg_iovec_tst.cpp
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2003-2020 D. Gilbert
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
+ * device driver.
+ * This C++ program will read a certain number of blocks of a given block
+ * size from a given sg device node using struct sg_iovec and write what is
+ * retrieved out to a normal file. The purpose is to test the sg_iovec
+ * mechanism within the sg_io_hdr and sg_io_v4 structures.
+ *
+ * struct sg_iovec and struct iovec [in include/uapi/uio.h] are basically
+ * the same thing: a pointer followed by a length (of type size_t). If
+ * applied to a disk then the pointer will hold a LBA and 'length' will
+ * be a number of logical blocks (which usually cannot exceed 2**32-1 .
+ *
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <poll.h>
+#include <limits.h>
+#include <time.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <linux/bsg.h>
+
+#ifndef HAVE_LINUX_SG_V4_HDR
+
+/* 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 .
+ * So to stop compile errors when directly importing include/uapi/scsi/sg.h
+ * undef __user before doing that include. */
+#define __user
+
+/* Want to block the original sg.h header from also being included. That
+ * causes lots of multiple definition errors. This will only work if this
+ * header is included _before_ the original sg.h header. */
+#define _SCSI_GENERIC_H /* original kernel header guard */
+#define _SCSI_SG_H /* glibc header guard */
+
+#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
+
+#else
+#define __user
+#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
+
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+
+// C++ local header
+#include "sg_scat_gath.h"
+
+static const char * version_str = "1.07 20201108";
+
+#define ME "sg_iovec_tst: "
+
+#define IOVEC_ELEMS 1024 /* match current UIO_MAXIOV in <linux/uio.h> */
+
+#define DEF_BLK_SZ 512
+#define SENSE_BUFF_LEN 32
+#define DEF_TIMEOUT 40000 /* 40,000 milliseconds */
+
+static struct sg_iovec iovec[IOVEC_ELEMS];
+
+static int verbose;
+
+static struct option long_options[] = {
+ {"async", no_argument, 0, 'a'},
+ {"bs", required_argument, 0, 'b'},
+ {"elem_size", required_argument, 0, 'e'},
+ {"elem-size", required_argument, 0, 'e'},
+ {"elemsz", required_argument, 0, 'e'},
+ {"fill", required_argument, 0, 'f'},
+ {"from_skip", no_argument, 0, 'F'},
+ {"from-skip", no_argument, 0, 'F'},
+ {"help", no_argument, 0, 'h'},
+ {"num", required_argument, 0, 'n'},
+ {"num_blks", required_argument, 0, 'n'},
+ {"num-blks", required_argument, 0, 'n'},
+ {"sgl", required_argument, 0, 'S'},
+ {"sgv4", no_argument, 0, '4'},
+ {"skip", required_argument, 0, 's'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+
+static void
+usage(void)
+{
+ printf("Usage: sg_iovec_tst [--async] [--bs=BS] [--elem_sz=ES] "
+ "[--fill=F_ELEMS]\n"
+ " [from_skip] [--help] --num=NUM [--sgl=SFN] "
+ "[--sgv4]\n"
+ " [--skip=SKIP] [--verbose] [--version] "
+ "SG_DEV OUT_F\n");
+ printf("where:\n"
+ " --async|-a async sg use (def: use ioctl(SGIO) )\n");
+ printf(" --bs=BS|-b BS logical block size of SG_DEV (def: 512 "
+ "bytes)\n");
+ printf(" --elem_sz=ES|-e ES iovec element size (def: BS bytes)\n");
+ printf(" --fill=F_ELEMS|-f F_ELEMS append F_ELEMS*ES zero bytes "
+ "onto OUT_F\n"
+ " after each iovec element (def: "
+ "0)\n");
+ printf(" --from_skip|-F sgl output starts from SKIP (def: 0)\n");
+ printf(" --help|-h this usage message\n");
+ printf(" --num=NUM|-n NUM number of blocks to read from SG_DEV\n");
+ printf(" --sgl=SFN|-S SFN Sgl FileName (SFN) that written to, with "
+ "addresses\n"
+ " and lengths having ES as their unit\n");
+ printf(" --sgv4|-4 use the sg v4 interface (def: v3 "
+ "interface)\n");
+ printf(" --skip=SKIP|-s SKIP SKIP blocks before reading S_DEV "
+ "(def: 0)\n");
+ printf(" --verbose|-v increase verbosity\n");
+ printf(" --version|-V print version and exit\n\n");
+ printf("Reads from SG_DEV and writes that data to OUT_F in binary. Uses "
+ "iovec\n(a scatter gather list) in linear mode (i.e. it cuts up "
+ "a contiguous\nbuffer). Example:\n"
+ " sg_iovec_tst -n 8k -e 4k /dev/sg3 out.bin\n");
+}
+
+/* Returns 0 if everything ok */
+static int
+sg_read(int sg_fd, uint8_t * buff, int num_blocks, int from_block, int bs,
+ int elem_size, int async)
+{
+ uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ uint8_t senseBuff[SENSE_BUFF_LEN];
+ struct sg_io_hdr io_hdr;
+ struct pollfd a_poll;
+ int dxfer_len = bs * num_blocks;
+ int k, pos, rem, res;
+ char b[128];
+
+ sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
+ sg_put_unaligned_be16((uint16_t)num_blocks, rdCmd + 7);
+
+ for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
+ iovec[k].iov_base = buff + pos;
+ iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
+ if (rem <= elem_size)
+ break;
+ pos += elem_size;
+ rem -= elem_size;
+ }
+ if (k >= IOVEC_ELEMS) {
+ fprintf(stderr, "Can't fit dxfer_len=%d bytes in %d iovec elements "
+ "(would need %d)\n", dxfer_len, IOVEC_ELEMS,
+ dxfer_len / elem_size);
+ fprintf(stderr, "Try expanding elem_size which is currently %d "
+ "bytes\n", elem_size);
+ return -1;
+ }
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(rdCmd);
+ io_hdr.cmdp = rdCmd;
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = dxfer_len;
+ io_hdr.iovec_count = k + 1;
+ io_hdr.dxferp = iovec;
+ io_hdr.mx_sb_len = SENSE_BUFF_LEN;
+ io_hdr.sbp = senseBuff;
+ io_hdr.timeout = DEF_TIMEOUT;
+ io_hdr.pack_id = from_block;
+ if (verbose)
+ fprintf(stderr, "cdb: %s\n", sg_get_command_str(rdCmd, 10, true,
+ sizeof(b), b));
+
+ if (async) {
+ res = write(sg_fd, &io_hdr, sizeof(io_hdr));
+ if (res < 0) {
+ perror("write(<sg_device>), error");
+ return -1;
+ } else if (res < (int)sizeof(io_hdr)) {
+ fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n",
+ res, (int)sizeof(io_hdr));
+ return -1;
+ }
+ a_poll.fd = sg_fd;
+ a_poll.events = POLLIN;
+ a_poll.revents = 0;
+ res = poll(&a_poll, 1, 2000 /* millisecs */ );
+ if (res < 0) {
+ perror("poll error on <sg_device>");
+ return -1;
+ }
+ if (0 == (POLLIN & a_poll.revents)) {
+ fprintf(stderr, "strange, poll() completed without data to "
+ "read\n");
+ return -1;
+ }
+ res = read(sg_fd, &io_hdr, sizeof(io_hdr));
+ if (res < 0) {
+ perror("read(<sg_device>), error");
+ return -1;
+ } else if (res < (int)sizeof(io_hdr)) {
+ fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n",
+ res, (int)sizeof(io_hdr));
+ return -1;
+ }
+ } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
+ perror("reading (SG_IO) on sg device, error");
+ return -1;
+ }
+ switch (sg_err_category3(&io_hdr)) {
+ case SG_LIB_CAT_CLEAN:
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
+ from_block, num_blocks);
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, "Unit attention\n");
+ return -1;
+ default:
+ sg_chk_n_print3("reading", &io_hdr, 1);
+ return -1;
+ }
+ return 0;
+}
+
+/* Returns 0 if everything ok */
+static int
+sg_read_v4(int sg_fd, uint8_t * buff, int num_blocks, int from_block, int bs,
+ int elem_size, int async)
+{
+ uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ uint8_t senseBuff[SENSE_BUFF_LEN];
+ struct sg_io_v4 io_hdr;
+ struct pollfd a_poll;
+ int dxfer_len = bs * num_blocks;
+ int k, pos, rem, res;
+ char b[128];
+
+ sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
+ sg_put_unaligned_be16((uint16_t)num_blocks, rdCmd + 7);
+
+ for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
+ iovec[k].iov_base = buff + pos;
+ iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
+ if (rem <= elem_size)
+ break;
+ pos += elem_size;
+ rem -= elem_size;
+ }
+ if (k >= IOVEC_ELEMS) {
+ fprintf(stderr, "Can't fit dxfer_len=%d bytes in %d iovec elements "
+ "(would need %d)\n", dxfer_len, IOVEC_ELEMS,
+ dxfer_len / elem_size);
+ fprintf(stderr, "Try expanding elem_size which is currently %d "
+ "bytes\n", elem_size);
+ return -1;
+ }
+ memset(&io_hdr, 0, sizeof(struct sg_io_v4));
+ io_hdr.guard = 'Q';
+ io_hdr.request_len = sizeof(rdCmd);
+ io_hdr.request = (uint64_t)(uintptr_t)rdCmd;
+ io_hdr.din_xfer_len = dxfer_len;
+ io_hdr.din_xferp = (uint64_t)(uintptr_t)iovec;
+ io_hdr.din_iovec_count = k + 1;
+ io_hdr.max_response_len = SG_DXFER_FROM_DEV;
+ io_hdr.response = (uint64_t)(uintptr_t)senseBuff;
+ io_hdr.timeout = DEF_TIMEOUT;
+ io_hdr.request_extra = from_block; /* pack_id */
+ if (verbose)
+ fprintf(stderr, "cdb: %s\n", sg_get_command_str(rdCmd, 10, true,
+ sizeof(b), b));
+
+ if (async) {
+ res = ioctl(sg_fd, SG_IOSUBMIT, &io_hdr);
+ if (res < 0) {
+ perror("ioctl(SG_IOSUBMIT <sg_device>), error");
+ return -1;
+ }
+ a_poll.fd = sg_fd;
+ a_poll.events = POLLIN;
+ a_poll.revents = 0;
+ res = poll(&a_poll, 1, 2000 /* millisecs */ );
+ if (res < 0) {
+ perror("poll error on <sg_device>");
+ return -1;
+ }
+ if (0 == (POLLIN & a_poll.revents)) {
+ fprintf(stderr, "strange, poll() completed without data to "
+ "read\n");
+ return -1;
+ }
+ res = ioctl(sg_fd, SG_IORECEIVE, &io_hdr);
+ if (res < 0) {
+ perror("ioctl(SG_IORECEIVE <sg_device>), error");
+ return -1;
+ }
+ } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
+ perror("ioctl(SG_IO) on sg device, error");
+ return -1;
+ }
+
+ res = sg_err_category_new(io_hdr.device_status, io_hdr.transport_status,
+ io_hdr.driver_status,
+ (const uint8_t *)(unsigned long)io_hdr.response,
+ io_hdr.response_len);
+ switch (res) {
+ case SG_LIB_CAT_CLEAN:
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
+ from_block, num_blocks);
+ break;
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ fprintf(stderr, "Unit attention\n");
+ return -1;
+ default:
+ sg_linux_sense_print("reading", io_hdr.device_status,
+ io_hdr.transport_status, io_hdr.driver_status,
+ senseBuff, io_hdr.response_len, true);
+ return -1;
+ }
+ return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool do_sgv4 = false;
+ bool do_async = false;
+ bool do_help = false;
+ bool from_skip = false;
+ bool blk_size_given = false;
+ bool elem_size_given = false;
+ int sg_fd, fd, c, res, res2, err, dxfer_len;
+ unsigned int k;
+ int blk_size = DEF_BLK_SZ;
+ int elem_size = blk_size;
+ int num_blks = 0;
+ int f_elems = 0;
+ int64_t start_blk = 0;
+ char * sg_dev_name = 0;
+ char * out_file_name = 0;
+ char * sgl_fn = 0;
+ uint8_t * buffp;
+ uint8_t * fillp = NULL;
+ FILE * fp = NULL;
+
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "4ab:e:f:Fhn:s:S:vV",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '4':
+ do_sgv4 = true;
+ break;
+ case 'a':
+ do_async = true;
+ break;
+ case 'b':
+ blk_size = sg_get_num(optarg);
+ if (blk_size < 1) {
+ printf("Couldn't decode positive number after '--bs=' "
+ "option\n");
+ sg_dev_name = 0;
+ } else
+ blk_size_given = true;
+ break;
+ case 'e':
+ elem_size = sg_get_num(optarg);
+ if (elem_size < 1) {
+ printf("Couldn't decode positive number after '--elem_size=' "
+ "option\n");
+ sg_dev_name = 0;
+ } else
+ elem_size_given = true;
+ break;
+ case 'f':
+ f_elems = sg_get_num(optarg);
+ if (f_elems < 0) {
+ printf("Couldn't decode number after '--fill=' option\n");
+ sg_dev_name = 0;
+ }
+ break;
+ case 'F':
+ from_skip = true;
+ break;
+ case 'h':
+ do_help = true;
+ break;
+ case 'n':
+ num_blks = sg_get_num(optarg);
+ if (num_blks < 1) {
+ printf("Couldn't decode positive number after '--num=' "
+ "option\n");
+ sg_dev_name = 0;
+ }
+ break;
+ case 's':
+ start_blk = sg_get_llnum(optarg);
+ if ((start_blk < 0) || (start_blk > INT_MAX)) {
+ printf("Couldn't decode number after '--skip=' option\n");
+ sg_dev_name = 0;
+ }
+ break;
+ case 'S':
+ if (sgl_fn) {
+ printf("Looks like --sgl=SFN has been given twice\n");
+ sg_dev_name = 0;
+ } else
+ sgl_fn = optarg;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'V':
+ printf("Version: %s\n", version_str);
+ return 0;
+ default:
+ fprintf(stderr, "unrecognised option code 0x%x ??\n", c);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (optind < argc) {
+ if (NULL == sg_dev_name) {
+ sg_dev_name = argv[optind];
+ ++optind;
+ }
+ if (optind < argc) {
+ if (sg_dev_name) {
+ out_file_name = argv[optind];
+ ++optind;
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ fprintf(stderr, "Unexpected extra argument: %s\n",
+ argv[optind]);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ }
+ if (do_help) {
+ usage();
+ return 0;
+ }
+ if (NULL == sg_dev_name) {
+ printf(">>> need sg node name (e.g. /dev/sg3)\n\n");
+ usage();
+ return 1;
+ }
+ if (NULL == out_file_name) {
+ printf(">>> need out filename (to place what is fetched by READ\n\n");
+ usage();
+ return 1;
+ }
+ if (0 == num_blks) {
+ printf(">>> need number of blocks to READ\n\n");
+ usage();
+ return 1;
+ }
+
+ if ((! elem_size_given) && blk_size_given)
+ elem_size = blk_size;
+
+ if (do_async)
+ sg_fd = open(sg_dev_name, O_RDWR);
+ else
+ sg_fd = open(sg_dev_name, O_RDONLY);
+ if (sg_fd < 0) {
+ perror(ME "sg device node open error");
+ return 1;
+ }
+ /* Don't worry, being very careful not to write to a none-sg file ... */
+ res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
+ if ((res < 0) || (k < 30000)) {
+ printf(ME "not a sg device, or driver prior to 3.x\n");
+ return 1;
+ }
+ fd = open(out_file_name, O_WRONLY | O_CREAT, 0666);
+ if (fd < 0) {
+ perror(ME "output file open error");
+ return 1;
+ }
+ if (f_elems > 0) {
+ fillp = (uint8_t *)calloc(f_elems, elem_size);
+ if (NULL == fillp) {
+ fprintf(stderr, "fill calloc for %d bytes failed\n",
+ f_elems * elem_size);
+ goto fini;
+ }
+ }
+ if (sgl_fn) {
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+ char s[128];
+
+ fp = fopen(sgl_fn, "w");
+ if (NULL == fp) {
+ err = errno;
+ fprintf(stderr, "Unable to open %s, error: %s\n", sgl_fn,
+ strerror(err));
+ res = sg_convert_errno(err);
+ goto fini;
+ }
+ strftime(s, sizeof(s), "%c", tm);
+ fprintf(fp, "# Scatter gather list generated by sg_iovec_tst "
+ "%s\n#\n", s);
+ }
+
+ dxfer_len = num_blks * blk_size;
+ buffp = (uint8_t *)calloc(num_blks, blk_size);
+ if (buffp) {
+ int dx_len;
+ int64_t curr_blk = from_skip ? start_blk : 0;
+
+ if (do_sgv4) {
+ if (sg_read(sg_fd, buffp, num_blks, (int)start_blk, blk_size,
+ elem_size, do_async))
+ goto free_buff;
+ } else {
+ if (sg_read_v4(sg_fd, buffp, num_blks, (int)start_blk, blk_size,
+ elem_size, do_async))
+ goto free_buff;
+ }
+ if (f_elems > 0) {
+ int fill_len = f_elems * elem_size;
+
+ for (dx_len = 0; dx_len < dxfer_len; dx_len += elem_size) {
+ if (write(fd, buffp + dx_len, elem_size) < 0) {
+ perror(ME "partial dxfer output write failed");
+ break;
+ }
+ if (sgl_fn) {
+ fprintf(fp, "%" PRId64 ",1\n", curr_blk);
+ curr_blk += f_elems + 1;
+ }
+ if (write(fd, fillp, fill_len) < 0) {
+ perror(ME "partial fill output write failed");
+ break;
+ }
+ }
+ } else if (write(fd, buffp, dxfer_len) < 0)
+ perror(ME "full output write failed");
+ else if (sgl_fn) {
+ for (dx_len = 0; dx_len < dxfer_len; dx_len += elem_size)
+ fprintf(fp, "%" PRId64 ",1\n", curr_blk++);
+ }
+free_buff:
+ free(buffp);
+ } else
+ fprintf(stderr, "user space calloc for %d bytes failed\n",
+ dxfer_len);
+ res = close(fd);
+ if (res < 0) {
+ perror(ME "output file close error");
+ close(sg_fd);
+ return 1;
+ }
+fini:
+ res2 = close(sg_fd);
+ if (res2 < 0) {
+ err = errno;
+ perror(ME "sg device close error");
+ if (0 == res)
+ res = sg_convert_errno(err);
+ }
+ if (fp)
+ fclose(fp);
+ return res;
+}
diff --git a/testing/sg_mrq_dd.cpp b/testing/sg_mrq_dd.cpp
index f304eb51..27424a02 100644
--- a/testing/sg_mrq_dd.cpp
+++ b/testing/sg_mrq_dd.cpp
@@ -30,7 +30,7 @@
*
*/
-static const char * version_str = "1.15 20201012";
+static const char * version_str = "1.16 20201105";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -241,6 +241,7 @@ struct global_collection /* one instance visible to all threads */
off_t outreg_st_size;
atomic<int> dio_incomplete_count;
atomic<int> sum_of_resids;
+ atomic<int> reason_res;
int verbose;
int dry_run;
bool mrq_eq_0; /* true when user gives mrq=0 */
@@ -279,7 +280,6 @@ typedef struct request_element
uint8_t sb[SENSE_BUFF_LEN];
int dio_incomplete_count;
int mmap_active;
- int resid;
int rd_p_id;
int rep_count;
int rq_id;
@@ -853,7 +853,7 @@ usage(int pg_num)
" from dd it defaults to stdout). If 'of=.' "
"uses /dev/null\n"
" oflag comma separated list from: [append,nocreat,\n"
- " <<list from iflag>>]\n"
+ " <<list from iflag>>]\n"
" seek block position to start writing to OFILE\n"
" skip block position to start reading from IFILE\n"
" --help|-h output this usage message then exit\n"
@@ -1212,7 +1212,7 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
if (clp->in_flags.random) {
ssize_t ssz;
- ssz = getrandom(&rep->seed, sizeof(rep->seed), 0);
+ ssz = getrandom(&rep->seed, sizeof(rep->seed), GRND_NONBLOCK);
if (ssz < (ssize_t)sizeof(rep->seed))
pr2serr_lk("[%d] %s: getrandom() failed, ret=%d\n", id, __func__,
(int)ssz);
@@ -1398,6 +1398,7 @@ fini:
clp->out_rem_count -= rep->out_local_count;
clp->in_partial += rep->in_local_partial;
clp->out_partial += rep->out_local_partial;
+ clp->sum_of_resids += rep->in_resid_bytes;
if (rep->alloc_bp)
free(rep->alloc_bp);
}
@@ -1665,6 +1666,7 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
int n_cmpl = ctl_v4p->info;
int n_good = 0;
int hole_count = 0;
+ int cat = 0;
int vb = clp->verbose;
int k, j, f1, slen, sstatus;
char b[160];
@@ -1731,7 +1733,8 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
if (ssh.response_code & 0x1) {
ok = true;
last_err_on_in = false;
- }
+ } else
+ cat = sg_err_category_sense(sbp, slen);
if (SPC_SK_MISCOMPARE == ssh.sense_key)
++num_miscompare;
@@ -1739,7 +1742,8 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
if (vb)
lk_chk_n_print4(" >>", a_v4p, vb > 4);
}
- }
+ } else if (! ok)
+ cat = SG_LIB_CAT_OTHER;
if (ok && f1) {
++n_good;
if (a_v4p->dout_xfer_len >= (uint32_t)clp->bs)
@@ -1775,6 +1779,8 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
if (all_good)
pr2serr_lk(" ... all good\n");
fini:
+ if (cat > 0)
+ clp->reason_res.store(cat);
return n_good;
}
@@ -4016,5 +4022,7 @@ fini:
(num_miscompare > 1) ? "s" : "", num_miscompare.load());
if (clp->verify && (SG_LIB_CAT_MISCOMPARE == res))
pr2serr("Verify/compare failed due to miscompare\n");
+ if (0 == res)
+ res = clp->reason_res.load();
return (res >= 0) ? res : SG_LIB_CAT_OTHER;
}
diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp
index b7381f44..9a53b98a 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -36,7 +36,7 @@
* renamed [20181221]
*/
-static const char * version_str = "1.95 20201012";
+static const char * version_str = "1.96 20201019";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -1200,7 +1200,7 @@ mrq_abort_thread(void * v_maip)
Mrq_abort_info l_mai = *(Mrq_abort_info *)v_maip;
struct sg_io_v4 ctl_v4;
- ssz = getrandom(&rn, sizeof(rn), 0);
+ ssz = getrandom(&rn, sizeof(rn), GRND_NONBLOCK);
if (ssz < (ssize_t)sizeof(rn))
pr2serr_lk("%s: getrandom() failed, returned %d\n", __func__,
(int)ssz);