aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2020-02-21 17:29:14 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2020-02-21 17:29:14 +0000
commit76b39a024cd642d677bda6da434f6f73cc80319e (patch)
tree1ee31ec26e55f8045198ccccd0c09bcd2040281b
parent2c0eaa5dae9285523e2a1c713369e689a85a4c96 (diff)
downloadsg3_utils-76b39a024cd642d677bda6da434f6f73cc80319e.tar.gz
sgp_dd: support memory-mapped IO via mmap flag; sg_rep_zones: add --num= and --wp options
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@842 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog4
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg_rep_zones.823
-rw-r--r--doc/sgp_dd.811
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_dd.c18
-rw-r--r--src/sg_rep_zones.c51
-rw-r--r--src/sgm_dd.c22
-rw-r--r--src/sgp_dd.c366
-rw-r--r--testing/sgh_dd.cpp144
10 files changed, 413 insertions, 230 deletions
diff --git a/ChangeLog b/ChangeLog
index 36a09741..96940c48 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@ Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
directory have their own "man" pages. There is also a sg3_utils man page.
-Changelog for sg3_utils-1.45 [20200210] [svn: r841]
+Changelog for sg3_utils-1.45 [20200220] [svn: r842]
- sg_get_elem_status: new utility [sbc4r16]
- sg_ses: bug: --page= being overridden when --control
and --data= also given; fix
@@ -46,12 +46,14 @@ Changelog for sg3_utils-1.45 [20200210] [svn: r841]
- sg_reassign: for defect list format 6 (vendor
specific) don't try to decode
- sg_rep_zones: expand some fields per zbc2r04
+ - add --num= and --wp options
- sg_verify: correct so issues VERIFY(16)
- add --0 and --ff options and implement
bytchk=3 properly
- sg_write_same: add --ff for 0xff fill
- sg_luns: report new "target commands" w-lun (19-117)
- sg_dd: add --verify support
+ - sgp_dd: support memory-mapped IO via mmap flag
- inhex directory: new, contains ASCII hex files
that can be used with the '--inhex=' option
- sg_lib: add sg_t10_uuid_desig2str()
diff --git a/debian/changelog b/debian/changelog
index 9b57c763..5d080f23 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.45-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Mon, 10 Feb 2020 23:00:00 -0500
+ -- Douglas Gilbert <dgilbert@interlog.com> Wed, 19 Feb 2020 22:00:00 -0500
sg3-utils (1.44-0.1) unstable; urgency=low
diff --git a/doc/sg_rep_zones.8 b/doc/sg_rep_zones.8
index df734d06..29fecd19 100644
--- a/doc/sg_rep_zones.8
+++ b/doc/sg_rep_zones.8
@@ -1,17 +1,18 @@
-.TH SG_REP_ZONES "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.TH SG_REP_ZONES "8" "February 2020" "sg3_utils\-1.45" SG3_UTILS
.SH NAME
sg_rep_zones \- send SCSI REPORT ZONES command
.SH SYNOPSIS
.B sg_rep_zones
-[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-raw\fR]
-[\fI\-\-readonly\fR] [\fI\-\-report=OPT\fR] [\fI\-\-start=LBA\fR]
-[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-num=NUM\fR]
+[\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-report=OPT\fR]
+[\fI\-\-start=LBA\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wp\fR]
+\fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
Sends a SCSI REPORT ZONES command to \fIDEVICE\fR and outputs the data
returned. This command is found in the ZBC draft standard, revision
-4c (zbc\-r04c.pdf).
+5 (zbc\-r05.pdf).
.SH OPTIONS
Arguments to long options are mandatory for short options as well.
.TP
@@ -30,6 +31,11 @@ where \fILEN\fR is the (maximum) response length in bytes. It is placed in
the cdb's "allocation length" field. If not given (or \fILEN\fR is zero)
then 8192 is used. The maximum allowed value of \fILEN\fR is 1048576.
.TP
+\fB\-n\fR, \fB\-\-num\fR=\fINUM\fR
+where \fINUM\fR is the (maximum) number of zone descriptors to print out.
+The default value is zero which is taken to mean print out all zone
+descriptors returned by the REPORT ZONES command.
+.TP
\fB\-p\fR, \fB\-\-partial\fR
set the PARTIAL bit in the cdb.
.TP
@@ -62,6 +68,11 @@ prefixed with '0x' or has a trailing 'h' which indicate hexadecimal.
increase the level of verbosity, (i.e. debug output).
.TP
\fB\-V\fR, \fB\-\-version\fR
+print the write pointer (in hex) only. In the absence of errors, then a hex
+LBA will be printed on each line, one line for each zone. Can be usefully
+combined with the \fI\-\-num=NUM\fR and \fI\-\-start=LBA\fR options.
+.TP
+\fB\-w\fR, \fB\-\-wp\fR
print the version string and then exit.
.SH EXIT STATUS
The exit status of sg_rep_zones is 0 when it is successful. Otherwise see
@@ -71,7 +82,7 @@ Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2014\-2016 Douglas Gilbert
+Copyright \(co 2014\-2020 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/sgp_dd.8 b/doc/sgp_dd.8
index 684e67ff..62a065bf 100644
--- a/doc/sgp_dd.8
+++ b/doc/sgp_dd.8
@@ -1,4 +1,4 @@
-.TH SGP_DD "8" "March 2019" "sg3_utils\-1.45" SG3_UTILS
+.TH SGP_DD "8" "February 2020" "sg3_utils\-1.45" SG3_UTILS
.SH NAME
sgp_dd \- copy data to and from files and devices, especially SCSI
devices
@@ -201,6 +201,13 @@ excl
causes the O_EXCL flag to be added to the open of \fIIFILE\fR and/or
\fIOFILE\fR.
.TP
+mmap
+can only be used in the \fIiflag=FLAGS\fR or the \fIoflag=FLAGS\fR argument
+list but not both. The nominated side of the copy will use memory mapped IO
+based on the mmap(2) system call. The sg driver will remap its DMA
+destination or source buffer into the user space when the mmap(2) system call
+is used on a sg device.
+.TP
fua
causes the FUA (force unit access) bit to be set in SCSI READ and/or WRITE
commands. This only has effect with sg devices. The 6 byte variants
@@ -314,7 +321,7 @@ Written by Douglas Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2000\-2019 Douglas Gilbert
+Copyright \(co 2000\-2020 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/sg3_utils.spec b/sg3_utils.spec
index 1e1a1619..0e8fb015 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -84,7 +84,7 @@ fi
%{_libdir}/*.la
%changelog
-* Mon Feb 10 2020 - dgilbert at interlog dot com
+* Wed Feb 19 2020 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.45
diff --git a/src/sg_dd.c b/src/sg_dd.c
index f3782b2d..55de0930 100644
--- a/src/sg_dd.c
+++ b/src/sg_dd.c
@@ -66,7 +66,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "6.09 20200117";
+static const char * version_str = "6.10 20200216";
#define ME "sg_dd: "
@@ -649,7 +649,7 @@ sg_read_low(int sg_fd, uint8_t * buff, int blocks, int64_t from_block,
sg_print_command_len(rdCmd, ifp->cdbsz);
while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
if (res < 0) {
if (ENOMEM == errno)
@@ -1054,7 +1054,7 @@ sg_write(int sg_fd, uint8_t * buff, int blocks, int64_t to_block,
sg_print_command_len(wrCmd, ofp->cdbsz);
while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
if (res < 0) {
if (ENOMEM == errno)
@@ -2061,7 +2061,8 @@ main(int argc, char * argv[])
}
} else {
while (((res = read(infd, wrkPos, blocks * blk_sz)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) ||
+ (EBUSY == errno)))
;
if (verbose > 2)
pr2serr("read(unix): count=%d, res=%d\n", blocks * blk_sz,
@@ -2089,7 +2090,8 @@ main(int argc, char * argv[])
if (out2f[0]) {
while (((res = write(out2fd, wrkPos, blocks * blk_sz)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) ||
+ (EBUSY == errno)))
;
if (verbose > 2)
pr2serr("write to of2: count=%d, res=%d\n", blocks * blk_sz,
@@ -2215,7 +2217,8 @@ main(int argc, char * argv[])
out_full += blocks; /* act as if written out without error */
else {
while (((res = write(outfd, wrkPos, blocks * blk_sz)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) ||
+ (EBUSY == errno)))
;
if (verbose > 2)
pr2serr("write(unix): count=%d, res=%d\n", blocks * blk_sz,
@@ -2283,7 +2286,8 @@ main(int argc, char * argv[])
else {
/* ... try writing to extend ofile to length prior to error */
while (((res = write(outfd, zeros_buff, penult_blocks * blk_sz))
- < 0) && ((EINTR == errno) || (EAGAIN == errno)))
+ < 0) && ((EINTR == errno) || (EAGAIN == errno) ||
+ (EBUSY == errno)))
;
if (verbose > 2)
pr2serr("write(unix, sparse after error): count=%d, res=%d\n",
diff --git a/src/sg_rep_zones.c b/src/sg_rep_zones.c
index cf390f19..388c4001 100644
--- a/src/sg_rep_zones.c
+++ b/src/sg_rep_zones.c
@@ -38,7 +38,7 @@
* and decodes the response. Based on zbc-r02.pdf
*/
-static const char * version_str = "1.20 20200122";
+static const char * version_str = "1.21 20200220";
#define MAX_RZONES_BUFF_LEN (1024 * 1024)
#define DEF_RZONES_BUFF_LEN (1024 * 8)
@@ -55,6 +55,7 @@ static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"hex", no_argument, 0, 'H'},
{"maxlen", required_argument, 0, 'm'},
+ {"num", required_argument, 0, 'n'},
{"partial", no_argument, 0, 'p'},
{"raw", no_argument, 0, 'r'},
{"readonly", no_argument, 0, 'R'},
@@ -62,6 +63,7 @@ static struct option long_options[] = {
{"start", required_argument, 0, 's'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
+ {"wp", no_argument, 0, 'w'},
{0, 0, 0, 0},
};
@@ -84,6 +86,8 @@ usage(int h)
" --maxlen=LEN|-m LEN max response length (allocation "
"length in cdb)\n"
" (def: 0 -> 8192 bytes)\n"
+ " --num=NUM|-n NUM number of zones to output (def: 0 -> "
+ "all)\n"
" --partial|-p sets PARTIAL bit in cdb (def: 0 -> "
"zone list\n"
" length not altered by allocation length "
@@ -95,7 +99,8 @@ usage(int h)
" --start=LBA|-s LBA report zones from the LBA (def: 0)\n"
" need not be a zone starting LBA\n"
" --verbose|-v increase verbosity\n"
- " --version|-V print version string and exit\n\n"
+ " --version|-V print version string and exit\n"
+ " --wp|-w output write pointer only\n\n"
"Sends a SCSI REPORT ZONES command and decodes the response. "
"Give\nhelp option twice (e.g. '-hh') to see reporting options "
"enumerated.\n");
@@ -291,10 +296,12 @@ main(int argc, char * argv[])
bool o_readonly = false;
bool verbose_given = false;
bool version_given = false;
+ bool wp_only = false;
int k, res, c, zl_len, len, zones, resid, rlen, zt, zc, same;
int sg_fd = -1;
int do_help = 0;
int do_hex = 0;
+ int do_num = 0;
int maxlen = 0;
int reporting_opt = 0;
int ret = 0;
@@ -310,7 +317,7 @@ main(int argc, char * argv[])
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hHm:o:prRs:vV", long_options,
+ c = getopt_long(argc, argv, "hHm:n:o:prRs:vVw", long_options,
&option_index);
if (c == -1)
break;
@@ -331,6 +338,13 @@ main(int argc, char * argv[])
return SG_LIB_SYNTAX_ERROR;
}
break;
+ case 'n':
+ do_num = sg_get_num(optarg);
+ if (do_num < 0) {
+ pr2serr("argument to '--num' should be zero or more\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
case 'o':
reporting_opt = sg_get_num_nomult(optarg);
if ((reporting_opt < 0) || (reporting_opt > 63)) {
@@ -363,6 +377,9 @@ main(int argc, char * argv[])
case 'V':
version_given = true;
break;
+ case 'w':
+ wp_only = true;
+ break;
default:
pr2serr("unrecognised option code 0x%x ??\n", c);
usage(1);
@@ -464,7 +481,8 @@ main(int argc, char * argv[])
((1 == do_hex) ? 1 : -1));
goto the_end;
}
- printf("Report zones response:\n");
+ if (! wp_only)
+ printf("Report zones response:\n");
if (len < 64) {
pr2serr("Zone length [%d] too short (perhaps after truncation\n)",
len);
@@ -472,16 +490,25 @@ main(int argc, char * argv[])
goto the_end;
}
same = reportZonesBuff[4] & 0xf;
- printf(" Same=%d: %s\n", same, same_desc_arr[same]);
- printf(" Maximum LBA: 0x%" PRIx64 "\n\n",
- sg_get_unaligned_be64(reportZonesBuff + 8));
+ if (! wp_only) {
+ printf(" Same=%d: %s\n", same, same_desc_arr[same]);
+ printf(" Maximum LBA: 0x%" PRIx64 "\n\n",
+ sg_get_unaligned_be64(reportZonesBuff + 8));
+ }
zones = (len - 64) / 64;
+ if (do_num > 0)
+ zones = (zones > do_num) ? do_num : zones;
for (k = 0, bp = reportZonesBuff + 64; k < zones; ++k, bp += 64) {
- printf(" Zone descriptor: %d\n", k);
+ if (! wp_only)
+ printf(" Zone descriptor: %d\n", k);
if (do_hex) {
hex2stdout(bp, len, -1);
continue;
}
+ if (wp_only) {
+ printf("0x%" PRIx64 "\n", sg_get_unaligned_be64(bp + 24));
+ continue;
+ }
zt = bp[0] & 0xf;
zc = (bp[1] >> 4) & 0xf;
printf(" Zone type: %s\n", zone_type_str(zt, b, sizeof(b),
@@ -497,9 +524,11 @@ main(int argc, char * argv[])
printf(" Write pointer LBA: 0x%" PRIx64 "\n",
sg_get_unaligned_be64(bp + 24));
}
- if ((64 + (64 * zones)) < zl_len)
- printf("\n>>> Beware: Zone list truncated, may need another "
- "call\n");
+ if ((do_num == 0) && (! wp_only)) {
+ if ((64 + (64 * zones)) < zl_len)
+ printf("\n>>> Beware: Zone list truncated, may need another "
+ "call\n");
+ }
} else if (SG_LIB_CAT_INVALID_OP == res)
pr2serr("Report zones command not supported\n");
else {
diff --git a/src/sgm_dd.c b/src/sgm_dd.c
index d40f7859..3932242b 100644
--- a/src/sgm_dd.c
+++ b/src/sgm_dd.c
@@ -1,7 +1,7 @@
/* A utility program for copying files. Specialised for "files" that
* represent devices that understand the SCSI command set.
*
- * Copyright (C) 1999 - 2019 D. Gilbert and P. Allworth
+ * Copyright (C) 1999 - 2020 D. Gilbert and P. Allworth
* 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)
@@ -69,7 +69,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "1.64 20191226";
+static const char * version_str = "1.65 20200216";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -501,7 +501,7 @@ sg_read(int sg_fd, uint8_t * buff, int blocks, int64_t from_block,
#if 1
while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
sleep(1);
if (res < 0) {
perror(ME "SG_IO error (sg_read)");
@@ -509,7 +509,7 @@ sg_read(int sg_fd, uint8_t * buff, int blocks, int64_t from_block,
}
#else
while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
if (res < 0) {
if (ENOMEM == errno)
@@ -519,7 +519,7 @@ sg_read(int sg_fd, uint8_t * buff, int blocks, int64_t from_block,
}
while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
if (res < 0) {
perror("reading (rd) on sg device, error");
@@ -594,7 +594,7 @@ sg_write(int sg_fd, uint8_t * buff, int blocks, int64_t to_block,
#if 1
while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
sleep(1);
if (res < 0) {
perror(ME "SG_IO error (sg_write)");
@@ -602,7 +602,7 @@ sg_write(int sg_fd, uint8_t * buff, int blocks, int64_t to_block,
}
#else
while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
if (res < 0) {
if (ENOMEM == errno)
@@ -612,7 +612,7 @@ sg_write(int sg_fd, uint8_t * buff, int blocks, int64_t to_block,
}
while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
if (res < 0) {
perror("writing (rd) on sg device, error");
@@ -1326,7 +1326,8 @@ main(int argc, char * argv[])
}
else {
while (((res = read(infd, wrkPos, blocks * blk_sz)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) ||
+ (EBUSY == errno)))
;
if (verbose > 2)
pr2serr("read(unix): count=%d, res=%d\n", blocks * blk_sz,
@@ -1380,7 +1381,8 @@ main(int argc, char * argv[])
out_full += blocks; /* act as if written out without error */
else {
while (((res = write(outfd, wrkPos, blocks * blk_sz)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) ||
+ (EBUSY == errno)))
;
if (verbose > 2)
pr2serr("write(unix): count=%d, res=%d\n", blocks * blk_sz,
diff --git a/src/sgp_dd.c b/src/sgp_dd.c
index d548601a..7f417b96 100644
--- a/src/sgp_dd.c
+++ b/src/sgp_dd.c
@@ -1,7 +1,7 @@
/* A utility program for copying files. Specialised for "files" that
* represent devices that understand the SCSI command set.
*
- * Copyright (C) 1999 - 2019 D. Gilbert and P. Allworth
+ * Copyright (C) 1999 - 2020 D. Gilbert and P. Allworth
* 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)
@@ -48,6 +48,7 @@
#include <signal.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
+#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
@@ -83,7 +84,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "5.73 20190618";
+static const char * version_str = "5.75 20200219";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -119,6 +120,14 @@ static const char * version_str = "5.73 20190618";
#define EBUFF_SZ 768
+#ifndef SG_FLAG_MMAP_IO
+#define SG_FLAG_MMAP_IO 4
+#endif
+
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+
+
struct flags_t {
bool append;
bool coe;
@@ -128,6 +137,7 @@ struct flags_t {
bool dsync;
bool excl;
bool fua;
+ bool mmap;
};
typedef struct request_collection
@@ -157,13 +167,21 @@ typedef struct request_collection
pthread_cond_t out_sync_cv; /* -/ hold writes until "in order" */
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 */
+ bool mmap_active;
int debug;
int dry_run;
} Rq_coll;
+typedef struct thread_arg
+{ /* pointer to this argument passed to thread */
+ int id;
+ Rq_coll * clp;
+} Thread_arg;
+
typedef struct request_element
{ /* one instance per worker thread */
bool wr;
@@ -227,6 +245,7 @@ static int ascending_val = 1;
static pthread_mutex_t strerr_mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_t threads[MAX_NUM_THREADS];
+static Thread_arg thr_arg_a[MAX_NUM_THREADS];
static bool shutting_down = false;
static bool do_sync = false;
@@ -234,8 +253,9 @@ static bool do_time = false;
static Rq_coll rcoll;
static struct timeval start_tm;
static int64_t dd_count = -1;
-static int num_threads = DEF_NUM_THREADS;
static int exit_status = 0;
+static char infn[INOUTF_SZ];
+static char outfn[INOUTF_SZ];
static const char * my_name = "sgp_dd: ";
@@ -412,13 +432,13 @@ usage()
" if file or device to read from (def: stdin)\n"
" iflag comma separated list from: [coe,dio,direct,dpo,"
"dsync,excl,\n"
- " fua, null]\n"
+ " fua,mmap,null]\n"
" of file or device to write to (def: stdout), "
"OFILE of '.'\n"
" treated as /dev/null\n"
" oflag comma separated list from: [append,coe,dio,"
- "direct,dpo,dsync,\n"
- " excl,fua,null]\n"
+ "direct,dpo,\n"
+ " dsync,excl,fua,mmap,null]\n"
" seek block position to start writing to OFILE\n"
" skip block position to start reading from IFILE\n"
" sync 0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
@@ -459,6 +479,32 @@ guarded_stop_both(Rq_coll * clp)
guarded_stop_out(clp);
}
+static int
+sgp_mem_mmap(int fd, int res_sz, uint8_t ** mmpp)
+{
+ int t;
+
+ if (ioctl(fd, SG_GET_RESERVED_SIZE, &t) < 0) {
+ perror("SG_GET_RESERVED_SIZE error");
+ return -1;
+ }
+ if (t < (int)sg_get_page_size())
+ t = sg_get_page_size();
+ if (res_sz > t) {
+ if (ioctl(fd, SG_SET_RESERVED_SIZE, &res_sz) < 0) {
+ perror("SG_SET_RESERVED_SIZE error");
+ return -1;
+ }
+ }
+ *mmpp = (uint8_t *)mmap(NULL, res_sz,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (MAP_FAILED == *mmpp) {
+ perror("mmap() failed");
+ return -1;
+ }
+ return 0;
+}
+
/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
static int
scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
@@ -567,34 +613,134 @@ cleanup_out(void * v_clp)
pthread_cond_broadcast(&clp->out_sync_cv);
}
+static int
+sg_prepare(int fd, int bs, int bpt)
+{
+ int res, t;
+
+ res = ioctl(fd, SG_GET_VERSION_NUM, &t);
+ if ((res < 0) || (t < 30000)) {
+ pr2serr("%ssg driver prior to 3.x.y\n", my_name);
+ return 1;
+ }
+ t = bs * bpt;
+ res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
+ if (res < 0)
+ perror("sgp_dd: SG_SET_RESERVED_SIZE error");
+ t = 1;
+ res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
+ if (res < 0)
+ perror("sgp_dd: SG_SET_FORCE_PACK_ID error");
+ return 0;
+}
+
+static int
+sg_in_open(const char * fnp, struct flags_t * flagp, int bs, int bpt)
+{
+ int flags = O_RDWR;
+ int fd, err;
+ char ebuff[800];
+
+ if (flagp->direct)
+ flags |= O_DIRECT;
+ if (flagp->excl)
+ flags |= O_EXCL;
+ if (flagp->dsync)
+ flags |= O_SYNC;
+
+ if ((fd = open(fnp, flags)) < 0) {
+ err = errno;
+ snprintf(ebuff, EBUFF_SZ, "%scould not open %s for sg "
+ "reading", my_name, fnp);
+ perror(ebuff);
+ return -sg_convert_errno(err);
+ }
+ if (sg_prepare(fd, bs, bpt))
+ return -SG_LIB_FILE_ERROR;
+ return fd;
+}
+
+static int
+sg_out_open(const char * fnp, struct flags_t * flagp, int bs, int bpt)
+{
+ int flags = O_RDWR;
+ int fd, err;
+ char ebuff[800];
+
+ if (flagp->direct)
+ flags |= O_DIRECT;
+ if (flagp->excl)
+ flags |= O_EXCL;
+ if (flagp->dsync)
+ flags |= O_SYNC;
+
+ if ((fd = open(fnp, flags)) < 0) {
+ err = errno;
+ snprintf(ebuff, EBUFF_SZ, "%scould not open %s for sg "
+ "writing", my_name, fnp);
+ perror(ebuff);
+ return -sg_convert_errno(err);
+ }
+ if (sg_prepare(fd, bs, bpt))
+ return -SG_LIB_FILE_ERROR;
+ return fd;
+}
+
static void *
-read_write_thread(void * v_clp)
+read_write_thread(void * v_tap)
{
+ Thread_arg * tap;
Rq_coll * clp;
Rq_elem rel;
Rq_elem * rep = &rel;
int sz;
volatile bool stop_after_write = false;
int64_t seek_skip;
- int blocks, status;
+ int blocks, status, id;
- clp = (Rq_coll *)v_clp;
+ tap = (Thread_arg *)v_tap;
+ id = tap->id;
+ clp = tap->clp;
sz = clp->bpt * clp->bs;
seek_skip = clp->seek - clp->skip;
memset(rep, 0, sizeof(Rq_elem));
- rep->buffp = sg_memalign(sz, 0 /* page align */, &rep->alloc_bp, false);
- if (NULL == rep->buffp)
- err_exit(ENOMEM, "out of memory creating user buffers\n");
-
/* Following clp members are constant during lifetime of thread */
rep->bs = clp->bs;
- rep->infd = clp->infd;
- rep->outfd = clp->outfd;
+ 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);
+ 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,
+ clp->bpt);
+ if (rep->outfd < 0) err_exit(-rep->outfd, "error opening outfn");
+
+ } else
+ rep->outfd = clp->outfd;
+ } else {
+ rep->infd = clp->infd;
+ rep->outfd = clp->outfd;
+ }
rep->debug = clp->debug;
rep->cdbsz_in = clp->cdbsz_in;
rep->cdbsz_out = clp->cdbsz_out;
rep->in_flags = clp->in_flags;
rep->out_flags = clp->out_flags;
+ if (clp->mmap_active) {
+ int fd = clp->in_flags.mmap ? rep->infd : rep->outfd;
+
+pr2serr("%s: id=%d, fd=%d calling sgp_mem_mmap()\n", __func__, id, fd);
+ status = sgp_mem_mmap(fd, sz, &rep->buffp);
+ if (status) err_exit(status, "sgp_mem_mmap() failed");
+ } else {
+ rep->buffp = sg_memalign(sz, 0 /* page align */, &rep->alloc_bp,
+ false);
+ if (NULL == rep->buffp)
+ err_exit(ENOMEM, "out of memory creating user buffers\n");
+ }
while(1) {
status = pthread_mutex_lock(&clp->in_mutex);
@@ -697,7 +843,7 @@ normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
char strerr_buff[STRERR_BUFF_LEN];
/* enters holding in_mutex */
- while (((res = read(clp->infd, rep->buffp, blocks * clp->bs)) < 0) &&
+ while (((res = read(rep->infd, rep->buffp, blocks * clp->bs)) < 0) &&
((EINTR == errno) || (EAGAIN == errno)))
;
if (res < 0) {
@@ -743,7 +889,7 @@ normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
char strerr_buff[STRERR_BUFF_LEN];
/* enters holding out_mutex */
- while (((res = write(clp->outfd, rep->buffp, rep->num_blks * clp->bs))
+ while (((res = write(rep->outfd, rep->buffp, rep->num_blks * clp->bs))
< 0) && ((EINTR == errno) || (EAGAIN == errno)))
;
if (res < 0) {
@@ -999,6 +1145,7 @@ sg_start_io(Rq_elem * rep)
bool fua = rep->wr ? rep->out_flags.fua : rep->in_flags.fua;
bool dpo = rep->wr ? rep->out_flags.dpo : rep->in_flags.dpo;
bool dio = rep->wr ? rep->out_flags.dio : rep->in_flags.dio;
+ bool mmap = rep->wr ? rep->out_flags.mmap : rep->in_flags.mmap;
int cdbsz = rep->wr ? rep->cdbsz_out : rep->cdbsz_in;
int res;
@@ -1014,7 +1161,7 @@ sg_start_io(Rq_elem * rep)
hp->cmdp = rep->cmd;
hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
hp->dxfer_len = rep->bs * rep->num_blks;
- hp->dxferp = rep->buffp;
+ hp->dxferp = mmap ? NULL : rep->buffp;
hp->mx_sb_len = sizeof(rep->sb);
hp->sbp = rep->sb;
hp->timeout = DEF_TIMEOUT;
@@ -1023,6 +1170,8 @@ sg_start_io(Rq_elem * rep)
hp->pack_id = (int)rep->pack_id;
if (dio)
hp->flags |= SG_FLAG_DIRECT_IO;
+ 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);
@@ -1031,7 +1180,7 @@ sg_start_io(Rq_elem * rep)
while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
sizeof(struct sg_io_hdr))) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
if (res < 0) {
if (ENOMEM == errno)
@@ -1063,7 +1212,7 @@ sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr,
sizeof(struct sg_io_hdr))) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
if (res < 0) {
perror("finishing io on sg device, error");
@@ -1117,27 +1266,6 @@ sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
}
static int
-sg_prepare(int fd, int bs, int bpt)
-{
- int res, t;
-
- res = ioctl(fd, SG_GET_VERSION_NUM, &t);
- if ((res < 0) || (t < 30000)) {
- pr2serr("%ssg driver prior to 3.x.y\n", my_name);
- return 1;
- }
- t = bs * bpt;
- res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
- if (res < 0)
- perror("sgp_dd: SG_SET_RESERVED_SIZE error");
- t = 1;
- res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
- if (res < 0)
- perror("sgp_dd: SG_SET_FORCE_PACK_ID error");
- return 0;
-}
-
-static int
process_flags(const char * arg, struct flags_t * fp)
{
char buff[256];
@@ -1171,6 +1299,8 @@ process_flags(const char * arg, struct flags_t * fp)
fp->excl = true;
else if (0 == strcmp(cp, "fua"))
fp->fua = true;
+ else if (0 == strcmp(cp, "mmap"))
+ fp->mmap = true;
else if (0 == strcmp(cp, "null"))
;
else {
@@ -1197,9 +1327,6 @@ num_chs_in_str(const char * s, int slen, int ch)
}
-#define STR_SZ 1024
-#define INOUTF_SZ 512
-
int
main(int argc, char * argv[])
{
@@ -1214,8 +1341,6 @@ main(int argc, char * argv[])
char str[STR_SZ];
char * key;
char * buf;
- char inf[INOUTF_SZ];
- char outf[INOUTF_SZ];
int res, k, err, keylen;
int64_t in_num_sect = 0;
int64_t out_num_sect = 0;
@@ -1233,13 +1358,14 @@ main(int argc, char * argv[])
sigaction(SIGUSR1, &actions, NULL);
#endif
memset(clp, 0, sizeof(*clp));
+ clp->num_threads = DEF_NUM_THREADS;
clp->bpt = DEF_BLOCKS_PER_TRANSFER;
clp->in_type = FT_OTHER;
clp->out_type = FT_OTHER;
clp->cdbsz_in = DEF_SCSI_CDBSZ;
clp->cdbsz_out = DEF_SCSI_CDBSZ;
- inf[0] = '\0';
- outf[0] = '\0';
+ infn[0] = '\0';
+ outfn[0] = '\0';
for (k = 1; k < argc; k++) {
if (argv[k]) {
@@ -1300,12 +1426,12 @@ main(int argc, char * argv[])
return SG_LIB_SYNTAX_ERROR;
}
} else if (strcmp(key,"if") == 0) {
- if ('\0' != inf[0]) {
+ if ('\0' != infn[0]) {
pr2serr("Second 'if=' argument??\n");
return SG_LIB_SYNTAX_ERROR;
} else {
- memcpy(inf, buf, INOUTF_SZ);
- inf[INOUTF_SZ - 1] = '\0';
+ memcpy(infn, buf, INOUTF_SZ);
+ infn[INOUTF_SZ - 1] = '\0';
}
} else if (0 == strcmp(key, "iflag")) {
if (process_flags(buf, &clp->in_flags)) {
@@ -1319,12 +1445,12 @@ main(int argc, char * argv[])
return SG_LIB_SYNTAX_ERROR;
}
} else if (strcmp(key,"of") == 0) {
- if ('\0' != outf[0]) {
+ if ('\0' != outfn[0]) {
pr2serr("Second 'of=' argument??\n");
return SG_LIB_SYNTAX_ERROR;
} else {
- memcpy(outf, buf, INOUTF_SZ);
- outf[INOUTF_SZ - 1] = '\0';
+ memcpy(outfn, buf, INOUTF_SZ);
+ outfn[INOUTF_SZ - 1] = '\0';
}
} else if (0 == strcmp(key, "oflag")) {
if (process_flags(buf, &clp->out_flags)) {
@@ -1346,7 +1472,7 @@ main(int argc, char * argv[])
} else if (0 == strcmp(key,"sync"))
do_sync = !! sg_get_num(buf);
else if (0 == strcmp(key,"thr"))
- num_threads = sg_get_num(buf);
+ clp->num_threads = sg_get_num(buf);
else if (0 == strcmp(key,"time"))
do_time = !! sg_get_num(buf);
else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
@@ -1436,19 +1562,24 @@ main(int argc, char * argv[])
pr2serr("bpt must be greater than 0\n");
return SG_LIB_SYNTAX_ERROR;
}
+ if (clp->in_flags.mmap && clp->out_flags.mmap) {
+ pr2serr("can only use mmap flag in iflag= or oflag=, not both\n");
+ return SG_LIB_SYNTAX_ERROR;
+ } else if (clp->in_flags.mmap || clp->out_flags.mmap)
+ clp->mmap_active = true;
/* defaulting transfer size to 128*2048 for CD/DVDs is too large
for the block layer in lk 2.6 and results in an EIO on the
SG_IO ioctl. So reduce it in that case. */
if ((clp->bs >= 2048) && (0 == bpt_given))
clp->bpt = DEF_BLOCKS_PER_2048TRANSFER;
- if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) {
+ if ((clp->num_threads < 1) || (clp->num_threads > MAX_NUM_THREADS)) {
pr2serr("too few or too many threads requested\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
if (clp->debug)
pr2serr("%sif=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%"
- PRId64 "\n", my_name, inf, skip, outf, seek, dd_count);
+ PRId64 "\n", my_name, infn, skip, outfn, seek, dd_count);
install_handler(SIGINT, interrupt_handler);
install_handler(SIGQUIT, interrupt_handler);
@@ -1457,33 +1588,19 @@ main(int argc, char * argv[])
clp->infd = STDIN_FILENO;
clp->outfd = STDOUT_FILENO;
- if (inf[0] && ('-' != inf[0])) {
- clp->in_type = dd_filetype(inf);
+ if (infn[0] && ('-' != infn[0])) {
+ clp->in_type = dd_filetype(infn);
if (FT_ERROR == clp->in_type) {
- pr2serr("%sunable to access %s\n", my_name, inf);
+ pr2serr("%sunable to access %s\n", my_name, infn);
return SG_LIB_FILE_ERROR;
} else if (FT_ST == clp->in_type) {
- pr2serr("%sunable to use scsi tape device %s\n", my_name, inf);
+ pr2serr("%sunable to use scsi tape device %s\n", my_name, infn);
return SG_LIB_FILE_ERROR;
} else if (FT_SG == clp->in_type) {
- flags = O_RDWR;
- if (clp->in_flags.direct)
- flags |= O_DIRECT;
- if (clp->in_flags.excl)
- flags |= O_EXCL;
- if (clp->in_flags.dsync)
- flags |= O_SYNC;
-
- if ((clp->infd = open(inf, flags)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%scould not open %s for sg "
- "reading", my_name, inf);
- perror(ebuff);
- return sg_convert_errno(err);
- }
- if (sg_prepare(clp->infd, clp->bs, clp->bpt))
- return SG_LIB_FILE_ERROR;
+ clp->infd = sg_in_open(infn, &clp->in_flags, clp->bs, clp->bpt);
+ if (clp->infd < 0)
+ return -clp->infd;
}
else {
flags = O_RDONLY;
@@ -1494,55 +1611,39 @@ main(int argc, char * argv[])
if (clp->in_flags.dsync)
flags |= O_SYNC;
- if ((clp->infd = open(inf, flags)) < 0) {
+ if ((clp->infd = open(infn, flags)) < 0) {
err = errno;
snprintf(ebuff, EBUFF_SZ, "%scould not open %s for reading",
- my_name, inf);
+ my_name, infn);
perror(ebuff);
return sg_convert_errno(err);
}
else if (skip > 0) {
off64_t offset = skip;
- offset *= clp->bs; /* could exceed 32 here! */
+ offset *= clp->bs; /* could exceed 32 bits here! */
if (lseek64(clp->infd, offset, SEEK_SET) < 0) {
err = errno;
snprintf(ebuff, EBUFF_SZ, "%scouldn't skip to required "
- "position on %s", my_name, inf);
+ "position on %s", my_name, infn);
perror(ebuff);
return sg_convert_errno(err);
}
}
}
}
- if (outf[0] && ('-' != outf[0])) {
- clp->out_type = dd_filetype(outf);
+ if (outfn[0] && ('-' != outfn[0])) {
+ clp->out_type = dd_filetype(outfn);
if (FT_ST == clp->out_type) {
- pr2serr("%sunable to use scsi tape device %s\n", my_name, outf);
+ pr2serr("%sunable to use scsi tape device %s\n", my_name, outfn);
return SG_LIB_FILE_ERROR;
- }
- else if (FT_SG == clp->out_type) {
- flags = O_RDWR;
- if (clp->out_flags.direct)
- flags |= O_DIRECT;
- if (clp->out_flags.excl)
- flags |= O_EXCL;
- if (clp->out_flags.dsync)
- flags |= O_SYNC;
-
- if ((clp->outfd = open(outf, flags)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%scould not open %s for sg "
- "writing", my_name, outf);
- perror(ebuff);
- return sg_convert_errno(err);
- }
-
- if (sg_prepare(clp->outfd, clp->bs, clp->bpt))
- return SG_LIB_FILE_ERROR;
- }
- else if (FT_DEV_NULL == clp->out_type)
+ } else if (FT_SG == clp->out_type) {
+ clp->outfd = sg_out_open(outfn, &clp->out_flags, clp->bs,
+ clp->bpt);
+ if (clp->outfd < 0)
+ return -clp->outfd;
+ } else if (FT_DEV_NULL == clp->out_type)
clp->outfd = -1; /* don't bother opening */
else {
if (FT_RAW != clp->out_type) {
@@ -1556,19 +1657,19 @@ main(int argc, char * argv[])
if (clp->out_flags.append)
flags |= O_APPEND;
- if ((clp->outfd = open(outf, flags, 0666)) < 0) {
+ if ((clp->outfd = open(outfn, flags, 0666)) < 0) {
err = errno;
snprintf(ebuff, EBUFF_SZ, "%scould not open %s for "
- "writing", my_name, outf);
+ "writing", my_name, outfn);
perror(ebuff);
return sg_convert_errno(err);
}
}
else { /* raw output file */
- if ((clp->outfd = open(outf, O_WRONLY)) < 0) {
+ if ((clp->outfd = open(outfn, O_WRONLY)) < 0) {
err = errno;
snprintf(ebuff, EBUFF_SZ, "%scould not open %s for raw "
- "writing", my_name, outf);
+ "writing", my_name, outfn);
perror(ebuff);
return sg_convert_errno(err);
}
@@ -1580,7 +1681,7 @@ main(int argc, char * argv[])
if (lseek64(clp->outfd, offset, SEEK_SET) < 0) {
err = errno;
snprintf(ebuff, EBUFF_SZ, "%scouldn't seek to required "
- "position on %s", my_name, outf);
+ "position on %s", my_name, outfn);
perror(ebuff);
return sg_convert_errno(err);
}
@@ -1603,22 +1704,22 @@ main(int argc, char * argv[])
}
if (0 != res) {
if (res == SG_LIB_CAT_INVALID_OP)
- pr2serr("read capacity not supported on %s\n", inf);
+ pr2serr("read capacity not supported on %s\n", infn);
else if (res == SG_LIB_CAT_NOT_READY)
- pr2serr("read capacity failed, %s not ready\n", inf);
+ pr2serr("read capacity failed, %s not ready\n", infn);
else
- pr2serr("Unable to read capacity on %s\n", inf);
+ pr2serr("Unable to read capacity on %s\n", infn);
in_num_sect = -1;
}
} else if (FT_BLOCK == clp->in_type) {
if (0 != read_blkdev_capacity(clp->infd, &in_num_sect,
&in_sect_sz)) {
- pr2serr("Unable to read block capacity on %s\n", inf);
+ pr2serr("Unable to read block capacity on %s\n", infn);
in_num_sect = -1;
}
if (clp->bs != in_sect_sz) {
pr2serr("logical block size on %s confusion; bs=%d, from "
- "device=%d\n", inf, clp->bs, in_sect_sz);
+ "device=%d\n", infn, clp->bs, in_sect_sz);
in_num_sect = -1;
}
}
@@ -1635,22 +1736,22 @@ main(int argc, char * argv[])
}
if (0 != res) {
if (res == SG_LIB_CAT_INVALID_OP)
- pr2serr("read capacity not supported on %s\n", outf);
+ pr2serr("read capacity not supported on %s\n", outfn);
else if (res == SG_LIB_CAT_NOT_READY)
- pr2serr("read capacity failed, %s not ready\n", outf);
+ pr2serr("read capacity failed, %s not ready\n", outfn);
else
- pr2serr("Unable to read capacity on %s\n", outf);
+ pr2serr("Unable to read capacity on %s\n", outfn);
out_num_sect = -1;
}
} else if (FT_BLOCK == clp->out_type) {
if (0 != read_blkdev_capacity(clp->outfd, &out_num_sect,
&out_sect_sz)) {
- pr2serr("Unable to read block capacity on %s\n", outf);
+ pr2serr("Unable to read block capacity on %s\n", outfn);
out_num_sect = -1;
}
if (clp->bs != out_sect_sz) {
pr2serr("logical block size on %s confusion: bs=%d, from "
- "device=%d\n", outf, clp->bs, out_sect_sz);
+ "device=%d\n", outfn, clp->bs, out_sect_sz);
out_num_sect = -1;
}
}
@@ -1726,12 +1827,14 @@ main(int argc, char * argv[])
}
/* vvvvvvvvvvv Start worker threads vvvvvvvvvvvvvvvvvvvvvvvv */
- if ((clp->out_rem_count > 0) && (num_threads > 0)) {
+ 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);
if (0 != status) err_exit(status, "lock out_mutex");
+ thr_arg_a[0].id = 0;
+ thr_arg_a[0].clp = clp;
status = pthread_create(&threads[0], NULL, read_write_thread,
- (void *)clp);
+ (void *)(thr_arg_a + 0));
if (0 != status) err_exit(status, "pthread_create");
if (clp->debug)
pr2serr("Starting worker thread k=0\n");
@@ -1745,19 +1848,22 @@ main(int argc, char * argv[])
if (0 != status) err_exit(status, "unlock out_mutex");
/* now start the rest of the threads */
- for (k = 1; k < num_threads; ++k) {
+ for (k = 1; k < clp->num_threads; ++k) {
+
+ thr_arg_a[k].id = k;
+ thr_arg_a[k].clp = clp;
status = pthread_create(&threads[k], NULL, read_write_thread,
- (void *)clp);
+ (void *)(thr_arg_a + k));
if (0 != status) err_exit(status, "pthread_create");
- if (clp->debug)
+ if (clp->debug > 2)
pr2serr("Starting worker thread k=%d\n", k);
}
/* now wait for worker threads to finish */
- for (k = 0; k < num_threads; ++k) {
+ for (k = 0; k < clp->num_threads; ++k) {
status = pthread_join(threads[k], &vp);
if (0 != status) err_exit(status, "pthread_join");
- if (clp->debug)
+ if (clp->debug > 2)
pr2serr("Worker thread k=%d terminated\n", k);
}
} /* started worker threads and here after they have all exited */
@@ -1767,7 +1873,7 @@ main(int argc, char * argv[])
if (do_sync) {
if (FT_SG == clp->out_type) {
- pr2serr(">> Synchronizing cache on %s\n", outf);
+ pr2serr(">> Synchronizing cache on %s\n", outfn);
res = sg_ll_sync_cache_10(clp->outfd, 0, 0, 0, 0, 0, false, 0);
if (SG_LIB_CAT_UNIT_ATTENTION == res) {
pr2serr("Unit attention(out), continuing\n");
diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp
index d74605b3..3f4d4433 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -108,7 +108,7 @@
using namespace std;
-static const char * version_str = "1.71 20200207";
+static const char * version_str = "1.73 20200215";
#ifdef __GNUC__
#ifndef __clang__
@@ -248,9 +248,11 @@ typedef struct global_collection
bool unit_nanosec; /* default duration unit is millisecond */
bool mrq_cmds; /* mrq=<NRQS>,C given */
bool mrq_async; /* either mrq_immed or no_waitq flags given */
+ bool noshare; /* don't use request sharing */
bool unbalanced_mrq; /* so _not_ sg->sg request sharing sync mrq */
bool verify; /* don't copy, verify like Unix: cmp */
bool prefetch; /* for verify: do PF(b),RD(a),V(b)_a_data */
+ bool unshare; /* let close() do file unshare operation */
const char * infp;
const char * outfp;
const char * out2fp;
@@ -270,6 +272,7 @@ typedef struct request_element
bool wr;
bool has_share;
bool both_sg;
+ bool mmap_active;
bool same_sg;
bool only_in_sg;
bool only_out_sg;
@@ -850,12 +853,13 @@ usage(int pg_num)
"[coe=0|1]\n"
" [deb=VERB] [dio=0|1] [elemsz_kb=ESK] "
"[fua=0|1|2|3]\n"
- " [mrq=[IO,]NRQS[,C]] [of2=OFILE2] [ofreg=OFREG] "
- "[ofsplit=OSP]\n"
- " [sync=0|1] [thr=THR] [time=0|1] [verbose=VERB] "
- "[--dry-run]\n"
- " [--prefetch] [--verbose] [--verify] "
- "[--version]\n\n"
+ " [mrq=[IO,]NRQS[,C]] [noshare=0|1] [of2=OFILE2] "
+ "[ofreg=OFREG]\n"
+ " [ofsplit=OSP] [sync=0|1] [thr=THR] [time=0|1] "
+ "[unshare=1|0]\n"
+ " [verbose=VERB] [--dry-run] [--prefetch] "
+ "[--verbose]\n"
+ " [--verify] [--version]\n\n"
" where the main options (shown in first group above) are:\n"
" bs must be device logical block size (default "
"512)\n"
@@ -865,7 +869,7 @@ usage(int pg_num)
"direct,dpo,\n"
" dsync,excl,fua,masync,mmap,mrq_immed,mrq_svb,"
"nodur,\n"
- " noshare,no_waitq,noxfer,null,qtail,same_fds,"
+ " no_waitq,noxfer,null,qtail,same_fds,"
"v3,v4,wq_excl]\n"
" of file or device to write to (def: /dev/null "
"N.B. different\n"
@@ -923,6 +927,7 @@ page2:
" mrq number of cmds placed in each sg call "
"(def: 0);\n"
" may have trailing ',C', to send bulk cdb_s\n"
+ " noshare 0->use request sharing(def), 1->don't\n"
" ofreg OFREG is regular file or pipe to send what is "
"read from\n"
" IFILE in the first half of each shared element\n"
@@ -934,6 +939,9 @@ page2:
"max 1024\n"
" time 0->no timing, 1->calc throughput(def), "
"2->nanosec precision\n"
+ " unshare 0->don't explicitly unshare after share; 1->let "
+ "close do\n"
+ " file unshare (default)\n"
" verbose same as 'deb=VERB': increase verbosity\n"
" --dry-run|-d prepare but bypass copy/read\n"
" --verbose|-v increase verbosity of utility\n\n"
@@ -966,10 +974,6 @@ page3:
" mrq_svb if mrq and sg->sg copy, do shared_variable_"
"blocking\n"
" nodur turns off command duration calculations\n"
- " noshare if IFILE and OFILE are sg devices, don't set "
- "up sharing\n"
- " (def: do)\n"
- " no_unshare rely on close() to clean up fd share\n"
" no_waitq when non-blocking (async) don't use wait "
"queue\n"
" qhead queue new request at head of block queue\n"
@@ -1237,20 +1241,22 @@ sg_unshare(int sg_fd, int id, bool vb_b)
static void
sg_noshare_enlarge(int sg_fd, bool vb_b)
{
- struct sg_extended_info sei;
- struct sg_extended_info * seip;
+ if (sg_version_ge_40030) {
+ struct sg_extended_info sei;
+ struct sg_extended_info * seip;
- seip = &sei;
- memset(seip, 0, sizeof(*seip));
- sei.sei_wr_mask |= SG_SEIM_TOT_FD_THRESH;
- seip->tot_fd_thresh = 96 * 1024 * 1024;
- if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
- pr2serr_lk("%s: ioctl(EXTENDED(TOT_FD_THRESH), failed errno=%d %s\n",
- __func__, errno, strerror(errno));
- return;
+ seip = &sei;
+ memset(seip, 0, sizeof(*seip));
+ sei.sei_wr_mask |= SG_SEIM_TOT_FD_THRESH;
+ seip->tot_fd_thresh = 96 * 1024 * 1024;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr_lk("%s: ioctl(EXTENDED(TOT_FD_THRESH), failed errno=%d "
+ "%s\n", __func__, errno, strerror(errno));
+ return;
+ }
+ if (vb_b)
+ pr2serr_lk("ioctl(TOT_FD_THRESH) ok\n");
}
- if (vb_b)
- pr2serr_lk("ioctl(TOT_FD_THRESH) ok\n");
}
static void
@@ -1393,6 +1399,9 @@ read_write_thread(void * v_tip)
if (fd < 0)
goto fini;
rep->infd = fd;
+ rep->mmap_active = in_mmap;
+ if (in_mmap && (vb > 4))
+ pr2serr_lk("thread=%d: mmap buffp=%p\n", rep->id, rep->buffp);
own_infd = true;
++num_sg;
if (vb > 2)
@@ -1404,6 +1413,10 @@ read_write_thread(void * v_tip)
if (fd < 0)
goto fini;
rep->outfd = fd;
+ if (! rep->mmap_active)
+ rep->mmap_active = out_mmap;
+ if (out_mmap && (vb > 4))
+ pr2serr_lk("thread=%d: mmap buffp=%p\n", rep->id, rep->buffp);
own_outfd = true;
++num_sg;
if (vb > 2)
@@ -1443,12 +1456,10 @@ read_write_thread(void * v_tip)
if (vb > 4)
pr2serr_lk("thread=%d: Skipping share because driver too old\n",
rep->id);
- } else if (clp->in_flags.noshare || clp->out_flags.noshare) {
- if (clp->nmrqs > 0)
- sg_share_prepare(rep->outfd, rep->infd, rep->id, vb > 9);
- else if (vb > 4)
+ } else if (clp->noshare) {
+ if (vb > 4)
pr2serr_lk("thread=%d: Skipping IFILE share with OFILE due to "
- "mrq>0\n", rep->id);
+ "noshare=1\n", rep->id);
} else if (sg_version_ge_40030 && in_is_sg && out_is_sg)
rep->has_share = sg_share_prepare(rep->outfd, rep->infd, rep->id,
vb > 9);
@@ -1613,7 +1624,7 @@ skip_force_out_sequence:
if (0 != status) err_exit(status, "unlock in_mutex");
fini:
- if (rep->mmap_len > 0) {
+ if (rep->mmap_active && (rep->mmap_len > 0)) {
if (munmap(rep->buffp, rep->mmap_len) < 0) {
int err = errno;
char bb[64];
@@ -1621,17 +1632,19 @@ fini:
pr2serr_lk("thread=%d: munmap() failed: %s\n", rep->id,
tsafe_strerror(err, bb));
}
-
+ if (vb > 4)
+ pr2serr_lk("thread=%d: munmap(%p, %d)\n", rep->id, rep->buffp,
+ rep->mmap_len);
+ rep->mmap_active = false;
} else if (rep->alloc_bp)
free(rep->alloc_bp);
if (sg_version_ge_40030) {
- if (clp->in_flags.noshare || clp->out_flags.noshare) {
- if ((clp->nmrqs > 0) &&
- (! (clp->in_flags.no_unshare || clp->out_flags.no_unshare)))
+ if (clp->noshare) {
+ if ((clp->nmrqs > 0) && clp->unshare)
sg_unshare(rep->infd, rep->id, vb > 9);
} else if (in_is_sg && out_is_sg)
- if (! (clp->in_flags.no_unshare || clp->out_flags.no_unshare))
+ if (clp->unshare)
sg_unshare(rep->infd, rep->id, vb > 9);
}
if (own_infd && (rep->infd >= 0)) {
@@ -2167,10 +2180,10 @@ chk_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
if (sres) {
pr2serr_lk("[%d] %s: secondary error: %s [%d], info=0x%x\n", id,
__func__, strerror(sres), sres, ctl_v4p->info);
- if (E2BIG == sres) {
- sg_take_snap(rep->infd, id, true);
- sg_take_snap(rep->outfd, id, true);
- }
+ if (E2BIG == sres) {
+ sg_take_snap(rep->infd, id, true);
+ sg_take_snap(rep->outfd, id, true);
+ }
}
/* Check if those submitted have finished or not */
for (k = 0; k < n_subm; ++k, ++a_np) {
@@ -2282,8 +2295,8 @@ sgh_do_async_mrq(Rq_elem * rep, mrq_arr_t & def_arr, int fd,
res = ioctl(fd, SG_IOSUBMIT, ctlop);
if (res < 0) {
err = errno;
- if (E2BIG == err)
- sg_take_snap(fd, rep->id, true);
+ if (E2BIG == err)
+ sg_take_snap(fd, rep->id, true);
pr2serr_lk("%s: ioctl(SG_IOSUBMIT, %s)-->%d, errno=%d: %s\n", __func__,
sg_flags_str(ctlop->flags, b_len, b), res, err,
strerror(err));
@@ -2679,9 +2692,9 @@ try_again:
if (res < 0) {
int err = errno;
- if (E2BIG == err)
- sg_take_snap(fd, id, true);
- else if (EBUSY == err) {
+ if (E2BIG == err)
+ sg_take_snap(fd, id, true);
+ else if (EBUSY == err) {
++num_ebusy;
std::this_thread::yield();/* allow another thread to progress */
goto try_again;
@@ -2816,7 +2829,7 @@ sg_start_io(Rq_elem * rep, mrq_arr_t & def_arr, int & pack_id,
}
rep->cmd[1] = 0x2; /* set IMMED (no fua or dpo) */
}
- if (mmap && (rep->outregfd >= 0))
+ if (mmap && (clp->noshare || (rep->outregfd >= 0)))
flags |= SG_FLAG_MMAP_IO;
if (noxfer)
flags |= SG_FLAG_NO_DXFER;
@@ -2994,8 +3007,8 @@ do_v4:
if (res < 0) {
if (ENOMEM == err)
return 1;
- if (E2BIG == err)
- sg_take_snap(fd, rep->id, true);
+ if (E2BIG == err)
+ sg_take_snap(fd, rep->id, true);
pr2serr_lk("%s tid=%d: %s %s ioctl(2) failed: %s\n", __func__,
rep->id, cp, sg_flags_str(h4p->flags, b_len, b),
strerror(err));
@@ -3413,7 +3426,7 @@ sg_prepare_resbuf(int fd, int bs, int bpt, bool def_res, int elem_sz,
pr2serr_lk("%ssg driver prior to 4.0.00, reduced functionality\n",
my_name);
}
- goto fini;
+ goto bypass;
}
if (! sg_version_ge_40030)
goto bypass;
@@ -3491,7 +3504,6 @@ bypass:
errno, strerror(errno));
}
}
-fini:
t = 1;
res = ioctl(fd, SG_SET_DEBUG, &t); /* more info in /proc/scsi/sg/debug */
if (res < 0)
@@ -3632,8 +3644,8 @@ sg_in_open(Gbl_coll *clp, const char *inf, uint8_t **mmpp, int * mmap_lenp)
clp->in_flags.wq_excl, mmpp);
if (n <= 0)
return -SG_LIB_FILE_ERROR;
- if (clp->in_flags.noshare || clp->out_flags.noshare)
- sg_noshare_enlarge(fd, clp->debug > 3);
+ if (clp->noshare)
+ sg_noshare_enlarge(fd, clp->debug > 3);
if (mmap_lenp)
*mmap_lenp = n;
return fd;
@@ -3666,8 +3678,8 @@ sg_out_open(Gbl_coll *clp, const char *outf, uint8_t **mmpp, int * mmap_lenp)
clp->out_flags.wq_excl, mmpp);
if (n <= 0)
return -SG_LIB_FILE_ERROR;
- if (clp->in_flags.noshare || clp->out_flags.noshare)
- sg_noshare_enlarge(fd, clp->debug > 3);
+ if (clp->noshare)
+ sg_noshare_enlarge(fd, clp->debug > 3);
if (mmap_lenp)
*mmap_lenp = n;
return fd;
@@ -3812,6 +3824,8 @@ parse_cmdline_sanity(int argc, char * argv[], Gbl_coll * clp, char * inf,
cp = strchr(cp, ',');
if (cp && ('C' == toupper(cp[1])))
clp->mrq_cmds = true;
+ } else if (0 == strcmp(key, "noshare")) {
+ clp->noshare = !! sg_get_num(buf);
} else if (0 == strcmp(key, "obs")) {
obs = sg_get_num(buf);
if (-1 == obs) {
@@ -3871,6 +3885,8 @@ parse_cmdline_sanity(int argc, char * argv[], Gbl_coll * clp, char * inf,
num_threads = sg_get_num(buf);
else if (0 == strcmp(key, "time"))
do_time = sg_get_num(buf);
+ else if (0 == strcmp(key, "unshare"))
+ clp->unshare = !! sg_get_num(buf); /* default: true */
else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
res = 0;
n = num_chs_in_str(key + 1, keylen - 1, 'd');
@@ -3985,9 +4001,16 @@ parse_cmdline_sanity(int argc, char * argv[], Gbl_coll * clp, char * inf,
pr2serr("mmap flag on both IFILE and OFILE doesn't work\n");
return SG_LIB_SYNTAX_ERROR;
}
- if (clp->out_flags.mmap && !(clp->in_flags.noshare ||
- clp->out_flags.noshare)) {
- pr2serr("oflag=mmap needs either iflag=noshare or oflag=noshare\n");
+ if (! clp->noshare) {
+ if (clp->in_flags.noshare || clp->out_flags.noshare)
+ clp->noshare = true;
+ }
+ if (clp->unshare) {
+ if (clp->in_flags.no_unshare || clp->out_flags.no_unshare)
+ clp->unshare = false;
+ }
+ if (clp->out_flags.mmap && ! clp->noshare) {
+ pr2serr("oflag=mmap needs either noshare=1\n");
return SG_LIB_SYNTAX_ERROR;
}
if ((clp->in_flags.mmap || clp->out_flags.mmap) &&
@@ -3995,9 +4018,8 @@ parse_cmdline_sanity(int argc, char * argv[], Gbl_coll * clp, char * inf,
pr2serr("can't have both 'mmap' and 'same_fds' flags\n");
return SG_LIB_SYNTAX_ERROR;
}
- if (((! clp->in_flags.noshare) && clp->in_flags.dio) ||
- ((! clp->out_flags.noshare) && clp->out_flags.dio)) {
- pr2serr("dio flag can only be used with noshare flag\n");
+ if ((! clp->noshare) && (clp->in_flags.dio || clp->out_flags.dio)) {
+ pr2serr("dio flag can only be used with noshare=1\n");
return SG_LIB_SYNTAX_ERROR;
}
if (clp->nmrqs > 0) {
@@ -4074,6 +4096,7 @@ main(int argc, char * argv[])
clp->cdbsz_in = DEF_SCSI_CDBSZ;
clp->cdbsz_out = DEF_SCSI_CDBSZ;
clp->nmrqs = DEF_NUM_MRQS;
+ clp->unshare = true;
inf[0] = '\0';
outf[0] = '\0';
out2f[0] = '\0';
@@ -4319,8 +4342,7 @@ main(int argc, char * argv[])
}
}
#if 0
- if (clp->mrq_async && !(clp->in_flags.noshare ||
- clp->out_flags.noshare)) {
+ if (clp->mrq_async && !(clp->noshare)) {
pr2serr("With mrq_immed also need noshare on sg-->sg copy\n");
return SG_LIB_SYNTAX_ERROR;
}