aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--doc/sgp_dd.84
-rw-r--r--examples/sgq_dd.c8
-rw-r--r--scripts/54-before-scsi-sg3_id.rules55
-rw-r--r--scripts/55-scsi-sg3_id.rules104
-rw-r--r--src/sg_inq.c2
-rw-r--r--src/sg_inq_data.c7
-rw-r--r--src/sgp_dd.c11
-rw-r--r--testing/Makefile2
-rw-r--r--testing/Makefile.cplus12
-rw-r--r--testing/sg_tst_excl.cpp361
-rw-r--r--testing/sg_tst_excl2.cpp18
-rw-r--r--testing/sg_tst_excl3.cpp21
-rw-r--r--testing/sg_tst_ioctl.c178
-rw-r--r--testing/sgh_dd.cpp (renamed from testing/sgh_dd.c)309
-rw-r--r--testing/sgs_dd.c10
-rw-r--r--testing/uapi_sg.h20
17 files changed, 951 insertions, 175 deletions
diff --git a/ChangeLog b/ChangeLog
index d3f49802..cc5d36d4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,12 +2,13 @@ Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
directory have their own "man" pages. There is also a sg3_utils man page.
-Changelog for sg3_utils-1.45 [20190303] [svn: r813]
+Changelog for sg3_utils-1.45 [20190325] [svn: r814]
- sg_ses: bug: --page= being overridden when --control
and --data= also given; fix
- sg_opcodes: expand MLU (spc5r20)
- sg_write_buffer: allow comma and period separated
lists when input from stdin
+ - sg_inq: update version descriptors to spc5r21
- sg_format: add --dcrt used twice (FOV=1 DCRT=0)
- sg_raw: fix --send bug when using stdin
- sg_vpd: 3pc VPD page add copy group descriptor
@@ -18,6 +19,7 @@ Changelog for sg3_utils-1.45 [20190303] [svn: r813]
- tweak sg_pt interface to better handle bidi
- sg_cmds_process_resp(): two arguments removed
- sg_pt_freebsd: fixes for FreeBSD 12.0 release
+ - scripts/scsi-sg3_id: update rules
- linux: add nanosecond durations when
SG3_UTILS_LINUX_NANO environment variable given
- rescan-scsi-bus: widen LUN 0 only scanning
diff --git a/doc/sgp_dd.8 b/doc/sgp_dd.8
index bd18d2a5..684e67ff 100644
--- a/doc/sgp_dd.8
+++ b/doc/sgp_dd.8
@@ -1,4 +1,4 @@
-.TH SGP_DD "8" "February 2019" "sg3_utils\-1.45" SG3_UTILS
+.TH SGP_DD "8" "March 2019" "sg3_utils\-1.45" SG3_UTILS
.SH NAME
sgp_dd \- copy data to and from files and devices, especially SCSI
devices
@@ -123,7 +123,7 @@ transfer. Only active when \fIOFILE\fR is a sg device file name.
.TP
\fBthr\fR=\fITHR\fR
where \fITHR\fR is the number or worker threads (default 4) that attempt to
-copy in parallel. Minimum is 1 and maximum is 16.
+copy in parallel. Minimum is 1 and maximum is 1024.
.TP
\fBtime\fR=0 | 1
when 1, the transfer is timed and throughput calculation is
diff --git a/examples/sgq_dd.c b/examples/sgq_dd.c
index 00a98aad..2f163ef1 100644
--- a/examples/sgq_dd.c
+++ b/examples/sgq_dd.c
@@ -1,6 +1,6 @@
/*
* A utility program for the Linux OS SCSI generic ("sg") device driver.
- * Copyright (C) 1999-2018 D. Gilbert and P. Allworth
+ * Copyright (C) 1999-2010 D. Gilbert and P. Allworth
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
@@ -53,7 +53,7 @@ typedef uint8_t u_char; /* horrible, for scsi.h */
#include "sg_unaligned.h"
-static char * version_str = "0.62 20181207";
+static char * version_str = "0.63 20190324";
/* resurrected from "0.55 20020509" */
#define DEF_BLOCK_SIZE 512
@@ -67,7 +67,7 @@ static char * version_str = "0.62 20181207";
#define SGP_READ10 0x28
#define SGP_WRITE10 0x2a
#define DEF_NUM_THREADS 4 /* actually degree of concurrency */
-#define MAX_NUM_THREADS 32
+#define MAX_NUM_THREADS 1024
#ifndef RAW_MAJOR
#define RAW_MAJOR 255 /*unlikey value */
@@ -244,7 +244,7 @@ usage()
" usually either 'if' or 'of' is a sg or raw device\n"
" 'bpt' is blocks_per_transfer (default is 128)\n"
" 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
- " 'thr' is number of queues, must be > 0, default 4, max 32\n");
+ " 'thr' is number of queues, must be > 0, default 4, max 1024\n");
fprintf(stderr, " 'coe' continue on sg error, 0->exit (def), "
"1->zero + continue\n"
" 'time' 0->no timing(def), 1->time plus calculate throughput\n"
diff --git a/scripts/54-before-scsi-sg3_id.rules b/scripts/54-before-scsi-sg3_id.rules
new file mode 100644
index 00000000..bb36650a
--- /dev/null
+++ b/scripts/54-before-scsi-sg3_id.rules
@@ -0,0 +1,55 @@
+# do not edit this file, it will be overwritten on update
+
+# persistent storage links: /dev/disk/{by-id,by-path}
+# scheme based on "Linux persistent device names", 2004, Hannes Reinecke <hare@suse.de>
+
+# This file contains rules for setting udev environment variables based on
+# hardware properties (serial numbers etc), which can be obtained without
+# actually reading from the device.
+#
+# Hopefully this will be integrated into systemd/udev soon (as 54-storage-hardware.rules).
+# Until then, we ship it here in sg3-utils.
+# It's important that rules dealing with low-level hardware attributes run
+# before the generic SCSI rules in 55-scsi-sg3_utils.rules.
+
+ACTION=="remove", GOTO="storage_hardware_end"
+SUBSYSTEM!="block", GOTO="block_storage_end"
+KERNEL!="sd*|sr*|cciss*", GOTO="block_storage_end"
+
+# ignore partitions that span the entire disk
+TEST=="whole_disk", GOTO="block_storage_end"
+
+# for partitions import parent information
+ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}!="?*", IMPORT{parent}="ID_*"
+
+# ATA
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode"
+
+# ATAPI devices (SPC-3 or later)
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode"
+
+# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures)
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode"
+
+# Fall back usb_id for USB devices
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+
+# FireWire
+ENV{ID_IEEE1394}!="?*", KERNEL=="sd*|sr*", ATTRS{ieee1394_id}=="?*", ENV{ID_IEEE1394}="$attr{ieee1394_id}"
+
+# by-path
+ENV{ID_PATH}!="?*", ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id"
+
+LABEL="block_storage_end"
+
+# SCSI tape devices
+SUBSYSTEM!="scsi_tape", GOTO="storage_hardware_end"
+KERNEL!="st*[0-9]|nst*[0-9]", GOTO="storage_hardware_end"
+
+ENV{ID_SERIAL}!="?*", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394"
+ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", ATTRS{serial}=="?*", IMPORT{builtin}="usb_id"
+
+# by-path
+ENV{ID_PATH}!="?*", IMPORT{builtin}="path_id"
+
+LABEL="storage_hardware_end"
diff --git a/scripts/55-scsi-sg3_id.rules b/scripts/55-scsi-sg3_id.rules
index 4975bc53..9117e2db 100644
--- a/scripts/55-scsi-sg3_id.rules
+++ b/scripts/55-scsi-sg3_id.rules
@@ -2,10 +2,35 @@
ACTION=="remove", GOTO="sg3_utils_id_end"
-SUBSYSTEM!="block", GOTO="sg3_utils_id_end"
+SUBSYSTEM=="block", GOTO="block_dev"
+
+# SCSI devices other than "block"
+# This code used to live in 60-persistent-storage-tape.rules.
+
+# type 8 devices are "Medium Changers"
+SUBSYSTEM=="scsi_generic", KERNEL=="sg*[0-9]", ATTRS{type}=="8", \
+ GOTO="scsi_inquiry"
+SUBSYSTEM=="scsi_changer", KERNEL=="sch*[0-9]", ATTRS{type}=="8", \
+ ENV{.INQUIRY_DEV}="$root/bsg/$id", GOTO="scsi_inquiry"
+
+# tapes need to be accessed through their bsg device
+KERNEL=="st*[0-9]|nst*[0-9]", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", \
+ ENV{.INQUIRY_DEV}="$root/bsg/$id", GOTO="scsi_inquiry"
+
+GOTO="sg3_utils_id_end"
+
+LABEL="block_dev"
# Import values for partitions
-ENV{DEVTYPE}=="partition", IMPORT{parent}="SCSI_*", ENV{ID_SCSI}="1"
+ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_SCSI", IMPORT{parent}="SCSI_*"
+ENV{DEVTYPE}=="partition", ENV{ID_SCSI}=="1", GOTO="compat"
+
+# Handle non-SCSI devices that implement SCSI inquiry
+KERNEL=="cciss*", ENV{DEVTYPE}=="disk", GOTO="sg_inquiry"
+
+# Ignore everything else except sd/sr
+KERNEL!="sd*[!0-9]|sr*", GOTO="sg3_utils_id_end"
+
# SCSI INQUIRY values
# If the 'inquiry' sysfs attribute is present the kernel will already
# have scanned for VPD pages, so if the vpd page attribute is not
@@ -14,30 +39,62 @@ ENV{DEVTYPE}=="partition", IMPORT{parent}="SCSI_*", ENV{ID_SCSI}="1"
# Set 'ID_SCSI_INQUIRY=0' in an earlier udev rule if the kernel
# fails to scan VPD pages correctly; the rules will then fall
# back to calling sg_vpd directly.
-KERNEL=="sd*[!0-9]|sr*", ENV{ID_SCSI_INQUIRY}!="?*", IMPORT{program}="/usr/bin/sg_inq --export --inhex=/sys/block/$kernel/device/inquiry --raw", ENV{ID_SCSI}="1", ENV{ID_SCSI_INQUIRY}="1"
-KERNEL=="sd*[!0-9]|sr*", ENV{ID_SCSI}!="1", IMPORT{program}="/usr/bin/sg_inq --export $tempnode", ENV{ID_SCSI}="1"
+LABEL="scsi_inquiry"
+ENV{ID_SCSI_INQUIRY}=="0", GOTO="sg_inquiry"
+
+# As of 2018/4.15, the kernel doesn't provide VPD pages for "SPC" devices
+# (SCSI version 0x03, ANSI INCITS 301-1997) in sysfs.
+# It's usually safe to try though (no counter-example is known),
+# and for scsi_id compatibility, we have to try.
+SUBSYSTEMS=="scsi", ATTRS{scsi_level}=="4", GOTO="sg_inquiry"
+
+# "inquiry" is an attribute of the scsi_device in sysfs,
+# we obtain it by using $id after an ATTRS match.
+SUBSYSTEMS=="scsi", ATTRS{inquiry}=="*", KERNELS=="[0-9]*:*[0-9]", \
+ ENV{.SYSFS_PATH}="$sys/class/scsi_device/$id/device"
+ENV{.SYSFS_PATH}=="", GOTO="sg_inquiry"
+
+IMPORT{program}="/usr/bin/sg_inq --export --inhex=$env{.SYSFS_PATH}/inquiry --raw", \
+ ENV{ID_SCSI}="1", ENV{ID_SCSI_INQUIRY}="1"
+# If inquiry sysfs attribute reading it failed, fallback to sg
+ENV{ID_SCSI}!="1", GOTO="sg_inquiry"
+# Read VPD pages 80 (sn) and 83 (di)
+IMPORT{program}="/usr/bin/sg_inq --export --inhex=$env{.SYSFS_PATH}/vpd_pg80 --raw"
+IMPORT{program}="/usr/bin/sg_inq --export --inhex=$env{.SYSFS_PATH}/vpd_pg83 --raw"
+GOTO="compat"
+
+LABEL="sg_inquiry"
+# Handle devices that have no inquiry attributes in sysfs
+ENV{.INQUIRY_DEV}=="", ENV{.INQUIRY_DEV}="$tempnode"
+
+IMPORT{program}="/usr/bin/sg_inq --export $env{.INQUIRY_DEV}", ENV{ID_SCSI}="1"
+# Give up if this fails, too
+ENV{ID_SCSI}!="1", GOTO="sg3_utils_id_end"
+IMPORT{program}="/usr/bin/sg_inq --export --page=sn $env{.INQUIRY_DEV}"
+IMPORT{program}="/usr/bin/sg_inq --export --page=di $env{.INQUIRY_DEV}"
+
+LABEL="compat"
+
# scsi_id compat mappings
-ENV{SCSI_VENDOR}=="?*", ENV{ID_VENDOR}="$env{SCSI_VENDOR}"
-ENV{SCSI_VENDOR_ENC}=="?*", ENV{ID_VENDOR_ENC}="$env{SCSI_VENDOR_ENC}"
-ENV{SCSI_MODEL}=="?*", ENV{ID_MODEL}="$env{SCSI_MODEL}"
-ENV{SCSI_MODEL_ENC}=="?*", ENV{ID_MODEL_ENC}="$env{SCSI_MODEL_ENC}"
-ENV{SCSI_REVISION}=="?*", ENV{ID_REVISION}="$env{SCSI_REVISION}"
-ENV{SCSI_TYPE}=="?*", ENV{ID_TYPE}="$env{SCSI_TYPE}"
-# SCSI EVPD page 0x80 values
-KERNEL=="sd*[!0-9]|sr*", ENV{ID_SCSI}=="1", ENV{ID_SCSI_INQUIRY}=="1", IMPORT{program}="/usr/bin/sg_inq --export --inhex=/sys/block/$kernel/device/vpd_pg80 --raw"
-KERNEL=="sd*[!0-9]|sr*", ENV{ID_SCSI}=="1", ENV{ID_SCSI_INQUIRY}!="1", IMPORT{program}="/usr/bin/sg_inq --export --page=sn $tempnode"
-# SCSI EVPD page 0x83 values
-KERNEL=="sd*[!0-9]", ENV{ID_SCSI}=="1", ENV{ID_SCSI_INQUIRY}=="1", IMPORT{program}="/usr/bin/sg_inq --export --inhex=/sys/block/$kernel/device/vpd_pg83 --raw"
-KERNEL=="sd*[!0-9]|sr*", ENV{ID_SCSI}=="1", ENV{ID_SCSI_INQUIRY}!="1", IMPORT{program}="/usr/bin/sg_inq --export --page=di $tempnode"
+ENV{ID_VENDOR}!="?*", ENV{SCSI_VENDOR}=="?*", ENV{ID_VENDOR}="$env{SCSI_VENDOR}"
+ENV{ID_VENDOR_ENC}!="?*", ENV{SCSI_VENDOR_ENC}=="?*", ENV{ID_VENDOR_ENC}="$env{SCSI_VENDOR_ENC}"
+ENV{ID_MODEL}!="?*", ENV{SCSI_MODEL}=="?*", ENV{ID_MODEL}="$env{SCSI_MODEL}"
+ENV{ID_MODEL_ENC}!="?*", ENV{SCSI_MODEL_ENC}=="?*", ENV{ID_MODEL_ENC}="$env{SCSI_MODEL_ENC}"
+ENV{ID_REVISION}!="?*", ENV{SCSI_REVISION}=="?*", ENV{ID_REVISION}="$env{SCSI_REVISION}"
+ENV{ID_TYPE}!="?*", ENV{SCSI_TYPE}=="?*", ENV{ID_TYPE}="$env{SCSI_TYPE}"
+ENV{ID_TARGET_PORT}!="?*", ENV{SCSI_IDENT_PORT_TARGET_PORT_GROUP}=="?*", \
+ PROGRAM="/bin/sh -c 'echo $env{SCSI_IDENT_PORT_TARGET_PORT_GROUP} | /bin/sed s/^0x//'", \
+ ENV{ID_TARGET_PORT}="$result"
# ID_WWN compat mapping
-ENV{SCSI_IDENT_LUN_NAA_REGEXT}=="?*", ENV{ID_WWN}!="?*", ENV{ID_WWN}="0x$env{SCSI_IDENT_LUN_NAA_REGEXT}"
-ENV{SCSI_IDENT_LUN_NAA_REG}=="?*", ENV{ID_WWN}!="?*", ENV{ID_WWN}="0x$env{SCSI_IDENT_LUN_NAA_REG}"
-ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{ID_WWN}!="?*", ENV{ID_WWN}="0x$env{SCSI_IDENT_LUN_NAA_EXT}"
-ENV{SCSI_IDENT_LUN_NAA_LOCAL}=="?*", ENV{ID_WWN}!="?*", ENV{ID_WWN}="0x$env{SCSI_IDENT_LUN_NAA_LOCAL}"
ENV{SCSI_IDENT_LUN_NAA_REGEXT}=="?*", ENV{ID_WWN_WITH_EXTENSION}!="?*", ENV{ID_WWN_WITH_EXTENSION}="0x$env{SCSI_IDENT_LUN_NAA_REGEXT}"
+ENV{SCSI_IDENT_LUN_NAA_REG}=="?*", ENV{ID_WWN_WITH_EXTENSION}!="?*", ENV{ID_WWN_WITH_EXTENSION}="0x$env{SCSI_IDENT_LUN_NAA_REG}"
ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{ID_WWN_WITH_EXTENSION}!="?*", ENV{ID_WWN_WITH_EXTENSION}="0x$env{SCSI_IDENT_LUN_NAA_EXT}"
-ENV{ID_WWN}=="?*", ENV{ID_WWN_WITH_EXTENSION}!="?*", ENV{ID_WWN_WITH_EXTENSION}="$env{ID_WWN}"
+ENV{SCSI_IDENT_LUN_NAA_LOCAL}=="?*", ENV{ID_WWN_WITH_EXTENSION}!="?*", ENV{ID_WWN_WITH_EXTENSION}="0x$env{SCSI_IDENT_LUN_NAA_LOCAL}"
+# ID_WWN has max 16 characters
+ENV{ID_WWN_WITH_EXTENSION}=="?*", ENV{ID_WWN}!="?*", \
+ PROGRAM="/bin/sh -c 'echo $env{ID_WWN_WITH_EXTENSION} | /bin/sed s/^\\\(0x.\\\{1,16\\\}\\\).*/\\1/'", \
+ ENV{ID_WWN}="$result"
# ata_id compability
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_ATA}=="?*", ENV{ID_BUS}="ata", ENV{ID_ATA}="1", ENV{ID_SERIAL}="$env{SCSI_IDENT_LUN_ATA}"
@@ -45,11 +102,14 @@ ENV{ID_SERIAL_SHORT}!="?*", ENV{SCSI_VENDOR}=="ATA", ENV{SCSI_IDENT_LUN_VENDOR}=
# Compat ID_SERIAL setting
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_REGEXT}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA_REGEXT}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_REGEXT}"
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_REG}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA_REG}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_REG}"
-ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_EXT}"
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA_EXT}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_EXT}"
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_EUI64}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="2$env{SCSI_IDENT_LUN_EUI64}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_EUI64}"
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAME}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="8$env{SCSI_IDENT_LUN_NAME}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAME}"
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_T10}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="1$env{SCSI_IDENT_LUN_T10}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_T10}"
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_LOCAL}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA_LOCAL}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_LOCAL}"
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_VENDOR}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="0$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_LUN_VENDOR}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_VENDOR}"
ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_SERIAL}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="S$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_SERIAL}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_SERIAL}"
+
+# Compat ID_SCSI_SERIAL setting
+ENV{ID_SCSI_SERIAL}!="?*", ENV{SCSI_IDENT_SERIAL}=="?*", ENV{ID_SCSI_SERIAL}="$env{SCSI_IDENT_SERIAL}"
LABEL="sg3_utils_id_end"
diff --git a/src/sg_inq.c b/src/sg_inq.c
index 186e5bf6..d3534cb8 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -51,7 +51,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * version_str = "1.99 20190109"; /* SPC-5 rev 20 */
+static const char * version_str = "2.00 20190313"; /* SPC-5 rev 21 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
diff --git a/src/sg_inq_data.c b/src/sg_inq_data.c
index 96792c58..cd33f755 100644
--- a/src/sg_inq_data.c
+++ b/src/sg_inq_data.c
@@ -47,7 +47,7 @@ const char * sg_ansi_version_arr[16] =
/* table from SPC-5 revision 16 [sorted numerically (from Annex E.9)] */
/* Can also be obtained from : http://www.t10.org/lists/stds.txt 20170114 */
-/* Corrected against spc5r18 on 20180201 */
+/* Corrected against spc5r21 on 20190312 */
#ifdef SG_SCSI_STRINGS
@@ -487,7 +487,8 @@ struct sg_lib_simple_value_name_t sg_version_descriptor_arr[] = {
{0x1749, "UAS ISO/IEC 14776-251:2014"},
{0x1761, "ACS-2 (no version claimed)"},
{0x1762, "ACS-2 ANSI INCITS 482-2013"},
- {0x1765, "ACS-3 (no version claimed)"},
+ {0x1765, "ACS-3 INCITS 522-2014"},
+ {0x1767, "ACS-4 INCITS 529-2018"},
{0x1780, "UAS-2 (no version claimed)"},
{0x1ea0, "SAT (no version claimed)"},
{0x1ea7, "SAT T10/1711-D rev 8"},
@@ -538,6 +539,8 @@ struct sg_lib_simple_value_name_t sg_version_descriptor_arr[] = {
{0xffc0, "IEEE 1667 (no version claimed)"},
{0xffc1, "IEEE 1667-2006"},
{0xffc2, "IEEE 1667-2009"},
+ {0xffc3, "IEEE 1667-2015"},
+ {0xffc4, "IEEE 1667-2018"},
{0xffff, NULL}, /* sentinel, leave at end */
};
diff --git a/src/sgp_dd.c b/src/sgp_dd.c
index 6a85c4aa..9321b9af 100644
--- a/src/sgp_dd.c
+++ b/src/sgp_dd.c
@@ -1,7 +1,7 @@
/* A utility program for copying files. Specialised for "files" that
* represent devices that understand the SCSI command set.
*
- * Copyright (C) 1999 - 2018 D. Gilbert and P. Allworth
+ * Copyright (C) 1999 - 2019 D. Gilbert and P. Allworth
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
@@ -68,7 +68,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "5.71 20181227";
+static const char * version_str = "5.72 20190324";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -86,7 +86,7 @@ static const char * version_str = "5.71 20181227";
#define SGP_READ10 0x28
#define SGP_WRITE10 0x2a
#define DEF_NUM_THREADS 4
-#define MAX_NUM_THREADS SG_MAX_QUEUE
+#define MAX_NUM_THREADS 1024 /* was SG_MAX_QUEUE (16) but no longer applies */
#ifndef RAW_MAJOR
#define RAW_MAJOR 255 /*unlikely value */
@@ -187,6 +187,8 @@ static int sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp);
static pthread_mutex_t strerr_mut = PTHREAD_MUTEX_INITIALIZER;
+static pthread_t threads[MAX_NUM_THREADS];
+
static bool shutting_down = false;
static bool do_sync = false;
static bool do_time = false;
@@ -383,7 +385,7 @@ usage()
" sync 0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
"after copy\n"
" thr is number of threads, must be > 0, default 4, "
- "max 16\n"
+ "max 1024\n"
" time 0->no timing(def), 1->time plus calculate "
"throughput\n"
" verbose same as 'deb=VERB': increase verbosity\n"
@@ -1177,7 +1179,6 @@ main(int argc, char * argv[])
int res, k, err, keylen;
int64_t in_num_sect = 0;
int64_t out_num_sect = 0;
- pthread_t threads[MAX_NUM_THREADS];
int in_sect_sz, out_sect_sz, status, n, flags;
void * vp;
Rq_coll * clp = &rcoll;
diff --git a/testing/Makefile b/testing/Makefile
index 518eb5cb..26868e92 100644
--- a/testing/Makefile
+++ b/testing/Makefile
@@ -5,7 +5,7 @@ INSTDIR=$(DESTDIR)/$(PREFIX)/bin
MANDIR=$(DESTDIR)/$(PREFIX)/man
EXECS = sg_iovec_tst sg_sense_test sg_queue_tst bsg_queue_tst sg_chk_asc \
- sg_tst_nvme sg_tst_ioctl sg_tst_bidi tst_sg_lib sgh_dd sgs_dd
+ sg_tst_nvme sg_tst_ioctl sg_tst_bidi tst_sg_lib sgs_dd
EXTRAS =
diff --git a/testing/Makefile.cplus b/testing/Makefile.cplus
index 96fcf5c6..34c94831 100644
--- a/testing/Makefile.cplus
+++ b/testing/Makefile.cplus
@@ -9,7 +9,8 @@ LD = g++
## CC = clang++
## LD = clang++
-EXECS = sg_tst_excl sg_tst_excl2 sg_tst_excl3 sg_tst_context sg_tst_async
+EXECS = sg_tst_excl sg_tst_excl2 sg_tst_excl3 sg_tst_context sg_tst_async \
+ sgh_dd
EXTRAS =
@@ -30,8 +31,10 @@ CPPFLAGS = -std=c++11 -pthread -g -O2 -W -Wall -iquote ../include -D_REENTRANT $
LDFLAGS = -std=c++11 -pthread
LIBFILESOLD = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_io_linux.o
-LIBFILESNEW = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_pt_linux.o ../lib/sg_pt_common.o \
- ../lib/sg_pt_linux_nvme.o ../lib/sg_io_linux.o ../lib/sg_cmds_basic.o
+LIBFILESNEW = ../lib/sg_pt_linux_nvme.o ../lib/sg_lib.o ../lib/sg_lib_data.o \
+ ../lib/sg_pt_linux.o ../lib/sg_io_linux.o \
+ ../lib/sg_pt_common.o ../lib/sg_cmds_basic.o \
+ ../lib/sg_cmds_basic2.o
all: $(EXECS)
@@ -60,6 +63,9 @@ sg_tst_context: sg_tst_context.o $(LIBFILESNEW)
sg_tst_async: sg_tst_async.o $(LIBFILESNEW)
$(LD) -o $@ $(LDFLAGS) $^
+sgh_dd: sgh_dd.o $(LIBFILESNEW)
+ $(LD) -o $@ $(LDFLAGS) -pthread $^
+
install: $(EXECS)
install -d $(INSTDIR)
for name in $^; \
diff --git a/testing/sg_tst_excl.cpp b/testing/sg_tst_excl.cpp
index d5c0bec8..84f83892 100644
--- a/testing/sg_tst_excl.cpp
+++ b/testing/sg_tst_excl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 Douglas Gilbert.
+ * Copyright (c) 2013-2019 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,10 +43,37 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef HAVE_LINUX_SG_V4_HDR
+
+/* Kernel uapi header contain __user decorations on user space pointers
+ * to indicate they are unsafe in the kernel space. However glibc takes
+ * all those __user decorations out from headers in /usr/include/linux .
+ * So to stop compile errors when directly importing include/uapi/scsi/sg.h
+ * undef __user before doing that include. */
+#define __user
+
+/* Want to block the original sg.h header from also being included. That
+ * causes lots of multiple definition errors. This will only work if this
+ * header is included _before_ the original sg.h header. */
+#define _SCSI_GENERIC_H /* original kernel header guard */
+#define _SCSI_SG_H /* glibc header guard */
+
+#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
+
+#else
+#define __user
+#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
+
#include "sg_lib.h"
#include "sg_io_linux.h"
+#include "sg_unaligned.h"
-static const char * version_str = "1.10 20181207";
+static const char * version_str = "1.11 20190121";
static const char * util_name = "sg_tst_excl";
/* This is a test program for checking O_EXCL on open() works. It uses
@@ -102,21 +129,25 @@ static mutex console_mutex;
static unsigned int odd_count;
static unsigned int ebusy_count;
static unsigned int eagain_count;
+static int sg_ifc_ver = 3;
static void
usage(void)
{
- printf("Usage: %s [-b] [-f] [-h] [-l <lba>] [-n <n_per_thr>] "
- "[-t <num_thrs>]\n"
- " [-V] [-w <wait_ms>] [-x] [-xx] "
- "<sg_disk_device>\n", util_name);
+ printf("Usage: %s [-b] [-f] [-h] [-i <sg_ver>] [-l <lba>] "
+ "[-n <n_per_thr>]\n"
+ " [-t <num_thrs>] [-V] [-w <wait_ms>] "
+ "[-x] [-xx]\n"
+ " <sg_disk_device>\n", util_name);
printf(" where\n");
printf(" -b block on open (def: O_NONBLOCK)\n");
printf(" -f force: any SCSI disk (def: only "
"scsi_debug)\n");
printf(" WARNING: <lba> written to\n");
printf(" -h print this usage message then exit\n");
+ printf(" -i <sg_ver> sg driver interface version (default: "
+ "3)\n");
printf(" -l <lba> logical block to increment (def: %u)\n",
DEF_LBA);
printf(" -n <n_per_thr> number of loops per thread "
@@ -153,9 +184,9 @@ usage(void)
* closes dev_name. If an error occurs returns -1 else returns 0 if
* first int read from lba is even otherwise returns 1. */
static int
-do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
- int excl, int wait_ms, int id, unsigned int & ebusy,
- unsigned int & eagains)
+do_rd_inc_wr_twice_v3(const char * dev_name, unsigned int lba, int block,
+ int excl, int wait_ms, int id, unsigned int & ebusy,
+ unsigned int & eagains)
{
int k, sg_fd, ok, res;
int odd = 0;
@@ -170,10 +201,8 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
char ebuff[EBUFF_SZ];
int open_flags = O_RDWR;
- r16CmdBlk[6] = w16CmdBlk[6] = (lba >> 24) & 0xff;
- r16CmdBlk[7] = w16CmdBlk[7] = (lba >> 16) & 0xff;
- r16CmdBlk[8] = w16CmdBlk[8] = (lba >> 8) & 0xff;
- r16CmdBlk[9] = w16CmdBlk[9] = lba & 0xff;
+ sg_put_unaligned_be64(lba, r16CmdBlk + 2);
+ sg_put_unaligned_be64(lba, w16CmdBlk + 2);
if (! block)
open_flags |= O_NONBLOCK;
if (excl)
@@ -190,8 +219,8 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
sleep(0); // process yield ??
}
if (sg_fd < 0) {
- snprintf(ebuff, EBUFF_SZ,
- "do_rd_inc_wr_twice: error opening file: %s", dev_name);
+ snprintf(ebuff, EBUFF_SZ, "%s: error opening file: %s", __func__,
+ dev_name);
perror(ebuff);
return -1;
}
@@ -215,7 +244,7 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
{
lock_guard<mutex> lg(console_mutex);
- perror("do_rd_inc_wr_twice: write(sg, READ_16)");
+ perror(" write(sg, READ_16)");
}
close(sg_fd);
return -1;
@@ -225,7 +254,7 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
{
lock_guard<mutex> lg(console_mutex);
- perror("do_rd_inc_wr_twice: write(sg, READ_16) 2");
+ perror(" write(sg, READ_16) 2");
}
close(sg_fd);
return -1;
@@ -245,7 +274,7 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
{
lock_guard<mutex> lg(console_mutex);
- perror("do_rd_inc_wr_twice: read(sg, READ_16)");
+ perror(" read(sg, READ_16)");
}
close(sg_fd);
return -1;
@@ -287,7 +316,7 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
{
lock_guard<mutex> lg(console_mutex);
- perror("do_rd_inc_wr_twice: read(sg, READ_16) 2");
+ perror(" read(sg, READ_16) 2");
}
close(sg_fd);
return -1;
@@ -303,8 +332,8 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
{
lock_guard<mutex> lg(console_mutex);
- fprintf(stderr, "Recovered error on READ_16, continuing "
- "2\n");
+ fprintf(stderr, "%s: Recovered error on READ_16, "
+ "continuing 2\n", __func__);
}
ok = 1;
break;
@@ -322,14 +351,12 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
return -1;
}
- u = (lb[0] << 24) + (lb[1] << 16) + (lb[2] << 8) + lb[3];
+ u = sg_get_unaligned_be32(lb);
+ // Assuming u starts test as even (probably 0), expect it to stay even
if (0 == k)
odd = (1 == (u % 2));
++u;
- lb[0] = (u >> 24) & 0xff;
- lb[1] = (u >> 16) & 0xff;
- lb[2] = (u >> 8) & 0xff;
- lb[3] = u & 0xff;
+ sg_put_unaligned_be32(u, lb);
if (wait_ms > 0) /* allow daylight for bad things ... */
this_thread::sleep_for(milliseconds{wait_ms});
@@ -355,7 +382,7 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
{
lock_guard<mutex> lg(console_mutex);
- perror("do_rd_inc_wr_twice: WRITE_16 SG_IO ioctl error");
+ perror(" WRITE_16 SG_IO ioctl error");
}
close(sg_fd);
return -1;
@@ -370,7 +397,8 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
{
lock_guard<mutex> lg(console_mutex);
- fprintf(stderr, "Recovered error on WRITE_16, continuing\n");
+ fprintf(stderr, "%s: Recovered error on WRITE_16, "
+ "continuing\n", __func__);
}
ok = 1;
break;
@@ -391,6 +419,263 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
return odd;
}
+/* Opens dev_name and spins if busy (i.e. gets EBUSY), sleeping for
+ * wait_ms milliseconds if wait_ms is positive.
+ * Reads lba (twice) and treats the first 4 bytes as an int (SCSI endian),
+ * increments it and writes it back. Repeats so that happens twice. Then
+ * closes dev_name. If an error occurs returns -1 else returns 0 if
+ * first int read from lba is even otherwise returns 1. */
+static int
+do_rd_inc_wr_twice_v4(const char * dev_name, unsigned int lba, int block,
+ int excl, int wait_ms, int id, unsigned int & ebusy,
+ unsigned int & eagains)
+{
+ int k, sg_fd, ok, res;
+ int odd = 0;
+ unsigned int u = 0;
+ struct sg_io_v4 pt, pt2;
+ unsigned char r16CmdBlk [READ16_CMD_LEN] =
+ {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+ unsigned char w16CmdBlk [WRITE16_CMD_LEN] =
+ {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+ unsigned char sense_buffer[64];
+ unsigned char lb[READ16_REPLY_LEN];
+ char ebuff[EBUFF_SZ];
+ int open_flags = O_RDWR;
+
+ sg_put_unaligned_be64(lba, r16CmdBlk + 2);
+ sg_put_unaligned_be64(lba, w16CmdBlk + 2);
+ if (! block)
+ open_flags |= O_NONBLOCK;
+ if (excl)
+ open_flags |= O_EXCL;
+
+ while (((sg_fd = open(dev_name, open_flags)) < 0) &&
+ (EBUSY == errno)) {
+ ++ebusy;
+ if (wait_ms > 0)
+ this_thread::sleep_for(milliseconds{wait_ms});
+ else if (0 == wait_ms)
+ this_thread::yield();
+ else if (-2 == wait_ms)
+ sleep(0); // process yield ??
+ }
+ if (sg_fd < 0) {
+ snprintf(ebuff, EBUFF_SZ, "%s: error opening file: %s", __func__,
+ dev_name);
+ perror(ebuff);
+ return -1;
+ }
+
+ for (k = 0; k < 2; ++k) {
+ /* Prepare READ_16 command */
+ memset(&pt, 0, sizeof(pt));
+ pt.guard = 'Q';
+ pt.request_len = sizeof(r16CmdBlk);
+ pt.max_response_len = sizeof(sense_buffer);
+ // pt.dxfer_direction = SG_DXFER_FROM_DEV;
+ pt.din_xfer_len = READ16_REPLY_LEN;
+ pt.din_xferp = (uint64_t)(sg_uintptr_t)lb;
+ pt.request = (uint64_t)(sg_uintptr_t)r16CmdBlk;
+ pt.response = (uint64_t)(sg_uintptr_t)sense_buffer;
+ pt.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ pt.request_extra = id; /* pack_id field */
+
+ // queue up two READ_16s to same LBA
+ if (ioctl(sg_fd, SG_IOSUBMIT, &pt) < 0) {
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ perror(" write(sg, READ_16)");
+ }
+ close(sg_fd);
+ return -1;
+ }
+ pt2 = pt;
+ if (ioctl(sg_fd, SG_IOSUBMIT, &pt2) < 0) {
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ perror(" write(sg, READ_16) 2");
+ }
+ close(sg_fd);
+ return -1;
+ }
+
+ while (((res = ioctl(sg_fd, SG_IORECEIVE, &pt)) < 0) &&
+ (EAGAIN == errno)) {
+ ++eagains;
+ if (wait_ms > 0)
+ this_thread::sleep_for(milliseconds{wait_ms});
+ else if (0 == wait_ms)
+ this_thread::yield();
+ else if (-2 == wait_ms)
+ sleep(0); // process yield ??
+ }
+ if (res < 0) {
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ perror(" read(sg, READ_16)");
+ }
+ close(sg_fd);
+ return -1;
+ }
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category_new(pt.device_status, pt.transport_status,
+ pt.driver_status, sense_buffer, pt.response_len)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ fprintf(stderr, "Recovered error on READ_16, continuing\n");
+ }
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ sg_linux_sense_print("READ_16 command error",
+ pt.device_status, pt.transport_status,
+ pt.driver_status, sense_buffer,
+ pt.response_len, true);
+ // sg_chk_n_print3("READ_16 command error", &pt, 1);
+ }
+ break;
+ }
+ if (ok) {
+ while (((res = ioctl(sg_fd, SG_IORECEIVE, &pt2)) < 0) &&
+ (EAGAIN == errno)) {
+ ++eagains;
+ if (wait_ms > 0)
+ this_thread::sleep_for(milliseconds{wait_ms});
+ else if (0 == wait_ms)
+ this_thread::yield();
+ else if (-2 == wait_ms)
+ sleep(0); // process yield ??
+ }
+ if (res < 0) {
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ perror(" read(sg, READ_16) 2");
+ }
+ close(sg_fd);
+ return -1;
+ }
+ pt = pt2;
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category_new(pt.device_status, pt.transport_status,
+ pt.driver_status, sense_buffer, pt.response_len)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ fprintf(stderr, "%s: Recovered error on READ_16, "
+ "continuing 2\n", __func__);
+ }
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ sg_linux_sense_print("READ_16 command error 2",
+ pt.device_status,
+ pt.transport_status,
+ pt.driver_status, sense_buffer,
+ pt.response_len, true);
+ // sg_chk_n_print3("READ_16 command error 2", &pt, 1);
+ }
+ break;
+ }
+ }
+ if (! ok) {
+ close(sg_fd);
+ return -1;
+ }
+
+ u = sg_get_unaligned_be32(lb);
+ // Assuming u starts test as even (probably 0), expect it to stay even
+ if (0 == k)
+ odd = (1 == (u % 2));
+ ++u;
+ sg_put_unaligned_be32(u, lb);
+
+ if (wait_ms > 0) /* allow daylight for bad things ... */
+ this_thread::sleep_for(milliseconds{wait_ms});
+ else if (0 == wait_ms)
+ this_thread::yield();
+ else if (-2 == wait_ms)
+ sleep(0); // process yield ??
+
+ /* Prepare WRITE_16 command */
+ memset(&pt, 0, sizeof(pt));
+ pt.guard = 'Q';
+ pt.request_len = sizeof(w16CmdBlk);
+ pt.max_response_len = sizeof(sense_buffer);
+ // pt.dxfer_direction = SG_DXFER_TO_DEV;
+ pt.dout_xfer_len = WRITE16_REPLY_LEN;
+ pt.dout_xferp = (uint64_t)(sg_uintptr_t)lb;
+ pt.request = (uint64_t)(sg_uintptr_t)w16CmdBlk;
+ pt.response = (uint64_t)(sg_uintptr_t)sense_buffer;
+ pt.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ pt.request_extra = id; /* pack_id field */
+
+ if (ioctl(sg_fd, SG_IO, &pt) < 0) {
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ perror(" WRITE_16 SG_IO ioctl error");
+ }
+ close(sg_fd);
+ return -1;
+ }
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category_new(pt.device_status, pt.transport_status,
+ pt.driver_status, sense_buffer, pt.response_len)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ fprintf(stderr, "%s: Recovered error on WRITE_16, "
+ "continuing\n", __func__);
+ }
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ {
+ lock_guard<mutex> lg(console_mutex);
+
+ sg_linux_sense_print("WRITE_16 command error",
+ pt.device_status, pt.transport_status,
+ pt.driver_status, sense_buffer,
+ pt.response_len, true);
+ }
+ break;
+ }
+ if (! ok) {
+ close(sg_fd);
+ return -1;
+ }
+ }
+ close(sg_fd);
+ return odd;
+}
+
#define INQ_REPLY_LEN 96
@@ -500,8 +785,18 @@ work_thread(const char * dev_name, unsigned int lba, int id, int block,
<< block << endl;
}
for (k = 0; k < num; ++k) {
- res = do_rd_inc_wr_twice(dev_name, lba, block, excl, wait_ms, k,
- thr_ebusy_count, thr_eagain_count);
+ if (sg_ifc_ver == 3)
+ res = do_rd_inc_wr_twice_v3(dev_name, lba, block, excl, wait_ms,
+ k, thr_ebusy_count, thr_eagain_count);
+ else if (sg_ifc_ver == 4)
+ res = do_rd_inc_wr_twice_v4(dev_name, lba, block, excl, wait_ms,
+ k, thr_ebusy_count, thr_eagain_count);
+ else {
+ lock_guard<mutex> lg(console_mutex);
+
+ cerr << "sg_ifc_ver=" << sg_ifc_ver << " not supported" << endl;
+ res = -1;
+ }
if (res < 0)
break;
if (res)
@@ -548,6 +843,12 @@ main(int argc, char * argv[])
else if (0 == memcmp("-h", argv[k], 2)) {
usage();
return 0;
+ } else if (0 == memcmp("-i", argv[k], 2)) {
+ ++k;
+ if ((k < argc) && isdigit(*argv[k]))
+ sg_ifc_ver = atoi(argv[k]);
+ else
+ break;
} else if (0 == memcmp("-l", argv[k], 2)) {
++k;
if ((k < argc) && isdigit(*argv[k]))
diff --git a/testing/sg_tst_excl2.cpp b/testing/sg_tst_excl2.cpp
index 9a03906c..491d7fc6 100644
--- a/testing/sg_tst_excl2.cpp
+++ b/testing/sg_tst_excl2.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014 Douglas Gilbert.
+ * Copyright (c) 2013-2019 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -45,8 +45,9 @@
#include <sys/stat.h>
#include "sg_lib.h"
#include "sg_pt.h"
+#include "sg_unaligned.h"
-static const char * version_str = "1.08 20181207";
+static const char * version_str = "1.09 20190321";
static const char * util_name = "sg_tst_excl2";
/* This is a test program for checking O_EXCL on open() works. It uses
@@ -209,10 +210,8 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
char ebuff[EBUFF_SZ];
int open_flags = O_RDWR;
- r16CmdBlk[6] = w16CmdBlk[6] = (lba >> 24) & 0xff;
- r16CmdBlk[7] = w16CmdBlk[7] = (lba >> 16) & 0xff;
- r16CmdBlk[8] = w16CmdBlk[8] = (lba >> 8) & 0xff;
- r16CmdBlk[9] = w16CmdBlk[9] = lba & 0xff;
+ sg_put_unaligned_be64(lba, r16CmdBlk + 2);
+ sg_put_unaligned_be64(lba, w16CmdBlk + 2);
if (! block)
open_flags |= O_NONBLOCK;
if (excl)
@@ -267,15 +266,12 @@ do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
goto err;
}
- u = (lb[0] << 24) + (lb[1] << 16) + (lb[2] << 8) + lb[3];
+ u = sg_get_unaligned_be32(lb);
// Assuming u starts test as even (probably 0), expect it to stay even
if (0 == k)
odd = (1 == (u % 2));
++u;
- lb[0] = (u >> 24) & 0xff;
- lb[1] = (u >> 16) & 0xff;
- lb[2] = (u >> 8) & 0xff;
- lb[3] = u & 0xff;
+ sg_put_unaligned_be32(u, lb);
if (wait_ms > 0) /* allow daylight for bad things ... */
this_thread::sleep_for(milliseconds{wait_ms});
diff --git a/testing/sg_tst_excl3.cpp b/testing/sg_tst_excl3.cpp
index bd15389a..6d32a4fb 100644
--- a/testing/sg_tst_excl3.cpp
+++ b/testing/sg_tst_excl3.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2018 Douglas Gilbert.
+ * Copyright (c) 2013-2019 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,10 +43,12 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+
#include "sg_lib.h"
#include "sg_pt.h"
+#include "sg_unaligned.h"
-static const char * version_str = "1.06 20181207";
+static const char * version_str = "1.07 20190321";
static const char * util_name = "sg_tst_excl3";
/* This is a test program for checking O_EXCL on open() works. It uses
@@ -54,7 +56,7 @@ static const char * util_name = "sg_tst_excl3";
* to "break" O_EXCL. The strategy is to open a device O_EXCL|O_NONBLOCK
* and do a double increment on a LB then close it from a single thread.
* the remaining threads open that device O_NONBLOCK and do a read and
- * note of the number is odd. Assuming the count starts as an even
+ * note if the number is odd. Assuming the count starts as an even
* (typically 0) then it should remain even. Odd instances
* are counted and reported at the end of the program, after all threads
* have completed.
@@ -215,10 +217,8 @@ do_rd_inc_wr_twice(const char * dev_name, int read_only, unsigned int lba,
char ebuff[EBUFF_SZ];
int open_flags = O_RDWR;
- r16CmdBlk[6] = w16CmdBlk[6] = (lba >> 24) & 0xff;
- r16CmdBlk[7] = w16CmdBlk[7] = (lba >> 16) & 0xff;
- r16CmdBlk[8] = w16CmdBlk[8] = (lba >> 8) & 0xff;
- r16CmdBlk[9] = w16CmdBlk[9] = lba & 0xff;
+ sg_put_unaligned_be64(lba, r16CmdBlk + 2);
+ sg_put_unaligned_be64(lba, w16CmdBlk + 2);
if (! block)
open_flags |= O_NONBLOCK;
if (excl)
@@ -273,7 +273,7 @@ do_rd_inc_wr_twice(const char * dev_name, int read_only, unsigned int lba,
goto err;
}
- u = (lb[0] << 24) + (lb[1] << 16) + (lb[2] << 8) + lb[3];
+ u = sg_get_unaligned_be32(lb);
// Assuming u starts test as even (probably 0), expect it to stay even
if (0 == k)
odd = (1 == (u % 2));
@@ -288,10 +288,7 @@ do_rd_inc_wr_twice(const char * dev_name, int read_only, unsigned int lba,
if (read_only)
break;
++u;
- lb[0] = (u >> 24) & 0xff;
- lb[1] = (u >> 16) & 0xff;
- lb[2] = (u >> 8) & 0xff;
- lb[3] = u & 0xff;
+ sg_put_unaligned_be32(u, lb);
/* Prepare WRITE_16 command */
clear_scsi_pt_obj(ptp);
diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c
index ea3dde90..5e6b0eff 100644
--- a/testing/sg_tst_ioctl.c
+++ b/testing/sg_tst_ioctl.c
@@ -15,6 +15,9 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
@@ -53,9 +56,9 @@
* later of the Linux sg driver. */
-static const char * version_str = "Version: 1.04 20190201";
+static const char * version_str = "Version: 1.06 20190323";
-#define INQ_REPLY_LEN 96
+#define INQ_REPLY_LEN 128
#define INQ_CMD_LEN 6
#define SDIAG_CMD_LEN 6
#define SENSE_BUFFER_LEN 96
@@ -80,11 +83,14 @@ static bool do_fork = false;
static bool ioctl_only = false;
static bool q_at_tail = false;
static bool write_only = false;
+static bool mrq_immed = false; /* if set, also sets mrq_iosubmit */
+static bool mrq_iosubmit = false;
static int childs_pid = 0;
static int q_len = DEF_Q_LEN;
static int sleep_secs = 0;
static int reserve_buff_sz = DEF_RESERVE_BUFF_SZ;
+static int num_mrqs = 0;
static int verbose = 0;
static const char * relative_cp = NULL;
@@ -93,13 +99,20 @@ static const char * relative_cp = NULL;
static void
usage(void)
{
- printf("Usage: sg_tst_ioctl [-f] [-h] [-l=Q_LEN] [-o] [-r=SZ] [-s=SEC] "
- "[-t]\n"
- " [-v] [-V] [-w] <sg_device> [<sg_device2>]\n"
+ printf("Usage: sg_tst_ioctl [-f] [-h] [-l=Q_LEN] [-m=MRQS[,I|S]] [-r=SZ] "
+ "[-s=SEC]\n"
+ " [-t] [-v] [-V] [-w] <sg_device> "
+ "[<sg_device2>]\n"
" where:\n"
" -f fork and test share between processes\n"
" -h help: print usage message then exit\n"
" -l=Q_LEN queue length, between 1 and 511 (def: 16)\n"
+ " -m=MRQS[,I|S] test multi-req, MRQS number to do; if "
+ "the letter\n"
+ " 'I' is appended after a comma, then do "
+ "IMMED mrq;\n"
+ " 'S' is appended, then use "
+ "ioctl(SG_IOSUBMIT)\n"
" -o ioctls only, then exit\n"
" -r=SZ reserve buffer size in KB (def: 256 --> 256 "
"KB)\n"
@@ -240,7 +253,6 @@ tst_ioctl(const char * fnp, int sg_fd, const char * fn2p, int sg_fd2,
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_UNSHARE;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_MASTER_FINI;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_MASTER_ERR;
- seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_CHECK_FOR_MORE;
seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
@@ -286,9 +298,6 @@ tst_ioctl(const char * fnp, int sg_fd, const char * fn2p, int sg_fd2,
if (SG_CTL_FLAGM_MASTER_ERR & seip->ctl_flags_rd_mask)
printf(" %sMASTER_ERR: %s\n", cp,
(SG_CTL_FLAGM_MASTER_ERR & cflags) ? "true" : "false");
- if (SG_CTL_FLAGM_CHECK_FOR_MORE & seip->ctl_flags_rd_mask)
- printf(" %sCHECK_FOR_MORE: %s\n", cp,
- (SG_CTL_FLAGM_CHECK_FOR_MORE & cflags) ? "true" : "false");
}
if (SG_SEIM_MINOR_INDEX & seip->sei_rd_mask)
printf(" %sminor_index: %u\n", cp, seip->minor_index);
@@ -437,14 +446,134 @@ bypass_share:
return 0;
}
-#include <linux/fs.h>
-#include <linux/blktrace_api.h>
+static int
+do_mrqs(int sg_fd, int sg_fd2, int mrqs)
+{
+ bool both = (sg_fd2 >= 0);
+ int k, arr_v4_sz, good;
+ int res = 0;
+ struct sg_io_v4 * arr_v4;
+ struct sg_io_v4 * h4p;
+ struct sg_io_v4 * mrq_h4p;
+ struct sg_io_v4 mrq_h4;
+ uint8_t sense_buffer[SENSE_BUFFER_LEN];
+ uint8_t inq_cdb[INQ_CMD_LEN] = /* Device Id VPD page */
+ {0x12, 0x1, 0x83, 0, INQ_REPLY_LEN, 0};
+ uint8_t sdiag_cdb[SDIAG_CMD_LEN] =
+ {0x1d, 0x10 /* PF */, 0, 0, 0, 0};
+ uint8_t inqBuff[INQ_REPLY_LEN];
+
+ if (both) {
+ struct sg_extended_info sei;
+ struct sg_extended_info * seip;
+
+ seip = &sei;
+ memset(seip, 0, sizeof(*seip));
+ seip->sei_wr_mask |= SG_SEIM_SHARE_FD;
+ seip->sei_rd_mask |= SG_SEIM_SHARE_FD;
+ seip->share_fd = sg_fd; /* master */
+ if (ioctl(sg_fd2, SG_SET_GET_EXTENDED, seip) < 0) {
+ res = errno;
+ pr2serr("ioctl(sg_fd2, SG_SET_GET_EXTENDED) shared_fd, "
+ "failed errno=%d %s\n", res, strerror(res));
+ return res;
+ }
+ }
+ memset(inqBuff, 0, sizeof(inqBuff));
+ mrq_h4p = &mrq_h4;
+ memset(mrq_h4p, 0, sizeof(*mrq_h4p));
+ mrq_h4p->guard = 'Q';
+ mrq_h4p->flags = SGV4_FLAG_MULTIPLE_REQS;
+ if (mrq_immed)
+ mrq_h4p->flags |= SGV4_FLAG_IMMED;
+ arr_v4 = calloc(mrqs, sizeof(struct sg_io_v4));
+ if (NULL == arr_v4) {
+ res = ENOMEM;
+ goto fini;
+ }
+ arr_v4_sz = mrqs * sizeof(struct sg_io_v4);
+
+ for (k = 0; k < mrqs; ++k) {
+ h4p = arr_v4 + k;
+
+ h4p->guard = 'Q';
+ /* ->protocol and ->subprotocol are already zero */
+ /* io_hdr[k].iovec_count = 0; */ /* memset takes care of this */
+ if (0 == (k % 2)) {
+ h4p->request_len = sizeof(sdiag_cdb);
+ h4p->request = (uint64_t)sdiag_cdb;
+ /* all din and dout fields are zero */
+ } else {
+ h4p->request_len = sizeof(inq_cdb);
+ h4p->request = (uint64_t)inq_cdb;
+ h4p->din_xfer_len = INQ_REPLY_LEN;
+ h4p->din_xferp = (uint64_t)inqBuff;
+ if (both)
+ h4p->flags |= SGV4_FLAG_DO_ON_OTHER;
+ }
+ h4p->response = (uint64_t)sense_buffer;
+ h4p->max_response_len = sizeof(sense_buffer);
+ h4p->timeout = 20000; /* 20000 millisecs == 20 seconds */
+ h4p->request_extra = k + 3; /* so pack_id doesn't start at 0 */
+ /* default is to queue at head (in SCSI mid level) */
+ if (q_at_tail)
+ h4p->flags |= SG_FLAG_Q_AT_TAIL;
+ else
+ h4p->flags |= SG_FLAG_Q_AT_HEAD;
+ }
+ mrq_h4p->din_xferp = (uint64_t)arr_v4;
+ mrq_h4p->din_xfer_len = arr_v4_sz;
+ mrq_h4p->dout_xferp = mrq_h4p->din_xferp;
+ mrq_h4p->dout_xfer_len = mrq_h4p->din_xfer_len;
+ if (ioctl(sg_fd, (mrq_iosubmit ? SG_IOSUBMIT : SG_IO), mrq_h4p) < 0) {
+ res = errno;
+ pr2serr("ioctl(SG_IO%s, mrq) failed, errno=%d %s\n",
+ (mrq_iosubmit ? "SUBMIT" : ""), res, strerror(res));
+ goto fini;
+ }
+ if (mrq_immed) {
+mrq_h4p->flags = SGV4_FLAG_MULTIPLE_REQS; // zap SGV4_FLAG_IMMED
+ if (ioctl(sg_fd, SG_IORECEIVE, mrq_h4p) < 0) {
+ res = errno;
+ pr2serr("ioctl(SG_IORECEIVE, mrq) failed, errno=%d %s\n",
+ res, strerror(res));
+ goto fini;
+ }
+ }
+
+ for (k = 0, good = 0; k < mrqs; ++k) {
+ h4p = arr_v4 + k;
+ if (! (h4p->driver_status || h4p->transport_status ||
+ h4p->device_status)) {
+ if (h4p->info & SG_INFO_MRQ_FINI)
+ ++good;
+ }
+ }
+ if (good > 0) {
+ printf("Final INQUIRY response:\n");
+ hex2stdout(inqBuff, INQ_REPLY_LEN, 0);
+ }
+ printf("Good responses: %d, bad responses: %d\n", good, mrqs - good);
+ if (mrq_h4p->driver_status != 0)
+ printf("Master mrq object: driver_status=%d\n",
+ mrq_h4p->driver_status);
+ h4p = arr_v4 + mrqs - 1;
+ if (h4p->driver_status != 0)
+ printf("Last mrq object: driver_status=%d\n", h4p->driver_status);
+
+fini:
+ if (arr_v4)
+ free(arr_v4);
+ return res;
+}
+
int
main(int argc, char * argv[])
{
bool done;
int sg_fd, k, ok, ver_num, pack_id, num_waiting;
+ int res = 0;
int sg_fd2 = -1;
int sock = -1;
uint8_t inq_cdb[INQ_CMD_LEN] =
@@ -479,6 +608,26 @@ main(int argc, char * argv[])
file_name = 0;
break;
}
+ } else if (0 == memcmp("-m=", argv[k], 3)) {
+ num_mrqs = sg_get_num(argv[k] + 3);
+ if (num_mrqs < 1) {
+ printf("Expect -m= to take a number greater than 0\n");
+ file_name = 0;
+ break;
+ }
+ if ((cp = strchr(argv[k] + 3, ','))) {
+ mrq_iosubmit = true;
+ if (toupper(cp[1]) == 'I')
+ mrq_immed = true;
+ else if (toupper(cp[1]) == 'S')
+ ;
+ else {
+ printf("-m= option expects 'A' or 'a' as a suffix, "
+ "after comma\n");
+ file_name = 0;
+ break;
+ }
+ }
} else if (0 == memcmp("-o", argv[k], 2))
ioctl_only = true;
else if (0 == memcmp("-r=", argv[k], 3)) {
@@ -562,6 +711,11 @@ main(int argc, char * argv[])
second_fname, sg_fd2);
}
+ if (num_mrqs > 0) {
+ res = do_mrqs(sg_fd, sg_fd2, num_mrqs);
+ goto out;
+ }
+
if (do_fork) {
int pid;
int sv[2];
@@ -731,5 +885,5 @@ out:
close(sg_fd);
if (sg_fd2 >= 0)
close(sg_fd2);
- return 0;
+ return res;
}
diff --git a/testing/sgh_dd.c b/testing/sgh_dd.cpp
index 5ff9e710..0fdfb366 100644
--- a/testing/sgh_dd.c
+++ b/testing/sgh_dd.cpp
@@ -22,7 +22,7 @@
* in this case) are transferred to or from the sg device in a single SCSI
* command.
*
- * This version is designed for the linux kernel 2.4, 2.6, 3 and 4 series.
+ * This version is designed for the linux kernel 2.4, 2.6, 3, 4 and 5 series.
*
* sgp_dd is a Posix threads specialization of the sg_dd utility. Both
* sgp_dd and sg_dd only perform special tasks when one or both of the given
@@ -46,7 +46,6 @@
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
-#include <stdatomic.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
@@ -67,6 +66,10 @@
#include <linux/fs.h> /* for BLKSSZGET and friends */
#include <sys/mman.h> /* for mmap() system call */
+#include <vector>
+#include <array>
+#include <atomic> // C++ header replacing <stdatomic.h>
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -98,7 +101,9 @@
#include "sg_pr2serr.h"
-static const char * version_str = "1.20 20190212";
+using namespace std;
+
+static const char * version_str = "1.21 20190324";
#ifdef __GNUC__
#ifndef __clang__
@@ -107,7 +112,7 @@ static const char * version_str = "1.20 20190212";
#endif
/* <<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>> xxxxxxxxxx beware next line */
-#define SGH_DD_READ_COMPLET_AFTER 1
+// #define SGH_DD_READ_COMPLET_AFTER 1
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -125,7 +130,8 @@ static const char * version_str = "1.20 20190212";
#define SGP_READ10 0x28
#define SGP_WRITE10 0x2a
#define DEF_NUM_THREADS 4
-#define MAX_NUM_THREADS SG_MAX_QUEUE
+#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 */
@@ -143,6 +149,8 @@ static const char * version_str = "1.20 20190212";
#define EBUFF_SZ 768
+#define PROC_SCSI_SG_VERSION "/proc/scsi/sg/version"
+
struct flags_t {
bool append;
bool coe;
@@ -160,6 +168,7 @@ struct flags_t {
bool swait;
bool v3;
bool v4;
+ bool v4_given;
};
typedef struct global_collection
@@ -177,6 +186,7 @@ typedef struct global_collection
int in_partial; /* | */
bool in_stop; /* | */
pthread_mutex_t in_mutex; /* -/ */
+ int nmrqs; /* Number of multi-reqs for sg v4 */
int outfd;
int64_t seek;
int out_type;
@@ -212,10 +222,12 @@ typedef struct request_element
{ /* one instance per worker thread */
bool wr;
bool has_share;
+ bool both_sg;
bool swait; /* interleave READ WRITE async copy segment: READ submit,
* WRITE submit, READ receive, WRITE receive */
int id;
int infd;
+ int nmrqs;
int outfd;
int out2fd;
int outregfd;
@@ -249,30 +261,39 @@ typedef struct thread_info
pthread_t a_pthr;
} Thread_info;
-static atomic_int mono_pack_id = 0;
-static atomic_long pos_index = 0;
+// typedef vector< pair<int, struct sg_io_v4> > mrq_arr_t;
+typedef array<uint8_t, 32> cmd_at;
+typedef pair< vector<struct sg_io_v4>, vector<cmd_at> > mrq_arr_t;
+
+static atomic<int> mono_pack_id(0);
+static atomic<long int> pos_index(0);
static sigset_t signal_set;
static pthread_t sig_listen_thread_id;
static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
-static void sg_in_rd_cmd(Gbl_coll * clp, Rq_elem * rep);
-static void sg_out_wr_cmd(Gbl_coll * clp, Rq_elem * rep, bool is_wr2);
+static void sg_in_rd_cmd(Gbl_coll * clp, Rq_elem * rep, mrq_arr_t & def_arr);
+static void sg_out_wr_cmd(Gbl_coll * clp, Rq_elem * rep, mrq_arr_t & def_arr,
+ bool is_wr2);
static bool normal_in_rd(Gbl_coll * clp, Rq_elem * rep, int blocks);
static void normal_out_wr(Gbl_coll * clp, Rq_elem * rep, int blocks);
-static int sg_start_io(Rq_elem * rep, bool is_wr2);
+static int sg_start_io(Rq_elem * rep, mrq_arr_t & def_arr, bool is_wr2);
static int sg_finish_io(bool wr, Rq_elem * rep, bool is_wr2);
static int sg_in_open(Gbl_coll *clp, const char *inf, uint8_t **mmpp,
int *mmap_len);
static int sg_out_open(Gbl_coll *clp, const char *outf, uint8_t **mmpp,
int *mmap_len);
-static void sg_in_out_interleave(Gbl_coll *clp, Rq_elem * rep);
+static void sg_in_out_interleave(Gbl_coll *clp, Rq_elem * rep,
+ mrq_arr_t & def_arr);
+static int sgh_do_def(Rq_elem * rep, mrq_arr_t & def_arr);
#define STRERR_BUFF_LEN 128
static pthread_mutex_t strerr_mut = PTHREAD_MUTEX_INITIALIZER;
+static bool have_sg_version = false;
+static int sg_version = 0;
static bool shutting_down = false;
static bool do_sync = false;
static bool do_time = true;
@@ -347,7 +368,6 @@ lk_chk_n_print3(const char * leadin, struct sg_io_hdr * hp, bool raw_sinfo)
pthread_mutex_unlock(&strerr_mut);
}
-
static void
lk_chk_n_print4(const char * leadin, struct sg_io_v4 * h4p, bool raw_sinfo)
{
@@ -359,6 +379,57 @@ lk_chk_n_print4(const char * leadin, struct sg_io_v4 * h4p, bool raw_sinfo)
}
static void
+hex2stderr_lk(const uint8_t * b_str, int len, int no_ascii)
+{
+ pthread_mutex_lock(&strerr_mut);
+ hex2stderr(b_str, len, no_ascii);
+ pthread_mutex_unlock(&strerr_mut);
+}
+
+static void
+v4hdr_out_lk(const char * leadin, const sg_io_v4 * h4p)
+{
+ pthread_mutex_lock(&strerr_mut);
+ if (leadin)
+ pr2serr("%s\n", leadin);
+ if (('Q' != h4p->guard) || (0 != h4p->protocol) ||
+ (0 != h4p->subprotocol))
+ pr2serr(" <<<sg_io_v4 _NOT_ properly set>>>\n");
+ pr2serr(" pointers: cdb=%s sense=%s din=%s dout=%s\n",
+ (h4p->request ? "y" : "NULL"), (h4p->response ? "y" : "NULL"),
+ (h4p->din_xferp ? "y" : "NULL"),
+ (h4p->dout_xferp ? "y" : "NULL"));
+ pr2serr(" lengths: cdb=%u sense=%u din=%u dout=%u\n",
+ h4p->request_len, h4p->max_response_len, h4p->din_xfer_len,
+ h4p->dout_xfer_len);
+ pr2serr(" flags=0x%x request_extra=0x%x OUT--> response_len=%d\n",
+ h4p->flags, h4p->request_extra, h4p->response_len);
+ pr2serr(" driver_status=0x%x transport_status=0x%x device_status=0x%x\n",
+ h4p->driver_status, h4p->transport_status, h4p->device_status);
+ pr2serr(" info=0x%x din_resid=%u dout_resid=%u spare_out=%u\n",
+ h4p->info, h4p->din_resid, h4p->dout_resid, h4p->spare_out);
+ pthread_mutex_unlock(&strerr_mut);
+}
+
+
+static void
+fetch_sg_version(void)
+{
+ FILE * fp;
+ char b[96];
+
+ have_sg_version = false;
+ sg_version = 0;
+ fp = fopen(PROC_SCSI_SG_VERSION, "r");
+ if (fp && fgets(b, sizeof(b) - 1, fp)) {
+ if (1 == sscanf(b, "%d", &sg_version))
+ have_sg_version = !!sg_version;
+ }
+ if (fp)
+ fclose(fp);
+}
+
+static void
calc_duration_throughput(int contin)
{
struct timeval end_tm, res_tm;
@@ -530,10 +601,10 @@ usage(int pg_num)
pr2serr(" [ae=AEN] [bpt=BPT] [cdbsz=6|10|12|16] [coe=0|1] "
"[deb=VERB]\n"
" [dio=0|1] [elemsz_kb=ESK] [fua=0|1|2|3] "
- "[of2=OFILE2]\n"
- " [ofreg=OFREG] [sync=0|1] [thr=THR] [time=0|1] "
- "[verbose=VERB]\n"
- " [--dry-run] [--verbose]\n\n"
+ "[mrq=NRQS]\n"
+ " [of2=OFILE2] [ofreg=OFREG] [sync=0|1] [thr=THR] "
+ "[time=0|1]\n"
+ " [verbose=VERB] [--dry-run] [--verbose]\n\n"
" where the main options (shown in first group above) are:\n"
" bs must be device logical block size (default "
"512)\n"
@@ -563,8 +634,8 @@ usage(int pg_num)
"is Linux specific and uses the v4 sg driver\n'share' capability "
"if available. Use '-hh' or '-hhh' for more information.\n"
#ifdef SGH_DD_READ_COMPLET_AFTER
- "\nIn this version oflag=swait does read completion _after_ "
- "write completion\n"
+ "\nIn this version oflag=swait does read completion _after_ "
+ "write completion\n"
#endif
);
return;
@@ -577,6 +648,7 @@ page2:
"are:\n"
" ae abort every n commands (def: 0 --> don't abort "
"any)\n"
+ " [requires commands with > 1 ms duration]\n"
" bpt is blocks_per_transfer (default is 128)\n"
" cdbsz size of SCSI READ or WRITE cdb (default is 10)\n"
" coe continue on error, 0->exit (def), "
@@ -589,6 +661,8 @@ page2:
" fua force unit access: 0->don't(def), 1->OFILE, "
"2->IFILE,\n"
" 3->OFILE+IFILE\n"
+ " mrq even number of cmds placed in each sg call "
+ "(def: 0)\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"
@@ -633,9 +707,9 @@ page3:
"is finished;\n"
" [oflag only] and IFILE and OFILE must be sg "
"devices\n"
- " v3 use v3 sg interface which is the default (also "
- "see v4)\n"
- " v4 use v4 sg interface (def: v3 unless other side "
+ " 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"
"\n"
"Copies IFILE to OFILE (and to OFILE2 if given). If IFILE and "
@@ -816,6 +890,7 @@ read_write_thread(void * v_tip)
bool own_outfd = false;
bool own_out2fd = false;
bool share_and_ofreg;
+ mrq_arr_t def_arr; /* MRQ deferred array (vector) */
tip = (Thread_info *)v_tip;
clp = tip->gcp;
@@ -825,7 +900,7 @@ read_write_thread(void * v_tip)
/* Following clp members are constant during lifetime of thread */
rep->id = tip->id;
if (vb > 0)
- pr2serr_lk("Starting worker thread %d\n", rep->id);
+ pr2serr_lk("%d <-- Starting worker thread\n", rep->id);
if (! clp->in_flags.mmap) {
rep->buffp = sg_memalign(sz, 0 /* page align */, &rep->alloc_bp,
false);
@@ -843,9 +918,14 @@ read_write_thread(void * v_tip)
rep->cdbsz_out = clp->cdbsz_out;
rep->in_flags = clp->in_flags;
rep->out_flags = clp->out_flags;
+ rep->nmrqs = clp->nmrqs;
rep->aen = clp->aen;
rep->rep_count = 0;
+ if ((FT_SG == clp->in_type) && (FT_SG == clp->out_type) &&
+ (rep->infd != rep->outfd))
+ rep->both_sg = true;
+
if (rep->in_flags.same_fds || rep->out_flags.same_fds) {
/* we are sharing a single pair of fd_s across all threads */
if (rep->out_flags.swait && (! swait_reported)) {
@@ -924,16 +1004,18 @@ read_write_thread(void * v_tip)
/* vvvvvvvvvvvvvv Main segment copy loop vvvvvvvvvvvvvvvvvvvvvvv */
while (1) {
rep->wr = false;
- my_index = atomic_fetch_add(&pos_index, clp->bpt);
+ my_index = atomic_fetch_add(&pos_index, (long int)clp->bpt);
/* Start of READ half of a segment */
status = pthread_mutex_lock(&clp->in_mutex);
if (0 != status) err_exit(status, "lock in_mutex");
+#if 0
if (clp->in_stop || (clp->in_count <= 0)) {
/* no more to do, exit loop then thread */
status = pthread_mutex_unlock(&clp->in_mutex);
if (0 != status) err_exit(status, "unlock in_mutex");
break;
}
+#endif
if (dd_count >= 0) {
if (my_index >= dd_count) {
status = pthread_mutex_unlock(&clp->in_mutex);
@@ -956,9 +1038,9 @@ read_write_thread(void * v_tip)
pthread_cleanup_push(cleanup_in, (void *)clp);
if (FT_SG == clp->in_type) {
if (rep->swait)
- sg_in_out_interleave(clp, rep);
+ sg_in_out_interleave(clp, rep, def_arr);
else
- sg_in_rd_cmd(clp, rep); /* unlocks in_mutex mid operation */
+ sg_in_rd_cmd(clp, rep, def_arr); /* unlocks in_mutex mid op */
} else {
stop_after_write = normal_in_rd(clp, rep, blocks);
status = pthread_mutex_unlock(&clp->in_mutex);
@@ -988,6 +1070,7 @@ read_write_thread(void * v_tip)
}
skip_force_out_sequence:
+#if 0
if (clp->out_stop || (clp->out_count <= 0)) {
if (! clp->out_stop)
clp->out_stop = true;
@@ -995,6 +1078,7 @@ skip_force_out_sequence:
if (0 != status) err_exit(status, "unlock out_mutex");
break;
}
+#endif
if (stop_after_write)
clp->out_stop = true;
@@ -1002,6 +1086,12 @@ skip_force_out_sequence:
clp->out_count -= blocks;
if (0 == rep->num_blks) {
+ if ((rep->nmrqs > 0) && (def_arr.first.size() > 0)) {
+ if (rep->debug)
+ pr2serr_lk("thread=%d: tail-end, to_do=%u\n", rep->id,
+ (uint32_t)def_arr.first.size());
+ sgh_do_def(rep, def_arr);
+ }
clp->out_stop = true;
stop_after_write = true;
status = pthread_mutex_unlock(&clp->out_mutex);
@@ -1027,7 +1117,7 @@ skip_force_out_sequence:
status = pthread_mutex_unlock(&clp->out_mutex);
if (0 != status) err_exit(status, "unlock out_mutex");
} else
- sg_out_wr_cmd(clp, rep, false); /* releases out_mutex */
+ sg_out_wr_cmd(clp, rep, def_arr, false); /* release out_mtx */
} else if (FT_DEV_NULL == clp->out_type) {
/* skip actual write operation */
clp->out_rem_count -= blocks;
@@ -1047,7 +1137,8 @@ skip_force_out_sequence:
pthread_cleanup_push(cleanup_out, (void *)clp);
status = pthread_mutex_lock(&clp->out2_mutex);
if (0 != status) err_exit(status, "lock out2_mutex");
- sg_out_wr_cmd(clp, rep, true); /* releases out2_mutex mid oper */
+ /* releases out2_mutex mid operation */
+ sg_out_wr_cmd(clp, rep, def_arr, true);
pthread_cleanup_pop(0);
}
@@ -1055,7 +1146,15 @@ skip_force_out_sequence:
pthread_cond_broadcast(&clp->out_sync_cv);
if (stop_after_write)
break;
- } /* end of while loop which copies segments */
+ } /* ^^^^^^^^^^ end of main while loop which copies segments ^^^^^^ */
+#if 0
+ if ((rep->nmrqs > 0) && (def_arr.first.size() > 0)) {
+ if (rep->debug)
+ pr2serr_lk("thread=%d: tail-end, to_do=%u\n", rep->id,
+ (uint32_t)def_arr.first.size());
+ sgh_do_def(rep, def_arr);
+ }
+#endif
status = pthread_mutex_lock(&clp->in_mutex);
if (0 != status) err_exit(status, "lock in_mutex");
@@ -1083,6 +1182,9 @@ fini:
if (own_out2fd && (rep->out2fd >= 0))
close(rep->out2fd);
pthread_cond_broadcast(&clp->out_sync_cv);
+ if (rep->num_blks > 0)
+ pr2serr("%d <-- thread exiting with rep->num_blks=%d\n", rep->id,
+ rep->num_blks);
return stop_after_write ? NULL : clp;
}
@@ -1254,13 +1356,13 @@ sg_build_scsi_cdb(uint8_t * cdbp, int cdb_sz, unsigned int blocks,
/* Enters this function holding in_mutex */
static void
-sg_in_rd_cmd(Gbl_coll * clp, Rq_elem * rep)
+sg_in_rd_cmd(Gbl_coll * clp, Rq_elem * rep, mrq_arr_t & def_arr)
{
int res;
int status;
while (1) {
- res = sg_start_io(rep, false);
+ res = sg_start_io(rep, def_arr, false);
if (1 == res)
err_exit(ENOMEM, "sg starting in command");
else if (res < 0) {
@@ -1374,7 +1476,7 @@ sg_wr_swap_share(Rq_elem * rep, int to_fd, bool before)
/* Enters this function holding out_mutex */
static void
-sg_out_wr_cmd(Gbl_coll * clp, Rq_elem * rep, bool is_wr2)
+sg_out_wr_cmd(Gbl_coll * clp, Rq_elem * rep, mrq_arr_t & def_arr, bool is_wr2)
{
int res;
int status;
@@ -1384,7 +1486,7 @@ sg_out_wr_cmd(Gbl_coll * clp, Rq_elem * rep, bool is_wr2)
sg_wr_swap_share(rep, rep->out2fd, true);
while (1) {
- res = sg_start_io(rep, is_wr2);
+ res = sg_start_io(rep, def_arr, is_wr2);
if (1 == res)
err_exit(ENOMEM, "sg starting out command");
else if (res < 0) {
@@ -1453,7 +1555,76 @@ fini:
/* Returns 0 on success, 1 if ENOMEM error else -1 for other errors. */
static int
-sg_start_io(Rq_elem * rep, bool is_wr2)
+sgh_do_def(Rq_elem * rep, mrq_arr_t & def_arr)
+{
+ int n, k, res, fd;
+ struct sg_io_v4 * a_v4p;
+ struct sg_io_v4 ctl_v4;
+
+ memset(&ctl_v4, 0, sizeof(ctl_v4));
+ ctl_v4.guard = 'Q';
+ a_v4p = def_arr.first.data();
+ n = def_arr.first.size();
+ for (k = 0; k < n; ++k) {
+ struct sg_io_v4 * h4p = a_v4p + k;
+ uint8_t *cmdp = &def_arr.second[k].front();
+
+ h4p->request = (uint64_t)cmdp;
+ if (rep->debug > 3) {
+ pr2serr_lk("def_arr[%d]:\n", k);
+ hex2stderr_lk((const uint8_t *)(a_v4p + k), sizeof(*a_v4p), 1);
+ }
+ }
+ if (rep->both_sg)
+ fd = rep->infd; /* assume share to rep->outfd */
+ else if (rep->infd >= 0)
+ fd = rep->infd;
+ else
+ fd = rep->outfd;
+ res = 0;
+ ctl_v4.flags = SGV4_FLAG_MULTIPLE_REQS | SGV4_FLAG_STOP_IF;
+ ctl_v4.din_xferp = (uint64_t)a_v4p;
+ ctl_v4.din_xfer_len = n * sizeof(*a_v4p);
+ ctl_v4.dout_xferp = (uint64_t)a_v4p;
+ ctl_v4.dout_xfer_len = n * sizeof(*a_v4p);
+ if (rep->debug > 2) {
+ pr2serr_lk("%s: Controlling object _before_ ioctl(SG_IO):\n",
+ __func__);
+ if (rep->debug > 3)
+ hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
+ v4hdr_out_lk("Controlling object before:", &ctl_v4);
+ }
+ res = ioctl(fd, SG_IO, &ctl_v4); // MULTIPLE_REQS | STOP_IF
+ if (res < 0) {
+ pr2serr_lk("%s: ioctl(SG_IO, MULTIPLE_REQS)-->%d, errno=%d: %s\n",
+ __func__, res, errno, strerror(errno));
+ def_arr.first.clear();
+ def_arr.second.clear();
+ return -1;
+ }
+ if (rep->debug > 2) {
+ pr2serr_lk("%s: Controlling object output by ioctl(SG_IO):\n",
+ __func__);
+ if (rep->debug > 3)
+ hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
+ v4hdr_out_lk("Controlling object after:", &ctl_v4);
+ if (rep->debug > 3) {
+ for (k = 0; k < n; ++k) {
+ pr2serr_lk("AFTER: def_arr[%d]:\n", k);
+ v4hdr_out_lk(NULL, (a_v4p + k));
+ // hex2stderr_lk((const uint8_t *)(a_v4p + k), sizeof(*a_v4p),
+ // 1);
+ }
+ }
+ }
+ def_arr.first.clear();
+ def_arr.second.clear();
+ return res;
+}
+
+/* Returns 0 on success, 1 if ENOMEM error else -1 for other errors. */
+static int
+sg_start_io(Rq_elem * rep, mrq_arr_t & def_arr, bool is_wr2)
{
bool wr = rep->wr;
bool fua = wr ? rep->out_flags.fua : rep->in_flags.fua;
@@ -1560,6 +1731,19 @@ do_v4:
h4p->usr_ptr = (uint64_t)rep;
h4p->request_extra = rep->rq_id; /* this is the pack_id */
h4p->flags = flags;
+ if (rep->nmrqs > 0) {
+ if (rep->both_sg && (rep->outfd == fd))
+ h4p->flags |= SGV4_FLAG_DO_ON_OTHER;
+ cmd_at cmd_obj;
+ uint8_t * cmdp = &(cmd_obj[0]);
+ memcpy(cmdp, rep->cmd, cdbsz);
+ def_arr.first.push_back(*h4p);
+ def_arr.second.push_back(cmd_obj);
+ res = 0;
+ if ((int)def_arr.first.size() >= rep->nmrqs)
+ res = sgh_do_def(rep, def_arr);
+ return res;
+ }
while (((res = ioctl(fd, SG_IOSUBMIT, h4p)) < 0) &&
((EINTR == errno) || (EAGAIN == errno)))
sched_yield(); /* another thread may be able to progress */
@@ -1587,7 +1771,7 @@ do_v4:
if (res < 0)
pr2serr_lk("%s: ioctl(SG_IOABORT) failed: %s [%d]\n",
__func__, safe_strerror(errno), errno);
- else if (rep->debug > 3)
+ else if (rep->debug > 1)
pr2serr_lk("%s: sending ioctl(SG_IOABORT) on rq_id=%d\n",
__func__, rep->rq_id);
} /* else got response, too late for timeout, so skip */
@@ -1676,6 +1860,8 @@ sg_finish_io(bool wr, Rq_elem * rep, bool is_wr2)
return 0;
do_v4:
+ if (rep->nmrqs > 0)
+ return 0;
h4p = &rep->io_hdr4;
while (((res = ioctl(fd, SG_IORECEIVE, h4p)) < 0) &&
((EINTR == errno) || (EAGAIN == errno)))
@@ -1710,10 +1896,9 @@ do_v4:
rep->rq_id, blk);
lk_chk_n_print4(ebuff, h4p, false);
if ((rep->debug > 4) && h4p->info)
- pr2serr_lk(" info=0x%x sg_info_check=%d another_waiting=%d "
- "direct=%d detaching=%d aborted=%d\n", h4p->info,
+ pr2serr_lk(" info=0x%x sg_info_check=%d direct=%d "
+ "detaching=%d aborted=%d\n", h4p->info,
!!(h4p->info & SG_INFO_CHECK),
- !!(h4p->info & SG_INFO_ANOTHER_WAITING),
!!(h4p->info & SG_INFO_DIRECT_IO),
!!(h4p->info & SG_INFO_DEVICE_DETACHING),
!!(h4p->info & SG_INFO_ABORTED));
@@ -1733,10 +1918,9 @@ do_v4:
pr2serr_lk("%s: tid,rq_id=%d,%d: completed %s\n", __func__, rep->id,
rep->rq_id, cp);
if ((rep->debug > 4) && h4p->info)
- pr2serr_lk(" info=0x%x sg_info_check=%d another_waiting=%d "
- "direct=%d detaching=%d aborted=%d\n", h4p->info,
+ pr2serr_lk(" info=0x%x sg_info_check=%d direct=%d "
+ "detaching=%d aborted=%d\n", h4p->info,
!!(h4p->info & SG_INFO_CHECK),
- !!(h4p->info & SG_INFO_ANOTHER_WAITING),
!!(h4p->info & SG_INFO_DIRECT_IO),
!!(h4p->info & SG_INFO_DEVICE_DETACHING),
!!(h4p->info & SG_INFO_ABORTED));
@@ -1746,14 +1930,14 @@ do_v4:
/* Enter holding in_mutex, exits holding nothing */
static void
-sg_in_out_interleave(Gbl_coll *clp, Rq_elem * rep)
+sg_in_out_interleave(Gbl_coll *clp, Rq_elem * rep, mrq_arr_t & def_arr)
{
int res, pid_read, pid_write;
int status;
while (1) {
/* start READ */
- res = sg_start_io(rep, false);
+ res = sg_start_io(rep, def_arr, false);
pid_read = rep->rq_id;
if (1 == res)
err_exit(ENOMEM, "sg interleave starting in command");
@@ -1768,7 +1952,7 @@ sg_in_out_interleave(Gbl_coll *clp, Rq_elem * rep)
/* start WRITE */
rep->wr = true;
- res = sg_start_io(rep, false);
+ res = sg_start_io(rep, def_arr, false);
pid_write = rep->rq_id;
if (1 == res)
err_exit(ENOMEM, "sg interleave starting out command");
@@ -1786,7 +1970,7 @@ sg_in_out_interleave(Gbl_coll *clp, Rq_elem * rep)
#ifdef SGH_DD_READ_COMPLET_AFTER
#warning "SGH_DD_READ_COMPLET_AFTER is set (testing)"
- goto write_complet;
+ goto write_complet;
read_complet:
#endif
@@ -1847,7 +2031,7 @@ read_complet:
#ifdef SGH_DD_READ_COMPLET_AFTER
- return;
+ return;
write_complet:
#endif
@@ -1892,7 +2076,7 @@ write_complet:
if (0 != status) err_exit(status, "unlock out_mutex");
#ifdef SGH_DD_READ_COMPLET_AFTER
- goto read_complet;
+ goto read_complet;
#endif
return;
default:
@@ -2011,9 +2195,10 @@ process_flags(const char * arg, struct flags_t * fp)
fp->swait = true;
else if (0 == strcmp(cp, "v3"))
fp->v3 = true;
- else if (0 == strcmp(cp, "v4"))
+ else if (0 == strcmp(cp, "v4")) {
fp->v4 = true;
- else {
+ fp->v4_given = true;
+ } else {
pr2serr("unrecognised flag: %s\n", cp);
return false;
}
@@ -2144,10 +2329,16 @@ main(int argc, char * argv[])
clp->out2_type = FT_DEV_NULL;
clp->cdbsz_in = DEF_SCSI_CDBSZ;
clp->cdbsz_out = DEF_SCSI_CDBSZ;
+ clp->nmrqs = DEF_NUM_MRQS;
inf[0] = '\0';
outf[0] = '\0';
out2f[0] = '\0';
outregf[0] = '\0';
+ fetch_sg_version();
+ if (sg_version > 40000) {
+ clp->in_flags.v4 = true;
+ clp->out_flags.v4 = true;
+ }
for (k = 1; k < argc; k++) {
if (argv[k]) {
@@ -2232,6 +2423,13 @@ main(int argc, char * argv[])
pr2serr("%sbad argument to 'iflag='\n", my_name);
return SG_LIB_SYNTAX_ERROR;
}
+ } else if (0 == strcmp(key, "mrq")) {
+ clp->nmrqs = sg_get_num(buf);
+ if ((-1 == clp->nmrqs) || (1 == (clp->nmrqs % 2))) {
+ pr2serr("%sbad argument to 'mrq=', want even number or "
+ "zero\n", my_name);
+ return SG_LIB_SYNTAX_ERROR;
+ }
} else if (0 == strcmp(key, "obs")) {
obs = sg_get_num(buf);
if (-1 == obs) {
@@ -2451,7 +2649,7 @@ main(int argc, char * argv[])
}
}
clp->infp = inf;
- if ((clp->in_flags.v3 || clp->in_flags.v4) &&
+ if ((clp->in_flags.v3 || clp->in_flags.v4_given) &&
(FT_SG != clp->in_type)) {
clp->in_flags.v3 = false;
clp->in_flags.v4 = false;
@@ -2518,7 +2716,7 @@ main(int argc, char * argv[])
}
}
clp->outfp = outf;
- if ((clp->out_flags.v3 || clp->out_flags.v4) &&
+ if ((clp->out_flags.v3 || clp->out_flags.v4_given) &&
(FT_SG != clp->out_type)) {
clp->out_flags.v3 = false;
clp->out_flags.v4 = false;
@@ -2588,16 +2786,16 @@ main(int argc, char * argv[])
clp->out2fp = out2f;
}
if ((FT_SG == clp->in_type ) && (FT_SG == clp->out_type)) {
- if (clp->in_flags.v4 && (! clp->out_flags.v3)) {
- if (! clp->out_flags.v4) {
+ if (clp->in_flags.v4_given && (! clp->out_flags.v3)) {
+ if (! clp->out_flags.v4_given) {
clp->out_flags.v4 = true;
if (clp->debug)
pr2serr("Changing OFILE from v3 to v4, use oflag=v3 to "
"force v3\n");
}
}
- if (clp->out_flags.v4 && (! clp->in_flags.v3)) {
- if (! clp->in_flags.v4) {
+ if (clp->out_flags.v4_given && (! clp->in_flags.v3)) {
+ if (! clp->in_flags.v4_given) {
clp->in_flags.v4 = true;
if (clp->debug)
pr2serr("Changing IFILE from v3 to v4, use iflag=v3 to "
@@ -2809,7 +3007,8 @@ main(int argc, char * argv[])
status = pthread_join(tip->a_pthr, &vp);
if (0 != status) err_exit(status, "pthread_join");
if (clp->debug > 0)
- pr2serr_lk("Worker thread k=%d terminated\n", k);
+ pr2serr_lk("%d <-- Worker thread terminated, vp=%s\n", k,
+ ((vp == clp) ? "clp" : "NULL (or !clp)"));
}
} /* started worker threads and here after they have all exited */
diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c
index 6642ac1f..624c41b3 100644
--- a/testing/sgs_dd.c
+++ b/testing/sgs_dd.c
@@ -1,7 +1,7 @@
/*
* Test code for the extensions to the Linux OS SCSI generic ("sg")
* device driver.
- * Copyright (C) 1999-2018 D. Gilbert and P. Allworth
+ * Copyright (C) 1999-2019 D. Gilbert and P. Allworth
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -78,7 +78,7 @@
#include "sg_unaligned.h"
-static const char * version_str = "4.03 20190105";
+static const char * version_str = "4.04 20190324";
static const char * my_name = "sgs_dd";
#define DEF_BLOCK_SIZE 512
@@ -89,9 +89,9 @@ static const char * my_name = "sgs_dd";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
#define DEF_TIMEOUT 40000 /* 40,000 millisecs == 40 seconds */
#define S_RW_LEN 10 /* Use SCSI READ(10) and WRITE(10) */
-#define SGQ_MAX_RD_AHEAD 4
-#define SGQ_MAX_WR_AHEAD 4
-#define SGQ_NUM_ELEMS (SGQ_MAX_RD_AHEAD+ SGQ_MAX_WR_AHEAD + 1)
+#define SGQ_MAX_RD_AHEAD 32
+#define SGQ_MAX_WR_AHEAD 32
+#define SGQ_NUM_ELEMS (SGQ_MAX_RD_AHEAD + SGQ_MAX_WR_AHEAD + 1)
#define SGQ_FREE 0
#define SGQ_IO_STARTED 1
diff --git a/testing/uapi_sg.h b/testing/uapi_sg.h
index f109690d..635bb587 100644
--- a/testing/uapi_sg.h
+++ b/testing/uapi_sg.h
@@ -14,7 +14,7 @@
* Later extensions (versions 2, 3 and 4) to driver:
* Copyright (C) 1998 - 2018 Douglas Gilbert
*
- * Version 4.0.06 (20190210)
+ * Version 4.0.07 (20190320)
* This version is for Linux 2.6, 3, 4 and 5 series kernels.
*
* Documentation
@@ -90,8 +90,7 @@ typedef struct sg_io_hdr {
/* following flag values can be OR-ed together in v3::flags or v4::flags */
#define SG_FLAG_DIRECT_IO 1 /* default is indirect IO */
/* SG_FLAG_UNUSED_LUN_INHIBIT is ignored in sg v4 driver */
-#define SG_FLAG_UNUSED_LUN_INHIBIT 2 /* default is overwrite lun in SCSI */
- /* command block (when <= SCSI_2) */
+#define SG_FLAG_UNUSED_LUN_INHIBIT 2 /* ignored, was LUN overwrite in cdb */
#define SG_FLAG_MMAP_IO 4 /* request memory mapped IO */
/* no transfer of kernel buffers to/from user space; used for sharing */
#define SG_FLAG_NO_DXFER 0x10000
@@ -110,12 +109,15 @@ typedef struct sg_io_hdr {
#define SGV4_FLAG_YIELD_TAG 0x8 /* sg_io_v4::request_tag set after SG_IOS */
#define SGV4_FLAG_Q_AT_TAIL SG_FLAG_Q_AT_TAIL
#define SGV4_FLAG_Q_AT_HEAD SG_FLAG_Q_AT_HEAD
-/* Flag values 0x100 and 0x200 not currently used */
+#define SGV4_FLAG_COMPLETE_B4 0x100
+#define SGV4_FLAG_SIG_ON_OTHER 0x200
#define SGV4_FLAG_IMMED 0x400 /* for polling with SG_IOR, ignored in SG_IOS */
-/* Flag value 0x800 not currently used */
+#define SGV4_FLAG_STOP_IF 0x800 /* Stops sync mrq if error or warning */
#define SGV4_FLAG_DEV_SCOPE 0x1000 /* permit SG_IOABORT to have wider scope */
#define SGV4_FLAG_SHARE 0x2000 /* share IO buffer; needs SG_SEIM_SHARE_FD */
+#define SGV4_FLAG_DO_ON_OTHER 0x4000 /* available on either of shared pair */
#define SGV4_FLAG_NO_DXFER SG_FLAG_NO_DXFER /* needed for sharing */
+#define SGV4_FLAG_MULTIPLE_REQS 0x20000 /* n sg_io_v4s in data-in */
/* Output (potentially OR-ed together) in v3::info or v4::info field */
#define SG_INFO_OK_MASK 0x1
@@ -127,8 +129,8 @@ typedef struct sg_io_hdr {
#define SG_INFO_DIRECT_IO 0x2 /* direct IO requested and performed */
#define SG_INFO_MIXED_IO 0x4 /* not used, always 0 */
#define SG_INFO_DEVICE_DETACHING 0x8 /* completed successfully but ... */
-#define SG_INFO_ANOTHER_WAITING 0x10 /* needs SG_CTL_FLAGM_CHECK_FOR_MORE */
-#define SG_INFO_ABORTED 0x20 /* this command has been aborted */
+#define SG_INFO_ABORTED 0x10 /* this command has been aborted */
+#define SG_INFO_MRQ_FINI 0x20 /* marks multi-reqs that have finished */
/*
* Pointer to object of this structure filled by ioctl(SG_GET_SCSI_ID). Last
@@ -196,8 +198,7 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
/* rd> 1: master finished 0: not; wr> 1: finish share post master */
#define SG_CTL_FLAGM_MASTER_FINI 0x100 /* wr> 0: setup for repeat slave req */
#define SG_CTL_FLAGM_MASTER_ERR 0x200 /* rd: sharing, master got error */
-#define SG_CTL_FLAGM_CHECK_FOR_MORE 0x400 /* additional ready to read? */
-#define SG_CTL_FLAGM_ALL_BITS 0x7ff /* should be OR of previous items */
+#define SG_CTL_FLAGM_ALL_BITS 0x3ff /* should be OR of previous items */
/* Write one of the following values to sg_extended_info::read_value, get... */
#define SG_SEIRV_INT_MASK 0x0 /* get SG_SEIM_ALL_BITS */
@@ -207,6 +208,7 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
#define SG_SEIRV_DEV_FL_RQS 0x4 /* sum(fl rqs) on all of dev's fds */
#define SG_SEIRV_TRC_SZ 0x5 /* current size of trace buffer */
#define SG_SEIRV_TRC_MAX_SZ 0x6 /* maximum size of trace buffer */
+#define SG_SEIRV_SUBMITTED 0x7 /* number of mrqs submitted+unread */
/*
* A pointer to the following structure is passed as the third argument to