diff --git a/COPYING b/COPYING index 6371eba1..c584dc50 100644 --- a/COPYING +++ b/COPYING @@ -4,7 +4,7 @@ Upstream Authors: Douglas Gilbert , Peter Allworth , James Bottomley , Lars Marowsky-Bree , - Kurt Garloff , + Kurt Garloff, Grant Grundler , Christophe Varoqui , Michael Weller , @@ -12,7 +12,7 @@ Upstream Authors: Douglas Gilbert , Copyright: -This software is copyright(c) 1994-2012 by the authors +This software is copyright(c) 1994-2021 by the authors Most of the code in this package is covered by a BSD license. On Debian systems, the complete text of the BSD License @@ -29,4 +29,4 @@ file. The later GPL-3 is found in /usr/share/common-licenses/GPL-3 file but no code in this package refers to that license. Douglas Gilbert -10th April 2012 +4th October 2021 diff --git a/CREDITS b/CREDITS index 78bda745..7d0781f8 100644 --- a/CREDITS +++ b/CREDITS @@ -89,7 +89,7 @@ Joe Krahn help with int64_t cleanup Kai Makisara help with tape minor numbers in lk 2.6 plus earlier advice [20081008] -Kurt Garloff original sg_start and sg_test_rwbuf. +Kurt Garloff: original sg_start and sg_test_rwbuf. Additions to sginfo and sg_map. Author of rescan-scsi-bus.sh with latest update to v1.57 [20130331] diff --git a/ChangeLog b/ChangeLog index d581db85..7714e814 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 pre-release sg3_utils-1.47 [20211001] [svn: r915] +Changelog for pre-release sg3_utils-1.47 [20211028] [svn: r916] - transport error handling improved. To fix report of a BAD_TARGET transport error but the utility still continued. - introduce SG_LIB_TRANSPORT_ERROR [35] exit status @@ -20,6 +20,10 @@ Changelog for pre-release sg3_utils-1.47 [20211001] [svn: r915] - sg_dd, sgm_dd, sgp_dd: don't close negative file descriptors - sg_dd: srand48_r() and mrand48_r() are GNU libc specific, put conditional in so non-reentrant version used otherwise + - 'iflag=00,ff' places the 32 bit block address (big endian) + into each block + - sgp_dd: major rework + - new: --chkaddr which checks for block address in each block - sg_xcopy: tweak CSCD identification descriptor - sg_get_elem_status: fix issue with '--maxlen=' option - add 2 depopulation revocation health attributes [sbc5r01] diff --git a/README.sg_start b/README.sg_start index 91366875..22034d4b 100644 --- a/README.sg_start +++ b/README.sg_start @@ -26,7 +26,7 @@ if test -x /bin/sg_start; then fi Enjoy! - Kurt Garloff +Kurt Garloff Postscript diff --git a/debian/changelog b/debian/changelog index 60dc3a8f..7e95f01f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,7 +2,7 @@ sg3-utils (1.47-0.1) unstable; urgency=low * New upstream version - -- Douglas Gilbert Fri, 01 Oct 2021 12:00:00 -0400 + -- Douglas Gilbert Thu, 28 Oct 2021 14:00:00 -0400 sg3-utils (1.46-0.1) unstable; urgency=low diff --git a/doc/sg_dd.8 b/doc/sg_dd.8 index 23b40b07..14290e07 100644 --- a/doc/sg_dd.8 +++ b/doc/sg_dd.8 @@ -293,7 +293,13 @@ Here is a list of flags and their meanings: this flag is only active with \fIiflag=\fR and when given replaces \fIif=IFILE\fR. If both are given an error is generated. The input will be a stream of zeros, similar to using "if=/dev/zero" alone (but a little -quicker). +quicker), apart from the following case. +.br +If 'iflag=00,ff' is given then the block address (lower 32 bits, in 4 +bytes, big endian) is placed, multiple times, in each block. The block +address takes into account the \fIskip=SKIP\fR setting. The +.B sgp_dd +utility has a \fI\-\-chkaddr\fR option that complements this option. .TP append causes the O_APPEND flag to be added to the open of \fIOFILE\fR. For regular @@ -356,8 +362,12 @@ causes the O_EXCL flag to be added to the open of \fIIFILE\fR and/or .TP ff this flag is only active with \fIiflag=\fR and when given replaces -\fIif=IFILE\fR. If both are given an error is generated. The input will -be a stream of 0xff bytes (or all bits set). +\fIif=IFILE\fR. If both are given an error is generated. The input will be +a stream of 0xff bytes (or all bits set), apart from the following case. +.br +If 'iflag=00,ff' is given then the block address (lower 32 bits, in 4 +bytes, big endian) is placed, multiple times, in each block. The block +address takes into account the \fIskip=SKIP\fR setting. .TP flock after opening the associated file (i.e. \fIIFILE\fR and/or \fIOFILE\fR) diff --git a/doc/sg_opcodes.8 b/doc/sg_opcodes.8 index cff8684a..bdae3b5c 100644 --- a/doc/sg_opcodes.8 +++ b/doc/sg_opcodes.8 @@ -1,4 +1,4 @@ -.TH SG_OPCODES "8" "March 2020" "sg3_utils\-1.46" SG3_UTILS +.TH SG_OPCODES "8" "October 2021" "sg3_utils\-1.47" SG3_UTILS .SH NAME sg_opcodes \- report supported SCSI commands or task management functions .SH SYNOPSIS @@ -89,7 +89,7 @@ sense key of "illegal request". additionally prints out an indication (0 or 1) whether the command effects all logical units in the containing target. MLU (Multiple Logical Units) is a bit in the REPORT SUPPORTED OPERATION CODES response -introduced by proposal 18-045r1 (and possibly in spc5r20). Without +introduced by proposal 18\-045r1 (and possibly in spc5r20). Without the option, the default output format which lists all opcodes, does not include a MLU indication. .TP diff --git a/doc/sg_requests.8 b/doc/sg_requests.8 index 81db55fe..f6a012c5 100644 --- a/doc/sg_requests.8 +++ b/doc/sg_requests.8 @@ -1,4 +1,4 @@ -.TH SG_REQUESTS "8" "July 2018" "sg3_utils\-1.45" SG3_UTILS +.TH SG_REQUESTS "8" "October 2021" "sg3_utils\-1.47" SG3_UTILS .SH NAME sg_requests \- send one or more SCSI REQUEST SENSE commands .SH SYNOPSIS @@ -32,7 +32,7 @@ but will most likely be ignored. when used once it changes the REQUEST SENSE opcode from 0x3 to 0xff which should be rejected by the \fIDEVICE\fR. There is a small chance that the device vendor has implemented a vendor specific command at that opcode (0xff). -When used twice the pass-through call to send the SCSI command is bypassed. +When used twice the pass\-through call to send the SCSI command is bypassed. The idea here is to measure the user space overhead of this package's library to set up and process the response of a SCSI command. This option will be typically used with the \fI\-\-num=NUM\fR and \fI\-\-time\fR @@ -130,7 +130,7 @@ Written by Douglas Gilbert. .SH "REPORTING BUGS" Report bugs to . .SH COPYRIGHT -Copyright \(co 2004\-2017 Douglas Gilbert +Copyright \(co 2004\-2021 Douglas Gilbert .br This software is distributed under a FreeBSD license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/doc/sg_ses.8 b/doc/sg_ses.8 index 00471afc..4afcb2b4 100644 --- a/doc/sg_ses.8 +++ b/doc/sg_ses.8 @@ -1,14 +1,15 @@ -.TH SG_SES "8" "May 2021" "sg3_utils\-1.47" SG3_UTILS +.TH SG_SES "8" "October 2021" "sg3_utils\-1.47" SG3_UTILS .SH NAME sg_ses \- access a SCSI Enclosure Services (SES) device .SH SYNOPSIS .B sg_ses -[\fI\-\-all\fR] [\fI\-\-descriptor=DES\fR] [\fI\-\-dev\-slot\-num=SN\fR] -[\fI\-\-eiioe=A_F\fR] [\fI\-\-filter\fR] [\fI\-\-get=STR\fR] [\fI\-\-hex\fR] -[\fI\-\-index=IIA\fR | \fI\-\-index=TIA,II\fR] [\fI\-\-inner\-hex\fR] -[\fI\-\-join\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR] [\fI\-\-quiet\fR] -[\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-sas\-addr=SA\fR] -[\fI\-\-status\fR] [\fI\-\-verbose\fR] [\fI\-\-warn\fR] \fIDEVICE\fR +[\fI\-\-all\fR] [\fI\-\-ALL\fR] [\fI\-\-descriptor=DES\fR] +[\fI\-\-dev\-slot\-num=SN\fR] [\fI\-\-eiioe=A_F\fR] [\fI\-\-filter\fR] +[\fI\-\-get=STR\fR] [\fI\-\-hex\fR] [\fI\-\-index=IIA\fR | +\fI\-\-index=TIA,II\fR] [\fI\-\-inner\-hex\fR] [\fI\-\-join\fR] +[\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR] [\fI\-\-quiet\fR] [\fI\-\-raw\fR] +[\fI\-\-readonly\fR] [\fI\-\-sas\-addr=SA\fR] [\fI\-\-status\fR] +[\fI\-\-verbose\fR] [\fI\-\-warn\fR] \fIDEVICE\fR .PP .B sg_ses \fI\-\-control\fR [\fI\-\-byte1=B1\fR] [\fI\-\-clear=STR\fR] @@ -92,6 +93,15 @@ shows (almost) all status dpages, following references and presenting the information as a long list whose indentation indicates the level of nesting. This option is actually the same as \fI\-\-join\fR, see its description for more information. +.br +If used twice, adds threshold elements to output (if they are available). +So it is the same as using \fI\-\-join\fRtwice. +.TP +\fB\-z\fR, \fB\-\-ALL\fR +shows (almost) all status dpages, following references and presenting +the information as a long list whose indentation indicates the level +of nesting. Also shows the threshold elements if they are available. +This option is the same as using \fI\-\-join\fR rwice. .TP \fB\-b\fR, \fB\-\-byte1\fR=\fIB1\fR some modifiable dpages may need byte 1 (i.e. the second byte) set. In the diff --git a/doc/sg_write_x.8 b/doc/sg_write_x.8 index 95352aae..e907c341 100644 --- a/doc/sg_write_x.8 +++ b/doc/sg_write_x.8 @@ -1,4 +1,4 @@ -.TH SG_WRITE_X "8" "June 2020" "sg3_utils\-1.45" SG3_UTILS +.TH SG_WRITE_X "8" "October 2021" "sg3_utils\-1.47" SG3_UTILS .SH NAME sg_write_x \- SCSI WRITE normal/ATOMIC/SAME/SCATTERED/STREAM, ORWRITE commands .SH SYNOPSIS @@ -133,11 +133,12 @@ range descriptors which is required by the WRITE SCATTERED cdb. In the absence of other information the logic will take a degenerate LBA range descriptor as a terminator of the scatter list. .PP -The current reference for these commands is draft SBC\-4 (T10/BSR INCITS -506) revision 15 dated 9 November 2017. All six SCSI commands are described -in that document. WRITE ATOMIC was added in SBC\-4 revision 3; WRITE STREAM -was added in SBC\-4 revision 7; WRITE SCATTERED was added in SBC\-4 -revision 11 while the others are in the SBC\-3 standard. +The reference for these commands is SBC\-4 (T10/BSR INCITS 506\-2021) +and draft SBC\-5 INCITS 571 revision 1 dated 21 May 2021. All six SCSI +commands are described in those documents. WRITE ATOMIC was added in +SBC\-4 revision 3; WRITE STREAM was added in SBC\-4 revision 7; WRITE +SCATTERED was added in SBC\-4 revision 11 while the others are in the +previous SBC\-3 standard. .SH OPTIONS Arguments to long options are mandatory for short options as well. The options are arranged in alphabetical order based on the long @@ -591,7 +592,7 @@ Written by Douglas Gilbert. .SH "REPORTING BUGS" Report bugs to . .SH COPYRIGHT -Copyright \(co 2017\-2020 Douglas Gilbert +Copyright \(co 2017\-2021 Douglas Gilbert .br This software is distributed under a FreeBSD license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/doc/sg_zone.8 b/doc/sg_zone.8 index a2aa45fc..bc916423 100644 --- a/doc/sg_zone.8 +++ b/doc/sg_zone.8 @@ -12,7 +12,7 @@ sg_zone \- send a SCSI ZONE modifying command .PP Sends a SCSI OPEN ZONE, CLOSE ZONE, FINISH ZONE, REMOVE ELEMENT AND MODIFY ZONES or SEQUENTIALIZE ZONE command to the \fIDEVICE\fR. All but the last -two are found in the ZBC standard (INCITS 536-2016). The REMOVE ELEMENT AND +two are found in the ZBC standard (INCITS 536-\2016). The REMOVE ELEMENT AND MODIFY ZONES command was added in zbc2r07 while the SEQUENTIALIZE ZONE command was added in zbc2r01b. .PP diff --git a/doc/sgp_dd.8 b/doc/sgp_dd.8 index a8899464..7f6115da 100644 --- a/doc/sgp_dd.8 +++ b/doc/sgp_dd.8 @@ -1,4 +1,4 @@ -.TH SGP_DD "8" "February 2020" "sg3_utils\-1.45" SG3_UTILS +.TH SGP_DD "8" "October 2021" "sg3_utils\-1.47" SG3_UTILS .SH NAME sgp_dd \- copy data to and from files and devices, especially SCSI devices @@ -10,7 +10,8 @@ devices .PP [\fIbpt=BPT\fR] [\fIcoe=\fR0|1] [\fIcdbsz=\fR6|10|12|16] [\fIdeb=VERB\fR] [\fIdio=\fR0|1] [\fIsync=\fR0|1] [\fIthr=THR\fR] [\fItime=\fR0|1] -[\fIverbose=VERB\fR] [\fI\-\-dry\-run\fR] [\fI\-\-verbose\fR] +[\fIverbose=VERB\fR] [\fI\-\-chkaddr\fR] [\fI\-\-dry\-run\fR] +[\fI\-\-verbose\fR] .SH DESCRIPTION .\" Add any additional description here .PP @@ -134,6 +135,14 @@ performed, outputting the results (to stderr) at completion. When increase verbosity. Same as \fIdeb=VERB\fR. Added for compatibility with sg_dd and sgm_dd. .TP +\fB\-c\fR, \fB\-\-chkaddr\fR +this option checks that every block read contains the (32 bit) block address +of that block. If that check fails, the copy exits with a miscompare error. +This check complements the 'sg_dd iflag=00,ff' generation of blocks that +contain their own (32 bit, big endian) block address. When \fI\-\-chkaddr\fR +is used once, only the first block address in each block is checked. When +used twice, each block address (that fits in a block) is checked. +.TP \fB\-d\fR, \fB\-\-dry\-run\fR does all the command line parsing and preparation but bypasses the actual copy or read. That preparation may include opening \fIIFILE\fR or @@ -321,7 +330,7 @@ Written by Douglas Gilbert and Peter Allworth. .SH "REPORTING BUGS" Report bugs to . .SH COPYRIGHT -Copyright \(co 2000\-2020 Douglas Gilbert +Copyright \(co 2000\-2021 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/lib/sg_lib.c b/lib/sg_lib.c index 02102a1c..8884379a 100644 --- a/lib/sg_lib.c +++ b/lib/sg_lib.c @@ -1796,7 +1796,7 @@ sg_get_sense_str(const char * lip, const uint8_t * sbp, int sb_len, { bool descriptor_format = false; bool sdat_ovfl = false; - bool valid; + bool valid_info_fld; int len, progress, n, r, pr, rem, blen; unsigned int info; uint8_t resp_code; @@ -1820,7 +1820,7 @@ sg_get_sense_str(const char * lip, const uint8_t * sbp, int sb_len, return n; } resp_code = 0x7f & sbp[0]; - valid = !!(sbp[0] & 0x80); + valid_info_fld = !!(sbp[0] & 0x80); len = sb_len; if (sg_scsi_normalize_sense(sbp, sb_len, &ssh)) { switch (ssh.response_code) { @@ -1882,7 +1882,7 @@ sg_get_sense_str(const char * lip, const uint8_t * sbp, int sb_len, r += sg_scnpr(b + r, blen - r, "%s", lip); if (len > 6) { info = sg_get_unaligned_be32(sbp + 3); - if (valid) + if (valid_info_fld) r += sg_scnpr(b + r, blen - r, " Info fld=0x%x [%u] ", info, info); else if (info > 0) @@ -1901,7 +1901,7 @@ sg_get_sense_str(const char * lip, const uint8_t * sbp, int sb_len, r += sg_scnpr(b + r, blen - r, " ILI"); /* incorrect block length requested */ r += sg_scnpr(b + r, blen - r, "\n"); - } else if (valid || (info > 0)) + } else if (valid_info_fld || (info > 0)) r += sg_scnpr(b + r, blen - r, "\n"); if ((len >= 14) && sbp[14]) r += sg_scnpr(b + r, blen - r, "%s Field replaceable unit " @@ -1984,16 +1984,16 @@ sg_get_sense_str(const char * lip, const uint8_t * sbp, int sb_len, r += sg_scnpr(b + r, blen - r, "Probably uninitialized data.\n%s " "Try to view as SCSI-1 non-extended sense:\n", lip); r += sg_scnpr(b + r, blen - r, " AdValid=%d Error class=%d Error " - "code=%d\n", valid, ((sbp[0] >> 4) & 0x7), + "code=%d\n", valid_info_fld, ((sbp[0] >> 4) & 0x7), (sbp[0] & 0xf)); - if (valid) + if (valid_info_fld) sg_scnpr(b + r, blen - r, "%s lba=0x%x\n", lip, sg_get_unaligned_be24(sbp + 1) & 0x1fffff); n += sg_scnpr(cbp + n, cblen - n, "%s\n", b); } check_raw: if (raw_sinfo) { - int embed_len; + int calculated_len; char z[64]; n += sg_scnpr(cbp + n, cblen - n, "%s Raw sense data (in hex), " @@ -2001,18 +2001,18 @@ sg_get_sense_str(const char * lip, const uint8_t * sbp, int sb_len, if (n >= (cblen - 1)) return n; if ((sb_len > 7) && (sbp[0] >= 0x70) && (sbp[0] < 0x74)) { - embed_len = sbp[7] + 8; - n += sg_scnpr(cbp + n, cblen - n, ", embedded_len=%d\n", - embed_len); + calculated_len = sbp[7] + 8; + n += sg_scnpr(cbp + n, cblen - n, ", calculated_len=%d\n", + calculated_len); } else { - embed_len = sb_len; + calculated_len = sb_len; n += sg_scnpr(cbp + n, cblen - n, "\n"); } if (n >= (cblen - 1)) return n; sg_scnpr(z, sizeof(z), "%.50s ", lip); - n += hex2str(sbp, embed_len, z, -1, cblen - n, cbp + n); + n += hex2str(sbp, calculated_len, z, -1, cblen - n, cbp + n); } return n; } diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c index a132d1cb..559e0efd 100644 --- a/lib/sg_lib_data.c +++ b/lib/sg_lib_data.c @@ -19,7 +19,7 @@ #include "sg_lib_data.h" -const char * sg_lib_version_str = "2.82 20210830"; +const char * sg_lib_version_str = "2.83 20211007"; /* spc6r05, sbc5r01, zbc2r10 */ @@ -1822,7 +1822,7 @@ struct sg_value_2names_t sg_exit_str_arr[] = { {32, "Logic error", "unexpected situation, contact author"}, {33, "SCSI command timeout", NULL}, /* OS timed out command */ {34, "Windows error number", "doesn't fit in 7 bits"}, - {35, "Transport error", "driver or interconnect error"}, + {35, "Transport error", "driver or interconnect error"}, {36, "No errors (false)", NULL}, {40, "Aborted command, protection error", NULL}, {41, "Aborted command, protection error with Info field", NULL}, diff --git a/scripts/README b/scripts/README index 082e0e67..72cbdcf5 100644 --- a/scripts/README +++ b/scripts/README @@ -6,9 +6,8 @@ This directory contains bash shell scripts. Most of them call one or more utilities from the sg3_utils package. They assume the sg3_utils package utilities are on the PATH of the user. -rescan-scsi-bus.sh is written by Kurt Garloff (see -http://www.garloff.de/kurt/linux/ under the "Rescan SCSI bus" heading) -with patches from Hannes Reinecke. +rescan-scsi-bus.sh is written by Kurt Garloff (formerly from Suse Labs) +with patches from Hannes Reinecke (Suse) and Redhat. scsi_logging_level is written by Andreas Herrmann . It sets the logging level of the SCSI subsystem in the Linux @@ -53,4 +52,4 @@ the sdparm package in its scripts directory. /dev/disk/by-id/wwn-0x5001501234567890-part1. Douglas Gilbert -8th March 2014 +4th October 2021 diff --git a/sg3_utils.spec b/sg3_utils.spec index 3cce953b..9869875d 100644 --- a/sg3_utils.spec +++ b/sg3_utils.spec @@ -84,7 +84,7 @@ fi %{_libdir}/*.la %changelog -* Fri Oct 01 2021 - dgilbert at interlog dot com +* Thu Oct 28 2021 - dgilbert at interlog dot com - track t10 changes * sg3_utils-1.47 diff --git a/src/sg_dd.c b/src/sg_dd.c index 7c7823b7..9d05c93b 100644 --- a/src/sg_dd.c +++ b/src/sg_dd.c @@ -70,7 +70,7 @@ #include "sg_unaligned.h" #include "sg_pr2serr.h" -static const char * version_str = "6.29 20210906"; +static const char * version_str = "6.30 20211027"; #define ME "sg_dd: " @@ -2105,11 +2105,13 @@ main(int argc, char * argv[]) outfd = STDOUT_FILENO; iflag.pdt = -1; oflag.pdt = -1; - if (iflag.ff) { + if (iflag.zero && iflag.ff) { + ccp = ""; + cc2p = "addr_as_data"; + } else if (iflag.ff) { ccp = "<0xff bytes>"; cc2p = "ff"; } else if (iflag.random) { - ccp = ""; cc2p = "random"; #ifdef HAVE_GETRANDOM @@ -2392,8 +2394,18 @@ main(int argc, char * argv[]) dio_incomplete_count++; } } else if (FT_RANDOM_0_FF == in_type) { + int j; + res = blocks * blk_sz; - if (iflag.zero) + if (iflag.zero && iflag.ff && (blk_sz >= 4)) { + uint32_t pos = (uint32_t)skip; + uint off; + + for (k = 0, off = 0; k < blocks; ++k, off += blk_sz, ++pos) { + for (j = 0; j < (blk_sz - 3); j += 4) + sg_put_unaligned_be32(pos, wrkPos + off + j); + } + } else if (iflag.zero) memset(wrkPos, 0, res); else if (iflag.ff) memset(wrkPos, 0xff, res); diff --git a/src/sg_ses.c b/src/sg_ses.c index 0368dd4a..ecebb9db 100644 --- a/src/sg_ses.c +++ b/src/sg_ses.c @@ -38,7 +38,7 @@ * commands tailored for SES (enclosure) devices. */ -static const char * version_str = "2.52 20210802"; /* ses4r04 */ +static const char * version_str = "2.53 20211022"; /* ses4r04 */ #define MX_ALLOC_LEN ((64 * 1024) - 4) /* max allowable for big enclosures */ #define MX_ELEM_HDR 1024 @@ -742,6 +742,7 @@ static bool active_et_aesp_arr[NUM_ACTIVE_ET_AESP_ARR] = { /* Command line long option names with corresponding short letter. */ static struct option long_options[] = { {"all", no_argument, 0, 'a'}, + {"ALL", no_argument, 0, 'z'}, {"byte1", required_argument, 0, 'b'}, {"clear", required_argument, 0, 'C'}, {"control", no_argument, 0, 'c'}, @@ -1149,7 +1150,7 @@ parse_cmd_line(struct opts_t *op, int argc, char *argv[]) int option_index = 0; c = getopt_long(argc, argv, "aA:b:cC:d:D:eE:fG:hHiI:jln:N:m:Mp:qrRs" - "S:vVwx:", long_options, &option_index); + "S:vVwx:z", long_options, &option_index); if (c == -1) break; @@ -1368,6 +1369,10 @@ parse_cmd_line(struct opts_t *op, int argc, char *argv[]) inhex_arg = optarg; op->do_data = true; break; + case 'z': /* --ALL and -z are synonyms for '--join --join' */ + /* -A already used for --sas-addr=SA shortened form */ + op->do_join += 2; + break; default: pr2serr("unrecognised option code 0x%x ??\n", c); goto err_help; @@ -3040,10 +3045,14 @@ threshold_helper(const char * header, const char * pad, case 0x4: /*temperature */ if (header) printf("%s", header); - printf("%shigh critical=%s, high warning=%s\n", pad, + printf("%shigh critical=%s, high warning=%s", pad, reserved_or_num(b, 128, tp[0] - TEMPERAT_OFF, -TEMPERAT_OFF), reserved_or_num(b2, 128, tp[1] - TEMPERAT_OFF, -TEMPERAT_OFF)); - printf("%slow warning=%s, low critical=%s (in Celsius)\n", pad, + if (op->do_filter && (0 == tp[2]) && (0 == tp[3])) { + printf(" (in Celsius)\n"); + break; + } + printf("\n%slow warning=%s, low critical=%s (in Celsius)\n", pad, reserved_or_num(b, 128, tp[2] - TEMPERAT_OFF, -TEMPERAT_OFF), reserved_or_num(b2, 128, tp[3] - TEMPERAT_OFF, -TEMPERAT_OFF)); break; diff --git a/src/sg_start.c b/src/sg_start.c index bd098282..890b696a 100644 --- a/src/sg_start.c +++ b/src/sg_start.c @@ -7,8 +7,8 @@ * * SPDX-License-Identifier: GPL-2.0-or-later - Start/Stop parameter by Kurt Garloff , 6/2000 - Sync cache parameter by Kurt Garloff , 1/2001 + Start/Stop parameter by Kurt Garloff, 6/2000 + Sync cache parameter by Kurt Garloff, 1/2001 Guard block device answering sg's ioctls. 12/2002 Convert to SG_IO ioctl so can use sg or block devices in 2.6.* 3/2003 diff --git a/src/sg_test_rwbuf.c b/src/sg_test_rwbuf.c index 84ef2072..d31abc6b 100644 --- a/src/sg_test_rwbuf.c +++ b/src/sg_test_rwbuf.c @@ -1,5 +1,5 @@ /* - * (c) 2000 Kurt Garloff + * (c) 2000 Kurt Garloff * heavily based on Douglas Gilbert's sg_rbuf program. * (c) 1999-2019 Douglas Gilbert * diff --git a/src/sginfo.c b/src/sginfo.c index 3529d1fd..ba094dc6 100644 --- a/src/sginfo.c +++ b/src/sginfo.c @@ -78,9 +78,9 @@ * sg device) * * 001208 Add Kurt Garloff's "-uno" flag for displaying info - * from a page number. [version 1.90] + * from a page number. [version 1.90] * - * Kurt Garloff + * Kurt Garloff * 20000715 allow displaying and modification of vendor specific pages * (unformatted - @ hexdatafield) * accept vendor lengths for those pages diff --git a/src/sgm_dd.c b/src/sgm_dd.c index d4e2ce16..4909c821 100644 --- a/src/sgm_dd.c +++ b/src/sgm_dd.c @@ -69,7 +69,7 @@ #include "sg_pr2serr.h" -static const char * version_str = "1.68 20210601"; +static const char * version_str = "1.17 20211024"; #define DEF_BLOCK_SIZE 512 #define DEF_BLOCKS_PER_TRANSFER 128 @@ -116,6 +116,7 @@ static int64_t out_full = 0; static int out_partial = 0; static int verbose = 0; static int dry_run = 0; +static int progress = 0; /* accept --progress or -p, does nothing */ static bool do_time = false; static bool start_tm_valid = false; @@ -338,7 +339,7 @@ scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) *num_sect = (int64_t)ui + 1; *sect_sz = sg_get_unaligned_be32(rcBuff + 4); } - if (verbose) + if (verb) pr2serr(" number of blocks=%" PRId64 " [0x%" PRIx64 "], block " "size=%d\n", *num_sect, *num_sect, *sect_sz); return 0; @@ -363,7 +364,7 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) return -1; } *num_sect = ((int64_t)ull / (int64_t)*sect_sz); - if (verbose) + if (verbose > 1) pr2serr(" [bgs64] number of blocks=%" PRId64 " [0x%" PRIx64 "], logical block size=%d\n", *num_sect, *num_sect, *sect_sz); @@ -375,7 +376,7 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) return -1; } *num_sect = (int64_t)ul; - if (verbose) + if (verbose > 1) pr2serr(" [bgs] number of blocks=%" PRId64 " [0x%" PRIx64 "], logical block size=%d\n", *num_sect, *num_sect, *sect_sz); @@ -881,6 +882,9 @@ main(int argc, char * argv[]) usage(); return 0; } + n = num_chs_in_str(key + 1, keylen - 1, 'p'); + progress += n; + res += n; n = num_chs_in_str(key + 1, keylen - 1, 'v'); verbose += n; res += n; @@ -900,7 +904,9 @@ main(int argc, char * argv[]) (0 == strcmp(key, "-?"))) { usage(); return 0; - } else if (0 == strncmp(key, "--verb", 6)) + } else if (0 == strncmp(key, "--prog", 6)) + ++progress; + else if (0 == strncmp(key, "--verb", 6)) ++verbose; else if (0 == strncmp(key, "--vers", 6)) version_given = true; @@ -972,7 +978,7 @@ main(int argc, char * argv[]) outfd = STDOUT_FILENO; if (inf[0] && ('-' != inf[0])) { in_type = dd_filetype(inf); - if (verbose) + if (verbose > 1) pr2serr(" >> Input file type: %s\n", dd_filetype_str(in_type, ebuff)); @@ -1054,7 +1060,7 @@ main(int argc, char * argv[]) perror(ebuff); return sg_convert_errno(err); } - if (verbose) + if (verbose > 1) pr2serr(" >> skip: lseek64 SEEK_SET, byte offset=0x%" PRIx64 "\n", (uint64_t)offset); } @@ -1063,7 +1069,7 @@ main(int argc, char * argv[]) if (outf[0] && ('-' != outf[0])) { out_type = dd_filetype(outf); - if (verbose) + if (verbose > 1) pr2serr(" >> Output file type: %s\n", dd_filetype_str(out_type, ebuff)); @@ -1159,7 +1165,7 @@ main(int argc, char * argv[]) perror(ebuff); return sg_convert_errno(err); } - if (verbose) + if (verbose > 1) pr2serr(" >> seek: lseek64 SEEK_SET, byte offset=0x%" PRIx64 "\n", (uint64_t)offset); } diff --git a/src/sgp_dd.c b/src/sgp_dd.c index 503df157..adf4c2ec 100644 --- a/src/sgp_dd.c +++ b/src/sgp_dd.c @@ -84,7 +84,7 @@ #include "sg_pr2serr.h" -static const char * version_str = "5.78 20210601"; +static const char * version_str = "5.82 20211027"; #define DEF_BLOCK_SIZE 512 #define DEF_BLOCKS_PER_TRANSFER 128 @@ -140,51 +140,54 @@ struct flags_t { bool mmap; }; -typedef struct request_collection +struct opts_t { /* one instance visible to all threads */ int infd; int64_t skip; int in_type; int cdbsz_in; struct flags_t in_flags; - int64_t in_blk; /* -\ next block address to read */ - int64_t in_count; /* | blocks remaining for next read */ - int64_t in_rem_count; /* | count of remaining in blocks */ - int in_partial; /* | */ - bool in_stop; /* | */ - pthread_mutex_t in_mutex; /* -/ */ + int64_t in_blk; /* next block address to read */ + int64_t in_count; /* blocks remaining for next read */ + int64_t in_rem_count; /* count of remaining in blocks */ + int in_partial; + pthread_mutex_t inout_mutex; int outfd; int64_t seek; int out_type; int cdbsz_out; struct flags_t out_flags; - int64_t out_blk; /* -\ next block address to write */ - int64_t out_count; /* | blocks remaining for next write */ - int64_t out_rem_count; /* | count of remaining out blocks */ - int out_partial; /* | */ - bool out_stop; /* | */ - pthread_mutex_t out_mutex; /* | */ - pthread_cond_t out_sync_cv; /* -/ hold writes until "in order" */ + int64_t out_blk; /* next block address to write */ + int64_t out_count; /* blocks remaining for next write */ + int64_t out_rem_count; /* count of remaining out blocks */ + int out_partial; + pthread_cond_t out_sync_cv; int bs; int bpt; int num_threads; - int dio_incomplete_count; /* -\ */ - int sum_of_resids; /* | */ - pthread_mutex_t aux_mutex; /* -/ (also serializes some printf()s */ + int dio_incomplete_count; + int sum_of_resids; bool mmap_active; + int chkaddr; /* check read data contains 4 byte, big endian block + * addresses, once: check only 4 bytes per block */ + int progress; /* accept --progress or -p, does nothing */ int debug; int dry_run; -} Rq_coll; +}; typedef struct thread_arg { /* pointer to this argument passed to thread */ int id; - Rq_coll * clp; + int64_t seek_skip; + struct opts_t * clp; } Thread_arg; typedef struct request_element { /* one instance per worker thread */ bool wr; + bool in_stop; + bool in_err; + bool out_err; int infd; int outfd; int64_t blk; @@ -210,10 +213,12 @@ static pthread_t sig_listen_thread_id; static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio"; -static void sg_in_operation(Rq_coll * clp, Rq_elem * rep); -static void sg_out_operation(Rq_coll * clp, Rq_elem * rep); -static bool normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks); -static void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks); +static void sg_in_operation(struct opts_t * clp, Rq_elem * rep); +static void sg_out_operation(struct opts_t * clp, Rq_elem * rep, + bool bump_out_blk); +static void normal_in_operation(struct opts_t * clp, Rq_elem * rep, int blocks); +static void normal_out_operation(struct opts_t * clp, Rq_elem * rep, int blocks, + bool bump_out_blk); static int sg_start_io(Rq_elem * rep); static int sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp); @@ -222,12 +227,18 @@ static int sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp); /* Assume initialized to 0, but want to start at 1, hence adding 1 in macro */ static atomic_uint ascending_val; +static atomic_uint num_eintr; +static atomic_uint num_eagain; +static atomic_uint num_ebusy; +static atomic_bool exit_threads; + #define GET_NEXT_PACK_ID(_v) (atomic_fetch_add(&ascending_val, _v) + (_v)) #else static pthread_mutex_t av_mut = PTHREAD_MUTEX_INITIALIZER; static int ascending_val = 1; +static volatile bool exit_threads; static unsigned int GET_NEXT_PACK_ID(unsigned int val) @@ -253,7 +264,7 @@ static Thread_arg thr_arg_a[MAX_NUM_THREADS]; static bool shutting_down = false; static bool do_sync = false; static bool do_time = false; -static Rq_coll rcoll; +static struct opts_t my_opts; static struct timeval start_tm; static int64_t dd_count = -1; static int exit_status = 0; @@ -278,7 +289,7 @@ calc_duration_throughput(int contin) } a = res_tm.tv_sec; a += (0.000001 * res_tm.tv_usec); - b = (double)rcoll.bs * (dd_count - rcoll.out_rem_count); + b = (double)my_opts.bs * (dd_count - my_opts.out_rem_count); pr2serr("time to transfer data %s %d.%06d secs", (contin ? "so far" : "was"), (int)res_tm.tv_sec, (int)res_tm.tv_usec); @@ -293,16 +304,16 @@ print_stats(const char * str) { int64_t infull, outfull; - if (0 != rcoll.out_rem_count) + if (0 != my_opts.out_rem_count) pr2serr(" remaining block count=%" PRId64 "\n", - rcoll.out_rem_count); - infull = dd_count - rcoll.in_rem_count; + my_opts.out_rem_count); + infull = dd_count - my_opts.in_rem_count; pr2serr("%s%" PRId64 "+%d records in\n", str, - infull - rcoll.in_partial, rcoll.in_partial); + infull - my_opts.in_partial, my_opts.in_partial); - outfull = dd_count - rcoll.out_rem_count; + outfull = dd_count - my_opts.out_rem_count; pr2serr("%s%" PRId64 "+%d records out\n", str, - outfull - rcoll.out_partial, rcoll.out_partial); + outfull - my_opts.out_partial, my_opts.out_partial); } static void @@ -451,6 +462,7 @@ usage() " time 0->no timing(def), 1->time plus calculate " "throughput\n" " verbose same as 'deb=VERB': increase verbosity\n" + " --chkaddr|-c check read data contains blk address\n" " --dry-run|-d prepare but bypass copy/read\n" " --help|-h output this usage message then exit\n" " --verbose|-v increase verbosity of utility\n" @@ -459,29 +471,6 @@ usage() "specialized for SCSI devices, uses multiple POSIX threads\n"); } -static void -guarded_stop_in(Rq_coll * clp) -{ - pthread_mutex_lock(&clp->in_mutex); - clp->in_stop = true; - pthread_mutex_unlock(&clp->in_mutex); -} - -static void -guarded_stop_out(Rq_coll * clp) -{ - pthread_mutex_lock(&clp->out_mutex); - clp->out_stop = true; - pthread_mutex_unlock(&clp->out_mutex); -} - -static void -guarded_stop_both(Rq_coll * clp) -{ - guarded_stop_in(clp); - guarded_stop_out(clp); -} - static int sgp_mem_mmap(int fd, int res_sz, uint8_t ** mmpp) { @@ -576,7 +565,7 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) static void * sig_listen_thread(void * v_clp) { - Rq_coll * clp = (Rq_coll *)v_clp; + struct opts_t * clp = (struct opts_t *)v_clp; int sig_number; while (1) { @@ -585,7 +574,11 @@ sig_listen_thread(void * v_clp) break; if (SIGINT == sig_number) { pr2serr("%sinterrupted by SIGINT\n", my_name); - guarded_stop_both(clp); +#ifdef HAVE_C11_ATOMICS + atomic_store(&exit_threads, true); +#else + exit_threads = true; +#endif pthread_cond_broadcast(&clp->out_sync_cv); } } @@ -595,24 +588,20 @@ sig_listen_thread(void * v_clp) static void cleanup_in(void * v_clp) { - Rq_coll * clp = (Rq_coll *)v_clp; + struct opts_t * clp = (struct opts_t *)v_clp; pr2serr("thread cancelled while in mutex held\n"); - clp->in_stop = true; - pthread_mutex_unlock(&clp->in_mutex); - guarded_stop_out(clp); + pthread_mutex_unlock(&clp->inout_mutex); pthread_cond_broadcast(&clp->out_sync_cv); } static void cleanup_out(void * v_clp) { - Rq_coll * clp = (Rq_coll *)v_clp; + struct opts_t * clp = (struct opts_t *)v_clp; pr2serr("thread cancelled while out mutex held\n"); - clp->out_stop = true; - pthread_mutex_unlock(&clp->out_mutex); - guarded_stop_in(clp); + pthread_mutex_unlock(&clp->inout_mutex); pthread_cond_broadcast(&clp->out_sync_cv); } @@ -693,28 +682,33 @@ static void * read_write_thread(void * v_tap) { Thread_arg * tap = (Thread_arg *)v_tap; - Rq_coll * clp = tap->clp; + struct opts_t * clp; Rq_elem rel; Rq_elem * rep = &rel; - int sz; - volatile bool stop_after_write = false; - int64_t seek_skip; + volatile bool stop_after_write, b; + bool enforce_write_ordering; + int sz, c_addr; + int64_t out_blk, out_count; + int64_t seek_skip = tap->seek_skip; int blocks, status; - sz = clp->bpt * clp->bs; - seek_skip = clp->seek - clp->skip; - memset(rep, 0, sizeof(Rq_elem)); + stop_after_write = false; + clp = tap->clp; + enforce_write_ordering = (FT_DEV_NULL != clp->out_type) && + (FT_SG != clp->out_type); + c_addr = clp->chkaddr; + memset(rep, 0, sizeof(*rep)); /* Following clp members are constant during lifetime of thread */ rep->bs = clp->bs; if ((clp->num_threads > 1) && clp->mmap_active) { /* sg devices need separate file descriptor */ if (clp->in_flags.mmap && (FT_SG == clp->in_type)) { - rep->infd = sg_in_open(infn, &clp->in_flags, clp->bs, clp->bpt); + rep->infd = sg_in_open(infn, &clp->in_flags, rep->bs, clp->bpt); if (rep->infd < 0) err_exit(-rep->infd, "error opening infn"); } else rep->infd = clp->infd; if (clp->out_flags.mmap && (FT_SG == clp->out_type)) { - rep->outfd = sg_out_open(outfn, &clp->out_flags, clp->bs, + rep->outfd = sg_out_open(outfn, &clp->out_flags, rep->bs, clp->bpt); if (rep->outfd < 0) err_exit(-rep->outfd, "error opening outfn"); @@ -724,6 +718,7 @@ read_write_thread(void * v_tap) rep->infd = clp->infd; rep->outfd = clp->outfd; } + sz = clp->bpt * rep->bs; rep->debug = clp->debug; rep->cdbsz_in = clp->cdbsz_in; rep->cdbsz_out = clp->cdbsz_out; @@ -742,12 +737,19 @@ read_write_thread(void * v_tap) } while(1) { - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) err_exit(status, "lock in_mutex"); - if (clp->in_stop || (clp->in_count <= 0)) { + if ((rep->in_stop) || (rep->in_err) || (rep->out_err)) + break; + status = pthread_mutex_lock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); +#ifdef HAVE_C11_ATOMICS + b = atomic_load(&exit_threads); +#else + b = exit_threads; +#endif + if (b || (clp->in_count <= 0)) { /* no more to do, exit loop then thread */ - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) err_exit(status, "unlock in_mutex"); + status = pthread_mutex_unlock(&clp->inout_mutex); + if (0 != status) err_exit(status, "unlock inout_mutex"); break; } blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count; @@ -756,117 +758,151 @@ read_write_thread(void * v_tap) rep->num_blks = blocks; clp->in_blk += blocks; clp->in_count -= blocks; + /* while we have this lock, find corresponding out_blk */ + out_blk = rep->blk + seek_skip; + out_count = clp->out_count; + if (! enforce_write_ordering) + clp->out_blk += blocks; + clp->out_count -= blocks; + status = pthread_mutex_unlock(&clp->inout_mutex); + if (0 != status) err_exit(status, "unlock inout_mutex"); pthread_cleanup_push(cleanup_in, (void *)clp); if (FT_SG == clp->in_type) - sg_in_operation(clp, rep); /* lets go of in_mutex mid operation */ - else { - stop_after_write = normal_in_operation(clp, rep, blocks); - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) err_exit(status, "unlock in_mutex"); + sg_in_operation(clp, rep); + else + normal_in_operation(clp, rep, blocks); + if (c_addr && (rep->bs > 3)) { + int k, j, off, num; + uint32_t addr = (uint32_t)rep->blk; + + num = (1 == c_addr) ? 4 : (rep->bs - 3); + for (k = 0, off = 0; k < blocks; ++k, ++addr, off += rep->bs) { + for (j = 0; j < num; j += 4) { + if (addr != sg_get_unaligned_be32(rep->buffp + off + j)) + break; + } + if (j < num) + break; + } + if (k < blocks) { + pr2serr("%s: chkaddr failure at addr=0x%x\n", __func__, addr); + rep->in_err = true; + } } pthread_cleanup_pop(0); + if (rep->in_err) { + status = pthread_mutex_lock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); + /* write-side not done, so undo changes to out_blk + out_count */ + if (! enforce_write_ordering) + clp->out_blk -= blocks; + clp->out_count += blocks; + status = pthread_mutex_unlock(&clp->inout_mutex); + if (0 != status) err_exit(status, "unlock inout_mutex"); + break; + } - status = pthread_mutex_lock(&clp->out_mutex); - if (0 != status) err_exit(status, "lock out_mutex"); - if (FT_DEV_NULL != clp->out_type) { - while ((! clp->out_stop) && - ((rep->blk + seek_skip) != clp->out_blk)) { + if (enforce_write_ordering) { + status = pthread_mutex_lock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); +#ifdef HAVE_C11_ATOMICS + b = atomic_load(&exit_threads); +#else + b = exit_threads; +#endif + while ((! b) && (out_blk != clp->out_blk)) { /* if write would be out of sequence then wait */ pthread_cleanup_push(cleanup_out, (void *)clp); - status = pthread_cond_wait(&clp->out_sync_cv, &clp->out_mutex); + status = pthread_cond_wait(&clp->out_sync_cv, + &clp->inout_mutex); if (0 != status) err_exit(status, "cond out_sync_cv"); pthread_cleanup_pop(0); } + status = pthread_mutex_unlock(&clp->inout_mutex); + if (0 != status) err_exit(status, "unlock inout_mutex"); } - if (clp->out_stop || (clp->out_count <= 0)) { - if (! clp->out_stop) - clp->out_stop = true; - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) err_exit(status, "unlock out_mutex"); +#ifdef HAVE_C11_ATOMICS + b = atomic_load(&exit_threads); +#else + b = exit_threads; +#endif + if (b || (out_count <= 0)) break; - } - if (stop_after_write) - clp->out_stop = true; + rep->wr = true; - rep->blk = clp->out_blk; - clp->out_blk += blocks; - clp->out_count -= blocks; + rep->blk = out_blk; if (0 == rep->num_blks) { - clp->out_stop = true; - stop_after_write = true; - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) err_exit(status, "unlock out_mutex"); break; /* read nothing so leave loop */ } pthread_cleanup_push(cleanup_out, (void *)clp); if (FT_SG == clp->out_type) - sg_out_operation(clp, rep); /* releases out_mutex mid operation */ + sg_out_operation(clp, rep, enforce_write_ordering); else if (FT_DEV_NULL == clp->out_type) { /* skip actual write operation */ clp->out_rem_count -= blocks; - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) err_exit(status, "unlock out_mutex"); - } - else { - normal_out_operation(clp, rep, blocks); - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) err_exit(status, "unlock out_mutex"); } + else + normal_out_operation(clp, rep, blocks, enforce_write_ordering); pthread_cleanup_pop(0); - if (stop_after_write) - break; - pthread_cond_broadcast(&clp->out_sync_cv); + if (enforce_write_ordering) + pthread_cond_broadcast(&clp->out_sync_cv); } /* end of while loop */ + if (rep->alloc_bp) free(rep->alloc_bp); - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) err_exit(status, "lock in_mutex"); - if (! clp->in_stop) - clp->in_stop = true; /* flag other workers to stop */ - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) err_exit(status, "unlock in_mutex"); + if (rep->in_err || rep->out_err) { + stop_after_write = true; +#ifdef HAVE_C11_ATOMICS + if (! atomic_load(&exit_threads)) + atomic_store(&exit_threads, true); +#else + if (! exit_threads) + exit_threads = true; +#endif + } pthread_cond_broadcast(&clp->out_sync_cv); - return stop_after_write ? NULL : clp; + return (stop_after_write || rep->in_stop) ? NULL : clp; } -static bool -normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks) +static void +normal_in_operation(struct opts_t * clp, Rq_elem * rep, int blocks) { - bool stop_after_write = false; - int res; + int res, status; char strerr_buff[STRERR_BUFF_LEN + 1]; - /* enters holding in_mutex */ - while (((res = read(rep->infd, rep->buffp, blocks * clp->bs)) < 0) && + while (((res = read(rep->infd, rep->buffp, blocks * rep->bs)) < 0) && ((EINTR == errno) || (EAGAIN == errno))) ; if (res < 0) { - if (clp->in_flags.coe) { + if (rep->in_flags.coe) { memset(rep->buffp, 0, rep->num_blks * rep->bs); pr2serr(">> substituted zeros for in blk=%" PRId64 " for %d " "bytes, %s\n", rep->blk, rep->num_blks * rep->bs, tsafe_strerror(errno, strerr_buff)); - res = rep->num_blks * clp->bs; + res = rep->num_blks * rep->bs; } else { pr2serr("error in normal read, %s\n", tsafe_strerror(errno, strerr_buff)); - clp->in_stop = true; - guarded_stop_out(clp); - return 1; + rep->in_stop = true; + rep->in_err = true; + return; } } - if (res < blocks * clp->bs) { + status = pthread_mutex_lock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); + if (res < blocks * rep->bs) { int o_blocks = blocks; - stop_after_write = true; - blocks = res / clp->bs; - if ((res % clp->bs) > 0) { + + rep->in_stop = true; + blocks = res / rep->bs; + if ((res % rep->bs) > 0) { blocks++; clp->in_partial++; } @@ -878,43 +914,49 @@ normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks) clp->in_count -= blocks; } clp->in_rem_count -= blocks; - return stop_after_write; + status = pthread_mutex_unlock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); } static void -normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks) +normal_out_operation(struct opts_t * clp, Rq_elem * rep, int blocks, + bool bump_out_blk) { - int res; + int res, status; char strerr_buff[STRERR_BUFF_LEN + 1]; - /* enters holding out_mutex */ - while (((res = write(rep->outfd, rep->buffp, rep->num_blks * clp->bs)) + while (((res = write(rep->outfd, rep->buffp, rep->num_blks * rep->bs)) < 0) && ((EINTR == errno) || (EAGAIN == errno))) ; if (res < 0) { - if (clp->out_flags.coe) { + if (rep->out_flags.coe) { pr2serr(">> ignored error for out blk=%" PRId64 " for %d bytes, " "%s\n", rep->blk, rep->num_blks * rep->bs, tsafe_strerror(errno, strerr_buff)); - res = rep->num_blks * clp->bs; + res = rep->num_blks * rep->bs; } else { pr2serr("error normal write, %s\n", tsafe_strerror(errno, strerr_buff)); - guarded_stop_in(clp); - clp->out_stop = true; + rep->out_err = true; return; } } - if (res < blocks * clp->bs) { - blocks = res / clp->bs; - if ((res % clp->bs) > 0) { + status = pthread_mutex_lock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); + if (res < blocks * rep->bs) { + blocks = res / rep->bs; + if ((res % rep->bs) > 0) { blocks++; clp->out_partial++; } rep->num_blks = blocks; } clp->out_rem_count -= blocks; + if (bump_out_blk) + clp->out_blk += blocks; + status = pthread_mutex_unlock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); } static int @@ -988,12 +1030,11 @@ sg_build_scsi_cdb(uint8_t * cdbp, int cdb_sz, unsigned int blocks, } static void -sg_in_operation(Rq_coll * clp, Rq_elem * rep) +sg_in_operation(struct opts_t * clp, Rq_elem * rep) { int res; int status; - /* enters holding in_mutex */ while (1) { res = sg_start_io(rep); if (1 == res) @@ -1001,31 +1042,25 @@ sg_in_operation(Rq_coll * clp, Rq_elem * rep) else if (res < 0) { pr2serr("%sinputting to sg failed, blk=%" PRId64 "\n", my_name, rep->blk); - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) err_exit(status, "unlock in_mutex"); - guarded_stop_both(clp); + rep->in_stop = true; + rep->in_err = true; return; } - /* Now release in mutex to let other reads run in parallel */ - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) err_exit(status, "unlock in_mutex"); - - res = sg_finish_io(rep->wr, rep, &clp->aux_mutex); + res = sg_finish_io(rep->wr, rep, &clp->inout_mutex); switch (res) { case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_UNIT_ATTENTION: /* try again with same addr, count info */ /* now re-acquire in mutex for balance */ /* N.B. This re-read could now be out of read sequence */ - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) err_exit(status, "lock in_mutex"); break; case SG_LIB_CAT_MEDIUM_HARD: - if (0 == clp->in_flags.coe) { + if (0 == rep->in_flags.coe) { pr2serr("error finishing sg in command (medium)\n"); if (exit_status <= 0) exit_status = res; - guarded_stop_both(clp); + rep->in_stop = true; + rep->in_err = true; return; } else { memset(rep->buffp, 0, rep->num_blks * rep->bs); @@ -1039,19 +1074,15 @@ sg_in_operation(Rq_coll * clp, Rq_elem * rep) #endif #endif case 0: + status = pthread_mutex_lock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); if (rep->dio_incomplete_count || rep->resid) { - status = pthread_mutex_lock(&clp->aux_mutex); - if (0 != status) err_exit(status, "lock aux_mutex"); clp->dio_incomplete_count += rep->dio_incomplete_count; clp->sum_of_resids += rep->resid; - status = pthread_mutex_unlock(&clp->aux_mutex); - if (0 != status) err_exit(status, "unlock aux_mutex"); } - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) err_exit(status, "lock in_mutex"); clp->in_rem_count -= rep->num_blks; - status = pthread_mutex_unlock(&clp->in_mutex); - if (0 != status) err_exit(status, "unlock in_mutex"); + status = pthread_mutex_unlock(&clp->inout_mutex); + if (0 != status) err_exit(status, "unlock inout_mutex"); return; case SG_LIB_CAT_ILLEGAL_REQ: if (clp->debug) @@ -1061,19 +1092,19 @@ sg_in_operation(Rq_coll * clp, Rq_elem * rep) pr2serr("error finishing sg in command (%d)\n", res); if (exit_status <= 0) exit_status = res; - guarded_stop_both(clp); + rep->in_stop = true; + rep->in_err = true; return; } - } + } /* end of while loop */ } static void -sg_out_operation(Rq_coll * clp, Rq_elem * rep) +sg_out_operation(struct opts_t * clp, Rq_elem * rep, bool bump_out_blk) { int res; int status; - /* enters holding out_mutex */ while (1) { res = sg_start_io(rep); if (1 == res) @@ -1081,31 +1112,23 @@ sg_out_operation(Rq_coll * clp, Rq_elem * rep) else if (res < 0) { pr2serr("%soutputting from sg failed, blk=%" PRId64 "\n", my_name, rep->blk); - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) err_exit(status, "unlock out_mutex"); - guarded_stop_both(clp); + rep->out_err = true; return; } - /* Now release in mutex to let other reads run in parallel */ - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) err_exit(status, "unlock out_mutex"); - - res = sg_finish_io(rep->wr, rep, &clp->aux_mutex); + res = sg_finish_io(rep->wr, rep, &clp->inout_mutex); switch (res) { case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_UNIT_ATTENTION: /* try again with same addr, count info */ /* now re-acquire out mutex for balance */ /* N.B. This re-write could now be out of write sequence */ - status = pthread_mutex_lock(&clp->out_mutex); - if (0 != status) err_exit(status, "lock out_mutex"); break; case SG_LIB_CAT_MEDIUM_HARD: - if (0 == clp->out_flags.coe) { + if (0 == rep->out_flags.coe) { pr2serr("error finishing sg out command (medium)\n"); if (exit_status <= 0) exit_status = res; - guarded_stop_both(clp); + rep->out_err = true; return; } else pr2serr(">> ignored error for out blk=%" PRId64 " for %d " @@ -1117,29 +1140,27 @@ sg_out_operation(Rq_coll * clp, Rq_elem * rep) #endif #endif case 0: + status = pthread_mutex_lock(&clp->inout_mutex); + if (0 != status) err_exit(status, "lock inout_mutex"); if (rep->dio_incomplete_count || rep->resid) { - status = pthread_mutex_lock(&clp->aux_mutex); - if (0 != status) err_exit(status, "lock aux_mutex"); clp->dio_incomplete_count += rep->dio_incomplete_count; clp->sum_of_resids += rep->resid; - status = pthread_mutex_unlock(&clp->aux_mutex); - if (0 != status) err_exit(status, "unlock aux_mutex"); } - status = pthread_mutex_lock(&clp->out_mutex); - if (0 != status) err_exit(status, "lock out_mutex"); clp->out_rem_count -= rep->num_blks; - status = pthread_mutex_unlock(&clp->out_mutex); - if (0 != status) err_exit(status, "unlock out_mutex"); + if (bump_out_blk) + clp->out_blk += rep->num_blks; + status = pthread_mutex_unlock(&clp->inout_mutex); + if (0 != status) err_exit(status, "unlock inout_mutex"); return; case SG_LIB_CAT_ILLEGAL_REQ: if (clp->debug) sg_print_command_len(rep->cdb, rep->cdbsz_out); /* FALL THROUGH */ default: + rep->out_err = true; pr2serr("error finishing sg out command (%d)\n", res); if (exit_status <= 0) exit_status = res; - guarded_stop_both(clp); return; } } @@ -1180,15 +1201,23 @@ sg_start_io(Rq_elem * rep) if (mmap) hp->flags |= SG_FLAG_MMAP_IO; if (rep->debug > 8) { - pr2serr("sg_start_io: SCSI %s, blk=%" PRId64 " num_blks=%d\n", - rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks); + pr2serr("%s: SCSI %s, blk=%" PRId64 " num_blks=%d\n", __func__, + rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks); sg_print_command(hp->cmdp); } while (((res = write(rep->wr ? rep->outfd : rep->infd, hp, sizeof(struct sg_io_hdr))) < 0) && - ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno))) - ; + ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno))) { +#ifdef HAVE_C11_ATOMICS + if (EINTR == errno) + atomic_fetch_add(&num_eintr, 1); + else if (EAGAIN == errno) + atomic_fetch_add(&num_eagain, 1); + else + atomic_fetch_add(&num_ebusy, 1); +#endif + } if (res < 0) { if (ENOMEM == errno) return 1; @@ -1240,21 +1269,22 @@ sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp) break; case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_UNIT_ATTENTION: - if (rep->debug > 8) + if (rep->debug) sg_chk_n_print3((wr ? "writing": "reading"), hp, false); return res; case SG_LIB_CAT_NOT_READY: default: - { + rep->out_err = false; + if (rep->debug) { char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "%s blk=%" PRId64, wr ? "writing": "reading", rep->blk); status = pthread_mutex_lock(a_mutp); - if (0 != status) err_exit(status, "lock aux_mutex"); + if (0 != status) err_exit(status, "lock inout_mutex"); sg_chk_n_print3(ebuff, hp, false); status = pthread_mutex_unlock(a_mutp); - if (0 != status) err_exit(status, "unlock aux_mutex"); + if (0 != status) err_exit(status, "unlock inout_mutex"); return res; } } @@ -1268,7 +1298,7 @@ sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp) rep->dio_incomplete_count = 0; rep->resid = hp->resid; if (rep->debug > 8) - pr2serr("sg_finish_io: completed %s\n", wr ? "WRITE" : "READ"); + pr2serr("%s: completed %s\n", __func__, wr ? "WRITE" : "READ"); return 0; } @@ -1351,9 +1381,10 @@ main(int argc, char * argv[]) int res, k, err, keylen; int64_t in_num_sect = 0; int64_t out_num_sect = 0; + int64_t seek_skip; int in_sect_sz, out_sect_sz, status, n, flags; void * vp; - Rq_coll * clp = &rcoll; + struct opts_t * clp = &my_opts; char ebuff[EBUFF_SZ]; #if SG_LIB_ANDROID struct sigaction actions; @@ -1484,6 +1515,9 @@ main(int argc, char * argv[]) do_time = !! sg_get_num(buf); else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) { res = 0; + n = num_chs_in_str(key + 1, keylen - 1, 'c'); + clp->chkaddr += n; + res += n; n = num_chs_in_str(key + 1, keylen - 1, 'd'); clp->dry_run += n; res += n; @@ -1492,6 +1526,9 @@ main(int argc, char * argv[]) usage(); return 0; } + n = num_chs_in_str(key + 1, keylen - 1, 'p'); + clp->progress += n; + res += n; n = num_chs_in_str(key + 1, keylen - 1, 'v'); if (n > 0) verbose_given = true; @@ -1507,14 +1544,18 @@ main(int argc, char * argv[]) key); return SG_LIB_SYNTAX_ERROR; } - } else if ((0 == strncmp(key, "--dry-run", 9)) || + } else if (0 == strncmp(key, "--chkaddr", 9)) + ++clp->chkaddr; + else if ((0 == strncmp(key, "--dry-run", 9)) || (0 == strncmp(key, "--dry_run", 9))) ++clp->dry_run; else if ((0 == strncmp(key, "--help", 6)) || (0 == strcmp(key, "-?"))) { usage(); return 0; - } else if (0 == strncmp(key, "--verb", 6)) { + } else if (0 == strncmp(key, "--prog", 6)) + ++clp->progress; + else if (0 == strncmp(key, "--verb", 6)) { verbose_given = true; ++clp->debug; /* --verbose */ } else if (0 == strncmp(key, "--vers", 6)) @@ -1584,7 +1625,7 @@ main(int argc, char * argv[]) usage(); return SG_LIB_SYNTAX_ERROR; } - if (clp->debug) + if (clp->debug > 2) pr2serr("%sif=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%" PRId64 "\n", my_name, infn, skip, outfn, seek, dd_count); @@ -1806,12 +1847,8 @@ main(int argc, char * argv[]) clp->out_rem_count = dd_count; clp->seek = seek; clp->out_blk = seek; - status = pthread_mutex_init(&clp->in_mutex, NULL); - if (0 != status) err_exit(status, "init in_mutex"); - status = pthread_mutex_init(&clp->out_mutex, NULL); - if (0 != status) err_exit(status, "init out_mutex"); - status = pthread_mutex_init(&clp->aux_mutex, NULL); - if (0 != status) err_exit(status, "init aux_mutex"); + status = pthread_mutex_init(&clp->inout_mutex, NULL); + if (0 != status) err_exit(status, "init inout_mutex"); status = pthread_cond_init(&clp->out_sync_cv, NULL); if (0 != status) err_exit(status, "init out_sync_cv"); @@ -1836,9 +1873,11 @@ main(int argc, char * argv[]) /* vvvvvvvvvvv Start worker threads vvvvvvvvvvvvvvvvvvvvvvvv */ if ((clp->out_rem_count > 0) && (clp->num_threads > 0)) { /* Run 1 work thread to shake down infant retryable stuff */ - status = pthread_mutex_lock(&clp->out_mutex); + status = pthread_mutex_lock(&clp->inout_mutex); if (0 != status) err_exit(status, "lock out_mutex"); + seek_skip = clp->seek - clp->skip; thr_arg_a[0].id = 0; + thr_arg_a[0].seek_skip = seek_skip; thr_arg_a[0].clp = clp; status = pthread_create(&threads[0], NULL, read_write_thread, (void *)(thr_arg_a + 0)); @@ -1848,16 +1887,17 @@ main(int argc, char * argv[]) /* wait for any broadcast */ pthread_cleanup_push(cleanup_out, (void *)clp); - status = pthread_cond_wait(&clp->out_sync_cv, &clp->out_mutex); + status = pthread_cond_wait(&clp->out_sync_cv, &clp->inout_mutex); if (0 != status) err_exit(status, "cond out_sync_cv"); pthread_cleanup_pop(0); - status = pthread_mutex_unlock(&clp->out_mutex); + status = pthread_mutex_unlock(&clp->inout_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); /* now start the rest of the threads */ for (k = 1; k < clp->num_threads; ++k) { thr_arg_a[k].id = k; + thr_arg_a[k].seek_skip = seek_skip; thr_arg_a[k].clp = clp; status = pthread_create(&threads[k], NULL, read_write_thread, (void *)(thr_arg_a + k)); @@ -1943,5 +1983,17 @@ main(int argc, char * argv[]) if (clp->sum_of_resids) pr2serr(">> Non-zero sum of residual counts=%d\n", clp->sum_of_resids); +#ifdef HAVE_C11_ATOMICS + { + unsigned int ui; + + if ((ui = atomic_load(&num_eagain)) > 0) + pr2serr(">> number of IO call yielding EAGAIN %u\n", ui); + if ((ui = atomic_load(&num_ebusy)) > 0) + pr2serr(">> number of IO call yielding EBUSY %u\n", ui); + if ((ui = atomic_load(&num_eintr)) > 0) + pr2serr(">> number of IO call yielding EINTR %u\n", ui); + } +#endif return (res >= 0) ? res : SG_LIB_CAT_OTHER; } diff --git a/testing/sg_mrq_dd.cpp b/testing/sg_mrq_dd.cpp index b554614a..bb6b3a03 100644 --- a/testing/sg_mrq_dd.cpp +++ b/testing/sg_mrq_dd.cpp @@ -30,7 +30,7 @@ * */ -static const char * version_str = "1.36 20210906"; +static const char * version_str = "1.38 20211028"; #define _XOPEN_SOURCE 600 #ifndef _GNU_SOURCE @@ -46,6 +46,7 @@ static const char * version_str = "1.36 20210906"; #include #include #include +#include /* for nanosleep() */ #include #include // #include @@ -69,7 +70,8 @@ static const char * version_str = "1.36 20210906"; #include #include // needed for std::this_thread::yield() #include -#include +#include // for infant_cv: copy/verify first segment + // single threaded #include #ifdef HAVE_CONFIG_H @@ -286,8 +288,8 @@ struct global_collection /* one instance visible to all threads */ atomic most_recent_pack_id; uint32_t sdt_ict; /* stall detection; initial check time (milliseconds) */ uint32_t sdt_crt; /* check repetition time (seconds), after first stall */ - int verbose; int dry_run; + int verbose; bool mrq_eq_0; /* true when user gives mrq=0 */ bool processed; bool cdbsz_given; @@ -319,6 +321,7 @@ typedef struct request_element bool stop_after_write; bool stop_now; int id; + int bs; int infd; int outfd; int outregfd; @@ -419,6 +422,7 @@ static struct global_collection gcoll; static struct timeval start_tm; static int num_threads = DEF_NUM_THREADS; static bool after1 = false; +static int listen_t_tid; static const char * my_name = "sg_mrq_dd: "; @@ -576,6 +580,8 @@ usage(int pg_num) "below:\n\n" " 00 use all zeros instead of if=IFILE (only in " "iflag)\n" + " 00,ff generates blocks that contain own (32 bit be) " + "blk addr\n" " append append output to OFILE (assumes OFILE is " "regular file)\n" " coe continue of error (reading, fills with zeros)\n" @@ -1051,7 +1057,7 @@ interrupt_handler(int sig) if (do_time > 0) calc_duration_throughput(0); print_stats(""); - kill(getpid (), sig); + kill(getpid(), sig); } static void @@ -1388,11 +1394,11 @@ static int scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) { int res; - uint8_t rcBuff[RCAP16_REPLY_LEN]; + uint8_t rcBuff[RCAP16_REPLY_LEN] = {}; res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, false, 0); if (0 != res) - return res; + goto bad; if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) && (0xff == rcBuff[3])) { @@ -1400,7 +1406,7 @@ scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, false, 0); if (0 != res) - return res; + goto bad; *num_sect = sg_get_unaligned_be64(rcBuff + 0) + 1; *sect_sz = sg_get_unaligned_be32(rcBuff + 8); } else { @@ -1409,6 +1415,10 @@ scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) *sect_sz = sg_get_unaligned_be32(rcBuff + 4); } return 0; +bad: + *num_sect = 0; + *sect_sz = 0; + return res; } /* Return of 0 -> success, -1 -> failure. BLKGETSIZE64, BLKGETSIZE and */ @@ -1476,6 +1486,7 @@ sig_listen_thread(struct global_collection * clp) tsp->tv_sec = ict_ms / 1000; tsp->tv_nsec = (ict_ms % 1000) * 1000 * 1000; /* DEF_SDT_ICT_MS */ + listen_t_tid = gettid(); // to facilitate sending SIGUSR2 to exit while (1) { sig_number = sigtimedwait(&signal_set, NULL, tsp); if (sig_number < 0) { @@ -1508,9 +1519,11 @@ sig_listen_thread(struct global_collection * clp) raise(SIGINT); break; } - if (SIGUSR2 == sig_number) + if (SIGUSR2 == sig_number) { + if (clp->verbose > 2) + pr2serr_lk("%s: SIGUSR2 received\n", __func__); break; - if (shutting_down) + } if (shutting_down) break; } /* end of while loop */ if (clp->verbose > 3) @@ -1589,6 +1602,7 @@ read_write_thread(struct global_collection * clp, int thr_idx, int slice_idx, out_mmap = (out_is_sg && (clp->out_flags.mmap > 0)); rep->clp = clp; rep->id = thr_idx; + rep->bs = clp->bs; if (in_is_sg && out_is_sg) rep->both_sg = true; @@ -1894,14 +1908,22 @@ normal_in_rd(Rq_elem * rep, int64_t lba, int blocks, int d_boff) long rn; uint8_t * bp; - if (clp->in_flags.zero) - memset(rep->buffp + d_boff, 0, blocks * clp->bs); + if (clp->in_flags.zero && clp->in_flags.ff && (rep->bs >= 4)) { + uint32_t pos = (uint32_t)lba; + uint off; + + for (k = 0, off = 0; k < blocks; ++k, off += rep->bs, ++pos) { + for (j = 0; j < (rep->bs - 3); j += 4) + sg_put_unaligned_be32(pos, rep->buffp + off + j); + } + } else if (clp->in_flags.zero) + memset(rep->buffp + d_boff, 0, blocks * rep->bs); else if (clp->in_flags.ff) - memset(rep->buffp + d_boff, 0xff, blocks * clp->bs); + memset(rep->buffp + d_boff, 0xff, blocks * rep->bs); else { bp = rep->buffp + d_boff; - for (k = 0; k < blocks; ++k, bp += clp->bs) { - for (j = 0; j < clp->bs; j += jbump) { + for (k = 0; k < blocks; ++k, bp += rep->bs) { + for (j = 0; j < rep->bs; j += jbump) { /* mrand48 takes uniformly from [-2^31, 2^31) */ #ifdef HAVE_SRAND48_R mrand48_r(&rep->drand, &rn); @@ -1916,7 +1938,7 @@ normal_in_rd(Rq_elem * rep, int64_t lba, int blocks, int d_boff) } if (clp->in_type != FT_FIFO) { - int64_t pos = lba * clp->bs; + int64_t pos = lba * rep->bs; if (rep->in_follow_on != pos) { if (lseek64(rep->infd, pos, SEEK_SET) < 0) { @@ -1929,18 +1951,18 @@ normal_in_rd(Rq_elem * rep, int64_t lba, int blocks, int d_boff) } } bp = rep->buffp + d_boff; - while (((res = read(rep->infd, bp, blocks * clp->bs)) < 0) && + while (((res = read(rep->infd, bp, blocks * rep->bs)) < 0) && ((EINTR == errno) || (EAGAIN == errno))) std::this_thread::yield();/* another thread may be able to progress */ if (res < 0) { err = errno; if (clp->in_flags.coe) { - memset(bp, 0, blocks * clp->bs); + memset(bp, 0, blocks * rep->bs); pr2serr_lk("[%d] %s : >> substituted zeros for in blk=%" PRId64 " for %d bytes, %s\n", id, __func__, lba, - blocks * clp->bs, + blocks * rep->bs, tsafe_strerror(err, strerr_buff)); - res = blocks * clp->bs; + res = blocks * rep->bs; } else { pr2serr_lk("[%d] %s: error in normal read, %s\n", id, __func__, tsafe_strerror(err, strerr_buff)); @@ -1948,11 +1970,11 @@ normal_in_rd(Rq_elem * rep, int64_t lba, int blocks, int d_boff) } } rep->in_follow_on += res; - if (res < blocks * clp->bs) { - blocks = res / clp->bs; - if ((res % clp->bs) > 0) { + if (res < blocks * rep->bs) { + blocks = res / rep->bs; + if ((res % rep->bs) > 0) { rep->in_local_partial++; - rep->in_resid_bytes = res % clp->bs; + rep->in_resid_bytes = res % rep->bs; } } return blocks; @@ -1974,7 +1996,7 @@ normal_out_wr(Rq_elem * rep, int64_t lba, int blocks, int d_boff) __func__, lba, blocks, d_boff); if (clp->in_type != FT_FIFO) { - int64_t pos = lba * clp->bs; + int64_t pos = lba * rep->bs; if (rep->out_follow_on != pos) { if (lseek64(rep->outfd, pos, SEEK_SET) < 0) { @@ -1986,7 +2008,7 @@ normal_out_wr(Rq_elem * rep, int64_t lba, int blocks, int d_boff) rep->out_follow_on = pos; } } - while (((res = write(rep->outfd, bp, blocks * clp->bs)) + while (((res = write(rep->outfd, bp, blocks * rep->bs)) < 0) && ((EINTR == errno) || (EAGAIN == errno))) std::this_thread::yield();/* another thread may be able to progress */ if (res < 0) { @@ -1994,8 +2016,8 @@ normal_out_wr(Rq_elem * rep, int64_t lba, int blocks, int d_boff) if (clp->out_flags.coe) { pr2serr_lk("[%d] %s: >> ignored error for out lba=%" PRId64 " for %d bytes, %s\n", id, __func__, lba, - blocks * clp->bs, tsafe_strerror(err, strerr_buff)); - res = blocks * clp->bs; + blocks * rep->bs, tsafe_strerror(err, strerr_buff)); + res = blocks * rep->bs; } else { pr2serr_lk("[%d] %s: error normal write, %s\n", id, __func__, @@ -2004,9 +2026,9 @@ normal_out_wr(Rq_elem * rep, int64_t lba, int blocks, int d_boff) } } rep->out_follow_on += res; - if (res < blocks * clp->bs) { - blocks = res / clp->bs; - if ((res % clp->bs) > 0) { + if (res < blocks * rep->bs) { + blocks = res / rep->bs; + if ((res % rep->bs) > 0) { blocks++; rep->out_local_partial++; } @@ -2236,12 +2258,12 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, cat = SG_LIB_CAT_OTHER; if (ok && f1) { ++n_good; - if (a_v4p->dout_xfer_len >= (uint32_t)clp->bs) + if (a_v4p->dout_xfer_len >= (uint32_t)rep->bs) good_outblks += (a_v4p->dout_xfer_len - a_v4p->dout_resid) / - clp->bs; - if (a_v4p->din_xfer_len >= (uint32_t)clp->bs) + rep->bs; + if (a_v4p->din_xfer_len >= (uint32_t)rep->bs) good_inblks += (a_v4p->din_xfer_len - a_v4p->din_resid) / - clp->bs; + rep->bs; } if (! ok) { if ((a_v4p->dout_xfer_len > 0) || (! clp->in_flags.coe)) @@ -2333,12 +2355,12 @@ sg_half_segment_mrq0(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr, t_v4p->flags = rflags; t_v4p->request_len = cdbsz; if (is_wr) { - t_v4p->dout_xfer_len = num * clp->bs; - t_v4p->dout_xferp = (uint64_t)(dp + (q_blks * clp->bs)); + t_v4p->dout_xfer_len = num * rep->bs; + t_v4p->dout_xferp = (uint64_t)(dp + (q_blks * rep->bs)); t_v4p->din_xfer_len = 0; } else { - t_v4p->din_xfer_len = num * clp->bs; - t_v4p->din_xferp = (uint64_t)(dp + (q_blks * clp->bs)); + t_v4p->din_xfer_len = num * rep->bs; + t_v4p->din_xferp = (uint64_t)(dp + (q_blks * rep->bs)); t_v4p->dout_xfer_len = 0; } t_v4p->timeout = clp->cmd_timeout; @@ -2457,13 +2479,13 @@ sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr, t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1]; if (is_wr) { rep->a_mrq_dout_blks += num; - t_v4p->dout_xfer_len = num * clp->bs; - t_v4p->dout_xferp = (uint64_t)(dp + (mrq_q_blks * clp->bs)); + t_v4p->dout_xfer_len = num * rep->bs; + t_v4p->dout_xferp = (uint64_t)(dp + (mrq_q_blks * rep->bs)); t_v4p->din_xfer_len = 0; } else { rep->a_mrq_din_blks += num; - t_v4p->din_xfer_len = num * clp->bs; - t_v4p->din_xferp = (uint64_t)(dp + (mrq_q_blks * clp->bs)); + t_v4p->din_xfer_len = num * rep->bs; + t_v4p->din_xferp = (uint64_t)(dp + (mrq_q_blks * rep->bs)); t_v4p->dout_xfer_len = 0; } t_v4p->timeout = clp->cmd_timeout; @@ -2621,7 +2643,7 @@ do_normal_normal_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, kk = min(seg_blks, clp->bpt); num = i_sg_it.linear_for_n_blks(kk); res = normal_in_rd(rep, i_sg_it.current_lba(), num, - d_off * clp->bs); + d_off * rep->bs); if (res < 0) { pr2serr_lk("[%d] %s: normal in failed d_off=%d, err=%d\n", id, __func__, d_off, -res); @@ -2644,7 +2666,7 @@ do_normal_normal_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, kk = min(seg_blks, clp->bpt); num = o_sg_it.linear_for_n_blks(kk); res = normal_out_wr(rep, o_sg_it.current_lba(), num, - d_off * clp->bs); + d_off * rep->bs); if (res < num) { if (res < 0) { pr2serr_lk("[%d] %s: normal out failed d_off=%d, err=%d\n", @@ -2660,7 +2682,7 @@ do_normal_normal_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, } } if (rep->in_resid_bytes > 0) { - res = extra_out_wr(rep, rep->in_resid_bytes, d_off * clp->bs); + res = extra_out_wr(rep, rep->in_resid_bytes, d_off * rep->bs); if (res < 0) pr2serr_lk("[%d] %s: extr out failed d_off=%d, err=%d\n", id, __func__, d_off, -res); @@ -2710,7 +2732,7 @@ do_normal_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, kk = min(seg_blks, clp->bpt); num = i_sg_it.linear_for_n_blks(kk); res = normal_in_rd(rep, i_sg_it.current_lba(), num, - d_off * clp->bs); + d_off * rep->bs); if (res < 0) { pr2serr_lk("[%d] %s: normal in failed d_off=%d, err=%d\n", id, __func__, d_off, -res); @@ -2774,7 +2796,7 @@ do_normal_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, kk = min(seg_blks, clp->bpt); num = o_sg_it.linear_for_n_blks(kk); res = normal_out_wr(rep, o_sg_it.current_lba(), num, - d_off * clp->bs); + d_off * rep->bs); if (res < num) { if (res < 0) { pr2serr_lk("[%d] %s: normal out failed d_off=%d, err=%d\n", @@ -2888,7 +2910,7 @@ do_both_sg_segment_mrq0(Rq_elem * rep, scat_gath_iter & i_sg_it, t_v4p->max_response_len = sizeof(rep->sb); t_v4p->flags = iflags; t_v4p->request_len = cdbsz; - t_v4p->din_xfer_len = num * clp->bs; + t_v4p->din_xfer_len = num * rep->bs; t_v4p->dout_xfer_len = 0; t_v4p->timeout = clp->cmd_timeout; t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off; @@ -2940,7 +2962,7 @@ do_both_sg_segment_mrq0(Rq_elem * rep, scat_gath_iter & i_sg_it, t_v4p->flags = oflags; t_v4p->request_len = cdbsz; t_v4p->din_xfer_len = 0; - t_v4p->dout_xfer_len = num * clp->bs; + t_v4p->dout_xfer_len = num * rep->bs; t_v4p->timeout = clp->cmd_timeout; t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off; clp->most_recent_pack_id.store(t_v4p->request_extra); @@ -3072,7 +3094,7 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, t_v4p->response = (uint64_t)rep->sb; t_v4p->max_response_len = sizeof(rep->sb); t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1]; - t_v4p->din_xfer_len = num * clp->bs; + t_v4p->din_xfer_len = num * rep->bs; rep->a_mrq_din_blks += num; t_v4p->dout_xfer_len = 0; t_v4p->timeout = clp->cmd_timeout; @@ -3100,7 +3122,7 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, t_v4p->max_response_len = sizeof(rep->sb); t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1]; t_v4p->din_xfer_len = 0; - t_v4p->dout_xfer_len = num * clp->bs; + t_v4p->dout_xfer_len = num * rep->bs; rep->a_mrq_dout_blks += num; t_v4p->timeout = clp->cmd_timeout; out_mrq_q_blks += num; @@ -4100,6 +4122,9 @@ do_count_work(struct global_collection * clp, const char * inf, return SG_LIB_CAT_OTHER; } } else if (clp->dd_count != 0) { /* and both input and output are soft */ + int64_t iposs = INT64_MAX; + int64_t oposs = INT64_MAX; + if (clp->dd_count > 0) { if (isglp->sum > clp->dd_count) { pr2serr("%sskip sgl sum [%" PRId64 "] exceeds COUNT\n", @@ -4115,9 +4140,6 @@ do_count_work(struct global_collection * clp, const char * inf, } /* clp->dd_count == SG_COUNT_INDEFINITE */ - int64_t iposs = INT64_MAX; - int64_t oposs = INT64_MAX; - if (in_num_sect > 0) iposs = in_num_sect + isglp->sum - isglp->high_lba_p1; if (out_num_sect > 0) @@ -4166,8 +4188,8 @@ main(int argc, char * argv[]) const char * cc2p; struct global_collection * clp = &gcoll; thread sig_listen_thr; - vector work_thr; - vector listen_thr; + vector work_thr_v; + vector listen_thr_v; char ebuff[EBUFF_SZ]; #if 0 /* SG_LIB_ANDROID */ struct sigaction actions; @@ -4277,7 +4299,10 @@ main(int argc, char * argv[]) } clp->in0fd = STDIN_FILENO; clp->out0fd = STDOUT_FILENO; - if (clp->in_flags.ff) { + if (clp->in_flags.ff && clp->in_flags.zero) { + ccp = ""; + cc2p = "addr_as_data"; + } else if (clp->in_flags.ff) { ccp = "<0xff bytes>"; cc2p = "ff"; } else if (clp->in_flags.random) { @@ -4488,7 +4513,7 @@ main(int argc, char * argv[]) goto fini; } - listen_thr.emplace_back(sig_listen_thread, clp); + listen_thr_v.emplace_back(sig_listen_thread, clp); if (do_time) { start_tm.tv_sec = 0; @@ -4504,14 +4529,14 @@ main(int argc, char * argv[]) cvp.out_fd = clp->out0fd; /* launch "infant" thread to catch early mortality, if any */ - work_thr.emplace_back(read_write_thread, clp, 0, 0, true); + work_thr_v.emplace_back(read_write_thread, clp, 0, 0, true); { unique_lock lk(clp->infant_mut); clp->infant_cv.wait(lk, []{ return gcoll.processed; }); } if (clp->cp_ver_arr[0].next_count_pos.load() < 0) { /* infant thread error-ed out, join with it */ - for (auto & t : work_thr) { + for (auto & t : work_thr_v) { if (t.joinable()) t.join(); } @@ -4520,15 +4545,15 @@ main(int argc, char * argv[]) /* now start the rest of the threads */ for (k = 1; k < num_threads; ++k) - work_thr.emplace_back(read_write_thread, clp, k, + work_thr_v.emplace_back(read_write_thread, clp, k, k % (int)num_slices, false); /* now wait for worker threads to finish */ - for (auto & t : work_thr) { + for (auto & t : work_thr_v) { if (t.joinable()) t.join(); } - } /* started worker threads and hereafter they have all exited */ + } /* worker threads hereafter have all exited */ jump: if (do_time && (start_tm.tv_sec || start_tm.tv_usec)) calc_duration_throughput(0); @@ -4549,15 +4574,22 @@ main(int argc, char * argv[]) } shutting_down = true; - for (auto & t : listen_thr) { + for (auto & t : listen_thr_v) { if (t.joinable()) { t.detach(); - t.~thread(); /* kill listening thread */ + if (listen_t_tid > 0) + kill(listen_t_tid, SIGUSR2); + // t.~thread(); /* kill listening thread; doesn't work */ + } + std::this_thread::yield(); // not enough it seems + { /* allow time for SIGUSR2 signal to get through */ + struct timespec tspec = {0, 400000}; /* 400 usecs */ + + nanosleep(&tspec, NULL); } } fini: - if ((STDIN_FILENO != clp->in0fd) && (clp->in0fd >= 0)) close(clp->in0fd); if ((STDOUT_FILENO != clp->out0fd) && (FT_DEV_NULL != clp->out_type) && diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp index bc98de43..38ee1d55 100644 --- a/testing/sgh_dd.cpp +++ b/testing/sgh_dd.cpp @@ -36,7 +36,7 @@ * renamed [20181221] */ -static const char * version_str = "2.16 20210906"; +static const char * version_str = "2.18 20211027"; #define _XOPEN_SOURCE 600 #ifndef _GNU_SOURCE @@ -253,6 +253,7 @@ struct global_collection int fail_mask; int verbose; int dry_run; + int chkaddr; bool aen_given; bool cdbsz_given; bool is_mrq_i; @@ -292,6 +293,7 @@ typedef struct request_element bool only_out_sg; // bool mrq_abort_thread_active; int id; + int bs; int infd; int outfd; int out2fd; @@ -509,7 +511,6 @@ usage(int pg_num) " 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" - " --prefetch|-p with verify: do pre-fetch first\n" " --verify|-x do a verify (compare) operation [def: do a " "copy]\n" " --version|-V output version string then exit\n\n" @@ -576,7 +577,12 @@ usage(int pg_num) "close do\n" " file unshare (default)\n" " verbose increase verbosity\n" + " --chkaddr|-c exits if read block does not contain " + "32 bit block\n" + " address, used once only checks first " + "address in block\n" " --dry-run|-d prepare but bypass copy/read\n" + " --prefetch|-p with verify: do pre-fetch first\n" " --verbose|-v increase verbosity of utility\n\n" "Use '-hhh' or '-hhhh' for more information about flags.\n" ); @@ -587,6 +593,8 @@ usage(int pg_num) "below:\n\n" " 00 use all zeros instead of if=IFILE (only in " "iflags)\n" + " 00,ff generates blocks that contain own (32 bit be) " + "blk address\n" " append append output to OFILE (assumes OFILE is " "regular file)\n" " coe continue of error (reading, fills with zeros)\n" @@ -1138,7 +1146,7 @@ static int scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz) { int res; - uint8_t rcBuff[RCAP16_REPLY_LEN]; + uint8_t rcBuff[RCAP16_REPLY_LEN] = {}; res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, false, 0); if (0 != res) @@ -1262,8 +1270,11 @@ sig_listen_thread(void * v_clp) raise(SIGINT); break; } - if (SIGUSR2 == sig_number) + if (SIGUSR2 == sig_number) { + if (clp->verbose > 2) + pr2serr_lk("%s: interrupted by SIGUSR2\n", __func__); break; + } if (shutting_down) break; } /* end of while loop */ @@ -1474,7 +1485,7 @@ read_write_thread(void * v_tip) struct global_collection * clp; Rq_elem rel {}; Rq_elem * rep = &rel; - int n, sz, blocks, status, vb, err, res, wr_blks; + int n, sz, blocks, status, vb, err, res, wr_blks, c_addr; int num_sg = 0; int64_t my_index; volatile bool stop_after_write = false; @@ -1488,7 +1499,9 @@ read_write_thread(void * v_tip) tip = (Thread_info *)v_tip; clp = tip->gcp; vb = clp->verbose; - sz = clp->bpt * clp->bs; + rep->bs = clp->bs; + sz = clp->bpt * rep->bs; + c_addr = clp->chkaddr; in_is_sg = (FT_SG == clp->in_type); in_mmap = (in_is_sg && (clp->in_flags.mmap > 0)); out_is_sg = (FT_SG == clp->out_type); @@ -1664,6 +1677,26 @@ read_write_thread(void * v_tip) status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); } + if (c_addr && (rep->bs > 3)) { + int k, j, off, num; + uint32_t addr = (uint32_t)rep->iblk; + + num = (1 == c_addr) ? 4 : (rep->bs - 3); + for (k = 0, off = 0; k < blocks; ++k, ++addr, off += rep->bs) { + for (j = 0; j < num; j += 4) { + if (addr != sg_get_unaligned_be32(rep->buffp + off + j)) + break; + } + if (j < num) + break; + } + if (k < blocks) { + pr2serr("%s: chkaddr failure at addr=0x%x\n", __func__, addr); + exit_status = SG_LIB_CAT_MISCOMPARE; + ++num_miscompare; + stop_both(clp); + } + } pthread_cleanup_pop(0); ++rep->rep_count; @@ -1705,7 +1738,7 @@ read_write_thread(void * v_tip) pthread_cleanup_push(cleanup_out, (void *)clp); if (rep->outregfd >= 0) { res = write(rep->outregfd, get_buffp(rep), - rep->clp->bs * rep->num_blks); + rep->bs * rep->num_blks); err = errno; if (res < 0) pr2serr_lk("%s: tid=%d: write(outregfd) failed: %s\n", @@ -1856,13 +1889,21 @@ normal_in_rd(Rq_elem * rep, int blocks) long rn; uint8_t * bp; - if (clp->in_flags.zero) - memset(rep->buffp, 0, blocks * clp->bs); + if (clp->in_flags.zero && clp->in_flags.ff && (rep->bs >= 4)) { + uint32_t pos = (uint32_t)rep->iblk; + uint off; + + for (k = 0, off = 0; k < blocks; ++k, off += rep->bs, ++pos) { + for (j = 0; j < (rep->bs - 3); j += 4) + sg_put_unaligned_be32(pos, rep->buffp + off + j); + } + } else if (clp->in_flags.zero) + memset(rep->buffp, 0, blocks * rep->bs); else if (clp->in_flags.ff) - memset(rep->buffp, 0xff, blocks * clp->bs); + memset(rep->buffp, 0xff, blocks * rep->bs); else { - for (k = 0, bp = rep->buffp; k < blocks; ++k, bp += clp->bs) { - for (j = 0; j < clp->bs; j += jbump) { + for (k = 0, bp = rep->buffp; k < blocks; ++k, bp += rep->bs) { + for (j = 0; j < rep->bs; j += jbump) { /* mrand48 takes uniformly from [-2^31, 2^31) */ #ifdef HAVE_SRAND48_R mrand48_r(&rep->drand, &rn); @@ -1877,7 +1918,7 @@ normal_in_rd(Rq_elem * rep, int blocks) return stop_after_write; } if (! same_fds) { /* each has own file pointer, so we need to move it */ - int64_t pos = rep->iblk * clp->bs; + int64_t pos = rep->iblk * rep->bs; if (lseek64(rep->infd, pos, SEEK_SET) < 0) { /* problem if pipe! */ pr2serr_lk("%s: tid=%d: >> lseek64(%" PRId64 "): %s\n", __func__, @@ -1887,19 +1928,19 @@ normal_in_rd(Rq_elem * rep, int blocks) } } /* enters holding in_mutex */ - while (((res = read(clp->infd, rep->buffp, blocks * clp->bs)) < 0) && + while (((res = read(clp->infd, rep->buffp, blocks * rep->bs)) < 0) && ((EINTR == errno) || (EAGAIN == errno))) std::this_thread::yield();/* another thread may be able to progress */ if (res < 0) { char strerr_buff[STRERR_BUFF_LEN + 1]; if (clp->in_flags.coe) { - memset(rep->buffp, 0, rep->num_blks * clp->bs); + memset(rep->buffp, 0, rep->num_blks * rep->bs); pr2serr_lk("tid=%d: >> substituted zeros for in blk=%" PRId64 " for %d bytes, %s\n", rep->id, rep->iblk, - rep->num_blks * clp->bs, + rep->num_blks * rep->bs, tsafe_strerror(errno, strerr_buff)); - res = rep->num_blks * clp->bs; + res = rep->num_blks * rep->bs; } else { pr2serr_lk("tid=%d: error in normal read, %s\n", rep->id, @@ -1908,12 +1949,12 @@ normal_in_rd(Rq_elem * rep, int blocks) return true; } } - if (res < blocks * clp->bs) { + if (res < blocks * rep->bs) { // int o_blocks = blocks; stop_after_write = true; - blocks = res / clp->bs; - if ((res % clp->bs) > 0) { + blocks = res / rep->bs; + if ((res % rep->bs) > 0) { blocks++; clp->in_partial++; } @@ -1938,7 +1979,7 @@ normal_out_wr(Rq_elem * rep, int blocks) if (clp->verbose > 4) pr2serr_lk("%s: tid=%d: oblk=%" PRIu64 ", blocks=%d\n", __func__, rep->id, rep->oblk, blocks); - while (((res = write(clp->outfd, rep->buffp, blocks * clp->bs)) + while (((res = write(clp->outfd, rep->buffp, blocks * rep->bs)) < 0) && ((EINTR == errno) || (EAGAIN == errno))) std::this_thread::yield();/* another thread may be able to progress */ if (res < 0) { @@ -1947,9 +1988,9 @@ normal_out_wr(Rq_elem * rep, int blocks) if (clp->out_flags.coe) { pr2serr_lk("tid=%d: >> ignored error for out blk=%" PRId64 " for %d bytes, %s\n", rep->id, rep->oblk, - rep->num_blks * clp->bs, + rep->num_blks * rep->bs, tsafe_strerror(errno, strerr_buff)); - res = rep->num_blks * clp->bs; + res = rep->num_blks * rep->bs; } else { pr2serr_lk("tid=%d: error normal write, %s\n", rep->id, @@ -1958,9 +1999,9 @@ normal_out_wr(Rq_elem * rep, int blocks) return; } } - if (res < blocks * clp->bs) { - blocks = res / clp->bs; - if ((res % clp->bs) > 0) { + if (res < blocks * rep->bs) { + blocks = res / rep->bs; + if ((res % rep->bs) > 0) { blocks++; clp->out_partial++; } @@ -2095,10 +2136,10 @@ sg_in_rd_cmd(struct global_collection * clp, Rq_elem * rep, stop_both(clp); return; } else { - memset(get_buffp(rep), 0, rep->num_blks * clp->bs); + memset(get_buffp(rep), 0, rep->num_blks * rep->bs); pr2serr_lk("tid=%d: >> substituted zeros for in blk=%" PRId64 " for %d bytes\n", rep->id, rep->iblk, - rep->num_blks * clp->bs); + rep->num_blks * rep->bs); } #if defined(__GNUC__) #if (__GNUC__ >= 7) @@ -2284,7 +2325,7 @@ sg_out_wr_cmd(Rq_elem * rep, mrq_arr_t & def_arr, bool is_wr2, bool prefetch) goto fini; } else pr2serr_lk(">> ignored error for %s blk=%" PRId64 " for %d " - "bytes\n", wr_or_ver, rep->oblk, nblks * clp->bs); + "bytes\n", wr_or_ver, rep->oblk, nblks * rep->bs); #if defined(__GNUC__) #if (__GNUC__ >= 7) __attribute__((fallthrough)); @@ -2420,12 +2461,12 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, } if (ok && f1) { ++n_good; - if (a_v4p->dout_xfer_len >= (uint32_t)clp->bs) + if (a_v4p->dout_xfer_len >= (uint32_t)rep->bs) good_outblks += (a_v4p->dout_xfer_len - a_v4p->dout_resid) / - clp->bs; - if (a_v4p->din_xfer_len >= (uint32_t)clp->bs) + rep->bs; + if (a_v4p->din_xfer_len >= (uint32_t)rep->bs) good_inblks += (a_v4p->din_xfer_len - a_v4p->din_resid) / - clp->bs; + rep->bs; } } /* end of request array scan loop */ if ((n_subm == num_mrq) || (vb < 3)) @@ -2536,12 +2577,12 @@ chk_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, } if (ok) { ++n_good; - if (a_np->dout_xfer_len >= (uint32_t)clp->bs) + if (a_np->dout_xfer_len >= (uint32_t)rep->bs) good_outblks += (a_np->dout_xfer_len - a_np->dout_resid) / - clp->bs; - if (a_np->din_xfer_len >= (uint32_t)clp->bs) + rep->bs; + if (a_np->din_xfer_len >= (uint32_t)rep->bs) good_inblks += (a_np->din_xfer_len - a_np->din_resid) / - clp->bs; + rep->bs; } } if ((n_subm == nrq) || (vb < 3)) @@ -3478,7 +3519,7 @@ sg_finish_io(bool wr, Rq_elem * rep, int pack_id, struct sg_io_extra *xtrp) char ebuff[EBUFF_SZ]; snprintf(ebuff, EBUFF_SZ, "%s blk=%" PRId64, cp, blk); - lk_chk_n_print3(ebuff, hp, false); + lk_chk_n_print3(ebuff, hp, clp->verbose > 1); return res; } } @@ -3554,7 +3595,7 @@ sg_finish_io(bool wr, Rq_elem * rep, int pack_id, struct sg_io_extra *xtrp) snprintf(ebuff, EBUFF_SZ, "%s rq_id=%d, blk=%" PRId64, cp, pack_id, blk); - lk_chk_n_print4(ebuff, h4p, false); + lk_chk_n_print4(ebuff, h4p, clp->verbose > 1); if ((clp->verbose > 4) && h4p->info) pr2serr_lk(" info=0x%x sg_info_check=%d direct=%d " "detaching=%d aborted=%d\n", h4p->info, @@ -4200,6 +4241,9 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp, clp->verbose = sg_get_num(buf); else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) { res = 0; + n = num_chs_in_str(key + 1, keylen - 1, 'c'); + clp->chkaddr += n; + res += n; n = num_chs_in_str(key + 1, keylen - 1, 'd'); clp->dry_run += n; res += n; @@ -4229,8 +4273,10 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp, key); return SG_LIB_SYNTAX_ERROR; } - } else if ((0 == strncmp(key, "--dry-run", 9)) || - (0 == strncmp(key, "--dry_run", 9))) + } else if (0 == strncmp(key, "--chkaddr", 9)) + ++clp->chkaddr; + else if ((0 == strncmp(key, "--dry-run", 9)) || + (0 == strncmp(key, "--dry_run", 9))) ++clp->dry_run; else if ((0 == strncmp(key, "--help", 6)) || (0 == strcmp(key, "-?"))) @@ -4418,18 +4464,20 @@ main(int argc, char * argv[]) out2f[0] = '\0'; outregf[0] = '\0'; fetch_sg_version(); - if (sg_version > 40000) { - clp->in_flags.v4 = true; - clp->out_flags.v4 = true; - if (sg_version >= 40045) - sg_version_ge_40045 = true; - } + if (sg_version >= 40045) + sg_version_ge_40045 = true; res = parse_cmdline_sanity(argc, argv, clp, inf, outf, out2f, outregf); if (SG_LIB_OK_FALSE == res) return 0; if (res) return res; + if (sg_version > 40000) { + if (! clp->in_flags.v3) + clp->in_flags.v4 = true; + if (! clp->out_flags.v3) + clp->out_flags.v4 = true; + } install_handler(SIGINT, interrupt_handler); install_handler(SIGQUIT, interrupt_handler); @@ -4439,7 +4487,10 @@ main(int argc, char * argv[]) clp->infd = STDIN_FILENO; clp->outfd = STDOUT_FILENO; - if (clp->in_flags.ff) { + if (clp->in_flags.ff && clp->in_flags.zero) { + ccp = ""; + cc2p = "addr_as_data"; + } else if (clp->in_flags.ff) { ccp = "<0xff bytes>"; cc2p = "ff"; } else if (clp->in_flags.random) { @@ -4471,6 +4522,9 @@ main(int argc, char * argv[]) return SG_LIB_FILE_ERROR; } else if (FT_SG == clp->in_type) { clp->infd = sg_in_open(clp, inf, NULL, NULL); + if (clp->verbose > 2) + pr2serr("using sg v%c interface on %s\n", + (clp->in_flags.v4 ? '4' : '3'), inf); if (clp->infd < 0) return -clp->infd; } else { @@ -4528,6 +4582,9 @@ main(int argc, char * argv[]) return SG_LIB_FILE_ERROR; } else if (FT_SG == clp->out_type) { clp->outfd = sg_out_open(clp, outf, NULL, NULL); + if (clp->verbose > 2) + pr2serr("using sg v%c interface on %s\n", + (clp->out_flags.v4 ? '4' : '3'), outf); if (clp->outfd < 0) return -clp->outfd; } else if (FT_DEV_NULL == clp->out_type) @@ -4908,13 +4965,23 @@ main(int argc, char * argv[]) } } /* started worker threads and here after they have all exited */ + if (do_time && (start_tm.tv_sec || start_tm.tv_usec)) + calc_duration_throughput(0); + shutting_down = true; + status = pthread_join(sig_listen_thread_id, &vp); + if (0 != status) err_exit(status, "pthread_join"); +#if 0 /* pthread_cancel() has issues and is not supported in Android */ status = pthread_kill(sig_listen_thread_id, SIGUSR2); if (0 != status) err_exit(status, "pthread_kill"); + std::this_thread::yield(); // not enough it seems + { /* allow time for SIGUSR2 signal to get through */ + struct timespec tspec = {0, 400000}; /* 400 usecs */ - if (do_time && (start_tm.tv_sec || start_tm.tv_usec)) - calc_duration_throughput(0); + nanosleep(&tspec, NULL); + } +#endif if (do_sync) { if (FT_SG == clp->out_type) { diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c index 8af8612b..65b2c061 100644 --- a/testing/sgs_dd.c +++ b/testing/sgs_dd.c @@ -84,7 +84,7 @@ #include "sg_unaligned.h" -static const char * version_str = "4.20 20210727"; +static const char * version_str = "4.21 20211006"; static const char * my_name = "sgs_dd"; #ifndef SGV4_FLAG_HIPRI @@ -1201,8 +1201,10 @@ process_flags(const char * arg, struct flags_t * fp) fp->tag = true; else if (0 == strcmp(cp, "v3")) { fp->v3 = true; + fp->v4 = false; fp->given_v3v4 = true; } else if (0 == strcmp(cp, "v4")) { + fp->v3 = false; fp->v4 = true; fp->given_v3v4 = true; } else { @@ -1251,6 +1253,8 @@ main(int argc, char * argv[]) clp->bpt = 0; clp->in_evfd = -1; clp->out_evfd = -1; + clp->iflag.v3 = true; + clp->oflag.v3 = true; inf[0] = '\0'; outf[0] = '\0'; if (argc < 2) { @@ -1288,7 +1292,9 @@ main(int argc, char * argv[]) pr2serr("%s: bad argument to 'iflag='\n", my_name); return SG_LIB_SYNTAX_ERROR; } - } else if (0 == strcmp(key,"no_sig")) { /* default changes */ + } else if (strcmp(key,"mrq") == 0) + ; /* do nothing */ + else if (0 == strcmp(key,"no_sig")) { /* default changes */ clp->no_sig = !!sg_get_num(buf); no_sig_given = true; } else if (0 == strcmp(key,"obs")) @@ -1309,6 +1315,8 @@ main(int argc, char * argv[]) seek = sg_get_num(buf); else if (0 == strcmp(key,"skip")) skip = sg_get_num(buf); + else if (0 == strcmp(key,"time")) + ; /* do nothing */ else if ((0 == strcmp(key,"-V")) || (0 == strcmp(key,"--version"))) { pr2serr("%s: version: %s\n", my_name, version_str); return 0; @@ -1499,9 +1507,11 @@ main(int argc, char * argv[]) } } if ((clp->in_is_sg || clp->out_is_sg) && !clp->iflag.given_v3v4 && - !clp->oflag.given_v3v4 && (clp->debug > 0)) + !clp->oflag.given_v3v4 && (clp->debug > 0)) { + clp->iflag.v3 = true; pr2serr("using sg driver version 3 interface on %s\n", clp->in_is_sg ? inf : outf); + } if (0 == count) return 0;