aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2020-04-11 04:07:35 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2020-04-11 04:07:35 +0000
commit0a9f85769a18562cb3cb5a59ec202e044b18389c (patch)
tree20138cf50c5ea4b3caf2c1ca176938d91fec9914
parent7f78fcb9b12c87fdc6eca644bc1582d8e5e1242c (diff)
downloadsg3_utils-0a9f85769a18562cb3cb5a59ec202e044b18389c.tar.gz
sg_lib: Linux NVMe SNTL: add read, write and verify
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@847 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog4
-rw-r--r--lib/sg_pt_common.c8
-rw-r--r--lib/sg_pt_linux_nvme.c224
-rw-r--r--src/sg_dd.c60
-rw-r--r--testing/sgh_dd.cpp42
5 files changed, 295 insertions, 43 deletions
diff --git a/ChangeLog b/ChangeLog
index 6569e87b..79c416b4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,12 +2,12 @@ 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 [20200403] [svn: r846]
+Changelog for sg3_utils-1.46 [20200410] [svn: r847]
- 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
+ - sg_lib: Linux NVMe SNTL: add read, write and verify
- testing/sgs_dd: add evfd flags and eventfd processing
- testing/sgh_dd: various fixes
diff --git a/lib/sg_pt_common.c b/lib/sg_pt_common.c
index 5d087171..b0db4f85 100644
--- a/lib/sg_pt_common.c
+++ b/lib/sg_pt_common.c
@@ -31,7 +31,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * scsi_pt_version_str = "3.13 20200313";
+static const char * scsi_pt_version_str = "3.14 20200410";
const char *
@@ -84,6 +84,9 @@ static struct sg_opcode_info_t sg_opcode_info_arr[] =
{0x2a, 0, 0, {10, /* WRITE(10) */
0xfb, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0,
0, 0} },
+ {0x2f, 0, 0, {10, /* VERIFY(10) */
+ 0xf6, 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) */
@@ -94,6 +97,9 @@ static struct sg_opcode_info_t sg_opcode_info_arr[] =
{0x8a, 0, 0, {16, /* WRITE(16) */
0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xc7} },
+ {0x8f, 0, 0, {16, /* VERIFY(16) */
+ 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x3f, 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} },
diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c
index 7b633feb..c606d1e6 100644
--- a/lib/sg_pt_linux_nvme.c
+++ b/lib/sg_pt_linux_nvme.c
@@ -41,7 +41,7 @@
* MA 02110-1301, USA.
*/
-/* sg_pt_linux_nvme version 1.10 20200313 */
+/* sg_pt_linux_nvme version 1.11 20200410 */
/* This file contains a small "SPC-only" SNTL to support the SES pass-through
* of SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS through NVME-MI
@@ -94,6 +94,8 @@
#define SCSI_MODE_SENSE10_OPC 0x5a
#define SCSI_MODE_SELECT10_OPC 0x55
#define SCSI_READ_CAPACITY10_OPC 0x25
+#define SCSI_VERIFY10_OPC 0x2f
+#define SCSI_VERIFY16_OPC 0x8f
#define SCSI_WRITE10_OPC 0x2a
#define SCSI_WRITE16_OPC 0x8a
#define SCSI_SERVICE_ACT_IN_OPC 0x9e
@@ -129,6 +131,19 @@
#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */
#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
+/* NVMe Admin commands */
+#define SG_NVME_AD_GET_FEATURE 0xa
+#define SG_NVME_AD_IDENTIFY 0x6 /* similar to SCSI INQUIRY */
+#define SG_NVME_AD_MI_RECEIVE 0x1e /* MI: Management Interface */
+#define SG_NVME_AD_MI_SEND 0x1d /* hmmm, same opcode as SEND DIAG */
+
+/* NVMe NVM (Non-Volatile Memory) commands */
+#define SG_NVME_NVM_COMPARE 0x5 /* SCSI VERIFY(BYTCHK=1) */
+#define SG_NVME_NVM_READ 0x2
+#define SG_NVME_NVM_VERIFY 0xc /* SCSI VERIFY(BYTCHK=0) */
+#define SG_NVME_NVM_WRITE 0x1
+
+#define SG_NVME_NVM_CDW12_FUA (1 << 30) /* Force Unit Access bit */
#if (HAVE_NVME && (! IGNORE_NVME))
@@ -285,7 +300,7 @@ sg_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
char nam[64];
if (vb)
- sg_get_nvme_opcode_name(*up, true, sizeof(nam), nam);
+ sg_get_nvme_opcode_name(*up, true /* ADMIN */, sizeof(nam), nam);
else
nam[0] = '\0';
cmdp->timeout_ms = (time_secs < 0) ? (-time_secs) : (1000 * time_secs);
@@ -427,7 +442,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; /* NVME Identify command opcode */
+ cmd.opcode = SG_NVME_AD_IDENTIFY;
cmd.nsid = nsid;
cmd.cdw10 = cns;
cmd.addr = (uint64_t)(sg_uintptr_t)up;
@@ -697,7 +712,7 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
return res;
}
memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0xa; /* NVMe Get feature command */
+ cmd.opcode = SG_NVME_AD_GET_FEATURE;
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);
@@ -746,7 +761,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; /* NVMe Get feature command */
+ cmd.opcode = SG_NVME_AD_GET_FEATURE;
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);
@@ -936,7 +951,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; /* NVMe MI Send; hmmm same opcode as SEND DIAG */
+ cmd.opcode = SG_NVME_AD_MI_SEND;
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?? */
@@ -991,7 +1006,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; /* NVMe MI Receive command */
+ cmd.opcode = SG_NVME_AD_MI_RECEIVE;
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?? */
@@ -1242,28 +1257,144 @@ fini:
return res;
}
+/* Since ptp can be a char device (e.g. /dev/nvme0) or a blocks device
+ * (e.g. /dev/nvme0n1 or /dev/nvme0n1p3) use NVME_IOCTL_IO_CMD which is
+ * common to both (and takes a timeout). The difficult is that
+ * NVME_IOCTL_IO_CMD takes a nvme_passthru_cmd object point. */
+static int
+sntl_do_nvm_cmd(struct sg_pt_linux_scsi * ptp, struct sg_nvme_user_io * iop,
+ bool is_read, int time_secs, int vb)
+{
+
+ const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd);
+ int res;
+ uint32_t n;
+ uint16_t sct_sc;
+ struct sg_nvme_passthru_cmd nvme_pt_cmd;
+ struct sg_nvme_passthru_cmd *cmdp = &nvme_pt_cmd;
+ const uint8_t * up = ((const uint8_t *)cmdp) + SG_NVME_PT_OPCODE;
+ void * dp;
+ char nam[64];
+
+ memset(cmdp, 0, sizeof(*cmdp));
+ cmdp->opcode = iop->opcode;
+ cmdp->flags = iop->flags;
+ cmdp->addr = iop->addr;
+ dp = (void *)iop->addr;
+ cmdp->cdw10 = iop->slba & 0xffffffff;
+ cmdp->cdw11 = (iop->slba >> 32) & 0xffffffff;
+ cmdp->cdw12 = iop->nblocks & 0xffff;
+ if (vb)
+ sg_get_nvme_opcode_name(*up, false /* NVM */ , sizeof(nam), nam);
+ else
+ nam[0] = '\0';
+ cmdp->timeout_ms = (time_secs < 0) ? (-time_secs) : (1000 * time_secs);
+ ptp->os_err = 0;
+ if (vb > 2) {
+ pr2ws("NVMe NVM command: %s\n", nam);
+ hex2stderr((const uint8_t *)cmdp, cmd_len, 1);
+ if ((vb > 3) && (! is_read) && dp) {
+ uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN);
+
+ if (len > 0) {
+ n = len;
+ if ((len < 512) || (vb > 5))
+ pr2ws("\nData-out buffer (%u bytes):\n", n);
+ else {
+ pr2ws("\nData-out buffer (first 512 of %u bytes):\n", n);
+ n = 512;
+ }
+ hex2stderr((const uint8_t *)dp, n, 0);
+ }
+ }
+ }
+ res = ioctl(ptp->dev_fd, NVME_IOCTL_IO_CMD, cmdp);
+ if (res < 0) { /* OS error (errno negated) */
+ ptp->os_err = -res;
+ if (vb > 1) {
+ pr2ws("%s: ioctl for %s [0x%x] failed: %s "
+ "(errno=%d)\n", __func__, nam, *up, strerror(-res), -res);
+ }
+ return res;
+ }
+
+ /* Now res contains NVMe completion queue CDW3 31:17 (15 bits) */
+ ptp->nvme_result = cmdp->result;
+ if (ptp->nvme_direct && ptp->io_hdr.response &&
+ (ptp->io_hdr.max_response_len > 3)) {
+ /* build 32 byte "sense" buffer */
+ uint8_t * sbp = (uint8_t *)(sg_uintptr_t)ptp->io_hdr.response;
+ uint16_t st = (uint16_t)res;
+
+ n = ptp->io_hdr.max_response_len;
+ n = (n < 32) ? n : 32;
+ memset(sbp, 0 , n);
+ ptp->io_hdr.response_len = n;
+ sg_put_unaligned_le32(cmdp->result,
+ sbp + SG_NVME_PT_CQ_RESULT);
+ if (n > 15) /* LSBit will be 0 (Phase bit) after (st << 1) */
+ sg_put_unaligned_le16(st << 1, sbp + SG_NVME_PT_CQ_STATUS_P);
+ }
+ /* clear upper bits (DNR and More) leaving ((SCT << 8) | SC) */
+ sct_sc = 0x7ff & res; /* 11 bits */
+ ptp->nvme_status = sct_sc;
+ ptp->nvme_stat_dnr = !!(0x4000 & res);
+ ptp->nvme_stat_more = !!(0x2000 & res);
+ if (sct_sc) { /* when non-zero, treat as command error */
+ if (vb > 1) {
+ char b[80];
+
+ pr2ws("%s: ioctl for %s [0x%x] failed, status: %s [0x%x]\n",
+ __func__, nam, *up,
+ sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b), sct_sc);
+ }
+ return SG_LIB_NVME_STATUS; /* == SCSI_PT_DO_NVME_STATUS */
+ }
+ if ((vb > 3) && is_read && dp) {
+ uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN);
+
+ if (len > 0) {
+ n = len;
+ if ((len < 1024) || (vb > 5))
+ pr2ws("\nData-in buffer (%u bytes):\n", n);
+ else {
+ pr2ws("\nData-in buffer (first 1024 of %u bytes):\n", n);
+ n = 1024;
+ }
+ hex2stderr((const uint8_t *)dp, n, 0);
+ }
+ }
+ return 0;
+}
+
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;
+ bool have_fua = !!(cdbp[1] & 0x8);
struct sg_nvme_user_io io;
struct sg_nvme_user_io * iop = &io;
memset(iop, 0, sizeof(*iop));
- iop->opcode = 0x2; /* NVMe Read command */
+ iop->opcode = SG_NVME_NVM_READ;
if (is_read10) {
- lba = sg_get_unaligned_be32(cdbp + 2);
- num = sg_get_unaligned_be16(cdbp + 7);
+ iop->slba = sg_get_unaligned_be32(cdbp + 2);
+ iop->nblocks = sg_get_unaligned_be16(cdbp + 7);
} else {
- lba = sg_get_unaligned_be64(cdbp + 2);
- num = sg_get_unaligned_be32(cdbp + 10);
+ uint32_t num = sg_get_unaligned_be32(cdbp + 10);
+
+ iop->slba = sg_get_unaligned_be64(cdbp + 2);
+ if (num > UINT16_MAX) {
+ mk_sense_invalid_fld(ptp, true, 11, -1, vb);
+ return 0;
+ } else
+ iop->nblocks = num;
}
- iop->slba = lba;
- iop->nblocks = num;
- return 0;
+ if (have_fua)
+ iop->nblocks |= SG_NVME_NVM_CDW12_FUA;
+ iop->addr = (uint64_t)ptp->io_hdr.din_xferp;
+ return sntl_do_nvm_cmd(ptp, iop, true /* is_read */, time_secs, vb);
}
static int
@@ -1271,21 +1402,61 @@ 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;
+ bool have_fua = !!(cdbp[1] & 0x8);
struct sg_nvme_user_io io;
struct sg_nvme_user_io * iop = &io;
memset(iop, 0, sizeof(*iop));
- iop->opcode = 0x3; /* NVMe Write command */
+ iop->opcode = SG_NVME_NVM_WRITE;
if (is_write10) {
- lba = sg_get_unaligned_be32(cdbp + 2);
- num = sg_get_unaligned_be16(cdbp + 7);
+ iop->slba = sg_get_unaligned_be32(cdbp + 2);
+ iop->nblocks = sg_get_unaligned_be16(cdbp + 7);
} else {
- lba = sg_get_unaligned_be64(cdbp + 2);
- num = sg_get_unaligned_be32(cdbp + 10);
+ uint32_t num = sg_get_unaligned_be32(cdbp + 10);
+
+ iop->slba = sg_get_unaligned_be64(cdbp + 2);
+ if (num > UINT16_MAX) {
+ mk_sense_invalid_fld(ptp, true, 11, -1, vb);
+ return 0;
+ } else
+ iop->nblocks = num;
}
- return 0;
+ if (have_fua)
+ iop->nblocks |= SG_NVME_NVM_CDW12_FUA;
+ iop->addr = (uint64_t)ptp->io_hdr.dout_xferp;
+ return sntl_do_nvm_cmd(ptp, iop, false, time_secs, vb);
+}
+
+static int
+sntl_verify(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool is_verify10 = (SCSI_VERIFY10_OPC == cdbp[0]);
+ uint8_t bytchk = (cdbp[1] >> 1) & 0x3;
+ struct sg_nvme_user_io io;
+ struct sg_nvme_user_io * iop = &io;
+
+ if (bytchk > 1) {
+ mk_sense_invalid_fld(ptp, true, 1, 2, vb);
+ return 0;
+ }
+ memset(iop, 0, sizeof(*iop));
+ iop->opcode = bytchk ? SG_NVME_NVM_COMPARE : SG_NVME_NVM_WRITE;
+ if (is_verify10) {
+ iop->slba = sg_get_unaligned_be32(cdbp + 2);
+ iop->nblocks = sg_get_unaligned_be16(cdbp + 7);
+ } else {
+ uint32_t num = sg_get_unaligned_be32(cdbp + 10);
+
+ iop->slba = sg_get_unaligned_be64(cdbp + 2);
+ if (num > UINT16_MAX) {
+ mk_sense_invalid_fld(ptp, true, 11, -1, vb);
+ return 0;
+ } else
+ iop->nblocks = num;
+ }
+ iop->addr = bytchk ? (uint64_t)ptp->io_hdr.dout_xferp : 0;
+ return sntl_do_nvm_cmd(ptp, iop, false, time_secs, vb);
}
/* Executes NVMe Admin command (or at least forwards it to lower layers).
@@ -1357,6 +1528,9 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
return sntl_mode_ss(ptp, cdbp, time_secs, vb);
case SCSI_READ_CAPACITY10_OPC:
return sntl_readcap(ptp, cdbp, time_secs, vb);
+ case SCSI_VERIFY10_OPC:
+ case SCSI_VERIFY16_OPC:
+ return sntl_verify(ptp, cdbp, time_secs, vb);
case SCSI_SERVICE_ACT_IN_OPC:
if (SCSI_READ_CAPACITY16_SA == (cdbp[1] & SCSI_SA_MSK))
return sntl_readcap(ptp, cdbp, time_secs, vb);
diff --git a/src/sg_dd.c b/src/sg_dd.c
index e659a2ca..03c26570 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.12 20200331";
+static const char * version_str = "6.13 20200410";
#define ME "sg_dd: "
@@ -114,7 +114,8 @@ static const char * version_str = "6.12 20200331";
#define FT_ST 16 /* filetype is st char device (tape) */
#define FT_BLOCK 32 /* filetype is block device */
#define FT_FIFO 64 /* filetype is a fifo (name pipe) */
-#define FT_ERROR 128 /* couldn't "stat" file */
+#define FT_NVME 128 /* NVMe char device (e.g. /dev/nvme2) */
+#define FT_ERROR 256 /* couldn't "stat" file */
#define DEV_NULL_MINOR_NUM 3
@@ -295,6 +296,47 @@ find_bsg_major(void)
fclose(fp);
}
+static bool nvme_major_checked = false;
+static int nvme_major = 0;
+
+static void
+find_nvme_major(void)
+{
+ int n;
+ char *cp;
+ FILE *fp;
+ const char *proc_devices = "/proc/devices";
+ char a[128];
+ char b[128];
+
+ if (NULL == (fp = fopen(proc_devices, "r"))) {
+ if (verbose)
+ pr2serr("fopen %s failed: %s\n", proc_devices, strerror(errno));
+ return;
+ }
+ while ((cp = fgets(b, sizeof(b), fp))) {
+ if ((1 == sscanf(b, "%126s", a)) &&
+ (0 == memcmp(a, "Character", 9)))
+ break;
+ }
+ while (cp && (cp = fgets(b, sizeof(b), fp))) {
+ if (2 == sscanf(b, "%d %126s", &n, a)) {
+ if (0 == strcmp("nvme", a)) {
+ nvme_major = n;
+ break;
+ }
+ } else
+ break;
+ }
+ if (verbose > 5) {
+ if (cp)
+ pr2serr("found nvme_major=%d\n", bsg_major);
+ else
+ pr2serr("found no nvme char device in %s\n", proc_devices);
+ }
+ fclose(fp);
+}
+
static int
dd_filetype(const char * filename)
@@ -323,6 +365,12 @@ dd_filetype(const char * filename)
}
if (bsg_major == (int)major(st.st_rdev))
return FT_SG;
+ if (! nvme_major_checked) {
+ nvme_major_checked = true;
+ find_nvme_major();
+ }
+ if (nvme_major == (int)major(st.st_rdev))
+ return FT_NVME; /* treat as sg device */
} else if (S_ISBLK(st.st_mode))
return FT_BLOCK;
else if (S_ISFIFO(st.st_mode))
@@ -348,6 +396,8 @@ dd_filetype_str(int ft, char * buff)
off += sg_scnpr(buff + off, 32, "SCSI tape device ");
if (FT_RAW & ft)
off += sg_scnpr(buff + off, 32, "raw device ");
+ if (FT_NVME & ft)
+ off += sg_scnpr(buff + off, 32, "NVMe char device ");
if (FT_OTHER & ft)
off += sg_scnpr(buff + off, 32, "other (perhaps ordinary file) ");
if (FT_ERROR & ft)
@@ -1326,6 +1376,9 @@ open_if(const char * inf, int64_t skip, int bpt, struct flags_t * ifp,
goto file_err;
}
}
+ } else if (FT_NVME & *in_typep) {
+ pr2serr("Don't support NVMe char devices as IFILE\n");
+ goto file_err;
} else {
flags = O_RDONLY;
if (ifp->direct)
@@ -1450,6 +1503,9 @@ open_of(const char * outf, int64_t seek, int bpt, struct flags_t * ofp,
goto file_err;
}
}
+ } else if (FT_NVME & *out_typep) {
+ pr2serr("Don't support NVMe char devices as OFILE\n");
+ goto file_err;
} else if (FT_DEV_NULL & *out_typep)
outfd = -1; /* don't bother opening */
else {
diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp
index d5d32eb0..599ee4bc 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -108,7 +108,7 @@
using namespace std;
-static const char * version_str = "1.78 20200403";
+static const char * version_str = "1.80 20200404";
#ifdef __GNUC__
#ifndef __clang__
@@ -237,6 +237,7 @@ typedef struct global_collection
atomic<int> dio_incomplete_count;
atomic<int> sum_of_resids;
int debug; /* both -v and deb=VERB bump this field */
+ int fail_mask;
int dry_run;
bool aen_given;
bool cdbsz_given;
@@ -590,6 +591,11 @@ sg_flags_str(int flags, int b_len, char * b)
if (n >= b_len)
goto fini;
}
+ if (SGV4_FLAG_EVENTFD & flags) { /* 0x40000 */
+ n += sg_scnpr(b + n, b_len - n, "EVFD|");
+ if (n >= b_len)
+ goto fini;
+ }
fini:
if (n < b_len) { /* trim trailing '\' */
if ('|' == b[n - 1])
@@ -852,14 +858,14 @@ usage(int pg_num)
pr2serr(" [ae=AEN[,MAEN]] [bpt=BPT] [cdbsz=6|10|12|16] "
"[coe=0|1]\n"
" [deb=VERB] [dio=0|1] [elemsz_kb=ESK] "
- "[fua=0|1|2|3]\n"
- " [mrq=[IO,]NRQS[,C]] [noshare=0|1] [of2=OFILE2] "
- "[ofreg=OFREG]\n"
- " [ofsplit=OSP] [sync=0|1] [thr=THR] [time=0|1] "
- "[unshare=1|0]\n"
- " [verbose=VERB] [--dry-run] [--prefetch] "
- "[--verbose]\n"
- " [--verify] [--version]\n\n"
+ "[fail_mask=FM]\n"
+ " [fua=0|1|2|3] [mrq=[IO,]NRQS[,C]] [noshare=0|1] "
+ "[of2=OFILE2]\n"
+ " [ofreg=OFREG] [ofsplit=OSP] [sync=0|1] [thr=THR] "
+ "[time=0|1]\n"
+ " [unshare=1|0] [verbose=VERB] [--dry-run] "
+ "[--prefetch]\n"
+ " [--verbose] [--verify] [--version]\n\n"
" where the main options (shown in first group above) are:\n"
" bs must be device logical block size (default "
"512)\n"
@@ -921,6 +927,7 @@ page2:
" dio is direct IO, 1->attempt, 0->indirect IO (def)\n"
" elemsz_kb scatter gather list element size in kilobytes "
"(def: 32[KB])\n"
+ " fail_mask 1: misuse KEEP_SHARE flag; 0: nothing (def)\n"
" fua force unit access: 0->don't(def), 1->OFILE, "
"2->IFILE,\n"
" 3->OFILE+IFILE\n"
@@ -2492,7 +2499,7 @@ sgh_do_deferred_mrq(Rq_elem * rep, mrq_arr_t & def_arr)
struct sg_io_v4 ctl_v4;
uint8_t * cmd_ap = NULL;
Gbl_coll * clp = rep->clp;
- const char * iosub_str = clp->unbalanced_mrq ? "SUBMIT" : "";
+ const char * iosub_str;
char b[80];
id = rep->id;
@@ -2701,14 +2708,14 @@ try_again:
std::this_thread::yield();/* allow another thread to progress */
goto try_again;
}
- pr2serr_lk("%s: ioctl(SG_IO%s, %s)-->%d, errno=%d: %s\n",
+ pr2serr_lk("%s: ioctl(%s, %s)-->%d, errno=%d: %s\n",
__func__, iosub_str, sg_flags_str(ctl_v4.flags, b_len, b),
res, err, strerror(err));
res = -1;
goto fini;
}
if (clp->debug > 4) {
- pr2serr_lk("%s: Controlling object output by ioctl(SG_IO%s):\n",
+ pr2serr_lk("%s: Controlling object output by ioctl(%s):\n",
__func__, iosub_str);
if (clp->debug > 5)
hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
@@ -2881,8 +2888,10 @@ 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)
+ if (clp->ofile2_given && wr && rep->has_share && ! is_wr2)
flags |= SGV4_FLAG_KEEP_SHARE; /* set on first write only */
+ else if (clp->fail_mask & 1)
+ flags |= SGV4_FLAG_KEEP_SHARE; /* troublemaking .... */
} else
memset(hp, 0, sizeof(struct sg_io_hdr));
if (clp->debug > 3) {
@@ -3797,6 +3806,13 @@ parse_cmdline_sanity(int argc, char * argv[], Gbl_coll * clp, char * inf,
"bytes)\n");
return SG_LIB_SYNTAX_ERROR;
}
+ } else if ((0 == strcmp(key, "fail_mask")) ||
+ (0 == strcmp(key, "fail-mask"))) {
+ clp->fail_mask = sg_get_num(buf);
+ if (clp->fail_mask < 0) {
+ pr2serr("fail_mask: couldn't decode argument\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
} else if (0 == strcmp(key, "fua")) {
n = sg_get_num(buf);
if (n & 1)