aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--include/sg_pt_linux.h7
-rw-r--r--lib/sg_io_linux.c8
-rw-r--r--lib/sg_lib_data.c5
-rw-r--r--lib/sg_pt_common.c21
-rw-r--r--lib/sg_pt_linux_nvme.c70
-rw-r--r--src/sg_dd.c13
-rw-r--r--src/sg_get_elem_status.c12
-rw-r--r--src/sg_write_x.c12
-rw-r--r--testing/sgh_dd.cpp12
-rw-r--r--testing/sgs_dd.c396
-rw-r--r--testing/uapi_sg.h13
12 files changed, 384 insertions, 195 deletions
diff --git a/ChangeLog b/ChangeLog
index c7ec5db7..6569e87b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,8 +2,14 @@ Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
directory have their own "man" pages. There is also a sg3_utils man page.
-Changelog for sg3_utils-1.46 [20200309] [svn: r845]
- - bump config to 1.46
+Changelog for sg3_utils-1.46 [20200403] [svn: r846]
+ - sg_dd: separate category for miscompare errors
+ - sg_get_elem_status: add res_all or ralwd bit, t10 is fuzzy
+ - sg_write_x: add dld bits to write(32) [sbc4r19a]
+ - sg_lib: restore elements and rebuild command added
+ - start SNTL work on read/write
+ - testing/sgs_dd: add evfd flags and eventfd processing
+ - testing/sgh_dd: various fixes
Changelog for sg3_utils-1.45 [20200229] [svn: r843]
- sg_get_elem_status: new utility [sbc4r16]
diff --git a/include/sg_pt_linux.h b/include/sg_pt_linux.h
index eb451e8d..d96e861c 100644
--- a/include/sg_pt_linux.h
+++ b/include/sg_pt_linux.h
@@ -171,6 +171,13 @@ struct sg_pt_base {
#ifndef NVME_IOCTL_SUBSYS_RESET
#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45)
#endif
+#ifndef NVME_IOCTL_RESCAN
+#define NVME_IOCTL_RESCAN _IO('N', 0x46)
+#endif
+#if 0
+#define NVME_IOCTL_ADMIN64_CMD _IOWR('N', 0x47, struct nvme_passthru_cmd64)
+#define NVME_IOCTL_IO64_CMD _IOWR('N', 0x48, struct nvme_passthru_cmd64)
+#endif
extern bool sg_bsg_nvme_char_major_checked;
extern int sg_bsg_major;
diff --git a/lib/sg_io_linux.c b/lib/sg_io_linux.c
index 15e684e2..25ccb47d 100644
--- a/lib/sg_io_linux.c
+++ b/lib/sg_io_linux.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999-2018 Douglas Gilbert.
+ * Copyright (c) 1999-2020 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -24,7 +24,7 @@
#include "sg_pr2serr.h"
-/* Version 1.10 20180613 */
+/* Version 1.11 20200401 */
void
@@ -62,7 +62,7 @@ sg_print_host_status(int host_status)
static const char * linux_driver_bytes[] = {
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
"DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
- "DRIVER_SENSE"
+ "DRIVER_SENSE",
};
#if 0
@@ -72,7 +72,7 @@ static const char * linux_driver_bytes[] = {
static const char * linux_driver_suggests[] = {
"SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
"SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
- "SUGGEST_SENSE"
+ "SUGGEST_SENSE",
};
#endif
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index 6c0326c3..725596f5 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -19,8 +19,8 @@
#include "sg_lib_data.h"
-const char * sg_lib_version_str = "2.72 20200125";
-/* spc5r22, sbc4r17, zbc2r04 */
+const char * sg_lib_version_str = "2.73 20200331";
+/* spc6r01, sbc4r19, zbc2r04 */
/* indexed by pdt; those that map to own index do not decay */
@@ -363,6 +363,7 @@ struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = {
{0x16, 0, "Get stream status"},
{0x17, 0, "Get physical element status"}, /* added sbc4r13 */
{0x18, 0, "Remove element and truncate"}, /* added sbc4r13 */
+ {0x19, 0, "Restore elements and rebuild"}, /* added sbc4r19 */
{0xffff, 0, NULL},
};
diff --git a/lib/sg_pt_common.c b/lib/sg_pt_common.c
index 47e01e1b..5d087171 100644
--- a/lib/sg_pt_common.c
+++ b/lib/sg_pt_common.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2019 Douglas Gilbert.
+ * Copyright (c) 2009-2020 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -31,7 +31,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * scsi_pt_version_str = "3.12 20190612";
+static const char * scsi_pt_version_str = "3.13 20200313";
const char *
@@ -76,10 +76,27 @@ static struct sg_opcode_info_t sg_opcode_info_arr[] =
0x1, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{0x1d, 0, 0, {6, /* SEND DIAGNOSTIC */
0xf7, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0x25, 0, 0, {10, /* READ CAPACITY(10) */
+ 0x1, 0xff, 0xff, 0xff, 0xff, 0, 0, 0x1, 0xc7, 0, 0, 0, 0, 0, 0} },
+ {0x28, 0, 0, {10, /* READ(10) */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0,
+ 0, 0} },
+ {0x2a, 0, 0, {10, /* WRITE(10) */
+ 0xfb, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0,
+ 0, 0} },
{0x55, 0, 0, {10, /* MODE SELECT(10) */
0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
{0x5a, 0, 0, {10, /* MODE SENSE(10) */
0x18, 0xff, 0xff, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
+ {0x88, 0, 0, {16, /* READ(16) */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xc7} },
+ {0x8a, 0, 0, {16, /* WRITE(16) */
+ 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xc7} },
+ {0x9e, 0x10, F_SA_LOW, {16, /* READ CAPACITY(16) [service action in] */
+ 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x1, 0xc7} },
{0xa0, 0, 0, {12, /* REPORT LUNS */
0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, 0} },
{0xa3, 0xc, F_SA_LOW, {12, /* REPORT SUPPORTED OPERATION CODES */
diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c
index 10e176b1..7b633feb 100644
--- a/lib/sg_pt_linux_nvme.c
+++ b/lib/sg_pt_linux_nvme.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Douglas Gilbert.
+ * Copyright (c) 2017-2020 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -41,7 +41,7 @@
* MA 02110-1301, USA.
*/
-/* sg_pt_linux_nvme version 1.09 20190303 */
+/* sg_pt_linux_nvme version 1.10 20200313 */
/* This file contains a small "SPC-only" SNTL to support the SES pass-through
* of SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS through NVME-MI
@@ -87,11 +87,15 @@
#define SCSI_SEND_DIAGNOSTIC_OPC 0x1d
#define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c
#define SCSI_MAINT_IN_OPC 0xa3
+#define SCSI_READ10_OPC 0x28
+#define SCSI_READ16_OPC 0x88
#define SCSI_REP_SUP_OPCS_OPC 0xc
#define SCSI_REP_SUP_TMFS_OPC 0xd
#define SCSI_MODE_SENSE10_OPC 0x5a
#define SCSI_MODE_SELECT10_OPC 0x55
#define SCSI_READ_CAPACITY10_OPC 0x25
+#define SCSI_WRITE10_OPC 0x2a
+#define SCSI_WRITE16_OPC 0x8a
#define SCSI_SERVICE_ACT_IN_OPC 0x9e
#define SCSI_READ_CAPACITY16_SA 0x10
#define SCSI_SA_MSK 0x1f
@@ -423,7 +427,7 @@ sntl_do_identify(struct sg_pt_linux_scsi * ptp, int cns, int nsid,
struct sg_nvme_passthru_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x6; /* Identify */
+ cmd.opcode = 0x6; /* NVME Identify command opcode */
cmd.nsid = nsid;
cmd.cdw10 = cns;
cmd.addr = (uint64_t)(sg_uintptr_t)up;
@@ -693,7 +697,7 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
return res;
}
memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0xa; /* Get feature */
+ cmd.opcode = 0xa; /* NVMe Get feature command */
cmd.nsid = SG_NVME_BROADCAST_NSID;
cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
@@ -742,7 +746,7 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
desc = !!(0x1 & cdbp[1]);
alloc_len = cdbp[4];
memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0xa; /* Get feature */
+ cmd.opcode = 0xa; /* NVMe Get feature command */
cmd.nsid = SG_NVME_BROADCAST_NSID;
cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
@@ -932,7 +936,7 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n",
__func__, dpg_cd, dpg_len);
memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x1d; /* MI send; hmmm same opcode as SEND DIAG */
+ cmd.opcode = 0x1d; /* NVMe MI Send; hmmm same opcode as SEND DIAG */
cmd.addr = (uint64_t)(sg_uintptr_t)dop;
cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
/* dout_len > 0x1000, is this a problem?? */
@@ -987,7 +991,7 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__,
dpg_cd);
memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x1e; /* MI receive */
+ cmd.opcode = 0x1e; /* NVMe MI Receive command */
cmd.addr = (uint64_t)(sg_uintptr_t)dip;
cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
/* din_len > 0x1000, is this a problem?? */
@@ -1238,6 +1242,52 @@ fini:
return res;
}
+static int
+sntl_read(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool is_read10 = (SCSI_READ10_OPC == cdbp[0]);
+ uint64_t lba;
+ uint32_t num;
+ struct sg_nvme_user_io io;
+ struct sg_nvme_user_io * iop = &io;
+
+ memset(iop, 0, sizeof(*iop));
+ iop->opcode = 0x2; /* NVMe Read command */
+ if (is_read10) {
+ lba = sg_get_unaligned_be32(cdbp + 2);
+ num = sg_get_unaligned_be16(cdbp + 7);
+ } else {
+ lba = sg_get_unaligned_be64(cdbp + 2);
+ num = sg_get_unaligned_be32(cdbp + 10);
+ }
+ iop->slba = lba;
+ iop->nblocks = num;
+ return 0;
+}
+
+static int
+sntl_write(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool is_write10 = (SCSI_WRITE10_OPC == cdbp[0]);
+ uint64_t lba;
+ uint32_t num;
+ struct sg_nvme_user_io io;
+ struct sg_nvme_user_io * iop = &io;
+
+ memset(iop, 0, sizeof(*iop));
+ iop->opcode = 0x3; /* NVMe Write command */
+ if (is_write10) {
+ lba = sg_get_unaligned_be32(cdbp + 2);
+ num = sg_get_unaligned_be16(cdbp + 7);
+ } else {
+ lba = sg_get_unaligned_be64(cdbp + 2);
+ num = sg_get_unaligned_be32(cdbp + 10);
+ }
+ return 0;
+}
+
/* Executes NVMe Admin command (or at least forwards it to lower layers).
* Returns 0 for success, negative numbers are negated 'errno' values from
* OS system calls. Positive return values are errors from this package.
@@ -1292,6 +1342,12 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
return sntl_tur(ptp, time_secs, vb);
case SCSI_REQUEST_SENSE_OPC:
return sntl_req_sense(ptp, cdbp, time_secs, vb);
+ case SCSI_READ10_OPC:
+ case SCSI_READ16_OPC:
+ return sntl_read(ptp, cdbp, time_secs, vb);
+ case SCSI_WRITE10_OPC:
+ case SCSI_WRITE16_OPC:
+ return sntl_write(ptp, cdbp, time_secs, vb);
case SCSI_SEND_DIAGNOSTIC_OPC:
return sntl_senddiag(ptp, cdbp, time_secs, vb);
case SCSI_RECEIVE_DIAGNOSTIC_OPC:
diff --git a/src/sg_dd.c b/src/sg_dd.c
index 93edc249..e659a2ca 100644
--- a/src/sg_dd.c
+++ b/src/sg_dd.c
@@ -66,7 +66,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "6.11 20200303";
+static const char * version_str = "6.12 20200331";
#define ME "sg_dd: "
@@ -139,6 +139,7 @@ static int out_partial = 0;
static int64_t out_sparse_num = 0;
static int recovered_errs = 0;
static int unrecovered_errs = 0;
+static int miscompare_errs = 0;
static int read_longs = 0;
static int num_retries = 0;
static int dry_run = 0;
@@ -219,8 +220,10 @@ print_stats(const char * str)
pr2serr("%s%d unrecovered errors\n", str, unrecovered_errs);
pr2serr("%s%d read_longs fetched part of unrecovered read errors\n",
str, read_longs);
- } else if (unrecovered_errs)
+ } else if (unrecovered_errs > 0)
pr2serr("%s%d unrecovered error(s)\n", str, unrecovered_errs);
+ if (miscompare_errs > 0)
+ pr2serr("%s%d miscompare error(s)\n", str, miscompare_errs);
}
@@ -1016,7 +1019,7 @@ err_out:
* SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_MEDIUM_HARD,
* SG_LIB_CAT_ABORTED_COMMAND, -2 -> recoverable (ENOMEM),
* -1 -> unrecoverable error + others. Note: if do_verify is true then does
- * a VERIFY rather tahn a WRITE command. */
+ * a VERIFY rather than a WRITE command. */
static int
sg_write(int sg_fd, uint8_t * buff, int blocks, int64_t to_block,
int bs, const struct flags_t * ofp, bool * diop)
@@ -1092,6 +1095,10 @@ sg_write(int sg_fd, uint8_t * buff, int blocks, int64_t to_block,
case SG_LIB_CAT_UNIT_ATTENTION:
sg_chk_n_print3(op_str, &io_hdr, verbose > 1);
return res;
+ case SG_LIB_CAT_MISCOMPARE:
+ ++miscompare_errs;
+ pr2serr("VERIFY reports miscompare\n");
+ return res;
case SG_LIB_CAT_NOT_READY:
++unrecovered_errs;
pr2serr("device not ready (w)\n");
diff --git a/src/sg_get_elem_status.c b/src/sg_get_elem_status.c
index 96841f68..bbf8ad34 100644
--- a/src/sg_get_elem_status.c
+++ b/src/sg_get_elem_status.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Douglas Gilbert.
+ * Copyright (c) 2019-2020 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -37,7 +37,7 @@
* given SCSI device.
*/
-static const char * version_str = "1.01 20191220"; /* sbc4r15,17 */
+static const char * version_str = "1.02 20200331"; /* sbc4r19 */
#ifndef UINT32_MAX
@@ -54,7 +54,7 @@ static const char * version_str = "1.01 20191220"; /* sbc4r15,17 */
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
struct gpes_desc_t { /* info in returned physical status descriptor */
- bool restore_allowed;
+ bool restoration_allowed;
uint32_t elem_id;
uint8_t phys_elem_type;
uint8_t phys_elem_health;
@@ -215,7 +215,7 @@ decode_elem_status_desc(const uint8_t * bp, struct gpes_desc_t * pedp)
if ((NULL == bp) || (NULL == pedp))
return;
pedp->elem_id = sg_get_unaligned_be32(bp + 4);
- pedp->restore_allowed = (bool)(bp[13] & 1);
+ pedp->restoration_allowed = (bool)(bp[13] & 1);
pedp->phys_elem_type = bp[14];
pedp->phys_elem_health = bp[15];
pedp->assoc_cap = sg_get_unaligned_be64(bp + 16);
@@ -549,8 +549,8 @@ start_response:
printf("depopulation operations in progress");
else if (0xff == j)
printf("depopulation completed, no errors");
- if (a_ped.restore_allowed)
- printf(" [restore allowed]");
+ if (a_ped.restoration_allowed)
+ printf(" [restoration allowed]");
printf("\n");
}
}
diff --git a/src/sg_write_x.c b/src/sg_write_x.c
index 0bc28c27..925aa92f 100644
--- a/src/sg_write_x.c
+++ b/src/sg_write_x.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Douglas Gilbert.
+ * Copyright (c) 2017-2020 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -38,7 +38,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.22 20191220";
+static const char * version_str = "1.23 20200331";
/* Protection Information refers to 8 bytes of extra information usually
* associated with each logical block and is often abbreviated to PI while
@@ -1092,6 +1092,14 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
x_cdb[10] |= 0x10;
if (op->fua)
x_cdb[10] |= 0x8;
+ if (op->dld) { /* added in sbc4r19 */
+ if (op->dld & 1)
+ x_cdb[11] |= 0x1;
+ if (op->dld & 2)
+ x_cdb[11] |= 0x2;
+ if (op->dld & 4)
+ x_cdb[11] |= 0x4;
+ }
sg_put_unaligned_be32(op->ref_tag, x_cdb + 20);
sg_put_unaligned_be16(op->app_tag, x_cdb + 24);
sg_put_unaligned_be16(op->tag_mask, x_cdb + 26);
diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp
index 9367043b..d5d32eb0 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -108,7 +108,7 @@
using namespace std;
-static const char * version_str = "1.76 20200307";
+static const char * version_str = "1.78 20200403";
#ifdef __GNUC__
#ifndef __clang__
@@ -545,8 +545,8 @@ sg_flags_str(int flags, int b_len, char * b)
if (n >= b_len)
goto fini;
}
- if (SGV4_FLAG_SIG_ON_OTHER & flags) { /* 0x200 */
- n += sg_scnpr(b + n, b_len - n, "SIGOTH|");
+ if (SGV4_FLAG_SIGNAL & flags) { /* 0x200 */
+ n += sg_scnpr(b + n, b_len - n, "SIGNAL|");
if (n >= b_len)
goto fini;
}
@@ -2881,6 +2881,8 @@ sg_start_io(Rq_elem * rep, mrq_arr_t & def_arr, int & pack_id,
if ((0 == xtrp->hpv4_ind) && (nblks < rep->num_blks))
flags |= SGV4_FLAG_KEEP_SHARE;
}
+ if (xtrp && wr && rep->has_share && ! is_wr2)
+ flags |= SGV4_FLAG_KEEP_SHARE; /* set on first write only */
} else
memset(hp, 0, sizeof(struct sg_io_hdr));
if (clp->debug > 3) {
@@ -3500,10 +3502,10 @@ bypass:
mmp = (uint8_t *)mmap(NULL, num, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (MAP_FAILED == mmp) {
- int err = errno;
+ int err = errno;
pr2serr_lk("sgh_dd: %s: sz=%d, fd=%d, mmap() failed: %s\n",
- __func__, num, fd, strerror(err));
+ __func__, num, fd, strerror(err));
return 0;
}
*mmpp = mmp;
diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c
index 6f71283c..8980864a 100644
--- a/testing/sgs_dd.c
+++ b/testing/sgs_dd.c
@@ -51,6 +51,8 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/eventfd.h>
+#include <sys/epoll.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
@@ -81,7 +83,7 @@
#include "sg_unaligned.h"
-static const char * version_str = "4.12 20190824";
+static const char * version_str = "4.14 20200330";
static const char * my_name = "sgs_dd";
#define DEF_BLOCK_SIZE 512
@@ -114,6 +116,7 @@ static const char * my_name = "sgs_dd";
struct flags_t {
bool dio;
+ bool evfd;
bool excl;
bool immed;
bool mmap;
@@ -147,13 +150,16 @@ typedef struct request_collection
{
bool in_is_sg;
bool out_is_sg;
+ bool no_sig;
bool use_rt_sig;
int infd;
+ int in_evfd;
int in_blk; /* most recent read */
int in_count; /* most recent read */
int in_done_count; /* count of completed in blocks */
int in_partial;
int outfd;
+ int out_evfd;
int lowest_seek;
int out_blk; /* most recent write */
int out_count; /* most recent write */
@@ -185,23 +191,27 @@ usage(void)
{
printf("Usage: "
"sgs_dd [bpt=BPT] [bs=BS] [count=NUM] [deb=DEB] [if=IFILE]\n"
- " [iflag=FLAGS] [of=OFILE] [oflag=FLAGS] "
- "[rt_sig=0|1]\n"
- " [seek=SEEK] [skip=SKIP] [--version]\n"
+ " [iflag=FLAGS] [no_sig=0|1] [of=OFILE] "
+ "[oflag=FLAGS]\n"
+ " [rt_sig=0|1] [seek=SEEK] [skip=SKIP] "
+ "[--version]\n"
"where:\n"
" bpt blocks_per_transfer (default: 65536/bs (or 128 for "
"bs=512))\n"
" bs must be the logical block size of device (def: 512)\n"
" deb debug: 0->no debug (def); > 0 -> more debug\n"
- " iflag comma separated list from: dio,excl,immed,mmap,noxfer,"
- "null,pack,\n"
- " tag,v3,v4 bound to IFILE\n"
+ " iflag comma separated list from: dio,evfd,excl,immed,mmap,"
+ "noxfer,\n"
+ " null,pack,tag,v3,v4 bound to IFILE\n"
+ " no_sig 0-> use signals (def); 1-> no signals, hard polling "
+ "instead\n"
" oflag same flags as iflag but bound to OFILE\n"
" rt_sig 0->use SIGIO (def); 1->use RT sig (SIGRTMIN + 1)\n"
" <other operands> as per dd command\n\n");
- printf("dd clone for testing Linux sg driver SIGPOLL and friends. Either "
- "IFILE or\nOFILE must be a scsi generic device. If OFILE not given "
- "then /dev/null\nassumed.\n");
+ printf("dd clone for testing Linux sg driver SIGPOLL and/or polling. "
+ "Either\nIFILE or OFILE must be a scsi generic device. If OFILE "
+ "not given then\n/dev/null assumed (rather than stdout like "
+ "dd).\n");
}
/* Return of 0 -> success, -1 -> failure, 2 -> try again */
@@ -242,8 +252,7 @@ read_capacity(int sg_fd, int * num_sect, int * sect_sz)
*sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
(rcBuff[6] << 8) | rcBuff[7];
#ifdef SG_DEBUG
- fprintf(stderr, "number of sectors=%d, sector size=%d\n", *num_sect,
- *sect_sz);
+ pr2serr("number of sectors=%d, sector size=%d\n", *num_sect, *sect_sz);
#endif
return 0;
}
@@ -252,9 +261,9 @@ read_capacity(int sg_fd, int * num_sect, int * sect_sz)
static int
sg_start_io(Rq_coll * clp, Rq_elem * rep)
{
+ int res;
int fd = rep->wr ? clp->outfd : clp->infd;
struct flags_t * flagp = rep->wr ? rep->oflagp : rep->iflagp;
- int res;
sg_io_hdr_t * hp = &rep->io_hdr;
struct sg_io_v4 * h4p = &rep->io_v4;
@@ -285,12 +294,14 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep)
hp->flags |= SGV4_FLAG_IMMED;
if (flagp->mmap)
hp->flags |= SG_FLAG_MMAP_IO;
+ if (flagp->evfd)
+ hp->flags |= SGV4_FLAG_EVENTFD;
#ifdef SG_DEBUG
- fprintf(stderr, "%s: SCSI %s, blk=%d num_blks=%d\n", __func__,
- rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
+ pr2serr("%s: SCSI %s, blk=%d num_blks=%d\n", __func__,
+ rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
sg_print_command(hp->cmdp);
- fprintf(stderr, "dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n",
- hp->dxfer_direction, hp->dxfer_len, hp->dxferp, hp->cmd_len);
+ pr2serr("dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n", hp->dxfer_direction,
+ hp->dxfer_len, hp->dxferp, hp->cmd_len);
#endif
while (((res = write(fd, hp, sizeof(sg_io_hdr_t))) < 0) &&
@@ -303,13 +314,13 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep)
rep->state = SGQ_IO_WAIT; /* busy so wait */
return 0;
}
- fprintf(stderr, "%s: write(): %s [%d]\n", __func__, strerror(errno),
- errno);
+ pr2serr("%s: write(): %s [%d]\n", __func__, strerror(errno), errno);
rep->state = SGQ_IO_ERR;
return res;
}
rep->state = SGQ_IO_STARTED;
- clp->sigs_waiting++;
+ if (! clp->no_sig)
+ clp->sigs_waiting++;
return 0;
do_v4:
memset(h4p, 0, sizeof(struct sg_io_v4));
@@ -338,6 +349,11 @@ do_v4:
h4p->flags |= SG_FLAG_MMAP_IO;
if (flagp->tag)
h4p->flags |= SGV4_FLAG_YIELD_TAG;
+ if (flagp->evfd)
+ h4p->flags |= SGV4_FLAG_EVENTFD;
+ if (! clp->no_sig)
+ h4p->flags |= SGV4_FLAG_SIGNAL;
+
while (((res = ioctl(fd, SG_IOSUBMIT, h4p)) < 0) && (EINTR == errno))
;
if (res < 0) {
@@ -347,16 +363,17 @@ do_v4:
rep->state = SGQ_IO_WAIT; /* busy so wait */
return 0;
}
- fprintf(stderr, "%s: ioctl(SG_IOSUBMIT): %s [%d]\n", __func__,
+ pr2serr("%s: ioctl(SG_IOSUBMIT): %s [%d]\n", __func__,
strerror(errno), errno);
rep->state = SGQ_IO_ERR;
return res;
}
rep->state = SGQ_IO_STARTED;
- clp->sigs_waiting++;
+ if (! clp->no_sig)
+ clp->sigs_waiting++;
#ifdef SG_DEBUG
if (rep->wr ? clp->oflag.tag : clp->iflag.tag)
- fprintf(stderr, "%s: generated_tag=0x%" PRIx64 "\n", __func__,
+ pr2serr("%s: generated_tag=0x%" PRIx64 "\n", __func__,
(uint64_t)h4p->generated_tag);
#endif
return 0;
@@ -366,10 +383,11 @@ do_v4:
static int
sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp)
{
+ struct flags_t *flagsp = wr ? &clp->oflag : &clp->iflag;
bool dio = false;
- bool is_v4 = wr ? clp->oflag.v4 : clp->iflag.v4;
- bool use_pack = wr ? clp->oflag.pack : clp->iflag.pack;
- bool use_tag = wr ? clp->oflag.tag : clp->iflag.tag;
+ bool is_v4 = flagsp->v4;
+ bool use_pack = flagsp->pack;
+ bool use_tag = flagsp->tag;
int fd = wr ? clp->outfd : clp->infd;
int res, id, n;
sg_io_hdr_t io_hdr;
@@ -382,21 +400,20 @@ sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp)
goto do_v4;
memset(&io_hdr, 0 , sizeof(sg_io_hdr_t));
while (((res = read(fd, &io_hdr, sizeof(sg_io_hdr_t))) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
rep = (Rq_elem *)io_hdr.usr_ptr;
if (rep)
- dio = rep->wr ? clp->oflag.dio : clp->iflag.dio;
+ dio = flagsp->dio;
if (res < 0) {
res = -errno;
- fprintf(stderr, "%s: read(): %s [%d]\n", __func__, strerror(errno),
- errno);
+ pr2serr("%s: read(): %s [%d]\n", __func__, strerror(errno), errno);
if (rep)
rep->state = SGQ_IO_ERR;
return res;
}
if (! (rep && (SGQ_IO_STARTED == rep->state))) {
- fprintf(stderr, "%s: bad usr_ptr\n", __func__);
+ pr2serr("%s: bad usr_ptr\n", __func__);
if (rep)
rep->state = SGQ_IO_ERR;
return -1;
@@ -410,8 +427,8 @@ sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp)
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error on block=%d, num=%d\n",
- rep->blk, rep->num_blks);
+ pr2serr("Recovered error on block=%d, num=%d\n", rep->blk,
+ rep->num_blks);
break;
case SG_LIB_CAT_UNIT_ATTENTION:
return 1;
@@ -425,8 +442,8 @@ sg_finish_io(Rq_coll * clp, bool wr, Rq_elem ** repp)
clp->sum_of_resids += hp->resid;
rep->state = SGQ_IO_FINISHED;
#ifdef SG_DEBUG
- fprintf(stderr, "%s: %s ", __func__, wr ? "writing" : "reading");
- fprintf(stderr, " SGQ_IO_FINISHED elem idx=%zd\n", rep - clp->elem);
+ pr2serr("%s: %s ", __func__, wr ? "writing" : "reading");
+ pr2serr(" SGQ_IO_FINISHED elem idx=%zd\n", rep - clp->elem);
#endif
return 0;
do_v4:
@@ -435,14 +452,14 @@ do_v4:
while (true) {
if ( ((res = ioctl(fd, SG_GET_NUM_WAITING, &n))) < 0) {
res = -errno;
- fprintf(stderr, "%s: ioctl(SG_GET_NUM_WAITING): %s [%d]\n",
+ pr2serr("%s: ioctl(SG_GET_NUM_WAITING): %s [%d]\n",
__func__, strerror(errno), errno);
return res;
}
if (n > 0) {
if ( ((res = ioctl(fd, SG_GET_PACK_ID, &id))) < 0) {
res = -errno;
- fprintf(stderr, "%s: ioctl(SG_GET_PACK_ID): %s [%d]\n",
+ pr2serr("%s: ioctl(SG_GET_PACK_ID): %s [%d]\n",
__func__, strerror(errno), errno);
return res;
}
@@ -458,20 +475,22 @@ do_v4:
else if (use_pack)
io_v4.request_extra = id;
io_v4.flags |= SGV4_FLAG_IMMED;
+ if (flagsp->evfd)
+ io_v4.flags |= SGV4_FLAG_EVENTFD;
while (((res = ioctl(fd, SG_IORECEIVE, &io_v4)) < 0) &&
- ((EINTR == errno) || (EAGAIN == errno)))
+ ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
;
rep = (Rq_elem *)(unsigned long)io_v4.usr_ptr;
if (res < 0) {
res = -errno;
- fprintf(stderr, "%s: ioctl(SG_IORECEIVE): %s [%d]\n", __func__,
+ pr2serr("%s: ioctl(SG_IORECEIVE): %s [%d]\n", __func__,
strerror(errno), errno);
if (rep)
rep->state = SGQ_IO_ERR;
return res;
}
if (! (rep && (SGQ_IO_STARTED == rep->state))) {
- fprintf(stderr, "%s: bad usr_ptr=0x%p\n", __func__, rep);
+ pr2serr("%s: bad usr_ptr=0x%p\n", __func__, rep);
if (rep)
rep->state = SGQ_IO_ERR;
return -1;
@@ -489,8 +508,8 @@ do_v4:
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
- fprintf(stderr, "Recovered error on block=%d, num=%d\n",
- rep->blk, rep->num_blks);
+ pr2serr("Recovered error on block=%d, num=%d\n", rep->blk,
+ rep->num_blks);
break;
case SG_LIB_CAT_UNIT_ATTENTION:
return 1;
@@ -508,32 +527,37 @@ do_v4:
clp->sum_of_resids += h4p->din_resid;
rep->state = SGQ_IO_FINISHED;
#ifdef SG_DEBUG
- fprintf(stderr, "%s: %s ", __func__, wr ? "writing" : "reading");
- fprintf(stderr, " SGQ_IO_FINISHED elem idx=%zd\n", rep - clp->elem);
+ pr2serr("%s: %s ", __func__, wr ? "writing" : "reading");
+ pr2serr(" SGQ_IO_FINISHED elem idx=%zd\n", rep - clp->elem);
if (use_pack)
- fprintf(stderr, "%s: pack_id=%d\n", __func__, h4p->request_extra);
+ pr2serr("%s: pack_id=%d\n", __func__, h4p->request_extra);
else if (use_tag)
- fprintf(stderr, "%s: request_tag=0x%" PRIx64 "\n", __func__,
+ pr2serr("%s: request_tag=0x%" PRIx64 "\n", __func__,
(uint64_t)h4p->request_tag);
#endif
return 0;
}
static int
-sz_reserve(int fd, int bs, int bpt, bool rt_sig, bool pack, bool tag, bool vb)
+sz_reserve(Rq_coll * clp, bool is_in)
{
- int res, t, flags;
+ const struct flags_t *flagsp = is_in ? &clp->iflag : &clp->oflag;
+ bool pack = flagsp->pack;
+ bool vb = clp->debug;
+ int res, t, flags, err;
+ int fd = is_in ? clp->infd : clp->outfd;
+ int tag = flagsp->tag;
struct sg_extended_info sei;
struct sg_extended_info * seip;
seip = &sei;
res = ioctl(fd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
- fprintf(stderr, "sgs_dd: sg driver prior to 3.0.00\n");
+ pr2serr("sgs_dd: sg driver prior to 3.0.00\n");
return 1;
} else if (t < 40000) {
if (vb)
- fprintf(stderr, "sgs_dd: warning: sg driver prior to 4.0.00\n");
+ pr2serr("sgs_dd: warning: sg driver prior to 4.0.00\n");
sgs_old_sg_driver = true;
} else if (t < 40030) {
sgs_old_sg_driver = false;
@@ -541,7 +565,7 @@ sz_reserve(int fd, int bs, int bpt, bool rt_sig, bool pack, bool tag, bool vb)
} else
sgs_full_v4_sg_driver = true;
res = 0;
- t = bs * bpt;
+ t = clp->bs * clp->bpt;
res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
if (res < 0)
perror("sgs_dd: SG_SET_RESERVED_SIZE error");
@@ -577,19 +601,45 @@ sz_reserve(int fd, int bs, int bpt, bool rt_sig, bool pack, bool tag, bool vb)
}
}
}
+ if (flagsp->evfd) {
+ int evfd = eventfd(0,0);
+
+ if (evfd < 0) {
+ err = errno;
+ pr2serr("eventfd() failed: %s\n", strerror(err));
+ return 1;
+ }
+ if (is_in)
+ clp->in_evfd = evfd;
+ else
+ clp->out_evfd = evfd;
+
+ memset(seip, 0, sizeof(*seip));
+ seip->sei_wr_mask |= SG_SEIM_EVENTFD;
+ seip->sei_rd_mask |= SG_SEIM_EVENTFD;
+ seip->share_fd = evfd;
+ if (ioctl(fd, SG_SET_GET_EXTENDED, seip) < 0) {
+ err = errno;
+ pr2serr("ioctl(EXTENDED(SG_SEIM_EVENTFD)) failed, "
+ "errno=%d %s\n", errno, strerror(errno));
+ return 1;
+ }
+ }
}
- if (-1 == fcntl(fd, F_SETOWN, getpid())) {
- perror("fcntl(F_SETOWN)");
- return 1;
- }
- flags = fcntl(fd, F_GETFL, 0);
- if (-1 == fcntl(fd, F_SETFL, flags | O_ASYNC)) {
- perror("fcntl(F_SETFL)");
- return 1;
- }
- if (rt_sig) { /* displaces SIGIO/SIGPOLL with SIGRTMIN + 1 */
- if (-1 == fcntl(fd, F_SETSIG, SIGRTMIN + 1))
- perror("fcntl(F_SETSIG)");
+ if (!clp->no_sig) {
+ if (-1 == fcntl(fd, F_SETOWN, getpid())) {
+ perror("fcntl(F_SETOWN)");
+ return 1;
+ }
+ flags = fcntl(fd, F_GETFL, 0);
+ if (-1 == fcntl(fd, F_SETFL, flags | O_ASYNC)) {
+ perror("fcntl(F_SETFL)");
+ return 1;
+ }
+ if (clp->use_rt_sig) {/* displaces SIGIO/SIGPOLL with SIGRTMIN + 1 */
+ if (-1 == fcntl(fd, F_SETSIG, SIGRTMIN + 1))
+ perror("fcntl(F_SETSIG)");
+ }
}
return 0;
}
@@ -614,7 +664,7 @@ init_elems(Rq_coll * clp)
rep->buffp = sg_memalign(clp->bpt * clp->bs, 0, &rep->free_buffp,
false);
if (NULL == rep->buffp) {
- fprintf(stderr, "out of memory creating user buffers\n");
+ pr2serr("out of memory creating user buffers\n");
res = -ENOMEM;
}
}
@@ -643,7 +693,7 @@ start_read(Rq_coll * clp)
char ebuff[EBUFF_SZ];
#ifdef SG_DEBUG
- fprintf(stderr, "%s: elem idx=%zd\n", __func__, rep - clp->elem);
+ pr2serr("%s: elem idx=%zd\n", __func__, rep - clp->elem);
#endif
rep->wr = false;
rep->blk = clp->in_blk;
@@ -659,7 +709,7 @@ start_read(Rq_coll * clp)
return res;
}
clp->bpt = (buf_sz + clp->bs - 1) / clp->bs;
- fprintf(stderr, "Reducing blocks per transfer to %d\n", clp->bpt);
+ pr2serr("Reducing blocks per transfer to %d\n", clp->bpt);
if (clp->bpt < 1)
return -ENOMEM;
res = sg_start_io(clp, rep);
@@ -667,8 +717,7 @@ start_read(Rq_coll * clp)
res = -ENOMEM;
}
else if (res < 0) {
- fprintf(stderr, "sgs_dd inputting from sg failed, blk=%d\n",
- rep->blk);
+ pr2serr("sgs_dd inputting from sg failed, blk=%d\n", rep->blk);
rep->state = SGQ_IO_ERR;
return res;
}
@@ -720,7 +769,7 @@ start_write(Rq_coll * clp)
return -1;
}
#ifdef SG_DEBUG
- fprintf(stderr, "%s: elem idx=%zd\n", __func__, rep - clp->elem);
+ pr2serr("%s: elem idx=%zd\n", __func__, rep - clp->elem);
#endif
rep->wr = true;
blocks = rep->num_blks;
@@ -732,7 +781,7 @@ start_write(Rq_coll * clp)
if (1 == res) /* ENOMEM, give up */
return -ENOMEM;
else if (res < 0) {
- fprintf(stderr, "sgs_dd output to sg failed, blk=%d\n", rep->blk);
+ pr2serr("sgs_dd output to sg failed, blk=%d\n", rep->blk);
rep->state = SGQ_IO_ERR;
return res;
}
@@ -771,14 +820,14 @@ do_sigwait(Rq_coll * clp, bool inc1_clear0)
struct timespec ts;
if (clp->debug > 9)
- fprintf(stderr, "%s: inc1_clear0=%d\n", __func__, (int)inc1_clear0);
+ pr2serr("%s: inc1_clear0=%d\n", __func__, (int)inc1_clear0);
ts.tv_sec = 60; /* 60 second timeout */
ts.tv_nsec = 0;
while (sigtimedwait(&clp->blocked_sigs, &info, &ts) < 0) {
if (EINTR != errno) {
int err = errno;
- fprintf(stderr, "%s: sigtimedwait(): %s [%d]\n", __func__,
+ pr2serr("%s: sigtimedwait(): %s [%d]\n", __func__,
strerror(err), err); /* EAGAIN is timeout */
return -err; /* EAGAIN is timeout error */
}
@@ -796,35 +845,50 @@ do_sigwait(Rq_coll * clp, bool inc1_clear0)
} else
clp->sigs_waiting = 0;
} else {
- fprintf(stderr, "%s: sigwaitinfo() returned si_signo=%d\n",
+ pr2serr("%s: sigwaitinfo() returned si_signo=%d\n",
__func__, info.si_signo);
return -EINVAL;
}
return 0;
}
-/* Returns 1 on success (found), 0 on not found, -1 on error. */
+/* Returns 1 (or more) on success (found), 0 on not found, -1 on error. */
static int
-do_poll_for_in(Rq_coll * clp, int fd)
+do_num_poll_in(Rq_coll * clp, int fd, bool is_evfd)
{
int err;
struct pollfd a_pollfd = {0, POLLIN | POLLOUT, 0};
- if (clp->sigs_waiting) {
- int res = do_sigwait(clp, true);
+ if (! clp->no_sig) {
+ if (clp->sigs_waiting) {
+ int res = do_sigwait(clp, true);
- if (res < 0)
- return res;
+ if (res < 0)
+ return res;
+ }
}
a_pollfd.fd = fd;
if (poll(&a_pollfd, 1, 0) < 0) {
err = errno;
- fprintf(stderr, "%s: poll(): %s [%d]\n", __func__, strerror(err),
- err);
+ pr2serr("%s: poll(): %s [%d]\n", __func__, strerror(err), err);
return -err;
}
- /* fprintf(stderr, "%s: revents=0x%x\n", __func__, a_pollfd.revents); */
- return !!(a_pollfd.revents & POLLIN);
+ /* pr2serr("%s: revents=0x%x\n", __func__, a_pollfd.revents); */
+ if (a_pollfd.revents & POLLIN) {
+ if (is_evfd) {
+ uint64_t count;
+
+ if (read(fd, &count, sizeof(count)) < 0) {
+ err = errno;
+ pr2serr("%s: read(): %s [%d]\n", __func__,
+ strerror(err), err);
+ return -err;
+ }
+ return (int)count;
+ } else
+ return 1; /* could be more but don't know without evfd */
+ } else
+ return 0;
}
static int
@@ -832,28 +896,35 @@ can_read_write(Rq_coll * clp)
{
Rq_elem * rep = NULL;
bool writeable = false;
+ bool in_is_evfd = (clp->in_evfd >= 0);
+ bool out_is_evfd = (clp->out_evfd >= 0);
int res = 0;
int reading = 0;
int writing = 0;
int rd_waiting = 0;
int wr_waiting = 0;
int sg_finished = 0;
+ int num;
+ int ofd = out_is_evfd ? clp->out_evfd : clp->outfd;
+ int ifd= in_is_evfd ? clp->in_evfd : clp->infd;
/* if write completion pending, then complete it + start read */
if (clp->out_is_sg) {
- while ((res = do_poll_for_in(clp, clp->outfd))) {
- if (res < 0)
- return res;
- res = sg_finish_io(clp, 1, &rep);
+ while ((res = do_num_poll_in(clp, ofd, out_is_evfd))) {
if (res < 0)
return res;
- else if (1 == res) {
- res = sg_start_io(clp, rep);
- if (0 != res)
- return -1; /* give up if any problems with retry */
+ num = res;
+ while (--num >= 0) {
+ res = sg_finish_io(clp, true /* write */, &rep);
+ if (res < 0)
+ return res;
+ else if (1 == res) {
+ res = sg_start_io(clp, rep);
+ if (0 != res)
+ return -1; /* give up if any problems with retry */
+ } else
+ sg_finished++;
}
- else
- sg_finished++;
}
while ((rep = clp->wr_posp) && (SGQ_IO_FINISHED == rep->state) &&
rep->wr && (rep != clp->rd_posp)) {
@@ -875,20 +946,22 @@ can_read_write(Rq_coll * clp)
/* if read completion pending, then complete it + start maybe write */
if (clp->in_is_sg) {
- while ((res = do_poll_for_in(clp, clp->infd))) {
+ while ((res = do_num_poll_in(clp, ifd, in_is_evfd))) {
if (res < 0)
return res;
- res = sg_finish_io(clp, 0, &rep);
- if (res < 0)
- return res;
- if (1 == res) {
- res = sg_start_io(clp, rep);
- if (0 != res)
- return -1; /* give up if any problems with retry */
- }
- else {
- sg_finished++;
- clp->in_done_count -= rep->num_blks;
+ num = res;
+ while (--num >= 0) {
+ res = sg_finish_io(clp, false /* read */, &rep);
+ if (res < 0)
+ return res;
+ if (1 == res) {
+ res = sg_start_io(clp, rep);
+ if (0 != res)
+ return -1; /* give up if any problems with retry */
+ } else {
+ sg_finished++;
+ clp->in_done_count -= rep->num_blks;
+ }
}
}
}
@@ -919,7 +992,7 @@ can_read_write(Rq_coll * clp)
}
if (clp->debug) {
if ((clp->debug >= 9) || wr_waiting || rd_waiting)
- fprintf(stderr, "%d/%d (nwb/nrb): read=%d/%d (do/wt) "
+ pr2serr("%d/%d (nwb/nrb): read=%d/%d (do/wt) "
"write=%d/%d (do/wt) writeable=%d sg_fin=%d\n",
clp->out_blk, clp->in_blk, reading, rd_waiting,
writing, wr_waiting, (int)writeable, sg_finished);
@@ -935,7 +1008,7 @@ can_read_write(Rq_coll * clp)
return SGQ_CAN_DO_NOTHING;
/* usleep(10000); */ /* hang about for 10 milliseconds */
- if (clp->sigs_waiting) {
+ if ((! clp->no_sig) && clp->sigs_waiting) {
res = do_sigwait(clp, false);
if (res < 0)
return res;
@@ -974,6 +1047,8 @@ process_flags(const char * arg, struct flags_t * fp)
*np++ = '\0';
if (0 == strcmp(cp, "dio"))
fp->dio = true;
+ else if (0 == strcmp(cp, "evfd"))
+ fp->evfd = true;
else if (0 == strcmp(cp, "excl"))
fp->excl = true;
else if (0 == strcmp(cp, "immed"))
@@ -1025,6 +1100,8 @@ main(int argc, char * argv[])
memset(clp, 0, sizeof(*clp));
clp->bpt = 0;
+ clp->in_evfd = -1;
+ clp->out_evfd = -1;
inf[0] = '\0';
outf[0] = '\0';
if (argc < 2) {
@@ -1051,7 +1128,7 @@ main(int argc, char * argv[])
else if (0 == strcmp(key,"count"))
count = sg_get_num(buf);
else if (0 == strcmp(key,"deb"))
- clp->debug = sg_get_num(buf);
+ clp->debug += sg_get_num(buf);
else if (0 == strcmp(key,"ibs"))
ibs = sg_get_num(buf);
else if (strcmp(key,"if") == 0) {
@@ -1062,7 +1139,9 @@ main(int argc, char * argv[])
pr2serr("%sbad argument to 'iflag='\n", my_name);
return SG_LIB_SYNTAX_ERROR;
}
- } else if (0 == strcmp(key,"obs"))
+ } else if (0 == strcmp(key,"no_sig"))
+ clp->no_sig = !!sg_get_num(buf);
+ else if (0 == strcmp(key,"obs"))
obs = sg_get_num(buf);
else if (strcmp(key,"of") == 0) {
memcpy(outf, buf, INOUTF_SZ);
@@ -1079,10 +1158,18 @@ main(int argc, char * argv[])
else if (0 == strcmp(key,"skip"))
skip = sg_get_num(buf);
else if ((0 == strcmp(key,"-V")) || (0 == strcmp(key,"--version"))) {
- fprintf(stderr, "%s: version: %s\n", my_name, version_str);
+ pr2serr("%s: version: %s\n", my_name, version_str);
return 0;
- } else {
- fprintf(stderr, "Unrecognized argument '%s'\n", key);
+ } else if (0 == strcmp(key,"-vvvv"))
+ clp->debug +=4;
+ else if (0 == strcmp(key,"-vvv"))
+ clp->debug +=3;
+ else if (0 == strcmp(key,"-vv"))
+ clp->debug +=2;
+ else if ((0 == strcmp(key,"-v")) || (0 == strcmp(key,"--verbose")))
+ ++clp->debug;
+ else {
+ pr2serr("Unrecognized argument '%s'\n", key);
usage();
return 1;
}
@@ -1093,7 +1180,7 @@ main(int argc, char * argv[])
bs_given = true;
if ((ibs && (ibs != clp->bs)) || (obs && (obs != clp->bs))) {
- fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
+ pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
usage();
return 1;
}
@@ -1102,26 +1189,28 @@ main(int argc, char * argv[])
if (0 == clp->bpt)
clp->bpt = 1;
if (! bs_given)
- fprintf(stderr, "Assume blocks size bs=%d [bytes] and blocks "
+ pr2serr("Assume blocks size bs=%d [bytes] and blocks "
"per transfer bpt=%d\n", clp->bs, clp->bpt);
} else if (! bs_given)
- fprintf(stderr, "Assume 'bs' (block size) of %d bytes\n", clp->bs);
+ pr2serr("Assume 'bs' (block size) of %d bytes\n", clp->bs);
if ((skip < 0) || (seek < 0)) {
- fprintf(stderr, "skip and seek cannot be negative\n");
+ pr2serr("skip and seek cannot be negative\n");
return 1;
}
#ifdef SG_DEBUG
- fprintf(stderr, "sgs_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
+ pr2serr("sgs_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
inf, skip, outf, seek, count);
#endif
- /* Need to block signals before SIGPOLL is enabled in sz_reserve() */
- sigemptyset(&clp->blocked_sigs);
- if (clp->use_rt_sig)
- sigaddset(&clp->blocked_sigs, SIGRTMIN + 1);
- sigaddset(&clp->blocked_sigs, SIGINT);
- sigaddset(&clp->blocked_sigs, SIGPOLL);
- sigprocmask(SIG_BLOCK, &clp->blocked_sigs, 0);
+ if (! clp->no_sig) {
+ /* Need to block signals before SIGPOLL is enabled in sz_reserve() */
+ sigemptyset(&clp->blocked_sigs);
+ if (clp->use_rt_sig)
+ sigaddset(&clp->blocked_sigs, SIGRTMIN + 1);
+ sigaddset(&clp->blocked_sigs, SIGINT);
+ sigaddset(&clp->blocked_sigs, SIGPOLL);
+ sigprocmask(SIG_BLOCK, &clp->blocked_sigs, 0);
+ }
clp->infd = STDIN_FILENO;
clp->outfd = STDOUT_FILENO;
@@ -1151,13 +1240,12 @@ main(int argc, char * argv[])
open_fl = clp->iflag.excl ? O_EXCL : 0;
open_fl |= (O_RDWR | O_NONBLOCK);
if ((clp->infd = open(inf, open_fl)) < 0) {
- fprintf(stderr, "If %s is a sg device, need read+write "
+ pr2serr("If %s is a sg device, need read+write "
"permissions, even to read it!\n", inf);
return 1;
}
clp->in_is_sg = true;
- if (sz_reserve(clp->infd, clp->bs, clp->bpt, clp->use_rt_sig,
- clp->iflag.pack, clp->iflag.tag, clp->debug))
+ if (sz_reserve(clp, true /* is_in */))
return 1;
if (sgs_old_sg_driver && (clp->iflag.v4 || clp->oflag.v4)) {
pr2serr("Unable to implement v4 flag because sg driver too "
@@ -1176,9 +1264,7 @@ main(int argc, char * argv[])
}
else {
clp->out_is_sg = true;
- if (sz_reserve(clp->outfd, clp->bs, clp->bpt,
- clp->use_rt_sig, clp->oflag.pack,
- clp->oflag.tag, clp->debug))
+ if (sz_reserve(clp, false /* hence ! is_in */))
return 1;
if (sgs_old_sg_driver && (clp->iflag.v4 || clp->oflag.v4)) {
pr2serr("Unable to implement v4 flag because sg driver "
@@ -1212,7 +1298,7 @@ main(int argc, char * argv[])
}
} else if ('\0' == outf[0]) {
if (STDIN_FILENO == clp->infd) {
- fprintf(stderr, "Can't have both 'if' as stdin _and_ 'of' as "
+ pr2serr("Can't have both 'if' as stdin _and_ 'of' as "
"/dev/null\n");
return 1;
}
@@ -1225,8 +1311,7 @@ main(int argc, char * argv[])
/* ignore any seek */
} else { /* must be '-' for stdout */
if (STDIN_FILENO == clp->infd) {
- fprintf(stderr, "Can't have both 'if' as stdin _and_ 'of' as "
- "stdout\n");
+ pr2serr("Can't have both 'if' as stdin _and_ 'of' as stdout\n");
return 1;
}
}
@@ -1236,12 +1321,11 @@ main(int argc, char * argv[])
if (clp->in_is_sg) {
res = read_capacity(clp->infd, &in_num_sect, &in_sect_sz);
if (2 == res) {
- fprintf(stderr, "Unit attention, media changed(in), try "
- "again\n");
+ pr2serr("Unit attention, media changed(in), try again\n");
res = read_capacity(clp->infd, &in_num_sect, &in_sect_sz);
}
if (0 != res) {
- fprintf(stderr, "Unable to read capacity on %s\n", inf);
+ pr2serr("Unable to read capacity on %s\n", inf);
in_num_sect = -1;
}
else {
@@ -1252,12 +1336,11 @@ main(int argc, char * argv[])
if (clp->out_is_sg) {
res = read_capacity(clp->outfd, &out_num_sect, &out_sect_sz);
if (2 == res) {
- fprintf(stderr, "Unit attention, media changed(out), try "
- "again\n");
+ pr2serr("Unit attention, media changed(out), try again\n");
res = read_capacity(clp->outfd, &out_num_sect, &out_sect_sz);
}
if (0 != res) {
- fprintf(stderr, "Unable to read capacity on %s\n", outf);
+ pr2serr("Unable to read capacity on %s\n", outf);
out_num_sect = -1;
}
else {
@@ -1266,8 +1349,8 @@ main(int argc, char * argv[])
}
}
#ifdef SG_DEBUG
- fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, "
- "out_num_sect=%d\n", count, in_num_sect, out_num_sect);
+ pr2serr("Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n",
+ count, in_num_sect, out_num_sect);
#endif
if (in_num_sect > 0) {
if (out_num_sect > 0)
@@ -1281,7 +1364,7 @@ main(int argc, char * argv[])
}
#ifdef SG_DEBUG
- fprintf(stderr, "Start of loop, count=%d, bpt=%d\n", count, clp->bpt);
+ pr2serr("Start of loop, count=%d, bpt=%d\n", count, clp->bpt);
#endif
clp->in_count = count;
@@ -1301,7 +1384,7 @@ main(int argc, char * argv[])
if (SGQ_CAN_READ & crw) {
res = start_read(clp);
if (res <= 0) {
- fprintf(stderr, "start_read: res=%d\n", res);
+ pr2serr("start_read: res=%d\n", res);
break;
}
res = 0;
@@ -1309,7 +1392,7 @@ main(int argc, char * argv[])
if (SGQ_CAN_WRITE & crw) {
res = start_write(clp);
if (res <= 0) {
- fprintf(stderr, "start_write: res=%d\n", res);
+ pr2serr("start_write: res=%d\n", res);
break;
}
res = 0;
@@ -1321,23 +1404,22 @@ main(int argc, char * argv[])
if (STDOUT_FILENO != clp->outfd)
close(clp->outfd);
if (0 != clp->out_count) {
- fprintf(stderr, "Some error occurred, remaining blocks=%d\n",
- clp->out_count);
+ pr2serr("Some error occurred, remaining blocks=%d\n", clp->out_count);
res = 1;
}
- fprintf(stderr, "%d+%d records in\n", count - clp->in_done_count,
- clp->in_partial);
- fprintf(stderr, "%d+%d records out\n", count - clp->out_done_count,
- clp->out_partial);
+ pr2serr("%d+%d records in\n", count - clp->in_done_count,
+ clp->in_partial);
+ pr2serr("%d+%d records out\n", count - clp->out_done_count,
+ clp->out_partial);
if (clp->dio_incomplete)
- fprintf(stderr, ">> Direct IO requested but incomplete %d times\n",
- clp->dio_incomplete);
+ pr2serr(">> Direct IO requested but incomplete %d times\n",
+ clp->dio_incomplete);
if (clp->sum_of_resids)
- fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
- clp->sum_of_resids);
- if (clp->debug > 0)
- fprintf(stderr, "SIGIO/SIGPOLL signals received: %d, RT sigs: %d\n",
- clp->sigs_io_received, clp->sigs_rt_received);
+ pr2serr(">> Non-zero sum of residual counts=%d\n",
+ clp->sum_of_resids);
+ if ((! clp->no_sig) && clp->debug > 0)
+ pr2serr("SIGIO/SIGPOLL signals received: %d, RT sigs: %d\n",
+ clp->sigs_io_received, clp->sigs_rt_received);
remove_elems(clp);
return res < 0 ? 99 : res;
}
diff --git a/testing/uapi_sg.h b/testing/uapi_sg.h
index 59b545bb..955790fa 100644
--- a/testing/uapi_sg.h
+++ b/testing/uapi_sg.h
@@ -14,7 +14,7 @@
* Later extensions (versions 2, 3 and 4) to driver:
* Copyright (C) 1998 - 2020 Douglas Gilbert
*
- * Version 4.0.11 (20200124)
+ * Version 4.0.13 (20200328)
* This version is for Linux 4 and 5 series kernels.
*
* Documentation
@@ -117,7 +117,7 @@ typedef struct sg_io_hdr {
#define SGV4_FLAG_NO_WAITQ 0x40 /* implies SGV4_FLAG_IMMED */
#define SGV4_FLAG_DOUT_OFFSET 0x80 /* dout byte offset in v4::spare_in */
#define SGV4_FLAG_COMPLETE_B4 0x100
-#define SGV4_FLAG_SIG_ON_OTHER 0x200
+#define SGV4_FLAG_SIGNAL 0x200 /* v3: ignored; v4 signal on completion */
#define SGV4_FLAG_IMMED 0x400 /* for polling with SG_IOR, ignored in SG_IOS */
#define SGV4_FLAG_STOP_IF 0x800 /* Stops sync mrq if error or warning */
#define SGV4_FLAG_DEV_SCOPE 0x1000 /* permit SG_IOABORT to have wider scope */
@@ -126,6 +126,7 @@ typedef struct sg_io_hdr {
#define SGV4_FLAG_KEEP_SHARE 0x8000 /* ... buffer for another dout command */
#define SGV4_FLAG_NO_DXFER SG_FLAG_NO_DXFER /* needed for sharing */
#define SGV4_FLAG_MULTIPLE_REQS 0x20000 /* n sg_io_v4s in data-in */
+#define SGV4_FLAG_EVENTFD 0x40000 /* signal completion on ... */
/* Output (potentially OR-ed together) in v3::info or v4::info field */
#define SG_INFO_OK_MASK 0x1
@@ -195,7 +196,8 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
#define SG_SEIM_SHARE_FD 0x20 /* slave gives fd of master: sharing */
#define SG_SEIM_CHG_SHARE_FD 0x40 /* master gives fd of new slave */
#define SG_SEIM_SGAT_ELEM_SZ 0x80 /* sgat element size (>= PAGE_SIZE) */
-#define SG_SEIM_ALL_BITS 0xff /* should be OR of previous items */
+#define SG_SEIM_EVENTFD 0x100 /* pass eventfd to driver */
+#define SG_SEIM_ALL_BITS 0x1ff /* should be OR of previous items */
/* flag and mask values for boolean fields follow */
#define SG_CTL_FLAGM_TIME_IN_NS 0x1 /* time: nanosecs (def: millisecs) */
@@ -213,7 +215,8 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
#define SG_CTL_FLAGM_MORE_ASYNC 0x800 /* yield EAGAIN in more cases */
#define SG_CTL_FLAGM_EXCL_WAITQ 0x1000 /* only 1 wake up per response */
#define SG_CTL_FLAGM_SNAP_DEV 0x2000 /* output to debugfs::snapped */
-#define SG_CTL_FLAGM_ALL_BITS 0x3fff /* should be OR of previous items */
+#define SG_CTL_FLAGM_RM_EVENTFD 0x4000 /* only if new eventfd wanted */
+#define SG_CTL_FLAGM_ALL_BITS 0x7fff /* should be OR of previous items */
/* Write one of the following values to sg_extended_info::read_value, get... */
#define SG_SEIRV_INT_MASK 0x0 /* get SG_SEIM_ALL_BITS */
@@ -252,7 +255,7 @@ struct sg_extended_info {
__u32 reserved_sz; /* data/sgl size of pre-allocated request */
__u32 tot_fd_thresh; /* total data/sgat for this fd, 0: no limit */
__u32 minor_index; /* rd: kernel's sg device minor number */
- __u32 share_fd; /* SHARE_FD and CHG_SHARE_FD use this */
+ __u32 share_fd; /* for SHARE_FD, CHG_SHARE_FD or EVENTFD */
__u32 sgat_elem_sz; /* sgat element size (must be power of 2) */
__u8 pad_to_96[52]; /* pad so struct is 96 bytes long */
};