aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog3
-rwxr-xr-xconfigure20
-rw-r--r--configure.ac2
-rw-r--r--debian/changelog8
-rw-r--r--doc/sg3_utils.82
-rw-r--r--examples/Makefile.cplus5
-rw-r--r--examples/sg_tst_async.cpp815
-rw-r--r--sg3_utils.spec8
-rw-r--r--src/sg_copy_results.c22
-rw-r--r--src/sg_xcopy.c6
10 files changed, 861 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog
index 3eb1705f..0fb180c1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,9 @@ 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.40 [20140626] [svn: r590]
+ - sg_copy_results: correct response length calculations
+
Changelog for sg3_utils-1.39 [20140612] [svn: r588]
- sg_rep_zones: new utility for ZBC REPORT ZONES
- sg_reset_wp: new utility, ZBC RESET WRITE POINTER
diff --git a/configure b/configure
index 665ee702..ef27c433 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for sg3_utils 1.39.
+# Generated by GNU Autoconf 2.69 for sg3_utils 1.40.
#
# Report bugs to <dgilbert@interlog.com>.
#
@@ -590,8 +590,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sg3_utils'
PACKAGE_TARNAME='sg3_utils'
-PACKAGE_VERSION='1.39'
-PACKAGE_STRING='sg3_utils 1.39'
+PACKAGE_VERSION='1.40'
+PACKAGE_STRING='sg3_utils 1.40'
PACKAGE_BUGREPORT='dgilbert@interlog.com'
PACKAGE_URL=''
@@ -1330,7 +1330,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures sg3_utils 1.39 to adapt to many kinds of systems.
+\`configure' configures sg3_utils 1.40 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1400,7 +1400,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of sg3_utils 1.39:";;
+ short | recursive ) echo "Configuration of sg3_utils 1.40:";;
esac
cat <<\_ACEOF
@@ -1512,7 +1512,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-sg3_utils configure 1.39
+sg3_utils configure 1.40
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1790,7 +1790,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by sg3_utils $as_me 1.39, which was
+It was created by sg3_utils $as_me 1.40, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2654,7 +2654,7 @@ fi
# Define the identity of the package.
PACKAGE='sg3_utils'
- VERSION='1.39'
+ VERSION='1.40'
cat >>confdefs.h <<_ACEOF
@@ -12322,7 +12322,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by sg3_utils $as_me 1.39, which was
+This file was extended by sg3_utils $as_me 1.40, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12388,7 +12388,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-sg3_utils config.status 1.39
+sg3_utils config.status 1.40
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index a48a6b95..d0ee2517 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT(sg3_utils, 1.39, dgilbert@interlog.com)
+AC_INIT(sg3_utils, 1.40, dgilbert@interlog.com)
AM_INIT_AUTOMAKE
AM_MAINTAINER_MODE
diff --git a/debian/changelog b/debian/changelog
index 38b3b375..8a22c008 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,14 @@
+sg3-utils (1.40-0.1) unstable; urgency=low
+
+ * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com> Fri, 13 Jun 2014 12:00:00 -0400
+
sg3-utils (1.39-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Thu, 12 June 2014 09:00:00 -0400
+ -- Douglas Gilbert <dgilbert@interlog.com> Thu, 12 Jun 2014 09:00:00 -0400
sg3-utils (1.38-0.1) unstable; urgency=low
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index c4b6f25a..84a6676a 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -1,4 +1,4 @@
-.TH SG3_UTILS "8" "June 2014" "sg3_utils\-1.39" SG3_UTILS
+.TH SG3_UTILS "8" "June 2014" "sg3_utils\-1.40" SG3_UTILS
.SH NAME
sg3_utils \- a package of utilities for sending SCSI commands
.SH SYNOPSIS
diff --git a/examples/Makefile.cplus b/examples/Makefile.cplus
index 5abe9002..f4932de6 100644
--- a/examples/Makefile.cplus
+++ b/examples/Makefile.cplus
@@ -9,7 +9,7 @@ LD = g++
## CC = clang++
## LD = clang++
-EXECS = sg_tst_excl sg_tst_excl2 sg_tst_excl3 sg_tst_context
+EXECS = sg_tst_excl sg_tst_excl2 sg_tst_excl3 sg_tst_context sg_tst_async
EXTRAS =
@@ -55,6 +55,9 @@ sg_tst_excl3: sg_tst_excl3.o $(LIBFILESNEW)
sg_tst_context: sg_tst_context.o $(LIBFILESNEW)
$(LD) -o $@ $(LDFLAGS) $^
+sg_tst_async: sg_tst_async.o $(LIBFILESOLD)
+ $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS)
install -d $(INSTDIR)
for name in $^; \
diff --git a/examples/sg_tst_async.cpp b/examples/sg_tst_async.cpp
new file mode 100644
index 00000000..ee834c51
--- /dev/null
+++ b/examples/sg_tst_async.cpp
@@ -0,0 +1,815 @@
+/*
+ * Copyright (c) 2013 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <vector>
+#include <system_error>
+#include <thread>
+#include <mutex>
+#include <chrono>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+static const char * version_str = "1.00 20140614";
+static const char * util_name = "sg_tst_async";
+
+/* This is a test program for checking the async usage of the Linux sg
+ * driver.
+ * multiple threads and can be run as multiple processes and attempts
+ * 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. Prior to the first
+ * increment, the value is checked for even or 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.
+ *
+ * This is C++ code with some things from C++11 (e.g. threads) and was
+ * only just able to compile (when some things were reverted) with gcc/g++
+ * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support
+ * was not available until g++ version 4.8.1 and that is only currently
+ * found in Fedora 19 .
+ *
+ * The build uses various object files from the <sg3_utils>/lib directory
+ * which is assumed to be a sibling of this examples directory. Those
+ * object files in the lib directory can be built with:
+ * cd <sg3_utils> ; ./configure ; cd lib; make
+ * Then to build sg_tst_excl concatenate the next 3 lines:
+ * g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o
+ * ../lib/sg_lib_data.o ../lib/sg_io_linux.o -o sg_tst_excl
+ * sg_tst_excl.cpp
+ *
+ * Currently this utility is Linux only and assumes the SG_IO v3 interface
+ * which is supported by sg and block devices (but not bsg devices which
+ * require the SG_IO v4 interface). This restriction is relaxed in the
+ * sg_tst_excl2 variant of this utility.
+ *
+ * BEWARE: this utility modifies a logical block (default LBA 1000) on the
+ * given device.
+ *
+ */
+
+using namespace std;
+using namespace std::chrono;
+
+#define DEF_NUM_PER_THREAD 200
+#define DEF_NUM_THREADS 4
+#define DEF_WAIT_MS 0 /* 0: yield; -1: don't wait; -2: sleep(0) */
+
+
+#define DEF_LBA 1000
+
+#define EBUFF_SZ 256
+
+static mutex odd_count_mutex;
+static mutex console_mutex;
+static unsigned int odd_count;
+static unsigned int ebusy_count;
+static unsigned int eagain_count;
+
+
+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(" 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(" -l <lba> logical block to increment (def: %u)\n",
+ DEF_LBA);
+ printf(" -n <n_per_thr> number of loops per thread "
+ "(def: %d)\n", DEF_NUM_PER_THREAD);
+ printf(" -t <num_thrs> number of threads (def: %d)\n",
+ DEF_NUM_THREADS);
+ printf(" -V print version number then exit\n");
+ printf(" -w <wait_ms> >0: sleep_for(<wait_ms>); =0: "
+ "yield(); -1: no\n"
+ " wait; -2: sleep(0) (def: %d)\n",
+ DEF_WAIT_MS);
+ printf(" -x don't use O_EXCL on first thread "
+ "(def: use\n"
+ " O_EXCL on all threads)\n"
+ " -xx don't use O_EXCL on any thread\n\n");
+ printf("Test O_EXCL open flag with Linux sg driver. Each open/close "
+ "cycle with the\nO_EXCL flag does a double increment on "
+ "lba (using its first 4 bytes).\nEach increment uses a READ_16, "
+ "READ_16, increment, WRITE_16 cycle. The two\nREAD_16s are "
+ "launched asynchronously. Note that '-xx' will run test\n"
+ "without any O_EXCL flags.\n");
+}
+
+
+#define TUR_CMD_LEN 6
+#define READ16_REPLY_LEN 512
+#define READ16_CMD_LEN 16
+#define WRITE16_REPLY_LEN 512
+#define WRITE16_CMD_LEN 16
+
+static int
+start_sg3_cmd(int sg_fd, int tur0_rd1_wr2, int pack_id, unsigned int lba,
+ unsigned char * lbp, int xfer_bytes)
+{
+ struct sg_io_hdr pt;
+ unsigned char turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0};
+ 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];
+ const char * np;
+
+ memset(&pt, 0, sizeof(pt));
+ switch (tur0_rd1_wr2) {
+ case 0:
+ np = "TEST UNIT READY";
+ pt.cmdp = turCmdBlk;
+ pt.cmd_len = sizeof(turCmdBlk);
+ pt.dxfer_direction = SG_DXFER_NONE;
+ break;
+ case 1:
+ np = "READ(16)";
+ r16CmdBlk[6] = (lba >> 24) & 0xff;
+ r16CmdBlk[7] = (lba >> 16) & 0xff;
+ r16CmdBlk[8] = (lba >> 8) & 0xff;
+ r16CmdBlk[9] = lba & 0xff;
+ pt.cmdp = r16CmdBlk;
+ pt.cmd_len = sizeof(r16CmdBlk);
+ pt.dxfer_direction = SG_DXFER_FROM_DEV;
+ pt.dxferp = lbp;
+ pt.dxfer_len = xfer_bytes;
+ break;
+ case 2:
+ np = "WRITE(16)";
+ w16CmdBlk[6] = (lba >> 24) & 0xff;
+ w16CmdBlk[7] = (lba >> 16) & 0xff;
+ w16CmdBlk[8] = (lba >> 8) & 0xff;
+ w16CmdBlk[9] = lba & 0xff;
+ pt.cmdp = w16CmdBlk;
+ pt.cmd_len = sizeof(w16CmdBlk);
+ pt.dxfer_direction = SG_DXFER_TO_DEV;
+ pt.dxferp = lbp;
+ pt.dxfer_len = xfer_bytes;
+ break;
+ default:
+ console_mutex.lock();
+ cerr << __func__ << ": unknown tur0_rd1_wr2=" << tur0_rd1_wr2 << endl;
+ console_mutex.unlock();
+ return -99;
+ }
+ pt.interface_id = 'S';
+ pt.mx_sb_len = sizeof(sense_buffer);
+ pt.sbp = sense_buffer; /* ignored .... */
+ pt.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ pt.pack_id = pack_id;
+
+ // queue up two READ_16s to same LBA
+ if (write(sg_fd, &pt, sizeof(pt)) < 0) {
+ console_mutex.lock();
+ cerr << __func__ << ": " << np << " pack_id=" << pack_id;
+ perror(" write(sg)");
+ console_mutex.unlock();
+ close(sg_fd);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+finish_sg3_cmd(int sg_fd, int tur0_rd1_wr2, int & pack_id, unsigned int lba,
+ unsigned char * lbp, int xfer_bytes, int wait_ms, int & eagains)
+{
+ int ok, res;
+ struct sg_io_hdr pt;
+ unsigned char turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0};
+ 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];
+ const char * np;
+
+ memset(&pt, 0, sizeof(pt));
+ switch (tur0_rd1_wr2) {
+ case 0:
+ np = "TEST UNIT READY";
+ pt.cmdp = turCmdBlk;
+ pt.cmd_len = sizeof(turCmdBlk);
+ pt.dxfer_direction = SG_DXFER_NONE;
+ break;
+ case 1:
+ np = "READ(16)";
+ r16CmdBlk[6] = (lba >> 24) & 0xff;
+ r16CmdBlk[7] = (lba >> 16) & 0xff;
+ r16CmdBlk[8] = (lba >> 8) & 0xff;
+ r16CmdBlk[9] = lba & 0xff;
+ pt.cmdp = r16CmdBlk;
+ pt.cmd_len = sizeof(r16CmdBlk);
+ pt.dxfer_direction = SG_DXFER_FROM_DEV;
+ pt.dxferp = lbp;
+ pt.dxfer_len = xfer_bytes;
+ break;
+ case 2:
+ np = "WRITE(16)";
+ w16CmdBlk[6] = (lba >> 24) & 0xff;
+ w16CmdBlk[7] = (lba >> 16) & 0xff;
+ w16CmdBlk[8] = (lba >> 8) & 0xff;
+ w16CmdBlk[9] = lba & 0xff;
+ pt.cmdp = w16CmdBlk;
+ pt.cmd_len = sizeof(w16CmdBlk);
+ pt.dxfer_direction = SG_DXFER_TO_DEV;
+ pt.dxferp = lbp;
+ pt.dxfer_len = xfer_bytes;
+ break;
+ default:
+ console_mutex.lock();
+ cerr << __func__ << ": unknown tur0_rd1_wr2=" << tur0_rd1_wr2 << endl;
+ console_mutex.unlock();
+ return -99;
+ }
+ pt.interface_id = 'S';
+ pt.mx_sb_len = sizeof(sense_buffer);
+ pt.sbp = sense_buffer;
+ pt.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ pt.pack_id = pack_id;
+
+ while (((res = read(sg_fd, &pt, sizeof(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) {
+ console_mutex.lock();
+ perror("do_rd_inc_wr_twice: read(sg, READ_16)");
+ console_mutex.unlock();
+ close(sg_fd);
+ return -1;
+ }
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category3(&pt)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ console_mutex.lock();
+ fprintf(stderr, "%s: Recovered error on %s, continuing\n",
+ __func__, np);
+ console_mutex.unlock();
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ console_mutex.lock();
+ sg_chk_n_print3(np, &pt, 1);
+ console_mutex.unlock();
+ break;
+ }
+ return ok ? 0 : -1;
+}
+
+/* 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(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_hdr 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;
+
+ 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;
+ 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,
+ "do_rd_inc_wr_twice: error opening file: %s", dev_name);
+ perror(ebuff);
+ return -1;
+ }
+
+ for (k = 0; k < 2; ++k) {
+ /* Prepare READ_16 command */
+ memset(&pt, 0, sizeof(pt));
+ pt.interface_id = 'S';
+ pt.cmd_len = sizeof(r16CmdBlk);
+ pt.mx_sb_len = sizeof(sense_buffer);
+ pt.dxfer_direction = SG_DXFER_FROM_DEV;
+ pt.dxfer_len = READ16_REPLY_LEN;
+ pt.dxferp = lb;
+ pt.cmdp = r16CmdBlk;
+ pt.sbp = sense_buffer;
+ pt.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ pt.pack_id = id;
+
+ // queue up two READ_16s to same LBA
+ if (write(sg_fd, &pt, sizeof(pt)) < 0) {
+ console_mutex.lock();
+ perror("do_rd_inc_wr_twice: write(sg, READ_16)");
+ console_mutex.unlock();
+ close(sg_fd);
+ return -1;
+ }
+ pt2 = pt;
+ if (write(sg_fd, &pt2, sizeof(pt2)) < 0) {
+ console_mutex.lock();
+ perror("do_rd_inc_wr_twice: write(sg, READ_16) 2");
+ console_mutex.unlock();
+ close(sg_fd);
+ return -1;
+ }
+
+ while (((res = read(sg_fd, &pt, sizeof(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) {
+ console_mutex.lock();
+ perror("do_rd_inc_wr_twice: read(sg, READ_16)");
+ console_mutex.unlock();
+ close(sg_fd);
+ return -1;
+ }
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category3(&pt)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ console_mutex.lock();
+ fprintf(stderr, "Recovered error on READ_16, continuing\n");
+ console_mutex.unlock();
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ console_mutex.lock();
+ sg_chk_n_print3("READ_16 command error", &pt, 1);
+ console_mutex.unlock();
+ break;
+ }
+ if (ok) {
+ while (((res = read(sg_fd, &pt2, sizeof(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) {
+ console_mutex.lock();
+ perror("do_rd_inc_wr_twice: read(sg, READ_16) 2");
+ console_mutex.unlock();
+ close(sg_fd);
+ return -1;
+ }
+ pt = pt2;
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category3(&pt)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ console_mutex.lock();
+ fprintf(stderr, "Recovered error on READ_16, continuing 2\n");
+ console_mutex.unlock();
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ console_mutex.lock();
+ sg_chk_n_print3("READ_16 command error 2", &pt, 1);
+ console_mutex.unlock();
+ break;
+ }
+ }
+ if (! ok) {
+ close(sg_fd);
+ return -1;
+ }
+
+ u = (lb[0] << 24) + (lb[1] << 16) + (lb[2] << 8) + lb[3];
+ 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;
+
+ 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.interface_id = 'S';
+ pt.cmd_len = sizeof(w16CmdBlk);
+ pt.mx_sb_len = sizeof(sense_buffer);
+ pt.dxfer_direction = SG_DXFER_TO_DEV;
+ pt.dxfer_len = WRITE16_REPLY_LEN;
+ pt.dxferp = lb;
+ pt.cmdp = w16CmdBlk;
+ pt.sbp = sense_buffer;
+ pt.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ pt.pack_id = id;
+
+ if (ioctl(sg_fd, SG_IO, &pt) < 0) {
+ console_mutex.lock();
+ perror("do_rd_inc_wr_twice: WRITE_16 SG_IO ioctl error");
+ console_mutex.unlock();
+ close(sg_fd);
+ return -1;
+ }
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category3(&pt)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ console_mutex.lock();
+ fprintf(stderr, "Recovered error on WRITE_16, continuing\n");
+ console_mutex.unlock();
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ console_mutex.lock();
+ sg_chk_n_print3("WRITE_16 command error", &pt, 1);
+ console_mutex.unlock();
+ break;
+ }
+ if (! ok) {
+ close(sg_fd);
+ return -1;
+ }
+ }
+ close(sg_fd);
+ return odd;
+}
+
+
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+
+/* Send INQUIRY and fetches response. If okay puts PRODUCT ID field
+ * in b (up to m_blen bytes). Does not use O_EXCL flag. Returns 0 on success,
+ * else -1 . */
+static int
+do_inquiry_prod_id(const char * dev_name, int block, int wait_ms,
+ unsigned int & ebusys, char * b, int b_mlen)
+{
+ int sg_fd, ok, ret;
+ struct sg_io_hdr pt;
+ unsigned char inqCmdBlk [INQ_CMD_LEN] =
+ {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+ unsigned char inqBuff[INQ_REPLY_LEN];
+ unsigned char sense_buffer[64];
+ char ebuff[EBUFF_SZ];
+ int open_flags = O_RDWR; /* O_EXCL | O_RDONLY fails with EPERM */
+
+ if (! block)
+ open_flags |= O_NONBLOCK;
+ while (((sg_fd = open(dev_name, open_flags)) < 0) &&
+ (EBUSY == errno)) {
+ ++ebusys;
+ 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,
+ "do_inquiry_prod_id: error opening file: %s", dev_name);
+ perror(ebuff);
+ return -1;
+ }
+ /* Prepare INQUIRY command */
+ memset(&pt, 0, sizeof(pt));
+ pt.interface_id = 'S';
+ pt.cmd_len = sizeof(inqCmdBlk);
+ /* pt.iovec_count = 0; */ /* memset takes care of this */
+ pt.mx_sb_len = sizeof(sense_buffer);
+ pt.dxfer_direction = SG_DXFER_FROM_DEV;
+ pt.dxfer_len = INQ_REPLY_LEN;
+ pt.dxferp = inqBuff;
+ pt.cmdp = inqCmdBlk;
+ pt.sbp = sense_buffer;
+ pt.timeout = 20000; /* 20000 millisecs == 20 seconds */
+ /* pt.flags = 0; */ /* take defaults: indirect IO, etc */
+ /* pt.pack_id = 0; */
+ /* pt.usr_ptr = NULL; */
+
+ if (ioctl(sg_fd, SG_IO, &pt) < 0) {
+ perror("do_inquiry_prod_id: Inquiry SG_IO ioctl error");
+ close(sg_fd);
+ return -1;
+ }
+
+ /* now for the error processing */
+ ok = 0;
+ switch (sg_err_category3(&pt)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = 1;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ fprintf(stderr, "Recovered error on INQUIRY, continuing\n");
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ sg_chk_n_print3("INQUIRY command error", &pt, 1);
+ break;
+ }
+ if (ok) {
+ /* Good, so fetch Product ID from response, copy to 'b' */
+ if (b_mlen > 0) {
+ if (b_mlen > 16) {
+ memcpy(b, inqBuff + 16, 16);
+ b[16] = '\0';
+ } else {
+ memcpy(b, inqBuff + 16, b_mlen - 1);
+ b[b_mlen - 1] = '\0';
+ }
+ }
+ ret = 0;
+ } else
+ ret = -1;
+ close(sg_fd);
+ return ret;
+}
+
+static void
+work_thread(const char * dev_name, unsigned int lba, int id, int block,
+ int excl, int num, int wait_ms)
+{
+ unsigned int thr_odd_count = 0;
+ unsigned int thr_ebusy_count = 0;
+ unsigned int thr_eagain_count = 0;
+ int k, res;
+
+ console_mutex.lock();
+ cerr << "Enter work_thread id=" << id << " excl=" << excl << " block="
+ << block << endl;
+ console_mutex.unlock();
+ 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 (res < 0)
+ break;
+ if (res)
+ ++thr_odd_count;
+ }
+ console_mutex.lock();
+ if (k < num)
+ cerr << "thread id=" << id << " FAILed at iteration: " << k << '\n';
+ else
+ cerr << "thread id=" << id << " normal exit" << '\n';
+ console_mutex.unlock();
+
+ odd_count_mutex.lock();
+ odd_count += thr_odd_count;
+ ebusy_count += thr_ebusy_count;
+ eagain_count += thr_eagain_count;
+ odd_count_mutex.unlock();
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ int k, res;
+ int block = 0;
+ int force = 0;
+ unsigned int lba = DEF_LBA;
+ int num_per_thread = DEF_NUM_PER_THREAD;
+ int num_threads = DEF_NUM_THREADS;
+ int wait_ms = DEF_WAIT_MS;
+ int no_o_excl = 0;
+ char * dev_name = NULL;
+ char b[64];
+
+ for (k = 1; k < argc; ++k) {
+ if (0 == memcmp("-b", argv[k], 2))
+ ++block;
+ else if (0 == memcmp("-f", argv[k], 2))
+ ++force;
+ else if (0 == memcmp("-h", argv[k], 2)) {
+ usage();
+ return 0;
+ } else if (0 == memcmp("-l", argv[k], 2)) {
+ ++k;
+ if ((k < argc) && isdigit(*argv[k]))
+ lba = (unsigned int)atoi(argv[k]);
+ else
+ break;
+ } else if (0 == memcmp("-n", argv[k], 2)) {
+ ++k;
+ if ((k < argc) && isdigit(*argv[k]))
+ num_per_thread = atoi(argv[k]);
+ else
+ break;
+ } else if (0 == memcmp("-t", argv[k], 2)) {
+ ++k;
+ if ((k < argc) && isdigit(*argv[k]))
+ num_threads = atoi(argv[k]);
+ else
+ break;
+ } else if (0 == memcmp("-V", argv[k], 2)) {
+ printf("%s version: %s\n", util_name, version_str);
+ return 0;
+ } else if (0 == memcmp("-w", argv[k], 2)) {
+ ++k;
+ if ((k < argc) && (isdigit(*argv[k]) || ('-' == *argv[k]))) {
+ if ('-' == *argv[k])
+ wait_ms = - atoi(argv[k] + 1);
+ else
+ wait_ms = atoi(argv[k]);
+ } else
+ break;
+ } else if (0 == memcmp("-xxx", argv[k], 4))
+ no_o_excl += 3;
+ else if (0 == memcmp("-xx", argv[k], 3))
+ no_o_excl += 2;
+ else if (0 == memcmp("-x", argv[k], 2))
+ ++no_o_excl;
+ else if (*argv[k] == '-') {
+ printf("Unrecognized switch: %s\n", argv[k]);
+ dev_name = NULL;
+ break;
+ }
+ else if (! dev_name)
+ dev_name = argv[k];
+ else {
+ printf("too many arguments\n");
+ dev_name = 0;
+ break;
+ }
+ }
+ if (0 == dev_name) {
+ usage();
+ return 1;
+ }
+ try {
+ struct stat a_stat;
+
+ if (stat(dev_name, &a_stat) < 0) {
+ perror("stat() on dev_name failed");
+ return 1;
+ }
+ if (! S_ISCHR(a_stat.st_mode)) {
+ fprintf(stderr, "%s should be a sg device which is a char "
+ "device. %s\n", dev_name, dev_name);
+ fprintf(stderr, "is not a char device and damage could be done "
+ "if it is a BLOCK\ndevice, exiting ...\n");
+ return 1;
+ }
+ if (! force) {
+ res = do_inquiry_prod_id(dev_name, block, wait_ms, ebusy_count,
+ b, sizeof(b));
+ if (res) {
+ fprintf(stderr, "INQUIRY failed on %s\n", dev_name);
+ return 1;
+ }
+ // For safety, since <lba> written to, only permit scsi_debug
+ // devices. Bypass this with '-f' option.
+ if (0 != memcmp("scsi_debug", b, 10)) {
+ fprintf(stderr, "Since this utility writes to LBA %d, only "
+ "devices with scsi_debug\nproduct ID accepted.\n",
+ lba);
+ return 2;
+ }
+ }
+
+ vector<thread *> vt;
+
+ for (k = 0; k < num_threads; ++k) {
+ int excl = 1;
+
+ if (no_o_excl > 1)
+ excl = 0;
+ else if ((0 == k) && (1 == no_o_excl))
+ excl = 0;
+
+ thread * tp = new thread {work_thread, dev_name, lba, k, block,
+ excl, num_per_thread, wait_ms};
+ vt.push_back(tp);
+ }
+
+ // g++ 4.7.3 didn't like range-for loop here
+ for (k = 0; k < (int)vt.size(); ++k)
+ vt[k]->join();
+
+ for (k = 0; k < (int)vt.size(); ++k)
+ delete vt[k];
+
+ if (no_o_excl)
+ cout << "Odd count: " << odd_count << endl;
+ else
+ cout << "Expecting odd count of 0, got " << odd_count << endl;
+ cout << "Number of EBUSYs: " << ebusy_count << endl;
+ cout << "Number of EAGAINs: " << eagain_count << endl;
+
+ }
+ catch(system_error& e) {
+ cerr << "got a system_error exception: " << e.what() << '\n';
+ auto ec = e.code();
+ cerr << "category: " << ec.category().name() << '\n';
+ cerr << "value: " << ec.value() << '\n';
+ cerr << "message: " << ec.message() << '\n';
+ cerr << "\nNote: if g++ may need '-pthread' or similar in "
+ "compile/link line" << '\n';
+ }
+ catch(...) {
+ cerr << "got another exception: " << '\n';
+ }
+ return 0;
+}
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 0e83c2cd..75852462 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -1,6 +1,6 @@
Summary: Utilities for devices that use SCSI command sets
Name: sg3_utils
-Version: 1.39
+Version: 1.40
# Release: 1%{?dist}
Release: 1
License: GPL
@@ -20,7 +20,7 @@ mode and log pages (sginfo, sg_modes and sg_logs); spin up and down
disks (sg_start); do self tests (sg_senddiag); and various other functions.
See the README, ChangeLog and COVERAGE files. Requires the linux kernel 2.4
series or later. In the 2.4 series SCSI generic device names (e.g. /dev/sg0)
-must be used. In the 2.6 series other device names may be used as
+must be used. In the 2.6 series and later other device names may be used as
well (e.g. /dev/sda).
Warning: Some of these tools access the internals of your system
@@ -79,6 +79,10 @@ fi
%{_libdir}/*.la
%changelog
+* Fri Jun 13 2014 - dgilbert at interlog dot com
+- track t10 changes
+ * sg3_utils-1.40
+
* Thu Jun 12 2014 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.39
diff --git a/src/sg_copy_results.c b/src/sg_copy_results.c
index 127d10f6..e27d42e4 100644
--- a/src/sg_copy_results.c
+++ b/src/sg_copy_results.c
@@ -35,7 +35,7 @@
and the optional list identifier passed as the list_id argument.
*/
-static const char * version_str = "1.9 20140515";
+static const char * version_str = "1.10 20140625";
#define MAX_XFER_LEN 10000
@@ -133,9 +133,9 @@ scsi_failed_segment_details(unsigned char *rcBuff, unsigned int rcBuffLen)
}
len = (rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) |
rcBuff[3];
- if (len + 3 > rcBuffLen) {
- pr2serr(" <<report too long for internal buffer, output "
- "truncated\n");
+ if (len + 4 > rcBuffLen) {
+ pr2serr(" <<report len %d > %d too long for internal buffer, output "
+ "truncated\n", len, rcBuffLen);
}
if (len < 52) {
pr2serr(" <<no segment details, response data length %d\n", len);
@@ -154,14 +154,14 @@ scsi_copy_status(unsigned char *rcBuff, unsigned int rcBuffLen)
unsigned int len;
if (rcBuffLen < 4) {
- pr2serr(" <<not enough data to procedd report>>\n");
+ pr2serr(" <<not enough data to proceed report>>\n");
return;
}
len = (rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) |
rcBuff[3];
- if (len > rcBuffLen) {
- pr2serr(" <<report too long for internal buffer, output "
- "truncated\n");
+ if (len + 4 > rcBuffLen) {
+ pr2serr(" <<report len %d > %d too long for internal buffer, output "
+ "truncated\n", len, rcBuffLen);
}
printf("Receive copy results (copy status):\n");
printf(" Held data discarded: %s\n", rcBuff[4] & 0x80 ? "Yes":"No");
@@ -193,9 +193,9 @@ scsi_operating_parameters(unsigned char *rcBuff, unsigned int rcBuffLen)
len = (rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) |
rcBuff[3];
- if (len > rcBuffLen) {
- pr2serr(" <<report too long for internal buffer, output "
- "truncated\n");
+ if (len + 4 > rcBuffLen) {
+ pr2serr(" <<report len %d > %d too long for internal buffer, output "
+ "truncated\n", len, rcBuffLen);
}
printf("Receive copy results (report operating parameters):\n");
printf(" Supports no list identifier (SNLID): %s\n",
diff --git a/src/sg_xcopy.c b/src/sg_xcopy.c
index 5b1f4cdb..ef239595 100644
--- a/src/sg_xcopy.c
+++ b/src/sg_xcopy.c
@@ -62,7 +62,7 @@
#include "sg_cmds_extra.h"
#include "sg_io_linux.h"
-static const char * version_str = "0.45 20140516";
+static const char * version_str = "0.46 20140625";
#define ME "sg_xcopy: "
@@ -749,8 +749,8 @@ scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
len = ((rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) |
rcBuff[3]) + 4;
if (len > rcBuffLen) {
- pr2serr(" <<report too long for internal buffer, output "
- "truncated\n");
+ pr2serr(" <<report len %d > %d too long for internal buffer, output "
+ "truncated\n", len, rcBuffLen);
}
if (verbose > 2) {
pr2serr("\nOutput response in hex:\n");