aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2021-06-23 18:09:48 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2021-06-23 18:09:48 +0000
commit303f8c98ee28150285d69bd213970144fe224da3 (patch)
tree3cdbf0cc482229b65481fe30049c92fa3c2f1faa
parentbee57c535c29a01cadfaa660d8c0182288960c33 (diff)
downloadsg3_utils-303f8c98ee28150285d69bd213970144fe224da3.tar.gz
Haiku OS support, add pt function list in sg_pt_dummy.c; add slices to sg_mrq_dd
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@905 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog21
-rw-r--r--Makefile.in2
-rw-r--r--README4
-rw-r--r--config.h.in6
-rwxr-xr-xconfigure45
-rw-r--r--configure.ac11
-rw-r--r--debian/changelog2
-rw-r--r--doc/Makefile.in2
-rw-r--r--doc/sg_inq.820
-rw-r--r--doc/sg_vpd.818
-rw-r--r--include/Makefile.in2
-rw-r--r--lib/Makefile.am12
-rw-r--r--lib/Makefile.in34
-rw-r--r--lib/sg_pt_common.c4
-rw-r--r--lib/sg_pt_dummy.c83
-rw-r--r--lib/sg_pt_haiku.c572
-rw-r--r--lib/sg_pt_osf1.c48
-rw-r--r--lib/sg_pt_solaris.c32
-rw-r--r--scripts/Makefile.in2
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/Makefile.in2
-rw-r--r--src/sg_luns.c9
-rw-r--r--testing/sg_mrq_dd.cpp1392
-rw-r--r--testing/sgh_dd.cpp631
24 files changed, 1992 insertions, 964 deletions
diff --git a/ChangeLog b/ChangeLog
index 465479c6..a9168f75 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 [20210610] [svn: r904]
+Changelog for pre-release sg3_utils-1.47 [20210623] [svn: r905]
- sg_rep_zones: add support for REPORT ZONE DOMAINS and
REPORT REALMS in this utility
- sg_raw: fix prints of NVMe NVM command names
@@ -13,14 +13,17 @@ Changelog for pre-release sg3_utils-1.47 [20210610] [svn: r904]
- sg_dd, sgm_dd, sgp_dd: don't close negative file descriptors
- sg_lib: add sg_scsi_status_is_good(),
sg_scsi_status_is_bad() and sg_get_zone_type_str()
- - pt_linux: fix verify(BytChk=0) which Linux SNTL translated
- to write, other SNTL cleanups
- - pt_linux_nvme: fix fua setting
- - pt: check_pt_file_handle() add return value of 5 for
- FreeBSD for nvme(cam)
- - pt: new configure option --enable-pt_dummy builds the
- library with sg_pt_dummy.c instead of OS specific code.
- To experiment with --inhex= decoding on netbsd
+ - pt_linux: fix verify(BytChk=0) which Linux SNTL translated
+ to write, other SNTL cleanups
+ - pt_linux_nvme: fix fua setting
+ - pt: check_pt_file_handle() add return value of 5 for
+ FreeBSD for nvme(cam)
+ - pt: new configure option --enable-pt_dummy builds the
+ library with sg_pt_dummy.c instead of OS specific code.
+ For experimenting with --inhex= decoding on netbsd
+ - pt: add Haiku OS support
+ - sg_pt_dummy.c: add list of functions that a new pt
+ needs to define
- move some hex files from examples to inhex directory
- major rework of lib/sg_pt_freebsd.c; make SNTL as similar
as feasible to the Linux implementation
diff --git a/Makefile.in b/Makefile.in
index ddb80900..56a1841a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -309,6 +309,8 @@ localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
diff --git a/README b/README
index 541562c5..26acbeaa 100644
--- a/README
+++ b/README
@@ -418,7 +418,7 @@ user reports are welcome as the author does not have equipment to test
this.
Other utilities in this package that use the SES Send and Receive commands,
-or the SNTL in the library are sg_senddiag, sg_inq and sg_readcap.
+or the SNTL in the library are sg_senddiag, sg_inq, sg_raw and sg_readcap.
Command line processing
@@ -541,4 +541,4 @@ See https://sg.danny.cz/sg/tools.html
Douglas Gilbert dgilbert@interlog.com
-4th May 2021
+17th June 2021
diff --git a/config.h.in b/config.h.in
index ed7d18a8..ea454245 100644
--- a/config.h.in
+++ b/config.h.in
@@ -129,6 +129,9 @@
/* sg3_utils on FreeBSD */
#undef SG_LIB_FREEBSD
+/* sg3_utils on Haiku */
+#undef SG_LIB_HAIKU
+
/* sg3_utils on linux */
#undef SG_LIB_LINUX
@@ -144,6 +147,9 @@
/* sg3_utils on Tru64 UNIX */
#undef SG_LIB_OSF1
+/* sg3_utils on other */
+#undef SG_LIB_OTHER
+
/* sg3_utils on Solaris */
#undef SG_LIB_SOLARIS
diff --git a/configure b/configure
index fd88c2b0..63b95625 100755
--- a/configure
+++ b/configure
@@ -665,6 +665,10 @@ PT_DUMMY_FALSE
PT_DUMMY_TRUE
DEBUG_FALSE
DEBUG_TRUE
+OS_OTHER_FALSE
+OS_OTHER_TRUE
+OS_HAIKU_FALSE
+OS_HAIKU_TRUE
OS_OPENBSD_FALSE
OS_OPENBSD_TRUE
OS_NETBSD_FALSE
@@ -683,6 +687,8 @@ OS_LINUX_FALSE
OS_LINUX_TRUE
OS_FREEBSD_FALSE
OS_FREEBSD_TRUE
+os_libs
+os_cflags
CPP
GETOPT_O_FILES
RT_LIB
@@ -13291,13 +13297,26 @@ printf "%s\n" "#define HAVE_NVME 1" >>confdefs.h
check_for_getrandom
CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO";;
- *-*-linux-gnu* | *-*-linux* | *)
+ *-*-linux-gnu* | *-*-linux*)
printf "%s\n" "#define SG_LIB_LINUX 1" >>confdefs.h
check_for_linux_sg_v4_hdr
check_for_getrandom
check_for_linux_nvme_headers;;
+ *-*-haiku*)
+
+printf "%s\n" "#define SG_LIB_HAIKU 1" >>confdefs.h
+
+ os_cflags=''
+
+ os_libs=''
+ ;;
+ *)
+
+printf "%s\n" "#define SG_LIB_OTHER 1" >>confdefs.h
+
+ isother=yes;;
esac
# Define platform-specific symbol.
@@ -13373,6 +13392,22 @@ else
OS_OPENBSD_FALSE=
fi
+ if echo $host_os | grep '^haiku' > /dev/null; then
+ OS_HAIKU_TRUE=
+ OS_HAIKU_FALSE='#'
+else
+ OS_HAIKU_TRUE='#'
+ OS_HAIKU_FALSE=
+fi
+
+ if test "x$isother" = "xyes"; then
+ OS_OTHER_TRUE=
+ OS_OTHER_FALSE='#'
+else
+ OS_OTHER_TRUE='#'
+ OS_OTHER_FALSE=
+fi
+
# Check whether --enable-debug was given.
if test ${enable_debug+y}
@@ -13648,6 +13683,14 @@ if test -z "${OS_OPENBSD_TRUE}" && test -z "${OS_OPENBSD_FALSE}"; then
as_fn_error $? "conditional \"OS_OPENBSD\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${OS_HAIKU_TRUE}" && test -z "${OS_HAIKU_FALSE}"; then
+ as_fn_error $? "conditional \"OS_HAIKU\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OS_OTHER_TRUE}" && test -z "${OS_OTHER_FALSE}"; then
+ as_fn_error $? "conditional \"OS_OTHER\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
if test -z "${DEBUG_TRUE}" && test -z "${DEBUG_FALSE}"; then
as_fn_error $? "conditional \"DEBUG\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
diff --git a/configure.ac b/configure.ac
index bb1e1477..b00d167f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,11 +106,18 @@ case "${host}" in
AC_DEFINE_UNQUOTED(HAVE_NVME, 1, [Found NVMe])
check_for_getrandom
CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO";;
- *-*-linux-gnu* | *-*-linux* | *)
+ *-*-linux-gnu* | *-*-linux*)
AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux])
check_for_linux_sg_v4_hdr
check_for_getrandom
check_for_linux_nvme_headers;;
+ *-*-haiku*)
+ AC_DEFINE_UNQUOTED(SG_LIB_HAIKU, 1, [sg3_utils on Haiku])
+ AC_SUBST([os_cflags], [''])
+ AC_SUBST([os_libs], ['']) ;;
+ *)
+ AC_DEFINE_UNQUOTED(SG_LIB_OTHER, 1, [sg3_utils on other])
+ isother=yes;;
esac
# Define platform-specific symbol.
@@ -123,6 +130,8 @@ AM_CONDITIONAL(OS_WIN32_CYGWIN, [echo $host_os | grep '^cygwin' > /dev/null])
AM_CONDITIONAL(OS_ANDROID, [echo $host_os | grep 'android' > /dev/null])
AM_CONDITIONAL(OS_NETBSD, [echo $host_os | grep 'netbsd' > /dev/null])
AM_CONDITIONAL(OS_OPENBSD, [echo $host_os | grep 'openbsd' > /dev/null])
+AM_CONDITIONAL(OS_HAIKU, [echo $host_os | grep '^haiku' > /dev/null])
+AM_CONDITIONAL(OS_OTHER, [test "x$isother" = "xyes"])
AC_ARG_ENABLE([debug],
[ --enable-debug Turn on debugging],
diff --git a/debian/changelog b/debian/changelog
index 4e993062..82003539 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 <dgilbert@interlog.com> Mon, 07 Jun 2021 11:00:00 -0400
+ -- Douglas Gilbert <dgilbert@interlog.com> Wed, 23 Jun 2021 14:00:00 -0400
sg3-utils (1.46-0.1) unstable; urgency=low
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 81cdadb3..4b9b7476 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -267,6 +267,8 @@ localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
diff --git a/doc/sg_inq.8 b/doc/sg_inq.8
index db591986..a0bfae94 100644
--- a/doc/sg_inq.8
+++ b/doc/sg_inq.8
@@ -1,4 +1,4 @@
-.TH SG_INQ "8" "March 2021" "sg3_utils\-1.46" SG3_UTILS
+.TH SG_INQ "8" "June 2021" "sg3_utils\-1.47" SG3_UTILS
.SH NAME
sg_inq \- issue SCSI INQUIRY command and/or decode its response
.SH SYNOPSIS
@@ -492,6 +492,24 @@ pages (but not their contents) try:
.PP
sg_inq \-e /dev/sda
.PP
+In Linux, binary images of some important VPD page responses (e.g. 0, 80h
+and 83h) are cached in files within the sysfs pseudo file system. Since
+VPD pages hardly ever change their contents, decoding those files will
+give the same output as probing the device with the added benefit that
+decoding those files doesn't need root permissions. If /dev/sg3 is a disk
+at 2:0:0:0 , then these three invocations should result in the same output:
+.PP
+ sg_inq \-\-raw \-\-inhex=/sys/class/scsi_generic/sg3/device/vpd_pg83
+.PP
+ sg_inq \-rI /sys/class/scsi_generic/sg3/device/vpd_pg83
+.PP
+ sg_inq \-r \-I /sys/class/scsi_disk/2:0:0:0/device/vpd_pg83
+.PP
+Without the \fI\-\-raw\fR option, the \fI\-\-inhex=FN\fR option would
+expect the contents of those files to be hexadecimal. vpd_pg83 contains
+the response (in binary) to the Device Identification VPD page whose page
+number is 83h (i.e. hexadecimal).
+.PP
Some VPD pages can be read with the sg_inq utility but a newer utility
called sg_vpd specializes in showing their contents. The sdparm utility
can also be used to show the contents of VPD pages.
diff --git a/doc/sg_vpd.8 b/doc/sg_vpd.8
index 17d5f130..e1e19956 100644
--- a/doc/sg_vpd.8
+++ b/doc/sg_vpd.8
@@ -1,4 +1,4 @@
-.TH SG_VPD "8" "March 2021" "sg3_utils\-1.46" SG3_UTILS
+.TH SG_VPD "8" "June 2021" "sg3_utils\-1.47" SG3_UTILS
.SH NAME
sg_vpd \- fetch SCSI VPD page and/or decode its response
.SH SYNOPSIS
@@ -314,6 +314,22 @@ excluding vendor specific VPD pages that start at page number 0xc0:
.PP
# sg_vpd \-\-all \-\-page=0xbf \-\-raw \-\-inhex=all_vpds.bin
.PP
+In Linux, binary images of some important VPD page responses (e.g. 0, 80h
+and 83h) are cached in files within the sysfs pseudo file system. Since
+VPD pages hardly ever change their contents, decoding those files will
+give the same output as probing the device with the added benefit that
+decoding those files doesn't need root permissions. The long and short
+forms are shown:
+.PP
+ sg_vpd \-\-raw \-\-inhex=/sys/class/scsi_generic/sg3/device/vpd_pg83
+.PP
+ sg_vpd \-rI /sys/class/scsi_generic/sg3/device/vpd_pg83
+.PP
+If /dev/sg3 is a disk at 2:0:0:0 , then this invocation should give more
+verbose output but essentially the same as the previous two examples.
+.PP
+ sg_vpd \-v \-r \-I /sys/class/scsi_disk/2:0:0:0/device/vpd_pg83
+.PP
Further examples can be found on the https://sg.danny.cz/sg/sg3_utils.html
web page.
.SH AUTHOR
diff --git a/include/Makefile.in b/include/Makefile.in
index 6ee60f64..f2d4fea1 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -287,6 +287,8 @@ localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f04c7778..97410dc3 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -42,6 +42,14 @@ if OS_OSF
libsgutils2_la_SOURCES += sg_pt_osf1.c
endif
+if OS_HAIKU
+if PT_DUMMY
+libsgutils2_la_SOURCES += sg_pt_dummy.c
+else
+libsgutils2_la_SOURCES += sg_pt_haiku.c
+endif
+endif
+
if OS_NETBSD
libsgutils2_la_SOURCES += sg_pt_dummy.c
endif
@@ -50,6 +58,10 @@ if OS_OPENBSD
libsgutils2_la_SOURCES += sg_pt_dummy.c
endif
+if OS_OTHER
+libsgutils2_la_SOURCES += sg_pt_dummy.c
+endif
+
if DEBUG
# This is active if --enable-debug given to ./configure
# removed -Wduplicated-branches because needs gcc-8
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 4e12ff91..0fdc8a37 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -100,8 +100,11 @@ host_triplet = @host@
@OS_FREEBSD_TRUE@@PT_DUMMY_FALSE@am__append_6 = sg_pt_freebsd.c
@OS_SOLARIS_TRUE@am__append_7 = sg_pt_solaris.c
@OS_OSF_TRUE@am__append_8 = sg_pt_osf1.c
-@OS_NETBSD_TRUE@am__append_9 = sg_pt_dummy.c
-@OS_OPENBSD_TRUE@am__append_10 = sg_pt_dummy.c
+@OS_HAIKU_TRUE@@PT_DUMMY_TRUE@am__append_9 = sg_pt_dummy.c
+@OS_HAIKU_TRUE@@PT_DUMMY_FALSE@am__append_10 = sg_pt_haiku.c
+@OS_NETBSD_TRUE@am__append_11 = sg_pt_dummy.c
+@OS_OPENBSD_TRUE@am__append_12 = sg_pt_dummy.c
+@OS_OTHER_TRUE@am__append_13 = sg_pt_dummy.c
subdir = lib
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
@@ -145,7 +148,7 @@ am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \
sg_cmds_basic.c sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c \
sg_pt_common.c sg_pt_dummy.c sg_pt_linux.c sg_io_linux.c \
sg_pt_linux_nvme.c sg_pt_win32.c sg_pt_freebsd.c \
- sg_pt_solaris.c sg_pt_osf1.c
+ sg_pt_solaris.c sg_pt_osf1.c sg_pt_haiku.c
@OS_LINUX_TRUE@@PT_DUMMY_TRUE@am__objects_1 = sg_pt_dummy.lo
@OS_LINUX_TRUE@@PT_DUMMY_FALSE@am__objects_2 = sg_pt_linux.lo \
@OS_LINUX_TRUE@@PT_DUMMY_FALSE@ sg_io_linux.lo \
@@ -156,14 +159,18 @@ am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \
@OS_FREEBSD_TRUE@@PT_DUMMY_FALSE@am__objects_6 = sg_pt_freebsd.lo
@OS_SOLARIS_TRUE@am__objects_7 = sg_pt_solaris.lo
@OS_OSF_TRUE@am__objects_8 = sg_pt_osf1.lo
-@OS_NETBSD_TRUE@am__objects_9 = sg_pt_dummy.lo
-@OS_OPENBSD_TRUE@am__objects_10 = sg_pt_dummy.lo
+@OS_HAIKU_TRUE@@PT_DUMMY_TRUE@am__objects_9 = sg_pt_dummy.lo
+@OS_HAIKU_TRUE@@PT_DUMMY_FALSE@am__objects_10 = sg_pt_haiku.lo
+@OS_NETBSD_TRUE@am__objects_11 = sg_pt_dummy.lo
+@OS_OPENBSD_TRUE@am__objects_12 = sg_pt_dummy.lo
+@OS_OTHER_TRUE@am__objects_13 = sg_pt_dummy.lo
am_libsgutils2_la_OBJECTS = sg_lib.lo sg_lib_data.lo sg_cmds_basic.lo \
sg_cmds_basic2.lo sg_cmds_extra.lo sg_cmds_mmc.lo \
sg_pt_common.lo $(am__objects_1) $(am__objects_2) \
$(am__objects_3) $(am__objects_4) $(am__objects_5) \
$(am__objects_6) $(am__objects_7) $(am__objects_8) \
- $(am__objects_9) $(am__objects_10)
+ $(am__objects_9) $(am__objects_10) $(am__objects_11) \
+ $(am__objects_12) $(am__objects_13)
libsgutils2_la_OBJECTS = $(am_libsgutils2_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -193,9 +200,10 @@ am__depfiles_remade = ./$(DEPDIR)/sg_cmds_basic.Plo \
./$(DEPDIR)/sg_cmds_mmc.Plo ./$(DEPDIR)/sg_io_linux.Plo \
./$(DEPDIR)/sg_lib.Plo ./$(DEPDIR)/sg_lib_data.Plo \
./$(DEPDIR)/sg_pt_common.Plo ./$(DEPDIR)/sg_pt_dummy.Plo \
- ./$(DEPDIR)/sg_pt_freebsd.Plo ./$(DEPDIR)/sg_pt_linux.Plo \
- ./$(DEPDIR)/sg_pt_linux_nvme.Plo ./$(DEPDIR)/sg_pt_osf1.Plo \
- ./$(DEPDIR)/sg_pt_solaris.Plo ./$(DEPDIR)/sg_pt_win32.Plo
+ ./$(DEPDIR)/sg_pt_freebsd.Plo ./$(DEPDIR)/sg_pt_haiku.Plo \
+ ./$(DEPDIR)/sg_pt_linux.Plo ./$(DEPDIR)/sg_pt_linux_nvme.Plo \
+ ./$(DEPDIR)/sg_pt_osf1.Plo ./$(DEPDIR)/sg_pt_solaris.Plo \
+ ./$(DEPDIR)/sg_pt_win32.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -350,6 +358,8 @@ localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
@@ -368,7 +378,8 @@ libsgutils2_la_SOURCES = sg_lib.c sg_lib_data.c sg_cmds_basic.c \
$(am__append_1) $(am__append_2) $(am__append_3) \
$(am__append_4) $(am__append_5) $(am__append_6) \
$(am__append_7) $(am__append_8) $(am__append_9) \
- $(am__append_10)
+ $(am__append_10) $(am__append_11) $(am__append_12) \
+ $(am__append_13)
@DEBUG_FALSE@DBG_CFLAGS =
# This is active if --enable-debug given to ./configure
@@ -481,6 +492,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_common.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_dummy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_freebsd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_haiku.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux_nvme.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_osf1.Plo@am__quote@ # am--include-marker
@@ -658,6 +670,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/sg_pt_common.Plo
-rm -f ./$(DEPDIR)/sg_pt_dummy.Plo
-rm -f ./$(DEPDIR)/sg_pt_freebsd.Plo
+ -rm -f ./$(DEPDIR)/sg_pt_haiku.Plo
-rm -f ./$(DEPDIR)/sg_pt_linux.Plo
-rm -f ./$(DEPDIR)/sg_pt_linux_nvme.Plo
-rm -f ./$(DEPDIR)/sg_pt_osf1.Plo
@@ -718,6 +731,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/sg_pt_common.Plo
-rm -f ./$(DEPDIR)/sg_pt_dummy.Plo
-rm -f ./$(DEPDIR)/sg_pt_freebsd.Plo
+ -rm -f ./$(DEPDIR)/sg_pt_haiku.Plo
-rm -f ./$(DEPDIR)/sg_pt_linux.Plo
-rm -f ./$(DEPDIR)/sg_pt_linux_nvme.Plo
-rm -f ./$(DEPDIR)/sg_pt_osf1.Plo
diff --git a/lib/sg_pt_common.c b/lib/sg_pt_common.c
index 60ecaa6c..345aec39 100644
--- a/lib/sg_pt_common.c
+++ b/lib/sg_pt_common.c
@@ -31,8 +31,10 @@
#include "sg_pt_nvme.h"
#endif
-static const char * scsi_pt_version_str = "3.17 20210503";
+static const char * scsi_pt_version_str = "3.18 20210617";
+/* List of external functions that need to be defined for each OS are
+ * listed at the top of sg_pt_dummy.c */
const char *
scsi_pt_version()
diff --git a/lib/sg_pt_dummy.c b/lib/sg_pt_dummy.c
index be84b503..d746c1ad 100644
--- a/lib/sg_pt_dummy.c
+++ b/lib/sg_pt_dummy.c
@@ -23,7 +23,53 @@
#include "sg_lib.h"
#include "sg_pr2serr.h"
-/* Version 1.01 20210609 */
+/* Version 1.02 20210618 */
+
+/* List of function names with external linkage that need to be defined
+ *
+ * check_pt_file_handle
+ * clear_scsi_pt_obj
+ * construct_scsi_pt_obj
+ * construct_scsi_pt_obj_with_fd
+ * destruct_scsi_pt_obj
+ * do_scsi_pt
+ * do_nvm_pt
+ * get_pt_actual_lengths
+ * get_pt_duration_ns
+ * get_pt_file_handle
+ * get_pt_nvme_nsid
+ * get_pt_req_lengths
+ * get_pt_result
+ * get_scsi_pt_cdb_buf
+ * get_scsi_pt_cdb_len
+ * get_scsi_pt_duration_ms
+ * get_scsi_pt_os_err
+ * get_scsi_pt_os_err_str
+ * get_scsi_pt_resid
+ * get_scsi_pt_result_category
+ * get_scsi_pt_sense_buf
+ * get_scsi_pt_sense_len
+ * get_scsi_pt_status_response
+ * get_scsi_pt_transport_err
+ * get_scsi_pt_transport_err_str
+ * partial_clear_scsi_pt_obj
+ * pt_device_is_nvme
+ * scsi_pt_close_device
+ * scsi_pt_open_device
+ * scsi_pt_open_flags
+ * set_pt_file_handle
+ * set_pt_metadata_xfer
+ * set_scsi_pt_cdb
+ * set_scsi_pt_data_in
+ * set_scsi_pt_data_out
+ * set_scsi_pt_flags
+ * set_scsi_pt_packet_id
+ * set_scsi_pt_sense
+ * set_scsi_pt_tag
+ * set_scsi_pt_task_attr
+ * set_scsi_pt_task_management
+ * set_scsi_pt_transport_err
+ */
/* Simply defines all the functions needed by the pt interface (see sg_pt.h).
* They do nothing. This allows decoding of hex files (e.g. with the --in=
@@ -344,6 +390,15 @@ check_pt_file_handle(int device_fd, const char * device_name, int vb)
return 0;
}
+/* Valid file handles (which is the return value) are >= 0 . Returns -1
+ * if there is no valid file handle. */
+int
+get_pt_file_handle(const struct sg_pt_base * vp)
+{
+ if (vp) { }
+ return -1;
+}
+
/* If a NVMe block device (which includes the NSID) handle is associated
* with 'vp', then its NSID is returned (values range from 0x1 to
* 0xffffffe). Otherwise 0 is returned. */
@@ -360,3 +415,29 @@ get_pt_result(const struct sg_pt_base * vp)
if (vp) { }
return 0;
}
+
+int
+set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb)
+{
+ if (vp) { }
+ if (dev_han) { }
+ if (vb) { }
+ return 0;
+}
+
+void
+set_pt_metadata_xfer(struct sg_pt_base * vp, uint8_t * mdxferp,
+ uint32_t mdxfer_len, bool out_true)
+{
+ if (vp) { }
+ if (mdxferp) { }
+ if (mdxfer_len) { }
+ if (out_true) { }
+}
+
+void
+set_scsi_pt_transport_err(struct sg_pt_base * vp, int err)
+{
+ if (vp) { }
+ if (err) { }
+}
diff --git a/lib/sg_pt_haiku.c b/lib/sg_pt_haiku.c
new file mode 100644
index 00000000..c9ed291c
--- /dev/null
+++ b/lib/sg_pt_haiku.c
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2017 Leorize.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <device/CAM.h>
+#include <scsi.h>
+
+#include "sg_lib.h"
+#include "sg_pt.h"
+
+#if defined(__GNUC__) || defined(__clang__)
+static int pr2ws(const char * fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+static int
+pr2ws(const char * fmt, ...)
+{
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+ va_end(args);
+ return n;
+}
+
+struct sg_pt_haiku_scsi {
+ raw_device_command raw_command;
+ size_t data_len;
+ int in_err;
+ int os_err;
+ int dev_fd;
+};
+
+struct sg_pt_base {
+ struct sg_pt_haiku_scsi impl;
+};
+
+/* Returns >= 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
+{
+ int oflags = O_NONBLOCK;
+
+ oflags |= (read_only ? O_RDONLY : O_RDWR);
+ return scsi_pt_open_flags(device_name, oflags, verbose);
+}
+
+/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */
+/* together. The 'flags' argument is advisory and may be ignored. */
+/* Returns >= 0 if successful, otherwise returns negated errno. */
+int
+scsi_pt_open_flags(const char * device_name, int flags, int verbose)
+{
+ int fd;
+
+ if (verbose > 1) {
+ pr2ws("open %s with flags=0x%x\n", device_name, flags);
+ }
+ fd = open(device_name, flags);
+ if (fd < 0)
+ fd = -errno;
+ return fd;
+}
+
+/* Returns 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_close_device(int device_fd)
+{
+ int res;
+
+ res = close(device_fd);
+ if (res < 0)
+ res = -errno;
+ return res;
+}
+
+struct sg_pt_base *
+construct_scsi_pt_obj_with_fd(int device_fd, int verbose)
+{
+ struct sg_pt_haiku_scsi * ptp;
+
+ /* The following 2 lines are temporary. It is to avoid a NULL pointer
+ * crash when an old utility is used with a newer library built after
+ * the sg_warnings_strm cleanup */
+ if (NULL == sg_warnings_strm)
+ sg_warnings_strm = stderr;
+
+ ptp = (struct sg_pt_haiku_scsi *)
+ calloc(1, sizeof(struct sg_pt_haiku_scsi));
+ if (ptp) {
+ ptp->raw_command.flags = B_RAW_DEVICE_REPORT_RESIDUAL;
+ ptp->dev_fd = device_fd;
+ } else if (verbose)
+ pr2ws("%s: malloc() out of memory\n", __func__);
+ return (struct sg_pt_base *)ptp;
+}
+
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+ return construct_scsi_pt_obj_with_fd(-1, 0);
+}
+
+void
+destruct_scsi_pt_obj(struct sg_pt_base * vp)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (ptp)
+ free(ptp);
+}
+
+void
+clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (ptp) {
+
+ int fd = ptp->dev_fd;
+ memset(ptp, 0, sizeof(struct sg_pt_haiku_scsi));
+ ptp->dev_fd = fd;
+ ptp->raw_command.flags = B_RAW_DEVICE_REPORT_RESIDUAL;
+ }
+}
+
+void
+set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
+ int cdb_len)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ for (int i = 0; i < 16; ++i)
+ if (ptp->raw_command.command[i])
+ ++ptp->in_err;
+ memcpy(ptp->raw_command.command, cdb, cdb_len);
+ ptp->raw_command.command_length = (uint8)cdb_len;
+}
+
+void
+set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
+ int max_sense_len)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (ptp->raw_command.sense_data)
+ ++ptp->in_err;
+ memset(sense, 0, max_sense_len);
+ ptp->raw_command.sense_data = sense;
+ ptp->raw_command.sense_data_length = max_sense_len;
+}
+
+/* Setup for data transfer from device */
+void
+set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
+ int dxfer_len)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (ptp->raw_command.data)
+ ++ptp->in_err;
+ if (dxfer_len > 0) {
+ ptp->raw_command.data = dxferp;
+ ptp->raw_command.data_length = dxfer_len;
+ ptp->data_len = dxfer_len;
+ ptp->raw_command.flags |= B_RAW_DEVICE_DATA_IN;
+ }
+}
+
+/* Setup for data transfer toward device */
+void
+set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
+ int dxfer_len)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (ptp->raw_command.data)
+ ++ptp->in_err;
+ if (dxfer_len > 0) {
+ ptp->raw_command.data = (unsigned char *)dxferp;
+ ptp->raw_command.data_length = dxfer_len;
+ ptp->data_len = dxfer_len;
+ ptp->raw_command.flags &= ~B_RAW_DEVICE_DATA_IN;
+ }
+}
+
+void
+set_scsi_pt_packet_id(struct sg_pt_base * vp __attribute__ ((unused)),
+ int pack_id __attribute__ ((unused)))
+{
+}
+
+void
+set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag __attribute__ ((unused)))
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ ++ptp->in_err;
+}
+
+void
+set_scsi_pt_task_management(struct sg_pt_base * vp,
+ int tmf_code __attribute__ ((unused)))
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ ++ptp->in_err;
+}
+
+void
+set_scsi_pt_task_attr(struct sg_pt_base * vp,
+ int attrib __attribute__ ((unused)),
+ int priority __attribute__ ((unused)))
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ ++ptp->in_err;
+}
+
+void
+set_scsi_pt_flags(struct sg_pt_base * vp __attribute__ ((unused)),
+ int flags __attribute__ ((unused)))
+{
+}
+
+/* Executes SCSI command (or at least forwards it to lower layers).
+ * Clears os_err field prior to active call (whose result may set it
+ * again). */
+int
+do_scsi_pt(struct sg_pt_base * vp, int fd, int timeout_secs, int verbose)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ ptp->os_err = 0;
+ if (ptp->in_err) {
+ if (verbose)
+ pr2ws("Replicated or unused set_scsi_pt...\n");
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ if (fd >= 0) {
+ if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
+ if (verbose)
+ pr2ws("%s: file descriptor given to create() and here "
+ "differ\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ ptp->dev_fd = fd;
+ } else if (ptp->dev_fd < 0) {
+ if (verbose)
+ pr2ws("%s: invalid file descriptors\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ } else
+ fd = ptp->dev_fd;
+
+ if (NULL == ptp->raw_command.command) {
+ if (verbose)
+ pr2ws("No SCSI command (cdb) given\n");
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ /* raw_command.timeout is in microseconds */
+ ptp->raw_command.timeout = ((timeout_secs > 0) ? (timeout_secs * 1000000) :
+ CAM_TIME_DEFAULT);
+
+ if (ioctl(fd, B_RAW_DEVICE_COMMAND, &ptp->raw_command) < 0) {
+ ptp->os_err = errno;
+ if (verbose > 1)
+ pr2ws("ioctl(B_RAW_DEVICE_COMMAND) failed: %s (errno=%d)\n",
+ safe_strerror(ptp->os_err), ptp->os_err);
+ return -ptp->os_err;
+ }
+
+ return SCSI_PT_DO_START_OK;
+}
+
+int
+get_scsi_pt_result_category(const struct sg_pt_base * vp)
+{
+ int cam_status_masked;
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (ptp->os_err)
+ return SCSI_PT_RESULT_OS_ERR;
+ cam_status_masked = ptp->raw_command.cam_status & CAM_STATUS_MASK;
+ if (cam_status_masked != CAM_REQ_CMP &&
+ cam_status_masked != CAM_REQ_CMP_ERR)
+ return SCSI_PT_RESULT_TRANSPORT_ERR;
+ else if ((SAM_STAT_CHECK_CONDITION == ptp->raw_command.scsi_status) ||
+ (SAM_STAT_COMMAND_TERMINATED == ptp->raw_command.scsi_status))
+ return SCSI_PT_RESULT_SENSE;
+ else if (ptp->raw_command.scsi_status)
+ return SCSI_PT_RESULT_STATUS;
+ else
+ return SCSI_PT_RESULT_GOOD;
+}
+
+int
+get_scsi_pt_resid(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ /* For various reasons Haiku return data_len - data_resid */
+ return ptp->data_len - ptp->raw_command.data_length;
+}
+
+int
+get_scsi_pt_status_response(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ return ptp->raw_command.scsi_status;
+}
+
+uint8_t *
+get_scsi_pt_sense_buf(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ return (uint8_t *)ptp->raw_command.sense_data;
+}
+
+int
+get_scsi_pt_sense_len(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ return ptp->raw_command.sense_data_length;
+}
+
+int
+get_scsi_pt_os_err(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ return ptp->os_err;
+}
+
+char *
+get_scsi_pt_os_err_str(const struct sg_pt_base * vp __attribute__ ((unused)),
+ int max_b_len, char * b)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ const char *cp;
+
+ cp = safe_strerror(ptp->os_err);
+ strncpy(b, cp, max_b_len);
+ if ((int)strlen(cp) >= max_b_len)
+ b[max_b_len - 1] = '\0';
+ return b;
+}
+
+int
+get_scsi_pt_transport_err(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if ((ptp->raw_command.cam_status & CAM_STATUS_MASK) != CAM_REQ_CMP ||
+ (ptp->raw_command.cam_status & CAM_STATUS_MASK) != CAM_REQ_CMP_ERR)
+ return ptp->raw_command.cam_status & CAM_STATUS_MASK;
+
+ return 0;
+}
+
+char *
+get_scsi_pt_transport_err_str(
+ const struct sg_pt_base * vp __attribute__ ((unused)),
+ int max_b_len, char * b)
+{
+ strncpy(b, "no transport error available", max_b_len);
+ b[max_b_len - 1] = '\0';
+ return b;
+}
+
+int
+get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused)))
+{
+ return -1;
+}
+
+void
+partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (NULL == ptp)
+ return;
+ ptp->in_err = 0;
+ ptp->os_err = 0;
+ ptp->data_len = 0;
+ ptp->raw_command.cam_status = 0;
+ ptp->raw_command.data_length = 0;
+}
+
+bool pt_device_is_nvme(const struct sg_pt_base * vp __attribute__ ((unused)))
+{
+ return 0;
+}
+
+int
+check_pt_file_handle(int device_fd, const char * device_name, int vb)
+{
+ if (device_fd) {}
+ if (device_name) {}
+ if (vb) {}
+ return 1; /* guess it is a SCSI generic pass-though device */
+}
+
+int
+do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose)
+{
+ if (vp) { }
+ if (submq) { }
+ if (timeout_secs) { }
+ if (verbose) { }
+ return SCSI_PT_DO_NOT_SUPPORTED;
+}
+
+void
+get_pt_actual_lengths(const struct sg_pt_base * vp, int * act_dinp,
+ int * act_doutp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (ptp->data_len == 0) {
+ if (act_dinp)
+ *act_dinp = 0;
+ if (act_doutp)
+ *act_doutp = 0;
+ } else if (act_dinp && (ptp->raw_command.flags & B_RAW_DEVICE_DATA_IN)) {
+ /* "For various reasons Haiku return data_len - data_resid" */
+ *act_dinp = ptp->raw_command.data_length;
+ if (act_doutp)
+ *act_doutp = 0;
+ } else if (act_doutp &&
+ !(ptp->raw_command.flags & B_RAW_DEVICE_DATA_IN)) {
+ *act_doutp = ptp->raw_command.data_length;
+ if (act_dinp)
+ *act_dinp = 0;
+ } else {
+ if (act_dinp)
+ *act_dinp = 0;
+ if (act_doutp)
+ *act_doutp = 0;
+ }
+}
+
+/* If not available return 0 otherwise return number of nanoseconds that the
+ * lower layers (and hardware) took to execute the command just completed. */
+uint64_t
+get_pt_duration_ns(const struct sg_pt_base * vp __attribute__ ((unused)))
+{
+ return 0;
+}
+
+/* Valid file handles (which is the return value) are >= 0 . Returns -1
+ * if there is no valid file handle. */
+int
+get_pt_file_handle(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ return ptp->dev_fd;
+}
+
+
+/* If a NVMe block device (which includes the NSID) handle is associated
+ * with 'vp', then its NSID is returned (values range from 0x1 to
+ * 0xffffffe). Otherwise 0 is returned. */
+uint32_t
+get_pt_nvme_nsid(const struct sg_pt_base * vp)
+{
+ if (vp) { }
+ return 0;
+}
+
+void
+get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp,
+ int * req_doutp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (ptp->data_len == 0) {
+ if (req_dinp)
+ *req_dinp = 0;
+ if (req_doutp)
+ *req_doutp = 0;
+ } else if (req_dinp && (ptp->raw_command.flags & B_RAW_DEVICE_DATA_IN)) {
+ /* "For various reasons Haiku return data_len - data_resid" */
+ *req_dinp = ptp->data_len;
+ if (req_doutp)
+ *req_doutp = 0;
+ } else if (req_doutp &&
+ !(ptp->raw_command.flags & B_RAW_DEVICE_DATA_IN)) {
+ *req_doutp = ptp->data_len;
+ if (req_dinp)
+ *req_dinp = 0;
+ } else {
+ if (req_dinp)
+ *req_dinp = 0;
+ if (req_doutp)
+ *req_doutp = 0;
+ }
+}
+
+uint32_t
+get_pt_result(const struct sg_pt_base * vp)
+{
+ return (uint32_t)get_scsi_pt_status_response(vp);
+}
+
+uint8_t *
+get_scsi_pt_cdb_buf(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ return (uint8_t *)ptp->raw_command.command;
+}
+
+int
+get_scsi_pt_cdb_len(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ return (int)ptp->raw_command.command_length;
+}
+
+/* Forget any previous dev_han and install the one given. May attempt to
+ * find file type (e.g. if pass-though) from OS so there could be an error.
+ * Returns 0 for success or the same value as get_scsi_pt_os_err()
+ * will return. dev_han should be >= 0 for a valid file handle or -1 . */
+int
+set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb)
+{
+ struct sg_pt_haiku_scsi * ptp = &vp->impl;
+
+ if (vb) {}
+ ptp->dev_fd = (dev_han < 0) ? -1 : dev_han;
+ ptp->in_err = 0;
+ ptp->os_err = 0;
+ return 0;
+}
+
+void
+set_scsi_pt_transport_err(struct sg_pt_base * vp, int err)
+{
+ if (vp) { }
+ if (err) { }
+}
+
+void
+set_pt_metadata_xfer(struct sg_pt_base * vp, uint8_t * mdxferp,
+ uint32_t mdxfer_len, bool out_true)
+{
+ if (vp) { }
+ if (mdxferp) { }
+ if (mdxfer_len) { }
+ if (out_true) { }
+}
diff --git a/lib/sg_pt_osf1.c b/lib/sg_pt_osf1.c
index 3296e18b..8fa5e3e3 100644
--- a/lib/sg_pt_osf1.c
+++ b/lib/sg_pt_osf1.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005-2020 Douglas Gilbert.
+ * Copyright (c) 2005-2021 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -30,7 +30,7 @@
#include "sg_lib.h"
#include "sg_pr2serr.h"
-/* Version 2.03 20200722 */
+/* Version 2.04 20210617 */
#define OSF1_MAXDEV 64
@@ -660,6 +660,16 @@ check_pt_file_handle(int device_fd, const char * device_name, int vb)
return 0;
}
+/* Valid file handles (which is the return value) are >= 0 . Returns -1
+ * if there is no valid file handle. */
+int
+get_pt_file_handle(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+ return ptp->dev_fd;
+}
+
/* If a NVMe block device (which includes the NSID) handle is associated
* with 'vp', then its NSID is returned (values range from 0x1 to
* 0xffffffe). Otherwise 0 is returned. */
@@ -676,3 +686,37 @@ get_pt_result(const struct sg_pt_base * vp)
if (vp) { }
return 0;
}
+
+/* Forget any previous dev_han and install the one given. May attempt to
+ * find file type (e.g. if pass-though) from OS so there could be an error.
+ * Returns 0 for success or the same value as get_scsi_pt_os_err()
+ * will return. dev_han should be >= 0 for a valid file handle or -1 . */
+int
+set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb)
+{
+ struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+ if (vb) {}
+ ptp->dev_fd = (dev_han < 0) ? -1 : dev_han;
+ ptp->in_err = 0;
+ ptp->os_err = 0;
+ ptp->is_nvme = false;
+ return 0;
+}
+
+void
+set_scsi_pt_transport_err(struct sg_pt_base * vp, int err)
+{
+ if (vp) { }
+ if (err) { }
+}
+
+void
+set_pt_metadata_xfer(struct sg_pt_base * vp, uint8_t * mdxferp,
+ uint32_t mdxfer_len, bool out_true)
+{
+ if (vp) { }
+ if (mdxferp) { }
+ if (mdxfer_len) { }
+ if (out_true) { }
+}
diff --git a/lib/sg_pt_solaris.c b/lib/sg_pt_solaris.c
index 2e5b430d..e6cfa575 100644
--- a/lib/sg_pt_solaris.c
+++ b/lib/sg_pt_solaris.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2020 Douglas Gilbert.
+ * Copyright (c) 2007-2021 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_solaris version 1.14 20200724 */
+/* sg_pt_solaris version 1.15 20210617 */
#include <stdio.h>
#include <stdlib.h>
@@ -539,6 +539,17 @@ check_pt_file_handle(int device_fd, const char * device_name, int vb)
return 0;
}
+/* Valid file handles (which is the return value) are >= 0 . Returns -1
+ * if there is no valid file handle. */
+int
+get_pt_file_handle(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+ return ptp->dev_fd;
+}
+
+
/* If a NVMe block device (which includes the NSID) handle is associated
* with 'vp', then its NSID is returned (values range from 0x1 to
* 0xffffffe). Otherwise 0 is returned. */
@@ -548,3 +559,20 @@ get_pt_nvme_nsid(const struct sg_pt_base * vp)
if (vp) { }
return 0;
}
+
+/* Forget any previous dev_han and install the one given. May attempt to
+ * find file type (e.g. if pass-though) from OS so there could be an error.
+ * Returns 0 for success or the same value as get_scsi_pt_os_err()
+ * will return. dev_han should be >= 0 for a valid file handle or -1 . */
+int
+set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb)
+{
+ struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+ if (vb) {}
+ ptp->dev_fd = (dev_han < 0) ? -1 : dev_han;
+ ptp->in_err = 0;
+ ptp->os_err = 0;
+ ptp->is_nvme = false;
+ return 0;
+}
diff --git a/scripts/Makefile.in b/scripts/Makefile.in
index ece7febc..bf2c5ad0 100644
--- a/scripts/Makefile.in
+++ b/scripts/Makefile.in
@@ -256,6 +256,8 @@ localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
diff --git a/sg3_utils.spec b/sg3_utils.spec
index ddd80ef8..f6b428be 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -84,7 +84,7 @@ fi
%{_libdir}/*.la
%changelog
-* Mon Jun 07 2021 - dgilbert at interlog dot com
+* Wed Jun 23 2021 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.47
diff --git a/src/Makefile.in b/src/Makefile.in
index f0fffb05..a388234c 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -609,6 +609,8 @@ localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
diff --git a/src/sg_luns.c b/src/sg_luns.c
index c538956c..342b24f5 100644
--- a/src/sg_luns.c
+++ b/src/sg_luns.c
@@ -34,7 +34,7 @@
* and decodes the response.
*/
-static const char * version_str = "1.46 20210610"; /* spc6r05 */
+static const char * version_str = "1.47 20210616"; /* spc6r05 */
#define MAX_RLUNS_BUFF_LEN (1024 * 1024)
#define DEF_RLUNS_BUFF_LEN (1024 * 8)
@@ -599,7 +599,12 @@ main(int argc, char * argv[])
sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
if (sg_fd < 0) {
- pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+ int err = -sg_fd;
+
+ pr2serr("open error: %s: %s\n", device_name, safe_strerror(err));
+ if ((! o_readonly) && ((err == EACCES) || (err == EROFS)))
+ pr2serr("Perhaps try again with --readonly option or with root "
+ "permissions\n");
return sg_convert_errno(-sg_fd);
}
if (decode_arg && (! lu_cong_arg_given)) {
diff --git a/testing/sg_mrq_dd.cpp b/testing/sg_mrq_dd.cpp
index 57382f4e..5f906028 100644
--- a/testing/sg_mrq_dd.cpp
+++ b/testing/sg_mrq_dd.cpp
@@ -30,7 +30,7 @@
*
*/
-static const char * version_str = "1.30 20210606";
+static const char * version_str = "1.31 20210620";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -138,6 +138,7 @@ using namespace std;
#define DEF_SCSI_CDB_SZ 10
#define MAX_SCSI_CDB_SZ 16 /* could be 32 */
#define PACK_ID_TID_MULTIPLIER (0x1000000) /* 16,777,216 */
+#define MAX_SLICES 16 /* number of IFILE,OFILE pairs */
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
#define READ_CAP_REPLY_LEN 8
@@ -154,22 +155,20 @@ using namespace std;
#define MAX_NUM_THREADS 1024 /* was SG_MAX_QUEUE with v3 driver */
#define DEF_MRQ_NUM 16
-#ifndef RAW_MAJOR
-#define RAW_MAJOR 255 /*unlikely value */
-#endif
-
+#define FT_UNKNOWN 0 /* yet to be checked */
#define FT_OTHER 1 /* filetype other than one of the following */
#define FT_SG 2 /* filetype is sg char device */
-#define FT_RAW 4 /* filetype is raw char device */
-#define FT_DEV_NULL 8 /* either "/dev/null" or "." as filename */
-#define FT_ST 16 /* filetype is st char device (tape) */
-#define FT_BLOCK 32 /* filetype is a block device */
-#define FT_FIFO 64 /* fifo (named or unnamed pipe (stdout)) */
+#define FT_DEV_NULL 4 /* either /dev/null, /dev/zero, or "." */
+#define FT_ST 8 /* filetype is st char device (tape) */
+#define FT_BLOCK 16 /* filetype is a block device */
+#define FT_FIFO 32 /* fifo (named or unnamed pipe (stdout)) */
+#define FT_CHAR 64 /* fifo (named or unnamed pipe (stdout)) */
#define FT_RANDOM_0_FF 128 /* iflag=00, iflag=ff and iflag=random
override if=IFILE */
#define FT_ERROR 256 /* couldn't "stat" file */
#define DEV_NULL_MINOR_NUM 3
+#define DEV_ZERO_MINOR_NUM 5
#define EBUFF_SZ 768
@@ -199,15 +198,44 @@ struct flags_t {
bool qtail;
bool random;
bool serial;
+ bool same_fds;
bool wq_excl;
bool zero;
int cdl; /* command duration limits, 0 --> no cdl */
int mmap;
};
-typedef pair<int64_t, int> get_next_res;
+typedef pair<int64_t, int> get_next_res_t; /* LBA, num */
typedef array<uint8_t, MAX_SCSI_CDB_SZ> cdb_arr_t;
+struct cp_ver_pair_t {
+ cp_ver_pair_t() {}
+
+ get_next_res_t get_next(int desired_num_blks);
+
+ enum class my_state {empty,
+ init,
+ underway,
+ ignore,
+ finished} state = {my_state::empty};
+
+ int my_index = 0;
+ int in_fd = -1;
+ int in_type = FT_UNKNOWN;
+ int out_fd = -1;
+ int out_type = FT_UNKNOWN;
+
+ int64_t dd_count = 0;
+ atomic<int64_t> next_count_pos {};
+ atomic<int64_t> in_rem_count {};
+ atomic<int64_t> out_rem_count {};
+ atomic<int> in_partial {};
+ atomic<int> out_partial {};
+ atomic<int> sum_of_resids {};
+};
+
+typedef array<cp_ver_pair_t, MAX_SLICES> cp_ver_arr_t;
+
/* There is one instance of this structure and it is at file scope so it is
* initialized to zero. The design of this copy multi-threaded copy algorithm
* attempts to have no locks on the fast path. Contention in gcoll.get_next()
@@ -216,6 +244,8 @@ typedef array<uint8_t, MAX_SCSI_CDB_SZ> cdb_arr_t;
* can occur at that point. */
struct global_collection /* one instance visible to all threads */
{
+ cp_ver_arr_t cp_ver_arr;
+
/* get_next() is the pivotal function for multi-threaded safety. It can
* be safely called from all threads with the desired number of blocks
* (typically mrq*bpt) and this function returns a pair. The first pair
@@ -225,24 +255,20 @@ struct global_collection /* one instance visible to all threads */
* returned pair is 0 then the calling thread should shutdown; a
* negative value indicates an error has occurred (e.g. in another
* thread) and the calling thread should shutdown. */
- get_next_res get_next(int desired_num_blks);
- atomic<int64_t> next_count_pos;
- int infd;
+ int in0fd;
int64_t dd_count;
- int in_type;
+ int in_type; /* expect all IFILEs to have same type */
int cdbsz_in;
int help;
struct flags_t in_flags;
- atomic<int64_t> in_rem_count; /* | count of remaining in blocks */
atomic<int> in_partial; /* | */
off_t in_st_size; /* Only for FT_OTHER (regular) file */
int mrq_num; /* if user gives 0, set this to 1 */
- int outfd;
+ int out0fd;
int out_type;
int cdbsz_out;
struct flags_t out_flags;
- atomic<int64_t> out_rem_count; /* | count of remaining out blocks */
atomic<int> out_partial; /* | */
off_t out_st_size; /* Only for FT_OTHER (regular) file */
condition_variable infant_cv; /* after thread:0 does first segment */
@@ -274,6 +300,8 @@ struct global_collection /* one instance visible to all threads */
bool unit_nanosec; /* default duration unit is millisecond */
bool verify; /* don't copy, verify like Unix: cmp */
bool prefetch; /* for verify: do PF(b),RD(a),V(b)_a_data */
+ vector<string> inf_v;
+ vector<string> outf_v;
const char * infp;
const char * outfp;
class scat_gath_list i_sgl;
@@ -361,10 +389,6 @@ static sigset_t signal_set;
static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
-static int sg_in_open(struct global_collection *clp, const char *inf,
- uint8_t **mmpp, int *mmap_len, bool move_data);
-static int sg_out_open(struct global_collection *clp, const char *outf,
- uint8_t **mmpp, int *mmap_len, bool move_data);
static int do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
scat_gath_iter & o_sg_it, int seg_blks,
vector<cdb_arr_t> & a_cdb,
@@ -423,6 +447,234 @@ pr2serr_lk(const char * fmt, ...)
}
static void
+usage(int pg_num)
+{
+ if (pg_num > 4)
+ goto page5;
+ if (pg_num > 3)
+ goto page4;
+ else if (pg_num > 2)
+ goto page3;
+ else if (pg_num > 1)
+ goto page2;
+
+ pr2serr("Usage: sg_mrq_dd [bs=BS] [conv=CONV] [count=COUNT] [ibs=BS] "
+ "[if=IFILE*]\n"
+ " [iflag=FLAGS] [obs=BS] [of=OFILE*] "
+ "[oflag=FLAGS]\n"
+ " [seek=SEEK] [skip=SKIP] [--help] [--verify] "
+ "[--version]\n\n");
+ pr2serr(" [bpt=BPT] [cdbsz=6|10|12|16] [cdl=CDL] "
+ "[dio=0|1]\n"
+ " [elemsz_kb=EKB] [ese=0|1] [fua=0|1|2|3] "
+ "[hipri=NRQS]\n"
+ " [mrq=NRQS] [ofreg=OFREG] [sdt=SDT] "
+ "[sync=0|1]\n"
+ " [thr=THR] [time=0|1|2[,TO]] [verbose=VERB] "
+ "[--dry-run]\n"
+ " [--pre-fetch] [--verbose] [--version]\n\n"
+ " where: operands have the form name=value and are pecular to "
+ "'dd'\n"
+ " style commands, and options start with one or "
+ "two hyphens;\n"
+ " the main operands and options (shown in first group "
+ "above) are:\n"
+ " bs must be device logical block size (default "
+ "512)\n"
+ " conv comma separated list from: [nocreat,noerror,"
+ "notrunc,\n"
+ " null,sync]\n"
+ " count number of blocks to copy (def: device size)\n"
+ " if file(s) or device(s) to read from (def: "
+ "stdin)\n"
+ " iflag comma separated list from: [00,coe,dio,"
+ "direct,dpo,\n"
+ " dsync,excl,ff,fua,masync,mmap,mout_if,nodur,"
+ "null,\n"
+ " order,qhead,qtail,random,same_fds,serial,"
+ "wq_excl]\n"
+ " of file(s) or device(s) to write to (def: "
+ "/dev/null)\n"
+ " 'of=.' also outputs to /dev/null\n"
+ " oflag comma separated list from: [append,nocreat,\n"
+ " <<list from iflag>>]\n"
+ " seek block position to start writing to OFILE\n"
+ " skip block position to start reading from IFILE\n"
+ " --help|-h output this usage message then exit\n"
+ " --verify|-x do a verify (compare) operation [def: do a "
+ "copy]\n"
+ " --version|-V output version string then exit\n\n"
+ "Copy IFILE to OFILE, similar to dd command. A comma separated "
+ "list of files\n may be given for IFILE*, ditto for OFILE*. "
+ "This utility is specialized for\nSCSI devices and uses the "
+ "'multiple requests' (mrq) in a single invocation\nfacility in "
+ "version 4 of the sg driver unless mrq=0. Usually one or both\n"
+ "IFILE and OFILE will be sg devices. With the --verify option "
+ "it does a\nverify/compare operation instead of a copy. This "
+ "utility is Linux specific.\nUse '-hh', '-hhh', '-hhhh' or "
+ "'-hhhhh' for more information.\n"
+ );
+ return;
+page2:
+ pr2serr("Syntax: sg_mrq_dd [operands] [options]\n\n"
+ " the lesser used operands and option are:\n\n"
+ " bpt is blocks_per_transfer (default is 128)\n"
+ " cdbsz size of SCSI READ, WRITE or VERIFY cdb_s "
+ "(default is 10)\n"
+ " cdl command duration limits value 0 to 7 (def: "
+ "0 (no cdl))\n"
+ " dio is direct IO, 1->attempt, 0->indirect IO (def)\n"
+ " elemsz_kb=EKB scatter gather list element size in "
+ "kibibytes;\n"
+ " must be power of two, >= page_size "
+ "(typically 4)\n"
+ " ese=0|1 exit on secondary error when 1, else continue\n"
+ " fua force unit access: 0->don't(def), 1->OFILE, "
+ "2->IFILE,\n"
+ " 3->OFILE+IFILE\n"
+ " ibs IFILE logical block size, cannot differ from "
+ "obs or bs\n"
+ " hipri similar to mrq=NRQS operand but also sets "
+ "hipri flag\n"
+ " mrq NRQS is number of cmds placed in each sg "
+ "ioctl\n"
+ " (def: 16). Does not set mrq hipri flag.\n"
+ " if mrq=0 does one-by-one, blocking "
+ "ioctl(SG_IO)s\n"
+ " obs OFILE logical block size, cannot differ from "
+ "ibs or bs\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"
+ " sdt stall detection times: CRT[,ICT]. CRT: check "
+ "repetition\n"
+ " time (after first) in seconds; ICT: initial "
+ "check time\n"
+ " in milliseconds. Default: 3,300 . Use CRT=0 "
+ "to disable\n"
+ " sync 0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
+ "after copy\n"
+ " thr is number of threads, must be > 0, default 4, "
+ "max 1024\n"
+ " time 0->no timing; 1/2->millisec/nanosec precision "
+ "(def: 1);\n"
+ " TO is command timeout in seconds (def: 60)\n"
+ " verbose increase verbosity (def: VERB=0)\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', '-hhhh' or '-hhhhh' for more information about "
+ "flags.\n"
+ );
+ return;
+page3:
+ pr2serr("Syntax: sg_mrq_dd [operands] [options]\n\n"
+ " where: 'iflag=<arg>' and 'oflag=<arg>' arguments are listed "
+ "below:\n\n"
+ " 00 use all zeros instead of if=IFILE (only in "
+ "iflag)\n"
+ " append append output to OFILE (assumes OFILE is "
+ "regular file)\n"
+ " coe continue of error (reading, fills with zeros)\n"
+ " dio sets the SG_FLAG_DIRECT_IO in sg requests\n"
+ " direct sets the O_DIRECT flag on open()\n"
+ " dpo sets the DPO (disable page out) in SCSI READs "
+ "and WRITEs\n"
+ " dsync sets the O_SYNC flag on open()\n"
+ " excl sets the O_EXCL flag on open()\n"
+ " ff use all 0xff bytes instead of if=IFILE (only in "
+ "iflag)\n"
+ " fua sets the FUA (force unit access) in SCSI READs "
+ "and WRITEs\n"
+ " hipri set HIPRI flag and use blk_poll() for "
+ "completions\n"
+ " masync set 'more async' flag on this sg device\n"
+ " mmap setup mmap IO on IFILE or OFILE\n"
+ " mmap,mmap when used twice, doesn't call munmap()\n"
+ " mout_if set META_OUT_IF flag on control object\n"
+ " nocreat will fail rather than create OFILE\n"
+ " nodur turns off command duration calculations\n"
+ " no_thresh skip checking per fd max data xfer size\n"
+ " order require write ordering on sg->sg copy; only "
+ "for oflag\n"
+ " qhead queue new request at head of block queue\n"
+ " qtail queue new request at tail of block queue (def: "
+ "q at head)\n"
+ " random use random data instead of if=IFILE (only in "
+ "iflag)\n"
+ " same_fds each thread of a IOFILE pair uses same fds\n"
+ " serial serialize sg command execution (def: overlap)\n"
+ " wq_excl set SG_CTL_FLAGM_EXCL_WAITQ on this sg fd\n"
+ "\n"
+ "Copies IFILE to OFILE (and to OFILE2 if given). If IFILE and "
+ "OFILE are sg\ndevices 'shared' mode is selected. "
+ "When sharing, the data stays in a\nsingle "
+ "in-kernel buffer which is copied (or mmap-ed) to the user "
+ "space\nif the 'ofreg=OFREG' is given. Use '-hhhh' or '-hhhhh' "
+ "for more information.\n"
+ );
+ return;
+page4:
+ pr2serr("pack_id:\n"
+ "These are ascending integers, starting at 1, associated with "
+ "each issued\nSCSI command. When both IFILE and OFILE are sg "
+ "devices, then the READ in\neach read-write pair is issued an "
+ "even pack_id and its WRITE pair is\ngiven the pack_id one "
+ "higher (i.e. an odd number). This enables a\n'cat '"
+ "/proc/scsi/sg/debug' user to see that progress is being "
+ "made.\n\n");
+ pr2serr("Debugging:\n"
+ "Apart from using one or more '--verbose' options which gets a "
+ "bit noisy\n'cat /proc/scsi/sg/debug' can give a good overview "
+ "of what is happening.\nThat does a sg driver object tree "
+ "traversal that does minimal locking\nto make sure that each "
+ "traversal is 'safe'. So it is important to note\nthe whole "
+ "tree is not locked. This means for fast devices the overall\n"
+ "tree state may change while the traversal is occurring. For "
+ "example,\nit has been observed that both the read- and write- "
+ "sides of a request\nshare show they are in 'active' state "
+ "which should not be possible.\nIt occurs because the read-side "
+ "probably jumped out of active state and\nthe write-side "
+ "request entered it while some other nodes were being "
+ "printed.\n\n");
+ pr2serr("Busy state:\n"
+ "Busy state (abbreviated to 'bsy' in the /proc/scsi/sg/debug "
+ "output)\nis entered during request setup and completion. It "
+ "is intended to be\na temporary state. It should not block "
+ "but does sometimes (e.g. in\nblock_get_request()). Even so "
+ "that blockage should be short and if not\nthere is a "
+ "problem.\n\n");
+ pr2serr("--verify :\n"
+ "For comparing IFILE with OFILE. Does repeated sequences of: "
+ "READ(ifile)\nand uses data returned to send to VERIFY(ofile, "
+ "BYTCHK=1). So the OFILE\ndevice/disk is doing the actual "
+ "comparison. Stops on first miscompare\nunless oflag=coe is "
+ "given\n\n");
+ pr2serr("--prefetch :\n"
+ "Used with --verify option. Prepends a PRE-FETCH(ofile, IMMED) "
+ "to verify\nsequence. This should speed the trailing VERIFY by "
+ "making sure that\nthe data it needs for the comparison is "
+ "already in its cache.\n");
+ return;
+page5:
+ pr2serr(" IFILE and/or OFILE lists\n\n"
+ "For dd, its if= operand takes a single file (or device), ditto "
+ "for the of=\noperand. This utility extends that to "
+ "allowing a comma separated list\nof files. Ideally if multiple "
+ "IFILEs are given, the same number of OFILEs\nshould be given. "
+ "Simple expansions occur to make the list lengths equal\n"
+ "(e.g. if 5 IFILEs are given but no OFILEs, then OFILEs is "
+ "expanded to 5\n'/dev/null' files). IFILE,OFILE pairs with "
+ "the same list position are\ncalled a 'slice'. Each slice is "
+ "processed (i.e. copy or verify) in one or\nmore threads. The "
+ "number of threads must be >= the number of slices. Best\nif "
+ "the number of threads is an integer multiple of the number of "
+ "slices.\nThe file type of multiple IFILEs must be the same, "
+ "ditto for OFILEs.\nSupport for slices is for testing rather "
+ "than a general mechanism.\n");
+}
+
+static void
lk_print_command_len(const char *prefix, uint8_t * cmdp, int len, bool lock)
{
if (lock) {
@@ -729,7 +981,14 @@ calc_duration_throughput(int contin)
}
a = res_tm.tv_sec;
a += (0.000001 * res_tm.tv_usec);
- b = (double)gcoll.bs * (gcoll.dd_count - gcoll.out_rem_count.load());
+
+ b = 0.0;
+ for (auto && cvp : gcoll.cp_ver_arr) {
+ if (cvp.state == cp_ver_pair_t::my_state::empty)
+ break;
+ b += (double)(cvp.dd_count - cvp.out_rem_count.load());
+ }
+ b *= (double)gcoll.bs;
pr2serr("time to %s data %s %d.%06d secs",
(gcoll.verify ? "verify" : "copy"), (contin ? "so far" : "was"),
(int)res_tm.tv_sec, (int)res_tm.tv_usec);
@@ -742,22 +1001,35 @@ calc_duration_throughput(int contin)
static void
print_stats(const char * str)
{
+ bool show_slice = (gcoll.cp_ver_arr.size() > 1);
+ int k = 0;
int64_t infull, outfull;
- if (0 != gcoll.out_rem_count.load())
- pr2serr(" remaining block count=%" PRId64 "\n",
- gcoll.out_rem_count.load());
- infull = gcoll.dd_count - gcoll.in_rem_count.load();
- pr2serr("%s%" PRId64 "+%d records in\n", str,
- infull, gcoll.in_partial.load());
-
- if (gcoll.out_type == FT_DEV_NULL)
- pr2serr("%s0+0 records out\n", str);
- else {
- outfull = gcoll.dd_count - gcoll.out_rem_count.load();
- pr2serr("%s%" PRId64 "+%d records %s\n", str,
- outfull, gcoll.out_partial.load(),
- (gcoll.verify ? "verified" : "out"));
+ for (auto && cvp : gcoll.cp_ver_arr) {
+ ++k;
+ if (cvp.state == cp_ver_pair_t::my_state::empty)
+ break;
+ if (cvp.state == cp_ver_pair_t::my_state::ignore) {
+ pr2serr(">>> IGNORING slice: %d\n", k);
+ continue;
+ }
+ if (show_slice)
+ pr2serr(">>> slice: %d\n", k);
+ if (0 != cvp.out_rem_count.load())
+ pr2serr(" remaining block count=%" PRId64 "\n",
+ cvp.out_rem_count.load());
+ infull = cvp.dd_count - cvp.in_rem_count.load();
+ pr2serr("%s%" PRId64 "+%d records in\n", str,
+ infull, cvp.in_partial.load());
+
+ if (cvp.out_type == FT_DEV_NULL)
+ pr2serr("%s0+0 records out\n", str);
+ else {
+ outfull = cvp.dd_count - cvp.out_rem_count.load();
+ pr2serr("%s%" PRId64 "+%d records %s\n", str,
+ outfull, cvp.out_partial.load(),
+ (gcoll.verify ? "verified" : "out"));
+ }
}
}
@@ -837,14 +1109,14 @@ dd_filetype(const char * filename, off_t & st_size)
return FT_ERROR;
if (S_ISCHR(st.st_mode)) {
if ((MEM_MAJOR == major(st.st_rdev)) &&
- (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
- return FT_DEV_NULL;
- if (RAW_MAJOR == major(st.st_rdev))
- return FT_RAW;
+ ((DEV_NULL_MINOR_NUM == minor(st.st_rdev)) ||
+ (DEV_ZERO_MINOR_NUM == minor(st.st_rdev))))
+ return FT_DEV_NULL; /* treat /dev/null + /dev/zero the same */
if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
return FT_SG;
if (SCSI_TAPE_MAJOR == major(st.st_rdev))
return FT_ST;
+ return FT_CHAR;
} else if (S_ISBLK(st.st_mode))
return FT_BLOCK;
else if (S_ISFIFO(st.st_mode))
@@ -853,215 +1125,229 @@ dd_filetype(const char * filename, off_t & st_size)
return FT_OTHER;
}
-static void
-usage(int pg_num)
+/* Returns reserved_buffer_size/mmap_size if success, else 0 for failure */
+static int
+sg_prepare_resbuf(int fd, struct global_collection *clp, bool is_in,
+ uint8_t **mmpp)
{
- if (pg_num > 3)
- goto page4;
- else if (pg_num > 2)
- goto page3;
- else if (pg_num > 1)
- goto page2;
+ static bool done = false;
+ bool no_dur = is_in ? clp->in_flags.no_dur : clp->out_flags.no_dur;
+ bool masync = is_in ? clp->in_flags.masync : clp->out_flags.masync;
+ bool wq_excl = is_in ? clp->in_flags.wq_excl : clp->out_flags.wq_excl;
+ bool skip_thresh = is_in ? clp->in_flags.no_thresh :
+ clp->out_flags.no_thresh;
+ int elem_sz = clp->elem_sz;
+ int res, t, num, err;
+ uint8_t *mmp;
+ struct sg_extended_info sei {};
+ struct sg_extended_info * seip = &sei;
- pr2serr("Usage: sg_mrq_dd [bs=BS] [conv=CONV] [count=COUNT] [ibs=BS] "
- "[if=IFILE]\n"
- " [iflag=FLAGS] [obs=BS] [of=OFILE] "
- "[oflag=FLAGS]\n"
- " [seek=SEEK] [skip=SKIP] [--help] [--verify] "
- "[--version]\n\n");
- pr2serr(" [bpt=BPT] [cdbsz=6|10|12|16] [cdl=CDL] "
- "[dio=0|1]\n"
- " [elemsz_kb=EKB] [ese=0|1] [fua=0|1|2|3] "
- "[hipri=NRQS]\n"
- " [mrq=NRQS] [ofreg=OFREG] [sdt=SDT] "
- "[sync=0|1]\n"
- " [thr=THR] [time=0|1|2[,TO]] [verbose=VERB] "
- "[--dry-run]\n"
- " [--pre-fetch] [--verbose] [--version]\n\n"
- " where: operands have the form name=value and are pecular to "
- "'dd'\n"
- " style commands, and options start with one or "
- "two hyphens;\n"
- " the main operands and options (shown in first group "
- "above) are:\n"
- " bs must be device logical block size (default "
- "512)\n"
- " conv comma separated list from: [nocreat,noerror,"
- "notrunc,\n"
- " null,sync]\n"
- " count number of blocks to copy (def: device size)\n"
- " if file or device to read from (def: stdin)\n"
- " iflag comma separated list from: [00,coe,dio,"
- "direct,dpo,\n"
- " dsync,excl,ff,fua,masync,mmap,mout_if,nodur,"
- "null,\n"
- " order,qhead,qtail,random,serial,wq_excl]\n"
- " of file or device to write to (def: /dev/null "
- "N.B. different\n"
- " from dd it defaults to stdout). If 'of=.' "
- "uses /dev/null\n"
- " oflag comma separated list from: [append,nocreat,\n"
- " <<list from iflag>>]\n"
- " seek block position to start writing to OFILE\n"
- " skip block position to start reading from IFILE\n"
- " --help|-h output this usage message then exit\n"
- " --verify|-x do a verify (compare) operation [def: do a "
- "copy]\n"
- " --version|-V output version string then exit\n\n"
- "Copy IFILE to OFILE, similar to dd command. This utility is "
- "specialized for\nSCSI devices and uses the 'multiple requests' "
- "(mrq) in a single invocation\nfacility in version 4 of the sg "
- "driver unless mrq=0. Usually one or both\nIFILE and OFILE will "
- "be sg devices. With the --verify option it does a\n"
- "verify/compare operation instead of a copy. This utility is "
- "Linux specific.\nUse '-hh', '-hhh' or '-hhhh' for more "
- "information.\n"
- );
- return;
-page2:
- pr2serr("Syntax: sg_mrq_dd [operands] [options]\n\n"
- " the lesser used operands and option are:\n\n"
- " bpt is blocks_per_transfer (default is 128)\n"
- " cdbsz size of SCSI READ, WRITE or VERIFY cdb_s "
- "(default is 10)\n"
- " cdl command duration limits value 0 to 7 (def: "
- "0 (no cdl))\n"
- " dio is direct IO, 1->attempt, 0->indirect IO (def)\n"
- " elemsz_kb=EKB scatter gather list element size in "
- "kibibytes;\n"
- " must be power of two, >= page_size "
- "(typically 4)\n"
- " ese=0|1 exit on secondary error when 1, else continue\n"
- " fua force unit access: 0->don't(def), 1->OFILE, "
- "2->IFILE,\n"
- " 3->OFILE+IFILE\n"
- " ibs IFILE logical block size, cannot differ from "
- "obs or bs\n"
- " hipri similar to mrq=NRQS operand but also sets "
- "hipri flag\n"
- " mrq NRQS is number of cmds placed in each sg "
- "ioctl\n"
- " (def: 16). Does not set mrq hipri flag.\n"
- " if mrq=0 does one-by-one, blocking "
- "ioctl(SG_IO)s\n"
- " obs OFILE logical block size, cannot differ from "
- "ibs or bs\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"
- " sdt stall detection times: CRT[,ICT]. CRT: check "
- "repetition\n"
- " time (after first) in seconds; ICT: initial "
- "check time\n"
- " in milliseconds. Default: 3,300 . Use CRT=0 "
- "to disable\n"
- " sync 0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
- "after copy\n"
- " thr is number of threads, must be > 0, default 4, "
- "max 1024\n"
- " time 0->no timing; 1/2->millisec/nanosec precision "
- "(def: 1);\n"
- " TO is command timeout in seconds (def: 60)\n"
- " verbose increase verbosity (def: VERB=0)\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"
- );
- return;
-page3:
- pr2serr("Syntax: sg_mrq_dd [operands] [options]\n\n"
- " where: 'iflag=<arg>' and 'oflag=<arg>' arguments are listed "
- "below:\n\n"
- " 00 use all zeros instead of if=IFILE (only in "
- "iflag)\n"
- " append append output to OFILE (assumes OFILE is "
- "regular file)\n"
- " coe continue of error (reading, fills with zeros)\n"
- " dio sets the SG_FLAG_DIRECT_IO in sg requests\n"
- " direct sets the O_DIRECT flag on open()\n"
- " dpo sets the DPO (disable page out) in SCSI READs "
- "and WRITEs\n"
- " dsync sets the O_SYNC flag on open()\n"
- " excl sets the O_EXCL flag on open()\n"
- " ff use all 0xff bytes instead of if=IFILE (only in "
- "iflag)\n"
- " fua sets the FUA (force unit access) in SCSI READs "
- "and WRITEs\n"
- " hipri set HIPRI flag and use blk_poll() for "
- "completions\n"
- " masync set 'more async' flag on this sg device\n"
- " mmap setup mmap IO on IFILE or OFILE\n"
- " mmap,mmap when used twice, doesn't call munmap()\n"
- " mout_if set META_OUT_IF flag on control object\n"
- " nocreat will fail rather than create OFILE\n"
- " nodur turns off command duration calculations\n"
- " no_thresh skip checking per fd max data xfer size\n"
- " order require write ordering on sg->sg copy; only "
- "for oflag\n"
- " qhead queue new request at head of block queue\n"
- " qtail queue new request at tail of block queue (def: "
- "q at head)\n"
- " random use random data instead of if=IFILE (only in "
- "iflag)\n"
- " serial serialize sg command execution (def: overlap)\n"
- " wq_excl set SG_CTL_FLAGM_EXCL_WAITQ on this sg fd\n"
- "\n"
- "Copies IFILE to OFILE (and to OFILE2 if given). If IFILE and "
- "OFILE are sg\ndevices 'shared' mode is selected. "
- "When sharing, the data stays in a\nsingle "
- "in-kernel buffer which is copied (or mmap-ed) to the user "
- "space\nif the 'ofreg=OFREG' is given. Use '-hhhh' for more "
- "information.\n"
- );
- return;
-page4:
- pr2serr("pack_id:\n"
- "These are ascending integers, starting at 1, associated with "
- "each issued\nSCSI command. When both IFILE and OFILE are sg "
- "devices, then the READ in\neach read-write pair is issued an "
- "even pack_id and its WRITE pair is\ngiven the pack_id one "
- "higher (i.e. an odd number). This enables a\n'cat '"
- "/proc/scsi/sg/debug' user to see that progress is being "
- "made.\n\n");
- pr2serr("Debugging:\n"
- "Apart from using one or more '--verbose' options which gets a "
- "bit noisy\n'cat /proc/scsi/sg/debug' can give a good overview "
- "of what is happening.\nThat does a sg driver object tree "
- "traversal that does minimal locking\nto make sure that each "
- "traversal is 'safe'. So it is important to note\nthe whole "
- "tree is not locked. This means for fast devices the overall\n"
- "tree state may change while the traversal is occurring. For "
- "example,\nit has been observed that both the read- and write- "
- "sides of a request\nshare show they are in 'active' state "
- "which should not be possible.\nIt occurs because the read-side "
- "probably jumped out of active state and\nthe write-side "
- "request entered it while some other nodes were being "
- "printed.\n\n");
- pr2serr("Busy state:\n"
- "Busy state (abbreviated to 'bsy' in the /proc/scsi/sg/debug "
- "output)\nis entered during request setup and completion. It "
- "is intended to be\na temporary state. It should not block "
- "but does sometimes (e.g. in\nblock_get_request()). Even so "
- "that blockage should be short and if not\nthere is a "
- "problem.\n\n");
- pr2serr("--verify :\n"
- "For comparing IFILE with OFILE. Does repeated sequences of: "
- "READ(ifile)\nand uses data returned to send to VERIFY(ofile, "
- "BYTCHK=1). So the OFILE\ndevice/disk is doing the actual "
- "comparison. Stops on first miscompare\nunless oflag=coe is "
- "given\n\n");
- pr2serr("--prefetch :\n"
- "Used with --verify option. Prepends a PRE-FETCH(ofile, IMMED) "
- "to verify\nsequence. This should speed the trailing VERIFY by "
- "making sure that\nthe data it needs for the comparison is "
- "already in its cache.\n");
- return;
+ res = ioctl(fd, SG_GET_VERSION_NUM, &t);
+ if ((res < 0) || (t < 40000)) {
+ if (ioctl(fd, SG_GET_RESERVED_SIZE, &num) < 0) {
+ perror("SG_GET_RESERVED_SIZE ioctl failed");
+ return 0;
+ }
+ if (! done) {
+ done = true;
+ pr2serr_lk("%ssg driver prior to 4.0.00, reduced functionality\n",
+ my_name);
+ }
+ goto bypass;
+ }
+ if (elem_sz >= 4096) {
+ seip->sei_rd_mask |= SG_SEIM_SGAT_ELEM_SZ;
+ res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
+ if (res < 0)
+ pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) rd "
+ "error: %s\n", __func__, strerror(errno));
+ if (elem_sz != (int)seip->sgat_elem_sz) {
+ seip->sei_wr_mask |= SG_SEIM_SGAT_ELEM_SZ;
+ seip->sgat_elem_sz = elem_sz;
+ res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
+ if (res < 0)
+ pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) "
+ "wr error: %s\n", __func__, strerror(errno));
+ }
+ }
+ if (no_dur || masync || skip_thresh) {
+ seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
+ if (no_dur) {
+ seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_DURATION;
+ seip->ctl_flags |= SG_CTL_FLAGM_NO_DURATION;
+ }
+ if (masync) {
+ seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_MORE_ASYNC;
+ seip->ctl_flags |= SG_CTL_FLAGM_MORE_ASYNC;
+ }
+ if (wq_excl) {
+ seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_EXCL_WAITQ;
+ seip->ctl_flags |= SG_CTL_FLAGM_EXCL_WAITQ;
+ }
+ if (skip_thresh) {
+ seip->tot_fd_thresh = 0;
+ sei.sei_wr_mask |= SG_SEIM_TOT_FD_THRESH;
+ }
+ res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
+ if (res < 0)
+ pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(NO_DURATION) "
+ "error: %s\n", __func__, strerror(errno));
+ }
+bypass:
+ num = clp->bs * clp->bpt;
+ res = ioctl(fd, SG_SET_RESERVED_SIZE, &num);
+ if (res < 0) {
+ perror("sg_mrq_dd: SG_SET_RESERVED_SIZE error");
+ return 0;
+ } else {
+ int nn;
+
+ res = ioctl(fd, SG_GET_RESERVED_SIZE, &nn);
+ if (res < 0) {
+ perror("sg_mrq_dd: SG_GET_RESERVED_SIZE error");
+ return 0;
+ }
+ if (nn < num) {
+ pr2serr_lk("%s: SG_GET_RESERVED_SIZE shows size truncated, "
+ "wanted %d got %d\n", __func__, num, nn);
+ return 0;
+ }
+ if (mmpp) {
+ mmp = (uint8_t *)mmap(NULL, num, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (MAP_FAILED == mmp) {
+ err = errno;
+ pr2serr_lk("sg_mrq_dd: %s: sz=%d, fd=%d, mmap() failed: %s\n",
+ __func__, num, fd, strerror(err));
+ return 0;
+ }
+ *mmpp = mmp;
+ }
+ }
+ t = 1;
+ res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
+ if (res < 0)
+ perror("sg_mrq_dd: SG_SET_FORCE_PACK_ID error");
+ if (clp->unit_nanosec) {
+ seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
+ seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS;
+ seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
+ if (ioctl(fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ res = -1;
+ pr2serr_lk("ioctl(EXTENDED(TIME_IN_NS)) failed, errno=%d %s\n",
+ errno, strerror(errno));
+ }
+ }
+ if (clp->verbose) {
+ t = 1;
+ /* more info in /proc/scsi/sg/debug */
+ res = ioctl(fd, SG_SET_DEBUG, &t);
+ if (res < 0)
+ perror("sg_mrq_dd: SG_SET_DEBUG error");
+ }
+ return (res < 0) ? 0 : num;
+}
+
+static int
+sg_in_open(struct global_collection *clp, const string & inf, uint8_t **mmpp,
+ int * mmap_lenp)
+{
+ int fd, err, n;
+ int flags = O_RDWR;
+ char ebuff[EBUFF_SZ];
+ const char * fnp = inf.data();
+
+ 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 ((fd = open(fnp, flags)) < 0) {
+ err = errno;
+ snprintf(ebuff, EBUFF_SZ, "%s: could not open %s for sg reading",
+ __func__, fnp);
+ perror(ebuff);
+ return -sg_convert_errno(err);
+ }
+ n = sg_prepare_resbuf(fd, clp, true, mmpp);
+ if (n <= 0)
+ return -SG_LIB_FILE_ERROR;
+ if (mmap_lenp)
+ *mmap_lenp = n;
+ return fd;
+}
+
+static int
+sg_out_open(struct global_collection *clp, const string & outf,
+ uint8_t **mmpp, int * mmap_lenp)
+{
+ int fd, err, n;
+ int flags = O_RDWR;
+ char ebuff[EBUFF_SZ];
+ const char * fnp = outf.data();
+
+ 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 ((fd = open(fnp, flags)) < 0) {
+ err = errno;
+ snprintf(ebuff, EBUFF_SZ, "%s: could not open %s for sg %s",
+ __func__, fnp, (clp->verify ? "verifying" : "writing"));
+ perror(ebuff);
+ return -sg_convert_errno(err);
+ }
+ n = sg_prepare_resbuf(fd, clp, false, mmpp);
+ if (n <= 0)
+ return -SG_LIB_FILE_ERROR;
+ if (mmap_lenp)
+ *mmap_lenp = n;
+ return fd;
}
+static int
+reg_file_open(struct global_collection *clp, const string & fn_s,
+ bool for_wr)
+{
+ int fd, flags;
+ char ebuff[EBUFF_SZ];
-get_next_res
-global_collection::get_next(int desired_num_blks)
+ if (for_wr) {
+ flags = O_WRONLY;
+ if (! clp->out_flags.nocreat)
+ flags |= O_CREAT;
+ if (clp->out_flags.append)
+ flags |= O_APPEND;
+ } else
+ flags = O_RDONLY;
+ 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 (for_wr)
+ fd = open(fn_s.data(), flags, 0666);
+ else
+ fd = open(fn_s.data(), flags);
+ if (fd < 0) {
+ int err = errno;
+ snprintf(ebuff, EBUFF_SZ, "%scould not open %s for %sing ",
+ my_name, fn_s.data(), (for_wr ? "writ" : "read"));
+ perror(ebuff);
+ return -err;
+ }
+ return fd;
+}
+
+get_next_res_t
+cp_ver_pair_t::get_next(int desired_num_blks)
{
int64_t expected, desired;
@@ -1151,6 +1437,16 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
#endif
}
+static void
+flag_all_stop(struct global_collection * clp)
+{
+ for (auto && elem : clp->cp_ver_arr) {
+ if (elem.state == cp_ver_pair_t::my_state::empty)
+ break;
+ elem.next_count_pos.store(-1);
+ }
+}
+
/* Has an infinite loop doing a timed wait for any signals in sig_set. After
* each timeout (300 ms) checks if the most_recent_pack_id atomic integer
* has changed. If not after another two timeouts announces a stall has
@@ -1197,7 +1493,7 @@ sig_listen_thread(struct global_collection * clp)
}
if (SIGINT == sig_number) {
pr2serr_lk("%sinterrupted by SIGINT\n", my_name);
- clp->next_count_pos.store(-1);
+ flag_all_stop(clp);
shutting_down.store(true);
}
} /* end of while loop */
@@ -1249,7 +1545,8 @@ sg_take_snap(int sg_fd, int id, bool vb_b)
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/* Each thread's "main" function */
static void
-read_write_thread(struct global_collection * clp, int id, bool singleton)
+read_write_thread(struct global_collection * clp, int thr_idx, int slice_idx,
+ bool singleton)
{
Rq_elem rel {};
Rq_elem * rep = &rel;
@@ -1260,8 +1557,11 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
bool in_is_sg, in_mmap, out_is_sg, out_mmap;
bool own_outfd = false;
bool only_one_sg = false;
+ struct cp_ver_pair_t & cvp = clp->cp_ver_arr[slice_idx];
class scat_gath_iter i_sg_it(clp->i_sgl);
class scat_gath_iter o_sg_it(clp->o_sgl);
+ const string & inf = clp->inf_v[slice_idx];
+ const string & outf = clp->outf_v[slice_idx];
vector<cdb_arr_t> a_cdb;
vector<struct sg_io_v4> a_v4;
@@ -1272,7 +1572,7 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
out_is_sg = (FT_SG == clp->out_type);
out_mmap = (out_is_sg && (clp->out_flags.mmap > 0));
rep->clp = clp;
- rep->id = id;
+ rep->id = thr_idx;
if (in_is_sg && out_is_sg)
rep->both_sg = true;
@@ -1284,8 +1584,12 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
rep->only_out_sg = true;
}
- if (vb > 2)
- pr2serr_lk("%d <-- Starting worker thread\n", id);
+ if (vb > 2) {
+ pr2serr_lk("%d <-- Starting worker thread, slice=%d\n", thr_idx,
+ slice_idx);
+ if (vb > 3)
+ pr2serr_lk(" %s ---> %s\n", inf.data(), outf.data());
+ }
if (! (rep->both_sg || in_mmap)) {
rep->buffp = sg_memalign(sz, 0 /* page align */, &rep->alloc_bp,
false);
@@ -1294,12 +1598,32 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
return;
}
}
- rep->infd = clp->infd;
- rep->outfd = clp->outfd;
+ rep->infd = clp->in0fd;
+ rep->outfd = clp->out0fd;
rep->outregfd = clp->outregfd;
rep->rep_count = 0;
rep->in_follow_on = -1;
rep->out_follow_on = -1;
+ if (cvp.state == cp_ver_pair_t::my_state::init)
+ cvp.state = cp_ver_pair_t::my_state::underway;
+ if (FT_OTHER == cvp.in_type) {
+ fd = reg_file_open(clp, inf, false);
+ if (fd < 0) {
+ pr2serr_lk("[%d]: unable to open IFILE of slice=%d\n", thr_idx,
+ slice_idx);
+ return;
+ }
+ rep->infd = fd;
+ }
+ if (FT_OTHER == cvp.out_type) {
+ fd = reg_file_open(clp, outf, true);
+ if (fd < 0) {
+ pr2serr_lk("[%d]: unable to open OFILE of slice=%d\n", thr_idx,
+ slice_idx);
+ return;
+ }
+ rep->outfd = fd;
+ }
if (rep->infd == rep->outfd) {
if (in_is_sg)
@@ -1310,80 +1634,100 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
ssize_t ssz = getrandom(&rep->seed, sizeof(rep->seed), GRND_NONBLOCK);
if (ssz < (ssize_t)sizeof(rep->seed)) {
- pr2serr_lk("[%d] %s: getrandom() failed, ret=%d\n", id, __func__,
- (int)ssz);
+ pr2serr_lk("[%d] %s: getrandom() failed, ret=%d\n", thr_idx,
+ __func__, (int)ssz);
rep->seed = (long)time(NULL);
}
#else
rep->seed = (long)time(NULL); /* use seconds since epoch as proxy */
#endif
if (vb > 1)
- pr2serr_lk("[%d] %s: seed=%ld\n", id, __func__, rep->seed);
+ pr2serr_lk("[%d] %s: seed=%ld\n", thr_idx, __func__, rep->seed);
srand48_r(rep->seed, &rep->drand);
}
- if (in_is_sg && clp->infp) {
- fd = sg_in_open(clp, clp->infp, (in_mmap ? &rep->buffp : NULL),
- (in_mmap ? &rep->mmap_len : NULL), true);
- if (fd < 0)
- goto fini;
+ if (in_is_sg && inf.size()) {
+ if ((clp->in_flags.same_fds || (0 == thr_idx)) &&
+ (cvp.in_fd >= 0))
+ fd = cvp.in_fd;
+ else {
+ fd = sg_in_open(clp, inf, (in_mmap ? &rep->buffp : NULL),
+ (in_mmap ? &rep->mmap_len : NULL));
+ if (fd < 0)
+ goto fini;
+ own_infd = true;
+ if (cvp.in_fd < 0)
+ cvp.in_fd = fd;
+ }
rep->infd = fd;
rep->mmap_active = in_mmap ? clp->in_flags.mmap : 0;
if (in_mmap && (vb > 4))
- pr2serr_lk("[%d] %s: mmap buffp=%p\n", id, __func__, rep->buffp);
- own_infd = true;
+ pr2serr_lk("[%d] %s: mmap buffp=%p\n", thr_idx, __func__,
+ rep->buffp);
++num_sg;
if (vb > 2)
- pr2serr_lk("[%d]: opened local sg IFILE\n", id);
+ pr2serr_lk("[%d]: opened local sg IFILE\n", thr_idx);
}
- if (out_is_sg && clp->outfp) {
- fd = sg_out_open(clp, clp->outfp, (out_mmap ? &rep->buffp : NULL),
- (out_mmap ? &rep->mmap_len : NULL), true);
- if (fd < 0)
- goto fini;
+ if (out_is_sg && outf.size()) {
+ if ((clp->out_flags.same_fds || (0 == thr_idx)) &&
+ (cvp.out_fd >= 0))
+ fd = cvp.out_fd;
+ else {
+ fd = sg_out_open(clp, outf, (out_mmap ? &rep->buffp : NULL),
+ (out_mmap ? &rep->mmap_len : NULL));
+ if (fd < 0)
+ goto fini;
+ own_outfd = true;
+ if (cvp.out_fd < 0)
+ cvp.out_fd = fd;
+ }
rep->outfd = fd;
if (! rep->mmap_active)
rep->mmap_active = out_mmap ? clp->out_flags.mmap : 0;
if (out_mmap && (vb > 4))
- pr2serr_lk("[%d]: mmap buffp=%p\n", id, rep->buffp);
- own_outfd = true;
+ pr2serr_lk("[%d]: mmap buffp=%p\n", thr_idx, rep->buffp);
++num_sg;
if (vb > 2)
- pr2serr_lk("[%d]: opened local sg OFILE\n", id);
+ pr2serr_lk("[%d]: opened local sg OFILE\n", thr_idx);
}
if (vb > 2) {
if (in_is_sg && (! own_infd))
- pr2serr_lk("[%d]: using global sg IFILE, fd=%d\n", id, rep->infd);
+ pr2serr_lk("[%d]: using global sg IFILE, fd=%d\n", thr_idx,
+ rep->infd);
if (out_is_sg && (! own_outfd))
- pr2serr_lk("[%d]: using global sg OFILE, fd=%d\n", id, rep->outfd);
+ pr2serr_lk("[%d]: using global sg OFILE, fd=%d\n", thr_idx,
+ rep->outfd);
}
if (rep->both_sg)
- rep->has_share = sg_share_prepare(rep->outfd, rep->infd, id, vb > 9);
+ rep->has_share = sg_share_prepare(rep->outfd, rep->infd, thr_idx,
+ vb > 9);
if (vb > 9)
- pr2serr_lk("[%d]: has_share=%s\n", id,
+ pr2serr_lk("[%d]: has_share=%s\n", thr_idx,
(rep->has_share ? "true" : "false"));
// share_and_ofreg = (rep->has_share && (rep->outregfd >= 0));
/* vvvvvvvvvvvvvv Main segment copy loop vvvvvvvvvvvvvvvvvvvvvvv */
while (! shutting_down) {
- get_next_res gnr = clp->get_next(clp->mrq_num * clp->bpt);
+ get_next_res_t gnr = cvp.get_next(clp->mrq_num * clp->bpt);
seg_blks = gnr.second;
if (seg_blks <= 0) {
if (seg_blks < 0)
res = -seg_blks;
+ else
+ cvp.state = cp_ver_pair_t::my_state::finished;
break;
}
if (! i_sg_it.set_by_blk_idx(gnr.first)) {
lock_guard<mutex> lk(strerr_mut);
- pr2serr_lk("[%d]: input set_by_blk_idx() failed\n", id);
+ pr2serr_lk("[%d]: input set_by_blk_idx() failed\n", thr_idx);
i_sg_it.dbg_print("input after set_by_blk_idx", false, vb > 5);
res = 2;
break;
}
if (! o_sg_it.set_by_blk_idx(gnr.first)) {
- pr2serr_lk("[%d]: output set_by_blk_idx() failed\n", id);
+ pr2serr_lk("[%d]: output set_by_blk_idx() failed\n", thr_idx);
res = 3;
break;
}
@@ -1433,8 +1777,11 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
}
} /* ^^^^^^^^^^ end of main while loop which copies segments ^^^^^^ */
- if (shutting_down)
+ if (shutting_down) {
+ if (vb > 3)
+ pr2serr_lk("%s: t=%d: shutting down\n", __func__, rep->id);
goto fini;
+ }
if (singleton) {
{
lock_guard<mutex> lk(clp->infant_mut);
@@ -1446,7 +1793,7 @@ read_write_thread(struct global_collection * clp, int id, bool singleton)
}
if (res < 0) {
if (seg_blks >= 0)
- clp->get_next(-1); /* flag error to main */
+ cvp.get_next(-1); /* flag error to main */
pr2serr_lk("%s: t=%d: aborting, res=%d\n", __func__, rep->id, res);
}
@@ -1495,11 +1842,15 @@ fini:
close(rep->outfd);
}
/* pass stats back to read-side */
- clp->in_rem_count -= rep->in_local_count;
- clp->out_rem_count -= rep->out_local_count;
- clp->in_partial += rep->in_local_partial;
- clp->out_partial += rep->out_local_partial;
- clp->sum_of_resids += rep->in_resid_bytes;
+ if (vb > 3)
+ pr2serr_lk("%s: [%d] leaving: in/out local count=%" PRId64 "/%"
+ PRId64 "\n", __func__, rep->id, rep->in_local_count,
+ rep->out_local_count);
+ cvp.in_rem_count -= rep->in_local_count;
+ cvp.out_rem_count -= rep->out_local_count;
+ cvp.in_partial += rep->in_local_partial;
+ cvp.out_partial += rep->out_local_partial;
+ cvp.sum_of_resids += rep->in_resid_bytes;
if (rep->alloc_bp)
free(rep->alloc_bp);
}
@@ -1555,7 +1906,7 @@ normal_in_rd(Rq_elem * rep, int64_t lba, int blocks, int d_boff)
}
}
bp = rep->buffp + d_boff;
- while (((res = read(clp->infd, bp, blocks * clp->bs)) < 0) &&
+ while (((res = read(rep->infd, bp, blocks * clp->bs)) < 0) &&
((EINTR == errno) || (EAGAIN == errno)))
std::this_thread::yield();/* another thread may be able to progress */
if (res < 0) {
@@ -1612,7 +1963,7 @@ normal_out_wr(Rq_elem * rep, int64_t lba, int blocks, int d_boff)
rep->out_follow_on = pos;
}
}
- while (((res = write(clp->outfd, bp, blocks * clp->bs))
+ while (((res = write(rep->outfd, bp, blocks * clp->bs))
< 0) && ((EINTR == errno) || (EAGAIN == errno)))
std::this_thread::yield();/* another thread may be able to progress */
if (res < 0) {
@@ -1653,7 +2004,7 @@ extra_out_wr(Rq_elem * rep, int num_bytes, int d_boff)
pr2serr_lk("[%d] %s: num_bytes=%d, d_boff=%d\n", id, __func__,
num_bytes, d_boff);
- while (((res = write(clp->outfd, bp, num_bytes))
+ while (((res = write(clp->out0fd, bp, num_bytes))
< 0) && ((EINTR == errno) || (EAGAIN == errno)))
std::this_thread::yield();/* another thread may be able to progress */
if (res < 0) {
@@ -2897,129 +3248,6 @@ sg_blk_poll(int fd, int num)
}
#endif
-/* Returns reserved_buffer_size/mmap_size if success, else 0 for failure */
-static int
-sg_prepare_resbuf(int fd, struct global_collection *clp, bool is_in,
- uint8_t **mmpp)
-{
- static bool done = false;
- bool no_dur = is_in ? clp->in_flags.no_dur : clp->out_flags.no_dur;
- bool masync = is_in ? clp->in_flags.masync : clp->out_flags.masync;
- bool wq_excl = is_in ? clp->in_flags.wq_excl : clp->out_flags.wq_excl;
- bool skip_thresh = is_in ? clp->in_flags.no_thresh :
- clp->out_flags.no_thresh;
- int elem_sz = clp->elem_sz;
- int res, t, num, err;
- uint8_t *mmp;
- struct sg_extended_info sei {};
- struct sg_extended_info * seip = &sei;
-
- res = ioctl(fd, SG_GET_VERSION_NUM, &t);
- if ((res < 0) || (t < 40000)) {
- if (ioctl(fd, SG_GET_RESERVED_SIZE, &num) < 0) {
- perror("SG_GET_RESERVED_SIZE ioctl failed");
- return 0;
- }
- if (! done) {
- done = true;
- pr2serr_lk("%ssg driver prior to 4.0.00, reduced functionality\n",
- my_name);
- }
- goto bypass;
- }
- if (elem_sz >= 4096) {
- seip->sei_rd_mask |= SG_SEIM_SGAT_ELEM_SZ;
- res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
- if (res < 0)
- pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) rd "
- "error: %s\n", __func__, strerror(errno));
- if (elem_sz != (int)seip->sgat_elem_sz) {
- seip->sei_wr_mask |= SG_SEIM_SGAT_ELEM_SZ;
- seip->sgat_elem_sz = elem_sz;
- res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
- if (res < 0)
- pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) "
- "wr error: %s\n", __func__, strerror(errno));
- }
- }
- if (no_dur || masync || skip_thresh) {
- seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
- if (no_dur) {
- seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_DURATION;
- seip->ctl_flags |= SG_CTL_FLAGM_NO_DURATION;
- }
- if (masync) {
- seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_MORE_ASYNC;
- seip->ctl_flags |= SG_CTL_FLAGM_MORE_ASYNC;
- }
- if (wq_excl) {
- seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_EXCL_WAITQ;
- seip->ctl_flags |= SG_CTL_FLAGM_EXCL_WAITQ;
- }
- if (skip_thresh) {
- seip->tot_fd_thresh = 0;
- sei.sei_wr_mask |= SG_SEIM_TOT_FD_THRESH;
- }
- res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
- if (res < 0)
- pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(NO_DURATION) "
- "error: %s\n", __func__, strerror(errno));
- }
-bypass:
- num = clp->bs * clp->bpt;
- res = ioctl(fd, SG_SET_RESERVED_SIZE, &num);
- if (res < 0) {
- perror("sg_mrq_dd: SG_SET_RESERVED_SIZE error");
- return 0;
- } else {
- int nn;
-
- res = ioctl(fd, SG_GET_RESERVED_SIZE, &nn);
- if (res < 0) {
- perror("sg_mrq_dd: SG_GET_RESERVED_SIZE error");
- return 0;
- }
- if (nn < num) {
- pr2serr_lk("%s: SG_GET_RESERVED_SIZE shows size truncated, "
- "wanted %d got %d\n", __func__, num, nn);
- return 0;
- }
- if (mmpp) {
- mmp = (uint8_t *)mmap(NULL, num, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
- if (MAP_FAILED == mmp) {
- err = errno;
- pr2serr_lk("sg_mrq_dd: %s: sz=%d, fd=%d, mmap() failed: %s\n",
- __func__, num, fd, strerror(err));
- return 0;
- }
- *mmpp = mmp;
- }
- }
- t = 1;
- res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
- if (res < 0)
- perror("sg_mrq_dd: SG_SET_FORCE_PACK_ID error");
- if (clp->unit_nanosec) {
- seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
- seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS;
- seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
- if (ioctl(fd, SG_SET_GET_EXTENDED, seip) < 0) {
- res = -1;
- pr2serr_lk("ioctl(EXTENDED(TIME_IN_NS)) failed, errno=%d %s\n",
- errno, strerror(errno));
- }
- }
- if (clp->verbose) {
- t = 1;
- /* more info in /proc/scsi/sg/debug */
- res = ioctl(fd, SG_SET_DEBUG, &t);
- if (res < 0)
- perror("sg_mrq_dd: SG_SET_DEBUG error");
- }
- return (res < 0) ? 0 : num;
-}
-
/* Returns the number of times 'ch' is found in string 's' given the
* string's length. */
static int
@@ -3173,6 +3401,9 @@ process_flags(const char * arg, struct flags_t * fp)
fp->random = true;
else if ((0 == strcmp(cp, "mout_if")) || (0 == strcmp(cp, "mout-if")))
fp->mout_if = true;
+ else if ((0 == strcmp(cp, "same_fds")) ||
+ (0 == strcmp(cp, "same-fds")))
+ fp->same_fds = true;
else if (0 == strcmp(cp, "serial"))
fp->serial = true;
else if (0 == strcmp(cp, "swait"))
@@ -3188,72 +3419,6 @@ process_flags(const char * arg, struct flags_t * fp)
return true;
}
-static int
-sg_in_open(struct global_collection *clp, const char *inf, uint8_t **mmpp,
- int * mmap_lenp, bool move_data)
-{
- int fd, err, n;
- int flags = O_RDWR;
- char ebuff[EBUFF_SZ];
-
- 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 ((fd = open(inf, flags)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%s: could not open %s for sg reading",
- __func__, inf);
- perror(ebuff);
- return -sg_convert_errno(err);
- }
- if (move_data) {
- n = sg_prepare_resbuf(fd, clp, true, mmpp);
- if (n <= 0)
- return -SG_LIB_FILE_ERROR;
- } else
- n = 0;
- if (mmap_lenp)
- *mmap_lenp = n;
- return fd;
-}
-
-static int
-sg_out_open(struct global_collection *clp, const char *outf, uint8_t **mmpp,
- int * mmap_lenp, bool move_data)
-{
- int fd, err, n;
- int flags = O_RDWR;
- char ebuff[EBUFF_SZ];
-
- 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 ((fd = open(outf, flags)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%s: could not open %s for sg %s",
- __func__, outf, (clp->verify ? "verifying" : "writing"));
- perror(ebuff);
- return -sg_convert_errno(err);
- }
- if (move_data) {
- n = sg_prepare_resbuf(fd, clp, false, mmpp);
- if (n <= 0)
- return -SG_LIB_FILE_ERROR;
- } else
- n = 0;
- if (mmap_lenp)
- *mmap_lenp = n;
- return fd;
-}
-
/* Process arguments given to 'conv=" option. Returns 0 on success,
* 1 on error. */
static int
@@ -3299,7 +3464,7 @@ process_conv(const char * arg, struct flags_t * ifp, struct flags_t * ofp)
static int
parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
- char * inf, char * outf, char * outregf)
+ char * outregf)
{
bool contra = false;
bool verbose_given = false;
@@ -3438,12 +3603,16 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
goto syn_err;
}
} else if (0 == strcmp(key, "if")) {
- if ('\0' != inf[0]) {
+ if (clp->inf_v.size() > 0) {
pr2serr("Second 'if=' argument??\n");
goto syn_err;
} else {
- memcpy(inf, buf, INOUTF_SZ);
- inf[INOUTF_SZ - 1] = '\0'; /* noisy compiler */
+ cp = buf;
+ while ((ccp = strchr(cp, ','))) {
+ clp->inf_v.push_back(string(cp , ccp - cp));
+ cp = ccp + 1;
+ }
+ clp->inf_v.push_back(string(cp , strlen(cp)));
}
} else if (0 == strcmp(key, "iflag")) {
if (! process_flags(buf, &clp->in_flags)) {
@@ -3497,12 +3666,16 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
outregf[INOUTF_SZ - 1] = '\0'; /* noisy compiler */
}
} else if (strcmp(key, "of") == 0) {
- if ('\0' != outf[0]) {
+ if (clp->outf_v.size() > 0) {
pr2serr("Second 'of=' argument??\n");
goto syn_err;
} else {
- memcpy(outf, buf, INOUTF_SZ);
- outf[INOUTF_SZ - 1] = '\0'; /* noisy compiler */
+ cp = buf;
+ while ((ccp = strchr(cp, ','))) {
+ clp->outf_v.push_back(string(cp , ccp - cp));
+ cp = ccp + 1;
+ }
+ clp->outf_v.push_back(string(cp , strlen(cp)));
}
} else if (0 == strcmp(key, "oflag")) {
if (! process_flags(buf, &clp->out_flags)) {
@@ -3728,10 +3901,10 @@ calc_count(struct global_collection * clp, const char * inf,
out_num_sect = -1;
}
if (FT_SG == clp->in_type) {
- res = scsi_read_capacity(clp->infd, &in_num_sect, &in_sect_sz);
+ res = scsi_read_capacity(clp->in0fd, &in_num_sect, &in_sect_sz);
if (2 == res) {
pr2serr("Unit attention, media changed(in), continuing\n");
- res = scsi_read_capacity(clp->infd, &in_num_sect,
+ res = scsi_read_capacity(clp->in0fd, &in_num_sect,
&in_sect_sz);
}
if (0 != res) {
@@ -3750,10 +3923,10 @@ calc_count(struct global_collection * clp, const char * inf,
}
}
if (FT_SG == clp->out_type) {
- res = scsi_read_capacity(clp->outfd, &out_num_sect, &out_sect_sz);
+ res = scsi_read_capacity(clp->out0fd, &out_num_sect, &out_sect_sz);
if (2 == res) {
pr2serr("Unit attention, media changed(out), continuing\n");
- res = scsi_read_capacity(clp->outfd, &out_num_sect,
+ res = scsi_read_capacity(clp->out0fd, &out_num_sect,
&out_sect_sz);
}
if (0 != res) {
@@ -3777,7 +3950,7 @@ calc_count(struct global_collection * clp, const char * inf,
if (FT_SG == clp->in_type)
;
else if (FT_BLOCK == clp->in_type) {
- if (0 != read_blkdev_capacity(clp->infd, &in_num_sect,
+ if (0 != read_blkdev_capacity(clp->in0fd, &in_num_sect,
&in_sect_sz)) {
pr2serr("Unable to read block capacity on %s\n", inf);
in_num_sect = -1;
@@ -3792,7 +3965,7 @@ calc_count(struct global_collection * clp, const char * inf,
if (FT_SG == clp->out_type)
;
else if (FT_BLOCK == clp->out_type) {
- if (0 != read_blkdev_capacity(clp->outfd, &out_num_sect,
+ if (0 != read_blkdev_capacity(clp->out0fd, &out_num_sect,
&out_sect_sz)) {
pr2serr("Unable to read block capacity on %s\n", outf);
out_num_sect = -1;
@@ -3943,10 +4116,12 @@ int
main(int argc, char * argv[])
{
bool fail_after_cli = false;
- char inf[INOUTF_SZ];
- char outf[INOUTF_SZ];
+ bool ifile_given = true;
+ // char inf[INOUTF_SZ];
+ // char outf[INOUTF_SZ];
char outregf[INOUTF_SZ];
- int res, k, err, flags;
+ int res, k, err;
+ size_t num_ifiles, num_ofiles, num_slices, inf0_sz;
int64_t in_num_sect = -1;
int64_t out_num_sect = -1;
const char * ccp = NULL;
@@ -3978,8 +4153,8 @@ main(int argc, char * argv[])
clp->cdbsz_in = DEF_SCSI_CDB_SZ;
clp->cdbsz_out = DEF_SCSI_CDB_SZ;
clp->mrq_num = DEF_MRQ_NUM;
- inf[0] = '\0';
- outf[0] = '\0';
+ // inf[0] = '\0';
+ // outf[0] = '\0';
outregf[0] = '\0';
fetch_sg_version();
if (sg_version >= 40045)
@@ -3990,7 +4165,7 @@ main(int argc, char * argv[])
fail_after_cli = true;
}
- res = parse_cmdline_sanity(argc, argv, clp, inf, outf, outregf);
+ res = parse_cmdline_sanity(argc, argv, clp, outregf);
if (SG_LIB_OK_FALSE == res)
return 0;
if (res)
@@ -4007,8 +4182,63 @@ main(int argc, char * argv[])
install_handler(SIGUSR1, siginfo_handler);
install_handler(SIGUSR2, siginfo2_handler);
- clp->infd = STDIN_FILENO;
- clp->outfd = STDOUT_FILENO;
+ num_ifiles = clp->inf_v.size();
+ num_ofiles = clp->outf_v.size();
+ if (num_ifiles > MAX_SLICES) {
+ pr2serr("%sonly support %d slices but given %zd IFILEs\n", my_name,
+ MAX_SLICES, num_ifiles);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (num_ofiles > MAX_SLICES) {
+ pr2serr("%sonly support %d slices but given %zd OFILEs\n", my_name,
+ MAX_SLICES, num_ifiles);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (0 == num_ofiles) {
+ if (0 == num_ifiles) {
+ pr2serr("%sexpect either if= or of= to be given\n", my_name);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ for (k = 0; k < (int)num_ifiles; ++k)
+ clp->outf_v.push_back("."); /* same as /dev/null */
+ }
+ if (0 == num_ifiles) {
+ ifile_given = false;
+ for (k = 0; k < (int)num_ofiles; ++k)
+ clp->inf_v.push_back("");
+ }
+ if ((num_ifiles > 1) && (num_ofiles > 1) && (num_ifiles != num_ofiles)) {
+ pr2serr("%snumber of IFILEs [%zd] and number of OFILEs [%zd] > 1 "
+ "and unequal\n", my_name, num_ifiles, num_ofiles);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if ((num_ifiles > 1) && (1 == num_ofiles)) {
+ /* if many IFILEs and one OFILE, replicate OFILE till same size */
+ for (k = 1; k < (int)num_ifiles; ++k)
+ clp->outf_v.push_back(clp->outf_v[0]);
+ num_ofiles = clp->outf_v.size();
+ } else if ((num_ofiles > 1) && (1 == num_ifiles)) {
+ /* if many OFILEs and one IFILE, replicate IFILE till same size */
+ for (k = 1; k < (int)num_ofiles; ++k)
+ clp->inf_v.push_back(clp->inf_v[0]);
+ num_ifiles = clp->inf_v.size();
+ }
+ num_slices = (num_ifiles > num_ofiles) ? num_ifiles : num_ofiles;
+ if ((int)num_slices > num_threads) {
+ pr2serr("%sNumber of slices [%zd] exceeds number of threads [%d].\n",
+ my_name, num_slices, num_threads);
+ pr2serr("Number of threads needs to be increased.\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ k = 0;
+ for (auto && cvp : clp->cp_ver_arr) {
+ if (k >= (int)num_slices)
+ break;
+ cvp.my_index = k++;
+ cvp.state = cp_ver_pair_t::my_state::init;
+ }
+ clp->in0fd = STDIN_FILENO;
+ clp->out0fd = STDOUT_FILENO;
if (clp->in_flags.ff) {
ccp = "<0xff bytes>";
cc2p = "ff";
@@ -4019,45 +4249,45 @@ main(int argc, char * argv[])
ccp = "<zero bytes>";
cc2p = "00";
}
+ inf0_sz = clp->inf_v.size() ? clp->inf_v[0].size() : 0;
if (ccp) {
- if (inf[0]) {
- pr2serr("%siflag=%s and if=%s contradict\n", my_name, cc2p, inf);
+ if (ifile_given) {
+ pr2serr("%siflag=%s and if=%s contradict\n", my_name, cc2p,
+ clp->inf_v[0].data());
return SG_LIB_CONTRADICT;
}
+ for (auto && cvp : clp->cp_ver_arr) {
+ if (cvp.state == cp_ver_pair_t::my_state::empty)
+ break;
+ cvp.in_type = FT_RANDOM_0_FF;
+ }
clp->in_type = FT_RANDOM_0_FF;
clp->infp = ccp;
- clp->infd = -1;
- } else if (inf[0] && ('-' != inf[0])) {
- clp->in_type = dd_filetype(inf, clp->in_st_size);
+ clp->in0fd = -1;
+ } else if (inf0_sz && ('-' != clp->inf_v[0].data()[0])) {
+ const string & inf_s = clp->inf_v[0];
+ const char * infp = inf_s.data();
+ clp->in_type = dd_filetype(infp, clp->in_st_size);
if (FT_ERROR == clp->in_type) {
- pr2serr("%sunable to access %s\n", my_name, inf);
+ pr2serr("%sunable to access %s\n", my_name, infp);
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, infp);
+ return SG_LIB_FILE_ERROR;
+ } else if (FT_CHAR == clp->in_type) {
+ pr2serr("%sunable to use unknown char device %s\n", my_name, infp);
return SG_LIB_FILE_ERROR;
} else if (FT_SG == clp->in_type) {
- clp->infd = sg_in_open(clp, inf, NULL, NULL, false);
- if (clp->infd < 0)
- return -clp->infd;
+ clp->in0fd = sg_in_open(clp, inf_s, NULL, NULL);
+ if (clp->in0fd < 0)
+ return -clp->in0fd;
} else {
- flags = O_RDONLY;
- 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 reading",
- my_name, inf);
- perror(ebuff);
- return sg_convert_errno(err);
- }
+ clp->in0fd = reg_file_open(clp, infp, false /* read */);
+ if (clp->in0fd < 0)
+ return sg_convert_errno(-clp->in0fd);
}
- clp->infp = inf;
+ clp->infp = infp;
}
if (clp->cdl_given && (! clp->cdbsz_given)) {
bool changed = false;
@@ -4077,12 +4307,15 @@ main(int argc, char * argv[])
if ((clp->verbose > 0) &&
(clp->in_flags.no_waitq || clp->out_flags.no_waitq))
pr2serr("no_waitq=<n> operand is now ignored\n");
- if (outf[0]) {
+ if (clp->outf_v.size()) {
+ const string & outf_s = clp->outf_v[0].data();
+ const char * outfp = outf_s.data();
+
clp->ofile_given = true;
- if (('-' == outf[0]))
+ if ('-' == outfp[0])
clp->out_type = FT_FIFO;
else
- clp->out_type = dd_filetype(outf, clp->out_st_size);
+ clp->out_type = dd_filetype(outfp, clp->out_st_size);
if ((FT_SG != clp->out_type) && clp->verify) {
pr2serr("%s --verify only supported by sg OFILEs\n", my_name);
@@ -4091,47 +4324,24 @@ main(int argc, char * argv[])
if (FT_FIFO == clp->out_type)
;
else 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, outfp);
+ return SG_LIB_FILE_ERROR;
+ } else if (FT_CHAR == clp->out_type) {
+ pr2serr("%sunable to use unknown char device %s\n", my_name,
+ outfp);
return SG_LIB_FILE_ERROR;
} else if (FT_SG == clp->out_type) {
- clp->outfd = sg_out_open(clp, outf, NULL, NULL, false);
- if (clp->outfd < 0)
- return -clp->outfd;
+ clp->out0fd = sg_out_open(clp, outf_s, NULL, NULL);
+ if (clp->out0fd < 0)
+ return -clp->out0fd;
} else if (FT_DEV_NULL == clp->out_type)
- clp->outfd = -1; /* don't bother opening */
+ clp->out0fd = -1; /* don't bother opening */
else {
- if (FT_RAW != clp->out_type) {
- flags = O_WRONLY;
- if (! clp->out_flags.nocreat)
- flags |= O_CREAT;
- 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->out_flags.append)
- flags |= O_APPEND;
-
- if ((clp->outfd = open(outf, flags, 0666)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%scould not open %s for "
- "writing", my_name, outf);
- perror(ebuff);
- return sg_convert_errno(err);
- }
- }
- else { /* raw output file */
- if ((clp->outfd = open(outf, O_WRONLY)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%scould not open %s for raw "
- "writing", my_name, outf);
- perror(ebuff);
- return sg_convert_errno(err);
- }
- }
+ clp->out0fd = reg_file_open(clp, outfp, true /* write */);
+ if (clp->out0fd < 0)
+ return sg_convert_errno(-clp->out0fd);
}
- clp->outfp = outf;
+ clp->outfp = outfp;
}
if (clp->verify && (clp->out_type == FT_DEV_NULL)) {
pr2serr("Can't do verify when OFILE not given\n");
@@ -4172,7 +4382,7 @@ main(int argc, char * argv[])
} else
clp->outregfd = -1;
- if ((STDIN_FILENO == clp->infd) && (STDOUT_FILENO == clp->outfd)) {
+ if ((STDIN_FILENO == clp->in0fd) && (STDOUT_FILENO == clp->out0fd)) {
pr2serr("Won't default both IFILE to stdin _and_ OFILE to "
"/dev/null\n");
pr2serr("For more information use '--help'\n");
@@ -4186,7 +4396,8 @@ main(int argc, char * argv[])
pr2serr("The seek= argument is not suitable for a pipe\n");
return SG_LIB_SYNTAX_ERROR;
}
- res = do_count_work(clp, inf, in_num_sect, outf, out_num_sect);
+ res = do_count_work(clp, clp->inf_v[0].data(), in_num_sect,
+ clp->outf_v[0].data(), out_num_sect);
if (res)
return res;
@@ -4213,8 +4424,13 @@ main(int argc, char * argv[])
}
}
- clp->in_rem_count = clp->dd_count;
- clp->out_rem_count = clp->dd_count;
+ for (auto && cvp : clp->cp_ver_arr) {
+ cvp.in_type = clp->in_type;
+ cvp.out_type = clp->out_type;
+ cvp.dd_count = clp->dd_count;
+ cvp.in_rem_count = clp->dd_count;
+ cvp.out_rem_count = clp->dd_count;
+ }
if (clp->dry_run > 0) {
pr2serr("Due to --dry-run option, bypass copy/read\n");
@@ -4243,13 +4459,18 @@ main(int argc, char * argv[])
/* vvvvvvvvvvv Start worker threads vvvvvvvvvvvvvvvvvvvvvvvv */
if (num_threads > 0) {
+ auto & cvp = clp->cp_ver_arr[0];
+
+ cvp.in_fd = clp->in0fd;
+ cvp.out_fd = clp->out0fd;
+
/* launch "infant" thread to catch early mortality, if any */
- work_thr.emplace_back(read_write_thread, clp, 0, true);
+ work_thr.emplace_back(read_write_thread, clp, 0, 0, true);
{
unique_lock<mutex> lk(clp->infant_mut);
clp->infant_cv.wait(lk, []{ return gcoll.processed; });
}
- if (clp->next_count_pos.load() < 0) {
+ if (clp->cp_ver_arr[0].next_count_pos.load() < 0) {
/* infant thread error-ed out, join with it */
for (auto & t : work_thr) {
if (t.joinable())
@@ -4260,7 +4481,8 @@ 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, false);
+ work_thr.emplace_back(read_write_thread, clp, k,
+ k % (int)num_slices, false);
/* now wait for worker threads to finish */
for (auto & t : work_thr) {
@@ -4274,11 +4496,12 @@ jump:
if (do_sync) {
if (FT_SG == clp->out_type) {
- pr2serr_lk(">> Synchronizing cache on %s\n", outf);
- res = sg_ll_sync_cache_10(clp->outfd, 0, 0, 0, 0, 0, false, 0);
+ pr2serr_lk(">> Synchronizing cache on %s\n",
+ (clp->outf_v.size() ? clp->outf_v[0].data() : "" ));
+ res = sg_ll_sync_cache_10(clp->out0fd, 0, 0, 0, 0, 0, false, 0);
if (SG_LIB_CAT_UNIT_ATTENTION == res) {
pr2serr_lk("Unit attention(out), continuing\n");
- res = sg_ll_sync_cache_10(clp->outfd, 0, 0, 0, 0, 0, false,
+ res = sg_ll_sync_cache_10(clp->out0fd, 0, 0, 0, 0, 0, false,
0);
}
if (0 != res)
@@ -4296,11 +4519,11 @@ jump:
fini:
- if ((STDIN_FILENO != clp->infd) && (clp->infd >= 0))
- close(clp->infd);
- if ((STDOUT_FILENO != clp->outfd) && (FT_DEV_NULL != clp->out_type) &&
- (clp->outfd >= 0))
- close(clp->outfd);
+ if ((STDIN_FILENO != clp->in0fd) && (clp->in0fd >= 0))
+ close(clp->in0fd);
+ if ((STDOUT_FILENO != clp->out0fd) && (FT_DEV_NULL != clp->out_type) &&
+ (clp->out0fd >= 0))
+ close(clp->out0fd);
if ((clp->outregfd >= 0) && (STDOUT_FILENO != clp->outregfd) &&
(FT_DEV_NULL != clp->outreg_type))
close(clp->outregfd);
@@ -4320,9 +4543,16 @@ fini:
close(fd);
}
}
- if (clp->sum_of_resids.load())
- pr2serr(">> Non-zero sum of residual counts=%d\n",
- clp->sum_of_resids.load());
+
+ k = 0;
+ for (auto && cvp : gcoll.cp_ver_arr) {
+ if (cvp.state == cp_ver_pair_t::my_state::empty)
+ break;
+ ++k;
+ if (cvp.sum_of_resids.load())
+ pr2serr(">> slice: %d, Non-zero sum of residual counts=%d\n",
+ k, cvp.sum_of_resids.load());
+ }
if (clp->verbose && (num_start_eagain > 0))
pr2serr("Number of start EAGAINs: %d\n", num_start_eagain.load());
if (clp->verbose && (num_fin_eagain > 0))
diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp
index 14b42824..a8fcd56e 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -36,7 +36,7 @@
* renamed [20181221]
*/
-static const char * version_str = "2.09 20210609";
+static const char * version_str = "2.10 20210621";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -150,15 +150,11 @@ using namespace std;
#define MAX_NUM_THREADS 1024 /* was SG_MAX_QUEUE with v3 driver */
#define DEF_NUM_MRQS 0
-#ifndef RAW_MAJOR
-#define RAW_MAJOR 255 /*unlikely value */
-#endif
-
#define FT_OTHER 1 /* filetype other than one of the following */
#define FT_SG 2 /* filetype is sg char device */
-#define FT_RAW 4 /* filetype is raw char device */
-#define FT_DEV_NULL 8 /* either "/dev/null" or "." as filename */
-#define FT_ST 16 /* filetype is st char device (tape) */
+#define FT_DEV_NULL 4 /* either /dev/null, /dev/zero or "." */
+#define FT_ST 8 /* filetype is st char device (tape) */
+#define FT_CHAR 16 /* filetype is st char device (tape) */
#define FT_BLOCK 32 /* filetype is a block device */
#define FT_FIFO 64 /* fifo (named or unnamed pipe (stdout)) */
#define FT_RANDOM_0_FF 128 /* iflag=00, iflag=ff and iflag=random
@@ -166,6 +162,7 @@ using namespace std;
#define FT_ERROR 256 /* couldn't "stat" file */
#define DEV_NULL_MINOR_NUM 3
+#define DEV_ZERO_MINOR_NUM 5
#define EBUFF_SZ 768
@@ -455,21 +452,237 @@ pr2serr_lk(const char * fmt, ...)
return n;
}
-#if 0 // not used yet
static void
-pr_errno_lk(int e_no, const char * fmt, ...)
+usage(int pg_num)
{
- char b[180];
- va_list args;
+ if (pg_num > 3)
+ goto page4;
+ else if (pg_num > 2)
+ goto page3;
+ else if (pg_num > 1)
+ goto page2;
- pthread_mutex_lock(&strerr_mut);
- va_start(args, fmt);
- vsnprintf(b, sizeof(b), fmt, args);
- fprintf(stderr, "%s: %s\n", b, strerror(e_no));
- va_end(args);
- pthread_mutex_unlock(&strerr_mut);
+ pr2serr("Usage: sgh_dd [bs=BS] [conv=CONVS] [count=COUNT] [ibs=BS] "
+ "[if=IFILE]\n"
+ " [iflag=FLAGS] [obs=BS] [of=OFILE] [oflag=FLAGS] "
+ "[seek=SEEK]\n"
+ " [skip=SKIP] [--help] [--version]\n\n");
+ pr2serr(" [ae=AEN[,MAEN]] [bpt=BPT] [cdbsz=6|10|12|16] "
+ "[coe=0|1]\n"
+ " [dio=0|1] [elemsz_kb=EKB] [fail_mask=FM] "
+ "[fua=0|1|2|3]\n"
+ " [mrq=[I|O,]NRQS[,C]] [noshare=0|1] "
+ "[of2=OFILE2]\n"
+ " [ofreg=OFREG] [ofsplit=OSP] [sdt=SDT] "
+ "[sync=0|1]\n"
+ " [thr=THR] [time=0|1|2[,TO]] [unshare=1|0] "
+ "[verbose=VERB]\n"
+ " [--dry-run] [--prefetch] [-v|-vv|-vvv] "
+ "[--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"
+ " conv comma separated list from: [nocreat,noerror,"
+ "notrunc,\n"
+ " null,sync]\n"
+ " count number of blocks to copy (def: device size)\n"
+ " if file or device to read from (def: stdin)\n"
+ " iflag comma separated list from: [00,coe,defres,dio,"
+ "direct,dpo,\n"
+ " dsync,excl,ff,fua,hipri,masync,mmap,mout_if,"
+ "mrq_immed,\n"
+ " mrq_svb,nocreat,nodur,noxfer,null,qhead,"
+ "qtail,\n"
+ " random,same_fds,v3,v4,wq_excl]\n"
+ " of file or device to write to (def: /dev/null "
+ "N.B. different\n"
+ " from dd it defaults to stdout). If 'of=.' "
+ "uses /dev/null\n"
+ " of2 second file or device to write to (def: "
+ "/dev/null)\n"
+ " oflag comma separated list from: [append,<<list from "
+ "iflag>>]\n"
+ " seek block position to start writing to OFILE\n"
+ " skip block position to start reading from IFILE\n"
+ " --help|-h output this usage message then exit\n"
+ " --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"
+ "Copy IFILE to OFILE, similar to dd command. This utility is "
+ "specialized for\nSCSI devices and uses multiple POSIX threads. "
+ "It expects one or both IFILE\nand OFILE to be sg devices. With "
+ "--verify option does a verify/compare\noperation instead of a "
+ "copy. This utility is Linux specific and uses the\nv4 sg "
+ "driver 'share' capability if available. Use '-hh', '-hhh' or "
+ "'-hhhh'\nfor more information.\n"
+ );
+ return;
+page2:
+ pr2serr("Syntax: sgh_dd [operands] [options]\n\n"
+ " where: operands have the form name=value and are pecular to "
+ "'dd'\n"
+ " style commands, and options start with one or "
+ "two hyphens;\n"
+ " the lesser used operands and option are:\n\n"
+ " ae AEN: abort every n commands (def: 0 --> don't "
+ "abort any)\n"
+ " MAEN: abort every n mrq commands (def: 0 --> "
+ "don't)\n"
+ " [requires commands with > 1 ms duration]\n"
+ " bpt is blocks_per_transfer (default is 128)\n"
+ " cdbsz size of SCSI READ, WRITE or VERIFY cdb_s "
+ "(default is 10)\n"
+ " coe continue on error, 0->exit (def), "
+ "1->zero + continue\n"
+ " dio is direct IO, 1->attempt, 0->indirect IO (def)\n"
+ " elemsz_kb scatter gather list element size in kilobytes "
+ "(def: 32[KB])\n"
+ " fail_mask 1: misuse KEEP_SHARE flag; 0: nothing (def)\n"
+ " fua force unit access: 0->don't(def), 1->OFILE, "
+ "2->IFILE,\n"
+ " 3->OFILE+IFILE\n"
+ " mrq number of cmds placed in each sg call "
+ "(def: 0);\n"
+ " may have trailing ',C', to send bulk cdb_s; "
+ "if preceded\n"
+ " by 'I' then mrq only on IFILE, likewise 'O' "
+ "for OFILE\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"
+ " ofsplit split ofile write in two at block OSP (def: 0 "
+ "(no split))\n"
+ " sdt stall detection times: CRT[,ICT]. CRT: check "
+ "repetition\n"
+ " time (after first) in seconds; ICT: initial "
+ "check time\n"
+ " in milliseconds. Default: 3,300 . Use CRT=0 "
+ "to disable\n"
+ " sync 0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
+ "after copy\n"
+ " thr is number of threads, must be > 0, default 4, "
+ "max 1024\n"
+ " time 0->no timing, 1->calc throughput(def), "
+ "2->nanosec\n"
+ " precision; TO is command timeout in seconds "
+ "(def: 60)\n"
+ " unshare 0->don't explicitly unshare after share; 1->let "
+ "close do\n"
+ " file unshare (default)\n"
+ " verbose increase verbosity\n"
+ " --dry-run|-d prepare but bypass copy/read\n"
+ " --verbose|-v increase verbosity of utility\n\n"
+ "Use '-hhh' or '-hhhh' for more information about flags.\n"
+ );
+ return;
+page3:
+ pr2serr("Syntax: sgh_dd [operands] [options]\n\n"
+ " where: 'iflag=<arg>' and 'oflag=<arg>' arguments are listed "
+ "below:\n\n"
+ " 00 use all zeros instead of if=IFILE (only in "
+ "iflags)\n"
+ " append append output to OFILE (assumes OFILE is "
+ "regular file)\n"
+ " coe continue of error (reading, fills with zeros)\n"
+ " defres keep default reserve buffer size (else its "
+ "bs*bpt)\n"
+ " dio sets the SG_FLAG_DIRECT_IO in sg requests\n"
+ " direct sets the O_DIRECT flag on open()\n"
+ " dpo sets the DPO (disable page out) in SCSI READs "
+ "and WRITEs\n"
+ " dsync sets the O_SYNC flag on open()\n"
+ " excl sets the O_EXCL flag on open()\n"
+ " ff use all 0xff bytes instead of if=IFILE (only in "
+ "iflags)\n"
+ " fua sets the FUA (force unit access) in SCSI READs "
+ "and WRITEs\n"
+ " hipri set HIPRI flag on command, uses blk_poll() to "
+ "complete\n"
+ " masync set 'more async' flag on this sg device\n"
+ " mmap setup mmap IO on IFILE or OFILE; OFILE only "
+ "with noshare\n"
+ " mmap,mmap when used twice, doesn't call munmap()\n"
+ " mout_if set META_OUT_IF flag on each request\n"
+ " mrq_immed if mrq active, do submit non-blocking (def: "
+ "ordered\n"
+ " blocking)\n"
+ " mrq_svb if mrq and sg->sg copy, do shared_variable_"
+ "blocking\n"
+ " nocreat will fail rather than create OFILE\n"
+ " nodur turns off command duration calculations\n"
+ " noxfer no transfer to/from the user space\n"
+ " no_thresh skip checking per fd max data xfer\n"
+ " null does nothing, placeholder\n"
+ " qhead queue new request at head of block queue\n"
+ " qtail queue new request at tail of block queue (def: "
+ "q at head)\n"
+ " random use random data instead of if=IFILE (only in "
+ "iflags)\n"
+ " same_fds each thread uses the same IFILE and OFILE(2) "
+ "file\n"
+ " descriptors (def: each threads has own file "
+ "descriptors)\n"
+ " swait this option is now ignored\n"
+ " v3 use v3 sg interface (def: v3 unless sg driver "
+ "is v4)\n"
+ " v4 use v4 sg interface (def: v3 unless sg driver "
+ "is v4)\n"
+ " wq_excl set SG_CTL_FLAGM_EXCL_WAITQ on this sg fd\n"
+ "\n"
+ "Copies IFILE to OFILE (and to OFILE2 if given). If IFILE and "
+ "OFILE are sg\ndevices 'shared' mode is selected unless "
+ "'noshare' is given to 'iflag=' or\n'oflag='. of2=OFILE2 uses "
+ "'oflag=FLAGS'. When sharing, the data stays in a\nsingle "
+ "in-kernel buffer which is copied (or mmap-ed) to the user "
+ "space\nif the 'ofreg=OFREG' is given. Use '-hhhh' for more "
+ "information.\n"
+ );
+ return;
+page4:
+ pr2serr("pack_id:\n"
+ "These are ascending integers, starting at 1, associated with "
+ "each issued\nSCSI command. When both IFILE and OFILE are sg "
+ "devices, then the READ in\neach read-write pair is issued an "
+ "even pack_id and its WRITE pair is\ngiven the pack_id one "
+ "higher (i.e. an odd number). This enables a\n'cat '"
+ "/proc/scsi/sg/debug' user to see that progress is being "
+ "made.\n\n");
+ pr2serr("Debugging:\n"
+ "Apart from using one or more '--verbose' options which gets a "
+ "bit noisy\n'cat /proc/scsi/sg/debug' can give a good overview "
+ "of what is happening.\nThat does a sg driver object tree "
+ "traversal that does minimal locking\nto make sure that each "
+ "traversal is 'safe'. So it is important to note\nthe whole "
+ "tree is not locked. This means for fast devices the overall\n"
+ "tree state may change while the traversal is occurring. For "
+ "example,\nit has been observed that both the read- and write- "
+ "sides of a request\nshare show they are in 'active' state "
+ "which should not be possible.\nIt occurs because the read-"
+ "sie probably jumped out of active state and\nthe write-side "
+ "request entered it while some other nodes were being "
+ "printed.\n\n");
+ pr2serr("Busy state:\n"
+ "Busy state (abbreviated to 'bsy' in the /proc/scsi/sg/debug "
+ "output)\nis entered during request setup and completion. It "
+ "is intended to be\na temporary state. It should not block "
+ "but does sometimes (e.g. in\nblock_get_request()). Even so "
+ "that blockage should be short and if not\nthere is a "
+ "problem.\n\n");
+ pr2serr("--verify :\n"
+ "For comparing IFILE with OFILE. Does repeated sequences of: "
+ "READ(ifile)\nand uses data returned to send to VERIFY(ofile, "
+ "BYTCHK=1). So the OFILE\ndevice/disk is doing the actual "
+ "comparison. Stops on first miscompare.\n\n");
+ pr2serr("--prefetch :\n"
+ "Used with --verify option. Prepends a PRE-FETCH(ofile, IMMED) "
+ "to verify\nsequence. This should speed the trailing VERIFY by "
+ "making sure that\nthe data it needs for the comparison is "
+ "already in its cache.\n");
+ return;
}
-#endif
static void
lk_print_command_len(const char *prefix, uint8_t * cmdp, int len, bool lock)
@@ -893,14 +1106,14 @@ dd_filetype(const char * filename, off_t & st_size)
return FT_ERROR;
if (S_ISCHR(st.st_mode)) {
if ((MEM_MAJOR == major(st.st_rdev)) &&
- (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
- return FT_DEV_NULL;
- if (RAW_MAJOR == major(st.st_rdev))
- return FT_RAW;
+ ((DEV_NULL_MINOR_NUM == minor(st.st_rdev)) ||
+ (DEV_ZERO_MINOR_NUM == minor(st.st_rdev))))
+ return FT_DEV_NULL; /* treat /dev/null + /dev/zero the same */
if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
return FT_SG;
if (SCSI_TAPE_MAJOR == major(st.st_rdev))
return FT_ST;
+ return FT_CHAR;
} else if (S_ISBLK(st.st_mode))
return FT_BLOCK;
else if (S_ISFIFO(st.st_mode))
@@ -909,265 +1122,6 @@ dd_filetype(const char * filename, off_t & st_size)
return FT_OTHER;
}
-#if 0
-static int
-dd_filetype(const char * filename)
-{
- struct stat st;
- size_t len = strlen(filename);
-
- if ((1 == len) && ('.' == filename[0]))
- return FT_DEV_NULL;
- if (stat(filename, &st) < 0)
- return FT_ERROR;
- if (S_ISCHR(st.st_mode)) {
- if ((MEM_MAJOR == major(st.st_rdev)) &&
- (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
- return FT_DEV_NULL;
- if (RAW_MAJOR == major(st.st_rdev))
- return FT_RAW;
- if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
- return FT_SG;
- if (SCSI_TAPE_MAJOR == major(st.st_rdev))
- return FT_ST;
- } else if (S_ISBLK(st.st_mode))
- return FT_BLOCK;
- return FT_OTHER;
-}
-#endif
-
-static void
-usage(int pg_num)
-{
- if (pg_num > 3)
- goto page4;
- else if (pg_num > 2)
- goto page3;
- else if (pg_num > 1)
- goto page2;
-
- pr2serr("Usage: sgh_dd [bs=BS] [conv=CONVS] [count=COUNT] [ibs=BS] "
- "[if=IFILE]\n"
- " [iflag=FLAGS] [obs=BS] [of=OFILE] [oflag=FLAGS] "
- "[seek=SEEK]\n"
- " [skip=SKIP] [--help] [--version]\n\n");
- pr2serr(" [ae=AEN[,MAEN]] [bpt=BPT] [cdbsz=6|10|12|16] "
- "[coe=0|1]\n"
- " [dio=0|1] [elemsz_kb=EKB] [fail_mask=FM] "
- "[fua=0|1|2|3]\n"
- " [mrq=[I|O,]NRQS[,C]] [noshare=0|1] "
- "[of2=OFILE2]\n"
- " [ofreg=OFREG] [ofsplit=OSP] [sdt=SDT] "
- "[sync=0|1]\n"
- " [thr=THR] [time=0|1|2[,TO]] [unshare=1|0] "
- "[verbose=VERB]\n"
- " [--dry-run] [--prefetch] [-v|-vv|-vvv] "
- "[--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"
- " conv comma separated list from: [nocreat,noerror,"
- "notrunc,\n"
- " null,sync]\n"
- " count number of blocks to copy (def: device size)\n"
- " if file or device to read from (def: stdin)\n"
- " iflag comma separated list from: [00,coe,defres,dio,"
- "direct,dpo,\n"
- " dsync,excl,ff,fua,hipri,masync,mmap,mout_if,"
- "mrq_immed,\n"
- " mrq_svb,nocreat,nodur,noxfer,null,qhead,"
- "qtail,\n"
- " random,same_fds,v3,v4,wq_excl]\n"
- " of file or device to write to (def: /dev/null "
- "N.B. different\n"
- " from dd it defaults to stdout). If 'of=.' "
- "uses /dev/null\n"
- " of2 second file or device to write to (def: "
- "/dev/null)\n"
- " oflag comma separated list from: [append,<<list from "
- "iflag>>]\n"
- " seek block position to start writing to OFILE\n"
- " skip block position to start reading from IFILE\n"
- " --help|-h output this usage message then exit\n"
- " --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"
- "Copy IFILE to OFILE, similar to dd command. This utility is "
- "specialized for\nSCSI devices and uses multiple POSIX threads. "
- "It expects one or both IFILE\nand OFILE to be sg devices. With "
- "--verify option does a verify/compare\noperation instead of a "
- "copy. This utility is Linux specific and uses the\nv4 sg "
- "driver 'share' capability if available. Use '-hh', '-hhh' or "
- "'-hhhh'\nfor more information.\n"
- );
- return;
-page2:
- pr2serr("Syntax: sgh_dd [operands] [options]\n\n"
- " where: operands have the form name=value and are pecular to "
- "'dd'\n"
- " style commands, and options start with one or "
- "two hyphens;\n"
- " the lesser used operands and option are:\n\n"
- " ae AEN: abort every n commands (def: 0 --> don't "
- "abort any)\n"
- " MAEN: abort every n mrq commands (def: 0 --> "
- "don't)\n"
- " [requires commands with > 1 ms duration]\n"
- " bpt is blocks_per_transfer (default is 128)\n"
- " cdbsz size of SCSI READ, WRITE or VERIFY cdb_s "
- "(default is 10)\n"
- " coe continue on error, 0->exit (def), "
- "1->zero + continue\n"
- " dio is direct IO, 1->attempt, 0->indirect IO (def)\n"
- " elemsz_kb scatter gather list element size in kilobytes "
- "(def: 32[KB])\n"
- " fail_mask 1: misuse KEEP_SHARE flag; 0: nothing (def)\n"
- " fua force unit access: 0->don't(def), 1->OFILE, "
- "2->IFILE,\n"
- " 3->OFILE+IFILE\n"
- " mrq number of cmds placed in each sg call "
- "(def: 0);\n"
- " may have trailing ',C', to send bulk cdb_s; "
- "if preceded\n"
- " by 'I' then mrq only on IFILE, likewise 'O' "
- "for OFILE\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"
- " ofsplit split ofile write in two at block OSP (def: 0 "
- "(no split))\n"
- " sdt stall detection times: CRT[,ICT]. CRT: check "
- "repetition\n"
- " time (after first) in seconds; ICT: initial "
- "check time\n"
- " in milliseconds. Default: 3,300 . Use CRT=0 "
- "to disable\n"
- " sync 0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
- "after copy\n"
- " thr is number of threads, must be > 0, default 4, "
- "max 1024\n"
- " time 0->no timing, 1->calc throughput(def), "
- "2->nanosec\n"
- " precision; TO is command timeout in seconds "
- "(def: 60)\n"
- " unshare 0->don't explicitly unshare after share; 1->let "
- "close do\n"
- " file unshare (default)\n"
- " verbose increase verbosity\n"
- " --dry-run|-d prepare but bypass copy/read\n"
- " --verbose|-v increase verbosity of utility\n\n"
- "Use '-hhh' or '-hhhh' for more information about flags.\n"
- );
- return;
-page3:
- pr2serr("Syntax: sgh_dd [operands] [options]\n\n"
- " where: 'iflag=<arg>' and 'oflag=<arg>' arguments are listed "
- "below:\n\n"
- " 00 use all zeros instead of if=IFILE (only in "
- "iflags)\n"
- " append append output to OFILE (assumes OFILE is "
- "regular file)\n"
- " coe continue of error (reading, fills with zeros)\n"
- " defres keep default reserve buffer size (else its "
- "bs*bpt)\n"
- " dio sets the SG_FLAG_DIRECT_IO in sg requests\n"
- " direct sets the O_DIRECT flag on open()\n"
- " dpo sets the DPO (disable page out) in SCSI READs "
- "and WRITEs\n"
- " dsync sets the O_SYNC flag on open()\n"
- " excl sets the O_EXCL flag on open()\n"
- " ff use all 0xff bytes instead of if=IFILE (only in "
- "iflags)\n"
- " fua sets the FUA (force unit access) in SCSI READs "
- "and WRITEs\n"
- " hipri set HIPRI flag on command, uses blk_poll() to "
- "complete\n"
- " masync set 'more async' flag on this sg device\n"
- " mmap setup mmap IO on IFILE or OFILE; OFILE only "
- "with noshare\n"
- " mmap,mmap when used twice, doesn't call munmap()\n"
- " mout_if set META_OUT_IF flag on each request\n"
- " mrq_immed if mrq active, do submit non-blocking (def: "
- "ordered\n"
- " blocking)\n"
- " mrq_svb if mrq and sg->sg copy, do shared_variable_"
- "blocking\n"
- " nocreat will fail rather than create OFILE\n"
- " nodur turns off command duration calculations\n"
- " noxfer no transfer to/from the user space\n"
- " no_thresh skip checking per fd max data xfer\n"
- " null does nothing, placeholder\n"
- " qhead queue new request at head of block queue\n"
- " qtail queue new request at tail of block queue (def: "
- "q at head)\n"
- " random use random data instead of if=IFILE (only in "
- "iflags)\n"
- " same_fds each thread use the same IFILE and OFILE(2) "
- "file\n"
- " descriptors (def: each threads has own file "
- "descriptors)\n"
- " swait this option is now ignored\n"
- " v3 use v3 sg interface (def: v3 unless sg driver "
- "is v4)\n"
- " v4 use v4 sg interface (def: v3 unless sg driver "
- "is v4)\n"
- " wq_excl set SG_CTL_FLAGM_EXCL_WAITQ on this sg fd\n"
- "\n"
- "Copies IFILE to OFILE (and to OFILE2 if given). If IFILE and "
- "OFILE are sg\ndevices 'shared' mode is selected unless "
- "'noshare' is given to 'iflag=' or\n'oflag='. of2=OFILE2 uses "
- "'oflag=FLAGS'. When sharing, the data stays in a\nsingle "
- "in-kernel buffer which is copied (or mmap-ed) to the user "
- "space\nif the 'ofreg=OFREG' is given. Use '-hhhh' for more "
- "information.\n"
- );
- return;
-page4:
- pr2serr("pack_id:\n"
- "These are ascending integers, starting at 1, associated with "
- "each issued\nSCSI command. When both IFILE and OFILE are sg "
- "devices, then the READ in\neach read-write pair is issued an "
- "even pack_id and its WRITE pair is\ngiven the pack_id one "
- "higher (i.e. an odd number). This enables a\n'cat '"
- "/proc/scsi/sg/debug' user to see that progress is being "
- "made.\n\n");
- pr2serr("Debugging:\n"
- "Apart from using one or more '--verbose' options which gets a "
- "bit noisy\n'cat /proc/scsi/sg/debug' can give a good overview "
- "of what is happening.\nThat does a sg driver object tree "
- "traversal that does minimal locking\nto make sure that each "
- "traversal is 'safe'. So it is important to note\nthe whole "
- "tree is not locked. This means for fast devices the overall\n"
- "tree state may change while the traversal is occurring. For "
- "example,\nit has been observed that both the read- and write- "
- "sides of a request\nshare show they are in 'active' state "
- "which should not be possible.\nIt occurs because the read-"
- "sie probably jumped out of active state and\nthe write-side "
- "request entered it while some other nodes were being "
- "printed.\n\n");
- pr2serr("Busy state:\n"
- "Busy state (abbreviated to 'bsy' in the /proc/scsi/sg/debug "
- "output)\nis entered during request setup and completion. It "
- "is intended to be\na temporary state. It should not block "
- "but does sometimes (e.g. in\nblock_get_request()). Even so "
- "that blockage should be short and if not\nthere is a "
- "problem.\n\n");
- pr2serr("--verify :\n"
- "For comparing IFILE with OFILE. Does repeated sequences of: "
- "READ(ifile)\nand uses data returned to send to VERIFY(ofile, "
- "BYTCHK=1). So the OFILE\ndevice/disk is doing the actual "
- "comparison. Stops on first miscompare.\n\n");
- pr2serr("--prefetch :\n"
- "Used with --verify option. Prepends a PRE-FETCH(ofile, IMMED) "
- "to verify\nsequence. This should speed the trailing VERIFY by "
- "making sure that\nthe data it needs for the comparison is "
- "already in its cache.\n");
- return;
-}
-
static inline void
stop_both(struct global_collection * clp)
{
@@ -4506,6 +4460,9 @@ main(int argc, char * argv[])
} else if (FT_ST == clp->in_type) {
pr2serr("%sunable to use scsi tape device %s\n", my_name, inf);
return SG_LIB_FILE_ERROR;
+ } else if (FT_CHAR == clp->in_type) {
+ pr2serr("%sunable to use unknown char device %s\n", my_name, inf);
+ return SG_LIB_FILE_ERROR;
} else if (FT_SG == clp->in_type) {
clp->infd = sg_in_open(clp, inf, NULL, NULL);
if (clp->infd < 0)
@@ -4557,48 +4514,37 @@ main(int argc, char * argv[])
if ((FT_SG != clp->out_type) && clp->verify) {
pr2serr("%s --verify only supported by sg OFILEs\n", my_name);
return SG_LIB_FILE_ERROR;
- }
- if (FT_ST == clp->out_type) {
+ } else if (FT_ST == clp->out_type) {
pr2serr("%sunable to use scsi tape device %s\n", my_name, outf);
return SG_LIB_FILE_ERROR;
- }
- else if (FT_SG == clp->out_type) {
+ } else if (FT_CHAR == clp->out_type) {
+ pr2serr("%sunable to use unknown char device %s\n", my_name, outf);
+ return SG_LIB_FILE_ERROR;
+ } else if (FT_SG == clp->out_type) {
clp->outfd = sg_out_open(clp, outf, NULL, NULL);
if (clp->outfd < 0)
return -clp->outfd;
- }
- else if (FT_DEV_NULL == clp->out_type)
+ } else if (FT_DEV_NULL == clp->out_type)
clp->outfd = -1; /* don't bother opening */
else {
- if (FT_RAW != clp->out_type) {
- flags = O_WRONLY;
- if (! clp->out_flags.nocreat)
- flags |= O_CREAT;
- 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->out_flags.append)
- flags |= O_APPEND;
-
- if ((clp->outfd = open(outf, flags, 0666)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%scould not open %s for "
- "writing", my_name, outf);
- perror(ebuff);
- return sg_convert_errno(err);
- }
- }
- else { /* raw output file */
- if ((clp->outfd = open(outf, O_WRONLY)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%scould not open %s for raw "
- "writing", my_name, outf);
- perror(ebuff);
- return sg_convert_errno(err);
- }
+ flags = O_WRONLY;
+ if (! clp->out_flags.nocreat)
+ flags |= O_CREAT;
+ 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->out_flags.append)
+ flags |= O_APPEND;
+
+ if ((clp->outfd = open(outf, flags, 0666)) < 0) {
+ err = errno;
+ snprintf(ebuff, EBUFF_SZ, "%scould not open %s for "
+ "writing", my_name, outf);
+ perror(ebuff);
+ return sg_convert_errno(err);
}
if (clp->seek > 0) {
off64_t offset = clp->seek;
@@ -4626,7 +4572,7 @@ main(int argc, char * argv[])
if (out2f[0])
clp->ofile2_given = true;
if (out2f[0] && ('-' != out2f[0])) {
- off_t out2_st_size;
+ off_t out2_st_size;
clp->out2_type = dd_filetype(out2f, out2_st_size);
if (FT_ST == clp->out2_type) {
@@ -4641,35 +4587,24 @@ main(int argc, char * argv[])
else if (FT_DEV_NULL == clp->out2_type)
clp->out2fd = -1; /* don't bother opening */
else {
- if (FT_RAW != clp->out2_type) {
- flags = O_WRONLY;
- if (! clp->out_flags.nocreat)
- flags |= O_CREAT;
- 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->out_flags.append)
- flags |= O_APPEND;
-
- if ((clp->out2fd = open(out2f, flags, 0666)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%scould not open %s for "
- "writing", my_name, out2f);
- perror(ebuff);
- return sg_convert_errno(err);
- }
- }
- else { /* raw output file */
- if ((clp->out2fd = open(out2f, O_WRONLY)) < 0) {
- err = errno;
- snprintf(ebuff, EBUFF_SZ, "%scould not open %s for raw "
- "writing", my_name, out2f);
- perror(ebuff);
- return sg_convert_errno(err);
- }
+ flags = O_WRONLY;
+ if (! clp->out_flags.nocreat)
+ flags |= O_CREAT;
+ 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->out_flags.append)
+ flags |= O_APPEND;
+
+ if ((clp->out2fd = open(out2f, flags, 0666)) < 0) {
+ err = errno;
+ snprintf(ebuff, EBUFF_SZ, "%scould not open %s for "
+ "writing", my_name, out2f);
+ perror(ebuff);
+ return sg_convert_errno(err);
}
if (clp->seek > 0) {
off64_t offset = clp->seek;
@@ -4733,7 +4668,7 @@ main(int argc, char * argv[])
clp->unbalanced_mrq = true;
}
if (outregf[0]) {
- off_t outrf_st_size;
+ off_t outrf_st_size;
int ftyp = dd_filetype(outregf, outrf_st_size);
clp->outreg_type = ftyp;