aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2017-11-04 21:01:08 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2017-11-04 21:01:08 +0000
commitb77f4de57f4048774e8a78fac9934b58d33b6a68 (patch)
tree917e55edd4852e89c071f01f4c375efcf6f82116 /examples
parent3d464ff0b558af456cd4baa79b609a0940489487 (diff)
downloadsg3_utils-b77f4de57f4048774e8a78fac9934b58d33b6a68.tar.gz
move some testing utilities out of the 'examples' and 'utils' directory into the new 'testing' directory; sg_vpd: add enclosure services device characteristics vpage
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@729 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'examples')
-rw-r--r--examples/Makefile22
-rw-r--r--examples/Makefile.cplus83
-rw-r--r--examples/README8
-rw-r--r--examples/bsg_queue_tst.c171
-rw-r--r--examples/sg_iovec_tst.c266
-rwxr-xr-xexamples/sg_persist_tst.sh130
-rw-r--r--examples/sg_queue_tst.c165
-rw-r--r--examples/sg_sense_test.c181
-rw-r--r--examples/sg_tst_async.cpp1215
-rw-r--r--examples/sg_tst_context.cpp479
-rw-r--r--examples/sg_tst_excl.cpp678
-rw-r--r--examples/sg_tst_excl2.cpp567
-rw-r--r--examples/sg_tst_excl3.cpp572
13 files changed, 8 insertions, 4529 deletions
diff --git a/examples/Makefile b/examples/Makefile
index 2f02328f..cd8920d8 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -8,13 +8,13 @@ CC = gcc
LD = gcc
EXECS = sg_simple1 sg_simple2 sg_simple3 sg_simple4 sg_simple16 \
- sg_iovec_tst scsi_inquiry sg_excl sg_sense_test sg_simple5 \
- sg__sat_identify sg__sat_phy_event sg__sat_set_features \
- sg_sat_chk_power sg_sat_smart_rd_data
+ scsi_inquiry sg_excl sg_simple5 sg__sat_identify \
+ sg__sat_phy_event sg__sat_set_features sg_sat_chk_power \
+ sg_sat_smart_rd_data
-EXTRAS = sg_queue_tst sgq_dd
+EXTRAS = sgq_dd
-BSG_EXTRAS = bsg_queue_tst
+BSG_EXTRAS =
MAN_PGS =
@@ -60,18 +60,12 @@ sg_simple4: sg_simple4.o $(LIBFILESOLD)
sg_simple16: sg_simple16.o $(LIBFILESOLD)
$(LD) -o $@ $(LDFLAGS) $^
-sg_iovec_tst: sg_iovec_tst.o $(LIBFILESOLD)
- $(LD) -o $@ $(LDFLAGS) $^
-
scsi_inquiry: scsi_inquiry.o
$(LD) -o $@ $(LDFLAGS) $^
sg_excl: sg_excl.o $(LIBFILESOLD)
$(LD) -o $@ $(LDFLAGS) $^
-sg_sense_test: sg_sense_test.o $(LIBFILESOLD)
- $(LD) -o $@ $(LDFLAGS) $^
-
sg_simple5: sg_simple5.o $(LIBFILESNEW)
$(LD) -o $@ $(LDFLAGS) $^
@@ -90,12 +84,6 @@ sg_sat_chk_power: sg_sat_chk_power.o $(LIBFILESOLD)
sg_sat_smart_rd_data: sg_sat_smart_rd_data.o $(LIBFILESOLD)
$(LD) -o $@ $(LDFLAGS) $^
-sg_queue_tst: sg_queue_tst.o $(LIBFILESOLD)
- $(LD) -o $@ $(LDFLAGS) $^
-
-bsg_queue_tst: bsg_queue_tst.o $(LIBFILESOLD)
- $(LD) -o $@ $(LDFLAGS) $^
-
sgq_dd: sgq_dd.o $(LIBFILESOLD)
$(LD) -o $@ $(LDFLAGS) $^
diff --git a/examples/Makefile.cplus b/examples/Makefile.cplus
deleted file mode 100644
index b884b291..00000000
--- a/examples/Makefile.cplus
+++ /dev/null
@@ -1,83 +0,0 @@
-SHELL = /bin/sh
-
-PREFIX=/usr/local
-INSTDIR=$(DESTDIR)/$(PREFIX)/bin
-MANDIR=$(DESTDIR)/$(PREFIX)/man
-
-CC = g++
-LD = g++
-## CC = clang++
-## LD = clang++
-
-EXECS = sg_tst_excl sg_tst_excl2 sg_tst_excl3 sg_tst_context sg_tst_async
-
-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 -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
-## CFLAGS = -g -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
-# CFLAGS = -g -O2 -Wall -iquote ../include -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS)
-# CFLAGS = -g -O2 -Wall -pedantic -iquote ../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) $^
-
-sg_tst_excl3: sg_tst_excl3.o $(LIBFILESNEW)
- $(LD) -o $@ $(LDFLAGS) $^
-
-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 $^; \
- 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/README b/examples/README
index 0a17fb64..0b86426c 100644
--- a/examples/README
+++ b/examples/README
@@ -10,10 +10,8 @@ There is an brief explanation of each example in the README file in
the main (i.e. this directory's parent) directory. There are also
some notes at the top of each source file.
-
-Those files with the extension "cpp" are C++ examples that use facilities
-in C++11. They can be built by calling 'make -f Makefile.cplus'. A
-gcc/g++ compiler of 4.7.3 vintage or later will be required.
+Some files that were previously in this directory have been moved to
+the 'testing' directory.
Douglas Gilbert
-10th July 2014
+4th November 2017
diff --git a/examples/bsg_queue_tst.c b/examples/bsg_queue_tst.c
deleted file mode 100644
index af9ccff0..00000000
--- a/examples/bsg_queue_tst.c
+++ /dev/null
@@ -1,171 +0,0 @@
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-/* If the following fails the Linux kernel is probably too old */
-#include <linux/bsg.h>
-
-
-#include "sg_lib.h"
-#include "sg_io_linux.h"
-#include "sg_linux_inc.h"
-
-/* This program was used to test SCSI mid level queue ordering.
- The default behaviour is to "queue at head" which is useful for
- error processing but not for streaming READ and WRITE commands.
-
-* Copyright (C) 2010-2016 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: bsg_queue_tst [-t] <bsg_device>
- -t queue at tail
-
- Version 0.90 (20100324)
-
-*/
-
-#define INQ_REPLY_LEN 96
-#define INQ_CMD_LEN 6
-#define SDIAG_CMD_LEN 6
-#define SENSE_BUFFER_LEN 96
-
-#define EBUFF_SZ 256
-
-#ifndef BSG_FLAG_Q_AT_TAIL
-#define BSG_FLAG_Q_AT_TAIL 0x10
-#endif
-
-#ifndef BSG_FLAG_Q_AT_HEAD
-#define BSG_FLAG_Q_AT_HEAD 0x20
-#endif
-
-
-int main(int argc, char * argv[])
-{
- int bsg_fd, k, ok;
- unsigned char inq_cdb[INQ_CMD_LEN] =
- {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
- unsigned char sdiag_cdb[SDIAG_CMD_LEN] =
- {0x1d, 0, 0, 0, 0, 0};
- unsigned char inqBuff[16][INQ_REPLY_LEN];
- struct sg_io_v4 io_hdr[16];
- struct sg_io_v4 rio_hdr;
- char * file_name = 0;
- char ebuff[EBUFF_SZ];
- unsigned char sense_buffer[16][SENSE_BUFFER_LEN];
- int q_at_tail = 0;
-
- for (k = 1; k < argc; ++k) {
- if (0 == memcmp("-t", argv[k], 2))
- ++q_at_tail;
- else if (*argv[k] == '-') {
- printf("Unrecognized switch: %s\n", argv[k]);
- file_name = 0;
- break;
- }
- else if (0 == file_name)
- file_name = argv[k];
- else {
- printf("too many arguments\n");
- file_name = 0;
- break;
- }
- }
- if (0 == file_name) {
- printf("Usage: 'bsg_queue_tst [-t] <bsg_device>'\n"
- "where:\n -t queue_at_tail (def: q_at_head)\n");
- return 1;
- }
-
- /* An access mode of O_RDWR is required for write()/read() interface */
- if ((bsg_fd = open(file_name, O_RDWR)) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- "bsg_queue_tst: error opening file: %s", file_name);
- perror(ebuff);
- return 1;
- }
-
- for (k = 0; k < 16; ++k) {
- /* Prepare INQUIRY command */
- memset(&io_hdr[k], 0, sizeof(struct sg_io_v4));
- io_hdr[k].guard = 'Q';
- /* io_hdr[k].iovec_count = 0; */ /* memset takes care of this */
- if (0 == (k % 3)) {
- io_hdr[k].request_len = sizeof(sdiag_cdb);
- io_hdr[k].request = (uint64_t)(long)sdiag_cdb;
- } else {
- io_hdr[k].request_len = sizeof(inq_cdb);
- io_hdr[k].request = (uint64_t)(long)inq_cdb;
- io_hdr[k].din_xfer_len = INQ_REPLY_LEN;
- io_hdr[k].din_xferp = (uint64_t)(long)inqBuff[k];
- }
- io_hdr[k].response = (uint64_t)(long)sense_buffer[k];
- io_hdr[k].max_response_len = SENSE_BUFFER_LEN;
- io_hdr[k].timeout = 20000; /* 20000 millisecs == 20 seconds */
- io_hdr[k].usr_ptr = k;
- /* default is to queue at head (in SCSI mid level) */
- if (q_at_tail)
- io_hdr[k].flags |= BSG_FLAG_Q_AT_TAIL;
- else
- io_hdr[k].flags |= BSG_FLAG_Q_AT_HEAD;
-
- if (write(bsg_fd, &io_hdr[k], sizeof(struct sg_io_v4)) < 0) {
- perror("bsg_queue_tst: bsg write error");
- close(bsg_fd);
- return 1;
- }
- }
- /* sleep(3); */
- for (k = 0; k < 16; ++k) {
- memset(&rio_hdr, 0, sizeof(struct sg_io_v4));
- rio_hdr.guard = 'Q';
- if (read(bsg_fd, &rio_hdr, sizeof(struct sg_io_v4)) < 0) {
- perror("bsg_queue_tst: bsg read error");
- close(bsg_fd);
- return 1;
- }
- /* now for the error processing */
- ok = 0;
- if (0 == rio_hdr.device_status)
- ok = 1;
- else {
- switch (sg_err_category_sense((unsigned char *)(long)
- rio_hdr.response, rio_hdr.response_len)) {
- case SG_LIB_CAT_CLEAN:
- ok = 1;
- break;
- case SG_LIB_CAT_RECOVERED:
- printf("Recovered error, continuing\n");
- ok = 1;
- break;
- default: /* won't bother decoding other categories */
- fprintf(stderr, "command error:\n");
- sg_print_sense(NULL, (unsigned char *)(long)rio_hdr.response,
- rio_hdr.response_len, 1);
- break;
- }
- }
-
- if (ok) { /* output result if it is available */
- /* if (0 == rio_hdr.pack_id) */
- if (0 == (rio_hdr.usr_ptr % 3))
- printf("SEND DIAGNOSTIC %d duration=%u\n",
- (int)rio_hdr.usr_ptr, rio_hdr.duration);
- else
- printf("INQUIRY %d duration=%u\n", (int)rio_hdr.usr_ptr,
- rio_hdr.duration);
- }
- }
-
- close(bsg_fd);
- return 0;
-}
diff --git a/examples/sg_iovec_tst.c b/examples/sg_iovec_tst.c
deleted file mode 100644
index 6a0848da..00000000
--- a/examples/sg_iovec_tst.c
+++ /dev/null
@@ -1,266 +0,0 @@
-#include <unistd.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "sg_lib.h"
-#include "sg_io_linux.h"
-#include "sg_unaligned.h"
-
-/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
- device driver.
-* Copyright (C) 2003-2015 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.
-
- This program will read a certain number of blocks of a given block size
- from a given sg device node and write what is retrieved out to a
- normal file. The purpose is to test the sg_iovec mechanism within the
- sg_io_hdr structure.
-
- Version 0.14 (20151209)
-*/
-
-
-#define ME "sg_iovec_tst: "
-
-#define A_PRIME 509
-#define IOVEC_ELEMS 2048
-
-#define SENSE_BUFF_LEN 32
-#define DEF_TIMEOUT 40000 /* 40,000 milliseconds */
-
-struct sg_iovec iovec[IOVEC_ELEMS];
-
-
-static void
-usage(void)
-{
- printf("Usage: sg_iovec_tst [-a] [-b=bs] -c=num [-e=es] [-h]\n"
- " <generic_device> <output_filename>\n");
- printf(" where: -a async sg use (def: use ioctl(SGIO) )\n");
- printf(" -b=bs block size (default 512 Bytes)\n");
- printf(" -c=num count of blocks to transfer\n");
- printf(" -e=es iovec element size (def: 509)\n");
- printf(" -h this usage message\n");
- printf(" reads from <generic_device> and sends to "
- "<output_filename>\nUses iovec (a scatter list) in linear "
- "mode\n");
-}
-
-/* Returns 0 if everything ok */
-static int sg_read(int sg_fd, unsigned char * buff, int num_blocks,
- int from_block, int bs, int elem_size, int async)
-{
- unsigned char rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- unsigned char senseBuff[SENSE_BUFF_LEN];
- struct sg_io_hdr io_hdr;
- struct pollfd a_poll;
- int dxfer_len = bs * num_blocks;
- int k, pos, rem, res;
-
- sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
- sg_put_unaligned_be16((uint16_t)from_block, rdCmd + 7);
-
- for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
- iovec[k].iov_base = buff + pos;
- iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
- if (rem <= elem_size)
- break;
- pos += elem_size;
- rem -= elem_size;
- }
- if (k >= IOVEC_ELEMS) {
- fprintf(stderr, "Can't fit dxfer_len=%d bytes in iovec\n", dxfer_len);
- return -1;
- }
- memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
- io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(rdCmd);
- io_hdr.cmdp = rdCmd;
- io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr.dxfer_len = dxfer_len;
- io_hdr.iovec_count = k + 1;
- io_hdr.dxferp = iovec;
- io_hdr.mx_sb_len = SENSE_BUFF_LEN;
- io_hdr.sbp = senseBuff;
- io_hdr.timeout = DEF_TIMEOUT;
- io_hdr.pack_id = from_block;
-
- if (async) {
- res = write(sg_fd, &io_hdr, sizeof(io_hdr));
- if (res < 0) {
- perror("write(<sg_device>), error");
- return -1;
- } else if (res < (int)sizeof(io_hdr)) {
- fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n",
- res, (int)sizeof(io_hdr));
- return -1;
- }
- a_poll.fd = sg_fd;
- a_poll.events = POLLIN;
- a_poll.revents = 0;
- res = poll(&a_poll, 1, 2000 /* millisecs */ );
- if (res < 0) {
- perror("poll error on <sg_device>");
- return -1;
- }
- if (0 == (POLLIN & a_poll.revents)) {
- fprintf(stderr, "strange, poll() completed without data to "
- "read\n");
- return -1;
- }
- res = read(sg_fd, &io_hdr, sizeof(io_hdr));
- if (res < 0) {
- perror("read(<sg_device>), error");
- return -1;
- } else if (res < (int)sizeof(io_hdr)) {
- fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n",
- res, (int)sizeof(io_hdr));
- return -1;
- }
- } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
- perror("reading (SG_IO) on sg device, error");
- return -1;
- }
- switch (sg_err_category3(&io_hdr)) {
- case SG_LIB_CAT_CLEAN:
- break;
- case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
- from_block, num_blocks);
- break;
- case SG_LIB_CAT_UNIT_ATTENTION:
- fprintf(stderr, "Unit attention\n");
- return -1;
- default:
- sg_chk_n_print3("reading", &io_hdr, 1);
- return -1;
- }
- return 0;
-}
-
-
-int main(int argc, char * argv[])
-{
- int sg_fd, fd, res, j, m, dxfer_len;
- unsigned int k, num;
- int do_async = 0;
- int do_help = 0;
- int blk_size = 512;
- int elem_size = A_PRIME;
- int count = 0;
- char * sg_file_name = 0;
- char * out_file_name = 0;
- unsigned char * buffp;
-
- for (j = 1; j < argc; ++j) {
- if (0 == strcmp("-a", argv[j]))
- do_async = 1;
- else if (0 == strncmp("-b=", argv[j], 3)) {
- m = 3;
- num = sscanf(argv[j] + m, "%d", &blk_size);
- if ((1 != num) || (blk_size <= 0)) {
- printf("Couldn't decode number after '-b' switch\n");
- sg_file_name = 0;
- break;
- }
- } else if (0 == strncmp("-c=", argv[j], 3)) {
- m = 3;
- num = sscanf(argv[j] + m, "%d", &count);
- if (1 != num) {
- printf("Couldn't decode number after '-c' switch\n");
- sg_file_name = 0;
- break;
- }
- } else if (0 == strncmp("-e=", argv[j], 3)) {
- m = 3;
- num = sscanf(argv[j] + m, "%d", &elem_size);
- if (1 != num) {
- printf("Couldn't decode number after '-e' switch\n");
- sg_file_name = 0;
- break;
- }
- } else if (0 == strcmp("-h", argv[j]))
- do_help = 1;
- else if (*argv[j] == '-') {
- printf("Unrecognized switch: %s\n", argv[j]);
- sg_file_name = 0;
- break;
- } else if (NULL == sg_file_name)
- sg_file_name = argv[j];
- else
- out_file_name = argv[j];
- }
- if (do_help) {
- usage();
- return 0;
- }
- if (NULL == sg_file_name) {
- printf(">>> need sg node name (e.g. /dev/sg3)\n\n");
- usage();
- return 1;
- }
- if (NULL == out_file_name) {
- printf(">>> need out filename (to place what is fetched by READ\n\n");
- usage();
- return 1;
- }
- if (0 == count) {
- printf(">>> need count of blocks to READ\n\n");
- usage();
- return 1;
- }
-
- if (do_async)
- sg_fd = open(sg_file_name, O_RDWR);
- else
- sg_fd = open(sg_file_name, O_RDONLY);
- if (sg_fd < 0) {
- perror(ME "sg device node open error");
- return 1;
- }
- /* Don't worry, being very careful not to write to a none-sg file ... */
- res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
- if ((res < 0) || (k < 30000)) {
- printf(ME "not a sg device, or driver prior to 3.x\n");
- return 1;
- }
- fd = open(out_file_name, O_WRONLY | O_CREAT, 0666);
- if (fd < 0) {
- perror(ME "output file open error");
- return 1;
- }
- dxfer_len = count * blk_size;
- buffp = (unsigned char *)malloc(dxfer_len);
- if (buffp) {
- if (0 == sg_read(sg_fd, buffp, count, 0, blk_size, elem_size,
- do_async)) {
- if (write(fd, buffp, dxfer_len) < 0)
- perror(ME "output write failed");
- }
- free(buffp);
- } else
- fprintf(stderr, "user space malloc for %d bytes failed\n",
- dxfer_len);
- res = close(fd);
- if (res < 0) {
- perror(ME "output file close error");
- close(sg_fd);
- return 1;
- }
- res = close(sg_fd);
- if (res < 0) {
- perror(ME "sg device close error");
- return 1;
- }
- return 0;
-}
diff --git a/examples/sg_persist_tst.sh b/examples/sg_persist_tst.sh
deleted file mode 100755
index b6b926bb..00000000
--- a/examples/sg_persist_tst.sh
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/bin/sh
-# This script is meant as an example of using the sg_persist utility
-# in the sg3_utils package. This script works as expected on the
-# author's Fujitsu MAM3184, Seagate ST373455 and ST9146803SS disks.
-#
-# Version 1.9 20140612
-
-# N.B. make sure the device name is correct for your environment.
-
-key="123abc"
-key2="333aaa"
-kk=${key}
-rtype="1"
-verbose=""
-
-usage()
-{
- echo "Usage: sg_persist_tst.sh [-e] [-h] [-s] [-v] <device>"
- echo " where:"
- echo -n " -e, --exclusive exclusive access (def: write "
- echo "exclusive)"
- echo " -h, --help print usage message"
- echo " -s, --second use second key"
- echo " -v, --verbose more verbose output"
- echo " -vv even more verbose output"
- echo " -vvv even more verbose output"
- echo ""
- echo "Test SCSI Persistent Reservations with sg_persist utility."
- echo "Default key is ${key} and alternate, second key is ${key2} ."
- echo "Should be harmless (unless one of those keys is already in use)."
- echo "The APTPL bit is not set in the PR register so a power cycle"
- echo "on the device will clear the reservation if this script stops"
- echo "(or is stopped) before clearing it. Tape drives only seem to "
- echo "support 'exclusive access' type (so use '-e')."
-}
-
-opt="$1"
-while test ! -z "$opt" -a -z "${opt##-*}"; do
- opt=${opt#-}
- case "$opt" in
- e|-exclusive) rtype="3" ;;
- h|-help) usage ; exit 0 ;;
- s|-second) kk=${key2} ;;
- vvv) verbose="-vvv" ;;
- vv) verbose="-vv" ;;
- v|-verbose) verbose="-v" ;;
- *) echo "Unknown option: -$opt " ; exit 1 ;;
- esac
- shift
- opt="$1"
-done
-
-if [ $# -lt 1 ]
- then
- usage
- exit 1
-fi
-
-echo ">>> try to report capabilities:"
-sg_persist -c ${verbose} $1
-res=$?
-case "$res" in
- 0) ;;
- 1) echo " syntax error" ;;
- 2) echo " not ready" ;;
- 3) echo " medium error" ;;
- 5) echo " illegal request, report capabilities not supported?" ;;
- 6) echo " unit attention" ;;
- 9) echo " illegal request, Persistent Reserve (In) not supported" ;;
- 11) echo " aborted command" ;;
- 15) echo " file error with $1 " ;;
- 20) echo " no sense" ;;
- 21) echo " recovered error" ;;
- 33) echo " timeout" ;;
- 97) echo " response fails sanity" ;;
- 98) echo " other SCSI error" ;;
- 99) echo " other error" ;;
- *) echo " unknown exit status for sg_persist: $res" ;;
-esac
-echo ""
-sleep 1
-
-echo ">>> check if any keys are registered:"
-sg_persist --no-inquiry --read-keys ${verbose} $1
-sleep 1
-
-echo
-echo ">>> register a key:"
-sg_persist -n --out --register --param-sark=${kk} ${verbose} $1
-sleep 1
-
-echo
-echo ">>> now key ${kk} should be registered:"
-sg_persist -n --read-keys ${verbose} $1
-sleep 1
-
-echo
-echo ">>> reserve the device (based on key ${kk}):"
-sg_persist -n --out --reserve --param-rk=${kk} --prout-type=${rtype} ${verbose} $1
-sleep 1
-
-echo
-echo ">>> check if the device is reserved (it should be now):"
-sg_persist -n --read-reservation ${verbose} $1
-sleep 1
-
-echo
-echo ">>> try to 'read full status' (may not be supported):"
-sg_persist -n --read-full-status ${verbose} $1
-sleep 1
-
-echo
-echo ">>> now release reservation:"
-sg_persist -n --out --release --param-rk=${kk} --prout-type=${rtype} ${verbose} $1
-sleep 1
-
-echo
-echo ">>> check if the device is reserved (it should _not_ be now):"
-sg_persist -n --read-reservation ${verbose} $1
-sleep 1
-
-echo
-echo ">>> unregister key ${kk}:"
-sg_persist -n --out --register --param-rk=${kk} ${verbose} $1
-sleep 1
-
-echo
-echo ">>> now key ${kk} should not be registered:"
-sg_persist -n -k ${verbose} $1
-sleep 1
diff --git a/examples/sg_queue_tst.c b/examples/sg_queue_tst.c
deleted file mode 100644
index 9690b0ba..00000000
--- a/examples/sg_queue_tst.c
+++ /dev/null
@@ -1,165 +0,0 @@
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-
-#include "sg_lib.h"
-#include "sg_io_linux.h"
-#include "sg_linux_inc.h"
-
-/* This program was used to test SCSI mid level queue ordering.
- The default behaviour is to "queue at head" which is useful for
- error processing but not for streaming READ and WRITE commands.
-
-* Copyright (C) 2010 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_queue_tst [-t] <sg_device>
- -t queue at tail
-
- Version 0.91 (20160528)
-
-*/
-
-#define INQ_REPLY_LEN 96
-#define INQ_CMD_LEN 6
-#define SDIAG_CMD_LEN 6
-#define SENSE_BUFFER_LEN 96
-
-#define EBUFF_SZ 256
-
-#ifndef SG_FLAG_Q_AT_TAIL
-#define SG_FLAG_Q_AT_TAIL 0x10
-#endif
-
-#ifndef SG_FLAG_Q_AT_HEAD
-#define SG_FLAG_Q_AT_HEAD 0x20
-#endif
-
-
-int main(int argc, char * argv[])
-{
- int sg_fd, k, ok;
- unsigned char inq_cdb[INQ_CMD_LEN] =
- {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
- unsigned char sdiag_cdb[SDIAG_CMD_LEN] =
- {0x1d, 0, 0, 0, 0, 0};
- unsigned char inqBuff[16][INQ_REPLY_LEN];
- sg_io_hdr_t io_hdr[16];
- sg_io_hdr_t rio_hdr;
- char * file_name = 0;
- char ebuff[EBUFF_SZ];
- unsigned char sense_buffer[16][SENSE_BUFFER_LEN];
- int q_at_tail = 0;
-
- for (k = 1; k < argc; ++k) {
- if (0 == memcmp("-t", argv[k], 2))
- ++q_at_tail;
- else if (*argv[k] == '-') {
- printf("Unrecognized switch: %s\n", argv[k]);
- file_name = 0;
- break;
- }
- else if (0 == file_name)
- file_name = argv[k];
- else {
- printf("too many arguments\n");
- file_name = 0;
- break;
- }
- }
- if (0 == file_name) {
- printf("Usage: 'sg_queue_tst [-t] <sg_device>'\n"
- "where:\n -t queue_at_tail (def: q_at_head)\n");
- return 1;
- }
-
- /* An access mode of O_RDWR is required for write()/read() interface */
- if ((sg_fd = open(file_name, O_RDWR)) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- "sg_queue_tst: error opening file: %s", file_name);
- perror(ebuff);
- return 1;
- }
-
- for (k = 0; k < 16; ++k) {
- /* Prepare INQUIRY command */
- memset(&io_hdr[k], 0, sizeof(sg_io_hdr_t));
- io_hdr[k].interface_id = 'S';
- /* io_hdr[k].iovec_count = 0; */ /* memset takes care of this */
- io_hdr[k].mx_sb_len = (unsigned char)sizeof(sense_buffer);
- if (0 == (k % 3)) {
- io_hdr[k].cmd_len = sizeof(sdiag_cdb);
- io_hdr[k].cmdp = sdiag_cdb;
- io_hdr[k].dxfer_direction = SG_DXFER_NONE;
- } else {
- io_hdr[k].cmd_len = sizeof(inq_cdb);
- io_hdr[k].cmdp = inq_cdb;
- io_hdr[k].dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr[k].dxfer_len = INQ_REPLY_LEN;
- io_hdr[k].dxferp = inqBuff[k];
- }
- io_hdr[k].sbp = sense_buffer[k];
- io_hdr[k].mx_sb_len = SENSE_BUFFER_LEN;
- io_hdr[k].timeout = 20000; /* 20000 millisecs == 20 seconds */
- io_hdr[k].pack_id = k;
- /* default is to queue at head (in SCSI mid level) */
- if (q_at_tail)
- io_hdr[k].flags |= SG_FLAG_Q_AT_TAIL;
- else
- io_hdr[k].flags |= SG_FLAG_Q_AT_HEAD;
- /* io_hdr[k].usr_ptr = NULL; */
-
- if (write(sg_fd, &io_hdr[k], sizeof(sg_io_hdr_t)) < 0) {
- perror("sg_queue_tst: sg write error");
- close(sg_fd);
- return 1;
- }
- }
- /* sleep(3); */
- for (k = 0; k < 16; ++k) {
- memset(&rio_hdr, 0, sizeof(sg_io_hdr_t));
- rio_hdr.interface_id = 'S';
- if (read(sg_fd, &rio_hdr, sizeof(sg_io_hdr_t)) < 0) {
- perror("sg_queue_tst: sg read error");
- close(sg_fd);
- return 1;
- }
- /* now for the error processing */
- ok = 0;
- switch (sg_err_category3(&rio_hdr)) {
- case SG_LIB_CAT_CLEAN:
- ok = 1;
- break;
- case SG_LIB_CAT_RECOVERED:
- printf("Recovered error, continuing\n");
- ok = 1;
- break;
- default: /* won't bother decoding other categories */
- sg_chk_n_print3("command error", &rio_hdr, 1);
- break;
- }
-
- if (ok) { /* output result if it is available */
- /* if (0 == rio_hdr.pack_id) */
- if (0 == (rio_hdr.pack_id % 3))
- printf("SEND DIAGNOSTIC %d duration=%u\n", rio_hdr.pack_id,
- rio_hdr.duration);
- else
- printf("INQUIRY %d duration=%u\n", rio_hdr.pack_id,
- rio_hdr.duration);
- }
- }
-
- close(sg_fd);
- return 0;
-}
diff --git a/examples/sg_sense_test.c b/examples/sg_sense_test.c
deleted file mode 100644
index 4ada185b..00000000
--- a/examples/sg_sense_test.c
+++ /dev/null
@@ -1,181 +0,0 @@
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <getopt.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "sg_lib.h"
-
-/* This is a simple program that tests the sense data descriptor format
- printout function in sg_lib.c
-
-* Copyright (C) 2004-2016 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.
-
-*/
-
-#define EBUFF_SZ 256
-
-#define ME "sg_sense_test: "
-
-static char * version_str = "2.00 20160128";
-
-static struct option long_options[] = {
- {"help", no_argument, 0, 'h'},
- {"leadin", required_argument, 0, 'l'},
- {"verbose", no_argument, 0, 'v'},
- {"version", no_argument, 0, 'V'},
- {0, 0, 0, 0}, /* sentinel */
-};
-
-
-static void
-usage()
-{
- fprintf(stderr,
- "Usage: %s [--help] [--leadin=STR] [--verbose] [--version]\n"
- " where: --help|-h print out usage message\n"
- " --leadin=STR|-l STR every line output by --sense "
- "should\n"
- " be prefixed by STR\n"
- " --verbose|-v increase verbosity\n"
- " --version|-V print version string and exit\n\n"
- "Test sense data handling of sg_lib. Overlaps somewhat with "
- "utils/tst_sg_lib\n", ME
- );
-
-}
-
-int main(int argc, char * argv[])
-{
- unsigned char err1[] = {0x72, 0x5, 0x24, 0x0, 0, 0, 0, 32,
- 0x2, 0x6, 0, 0, 0xc8, 0x0, 0x3, 0,
- 0, 0xa, 0x80, 0, 1, 2, 3, 4,
- 0xaa, 0xbb, 0xcc, 0xdd,
- 1, 0xa, 0, 0, 1, 2, 3, 4,
- 0xaa, 0xbb, 0xee, 0xff};
- unsigned char err2[] = {0x72, SPC_SK_MEDIUM_ERROR, 0x11, 0xb, 0x80, 0, 0,
- 32,
- 0x2, 0x6, 0, 0, 0xc8, 0x0, 0x3, 0,
- 0, 0xa, 0x80, 0, 1, 2, 3, 4,
- 0xaa, 0xbb, 0xcc, 0xdd,
- 1, 0xa, 0, 0, 1, 2, 3, 4,
- 0xaa, 0xbb, 0xee, 0xff};
- /* Set SDAT_OVFL */
- unsigned char err3[] = {0x72, SPC_SK_NO_SENSE, 0x4, 0x4, 0, 0, 0, 8,
- 0x2, 0x6, 0, 0, 0xc8, 0x12, 0x34, 0};
- unsigned char err4[] = {0x73, SPC_SK_COPY_ABORTED, 0x8, 0x4, 0, 0, 0, 22,
- 0x2, 0x6, 0, 0, 0xc8, 0x0, 0x3, 0,
- 0x3, 0x2, 0, 0x55,
- 0x5, 0x2, 0, 0x20,
- 0x85, 0x4, 0, 0x20, 0x33, 0x44};
- /* Set Filemark, EOM, ILI and SDAT_OVFL */
- unsigned char err5[] = {0xf1, 0, (0xf0 | SPC_SK_ILLEGAL_REQUEST), 0x11,
- 0x22, 0x33, 0x44, 0xa,
- 0x0, 0x0, 0, 0, 0x4, 0x1, 0, 0xcf, 0, 5,};
- unsigned char err6[] = {0x72, SPC_SK_NO_SENSE, 0x4, 0x1, 0, 0, 0, 14,
- 0x9, 0xc, 1, 0, 0x11, 0x22, 0x66, 0x33,
- 0x77, 0x44, 0x88, 0x55, 0x1, 0x2};
- unsigned char err7[] = {0xf1, 0, 0xe5, 0x11, 0x22, 0x33, 0x44, 0xa,
- 0x0, 0x0, 0x0, 0x0, 0x24, 0x1, 0xbb,
- 0xc9, 0x0, 0x2};
- const char * leadin = NULL;
- char b[2048];
- int c, k, prev_len;
- int verbose = 0;
-
- while (1) {
- int option_index = 0;
-
- c = getopt_long(argc, argv, "hl:vV", long_options,
- &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- case '?':
- usage();
- return 0;
- case 'l':
- leadin = optarg;
- break;
- case 'v':
- ++verbose;
- break;
- case 'V':
- fprintf(stderr, "version: %s\n", version_str);
- return 0;
- default:
- fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
- usage();
- return 1;
- }
- }
- if (optind < argc) {
- if (optind < argc) {
- for (; optind < argc; ++optind)
- fprintf(stderr, "Unexpected extra argument: %s\n",
- argv[optind]);
- usage();
- return 1;
- }
- }
-
- printf("err1 test:\n");
- sg_print_sense(leadin, err1, sizeof(err1), verbose /* raw_info */);
- printf("\n");
- printf("err2 test:\n");
- sg_print_sense(leadin, err2, sizeof(err2), verbose);
- printf("\n");
- printf("err3 test:\n");
- sg_print_sense(leadin, err3, sizeof(err3), verbose);
- printf("\n");
- printf("err4 test:\n");
- sg_print_sense(leadin, err4, sizeof(err4), verbose);
- printf("\n");
- printf("err5 test: Set Filemark, EOM, ILI and SDAT_OVFL\n");
- sg_print_sense(leadin, err5, sizeof(err5), verbose);
- printf("\n");
- printf("err6 test:\n");
- sg_print_sense(leadin, err6, sizeof(err6), verbose);
- printf("\n");
- printf("err7 test:\n");
- sg_print_sense(leadin, err7, sizeof(err7), verbose);
-
- if (verbose > 1) {
- printf("\n\nTry different output string sizes with "
- "sg_get_sense_str(err2):\n");
- for (k = 1, prev_len = -1; k < 512; ++k) {
- /* snprintf(leadin, sizeof(leadin), "blen=%d", k); */
- sg_get_sense_str(NULL, err2, sizeof(err2), 0, k, b);
- printf("%s\n", b);
- if (prev_len == (int)strlen(b))
- break;
- else
- prev_len = strlen(b);
- }
- }
-
- if (verbose > 2) {
- printf("\n\nTry different output string sizes with "
- "sg_get_sense_str(err4):\n");
- for (k = 1, prev_len = -1; k < 512; ++k) {
- /* snprintf(leadin, sizeof(leadin), "blen=%d", k); */
- sg_get_sense_str(NULL, err4, sizeof(err4), 0, k, b);
- printf("%s\n", b);
- if (prev_len == (int)strlen(b))
- break;
- else
- prev_len = strlen(b);
- }
- }
- return 0;
-}
diff --git a/examples/sg_tst_async.cpp b/examples/sg_tst_async.cpp
deleted file mode 100644
index 67f247cc..00000000
--- a/examples/sg_tst_async.cpp
+++ /dev/null
@@ -1,1215 +0,0 @@
-/*
- * Copyright (c) 2014-2015 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 <map>
-#include <list>
-#include <system_error>
-#include <thread>
-#include <mutex>
-#include <chrono>
-#include <atomic>
-#include <random>
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <poll.h>
-#include <errno.h>
-#include <ctype.h>
-#include <time.h>
-#include <limits.h>
-#include <getopt.h>
-#define __STDC_FORMAT_MACROS 1
-#include <inttypes.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "sg_lib.h"
-#include "sg_io_linux.h"
-#include "sg_unaligned.h"
-
-static const char * version_str = "1.11 20150227";
-static const char * util_name = "sg_tst_async";
-
-/* This is a test program for checking the async usage of the Linux sg
- * driver. Each thread opens 1 file descriptor to the next sg device (1
- * or more can be given on the command line) and then starts up to 16
- * commands while checking with the poll command (or
- * ioctl(SG_GET_NUM_WAITING) ) for the completion of those commands. Each
- * command has a unique "pack_id" which is a sequence starting at 1.
- * Either TEST UNIT UNIT, READ(16) or WRITE(16) commands are issued.
- *
- * 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 . It should build okay on
- * recent distributions.
- *
- * 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_package_root> ; ./configure ; cd lib; make
- * cd ../examples
- * Then to build sg_tst_async 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_async
- * sg_tst_async.cpp
- * or use the C++ Makefile in that directory:
- * make -f Makefile.cplus sg_tst_async
- *
- * Currently this utility is Linux only and uses the sg driver. The bsg
- * driver is known to be broken (it doesn't match responses to the
- * correct file descriptor that requested them) so this utility won't
- * be extended to bsg until that is fixed.
- *
- * BEWARE: >>> This utility will modify a logical block (default LBA 1000)
- * on the given device when the '-W' option is given.
- *
- */
-
-using namespace std;
-using namespace std::chrono;
-
-#define DEF_NUM_PER_THREAD 1000
-#define DEF_NUM_THREADS 4
-#define DEF_WAIT_MS 10 /* 0: yield or no wait */
-#define DEF_TIMEOUT_MS 20000 /* 20 seconds */
-#define DEF_LB_SZ 512
-#define DEF_BLOCKING 0
-#define DEF_DIRECT 0 /* 1: direct_io [future maybe 2: mmap IO] */
-#define DEF_NO_XFER 0
-#define DEF_LBA 1000
-
-#define MAX_Q_PER_FD 16 /* sg driver per file descriptor limit */
-#define MAX_CONSEC_NOMEMS 16
-#define URANDOM_DEV "/dev/urandom"
-
-#ifndef SG_FLAG_Q_AT_TAIL
-#define SG_FLAG_Q_AT_TAIL 0x10
-#endif
-#ifndef SG_FLAG_Q_AT_HEAD
-#define SG_FLAG_Q_AT_HEAD 0x20
-#endif
-
-
-
-#define EBUFF_SZ 256
-
-static mutex console_mutex;
-static mutex rand_lba_mutex;
-static atomic<int> async_starts(0);
-static atomic<int> async_finishes(0);
-static atomic<int> ebusy_count(0);
-static atomic<int> start_eagain_count(0);
-static atomic<int> fin_eagain_count(0);
-static atomic<int> uniq_pack_id(1);
-
-static int page_size = 4096; /* rough guess, will ask sysconf() */
-
-enum command2execute {SCSI_TUR, SCSI_READ16, SCSI_WRITE16};
-/* Linux Block layer queue disciplines: */
-enum blkLQDiscipline {BLQ_DEFAULT, BLQ_AT_HEAD, BLQ_AT_TAIL};
-/* Queue disciplines of this utility. When both completions and
- * queuing a new command are both possible: */
-enum myQDiscipline {MYQD_LOW, /* favour completions over new cmds */
- MYQD_MEDIUM,
- MYQD_HIGH}; /* favour new cmds over completions */
-
-struct opts_t {
- vector<const char *> dev_names;
- int direct;
- int maxq_per_thread;
- int num_per_thread;
- bool block;
- uint64_t lba;
- unsigned int hi_lba; /* last one, inclusive range */
- vector<unsigned int> hi_lbas; /* only used when hi_lba=-1 */
- int lb_sz;
- bool no_xfer;
- int stats;
- int verbose;
- int wait_ms;
- command2execute c2e;
- blkLQDiscipline blqd;
- myQDiscipline myqd;
-};
-
-#if 0
-class Rand_uint {
-public:
- Rand_uint(unsigned int lo, unsigned int hi) : p{lo, hi} {}
- unsigned int operator()() const { return r(); }
-private:
- uniform_int_distribution<unsigned int>::param_type p;
- auto r = bind(uniform_int_distribution<unsigned int>{p},
- default_random_engine());
- /* compiler thinks auto should be a static, bs again? */
-};
-#endif
-
-#if 0
-class Rand_uint {
-public:
- Rand_uint(unsigned int lo, unsigned int hi, unsigned int my_seed)
- : r(bind(uniform_int_distribution<unsigned int>{lo, hi},
- default_random_engine())) { r.seed(myseed); }
- unsigned int operator()() const { return r(); }
-private:
- function<unsigned int()> r;
-};
-#endif
-
-/* Use this class to wrap C++11 <random> features to produce uniform random
- * unsigned ints in the range [lo, hi] (inclusive) given a_seed */
-class Rand_uint {
-public:
- Rand_uint(unsigned int lo, unsigned int hi, unsigned int a_seed)
- : uid(lo, hi), dre(a_seed) { }
- /* uid ctor takes inclusive range when integral type */
-
- unsigned int get() { return uid(dre); }
-
-private:
- uniform_int_distribution<unsigned int> uid;
- default_random_engine dre;
-};
-
-static struct option long_options[] = {
- {"direct", no_argument, 0, 'd'},
- {"force", no_argument, 0, 'f'},
- {"help", no_argument, 0, 'h'},
- {"lba", required_argument, 0, 'l'},
- {"maxqpt", required_argument, 0, 'M'},
- {"numpt", required_argument, 0, 'n'},
- {"noxfer", no_argument, 0, 'N'},
- {"qat", required_argument, 0, 'q'},
- {"qfav", required_argument, 0, 'Q'},
- {"read", no_argument, 0, 'R'},
- {"szlb", required_argument, 0, 's'},
- {"stats", no_argument, 0, 'S'},
- {"tnum", required_argument, 0, 't'},
- {"tur", no_argument, 0, 'T'},
- {"verbose", no_argument, 0, 'v'},
- {"version", no_argument, 0, 'V'},
- {"wait", required_argument, 0, 'w'},
- {"write", no_argument, 0, 'W'},
- {0, 0, 0, 0},
-};
-
-
-static void
-usage(void)
-{
- printf("Usage: %s [--direct] [--force] [--help] [--lba=LBA+] "
- "[--maxqpt=QPT]\n"
- " [--numpt=NPT] [--noxfer] [--qat=AT] "
- "[-qfav=FAV]\n"
- " [--read] [--szlb=LB] [--stats] [--tnum=NT] "
- "[--tur]\n"
- " [--verbose] [--version] [--wait=MS] "
- "[--write]\n"
- " <sg_disk_device>*\n",
- util_name);
- printf(" where\n");
- printf(" --direct|-d do direct_io (def: indirect)\n");
- printf(" --force|-f force: any sg device (def: only scsi_debug "
- "owned)\n");
- printf(" WARNING: <lba> written to if '-W' given\n");
- printf(" --help|-h print this usage message then exit\n");
- printf(" --lba=LBA|-l LBA logical block to access (def: %u)\n",
- DEF_LBA);
- printf(" --lba=LBA,HI_LBA|-l LBA,HI_LBA logical block range "
- "(inclusive)\n"
- " if hi_lba=-1 assume last block on "
- "device\n");
- printf(" --maxqpt=QPT|-M QPT maximum commands queued per thread "
- "(def:%d)\n", MAX_Q_PER_FD);
- printf(" --numpt=NPT|-n NPT number of commands per thread "
- "(def: %d)\n", DEF_NUM_PER_THREAD);
- printf(" --noxfer|-N no data xfer (def: xfer on READ and "
- "WRITE)\n");
- printf(" --qat=AT|-q AT AT=0: q_at_head; AT=1: q_at_tail\n");
- printf(" --qfav=FAV|-Q FAV FAV=0: favour completions (smaller q),\n"
- " FAV=1: medium,\n"
- " FAV=2: favour submissions (larger q, "
- "default)\n");
- printf(" --read|-R do READs (def: TUR)\n");
- printf(" --szlb=LB|-s LB logical block size (def: 512)\n");
- printf(" --stats|-S show more statistics on completion\n");
- printf(" --tnum=NT|-t NT number of threads (def: %d)\n",
- DEF_NUM_THREADS);
- printf(" --tur|-T do TEST UNIT READYs (default is TURs)\n");
- printf(" --verbose|-v increase verbosity\n");
- printf(" --version|-V print version number then exit\n");
- printf(" --wait=MS|-w MS >0: poll(<wait_ms>); =0: poll(0); (def: "
- "%d)\n", DEF_WAIT_MS);
- printf(" --write|-W do WRITEs (def: TUR)\n\n");
- printf("Multiple threads send READ(16), WRITE(16) or TEST UNIT READY "
- "(TUR) SCSI\ncommands. There can be 1 or more <sg_disk_device>s "
- "and each thread takes\nthe next in a round robin fashion. "
- "Each thread queues up to 16 commands.\nOne block is transferred "
- "by each READ and WRITE; zeros are written. If a\nlogical block "
- "range is given, a uniform distribution generates a pseudo\n"
- "random sequence of LBAs.\n");
-}
-
-#ifdef __GNUC__
-static int pr2serr_lk(const char * fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
-static void pr_errno_lk(int e_no, const char * fmt, ...)
- __attribute__ ((format (printf, 2, 3)));
-#else
-static int pr2serr_lk(const char * fmt, ...);
-static void pr_errno_lk(int e_no, const char * fmt, ...);
-#endif
-
-
-static int
-pr2serr_lk(const char * fmt, ...)
-{
- int n;
- va_list args;
- lock_guard<mutex> lg(console_mutex);
-
- va_start(args, fmt);
- n = vfprintf(stderr, fmt, args);
- va_end(args);
- return n;
-}
-
-static void
-pr_errno_lk(int e_no, const char * fmt, ...)
-{
- char b[160];
- va_list args;
- lock_guard<mutex> lg(console_mutex);
-
- va_start(args, fmt);
- vsnprintf(b, sizeof(b), fmt, args);
- fprintf(stderr, "%s: %s\n", b, strerror(e_no));
- va_end(args);
-}
-
-static unsigned int
-get_urandom_uint(void)
-{
- unsigned int res = 0;
- int n;
- unsigned char b[sizeof(unsigned int)];
- lock_guard<mutex> lg(rand_lba_mutex);
-
- int fd = open(URANDOM_DEV, O_RDONLY);
- if (fd >= 0) {
- n = read(fd, b, sizeof(unsigned int));
- if (sizeof(unsigned int) == n)
- memcpy(&res, b, sizeof(unsigned int));
- close(fd);
- }
- return res;
-}
-
-#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
-
-/* Returns 0 if command injected okay, else -1 */
-static int
-start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
- unsigned char * lbp, int xfer_bytes, int flags,
- unsigned int & eagains)
-{
- 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 = NULL;
-
- memset(&pt, 0, sizeof(pt));
- switch (cmd2exe) {
- case SCSI_TUR:
- np = "TEST UNIT READY";
- pt.cmdp = turCmdBlk;
- pt.cmd_len = sizeof(turCmdBlk);
- pt.dxfer_direction = SG_DXFER_NONE;
- break;
- case SCSI_READ16:
- np = "READ(16)";
- if (lba > 0xffffffff)
- sg_put_unaligned_be32(lba >> 32, &r16CmdBlk[2]);
- sg_put_unaligned_be32(lba & 0xffffffff, &r16CmdBlk[6]);
- 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 SCSI_WRITE16:
- np = "WRITE(16)";
- if (lba > 0xffffffff)
- sg_put_unaligned_be32(lba >> 32, &w16CmdBlk[2]);
- sg_put_unaligned_be32(lba & 0xffffffff, &w16CmdBlk[6]);
- pt.cmdp = w16CmdBlk;
- pt.cmd_len = sizeof(w16CmdBlk);
- pt.dxfer_direction = SG_DXFER_TO_DEV;
- pt.dxferp = lbp;
- pt.dxfer_len = xfer_bytes;
- break;
- }
- pt.interface_id = 'S';
- pt.mx_sb_len = sizeof(sense_buffer);
- pt.sbp = sense_buffer; /* ignored .... */
- pt.timeout = DEF_TIMEOUT_MS;
- pt.pack_id = pack_id;
- pt.flags = flags;
-
- for (int k = 0; write(sg_fd, &pt, sizeof(pt)) < 0; ++k) {
- if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) {
- this_thread::yield();
- continue;
- }
- if (EAGAIN == errno) {
- ++eagains;
- this_thread::yield();
- continue;
- }
- pr_errno_lk(errno, "%s: %s, pack_id=%d", __func__, np, pack_id);
- return -1;
- }
- return 0;
-}
-
-static int
-finish_sg3_cmd(int sg_fd, command2execute cmd2exe, int & pack_id, int wait_ms,
- unsigned int & eagains)
-{
- int ok, res;
- struct sg_io_hdr pt;
- unsigned char sense_buffer[64];
- const char * np = NULL;
-
- memset(&pt, 0, sizeof(pt));
- switch (cmd2exe) {
- case SCSI_TUR:
- np = "TEST UNIT READY";
- break;
- case SCSI_READ16:
- np = "READ(16)";
- break;
- case SCSI_WRITE16:
- np = "WRITE(16)";
- break;
- }
- pt.interface_id = 'S';
- pt.mx_sb_len = sizeof(sense_buffer);
- pt.sbp = sense_buffer;
- pt.timeout = DEF_TIMEOUT_MS;
- pt.pack_id = 0;
-
- 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) {
- pr_errno_lk(errno, "%s: %s", __func__, np);
- return -1;
- }
- /* now for the error processing */
- pack_id = pt.pack_id;
- ok = 0;
- switch (sg_err_category3(&pt)) {
- case SG_LIB_CAT_CLEAN:
- ok = 1;
- break;
- case SG_LIB_CAT_RECOVERED:
- pr2serr_lk("%s: Recovered error on %s, continuing\n", __func__, np);
- ok = 1;
- break;
- default: /* won't bother decoding other categories */
- {
- lock_guard<mutex> lg(console_mutex);
- sg_chk_n_print3(np, &pt, 1);
- }
- break;
- }
- return ok ? 0 : -1;
-}
-
-/* Should have page alignment if direct_io chosen */
-static unsigned char *
-get_aligned_heap(int bytes_at_least)
-{
- int n;
- void * wp;
-
- if (bytes_at_least < page_size)
- n = page_size;
- else
- n = bytes_at_least;
-#if 1
- int err = posix_memalign(&wp, page_size, n);
- if (err) {
- pr2serr_lk("posix_memalign: error [%d] out of memory?\n", err);
- return NULL;
- }
- memset(wp, 0, n);
- return (unsigned char *)wp;
-#else
- /* hack if posix_memalign() is not available */
- if (n == page_size) {
- wp = calloc(page_size, 1);
- memset(wp, 0, n);
- return (unsigned char *)wp;
- } else {
- pr2serr_lk("get_aligned_heap: too fiddly to align, choose smaller "
- "lb_sz\n");
- return NULL;
- }
-#endif
-}
-
-static void
-work_thread(int id, struct opts_t * op)
-{
- int thr_async_starts = 0;
- int thr_async_finishes = 0;
- unsigned int thr_start_eagain_count = 0;
- unsigned int thr_fin_eagain_count = 0;
- unsigned int seed = 0;
- unsigned int hi_lba;
- int k, n, res, sg_fd, num_outstanding, do_inc, npt, pack_id, sg_flags;
- int num_waiting_read, num_to_read;
- int open_flags = O_RDWR;
- bool is_rw = (SCSI_TUR != op->c2e);
- char ebuff[EBUFF_SZ];
- uint64_t lba;
- unsigned char * lbp;
- const char * dev_name;
- const char * err = NULL;
- Rand_uint * ruip = NULL;
- struct pollfd pfd[1];
- list<unsigned char *> free_lst; /* of aligned lb buffers */
- map<int, unsigned char *> pi_2_buff; /* pack_id -> lb buffer */
- map<int, uint64_t> pi_2_lba; /* pack_id -> LBA */
-
- /* device name and hi_lba may depend on id */
- n = op->dev_names.size();
- dev_name = op->dev_names[id % n];
- if ((UINT_MAX == op->hi_lba) && (n == (int)op->hi_lbas.size()))
- hi_lba = op->hi_lbas[id % n];
- else
- hi_lba = op->hi_lba;
-
- if (op->verbose) {
- if ((op->verbose > 1) && hi_lba)
- pr2serr_lk("Enter work_thread id=%d using %s\n"
- " LBA range: 0x%x to 0x%x (inclusive)\n",
- id, dev_name, (unsigned int)op->lba, hi_lba);
- else
- pr2serr_lk("Enter work_thread id=%d using %s\n", id, dev_name);
- }
- if (! op->block)
- open_flags |= O_NONBLOCK;
-
- sg_fd = open(dev_name, open_flags);
- if (sg_fd < 0) {
- pr_errno_lk(errno, "%s: id=%d, error opening file: %s", __func__, id,
- dev_name);
- return;
- }
- pfd[0].fd = sg_fd;
- pfd[0].events = POLLIN;
- if (is_rw && hi_lba) {
- seed = get_urandom_uint();
- if (op->verbose > 1)
- pr2serr_lk(" id=%d, /dev/urandom seed=0x%x\n", id, seed);
- ruip = new Rand_uint((unsigned int)op->lba, hi_lba, seed);
- }
-
- sg_flags = 0;
- if (BLQ_AT_TAIL == op->blqd)
- sg_flags |= SG_FLAG_Q_AT_TAIL;
- else if (BLQ_AT_HEAD == op->blqd)
- sg_flags |= SG_FLAG_Q_AT_HEAD;
- if (op->direct)
- sg_flags |= SG_FLAG_DIRECT_IO;
- if (op->no_xfer)
- sg_flags |= SG_FLAG_NO_DXFER;
- if (op->verbose > 1)
- pr2serr_lk(" id=%d, sg_flags=0x%x, %s cmds\n", id, sg_flags,
- ((SCSI_TUR == op->c2e) ? "TUR":
- ((SCSI_READ16 == op->c2e) ? "READ" : "WRITE")));
-
- npt = op->num_per_thread;
- /* main loop, continues until num_per_thread exhausted and there are
- * no more outstanding responses */
- for (k = 0, num_outstanding = 0; (k < npt) || num_outstanding;
- k = do_inc ? k + 1 : k) {
- do_inc = 0;
- if ((num_outstanding < op->maxq_per_thread) && (k < npt)) {
- do_inc = 1;
- pack_id = uniq_pack_id.fetch_add(1);
- if (is_rw) { /* get new lb buffer or one from free list */
- if (free_lst.empty()) {
- lbp = get_aligned_heap(op->lb_sz);
- if (NULL == lbp) {
- err = "out of memory";
- break;
- }
- } else {
- lbp = free_lst.back();
- free_lst.pop_back();
- }
- } else
- lbp = NULL;
- if (is_rw) {
- if (ruip) {
- lba = ruip->get(); /* fetch a random LBA */
- if (op->verbose > 3)
- pr2serr_lk(" id=%d: start IO at lba=0x%" PRIx64 "\n",
- id, lba);
- } else
- lba = op->lba;
- } else
- lba = 0;
- if (start_sg3_cmd(sg_fd, op->c2e, pack_id, lba, lbp, op->lb_sz,
- sg_flags, thr_start_eagain_count)) {
- err = "start_sg3_cmd()";
- break;
- }
- ++thr_async_starts;
- ++num_outstanding;
- pi_2_buff[pack_id] = lbp;
- if (ruip)
- pi_2_lba[pack_id] = lba;
- }
- num_to_read = 0;
- if ((num_outstanding >= op->maxq_per_thread) || (k >= npt)) {
- /* full queue or finished injecting */
- num_waiting_read = 0;
- if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting_read) < 0) {
- err = "ioctl(SG_GET_NUM_WAITING) failed";
- break;
- }
- if (1 == num_waiting_read)
- num_to_read = num_waiting_read;
- else if (num_waiting_read > 0) {
- if (k >= npt)
- num_to_read = num_waiting_read;
- else {
- switch (op->myqd) {
- case MYQD_LOW:
- num_to_read = num_waiting_read;
- break;
- case MYQD_MEDIUM:
- num_to_read = num_waiting_read / 2;
- break;
- case MYQD_HIGH:
- default:
- num_to_read = 1;
- break;
- }
- }
- } else { /* nothing waiting to be read */
- n = (op->wait_ms > 0) ? op->wait_ms : 0;
- while (0 == (res = poll(pfd, 1, n))) {
- if (res < 0) {
- err = "poll(wait_ms) failed";
- break;
- }
- }
- if (err)
- break;
- }
- } else { /* not full, not finished injecting */
- if (MYQD_HIGH == op->myqd)
- num_to_read = 0;
- else {
- num_waiting_read = 0;
- if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting_read) < 0) {
- err = "ioctl(SG_GET_NUM_WAITING) failed";
- break;
- }
- if (num_waiting_read > 0)
- num_to_read = num_waiting_read /
- ((MYQD_LOW == op->myqd) ? 1 : 2);
- else
- num_to_read = 0;
- }
- }
-
- while (num_to_read-- > 0) {
- if (finish_sg3_cmd(sg_fd, op->c2e, pack_id, op->wait_ms,
- thr_fin_eagain_count)) {
- err = "finish_sg3_cmd()";
- if (ruip && (pack_id > 0)) {
- auto q = pi_2_lba.find(pack_id);
-
- if (q != pi_2_lba.end()) {
- snprintf(ebuff, sizeof(ebuff), "%s: lba=0x%" PRIx64 ,
- err, q->second);
- err = ebuff;
- }
- }
- break;
- }
- ++thr_async_finishes;
- --num_outstanding;
- auto p = pi_2_buff.find(pack_id);
-
- if (p == pi_2_buff.end()) {
- snprintf(ebuff, sizeof(ebuff), "pack_id=%d from "
- "finish_sg3_cmd() not found\n", pack_id);
- if (! err)
- err = ebuff;
- } else {
- lbp = p->second;
- pi_2_buff.erase(p);
- if (lbp)
- free_lst.push_front(lbp);
- }
- if (ruip && (pack_id > 0)) {
- auto q = pi_2_lba.find(pack_id);
-
- if (q != pi_2_lba.end()) {
- if (op->verbose > 3)
- pr2serr_lk(" id=%d: finish IO at lba=0x%" PRIx64
- "\n", id, q->second);
- pi_2_lba.erase(q);
- }
- }
- if (err)
- break;
- }
- if (err)
- break;
- }
- close(sg_fd); // sg driver will handle any commands "in flight"
- if (ruip)
- delete ruip;
-
- if (err || (k < npt)) {
- if (k < npt)
- pr2serr_lk("thread id=%d FAILed at iteration %d%s%s\n", id, k,
- (err ? ", Reason: " : ""), (err ? err : ""));
- else
- pr2serr_lk("thread id=%d FAILed on last%s%s\n", id,
- (err ? ", Reason: " : ""), (err ? err : ""));
- }
- n = pi_2_buff.size();
- if (n > 0)
- pr2serr_lk("thread id=%d Still %d elements in pi_2_buff map on "
- "exit\n", id, n);
- for (k = 0; ! free_lst.empty(); ++k) {
- lbp = free_lst.back();
- free_lst.pop_back();
- if (lbp)
- free(lbp);
- }
- if ((op->verbose > 2) && (k > 0))
- pr2serr_lk("thread id=%d Maximum number of READ/WRITEs queued: %d\n",
- id, k);
- async_starts += thr_async_starts;
- async_finishes += thr_async_finishes;
- start_eagain_count += thr_start_eagain_count;
- fin_eagain_count += thr_fin_eagain_count;
-}
-
-#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, 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];
- int open_flags = O_RDWR; /* O_EXCL | O_RDONLY fails with EPERM */
-
- if (! block)
- open_flags |= O_NONBLOCK;
- sg_fd = open(dev_name, open_flags);
- if (sg_fd < 0) {
- pr_errno_lk(errno, "%s: error opening file: %s", __func__, dev_name);
- 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) {
- pr_errno_lk(errno, "%s: Inquiry SG_IO ioctl error", __func__);
- 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:
- pr2serr_lk("Recovered error on INQUIRY, continuing\n");
- ok = 1;
- break;
- default: /* won't bother decoding other categories */
- {
- lock_guard<mutex> lg(console_mutex);
- 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;
-}
-
-/* Only allow ranges up to 2**32-1 upper limit, so READ CAPACITY(10)
- * sufficient. Return of 0 -> success, -1 -> failure, 2 -> try again */
-static int
-do_read_capacity(const char * dev_name, int block, unsigned int * last_lba,
- unsigned int * blk_sz)
-{
- int res, sg_fd;
- unsigned char rcCmdBlk [10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- unsigned char rcBuff[64];
- unsigned char sense_b[64];
- sg_io_hdr_t io_hdr;
- int open_flags = O_RDWR; /* O_EXCL | O_RDONLY fails with EPERM */
-
- if (! block)
- open_flags |= O_NONBLOCK;
- sg_fd = open(dev_name, open_flags);
- if (sg_fd < 0) {
- pr_errno_lk(errno, "%s: error opening file: %s", __func__, dev_name);
- return -1;
- }
- /* Prepare READ CAPACITY(10) command */
- memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
- io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(rcCmdBlk);
- io_hdr.mx_sb_len = sizeof(sense_b);
- io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr.dxfer_len = sizeof(rcBuff);
- io_hdr.dxferp = rcBuff;
- io_hdr.cmdp = rcCmdBlk;
- io_hdr.sbp = sense_b;
- io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */;
-
- if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
- pr_errno_lk(errno, "%s (SG_IO) error", __func__);
- close(sg_fd);
- return -1;
- }
- res = sg_err_category3(&io_hdr);
- if (SG_LIB_CAT_UNIT_ATTENTION == res) {
- lock_guard<mutex> lg(console_mutex);
- sg_chk_n_print3("read capacity", &io_hdr, 1);
- close(sg_fd);
- return 2; /* probably have another go ... */
- } else if (SG_LIB_CAT_CLEAN != res) {
- lock_guard<mutex> lg(console_mutex);
- sg_chk_n_print3("read capacity", &io_hdr, 1);
- close(sg_fd);
- return -1;
- }
- *last_lba = sg_get_unaligned_be32(&rcBuff[0]);
- *blk_sz = sg_get_unaligned_be32(&rcBuff[4]);
- close(sg_fd);
- return 0;
-}
-
-
-int
-main(int argc, char * argv[])
-{
- int k, n, c, res;
- int force = 0;
- int64_t ll;
- int num_threads = DEF_NUM_THREADS;
- char b[128];
- struct timespec start_tm, end_tm;
- struct opts_t opts;
- struct opts_t * op;
- const char * cp;
- const char * dev_name;
-
- op = &opts;
- memset(op, 0, sizeof(opts));
- op->direct = DEF_DIRECT;
- op->lba = DEF_LBA;
- op->hi_lba = 0;
- op->lb_sz = DEF_LB_SZ;
- op->maxq_per_thread = MAX_Q_PER_FD;
- op->num_per_thread = DEF_NUM_PER_THREAD;
- op->no_xfer = !! DEF_NO_XFER;
- op->verbose = 0;
- op->wait_ms = DEF_WAIT_MS;
- op->c2e = SCSI_TUR;
- op->blqd = BLQ_DEFAULT;
- op->block = !! DEF_BLOCKING;
- op->myqd = MYQD_HIGH;
- page_size = sysconf(_SC_PAGESIZE);
-
- while (1) {
- int option_index = 0;
-
- c = getopt_long(argc, argv, "dfhl:M:n:Nq:Q:Rs:St:TvVw:W",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'd':
- op->direct = 1;
- break;
- case 'f':
- force = true;
- break;
- case 'h':
- case '?':
- usage();
- return 0;
- case 'l':
- if (isdigit(*optarg)) {
- ll = sg_get_llnum(optarg);
- if (-1 == ll) {
- pr2serr_lk("could not decode lba\n");
- return 1;
- } else
- op->lba = (uint64_t)ll;
- cp = strchr(optarg, ',');
- if (cp) {
- if (0 == strcmp("-1", cp + 1))
- op->hi_lba = UINT_MAX;
- else {
- ll = sg_get_llnum(cp + 1);
- if ((-1 == ll) || (ll > UINT_MAX)) {
- pr2serr_lk("could not decode hi_lba, or > "
- "UINT_MAX\n");
- return 1;
- } else
- op->hi_lba = (unsigned int)ll;
- }
- }
- } else {
- pr2serr_lk("--lba= expects a number\n");
- return 1;
- }
- break;
- case 'M':
- if (isdigit(*optarg)) {
- n = atoi(optarg);
- if ((n < 1) || (n > MAX_Q_PER_FD)) {
- pr2serr_lk("-M expects a value from 1 to %d\n",
- MAX_Q_PER_FD);
- return 1;
- }
- op->maxq_per_thread = n;
- } else {
- pr2serr_lk("--maxqpt= expects a number\n");
- return 1;
- }
- break;
- case 'n':
- if (isdigit(*optarg))
- op->num_per_thread = sg_get_num(optarg);
- else {
- pr2serr_lk("--numpt= expects a number\n");
- return 1;
- }
- break;
- case 'N':
- op->no_xfer = true;
- break;
- case 'q':
- if (isdigit(*optarg)) {
- n = atoi(optarg);
- if (0 == n)
- op->blqd = BLQ_AT_HEAD;
- else if (1 == n)
- op->blqd = BLQ_AT_TAIL;
- } else {
- pr2serr_lk("--qat= expects a number: 0 or 1\n");
- return 1;
- }
- break;
- case 'Q':
- if (isdigit(*optarg)) {
- n = atoi(optarg);
- if (0 == n)
- op->myqd = MYQD_LOW;
- else if (1 == n)
- op->myqd = MYQD_MEDIUM;
- else if (2 == n)
- op->myqd = MYQD_HIGH;
- } else {
- pr2serr_lk("--qfav= expects a number: 0, 1 or 2\n");
- return 1;
- }
- break;
- case 'R':
- op->c2e = SCSI_READ16;
- break;
- case 's':
- if (isdigit(*optarg)) {
- op->lb_sz = atoi(optarg);
- if (op->lb_sz < 256) {
- cerr << "Strange lb_sz, using 256" << endl;
- op->lb_sz = 256;
- }
- } else {
- pr2serr_lk("--szlb= expects a number\n");
- return 1;
- }
- break;
- case 'S':
- ++op->stats;
- break;
- case 't':
- if (isdigit(*optarg))
- num_threads = atoi(optarg);
- else {
- pr2serr_lk("--tnum= expects a number\n");
- return 1;
- }
- break;
- case 'T':
- op->c2e = SCSI_TUR;
- break;
- case 'v':
- ++op->verbose;
- break;
- case 'V':
- pr2serr_lk("version: %s\n", version_str);
- return 0;
- case 'w':
- if ((isdigit(*optarg) || ('-' == *optarg))) {
- if ('-' == *optarg)
- op->wait_ms = - atoi(optarg + 1);
- else
- op->wait_ms = atoi(optarg);
- } else {
- pr2serr_lk("--wait= expects a number\n");
- return 1;
- }
- break;
- case 'W':
- op->c2e = SCSI_WRITE16;
- break;
- default:
- pr2serr_lk("unrecognised option code 0x%x ??\n", c);
- usage();
- return 1;
- }
- }
- if (optind < argc) {
- if (optind < argc) {
- for (; optind < argc; ++optind)
- op->dev_names.push_back(argv[optind]);
- }
- }
-
- if (0 == op->dev_names.size()) {
- usage();
- return 1;
- }
- if (op->hi_lba && (op->lba > op->hi_lba)) {
- cerr << "lba,hi_lba range is illegal" << endl;
- return 1;
- }
-
- try {
- struct stat a_stat;
-
- for (k = 0; k < (int)op->dev_names.size(); ++k) {
- dev_name = op->dev_names[k];
- if (stat(dev_name, &a_stat) < 0) {
- snprintf(b, sizeof(b), "could not stat() %s", dev_name);
- perror(b);
- return 1;
- }
- if (! S_ISCHR(a_stat.st_mode)) {
- pr2serr_lk("%s should be a sg device which is a char "
- "device. %s\n", dev_name, dev_name);
- pr2serr_lk("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, op->block, b, sizeof(b));
- if (res) {
- pr2serr_lk("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)) {
- pr2serr_lk("Since this utility may write to LBAs, "
- "only devices with the\n"
- "product ID 'scsi_debug' accepted. Use '-f' "
- "to override.\n");
- return 2;
- }
- }
- if (UINT_MAX == op->hi_lba) {
- unsigned int last_lba;
- unsigned int blk_sz;
-
- res = do_read_capacity(dev_name, op->block, &last_lba,
- &blk_sz);
- if (2 == res)
- res = do_read_capacity(dev_name, op->block, &last_lba,
- &blk_sz);
- if (res) {
- pr2serr_lk("READ CAPACITY(10) failed on %s\n", dev_name);
- return 1;
- }
- op->hi_lbas.push_back(last_lba);
- if (blk_sz != (unsigned int)op->lb_sz)
- pr2serr_lk(">>> warning: Logical block size (%d) of %s\n"
- " differs from command line option (or "
- "default)\n", blk_sz, dev_name);
- }
- }
-
- start_tm.tv_sec = 0;
- start_tm.tv_nsec = 0;
- if (clock_gettime(CLOCK_MONOTONIC, &start_tm) < 0)
- perror("clock_gettime failed");
-
- vector<thread *> vt;
-
- /* start multi-threaded section */
- for (k = 0; k < num_threads; ++k) {
- thread * tp = new thread {work_thread, k, op};
- 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();
- /* end multi-threaded section, just this main thread left */
-
- for (k = 0; k < (int)vt.size(); ++k)
- delete vt[k];
-
- n = uniq_pack_id.load() - 1;
- if ((n > 0) && (0 == clock_gettime(CLOCK_MONOTONIC, &end_tm))) {
- struct timespec res_tm;
- double a, b;
-
- res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
- res_tm.tv_nsec = end_tm.tv_nsec - start_tm.tv_nsec;
- if (res_tm.tv_nsec < 0) {
- --res_tm.tv_sec;
- res_tm.tv_nsec += 1000000000;
- }
- a = res_tm.tv_sec;
- a += (0.000001 * (res_tm.tv_nsec / 1000));
- b = (double)n;
- if (a > 0.000001) {
- printf("Time to complete %d commands was %d.%06d seconds\n",
- n, (int)res_tm.tv_sec, (int)(res_tm.tv_nsec / 1000));
- printf("Implies %.0f IOPS\n", (b / a));
- }
- }
-
- if (op->verbose || op->stats) {
- cout << "Number of async_starts: " << async_starts.load() << endl;
- cout << "Number of async_finishes: " << async_finishes.load() <<
- endl;
- cout << "Last pack_id: " << n << endl;
- cout << "Number of EBUSYs: " << ebusy_count.load() << endl;
- cout << "Number of start EAGAINs: " << start_eagain_count.load()
- << endl;
- cout << "Number of finish EAGAINs: " << fin_eagain_count.load()
- << 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_context.cpp b/examples/sg_tst_context.cpp
deleted file mode 100644
index 6ac99354..00000000
--- a/examples/sg_tst_context.cpp
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * 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.02 20140828";
-static const char * util_name = "sg_tst_context";
-
-/* This is a test program for checking that file handles keep their
- * context properly when sent (synchronous) SCSI pass-through commands.
- * A disk device is assumed and even-numbered threads send TEST UNIT
- * READY commands while odd-numbered threads send alternating START STOP
- * UNIT commands (i.e. start then stop then start, etc). The point is to
- * check the results to make sure that they don't get the other command's
- * response. For example a START STOP UNIT command should not see a "not
- * ready" sense key.
- *
- * 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 found in Fedora
- * 19 and Ubuntu 13.10 .
- *
- * 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_context 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_context
- * sg_tst_context.cpp
- * Alternatively use 'make -f Makefile.cplus sg_tst_context'
- *
- */
-
-using namespace std;
-using namespace std::chrono;
-
-#define DEF_NUM_PER_THREAD 200
-#define DEF_NUM_THREADS 2
-
-#define EBUFF_SZ 256
-
-
-static mutex count_mutex;
-static mutex console_mutex;
-static unsigned int even_notreadys;
-static unsigned int odd_notreadys;
-static unsigned int ebusy_count;
-
-
-static void
-usage(void)
-{
- printf("Usage: %s [-e] [-h] [-n <n_per_thr>] [-N] [-R] [-s]\n"
- " [-t <num_thrs>] [-V] <disk_device>\n",
- util_name);
- printf(" where\n");
- printf(" -e use O_EXCL on open (def: don't)\n");
- printf(" -h print this usage message then exit\n");
- printf(" -n <n_per_thr> number of loops per thread "
- "(def: %d)\n", DEF_NUM_PER_THREAD);
- printf(" -N use O_NONBLOCK on open (def: don't)\n");
- printf(" -R make sure device in ready (started) "
- "state after\n"
- " test (do extra iteration if "
- "necessary)\n");
- printf(" -s share an open file handle (def: one "
- "per thread)\n");
- printf(" -t <num_thrs> number of threads (def: %d)\n",
- DEF_NUM_THREADS);
- printf(" -V print version number then exit\n\n");
- printf("Test if file handles keep context through to their responses. "
- "Sends\nTEST UNIT READY commands on even threads (origin 0) and "
- "START STOP\nUNIT commands on odd threads. Expect NOT READY "
- "sense keys only\nfrom the even threads (i.e from TUR)\n");
-}
-
-static int
-pt_err(int res)
-{
- 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);
- return (res < 0) ? res : -EPERM /* -1 */;
-}
-
-static int
-pt_cat_no_good(int cat, struct sg_pt_base * ptp, const unsigned char * sbp)
-{
- int slen;
- char b[256];
- const int bl = (int)sizeof(b);
- const char * cp = NULL;
-
- b[0] = '\0';
- 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);
- cp = " scsi status: %s\n";
- break;
- case SCSI_PT_RESULT_SENSE:
- slen = get_scsi_pt_sense_len(ptp);
- sg_get_sense_str("", sbp, slen, 1, bl, b);
- cp = "%s\n";
- break;
- case SCSI_PT_RESULT_TRANSPORT_ERR:
- get_scsi_pt_transport_err_str(ptp, bl, b);
- cp = " transport: %s\n";
- break;
- case SCSI_PT_RESULT_OS_ERR:
- get_scsi_pt_os_err_str(ptp, bl, b);
- cp = " os: %s\n";
- break;
- default:
- cp = " unknown pt result category (%d)\n";
- break;
- }
- if (cp) {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, cp, b);
- }
- return -EIO /* -5 */;
-}
-
-#define TUR_CMD_LEN 6
-#define SSU_CMD_LEN 6
-#define NOT_READY SG_LIB_CAT_NOT_READY
-
-/* Returns 0 for good, 1024 for a sense key of NOT_READY, or a negative
- * errno */
-static int
-do_tur(int pt_fd, int id)
-{
- int slen, res, cat;
- struct sg_pt_base * ptp = NULL;
- unsigned char turCmdBlk [TUR_CMD_LEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
- unsigned char sense_buffer[64];
-
- ptp = construct_scsi_pt_obj();
- set_scsi_pt_cdb(ptp, turCmdBlk, sizeof(turCmdBlk));
- set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
- res = do_scsi_pt(ptp, pt_fd, 20 /* secs timeout */, 1);
- if (res) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "TEST UNIT READY do_scsi_pt() submission error, "
- "id=%d\n", id);
- }
- res = pt_err(res);
- goto err;
- }
- cat = get_scsi_pt_result_category(ptp);
- if (SCSI_PT_RESULT_GOOD != cat) {
- slen = get_scsi_pt_sense_len(ptp);
- if ((SCSI_PT_RESULT_SENSE == cat) &&
- (NOT_READY == sg_err_category_sense(sense_buffer, slen))) {
- res = 1024;
- goto err;
- }
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "TEST UNIT READY do_scsi_pt() category problem, "
- "id=%d\n", id);
- }
- res = pt_cat_no_good(cat, ptp, sense_buffer);
- goto err;
- }
- res = 0;
-err:
- if (ptp)
- destruct_scsi_pt_obj(ptp);
- return res;
-}
-
-/* Returns 0 for good, 1024 for a sense key of NOT_READY, or a negative
- * errno */
-static int
-do_ssu(int pt_fd, int id, bool start)
-{
- int slen, res, cat;
- struct sg_pt_base * ptp = NULL;
- unsigned char ssuCmdBlk [SSU_CMD_LEN] = {0x1b, 0x0, 0x0, 0x0, 0x0, 0x0};
- unsigned char sense_buffer[64];
-
- if (start)
- ssuCmdBlk[4] |= 0x1;
- ptp = construct_scsi_pt_obj();
- set_scsi_pt_cdb(ptp, ssuCmdBlk, sizeof(ssuCmdBlk));
- set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
- res = do_scsi_pt(ptp, pt_fd, 40 /* secs timeout */, 1);
- if (res) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "START STOP UNIT do_scsi_pt() submission error, "
- "id=%d\n", id);
- }
- res = pt_err(res);
- goto err;
- }
- cat = get_scsi_pt_result_category(ptp);
- if (SCSI_PT_RESULT_GOOD != cat) {
- slen = get_scsi_pt_sense_len(ptp);
- if ((SCSI_PT_RESULT_SENSE == cat) &&
- (NOT_READY == sg_err_category_sense(sense_buffer, slen))) {
- res = 1024;
- goto err;
- }
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "START STOP UNIT do_scsi_pt() category problem, "
- "id=%d\n", id);
- }
- res = pt_cat_no_good(cat, ptp, sense_buffer);
- goto err;
- }
- res = 0;
-err:
- if (ptp)
- destruct_scsi_pt_obj(ptp);
- return res;
-}
-
-static void
-work_thread(const char * dev_name, int id, int num, bool share,
- int pt_fd, int nonblock, int oexcl, bool ready_after)
-{
- unsigned int thr_even_notreadys = 0;
- unsigned int thr_odd_notreadys = 0;
- unsigned int thr_ebusy_count = 0;
- int k;
- int res = 0;
- bool started = true;
- char ebuff[EBUFF_SZ];
-
- {
- lock_guard<mutex> lg(console_mutex);
-
- cerr << "Enter work_thread id=" << id << " num=" << num << " share="
- << share << endl;
- }
- if (! share) {
- int open_flags = O_RDWR;
-
- if (nonblock)
- open_flags |= O_NONBLOCK;
- if (oexcl)
- open_flags |= O_EXCL;
- while (((pt_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0)
- && (-EBUSY == pt_fd)) {
- ++thr_ebusy_count;
- this_thread::yield(); // give other threads a chance
- }
- if (pt_fd < 0) {
- snprintf(ebuff, EBUFF_SZ, "work_thread id=%d: error opening: %s",
- id, dev_name);
- perror(ebuff);
- return;
- }
- if (thr_ebusy_count) {
- lock_guard<mutex> lg(count_mutex);
-
- ebusy_count += thr_ebusy_count;
- }
- }
- for (k = 0; k < num; ++k) {
- if (0 == (id % 2)) {
- res = do_tur(pt_fd, id);
- if (1024 == res) {
- ++thr_even_notreadys;
- res = 0;
- }
- } else {
- started = (0 == (k % 2));
- res = do_ssu(pt_fd, id, started);
- if (1024 == res) {
- ++thr_odd_notreadys;
- res = 0;
- }
- }
- if (res)
- break;
- if (ready_after && (! started))
- do_ssu(pt_fd, id, true);
- }
- if (! share)
- scsi_pt_close_device(pt_fd);
-
- {
- lock_guard<mutex> lg(count_mutex);
-
- even_notreadys += thr_even_notreadys;
- odd_notreadys += thr_odd_notreadys;
- }
-
- {
- lock_guard<mutex> lg(console_mutex);
-
- if (k < num)
- cerr << "thread id=" << id << " FAILed at iteration: " << k
- << " [negated errno: " << res << "]\n";
- else
- cerr << "thread id=" << id << " normal exit" << '\n';
- }
-}
-
-
-int
-main(int argc, char * argv[])
-{
- int k;
- int pt_fd = -1;
- int oexcl = 0;
- int nonblock = 0;
- int num_per_thread = DEF_NUM_PER_THREAD;
- bool ready_after = false;
- bool share = false;
- int num_threads = DEF_NUM_THREADS;
- char * dev_name = NULL;
- char ebuff[EBUFF_SZ];
-
- for (k = 1; k < argc; ++k) {
- if (0 == memcmp("-e", argv[k], 2))
- ++oexcl;
- else if (0 == memcmp("-h", argv[k], 2)) {
- usage();
- return 0;
- } 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("-N", argv[k], 2))
- ++nonblock;
- else if (0 == memcmp("-R", argv[k], 2))
- ready_after = true;
- else if (0 == memcmp("-s", argv[k], 2))
- share = true;
- 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 (*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 {
- if (share) {
- int open_flags = O_RDWR;
-
- if (nonblock)
- open_flags |= O_NONBLOCK;
- if (oexcl)
- open_flags |= O_EXCL;
- while (((pt_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0)
- && (-EBUSY == pt_fd)) {
- ++ebusy_count;
- sleep(0); // process yield ??
- }
- if (pt_fd < 0) {
- snprintf(ebuff, EBUFF_SZ, "main: error opening: %s",
- dev_name);
- perror(ebuff);
- return 1;
- }
- }
-
- vector<thread *> vt;
-
- for (k = 0; k < num_threads; ++k) {
- thread * tp = new thread {work_thread, dev_name, k, num_per_thread,
- share, pt_fd, nonblock, oexcl,
- ready_after};
- 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];
-
- if (share)
- scsi_pt_close_device(pt_fd);
-
- cout << "Expected not_readys on TEST UNIT READY: " << even_notreadys
- << endl;
- cout << "UNEXPECTED not_readys on START STOP UNIT: "
- << odd_notreadys << endl;
- if (ebusy_count)
- cout << "Number of EBUSYs (on open): " << ebusy_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_excl.cpp b/examples/sg_tst_excl.cpp
deleted file mode 100644
index 5b4e6956..00000000
--- a/examples/sg_tst_excl.cpp
+++ /dev/null
@@ -1,678 +0,0 @@
-/*
- * Copyright (c) 2013-2014 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.09 20140828";
-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
- * or use the C++ Makefile in that directory:
- * make -f Makefile.cplus sg_tst_excl
- *
- * 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 READ16_REPLY_LEN 512
-#define READ16_CMD_LEN 16
-#define WRITE16_REPLY_LEN 512
-#define WRITE16_CMD_LEN 16
-
-/* 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) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- perror("do_rd_inc_wr_twice: write(sg, READ_16)");
- }
- close(sg_fd);
- return -1;
- }
- pt2 = pt;
- if (write(sg_fd, &pt2, sizeof(pt2)) < 0) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- perror("do_rd_inc_wr_twice: write(sg, READ_16) 2");
- }
- 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) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- perror("do_rd_inc_wr_twice: read(sg, READ_16)");
- }
- 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:
- {
- 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_chk_n_print3("READ_16 command error", &pt, 1);
- }
- 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) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- perror("do_rd_inc_wr_twice: read(sg, READ_16) 2");
- }
- 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:
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "Recovered error on READ_16, continuing "
- "2\n");
- }
- ok = 1;
- break;
- default: /* won't bother decoding other categories */
- {
- lock_guard<mutex> lg(console_mutex);
-
- sg_chk_n_print3("READ_16 command error 2", &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 */
- pt.pack_id = id;
-
- if (ioctl(sg_fd, SG_IO, &pt) < 0) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- 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:
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "Recovered error on WRITE_16, continuing\n");
- }
- ok = 1;
- break;
- default: /* won't bother decoding other categories */
- {
- lock_guard<mutex> lg(console_mutex);
-
- 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). 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;
-
- {
- lock_guard<mutex> lg(console_mutex);
-
- cerr << "Enter work_thread id=" << id << " excl=" << excl << " 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 (res < 0)
- break;
- if (res)
- ++thr_odd_count;
- }
- {
- lock_guard<mutex> lg(console_mutex);
-
- if (k < num)
- cerr << "thread id=" << id << " FAILed at iteration: " << k <<
- '\n';
- else
- cerr << "thread id=" << id << " normal exit" << '\n';
- }
- {
- lock_guard<mutex> lg(odd_count_mutex);
-
- odd_count += thr_odd_count;
- ebusy_count += thr_ebusy_count;
- eagain_count += thr_eagain_count;
- }
-}
-
-
-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/examples/sg_tst_excl2.cpp b/examples/sg_tst_excl2.cpp
deleted file mode 100644
index 24d3677b..00000000
--- a/examples/sg_tst_excl2.cpp
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * Copyright (c) 2013-2014 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.07 20140828";
-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 ebusy_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] "
- "<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\n");
- printf("Test O_EXCL open flag with pass-through drivers. Each "
- "open/close cycle with\nthe O_EXCL flag does a double increment "
- "on lba (using its first 4 bytes).\n");
-}
-
-/* Assumed a lock (mutex) held when pt_err() is called */
-static int
-pt_err(int res)
-{
- 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);
- return -1;
-}
-
-/* Assumed a lock (mutex) held when pt_cat_no_good() is called */
-static int
-pt_cat_no_good(int cat, 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;
- }
- 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 and spins if busy (i.e. gets EBUSY), sleeping for
- * wait_ms milliseconds if wait_ms is positive. 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 block,
- int excl, int wait_ms, unsigned int & ebusys)
-{
- 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;
-
- 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 = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
- (-EBUSY == sg_fd)) {
- ++ebusys;
- 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);
- {
- lock_guard<mutex> lg(console_mutex);
-
- 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) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "READ_16 do_scsi_pt() submission error\n");
- res = pt_err(res);
- }
- goto err;
- }
- cat = get_scsi_pt_result_category(ptp);
- if (SCSI_PT_RESULT_GOOD != cat) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "READ_16 do_scsi_pt() category problem\n");
- res = pt_cat_no_good(cat, ptp, sense_buffer);
- }
- goto err;
- }
-
- 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) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "WRITE_16 do_scsi_pt() submission error\n");
- res = pt_err(res);
- }
- goto err;
- }
- cat = get_scsi_pt_result_category(ptp);
- if (SCSI_PT_RESULT_GOOD != cat) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n");
- res = pt_cat_no_good(cat, ptp, sense_buffer);
- }
- goto err;
- }
- }
-err:
- 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). 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, 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];
- int open_flags = O_RDWR; /* since O_EXCL | O_RDONLY gives EPERM */
-
- if (! block)
- open_flags |= O_NONBLOCK;
- while (((sg_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
- (-EBUSY == sg_fd)) {
- ++ebusys;
- 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) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "INQUIRY do_scsi_pt() submission error\n");
- res = pt_err(res);
- }
- goto err;
- }
- cat = get_scsi_pt_result_category(ptp);
- if (SCSI_PT_RESULT_GOOD != cat) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "INQUIRY do_scsi_pt() category problem\n");
- res = pt_cat_no_good(cat, ptp, sense_buffer);
- }
- goto err;
- }
-
- /* 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';
- }
- }
-err:
- 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 block,
- int excl, int num, int wait_ms)
-{
- unsigned int thr_odd_count = 0;
- unsigned int thr_ebusy_count = 0;
- int k, res;
-
- {
- lock_guard<mutex> lg(console_mutex);
-
- cerr << "Enter work_thread id=" << id << " excl=" << excl << " block="
- << block << endl;
- }
- for (k = 0; k < num; ++k) {
- res = do_rd_inc_wr_twice(dev_name, lba, block, excl, wait_ms,
- thr_ebusy_count);
- if (res < 0)
- break;
- if (res)
- ++thr_odd_count;
- }
- {
- lock_guard<mutex> lg(console_mutex);
-
- if (k < num)
- cerr << "thread id=" << id << " FAILed at iteration: " << k <<
- '\n';
- else
- cerr << "thread id=" << id << " normal exit" << '\n';
- }
-
- {
- lock_guard<mutex> lg(odd_count_mutex);
-
- odd_count += thr_odd_count;
- ebusy_count += thr_ebusy_count;
- }
-}
-
-
-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 exclude_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("-x", argv[k], 2))
- ++exclude_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 {
-
- 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 = ((0 == k) && exclude_o_excl) ? 0 : 1;
-
- thread * tp = new thread {work_thread, dev_name, lba, k, block,
- 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: " << ebusy_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_excl3.cpp b/examples/sg_tst_excl3.cpp
deleted file mode 100644
index 6b6b880b..00000000
--- a/examples/sg_tst_excl3.cpp
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
- * Copyright (c) 2013-2014 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.05 20140828";
-static const char * util_name = "sg_tst_excl3";
-
-/* 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 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
- * (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 found in Fedora
- * 19 and Ubuntu 13.10 .
- *
- * 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_excl3 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_excl3
- * sg_tst_excl3.cpp
- * Alternatively use 'make -f Makefile.cplus sg_tst_excl3'
- *
- * 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. It works on standard block driver (e.g. /dev/sdc)
- * in lk 3.10.4 (most of the time). It fails on bsg driver in lk 3.10.4
- * because it ignores the O_EXCL flag (and that is unlikely to change in
- * the short term).
- *
- */
-
-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 void
-usage(void)
-{
- printf("Usage: %s [-b] [-f] [-h] [-l <lba>] [-n <n_per_thr>]\n"
- " [-R] [-t <num_thrs>] [-V] [-w <wait_ms>] "
- "[-x]\n"
- " <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(" -R all readers; so first thread (id=0) "
- "just reads\n");
- 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 first thread)\n\n");
- printf("Test O_EXCL open flag with pass-through drivers. First thread "
- "(id=0) does\nopen/close cycle with the O_EXCL flag then does a "
- "double increment on\nlba (using its first 4 bytes). Remaining "
- "theads read (without\nO_EXCL flag on open) and check the "
- "value is even.\n");
-}
-
-/* Assumed a lock (mutex) held when pt_err() is called */
-static int
-pt_err(int res)
-{
- 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);
- return -1;
-}
-
-/* Assumed a lock (mutex) held when pt_cat_no_good() is called */
-static int
-pt_cat_no_good(int cat, 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;
- }
- 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 and spins if busy (i.e. gets EBUSY), sleeping for
- * wait_ms milliseconds if wait_ms is positive. 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, int read_only, unsigned int lba,
- int block, int excl, int wait_ms, unsigned int & ebusys)
-{
- 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;
-
- 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 = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
- (-EBUSY == sg_fd)) {
- ++ebusys;
- 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);
- {
- lock_guard<mutex> lg(console_mutex);
-
- 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) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "READ_16 do_scsi_pt() submission error\n");
- res = pt_err(res);
- }
- goto err;
- }
- cat = get_scsi_pt_result_category(ptp);
- if (SCSI_PT_RESULT_GOOD != cat) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "READ_16 do_scsi_pt() category problem\n");
- res = pt_cat_no_good(cat, ptp, sense_buffer);
- }
- goto err;
- }
-
- 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));
-
- 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 ??
-
- if (read_only)
- break;
- ++u;
- lb[0] = (u >> 24) & 0xff;
- lb[1] = (u >> 16) & 0xff;
- lb[2] = (u >> 8) & 0xff;
- lb[3] = u & 0xff;
-
- /* 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) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "WRITE_16 do_scsi_pt() submission error\n");
- res = pt_err(res);
- }
- goto err;
- }
- cat = get_scsi_pt_result_category(ptp);
- if (SCSI_PT_RESULT_GOOD != cat) {
- {
- lock_guard<mutex> lg(console_mutex);
-
- fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n");
- res = pt_cat_no_good(cat, ptp, sense_buffer);
- }
- goto err;
- }
- }
-err:
- 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). 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, 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];
- int open_flags = O_RDWR; /* since O_EXCL | O_RDONLY gives EPERM */
-
- if (! block)
- open_flags |= O_NONBLOCK;
- while (((sg_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
- (-EBUSY == sg_fd)) {
- ++ebusys;
- 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() submission error\n");
- res = pt_err(res);
- goto err;
- }
- cat = get_scsi_pt_result_category(ptp);
- if (SCSI_PT_RESULT_GOOD != cat) {
- fprintf(stderr, "INQUIRY do_scsi_pt() category problem\n");
- res = pt_cat_no_good(cat, ptp, sense_buffer);
- goto err;
- }
-
- /* 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';
- }
- }
-err:
- if (ptp)
- destruct_scsi_pt_obj(ptp);
- close(sg_fd);
- return res;
-}
-
-static void
-work_thread(const char * dev_name, unsigned int lba, int id, int block,
- int excl, bool all_readers, int num, int wait_ms)
-{
- unsigned int thr_odd_count = 0;
- unsigned int thr_ebusy_count = 0;
- int k, res;
- int reader = ((id > 0) || (all_readers));
-
- {
- lock_guard<mutex> lg(console_mutex);
-
- cerr << "Enter work_thread id=" << id << " excl=" << excl << " block="
- << block << " reader=" << reader << endl;
- }
- for (k = 0; k < num; ++k) {
- res = do_rd_inc_wr_twice(dev_name, reader, lba, block, excl,
- wait_ms, thr_ebusy_count);
- if (res < 0)
- break;
- if (res)
- ++thr_odd_count;
- }
- {
- lock_guard<mutex> lg(console_mutex);
-
- if (k < num)
- cerr << "thread id=" << id << " FAILed at iteration: " << k
- << '\n';
- else
- cerr << "thread id=" << id << " normal exit" << '\n';
- }
-
- {
- lock_guard<mutex> lg(odd_count_mutex);
-
- odd_count += thr_odd_count;
- ebusy_count += thr_ebusy_count;
- }
-}
-
-
-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;
- bool all_readers = false;
- int num_threads = DEF_NUM_THREADS;
- int wait_ms = DEF_WAIT_MS;
- int exclude_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("-R", argv[k], 2))
- all_readers = true;
- 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_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 {
-
- 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 = ((0 == k) && (! exclude_o_excl)) ? 1 : 0;
-
- thread * tp = new thread {work_thread, dev_name, lba, k, block,
- excl, all_readers, 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: " << ebusy_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;
-}