aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BSD_LICENSE2
-rw-r--r--ChangeLog4
-rw-r--r--README13
-rw-r--r--debian/changelog2
-rw-r--r--examples/Makefile.cplus72
-rw-r--r--examples/sg_excl.c12
-rw-r--r--examples/sg_tst_excl.cpp511
-rw-r--r--examples/sg_tst_excl2.cpp509
-rw-r--r--include/sg_cmds_extra.h12
-rw-r--r--lib/sg_cmds_basic.c2
-rw-r--r--lib/sg_cmds_extra.c111
-rw-r--r--sg3_utils.spec2
12 files changed, 1236 insertions, 16 deletions
diff --git a/BSD_LICENSE b/BSD_LICENSE
index 8997a2ef..70d5c6df 100644
--- a/BSD_LICENSE
+++ b/BSD_LICENSE
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999-2011 Douglas Gilbert.
+ * Copyright (c) 1999-2013 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/ChangeLog b/ChangeLog
index 0572fec7..4307c119 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@ Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
directory have their own "man" pages. There is also a sg3_utils man page.
-Changelog for sg3_utils-1.37 [20130730] [svn: r503]
+Changelog for sg3_utils-1.37 [20130805] [svn: r504]
- sg_compare_and_write: fix wrprotect setting
- sg_inq: fix referrals VPD page
- dev_id VPD: T10 vendor id designator clean up
@@ -12,10 +12,12 @@ Changelog for sg3_utils-1.37 [20130730] [svn: r503]
- sg_vpd: dev_id VPD: T10 vendor id designator clean up
- sg_libs: extended copy opcode renamed (spc4r34)
- sg_ll_receive_copy_results(): expand for all sa_s
+ - add sg_ll_3party_copy_out()
- add dStrHexErr(): ascii hex to stderr
- add dStrHexStr(): ascii hex to string
- scripts/rescan-scsi-bus.sh KR's v1.57 + HR patch
- Makefile.am cleanup
+ - examples: add sg_tst_excl and sg_tst_excl2
Changelog for sg3_utils-1.36 [20130531] [svn: r497]
- sg_vpd: Protocol-specific port information VPD page
diff --git a/README b/README
index bb3ffd24..38275f8f 100644
--- a/README
+++ b/README
@@ -261,7 +261,8 @@ subdirectory:
- bsg_queue_tst, sg_excl, scsi_inquiry, sg_iovec_tst, sg_queue_tst,
sg_sat_chk_power, sg__sat_identify, sg__sat_phy_event,
sg__sat_set_features, sg_sat_smart_rd_data, sg_simple1, sg_simple2,
- sg_simple3, sg_simple4, sg_simple5 and sg_simple16
+ sg_simple3, sg_simple4, sg_simple5, sg_simple16, sg_tst_excl and
+ sg_tst_excl2
Also in that subdirectory is a script to test sg_persist, an example data
file for sg_persist (called "transport_ids.txt") and an example data file for
@@ -326,6 +327,14 @@ descriptor (as defined in SAT).
through the SAT-defined ATA PASS_THROUGH (16) SCSI command. If
successful, the 256 word (512 byte) response is output.
+"sg_tst_excl" and "sg_tst_excl2" use multiple threads to bombard the
+given device with O_EXCL open flags, so only one should succeed at a
+time. While holding O_EXCL control a thread attempts a double increment
+on an integer in the given LBA. If the integer starts even (after the
+first read) then it should remain even if the O_EXCL flag is doing its job.
+The "sg_tst_excl" variant uses the Linux SG_IO v3 interface while the
+"sg_tst_excl2" uses the more generic sg_pt infrastructure.
+
Command line processing
=======================
@@ -382,4 +391,4 @@ See http://sg.danny.cz/sg/tools.html
Douglas Gilbert
-30th July 2013
+2nd August 2013
diff --git a/debian/changelog b/debian/changelog
index dba07c00..9deeaa2e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.37-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Tue, 30 Jul 2013 20:00:00 -0400
+ -- Douglas Gilbert <dgilbert@interlog.com> Fri, 02 Aug 2013 17:00:00 -0400
sg3-utils (1.36-0.1) unstable; urgency=low
diff --git a/examples/Makefile.cplus b/examples/Makefile.cplus
new file mode 100644
index 00000000..21acff0e
--- /dev/null
+++ b/examples/Makefile.cplus
@@ -0,0 +1,72 @@
+SHELL = /bin/sh
+
+PREFIX=/usr/local
+INSTDIR=$(DESTDIR)/$(PREFIX)/bin
+MANDIR=$(DESTDIR)/$(PREFIX)/man
+
+CC = g++
+LD = g++
+
+EXECS = sg_tst_excl sg_tst_excl2
+
+EXTRAS =
+
+BSG_EXTRAS =
+
+
+MAN_PGS =
+MAN_PREF = man8
+
+LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+
+CPPFLAGS = -std=c++11 -pthread -g -O2 -W -Wall -I../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+## CFLAGS = -g -O2 -W -Wall -I../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+# CFLAGS = -g -O2 -Wall -I../include -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS)
+# CFLAGS = -g -O2 -Wall -pedantic -I../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+
+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
+
+all: $(EXECS)
+
+extras: $(EXTRAS)
+
+
+depend dep:
+ for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+ done > .depend
+
+clean:
+ /bin/rm -f *.o $(EXECS) $(EXTRAS) $(BSG_EXTRAS) core .depend
+
+sg_tst_excl: sg_tst_excl.o $(LIBFILESOLD)
+ $(LD) -o $@ $(LDFLAGS) $^
+
+sg_tst_excl2: sg_tst_excl2.o $(LIBFILESNEW)
+ $(LD) -o $@ $(LDFLAGS) $^
+
+install: $(EXECS)
+ install -d $(INSTDIR)
+ for name in $^; \
+ do install -s -o root -g root -m 755 $$name $(INSTDIR); \
+ done
+ install -d $(MANDIR)/$(MAN_PREF)
+ for mp in $(MAN_PGS); \
+ do install -o root -g root -m 644 $$mp $(MANDIR)/$(MAN_PREF); \
+ gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \
+ done
+
+uninstall:
+ dists="$(EXECS)"; \
+ for name in $$dists; do \
+ rm -f $(INSTDIR)/$$name; \
+ done
+ for mp in $(MAN_PGS); do \
+ rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \
+ done
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/examples/sg_excl.c b/examples/sg_excl.c
index 743b9fa1..6f9aa3ad 100644
--- a/examples/sg_excl.c
+++ b/examples/sg_excl.c
@@ -14,15 +14,15 @@
executing a SCSI INQUIRY command and a
TEST UNIT READY command using the SCSI generic (sg) driver
-* Copyright (C) 2003 D. Gilbert
+* Copyright (C) 2003-2013 D. Gilbert
* 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)
* any later version.
- Invocation: sg_simple1 [-x] <sg_device>
+ Invocation: sg_excl [-x] <sg_device>
- Version 03.57 (20020226)
+ Version 3.58 (20130731)
6 byte INQUIRY command:
[0x12][ |lu][pg cde][res ][al len][cntrl ]
@@ -71,7 +71,7 @@ int main(int argc, char * argv[])
}
}
if (0 == file_name) {
- printf("Usage: 'sg_simple1 [-x] <sg_device>'\n");
+ printf("Usage: 'sg_excl [-x] <sg_device>'\n");
return 1;
}
@@ -189,8 +189,8 @@ int main(int argc, char * argv[])
if (do_extra)
printf("TEST UNIT READY duration=%u millisecs, resid=%d, "
- "msg_status=%d\n", io_hdr.duration, io_hdr.resid,
- (int)io_hdr.msg_status);
+ "msg_status=%d\n", io_hdr.duration, io_hdr.resid,
+ (int)io_hdr.msg_status);
sleep(60);
close(sg_fd);
diff --git a/examples/sg_tst_excl.cpp b/examples/sg_tst_excl.cpp
new file mode 100644
index 00000000..77bdfe3f
--- /dev/null
+++ b/examples/sg_tst_excl.cpp
@@ -0,0 +1,511 @@
+/*
+ * 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.03 20130803";
+static const char * util_name = "sg_tst_excl";
+
+/* This is a test program for checking O_EXCL on open() works. It uses
+ * 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 bounce_count;
+
+
+static void
+usage(void)
+{
+ printf("Usage: %s [-l <lba>] [-n <n_per_thr>] [-t <num_thrs>]\n"
+ " [-V] [-w <wait_ms>] [-x] "
+ "<disk_device>\n", util_name);
+ printf(" where\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\n");
+ printf("Test O_EXCL open flag with sg driver. Each open/close "
+ "cycle with the\nO_EXCL flag does a double increment on "
+ "lba (using its first 4 bytes).\n");
+}
+
+
+#define READ16_REPLY_LEN 512
+#define READ16_CMD_LEN 16
+#define WRITE16_REPLY_LEN 512
+#define WRITE16_CMD_LEN 16
+
+/* Opens dev_name O_EXCL | O_NONBLOCK and spins if busy, sleeping for
+ * wait_ms milliseconds if wait_ms is non-negative (else tight spin).
+ * Reads lba 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 excl,
+ int wait_ms, unsigned int & bounces)
+{
+ int k, sg_fd, ok;
+ int odd = 0;
+ unsigned int u = 0;
+ struct sg_io_hdr pt;
+ 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 | O_NONBLOCK;
+
+ 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 (excl)
+ open_flags |= O_EXCL;
+
+ while (((sg_fd = open(dev_name, open_flags)) < 0) &&
+ (EBUSY == errno)) {
+ ++bounces;
+ 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 */
+
+ if (ioctl(sg_fd, SG_IO, &pt) < 0) {
+ perror("do_rd_inc_wr_twice: READ_16 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:
+ printf("Recovered error on READ_16, continuing\n");
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ sg_chk_n_print3("READ_16 command error", &pt, 1);
+ 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 */
+
+ if (ioctl(sg_fd, SG_IO, &pt) < 0) {
+ perror("do_rd_inc_wr_twice: WRITE_16 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:
+ printf("Recovered error on WRITE_16, continuing\n");
+ ok = 1;
+ break;
+ default: /* won't bother decoding other categories */
+ sg_chk_n_print3("WRITE_16 command error", &pt, 1);
+ 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). Returns 0 on success, else -1 . */
+static int
+do_inquiry_prod_id(const char * dev_name, int wait_ms,
+ unsigned int & bounces, 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];
+
+ /* O_RDONLY | O_EXCL leads to EPERM (undocumented, historic) */
+ while (((sg_fd = open(dev_name, O_RDWR | O_EXCL | O_NONBLOCK)) < 0) &&
+ (EBUSY == errno)) {
+ ++bounces;
+ 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:
+ printf("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 excl,
+ int num, int wait_ms)
+{
+ unsigned int thr_odd_count = 0;
+ unsigned int thr_bounce_count = 0;
+ int k, res;
+
+ console_mutex.lock();
+ cerr << "Enter work_thread id=" << id << " excl=" << excl << endl;
+ console_mutex.unlock();
+ for (k = 0; k < num; ++k) {
+ res = do_rd_inc_wr_twice(dev_name, lba, excl, wait_ms,
+ thr_bounce_count);
+ if (res < 0)
+ break;
+ if (res)
+ ++thr_odd_count;
+ }
+ if (k < num) {
+ console_mutex.lock();
+ cerr << "thread id=" << id << " failed k=" << k << '\n';
+ console_mutex.unlock();
+ }
+
+ odd_count_mutex.lock();
+ odd_count += thr_odd_count;
+ bounce_count += thr_bounce_count;
+ odd_count_mutex.unlock();
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ int k, res;
+ 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 exclude_excl1 = 0;
+ char * dev_name = NULL;
+ char b[64];
+
+ for (k = 1; k < argc; ++k) {
+ 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("-x", argv[k], 2))
+ ++exclude_excl1;
+ 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 {
+
+ res = do_inquiry_prod_id(dev_name, wait_ms, bounce_count, b,
+ sizeof(b));
+ if (res) {
+ fprintf(stderr, "INQUIRY failed on %s\n", dev_name);
+ return 1;
+ }
+ // Comment out next block to use with non scsi_debug devices
+ // If so, be careful, as it writes to a LBA <lba>
+ 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 = ((0 == k) && exclude_excl1) ? 0 : 1;
+
+ thread * tp = new thread {work_thread, dev_name, lba, k, 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];
+
+ cout << "Expecting odd count of 0, got " << odd_count << endl;
+ cout << "Number of EBUSYs: bounce_count=" << bounce_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/examples/sg_tst_excl2.cpp b/examples/sg_tst_excl2.cpp
new file mode 100644
index 00000000..10677bc8
--- /dev/null
+++ b/examples/sg_tst_excl2.cpp
@@ -0,0 +1,509 @@
+/*
+ * 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_pt.h"
+
+static const char * version_str = "1.03 20130803";
+static const char * util_name = "sg_tst_excl2";
+
+/* This is a test program for checking O_EXCL on open() works. It uses
+ * 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_excl2 concatenate the next 3 lines:
+ * g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o
+ * ../lib/sg_lib_data.o ../lib/sg_pt_linux.o -o sg_tst_excl2
+ * sg_tst_excl2.cpp
+ * Alternatively use 'make -f Makefile.cplus sg_tst_excl2'
+ *
+ * BEWARE: this utility modifies a logical block (default LBA 1000) on the
+ * given device.
+ *
+ * Test breaks sg driver in lk 3.10.4 but works with proposed fix so should
+ * work soon thereafter. Works on standard block driver (e.g. /dev/sdc) in
+ * lk 3.10.4 . Fails on bsg driver in lk 3.10.4 because it ignores the
+ * O_EXCL flag (and that is unlikely to change).
+ *
+ */
+
+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 bounce_count;
+
+
+static void
+usage(void)
+{
+ printf("Usage: %s [-l <lba>] [-n <n_per_thr>] [-t <num_thrs>]\n"
+ " [-V] [-w <wait_ms>] [-x] "
+ "<disk_device>\n", util_name);
+ printf(" where\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\n");
+ printf("Test O_EXCL open flag with sg driver. Each open/close "
+ "cycle with the\nO_EXCL flag does a double increment on "
+ "lba (using its first 4 bytes).\n");
+}
+
+static int
+pt_err(int res, int sg_fd, struct sg_pt_base * ptp)
+{
+ if (res < 0)
+ fprintf(stderr, " pass through os error: %s\n",
+ safe_strerror(-res));
+ else if (SCSI_PT_DO_BAD_PARAMS == res)
+ fprintf(stderr, " bad pass through setup\n");
+ else if (SCSI_PT_DO_TIMEOUT == res)
+ fprintf(stderr, " pass through timeout\n");
+ else
+ fprintf(stderr, " do_scsi_pt error=%d\n", res);
+ if (ptp)
+ destruct_scsi_pt_obj(ptp);
+ scsi_pt_close_device(sg_fd);
+ return -1;
+}
+
+static int
+pt_cat_no_good(int cat, int sg_fd, struct sg_pt_base * ptp,
+ const unsigned char * sbp)
+{
+ int slen;
+ char b[256];
+ const int bl = (int)sizeof(b);
+
+ switch (cat) {
+ case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
+ sg_get_scsi_status_str(get_scsi_pt_status_response(ptp), bl, b);
+ fprintf(stderr, " scsi status: %s\n", b);
+ break;
+ case SCSI_PT_RESULT_SENSE:
+ slen = get_scsi_pt_sense_len(ptp);
+ sg_get_sense_str("", sbp, slen, 1, bl, b);
+ fprintf(stderr, "%s", b);
+ break;
+ case SCSI_PT_RESULT_TRANSPORT_ERR:
+ get_scsi_pt_transport_err_str(ptp, bl, b);
+ fprintf(stderr, " transport: %s", b);
+ break;
+ case SCSI_PT_RESULT_OS_ERR:
+ get_scsi_pt_os_err_str(ptp, bl, b);
+ fprintf(stderr, " os: %s", b);
+ break;
+ default:
+ fprintf(stderr, " unknown pt result category (%d)\n", cat);
+ break;
+ }
+ if (ptp)
+ destruct_scsi_pt_obj(ptp);
+ scsi_pt_close_device(sg_fd);
+ return -1;
+}
+
+#define READ16_REPLY_LEN 512
+#define READ16_CMD_LEN 16
+#define WRITE16_REPLY_LEN 512
+#define WRITE16_CMD_LEN 16
+
+/* Opens dev_name either O_EXCL | O_NONBLOCK, if excl is 1; or O_NONBLOCK
+ * if excl is 0. Spins if busy, sleeping for wait_ms milliseconds if wait_ms
+ * is non-negative (else tight spin). Reads lba 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 is even otherwise returns 1. */
+static int
+do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int excl,
+ int wait_ms, unsigned int & bounces)
+{
+ int k, sg_fd, res, cat;
+ int odd = 0;
+ unsigned int u = 0;
+ struct sg_pt_base * ptp = NULL;
+ 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 | O_NONBLOCK;
+
+ 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 (excl)
+ open_flags |= O_EXCL;
+
+ while (((sg_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
+ (-EBUSY == sg_fd)) {
+ ++bounces;
+ if (wait_ms > 0)
+ this_thread::sleep_for(milliseconds{wait_ms});
+ else if (0 == wait_ms)
+ this_thread::yield(); // 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;
+ }
+
+ ptp = construct_scsi_pt_obj();
+ for (k = 0; k < 2; ++k) {
+ /* Prepare READ_16 command */
+ clear_scsi_pt_obj(ptp);
+ set_scsi_pt_cdb(ptp, r16CmdBlk, sizeof(r16CmdBlk));
+ set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+ set_scsi_pt_data_in(ptp, lb, READ16_REPLY_LEN);
+ res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+ if (res) {
+ fprintf(stderr, "READ_16 do_scsi_pt() error\n");
+ pt_err(res, sg_fd, ptp);
+ }
+ cat = get_scsi_pt_result_category(ptp);
+ if (SCSI_PT_RESULT_GOOD != cat) {
+ fprintf(stderr, "READ_16 do_scsi_pt() category problem\n");
+ return pt_cat_no_good(cat, sg_fd, ptp, sense_buffer);
+ }
+
+ u = (lb[0] << 24) + (lb[1] << 16) + (lb[2] << 8) + lb[3];
+ // 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;
+
+ if (wait_ms > 0) /* allow daylight for bad things ... */
+ this_thread::sleep_for(milliseconds{wait_ms});
+ else if (0 == wait_ms)
+ this_thread::yield(); // thread yield
+ else if (-2 == wait_ms)
+ sleep(0); // process yield ??
+
+ /* Prepare WRITE_16 command */
+ clear_scsi_pt_obj(ptp);
+ set_scsi_pt_cdb(ptp, w16CmdBlk, sizeof(w16CmdBlk));
+ set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+ set_scsi_pt_data_out(ptp, lb, WRITE16_REPLY_LEN);
+ res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+ if (res) {
+ fprintf(stderr, "WRITE_16 do_scsi_pt() error\n");
+ pt_err(res, sg_fd, ptp);
+ }
+ cat = get_scsi_pt_result_category(ptp);
+ if (SCSI_PT_RESULT_GOOD != cat) {
+ fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n");
+ return pt_cat_no_good(cat, sg_fd, ptp, sense_buffer);
+ }
+ }
+
+ if (ptp)
+ destruct_scsi_pt_obj(ptp);
+ scsi_pt_close_device(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). Returns 0 on success, else -1 . */
+static int
+do_inquiry_prod_id(const char * dev_name, int wait_ms,
+ unsigned int & bounces, char * b, int b_mlen)
+{
+ int sg_fd, res, cat;
+ struct sg_pt_base * ptp = NULL;
+ 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];
+ const int open_flags = O_RDWR | O_EXCL | O_NONBLOCK;
+
+ /* O_RDONLY | O_EXCL leads to EPERM (undocumented, historic) */
+ while (((sg_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
+ (-EBUSY == sg_fd)) {
+ ++bounces;
+ if (wait_ms > 0)
+ this_thread::sleep_for(milliseconds{wait_ms});
+ else if (0 == wait_ms)
+ this_thread::yield(); // 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 */
+ ptp = construct_scsi_pt_obj();
+ clear_scsi_pt_obj(ptp);
+ set_scsi_pt_cdb(ptp, inqCmdBlk, sizeof(inqCmdBlk));
+ set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+ set_scsi_pt_data_in(ptp, inqBuff, INQ_REPLY_LEN);
+ res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+ if (res) {
+ fprintf(stderr, "INQUIRY do_scsi_pt() error\n");
+ pt_err(res, sg_fd, ptp);
+ }
+ cat = get_scsi_pt_result_category(ptp);
+ if (SCSI_PT_RESULT_GOOD != cat) {
+ fprintf(stderr, "INQUIRY do_scsi_pt() category problem\n");
+ return pt_cat_no_good(cat, sg_fd, ptp, sense_buffer);
+ }
+
+ /* 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';
+ }
+ }
+ if (ptp)
+ destruct_scsi_pt_obj(ptp);
+ close(sg_fd);
+ return 0;
+}
+
+static void
+work_thread(const char * dev_name, unsigned int lba, int id, int excl,
+ int num, int wait_ms)
+{
+ unsigned int thr_odd_count = 0;
+ unsigned int thr_bounce_count = 0;
+ int k, res;
+
+ console_mutex.lock();
+ cerr << "Enter work_thread id=" << id << " excl=" << excl << endl;
+ console_mutex.unlock();
+ for (k = 0; k < num; ++k) {
+ res = do_rd_inc_wr_twice(dev_name, lba, excl, wait_ms,
+ thr_bounce_count);
+ if (res < 0)
+ break;
+ if (res)
+ ++thr_odd_count;
+ }
+ if (k < num) {
+ console_mutex.lock();
+ cerr << "thread id=" << id << " failed k=" << k << '\n';
+ console_mutex.unlock();
+ }
+
+ odd_count_mutex.lock();
+ odd_count += thr_odd_count;
+ bounce_count += thr_bounce_count;
+ odd_count_mutex.unlock();
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ int k, res;
+ 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 exclude_excl1 = 0;
+ char * dev_name = NULL;
+ char b[64];
+
+ for (k = 1; k < argc; ++k) {
+ 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("-x", argv[k], 2))
+ ++exclude_excl1;
+ 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 {
+
+ res = do_inquiry_prod_id(dev_name, wait_ms, bounce_count, b,
+ sizeof(b));
+ if (res) {
+ fprintf(stderr, "INQUIRY failed on %s\n", dev_name);
+ return 1;
+ }
+ // Comment out next block to use with non scsi_debug devices
+ // If so, be careful, as it writes to a LBA <lba>
+ 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 = ((0 == k) && exclude_excl1) ? 0 : 1;
+
+ thread * tp = new thread {work_thread, dev_name, lba, k, excl,
+ num_per_thread, wait_ms};
+ vt.push_back(tp);
+ }
+
+ for (k = 0; k < (int)vt.size(); ++k)
+ vt[k]->join();
+
+ for (k = 0; k < (int)vt.size(); ++k)
+ delete vt[k];
+
+ cout << "Expecting odd count of 0, got " << odd_count << endl;
+ cout << "Number of EBUSYs: bounce_count=" << bounce_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/include/sg_cmds_extra.h b/include/sg_cmds_extra.h
index d06e5d05..b5cfe750 100644
--- a/include/sg_cmds_extra.h
+++ b/include/sg_cmds_extra.h
@@ -292,6 +292,18 @@ extern int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id,
extern int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len,
int noisy, int verbose);
+/* Handles various service actions associated with opcode 0x83 which is
+ * called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4) and
+ * WRITE USING TOKEN commands. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+extern int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id,
+ int group_num, int timeout_secs,
+ void * paramp, int param_len, int noisy,
+ int verbose);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/sg_cmds_basic.c b/lib/sg_cmds_basic.c
index f0a3dee1..8b908332 100644
--- a/lib/sg_cmds_basic.c
+++ b/lib/sg_cmds_basic.c
@@ -27,7 +27,7 @@
#endif
-static const char * version_str = "1.62 20130728";
+static const char * version_str = "1.63 20130805";
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
diff --git a/lib/sg_cmds_extra.c b/lib/sg_cmds_extra.c
index 345d51bd..af456841 100644
--- a/lib/sg_cmds_extra.c
+++ b/lib/sg_cmds_extra.c
@@ -2310,9 +2310,8 @@ sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
/* SPC-4 rev 35 and later calls this opcode (0x83) "Third-party copy OUT"
* The original EXTENDED COPY command (now called EXTENDED COPY (LID1))
- * is the only one supported by sg_ll_extended_copy(). Another function
- * perhaps sg_ll_3party_copy_out() is needed for the other service actions
- * ( > 0 ). */
+ * is the only one supported by sg_ll_extended_copy(). See function
+ * sg_ll_3party_copy_out() for the other service actions ( > 0 ). */
/* Invokes a SCSI EXTENDED COPY (LID1) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Receive copy results not supported,
@@ -2384,3 +2383,109 @@ sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, int noisy,
destruct_scsi_pt_obj(ptvp);
return ret;
}
+
+/* Handles various service actions associated with opcode 0x83 which is
+ * called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4) and
+ * WRITE USING TOKEN commands. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int
+sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id, int group_num,
+ int timeout_secs, void * paramp, int param_len,
+ int noisy, int verbose)
+{
+ int k, res, ret, sense_cat, tmout;
+ unsigned char xcopyCmdBlk[EXTENDED_COPY_CMDLEN] =
+ {EXTENDED_COPY_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char sense_b[SENSE_BUFF_LEN];
+ struct sg_pt_base * ptvp;
+ const char * opcode_name = "bad sa";
+
+ if (NULL == sg_warnings_strm)
+ sg_warnings_strm = stderr;
+ switch (sa) {
+ case 0x0:
+ case 0x1:
+ opcode_name = (0x0 == sa) ? "Extended copy (LID1)" :
+ "Extended copy (LID4)";
+ xcopyCmdBlk[10] = (unsigned char)((param_len >> 24) & 0xff);
+ xcopyCmdBlk[11] = (unsigned char)((param_len >> 16) & 0xff);
+ xcopyCmdBlk[12] = (unsigned char)((param_len >> 8) & 0xff);
+ xcopyCmdBlk[13] = (unsigned char)(param_len & 0xff);
+ break;
+ case 0x10:
+ case 0x11:
+ opcode_name = (0x10 == sa) ? "Populate token" : "Write using token";
+ xcopyCmdBlk[6] = (unsigned char)((list_id >> 24) & 0xff);
+ xcopyCmdBlk[7] = (unsigned char)((list_id >> 16) & 0xff);
+ xcopyCmdBlk[8] = (unsigned char)((list_id >> 8) & 0xff);
+ xcopyCmdBlk[9] = (unsigned char)(list_id & 0xff);
+ xcopyCmdBlk[10] = (unsigned char)((param_len >> 24) & 0xff);
+ xcopyCmdBlk[11] = (unsigned char)((param_len >> 16) & 0xff);
+ xcopyCmdBlk[12] = (unsigned char)((param_len >> 8) & 0xff);
+ xcopyCmdBlk[13] = (unsigned char)(param_len & 0xff);
+ xcopyCmdBlk[14] = (unsigned char)(group_num & 0x1f);
+ break;
+ case 0x1c:
+ opcode_name = "Copy operation abort";
+ xcopyCmdBlk[2] = (unsigned char)((list_id >> 24) & 0xff);
+ xcopyCmdBlk[3] = (unsigned char)((list_id >> 16) & 0xff);
+ xcopyCmdBlk[4] = (unsigned char)((list_id >> 8) & 0xff);
+ xcopyCmdBlk[5] = (unsigned char)(list_id & 0xff);
+ break;
+ default:
+ fprintf(sg_warnings_strm, "sg_ll_3party_copy_out: unknown service "
+ "action 0x%x\n", sa);
+ return -1;
+ }
+ tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT;
+
+ if (verbose) {
+ fprintf(sg_warnings_strm, " %s cmd: ", opcode_name);
+ for (k = 0; k < EXTENDED_COPY_CMDLEN; ++k)
+ fprintf(sg_warnings_strm, "%02x ", xcopyCmdBlk[k]);
+ fprintf(sg_warnings_strm, "\n");
+ if ((verbose > 1) && paramp && param_len) {
+ fprintf(sg_warnings_strm, " %s parameter list:\n",
+ opcode_name);
+ dStrHexErr((const char *)paramp, param_len, -1);
+ }
+ }
+
+ ptvp = construct_scsi_pt_obj();
+ if (NULL == ptvp) {
+ fprintf(sg_warnings_strm, "%s: out of memory\n", opcode_name);
+ return -1;
+ }
+ set_scsi_pt_cdb(ptvp, xcopyCmdBlk, sizeof(xcopyCmdBlk));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+ res = do_scsi_pt(ptvp, sg_fd, tmout, verbose);
+ ret = sg_cmds_process_resp(ptvp, opcode_name, res, 0, sense_b,
+ noisy, verbose, &sense_cat);
+ if (-1 == ret)
+ ;
+ else if (-2 == ret) {
+ switch (sense_cat) {
+ case SG_LIB_CAT_NOT_READY:
+ case SG_LIB_CAT_INVALID_OP:
+ case SG_LIB_CAT_ILLEGAL_REQ:
+ case SG_LIB_CAT_UNIT_ATTENTION:
+ case SG_LIB_CAT_ABORTED_COMMAND:
+ ret = sense_cat;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ ret = 0;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ } else
+ ret = 0;
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 5954eb3d..ec7571af 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -79,7 +79,7 @@ fi
%{_libdir}/*.la
%changelog
-* Tue Jul 30 2013 - dgilbert at interlog dot com
+* Fri Aug 02 2013 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.37