diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2018-12-17 21:16:36 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2018-12-17 21:16:36 +0000 |
commit | 18c9852db91032afe4bedd85857d558402b360e2 (patch) | |
tree | e1f3ef6efab1d17665cb19e85d43439888291a6e /testing | |
parent | d906e8cb000e95d9260d1833d3692c1c763e489a (diff) | |
download | sg3_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.cpp | 338 | ||||
-rw-r--r-- | testing/sg_tst_ioctl.c | 519 | ||||
-rw-r--r-- | testing/sgs_dd.c | 24 | ||||
-rw-r--r-- | testing/uapi_sg.h | 16 |
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) |