aboutsummaryrefslogtreecommitdiff
path: root/testing
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2018-12-17 21:16:36 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2018-12-17 21:16:36 +0000
commit18c9852db91032afe4bedd85857d558402b360e2 (patch)
treee1f3ef6efab1d17665cb19e85d43439888291a6e /testing
parentd906e8cb000e95d9260d1833d3692c1c763e489a (diff)
downloadsg3_utils-18c9852db91032afe4bedd85857d558402b360e2.tar.gz
testing: improve sgs_dd, sg_tst_async and sg_tst_ioctl sg v4 test utilities
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@799 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'testing')
-rw-r--r--testing/sg_tst_async.cpp338
-rw-r--r--testing/sg_tst_ioctl.c519
-rw-r--r--testing/sgs_dd.c24
-rw-r--r--testing/uapi_sg.h16
4 files changed, 677 insertions, 220 deletions
diff --git a/testing/sg_tst_async.cpp b/testing/sg_tst_async.cpp
index a6ac65ee..d16b22fc 100644
--- a/testing/sg_tst_async.cpp
+++ b/testing/sg_tst_async.cpp
@@ -82,7 +82,7 @@
#include "sg_pt.h"
#include "sg_cmds.h"
-static const char * version_str = "1.22 20181207";
+static const char * version_str = "1.23 20181216";
static const char * util_name = "sg_tst_async";
/* This is a test program for checking the async usage of the Linux sg
@@ -185,7 +185,9 @@ struct opts_t {
bool no_xfer;
bool pack_id_force;
bool sg_vn_ge_30901;
+ bool submit;
bool verbose_given;
+ bool v4;
bool version_given;
int maxq_per_thread;
int num_per_thread;
@@ -245,6 +247,7 @@ private:
};
static struct option long_options[] = {
+ {"v4", no_argument, 0, '4'},
{"cmd-time", no_argument, 0, 'c'},
{"cmd_time", no_argument, 0, 'c'},
{"direct", no_argument, 0, 'd'},
@@ -265,8 +268,9 @@ static struct option long_options[] = {
{"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'},
+ {"submit", no_argument, 0, 'u'},
+ {"szlb", required_argument, 0, 's'},
{"tnum", required_argument, 0, 't'},
{"tur", no_argument, 0, 'T'},
{"verbose", no_argument, 0, 'v'},
@@ -285,11 +289,12 @@ usage(void)
"[--mmap-io]\n"
" [--numpt=NPT] [--noxfer] [--pack-id] "
"[--qat=AT]\n"
- " [-qfav=FAV] [--read] [--szlb=LB[,NLBS]] "
- "[--stats]\n"
- " [--tnum=NT] [--tur] [--verbose] [--version] "
- "[--wait=MS]\n"
- " [--write] <sg_disk_device>*\n",
+ " [-qfav=FAV] [--read] [--stats] [--submit]\n"
+ " [--szlb=LB[,NLBS]] [--tnum=NT] [--tur] "
+ "[--v4]\n"
+ " [--verbose] [--version] [--wait=MS] "
+ "[--write]\n"
+ " <sg_disk_device>*\n",
util_name);
printf(" where\n");
printf(" --cmd-time|-c calculate per command average time (ns)\n");
@@ -324,13 +329,17 @@ usage(void)
" FAV=2: favour submissions (larger q, "
"default)\n");
printf(" --read|-R do READs (def: TUR)\n");
+ printf(" --stats|-S show more statistics on completion\n");
+ printf(" --submit|-u use SG_IOSUBMIT+SG_IORECEIVE instead of "
+ "write+read\n");
printf(" --szlb=LB[,NLBS]| LB is logical block size (def: 512)\n");
printf(" -s LB[,NLBS] NLBS is number of logical blocks (def: "
"1)\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(" --v4|-4 use sg v4 interface (def: v3). If given "
+ "sets --submit\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: "
@@ -410,11 +419,12 @@ get_urandom_uint(void)
* not done due to queue data size limit struck. */
static int
start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
- uint8_t * lbp, int xfer_bytes, int flags,
+ uint8_t * lbp, int xfer_bytes, int flags, bool submit,
unsigned int & eagains, unsigned int & ebusy,
unsigned int & e2big, unsigned int & edom)
{
struct sg_io_hdr pt;
+ struct sg_io_v4 p4t;
uint8_t turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0};
uint8_t r16CmdBlk[READ16_CMD_LEN] =
{0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
@@ -422,46 +432,56 @@ start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
{0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
uint8_t sense_buffer[64];
const char * np = NULL;
-
- memset(&pt, 0, sizeof(pt));
+ struct sg_io_hdr * ptp;
+
+ if (submit) { /* nest a v3 interface inside a store for v4 */
+ memset(&p4t, 0, sizeof(p4t));
+ ptp = (struct sg_io_hdr *)&p4t; /* p4t is larger than pt */
+ } else {
+ ptp = &pt;
+ memset(ptp, 0, sizeof(*ptp));
+ }
switch (cmd2exe) {
case SCSI_TUR:
np = "TEST UNIT READY";
- pt.cmdp = turCmdBlk;
- pt.cmd_len = sizeof(turCmdBlk);
- pt.dxfer_direction = SG_DXFER_NONE;
+ ptp->cmdp = turCmdBlk;
+ ptp->cmd_len = sizeof(turCmdBlk);
+ ptp->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;
+ ptp->cmdp = r16CmdBlk;
+ ptp->cmd_len = sizeof(r16CmdBlk);
+ ptp->dxfer_direction = SG_DXFER_FROM_DEV;
+ ptp->dxferp = lbp;
+ ptp->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;
+ ptp->cmdp = w16CmdBlk;
+ ptp->cmd_len = sizeof(w16CmdBlk);
+ ptp->dxfer_direction = SG_DXFER_TO_DEV;
+ ptp->dxferp = lbp;
+ ptp->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) {
+ ptp->interface_id = 'S';
+ ptp->mx_sb_len = sizeof(sense_buffer);
+ ptp->sbp = sense_buffer; /* ignored .... */
+ ptp->timeout = DEF_TIMEOUT_MS;
+ ptp->pack_id = pack_id;
+ ptp->flags = flags;
+
+ for (int k = 0;
+ (submit ? ioctl(sg_fd, SG_IOSUBMIT, ptp) :
+ write(sg_fd, ptp, sizeof(*ptp)) < 0);
+ ++k) {
if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) {
this_thread::yield();
continue;
@@ -486,39 +506,212 @@ start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
static int
finish_sg3_cmd(int sg_fd, command2execute cmd2exe, bool pack_id_force,
- int & pack_id, int wait_ms, unsigned int & eagains,
- unsigned int & nanosecs)
+ int & pack_id, bool receive, int wait_ms,
+ unsigned int & eagains, unsigned int & nanosecs)
{
bool ok;
int res, k;
+ uint8_t sense_buffer[64];
+ const char * np = NULL;
struct sg_io_hdr pt;
+ struct sg_io_hdr * ptp;
+ struct sg_io_v4 p4t;
+
+ if (receive) { /* nest a v3 interface inside a store for v4 */
+ memset(&p4t, 0, sizeof(p4t));
+ ptp = (struct sg_io_hdr *)&p4t; /* p4t is larger than pt */
+ } else {
+ ptp = &pt;
+ memset(ptp, 0, sizeof(*ptp));
+ }
+ switch (cmd2exe) {
+ case SCSI_TUR:
+ np = "TEST UNIT READY";
+ ptp->dxfer_direction = SG_DXFER_NONE;
+ break;
+ case SCSI_READ16:
+ np = "READ(16)";
+ ptp->dxfer_direction = SG_DXFER_FROM_DEV;
+ break;
+ case SCSI_WRITE16:
+ np = "WRITE(16)";
+ ptp->dxfer_direction = SG_DXFER_TO_DEV;
+ break;
+ }
+ ptp->interface_id = 'S';
+ ptp->mx_sb_len = sizeof(sense_buffer);
+ ptp->sbp = sense_buffer;
+ ptp->timeout = DEF_TIMEOUT_MS;
+ /* if SG_SET_FORCE_PACK_ID, then need to set ptp->dxfer_direction */
+ ptp->pack_id = pack_id;
+
+ k = 0;
+ while ((((res = receive ? ioctl(sg_fd, SG_IORECEIVE, ptp) :
+ read(sg_fd, ptp, sizeof(*ptp)))) < 0) &&
+ (EAGAIN == errno)) {
+ ++eagains;
+ if (pack_id_force) {
+ ++k;
+ if (k > 10000) {
+ pr2serr_lk("%s: unable to find pack_id=%d\n", __func__,
+ pack_id);
+ return -1; /* crash out */
+ }
+ }
+ 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 = ptp->pack_id;
+ ok = false;
+ switch (sg_err_category3(ptp)) {
+ case SG_LIB_CAT_CLEAN:
+ ok = true;
+ break;
+ case SG_LIB_CAT_RECOVERED:
+ pr2serr_lk("%s: Recovered error on %s, continuing\n", __func__, np);
+ ok = true;
+ break;
+ default: /* won't bother decoding other categories */
+ {
+ lock_guard<mutex> lg(console_mutex);
+ sg_chk_n_print3(np, ptp, 1);
+ }
+ break;
+ }
+ if (ok)
+ nanosecs = ptp->duration;
+ return ok ? 0 : -1;
+}
+
+/* Returns 0 if command injected okay, return -1 for error and 2 for
+ * not done due to queue data size limit struck. */
+static int
+start_sg4_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
+ uint8_t * lbp, int xfer_bytes, int flags, bool submit,
+ unsigned int & eagains, unsigned int & ebusy,
+ unsigned int & e2big, unsigned int & edom)
+{
+ struct sg_io_v4 p4t;
+ uint8_t turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0};
+ uint8_t r16CmdBlk[READ16_CMD_LEN] =
+ {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+ uint8_t w16CmdBlk[WRITE16_CMD_LEN] =
+ {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
uint8_t sense_buffer[64];
const char * np = NULL;
+ struct sg_io_v4 * ptp;
- memset(&pt, 0, sizeof(pt));
+ if (! submit) {
+ pr2serr_lk("%s: logic error, submit must be true, isn't\n", __func__);
+ return -1;
+ }
+ ptp = &p4t;
+ memset(ptp, 0, sizeof(*ptp));
switch (cmd2exe) {
case SCSI_TUR:
np = "TEST UNIT READY";
- pt.dxfer_direction = SG_DXFER_NONE;
+ ptp->request = (uint64_t)turCmdBlk;
+ ptp->request_len = sizeof(turCmdBlk);
break;
case SCSI_READ16:
np = "READ(16)";
- pt.dxfer_direction = SG_DXFER_FROM_DEV;
+ if (lba > 0xffffffff)
+ sg_put_unaligned_be32(lba >> 32, &r16CmdBlk[2]);
+ sg_put_unaligned_be32(lba & 0xffffffff, &r16CmdBlk[6]);
+ ptp->request = (uint64_t)r16CmdBlk;
+ ptp->request_len = sizeof(r16CmdBlk);
+ ptp->din_xferp = (uint64_t)lbp;
+ ptp->din_xfer_len = xfer_bytes;
break;
case SCSI_WRITE16:
np = "WRITE(16)";
- pt.dxfer_direction = SG_DXFER_TO_DEV;
+ if (lba > 0xffffffff)
+ sg_put_unaligned_be32(lba >> 32, &w16CmdBlk[2]);
+ sg_put_unaligned_be32(lba & 0xffffffff, &w16CmdBlk[6]);
+ ptp->request = (uint64_t)w16CmdBlk;
+ ptp->request_len = sizeof(w16CmdBlk);
+ ptp->dout_xferp = (uint64_t)lbp;
+ ptp->dout_xfer_len = xfer_bytes;
break;
}
- pt.interface_id = 'S';
- pt.mx_sb_len = sizeof(sense_buffer);
- pt.sbp = sense_buffer;
- pt.timeout = DEF_TIMEOUT_MS;
- /* if SG_SET_FORCE_PACK_ID, then need to set pt.dxfer_direction */
- pt.pack_id = pack_id;
+ ptp->guard = 'Q';
+ ptp->max_response_len = sizeof(sense_buffer);
+ ptp->response = (uint64_t)sense_buffer; /* ignored .... */
+ ptp->timeout = DEF_TIMEOUT_MS;
+ ptp->request_extra = pack_id;
+ ptp->flags = flags;
+
+ for (int k = 0; ioctl(sg_fd, SG_IOSUBMIT, ptp) < 0; ++k) {
+ if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) {
+ this_thread::yield();
+ continue;
+ } else if (EAGAIN == errno) {
+ ++eagains;
+ this_thread::yield();
+ continue;
+ } else if (EBUSY == errno) {
+ ++ebusy;
+ this_thread::yield();
+ continue;
+ } else if (E2BIG == errno) {
+ ++e2big;
+ return 2;
+ } else if (EDOM == errno)
+ ++edom;
+ pr_errno_lk(errno, "%s: %s, pack_id=%d", __func__, np, pack_id);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+finish_sg4_cmd(int sg_fd, command2execute cmd2exe, bool pack_id_force,
+ int & pack_id, bool receive, int wait_ms,
+ unsigned int & eagains, unsigned int & nanosecs)
+{
+ bool ok;
+ int res, k;
+ uint8_t sense_buffer[64];
+ const char * np = NULL;
+ struct sg_io_v4 * ptp;
+ struct sg_io_v4 p4t;
+
+ if (! receive) {
+ pr2serr_lk("%s: logic error, receive must be true, isn't\n",
+ __func__);
+ return -1;
+ }
+ ptp = &p4t;
+ memset(ptp, 0, sizeof(*ptp));
+ switch (cmd2exe) {
+ case SCSI_TUR:
+ np = "TEST UNIT READY";
+ break;
+ case SCSI_READ16:
+ np = "READ(16)";
+ break;
+ case SCSI_WRITE16:
+ np = "WRITE(16)";
+ break;
+ }
+ ptp->guard = 'S';
+ ptp->max_response_len = sizeof(sense_buffer);
+ ptp->response = (uint64_t)sense_buffer;
+ ptp->timeout = DEF_TIMEOUT_MS;
+ /* if SG_SET_FORCE_PACK_ID, then need to set ptp->dxfer_direction */
+ ptp->request_extra = pack_id;
k = 0;
- while (((res = read(sg_fd, &pt, sizeof(pt))) < 0) &&
+ while ((((res = ioctl(sg_fd, SG_IORECEIVE, ptp))) < 0) &&
(EAGAIN == errno)) {
++eagains;
if (pack_id_force) {
@@ -541,9 +734,13 @@ finish_sg3_cmd(int sg_fd, command2execute cmd2exe, bool pack_id_force,
return -1;
}
/* now for the error processing */
- pack_id = pt.pack_id;
+ pack_id = ptp->request_extra;
ok = false;
- switch (sg_err_category3(&pt)) {
+ res = sg_err_category_new(ptp->device_status, ptp->transport_status,
+ ptp->driver_status,
+ (const uint8_t *)ptp->response,
+ ptp->response_len);
+ switch (res) {
case SG_LIB_CAT_CLEAN:
ok = true;
break;
@@ -554,12 +751,17 @@ finish_sg3_cmd(int sg_fd, command2execute cmd2exe, bool pack_id_force,
default: /* won't bother decoding other categories */
{
lock_guard<mutex> lg(console_mutex);
- sg_chk_n_print3(np, &pt, 1);
+
+ sg_linux_sense_print(np, ptp->device_status,
+ ptp->transport_status,
+ ptp->driver_status,
+ (const uint8_t *)ptp->response,
+ ptp->response_len, true);
}
break;
}
if (ok)
- nanosecs = pt.duration;
+ nanosecs = ptp->duration;
return ok ? 0 : -1;
}
@@ -870,10 +1072,15 @@ work_thread(int id, struct opts_t * op)
lba = 0;
if (vb > 4)
pr2serr_lk("t_id=%d: starting pack_id=%d\n", id, pack_id);
- res = start_sg3_cmd(sg_fd, op->c2e, pack_id, lba, lbp,
- op->lb_sz * op->num_lbs, sg_flags,
- thr_start_eagain_count, thr_start_ebusy_count,
- thr_start_e2big_count, thr_start_edom_count);
+ res = (op->v4) ?
+ start_sg4_cmd(sg_fd, op->c2e, pack_id, lba, lbp,
+ op->lb_sz * op->num_lbs, sg_flags, op->submit,
+ thr_start_eagain_count, thr_start_ebusy_count,
+ thr_start_e2big_count, thr_start_edom_count) :
+ start_sg3_cmd(sg_fd, op->c2e, pack_id, lba, lbp,
+ op->lb_sz * op->num_lbs, sg_flags, op->submit,
+ thr_start_eagain_count, thr_start_ebusy_count,
+ thr_start_e2big_count, thr_start_edom_count);
if (res) {
if (res > 1) { /* here if E2BIG, start not done, try finish */
do_inc = 0;
@@ -989,8 +1196,14 @@ work_thread(int id, struct opts_t * op)
} else
pack_id = -1;
ask = pack_id;
- if (finish_sg3_cmd(sg_fd, op->c2e, op->pack_id_force, pack_id,
- op->wait_ms, thr_fin_eagain_count, nanosecs)) {
+ res = (op->v4) ?
+ finish_sg4_cmd(sg_fd, op->c2e, op->pack_id_force, pack_id,
+ op->submit, op->wait_ms,
+ thr_fin_eagain_count, nanosecs) :
+ finish_sg3_cmd(sg_fd, op->c2e, op->pack_id_force, pack_id,
+ op->submit, op->wait_ms,
+ thr_fin_eagain_count, nanosecs);
+ if (res) {
err = "finish_sg3_cmd()";
if (ruip && (pack_id > 0)) {
auto q = pi_2_lba.find(pack_id);
@@ -1260,12 +1473,15 @@ main(int argc, char * argv[])
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "cdfghl:mM:n:Npq:Q:Rs:St:TvVw:W",
+ c = getopt_long(argc, argv, "4cdfghl:mM:n:Npq:Q:Rs:St:TuvVw:W",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
+ case '4':
+ op->v4 = true;
+ break;
case 'c':
op->cmd_time = true;
break;
@@ -1405,6 +1621,9 @@ main(int argc, char * argv[])
case 'T':
op->c2e = SCSI_TUR;
break;
+ case 'u':
+ op->submit = true;
+ break;
case 'v':
op->verbose_given = true;
++op->verbose;
@@ -1481,6 +1700,13 @@ main(int argc, char * argv[])
cerr << "lba,hi_lba range is illegal" << endl;
return 1;
}
+ if (op->v4) {
+ if (! op->submit) {
+ op->submit = true;
+ if (op->verbose > 1)
+ cerr << "when --v4 is given, --submit will be set" << endl;
+ }
+ }
try {
int sg_ver_num;
diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c
index d7dc227b..d0d7d153 100644
--- a/testing/sg_tst_ioctl.c
+++ b/testing/sg_tst_ioctl.c
@@ -21,6 +21,8 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/socket.h> /* For passing fd_s via Unix sockets */
+
/* Kernel uapi header contain __user decorations on user space pointers
* to indicate they are unsafe in the kernel space. However glibc takes
* all those __user decorations out from headers in /usr/include/linux .
@@ -45,7 +47,7 @@
* later of the Linux sg driver. */
-static const char * version_str = "Version: 0.98 20181207";
+static const char * version_str = "Version: 1.01 20181216";
#define INQ_REPLY_LEN 96
#define INQ_CMD_LEN 6
@@ -67,14 +69,29 @@ static const char * version_str = "Version: 0.98 20181207";
#define DEF_RESERVE_BUFF_SZ (256 * 1024)
+static bool is_parent = false;
+static bool do_fork = false;
+static bool ioctl_only = false;
+static bool q_at_tail = false;
+static bool write_only = false;
+
+static int childs_pid = 0;
+static int q_len = DEF_Q_LEN;
+static int sleep_secs = 0;
+static int reserve_buff_sz = DEF_RESERVE_BUFF_SZ;
+static int verbose = 0;
+
+static const char * relative_cp = NULL;
+
static void
usage(void)
{
- printf("Usage: 'sg_tst_ioctl [-h] [-l=Q_LEN] [-o] [-r=SZ] [-s=SEC] "
+ printf("Usage: 'sg_tst_ioctl [-f] [-h] [-l=Q_LEN] [-o] [-r=SZ] [-s=SEC] "
"[-t]\n"
- " [-v] [-V] [-w] <sg_device>'\n"
+ " [-v] [-V] [-w] <sg_device> [<sg_device2>]'\n"
" where:\n"
+ " -f fork and test share between processes\n"
" -h help: print usage message then exit\n"
" -l=Q_LEN queue length, between 1 and 511 (def: 16)\n"
" -o ioctls only, then exit\n"
@@ -87,125 +104,114 @@ usage(void)
" -w write (submit) only then exit\n");
}
-
-int
-main(int argc, char * argv[])
+/* This function taken from Keith Parkard's blog dated 2101205 */
+static ssize_t
+sock_fd_write(int sock, const void *buf, ssize_t buflen, int fd)
{
- bool done;
- bool q_at_tail = false;
- bool ioctl_only = false;
- bool write_only = false;
- int sg_fd, k, ok, ver_num, pack_id, num_waiting, access_count;
- int sg_fd2 = -1;
- uint8_t inq_cdb[INQ_CMD_LEN] =
- {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
- uint8_t sdiag_cdb[SDIAG_CMD_LEN] =
- {0x1d, 0, 0, 0, 0, 0};
- uint8_t inqBuff[MAX_Q_LEN][INQ_REPLY_LEN];
- sg_io_hdr_t io_hdr[MAX_Q_LEN];
- sg_io_hdr_t rio_hdr;
- char * file_name = 0;
- char ebuff[EBUFF_SZ];
- uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
- int q_len = DEF_Q_LEN;
- int sleep_secs = 0;
- int reserve_buff_sz = DEF_RESERVE_BUFF_SZ;
- int verbose = 0;
- uint32_t cflags;
- struct sg_extended_info sei;
- struct sg_extended_info * seip;
- const char * second_fname = "/dev/sg2";
- struct sg_scsi_id ssi;
+ ssize_t size;
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ struct cmsghdr cmsghdr;
+ char control[CMSG_SPACE(sizeof (int))];
+ } cmsgu;
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = (void *)buf; /* OS shouldn't write back in this */
+ iov.iov_len = buflen;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (fd != -1) {
+ msg.msg_control = cmsgu.control;
+ msg.msg_controllen = sizeof(cmsgu.control);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof (int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ printf ("passing fd %d\n", fd);
+ *((int *) CMSG_DATA(cmsg)) = fd;
+ } else {
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ printf ("not passing fd\n");
+ }
- for (k = 1; k < argc; ++k) {
- if (0 == memcmp("-t", argv[k], 2))
- q_at_tail = true;
- else if (0 == memcmp("-h", argv[k], 2)) {
- file_name = 0;
- break;
- } else if (0 == memcmp("-l=", argv[k], 3)) {
- q_len = atoi(argv[k] + 3);
- if ((q_len > 511) || (q_len < 1)) {
- printf("Expect -l= to take a number (q length) between 1 "
- "and 511\n");
- file_name = 0;
- break;
- }
- } else if (0 == memcmp("-o", argv[k], 2))
- ioctl_only = true;
- else if (0 == memcmp("-r=", argv[k], 3)) {
- reserve_buff_sz = atoi(argv[k] + 3);
- if (reserve_buff_sz < 0) {
- printf("Expect -r= to take a number 0 or higher\n");
- file_name = 0;
- break;
+ size = sendmsg(sock, &msg, 0);
+
+ if (size < 0)
+ perror ("sendmsg");
+ return size;
+}
+
+/* This function taken from Keith Parkard's blog dated 2101205 */
+static ssize_t
+sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd)
+{
+ ssize_t size;
+
+ if (fd) {
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ struct cmsghdr cmsghdr;
+ char control[CMSG_SPACE(sizeof (int))];
+ } cmsgu;
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = buf;
+ iov.iov_len = bufsize;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgu.control;
+ msg.msg_controllen = sizeof(cmsgu.control);
+ size = recvmsg (sock, &msg, 0);
+ if (size < 0) {
+ perror ("recvmsg");
+ exit(1);
+ }
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ if (cmsg->cmsg_level != SOL_SOCKET) {
+ fprintf (stderr, "invalid cmsg_level %d\n",
+ cmsg->cmsg_level);
+ exit(1);
}
- } else if (0 == memcmp("-s=", argv[k], 3)) {
- sleep_secs = atoi(argv[k] + 3);
- if (sleep_secs < 0) {
- printf("Expect -s= to take a number 0 or higher\n");
- file_name = 0;
- break;
+ if (cmsg->cmsg_type != SCM_RIGHTS) {
+ fprintf (stderr, "invalid cmsg_type %d\n",
+ cmsg->cmsg_type);
+ exit(1);
}
- } else if (0 == memcmp("-vvvv", argv[k], 5))
- verbose += 4;
- else if (0 == memcmp("-vvv", argv[k], 4))
- verbose += 3;
- else if (0 == memcmp("-vv", argv[k], 3))
- verbose += 2;
- else if (0 == memcmp("-v", argv[k], 2))
- verbose += 1;
- else if (0 == memcmp("-V", argv[k], 2)) {
- printf("%s\n", version_str);
- file_name = 0;
- break;
- } else if (0 == memcmp("-w", argv[k], 2))
- write_only = true;
- 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) {
- usage();
- 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;
- }
- if (verbose)
- fprintf(stderr, "opened given file: %s successfully, fd=%d\n",
- file_name, sg_fd);
- if (ioctl(sg_fd, SG_GET_VERSION_NUM, &ver_num) < 0) {
- pr2serr("ioctl(SG_GET_VERSION_NUM) failed, errno=%d %s\n", errno,
- strerror(errno));
- goto out;
+ *fd = *((int *) CMSG_DATA(cmsg));
+ printf ("received fd %d\n", *fd);
+ } else
+ *fd = -1;
+ } else {
+ size = read (sock, buf, bufsize);
+ if (size < 0) {
+ perror("read");
+ exit(1);
+ }
}
- printf("Linux sg driver version: %d\n", ver_num);
+ return size;
+}
- if ((sg_fd2 = open(second_fname, O_RDWR)) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- "%s: error opening file: %s", __func__, second_fname);
- perror(ebuff);
- return 1;
- }
- if (verbose)
- fprintf(stderr, "opened second file: %s successfully, fd=%d\n",
- second_fname, sg_fd2);
+static int
+tst_ioctl(const char * fnp, int sg_fd, const char * fn2p, int sg_fd2,
+ int sock, const char * cp)
+{
+ uint32_t cflags;
+ struct sg_extended_info sei;
+ struct sg_extended_info * seip;
seip = &sei;
memset(seip, 0, sizeof(*seip));
@@ -224,6 +230,8 @@ main(int argc, char * argv[])
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_OTHER_OPENS;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_ORPHANS;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_Q_TAIL;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_IS_SHARE;
+ seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_IS_MASTER;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_UNSHARE;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_MASTER_FINI;
seip->ctl_flags_rd_mask |= SG_CTL_FLAGM_MASTER_ERR;
@@ -233,48 +241,54 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
#if 1
- printf("SG_SET_GET_EXTENDED ioctl ok\n");
+ printf("%sSG_SET_GET_EXTENDED ioctl ok\n", cp);
if (SG_SEIM_RESERVED_SIZE & seip->valid_rd_mask)
- printf(" reserved size: %u\n", seip->reserved_sz);
+ printf(" %sreserved size: %u\n", cp, seip->reserved_sz);
if (SG_SEIM_MINOR_INDEX & seip->valid_rd_mask)
- printf(" minor index: %u\n", seip->minor_index);
+ printf(" %sminor index: %u\n", cp, seip->minor_index);
if (SG_SEIM_RQ_REM_THRESH & seip->valid_rd_mask)
- printf(" rq_rem_sgat_thresh: %u\n", seip->rq_rem_sgat_thresh);
+ printf(" %srq_rem_sgat_thresh: %u\n", cp, seip->rq_rem_sgat_thresh);
if (SG_SEIM_TOT_FD_THRESH & seip->valid_rd_mask)
- printf(" tot_fd_thresh: %u\n", seip->tot_fd_thresh);
+ printf(" %stot_fd_thresh: %u\n", cp, seip->tot_fd_thresh);
if ((SG_SEIM_CTL_FLAGS & seip->valid_rd_mask) ||
(SG_SEIM_CTL_FLAGS & seip->valid_wr_mask)) {
cflags = seip->ctl_flags;
if (SG_CTL_FLAGM_TIME_IN_NS & seip->ctl_flags_rd_mask)
- printf(" TIME_IN_NS: %s\n",
+ printf(" %sTIME_IN_NS: %s\n", cp,
(SG_CTL_FLAGM_TIME_IN_NS & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_OTHER_OPENS & seip->ctl_flags_rd_mask)
- printf(" OTHER_OPENS: %s\n",
+ printf(" %sOTHER_OPENS: %s\n", cp,
(SG_CTL_FLAGM_OTHER_OPENS & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_ORPHANS & seip->ctl_flags_rd_mask)
- printf(" ORPHANS: %s\n",
+ printf(" %sORPHANS: %s\n", cp,
(SG_CTL_FLAGM_ORPHANS & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_Q_TAIL & seip->ctl_flags_rd_mask)
- printf(" Q_TAIL: %s\n",
+ printf(" %sQ_TAIL: %s\n", cp,
(SG_CTL_FLAGM_Q_TAIL & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_IS_SHARE & seip->ctl_flags_rd_mask)
+ printf(" %sIS_SHARE: %s\n", cp,
+ (SG_CTL_FLAGM_IS_SHARE & cflags) ? "true" : "false");
+ if (SG_CTL_FLAGM_IS_MASTER & seip->ctl_flags_rd_mask)
+ printf(" %sIS_MASTER: %s\n", cp,
+ (SG_CTL_FLAGM_IS_MASTER & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_UNSHARE & seip->ctl_flags_rd_mask)
- printf(" UNSHARE: %s\n",
+ printf(" %sUNSHARE: %s\n", cp,
(SG_CTL_FLAGM_UNSHARE & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_MASTER_FINI & seip->ctl_flags_rd_mask)
- printf(" MASTER_FINI: %s\n",
+ printf(" %sMASTER_FINI: %s\n", cp,
(SG_CTL_FLAGM_MASTER_FINI & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_MASTER_ERR & seip->ctl_flags_rd_mask)
- printf(" MASTER_ERR: %s\n",
+ printf(" %sMASTER_ERR: %s\n", cp,
(SG_CTL_FLAGM_MASTER_ERR & cflags) ? "true" : "false");
if (SG_CTL_FLAGM_CHECK_FOR_MORE & seip->ctl_flags_rd_mask)
- printf(" CHECK_FOR_MORE: %s\n",
+ printf(" %sCHECK_FOR_MORE: %s\n", cp,
(SG_CTL_FLAGM_CHECK_FOR_MORE & cflags) ? "true" : "false");
}
if (SG_SEIM_MINOR_INDEX & seip->valid_rd_mask)
- printf(" minor_index: %u\n", seip->minor_index);
+ printf(" %sminor_index: %u\n", cp, seip->minor_index);
printf("\n");
#endif
@@ -285,10 +299,20 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
+ }
+ printf(" %sread_value[SG_SEIRV_INT_MASK]= %u\n", cp, seip->read_value);
+
+ memset(seip, 0, sizeof(*seip));
+ seip->valid_wr_mask |= SG_SEIM_READ_VAL;
+ seip->valid_rd_mask |= SG_SEIM_READ_VAL;
+ seip->read_value = SG_SEIRV_BOOL_MASK;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ return 1;
}
- printf("SG_SET_GET_EXTENDED ioctl ok\n");
- printf(" read_value[SG_SEIRV_INT_MASK]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_BOOL_MASK]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -297,9 +321,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_VERS_NUM]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_VERS_NUM]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -308,9 +332,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_FL_RQS]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_FL_RQS]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -319,9 +343,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_DEV_FL_RQS]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_DEV_FL_RQS]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -330,9 +354,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_TRC_SZ]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_TRC_SZ]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_READ_VAL;
@@ -341,9 +365,9 @@ main(int argc, char * argv[])
if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
pr2serr("ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n", errno,
strerror(errno));
- goto out;
+ return 1;
}
- printf(" read_value[SG_SEIRV_TRC_MAX_SZ]= %u\n", seip->read_value);
+ printf(" %sread_value[SG_SEIRV_TRC_MAX_SZ]= %u\n", cp, seip->read_value);
memset(seip, 0, sizeof(*seip));
seip->valid_wr_mask |= SG_SEIM_SHARE_FD;
@@ -353,10 +377,15 @@ main(int argc, char * argv[])
#else
seip->share_fd = sg_fd;
#endif
- if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0)
- pr2serr("ioctl(SG_SET_GET_EXTENDED) shared_fd=%d, failed errno=%d "
- "%s\n", sg_fd2, errno, strerror(errno));
- printf(" read back shared_fd= %u\n", seip->share_fd);
+ if (do_fork && is_parent)
+ goto bypass_share;
+ if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ pr2serr("%sioctl(SG_SET_GET_EXTENDED) shared_fd=%d, failed errno=%d "
+ "%s\n", cp, sg_fd2, errno, strerror(errno));
+ }
+ printf(" %sshare successful, read back shared_fd= %d\n", cp,
+ (int)seip->share_fd);
+bypass_share:
// printf("SG_IOSUBMIT value=0x%lx\n", SG_IOSUBMIT);
// printf("SG_IORECEIVE value=0x%lx\n", SG_IORECEIVE);
@@ -364,17 +393,203 @@ main(int argc, char * argv[])
pr2serr("ioctl(SG_GET_TRANSFORM) fail expected, errno=%d %s\n",
errno, strerror(errno));
else
- printf("SG_GET_TRANSFORM okay (does nothing)\n");
+ printf("%sSG_GET_TRANSFORM okay (does nothing)\n", cp);
if (ioctl(sg_fd, SG_SET_TRANSFORM, NULL) < 0)
pr2serr("ioctl(SG_SET_TRANSFORM) fail expected, errno=%d %s\n",
errno, strerror(errno));
else
- printf("SG_SET_TRANSFORM okay (does nothing)\n");
+ printf("%sSG_SET_TRANSFORM okay (does nothing)\n", cp);
printf("\n");
+ /* test sending a sg file descriptor between 2 processes using UNIX
+ * sockets */
+ if (do_fork && is_parent && fnp && (sock >= 0)) { /* master/READ side */
+ int res;
+ int fd_ma = open(fnp, O_RDWR);
+
+ if (fd_ma < 0) {
+ pr2serr("%s: opening %s failed: %s\n", __func__, fnp,
+ strerror(errno));
+ return 1;
+ }
+ res = sock_fd_write(sock, "boo", 4, fd_ma);
+ if (res < 0)
+ pr2serr("%s: sock_fd_write() failed\n", __func__);
+ else
+ printf("%s: sock_fd_write() returned: %d\n", __func__, res);
+ } else if (do_fork && !is_parent && fn2p && (sock >= 0)) {
+ int res, fd_ma;
+ /* int fd_sl = open(fn2p, O_RDWR); not needed */
+ uint8_t b[32];
+
+ fd_ma = -1;
+ res = sock_fd_read(sock, b, sizeof(b), &fd_ma);
+ if (res < 0)
+ pr2serr("%s: sock_fd_read() failed\n", __func__);
+ else
+ printf("%s: sock_fd_read() returned: %d, fd_ma=%d\n", __func__,
+ res, fd_ma);
+ /* yes it works! */
+ }
+ return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool done;
+ int sg_fd, k, ok, ver_num, pack_id, num_waiting, access_count;
+ int sg_fd2 = -1;
+ int sock = -1;
+ uint8_t inq_cdb[INQ_CMD_LEN] =
+ {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+ uint8_t sdiag_cdb[SDIAG_CMD_LEN] =
+ {0x1d, 0, 0, 0, 0, 0};
+ uint8_t inqBuff[MAX_Q_LEN][INQ_REPLY_LEN];
+ sg_io_hdr_t io_hdr[MAX_Q_LEN];
+ sg_io_hdr_t rio_hdr;
+ char * file_name = 0;
+ char ebuff[EBUFF_SZ];
+ uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN];
+ const char * second_fname = NULL;
+ const char * cp;
+ struct sg_scsi_id ssi;
+
+ for (k = 1; k < argc; ++k) {
+ if (0 == memcmp("-f", argv[k], 2))
+ do_fork = true;
+ else if (0 == memcmp("-h", argv[k], 2)) {
+ file_name = 0;
+ break;
+ } else if (0 == memcmp("-l=", argv[k], 3)) {
+ q_len = atoi(argv[k] + 3);
+ if ((q_len > 511) || (q_len < 1)) {
+ printf("Expect -l= to take a number (q length) between 1 "
+ "and 511\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-o", argv[k], 2))
+ ioctl_only = true;
+ else if (0 == memcmp("-r=", argv[k], 3)) {
+ reserve_buff_sz = atoi(argv[k] + 3);
+ if (reserve_buff_sz < 0) {
+ printf("Expect -r= to take a number 0 or higher\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-s=", argv[k], 3)) {
+ sleep_secs = atoi(argv[k] + 3);
+ if (sleep_secs < 0) {
+ printf("Expect -s= to take a number 0 or higher\n");
+ file_name = 0;
+ break;
+ }
+ } else if (0 == memcmp("-t", argv[k], 2))
+ q_at_tail = true;
+ else if (0 == memcmp("-vvvv", argv[k], 5))
+ verbose += 4;
+ else if (0 == memcmp("-vvv", argv[k], 4))
+ verbose += 3;
+ else if (0 == memcmp("-vv", argv[k], 3))
+ verbose += 2;
+ else if (0 == memcmp("-v", argv[k], 2))
+ verbose += 1;
+ else if (0 == memcmp("-V", argv[k], 2)) {
+ printf("%s\n", version_str);
+ file_name = 0;
+ break;
+ } else if (0 == memcmp("-w", argv[k], 2))
+ write_only = true;
+ 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 if (NULL == second_fname)
+ second_fname = argv[k];
+ else {
+ printf("too many arguments\n");
+ file_name = 0;
+ break;
+ }
+ }
+ if (0 == file_name) {
+ printf("No filename (sg device) given\n\n");
+ usage();
+ 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,
+ "error opening file: %s", file_name);
+ perror(ebuff);
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, "opened given file: %s successfully, fd=%d\n",
+ file_name, sg_fd);
+
+ if (ioctl(sg_fd, SG_GET_VERSION_NUM, &ver_num) < 0) {
+ pr2serr("ioctl(SG_GET_VERSION_NUM) failed, errno=%d %s\n", errno,
+ strerror(errno));
+ goto out;
+ }
+ printf("Linux sg driver version: %d\n", ver_num);
+
+ if (second_fname) {
+ if ((sg_fd2 = open(second_fname, O_RDWR)) < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "%s: error opening file: %s", __func__, second_fname);
+ perror(ebuff);
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, "opened second file: %s successfully, fd=%d\n",
+ second_fname, sg_fd2);
+ }
+
+ if (do_fork) {
+ int pid;
+ int sv[2];
+
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+ printf("socketpair: sv[0]=%d, sv[1]=%d sg_fd=%d\n", sv[0], sv[1],
+ sg_fd);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ goto out;
+ } else if (0 == pid) {
+ relative_cp = "child ";
+ is_parent = false;
+ close(sv[0]);
+ sock = sv[1];
+ } else {
+ relative_cp = "parent ";
+ is_parent = true;
+ childs_pid = pid;
+ close(sv[1]);
+ sock = sv[0];
+ }
+ }
+
+ cp = do_fork ? relative_cp : "";
+ if (tst_ioctl(file_name, sg_fd, second_fname, sg_fd2, sock, cp))
+ goto out;
if (ioctl_only)
goto out;
+ if (do_fork && !is_parent)
+ return 0;
+
printf("start write() calls\n");
for (k = 0; k < q_len; ++k) {
/* Prepare INQUIRY command */
@@ -405,7 +620,7 @@ main(int argc, char * argv[])
/* 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");
+ pr2serr("%ssg write errno=%d [%s]\n", cp, errno, strerror(errno));
close(sg_fd);
return 1;
}
@@ -440,6 +655,8 @@ main(int argc, char * argv[])
if (write_only)
goto out;
+ if (do_fork)
+ printf("\n\nFollowing starting with get_pack_id are all CHILD\n");
if (ioctl(sg_fd, SG_GET_PACK_ID, &pack_id) < 0)
pr2serr("ioctl(SG_GET_PACK_ID) failed, errno=%d %s\n",
errno, strerror(errno));
@@ -475,7 +692,7 @@ main(int argc, char * argv[])
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");
+ perror("sg read error");
close(sg_fd);
return 1;
}
diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c
index 16aab4fa..3d1a1076 100644
--- a/testing/sgs_dd.c
+++ b/testing/sgs_dd.c
@@ -11,7 +11,7 @@
*
* This program is a specialisation of the Unix "dd" command in which
* one or both of the given files is a scsi generic device.
- * A block size ('bs') is assumed to be 512 if not given. This
+ * A logical block size ('bs') is assumed to be 512 if not given. This
* program complains if 'ibs' or 'obs' are given with some other value
* than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
* 'of' is not given or 'of=-' then stdout assumed.
@@ -88,7 +88,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "1.05 20181213";
+static const char * version_str = "1.06 20181215";
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wclobbered"
@@ -483,7 +483,8 @@ usage(int pg_num)
"[time=0|1]\n"
" [verbose=VERB] [--dry-run] [--verbose]\n\n"
" where the main options (shown in first group above) are:\n"
- " bs must be device block size (default 512)\n"
+ " bs must be device logical block size (default "
+ "512)\n"
" count number of blocks to copy (def: device size)\n"
" if file or device to read from (def: stdin)\n"
" iflag comma separated list from: [2fds,coe,defres,dio,"
@@ -846,6 +847,11 @@ read_write_thread(void * v_tip)
rep->wr = true;
status = pthread_mutex_lock(&clp->out_mutex);
if (0 != status) err_exit(status, "lock out_mutex");
+
+ /* Make sure the OFILE (+ OFILE2) are in same sequence as IFILE */
+ if ((rep->out2fd < 0) && (FT_SG == clp->in_type) &&
+ (FT_SG == clp->out_type))
+ goto skip_force_out_sequence;
if (share_and_of2 || (FT_DEV_NULL != clp->out_type)) {
while ((! clp->out_stop) &&
((rep->blk + seek_skip) != clp->out_blk)) {
@@ -856,6 +862,7 @@ read_write_thread(void * v_tip)
pthread_cleanup_pop(0);
}
}
+skip_force_out_sequence:
if (clp->out_stop || (clp->out_count <= 0)) {
if (! clp->out_stop)
@@ -1875,7 +1882,8 @@ main(int argc, char * argv[])
}
if (clp->bs <= 0) {
clp->bs = DEF_BLOCK_SIZE;
- pr2serr("Assume default 'bs' (block size) of %d bytes\n", clp->bs);
+ pr2serr("Assume default 'bs' ((logical) block size) of %d bytes\n",
+ clp->bs);
}
if ((ibs && (ibs != clp->bs)) || (obs && (obs != clp->bs))) {
pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
@@ -2103,8 +2111,8 @@ main(int argc, char * argv[])
in_num_sect = -1;
}
if (clp->bs != in_sect_sz) {
- pr2serr("block size on %s confusion; bs=%d, from device=%d\n",
- inf, clp->bs, in_sect_sz);
+ pr2serr("logical block size on %s confusion; bs=%d, from "
+ "device=%d\n", inf, clp->bs, in_sect_sz);
in_num_sect = -1;
}
}
@@ -2135,8 +2143,8 @@ main(int argc, char * argv[])
out_num_sect = -1;
}
if (clp->bs != out_sect_sz) {
- pr2serr("block size on %s confusion: bs=%d, from device=%d\n",
- outf, clp->bs, out_sect_sz);
+ pr2serr("logical block size on %s confusion: bs=%d, from "
+ "device=%d\n", outf, clp->bs, out_sect_sz);
out_num_sect = -1;
}
}
diff --git a/testing/uapi_sg.h b/testing/uapi_sg.h
index 4985fd9f..4d3ccba6 100644
--- a/testing/uapi_sg.h
+++ b/testing/uapi_sg.h
@@ -13,7 +13,7 @@
* Version 2 and 3 extensions to driver:
* Copyright (C) 1998 - 2018 Douglas Gilbert
*
- * Version: 3.9.02 (20181203)
+ * Version: 4.0.02 (20181216)
* This version is for Linux 2.6, 3 and 4 series kernels.
*
* Documentation
@@ -105,11 +105,10 @@ typedef struct sg_io_hdr {
*/
#define SGV4_FLAG_DIRECT_IO SG_FLAG_DIRECT_IO
#define SGV4_FLAG_MMAP_IO SG_FLAG_MMAP_IO
-#define SGV4_FLAG_YIELD_TAG 0x8 /* sg_io_v4::request_attr set after SG_IOS */
+#define SGV4_FLAG_YIELD_TAG 0x8 /* sg_io_v4::request_tag set after SG_IOS */
#define SGV4_FLAG_Q_AT_TAIL SG_FLAG_Q_AT_TAIL
#define SGV4_FLAG_Q_AT_HEAD SG_FLAG_Q_AT_HEAD
#define SGV4_FLAG_FIND_BY_TAG 0x100 /* in SG_IOR, def: find by pack_id */
-/* Flag value 0x200 not currently used */
#define SGV4_FLAG_IMMED 0x400 /* for polling with SG_IOR else ignored */
/* Flag value 0x800 not currently used */
#define SGV4_FLAG_DEV_SCOPE 0x1000 /* permit SG_IOABORT to have wider scope */
@@ -124,7 +123,7 @@ typedef struct sg_io_hdr {
#define SG_INFO_DIRECT_IO_MASK 0x6
#define SG_INFO_INDIRECT_IO 0x0 /* data xfer via kernel buffers (or no xfer) */
#define SG_INFO_DIRECT_IO 0x2 /* direct IO requested and performed */
-#define SG_INFO_MIXED_IO 0x4 /* part direct, part indirect IO */
+#define SG_INFO_MIXED_IO 0x4 /* not used, always 0 */
#define SG_INFO_DEVICE_DETACHING 0x8 /* completed successfully but ... */
#define SG_INFO_ANOTHER_WAITING 0x10 /* needs SG_CTL_FLAGM_CHECK_FOR_MORE */
@@ -194,7 +193,7 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
#define SG_SEIRV_BOOL_MASK 0x1 /* get SG_CTL_FLAGM_ALL_BITS */
#define SG_SEIRV_VERS_NUM 0x2 /* get driver version number as int */
#define SG_SEIRV_FL_RQS 0x3 /* number of requests in free list */
-#define SG_SEIRV_DEV_FL_RQS 0x4 /* sum of rqs on all fds on this dev */
+#define SG_SEIRV_DEV_FL_RQS 0x4 /* sum(fl rqs) on all of dev's fds */
#define SG_SEIRV_TRC_SZ 0x5 /* current size of trace buffer */
#define SG_SEIRV_TRC_MAX_SZ 0x6 /* maximum size of trace buffer */
@@ -413,11 +412,18 @@ struct sg_header {
* the sg_v3 interface (structure defined above) and the sg_v4 interface
* (structure defined in <include/uapi/linux/bsg.h> ). Following "read" and
* "write" terms are from the driver's POV, the _IO macros from users' POV.
+ *
+ * SG_IOSUBMIT and SG_IORECEIVE can take a pointer to a v4 or v3 interface
+ * which have different lengths (v4 is longer) but their definitions below
+ * encode v4's length into the ioctl number. So users of the v3 interface
+ * would be best to place a v3 structure inside (overwriting and at the start
+ * of) a v4 structure and pass a pointer to that v4 structure to the ioctl.
*/
/* via pointer reads sg v3 or v4 object, optionally writes tag, so _IOWR */
#define SG_IOSUBMIT _IOWR(SG_IOCTL_MAGIC_NUM, 0x41, struct sg_io_v4)
/* via pointer optionally reads tag, writes sg v3 or v4 object, so _IOWR */
#define SG_IORECEIVE _IOWR(SG_IOCTL_MAGIC_NUM, 0x42, struct sg_io_v4)
+
/* via pointer reads v4 object (including tag), writes nothing, so _IOW */
#define SG_IOABORT _IOW(SG_IOCTL_MAGIC_NUM, 0x43, struct sg_io_v4)