aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--README.freebsd16
-rw-r--r--debian/changelog2
-rw-r--r--doc/README12
-rw-r--r--doc/sg3_utils.82
-rw-r--r--doc/sg_raw.817
-rw-r--r--doc/sg_start.816
-rw-r--r--include/sg_lib.h6
-rw-r--r--include/sg_pt.h8
-rw-r--r--inhex/nvme_read_ctl.hex5
-rw-r--r--inhex/nvme_read_oob_ctl.hex47
-rw-r--r--lib/sg_lib.c13
-rw-r--r--lib/sg_lib_data.c2
-rw-r--r--lib/sg_pt_freebsd.c1316
-rw-r--r--lib/sg_pt_linux.c2
-rw-r--r--lib/sg_pt_linux_nvme.c100
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_inq.c8
-rw-r--r--src/sg_raw.c30
-rw-r--r--suse/sg3_utils.spec4
-rw-r--r--testing/sg_mrq_dd.cpp103
-rw-r--r--testing/sgh_dd.cpp138
22 files changed, 1359 insertions, 502 deletions
diff --git a/ChangeLog b/ChangeLog
index 6a80dc27..4726b3a7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,8 +2,16 @@ 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 pre-release sg3_utils-1.47 [20210414] [svn: r895]
- - move some hex file from examples to inhex directory
+Changelog for pre-release sg3_utils-1.47 [20210501] [svn: r896]
+ - sg_raw: fix prints of NVMe NVM command names
+ - sg_lib: add sg_scsi_status_is_good()
+ - pt_linux: fix verify(BytChk=0) which Linux SNTL translated
+ to write, other SNTL cleanups
+ - pt: check_pt_file_handle() add return value of 5 for
+ FreeBSD for nvme(cam)
+ - move some hex files from examples to inhex directory
+ - major rework of lib/sg_pt_freebsd.c; make SNTL as similar
+ as feasible to the Linux implementation
- add testing/sg_take_snap
- change links to http://sg.danny/cz/sg/* to https
diff --git a/README.freebsd b/README.freebsd
index b7aaeb08..411f0984 100644
--- a/README.freebsd
+++ b/README.freebsd
@@ -92,7 +92,19 @@ devices: "nvmecontrol devlist" can be used. Any many, but not all
contexts, the device name can be used without the '/dev/' prefix.
FreeBSD is relatively unique in this respect and support for this
abbreviated form has been broken in this package and fixed in
-release 1.46 .
+sg3_utils release 1.46 .
+
+Device naming for NVMe is a bit more complex. Controllers have names
+like /dev/nvme0 and namespaces /dev/nvme0ns1 . Partitions are not
+supported on /dev/nvme0ns1 type nodes. Instead there are /dev/nvd0
+and /dev/nvd0p<m> where <m> is th partition number starting at 1.
+The nvd driver (written by Intel) is not CAM compatible and has its
+own utility nvmecontrol which has similar capabilities as camcontrol
+has for CAM devices. In FreeBSD release 12 the nda driver was
+introduced with names like /dev/nda0 and /dev/nda0n<m>. The difference
+is that nda is CAM compatible. From the point of view of this package,
+the nda driver is preferred as CAM supports NVMe command timeouts and
+the error processing is more mature.
FreeBSD installation
====================
@@ -149,4 +161,4 @@ utilities.
Douglas Gilbert
-8th April 2021
+1st May 2021
diff --git a/debian/changelog b/debian/changelog
index 0573b641..77670146 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.47-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Wed, 14 Apr 2021 22:00:00 -0400
+ -- Douglas Gilbert <dgilbert@interlog.com> Sat, 01 May 2021 21:00:00 -0400
sg3-utils (1.46-0.1) unstable; urgency=low
diff --git a/doc/README b/doc/README
index 89205622..d20d510f 100644
--- a/doc/README
+++ b/doc/README
@@ -5,22 +5,22 @@ removed in version 1.24 .
Here are the urls of the files that were previously here plus a brief
summary:
-http://sg.danny.cz/sg/sg3_utils.html
+https://sg.danny.cz/sg/sg3_utils.html
- overview and examples of this package
-http://sg.danny.cz/sg/sg_dd.html
+https://sg.danny.cz/sg/sg_dd.html
- discussion and examples of the sg_dd utility which is a dd variant
(also discusses the sgm_dd, sgp_dd and sg_read utilities)
-http://sg.danny.cz/sg/sg_ses.html
+https://sg.danny.cz/sg/sg_ses.html
- discussion and examples of the sg_ses utility. SCSI Enclosure
Services (SES) devices may contain a lot of information,
structured in a non-trivial way.
-http://sg.danny.cz/sg/sg_io.html
+https://sg.danny.cz/sg/sg_io.html
- discussion of Linux SG_IO ioctl (SCSI pass-through)
-http://sg.danny.cz/sg/tools.html
+https://sg.danny.cz/sg/tools.html
- overview of around 25 storage and SCSI tools
@@ -31,4 +31,4 @@ sg_scan.8 . sg_scan is not supported for other ports.
Douglas Gilbert
-10th February 2010
+16th April 2021
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index 5f93cbd0..a4716e3e 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -1,4 +1,4 @@
-.TH SG3_UTILS "8" "April 2021" "sg3_utils\-1.47" SG3_UTILS
+.TH SG3_UTILS "8" "May 2021" "sg3_utils\-1.47" SG3_UTILS
.SH NAME
sg3_utils \- a package of utilities for sending SCSI commands
.SH SYNOPSIS
diff --git a/doc/sg_raw.8 b/doc/sg_raw.8
index 95519848..fe157392 100644
--- a/doc/sg_raw.8
+++ b/doc/sg_raw.8
@@ -1,4 +1,4 @@
-.TH SG_RAW "8" "January 2021" "sg3_utils\-1.46" SG3_UTILS
+.TH SG_RAW "8" "April 2021" "sg3_utils\-1.47" SG3_UTILS
.SH NAME
sg_raw \- send arbitrary SCSI or NVMe command to a device
.SH SYNOPSIS
@@ -58,8 +58,9 @@ the NVMe command set.
Display usage information and exit.
.TP
\fB\-i\fR, \fB\-\-infile\fR=\fIIFILE\fR
-Read data from \fIIFILE\fR instead of stdin. This option is ignored if
-\fB\-\-send\fR is not specified.
+Read binary data from \fIIFILE\fR instead of stdin. This option is ignored
+if \fB\-\-send\fR is not specified. That data, if used, will become the
+the command's "data\-out" buffer.
.TP
\fB\-n\fR, \fB\-\-nosense\fR
Don't display SCSI Sense information.
@@ -70,8 +71,9 @@ NVM command set (e.g. the Read and Write (user data) commands) this option
needs to be given.
.TP
\fB\-o\fR, \fB\-\-outfile\fR=\fIOFILE\fR
-Write data received from the \fIDEVICE\fR to \fIOFILE\fR. The data is
-written in binary. By default, data is dumped in hex format to stdout.
+Write data received from the \fIDEVICE\fR to \fIOFILE\fR. That data is
+the command's "data\-in" buffer. The data is written in binary. By default,
+data is dumped in hex format to stdout.
If \fIOFILE\fR is '\-' then data is dumped in binary to stdout.
This option is ignored if \fI\-\-request\fR is not specified.
.TP
@@ -200,7 +202,7 @@ followed by seven "ff"s. A similar arrangement is made for the length
of that buffer (in bytes), but since that is a 32 byte quantity, the
first 4 bytes (all "ff"s) are removed.
.PP
-Several command file examples can be found in the examples directory of this
+Several command file examples can be found in the inhex directory of this
package's source tarball: nvme_identify_ctl.hex, nvme_dev_self_test.hex,
nvme_read_ctl.hex and nvme_write_ctl.hex .
.PP
@@ -249,6 +251,9 @@ the SCSI ATA PASS\-THROUGH(12) command (see the SAT or SAT\-2 standard at
https://www.t10.org). Notice that the STANDBY IMMEDIATE command does not
send or receive any additional data, however if it fails sense data
should be returned and displayed.
+.TP
+For NVME examples see the files in this package's inhex directory that
+start with 'nvme_' such as inhex/nvme_identify_ctl.hex .
.SH EXIT STATUS
The exit status of sg_raw is 0 when it is successful. Otherwise see
the sg3_utils(8) man page.
diff --git a/doc/sg_start.8 b/doc/sg_start.8
index c9a44491..1b251c1e 100644
--- a/doc/sg_start.8
+++ b/doc/sg_start.8
@@ -1,4 +1,4 @@
-.TH SG_START "8" "September 2020" "sg3_utils\-1.43" SG3_UTILS
+.TH SG_START "8" "April 2021" "sg3_utils\-1.47" SG3_UTILS
.SH NAME
sg_start \- send SCSI START STOP UNIT command: start, stop, load or eject
medium
@@ -40,6 +40,11 @@ This utility supports two command line syntaxes, the preferred one is
shown first in the synopsis and explained in this section. A later
section on the old command line syntax outlines the second group of
options.
+.PP
+Linux note: best not to use a standard block device name (e.g. /dev/sdc)
+with the \fI\-\-stop\fR option. Use a sg or bsg device node instead (see
+lsscsi(8) ). The block layer will sometimes notice the disk spinning
+down and decide: "that's not right" and spin it up again!
.SH OPTIONS
Arguments to long options are mandatory for short options as well.
.TP
@@ -52,6 +57,8 @@ same action as \fI\-\-start\fR.
\fB\-e\fR, \fB\-\-eject\fR
stop the medium and eject it from the drive. Only appropriate for a
device with removable medium. Might be ignored (prevented), see below.
+Note, this is an operation that can be done on a tape drive or CD/DVD/BD
+player, not on a hard disk or SSD!
.TP
\fB\-h\fR, \fB\-\-help\fR
print out the usage message then exit.
@@ -117,6 +124,9 @@ other options, this option defaults (i.e. set the START cdb bit).
.TP
\fB\-S\fR, \fB\-\-stop\fR
stop (spin\-down) the \fIDEVICE\fR. This clears the START bit in the cdb.
+This operation is typically done on a hard disk or SSD. In the case of a
+SSD it will be placed in low power mode and may need a start operation
+later before normal IO can resume.
.TP
\fB\-v\fR, \fB\-\-verbose\fR
increase the level of verbosity. Can be used multiple times.
@@ -264,10 +274,10 @@ Written by K. Garloff and D. Gilbert
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2002\-2020 Kurt Garloff, Douglas Gilbert
+Copyright \(co 2002\-2021 Kurt Garloff, Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
.SH "SEE ALSO"
.B sg_prevent(sg3_utils), sg_requests(sg3_utils), sg_turs(sg3_utils)
-.B sdparm(sdparm)
+.B sdparm(sdparm), lsscsi(lsscsi)
diff --git a/include/sg_lib.h b/include/sg_lib.h
index 006c566c..ae228bcc 100644
--- a/include/sg_lib.h
+++ b/include/sg_lib.h
@@ -67,7 +67,7 @@ extern "C" {
/* The SCSI status codes as found in SAM-4 at www.t10.org */
#define SAM_STAT_GOOD 0x0
#define SAM_STAT_CHECK_CONDITION 0x2
-#define SAM_STAT_CONDITION_MET 0x4
+#define SAM_STAT_CONDITION_MET 0x4 /* this is not an error */
#define SAM_STAT_BUSY 0x8
#define SAM_STAT_INTERMEDIATE 0x10 /* obsolete in SAM-4 */
#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 /* obsolete in SAM-4 */
@@ -189,6 +189,10 @@ struct sg_scsi_sense_hdr {
uint8_t additional_length; /* zero for fixed format sense data */
};
+/* Returns true when status is SAM_STAT_GOOD or SAM_STAT_CONDITION_MET,
+ * returns false otherwise. Ignores bit 0. */
+bool sg_scsi_status_is_good(int sstatus);
+
/* Maps the salient data from a sense buffer which is in either fixed or
* descriptor format into a structure mimicking a descriptor format
* header (i.e. the first 8 bytes of sense descriptor format).
diff --git a/include/sg_pt.h b/include/sg_pt.h
index ab077f31..ba92cb02 100644
--- a/include/sg_pt.h
+++ b/include/sg_pt.h
@@ -2,7 +2,7 @@
#define SG_PT_H
/*
- * Copyright (c) 2005-2020 Douglas Gilbert.
+ * Copyright (c) 2005-2021 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.
@@ -21,7 +21,7 @@ extern "C" {
* structure "derived" (using a C++ term) from this one. It compiles
* because 'struct sg_pt_base' is only referenced (by pointer: 'objp')
* in this interface. An instance of this structure represents the
- * context of one SCSI command.
+ * context of one SCSI (or NVME) command.
* If an instance of sg_pt_base is shared across several threads then
* it is up to the application to take care of multi-threaded issues
* with that instance. */
@@ -57,7 +57,9 @@ int scsi_pt_close_device(int device_fd);
* device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
* secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
* char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
- * NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
+ * NSID), 5 is also a NVMe device (FreeBSD CAM NVMe (e.g. /dev/nda0)) or 0
+ * if something else (e.g. ATA block device) or dev_fd < 0.
+ * The return value differs somewhat by OS.
* If error, returns negated errno (operating system) value. */
int check_pt_file_handle(int dev_fd, const char * device_name, int verbose);
diff --git a/inhex/nvme_read_ctl.hex b/inhex/nvme_read_ctl.hex
index 397072d8..1473d410 100644
--- a/inhex/nvme_read_ctl.hex
+++ b/inhex/nvme_read_ctl.hex
@@ -11,7 +11,7 @@
# fffffffe use byte length of data-in buffer
# fffffffd use byte length of data-out buffer
#
-# 512 byte logical block size is assumed. Read 4 blocks hence 2048bytes.
+# 512 byte logical block size is assumed. Read 4 blocks hence 2048 bytes.
# The first LBA read is 0x12345 and the namespace is 1. If successful
# the four blocks will be read into the data-in buffer. Submission queu
# 0 is used (the same queue that Admin commands use). The NVM opcode for
@@ -27,7 +27,8 @@
#
# A typical invocation in Linux and FreeBSD would look like this:
# sg_raw --cmdfile=nvme_read_ctl.hex --nvm -r 2048
-# --outfile=t.bin /dev/nvme0
+# --outfile=t.bin /dev/nvme0n1
+# In FreeBSD the device name would be /dev/nvme0ns1
#
# Notice the '--nvm' option which is needed to distiguish a NVM
# command from an Admin command as Admin commands are the default
diff --git a/inhex/nvme_read_oob_ctl.hex b/inhex/nvme_read_oob_ctl.hex
new file mode 100644
index 00000000..13b0b118
--- /dev/null
+++ b/inhex/nvme_read_oob_ctl.hex
@@ -0,0 +1,47 @@
+# 64 byte NVMe, Read command (a NVM command) which what should be an
+# Out-of-Bounds LBA (around 377 TB with 512 byte sectors. This file is
+# suitable for:
+# sg_raw --cmdfile=<this_file_name> --nvm --request=2048 <nvme_device>
+#
+# The address field (at byte offset 24, 8 bytes and little endian) gives
+# special meaning to the highest address pointers:
+# ffffffff fffffffe use address of data-in buffer
+# ffffffff fffffffd use address of data-out buffer
+#
+# The data length field (at byte offset 36, 4 bytes and little endian)
+# gives special meaning to the highest block counts:
+# fffffffe use byte length of data-in buffer
+# fffffffd use byte length of data-out buffer
+#
+# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+# This NVMe (NVM) Read command purposely has a very large starting LBA
+# in order to get a "Attempted write to read only range" error. This is
+# to test error reporting.
+# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+#
+# 512 byte logical block size is assumed. Read 4 blocks hence 2048 bytes.
+# The first LBA read is 0xabcd012345 and the namespace is 1. If successful
+# the four blocks will be read into the data-in buffer. Submission queu
+# 0 is used (the same queue that Admin commands use). The NVM opcode for
+# the Read command is 0x2 and appears in the first command byte.
+
+02 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 fe ff ff ff ff ff ff ff
+00 00 00 00 fe ff ff ff 45 23 01 cd ab 00 00 00
+03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# Notice NVMe uses its quirky "0's based" number of blocks so
+# 03 appears at byte offset 48 to mean "read 4 blocks".
+#
+# A typical invocation in Linux and FreeBSD would look like this:
+# sg_raw --cmdfile=nvme_read_oob_ctl.hex --nvm -r 2048
+# --outfile=t.bin /dev/nvme0n1
+# In FreeBSD the device name would be /dev/nvme0ns1
+#
+# Notice the '--nvm' option which is needed to distiguish a NVM
+# command from an Admin command as Admin commands are the default
+# in this utility.
+#
+# This utility (and most others in the package) aligns data-in and
+# data-out buffers to the beginning of pages which are 4096 bytes
+# long at a minimum. This is the way NVMe likes things as well.
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index 1860e69a..780a3a4d 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -261,6 +261,19 @@ static const struct sg_lib_simple_value_name_t sstatus_str_arr[] = {
{0xffff, NULL},
};
+bool
+sg_scsi_status_is_good(int sstatus)
+{
+ sstatus &= 0xfe;
+ switch (sstatus) {
+ case SAM_STAT_GOOD:
+ case SAM_STAT_CONDITION_MET:
+ return true;
+ default:
+ return false;
+ }
+}
+
void
sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff)
{
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index eb18be62..f8bcd67e 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -19,7 +19,7 @@
#include "sg_lib_data.h"
-const char * sg_lib_version_str = "2.79 20210304";
+const char * sg_lib_version_str = "2.80 20210419";
/* spc6r05, sbc4r22, zbc2r09 */
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index 4a3845f6..65399f55 100644
--- a/lib/sg_pt_freebsd.c
+++ b/lib/sg_pt_freebsd.c
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_freebsd version 1.39 20210225 */
+/* sg_pt_freebsd version 1.40 20210501 */
#include <stdio.h>
#include <stdlib.h>
@@ -46,30 +46,36 @@
#if (HAVE_NVME && (! IGNORE_NVME))
#include "freebsd_nvme_ioctl.h"
#else
-#define NVME_CTRLR_PREFIX "/dev/nvme"
-#define NVME_NS_PREFIX "ns"
+#define NVME_CTRLR_PREFIX "/dev/nvme"
+#define NVME_NS_PREFIX "ns"
#endif
+#define SG_NVME_NVD_PREFIX "/dev/nvd" /* >= FreeBSD 9.2 */
+#define SG_NVME_NDA_PREFIX "/dev/nda" /* >= FreeBSD 12.0, CAM compatible */
#define FREEBSD_MAXDEV 64
#define FREEBSD_FDOFFSET 16;
+#if __FreeBSD_version > 500000
+#define CAM_ERROR_PRINT(a, b, c, d, e) cam_error_print(a, b, c, d, e);
+#else
+#define CAM_ERROR_PRINT(a, b, c, d, e)
+#endif
-struct freebsd_dev_channel {
- int unitnum; // the SCSI unit number
- bool is_nvme; /* OS device type, if false ignore nvme_our_sntl */
- bool nvme_our_sntl; /* true: our SNTL; false: received NVMe command */
- bool is_char;
+
+struct freebsd_dev_channel { /* one instance per open file descriptor */
+ bool is_nvme_dev; /* true if NVMe device attached, else SCSI */
+ bool is_cam_nvme; /* NVMe via /dev/nda<n> or /dev/pass<n> devices */
+ bool is_pass; /* CAM passthrough device (i.e. 'pass<n>') */
+ int unitnum; /* the SCSI unit number, NVMe controller id? */
uint32_t nsid;
- uint32_t nv_ctrlid;
- int dev_fd; // for NVMe, use -1 to indicate not provided
- uint32_t nvme_result; // cdw0 from completion
- uint16_t nvme_status; // from completion: ((sct << 8) | sc)
+ // uint32_t nv_ctrlid; /* unitnum seems to have this role */
+ int nvme_fd_ns; // for non-CAM NVMe, use -1 to indicate not provided
+ int nvme_fd_ctrl; // open("/dev/nvme<n>") if needed */
char* devname; // the device name
struct cam_device* cam_dev;
uint8_t * nvme_id_ctlp;
uint8_t * free_nvme_id_ctlp;
- uint8_t cq_dw0_3[16];
struct sg_sntl_dev_state_t dev_stat; // owner
};
@@ -79,8 +85,7 @@ static struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV];
#define DEF_TIMEOUT 60000 /* 60,000 milliseconds (60 seconds) */
-struct sg_pt_freebsd_scsi {
- struct cam_device* cam_dev; // copy held for error processing
+struct sg_pt_freebsd_scsi { /* context of one SCSI/NVME command (pt object) */
union ccb *ccb;
uint8_t * cdb;
int cdb_len;
@@ -95,7 +100,9 @@ struct sg_pt_freebsd_scsi {
uint32_t dxfer_ilen;
uint32_t dxfer_olen;
uint32_t mdxfer_len;
- bool mdxfer_out;
+ uint32_t nvme_result; // cdw0 from completion
+ uint16_t nvme_status; // from completion: ((sct << 8) | sc)
+ uint8_t cq_dw0_3[16];
int timeout_ms;
int scsi_status;
int resid;
@@ -106,18 +113,22 @@ struct sg_pt_freebsd_scsi {
int dev_han; // should be >= FREEBSD_FDOFFSET then
// (dev_han - FREEBSD_FDOFFSET) is the
// index into devicetable[]
- bool is_nvme; // copy of same field in fdc object
- bool nvme_our_sntl; // copy of same field in fdc object
- struct sg_sntl_dev_state_t * dev_statp; // points to associated fdc
+ bool mdxfer_out;
+ bool is_nvme_dev; /* copied from owning mchanp */
+ bool nvme_our_sntl; /* true: our SNTL; false: received NVMe command */
+ struct freebsd_dev_channel * mchanp; /* associated device info */
};
struct sg_pt_base {
struct sg_pt_freebsd_scsi impl;
};
-static const uint32_t broadcast_nsid = SG_NVME_BROADCAST_NSID;
+// static const uint32_t broadcast_nsid = SG_NVME_BROADCAST_NSID;
-static int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb);
+#if (HAVE_NVME && (! IGNORE_NVME))
+static int sg_do_nvme_pt(struct sg_pt_freebsd_scsi * ptp, int fd,
+ bool is_admin, int timeout_secs, int vb);
+#endif
@@ -141,6 +152,32 @@ get_fdc_cp(const struct sg_pt_freebsd_scsi * ptp)
return devicetable[han];
}
+/* This works with /dev/nvme*, /dev/nvd* and /dev/nda* but not /dev/pass* */
+static int
+nvme_get_nsid(int fd, uint32_t *nsid, char *b, int blen, int vb)
+{
+ struct nvme_get_nsid gnsid;
+ int n_cdev = sizeof(gnsid.cdev);
+
+ if (ioctl(fd, NVME_GET_NSID, &gnsid) < 0) {
+ int err = errno;
+
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_GET_NSID) failed, errno=%d", __func__, err);
+ return -err;
+ }
+ if (n_cdev < blen) {
+ strncpy(b, gnsid.cdev, n_cdev);
+ b[n_cdev] = '\0';
+ } else {
+ strncpy(b, gnsid.cdev, blen);
+ b[blen - 1] = '\0';
+ }
+ if (nsid != NULL)
+ *nsid = gnsid.nsid;
+ return 0;
+}
+
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
int
scsi_pt_open_device(const char * device_name, bool read_only, int vb)
@@ -151,6 +188,43 @@ scsi_pt_open_device(const char * device_name, bool read_only, int vb)
return scsi_pt_open_flags(device_name, oflags, vb);
}
+/* Get a get device CCB for the specified device, borrowed from camdd.c */
+int
+sg_cam_get_cgd(struct cam_device *device, struct ccb_getdev *cgd, int vb)
+{
+ union ccb *ccb;
+ FILE * ferrp = sg_warnings_strm ? sg_warnings_strm : stderr;
+ int retval = 0;
+
+ ccb = cam_getccb(device);
+ if (ccb == NULL) {
+ if (vb)
+ pr2ws("%s: couldn't allocate CCB", __func__);
+ return -ENOMEM;
+ }
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cgd);
+ ccb->ccb_h.func_code = XPT_GDEV_TYPE;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ if (vb) {
+ pr2ws("%s: error sending Get Device Information CCB", __func__);
+ CAM_ERROR_PRINT(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, ferrp);
+ }
+ retval = -ENODEV;
+ goto bailout;
+ }
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (vb)
+ CAM_ERROR_PRINT(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, ferrp);
+ retval = -ENODEV;
+ goto bailout;
+ }
+ bcopy(&ccb->cgd, cgd, sizeof(struct ccb_getdev));
+bailout:
+ cam_freeccb(ccb);
+ return retval;
+}
+
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
* together. The 'oflags' is only used on NVMe devices. It is ignored on
* SCSI and ATA devices in FreeBSD.
@@ -158,16 +232,19 @@ scsi_pt_open_device(const char * device_name, bool read_only, int vb)
int
scsi_pt_open_flags(const char * device_name, int oflags, int vb)
{
- bool is_char, is_block, possible_nvme;
- char tmp, first_ch;
- int k, err, dev_fd, ret;
- uint32_t nsid, nv_ctrlid;
+ bool maybe_non_cam_nvme = false;
+ bool basnam0_n;
+ char first_ch;
+ int k, err, dev_fd, ret, handle_idx;
ssize_t s;
struct freebsd_dev_channel *fdc_p = NULL;
struct cam_device* cam_dev;
struct stat a_stat;
char dev_nm[PATH_MAX];
+ if (vb > 6)
+ pr2ws("%s: device_name=%s, oflags=0x%x\n", __func__, device_name,
+ oflags);
// Search table for a free entry
for (k = 0; k < FREEBSD_MAXDEV; k++)
if (! devicetable[k])
@@ -181,59 +258,7 @@ scsi_pt_open_flags(const char * device_name, int oflags, int vb)
ret = -EMFILE;
goto err_out;
}
- first_ch = device_name[0];
- if (('/' != first_ch) && ('.' != first_ch)) {
- char b[PATH_MAX];
-
- /* Step 1: if device_name is symlink, follow it */
- s = readlink(device_name, b, sizeof(b));
- if (s <= 0) {
- strncpy(b, device_name, PATH_MAX - 1);
- b[PATH_MAX - 1] = '\0';
- }
- /* Step 2: if no leading '/' nor '.' given, prepend '/dev/' */
- first_ch = b[0];
- if (('/' != first_ch) && ('.' != first_ch))
- snprintf(dev_nm, PATH_MAX, "%s%s", "/dev/", b);
- else
- strcpy(dev_nm, b);
- } else
- strcpy(dev_nm, device_name);
- if (stat(dev_nm, &a_stat) < 0) {
- err = errno;
- pr2ws("%s: unable to stat(%s): %s\n", __func__, dev_nm,
- strerror(err));
- ret = -err;
- goto err_out;
- }
- is_block = S_ISBLK(a_stat.st_mode);
- is_char = S_ISCHR(a_stat.st_mode);
- if (! (is_block || is_char)) {
- if (vb)
- pr2ws("%s: %s is not char nor block device\n", __func__,
- dev_nm);
- ret = -ENODEV;
- goto err_out;
- }
-
- /* Some code borrowed from smartmontools, Christian Franke */
- nsid = broadcast_nsid;
- nv_ctrlid = broadcast_nsid;
- possible_nvme = false;
- while (true) { /* dummy loop, so can 'break' out */
- if(sscanf(dev_nm, NVME_CTRLR_PREFIX "%u%c", &nv_ctrlid, &tmp) == 1) {
- if(nv_ctrlid == broadcast_nsid)
- break;
- } else if (sscanf(dev_nm, NVME_CTRLR_PREFIX "%d" NVME_NS_PREFIX
- "%d%c", &nv_ctrlid, &nsid, &tmp) == 2) {
- if((nv_ctrlid == broadcast_nsid) || (nsid == broadcast_nsid))
- break;
- } else
- break;
- possible_nvme = true;
- break;
- }
-
+ handle_idx = k;
fdc_p = (struct freebsd_dev_channel *)
calloc(1,sizeof(struct freebsd_dev_channel));
if (fdc_p == NULL) {
@@ -241,68 +266,150 @@ scsi_pt_open_flags(const char * device_name, int oflags, int vb)
ret = -ENOMEM;
goto err_out;
}
- fdc_p->dev_fd = -1;
-#if (HAVE_NVME && (! IGNORE_NVME))
- sntl_init_dev_stat(&fdc_p->dev_stat);
-#endif
+ fdc_p->nvme_fd_ns = -1;
+ fdc_p->nvme_fd_ctrl = -1;
if (! (fdc_p->devname = (char *)calloc(1, DEV_IDLEN+1))) {
ret = -ENOMEM;
goto err_out;
}
-
- if (possible_nvme) {
- // we should always open controller, not namespace device
- snprintf(fdc_p->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d",
- nv_ctrlid);
- dev_fd = open(fdc_p->devname, oflags);
- if (dev_fd < 0) {
- err = errno;
- if (vb)
- pr2ws("%s: open(%s) failed: %s (errno=%d), try SCSI/ATA\n",
- __func__, fdc_p->devname, strerror(err), err);
- goto scsi_ata_try;
- }
- fdc_p->is_nvme = true;
- fdc_p->nvme_our_sntl = true; /* guess at this stage */
- fdc_p->is_char = is_char;
- fdc_p->nsid = (broadcast_nsid == nsid) ? 0 : nsid;
- fdc_p->nv_ctrlid = nv_ctrlid;
- fdc_p->dev_fd = dev_fd;
- devicetable[k] = fdc_p;
- return k + FREEBSD_FDOFFSET;
- }
-
-scsi_ata_try:
- fdc_p->is_char = is_char;
- if (cam_get_device(dev_nm, fdc_p->devname, DEV_IDLEN,
+ /* Don't know yet whether device_name is a SCSI, NVME(non-CAM) or
+ * NVME(CAM) device. Start by assuming it is CAM. */
+ if (cam_get_device(device_name, fdc_p->devname, DEV_IDLEN,
&(fdc_p->unitnum)) == -1) {
- if (vb)
- pr2ws("bad device name structure\n");
- errno = EINVAL;
- ret = -errno;
+ if (vb > 3)
+ pr2ws("%s: cam_get_device(%s) fails, should work for SCSI and "
+ "NVMe devices\n", __func__, device_name, errno);
+ ret = -EINVAL;
goto err_out;
- }
- if (vb > 4)
- pr2ws("%s: cam_get_device, f->devname: %s, f->unitnum=%d\n", __func__,
- fdc_p->devname, fdc_p->unitnum);
+ } else if (vb > 6)
+ pr2ws("%s: cam_get_device() works, devname=%s unit=%u\n", __func__,
+ fdc_p->devname, fdc_p->unitnum);
if (! (cam_dev = cam_open_spec_device(fdc_p->devname,
fdc_p->unitnum, O_RDWR, NULL))) {
- if (vb)
+ if (vb > 6) {
pr2ws("cam_open_spec_device: %s\n", cam_errbuf);
- errno = EPERM; /* permissions or not CAM device (NVMe ?) */
- ret = -errno;
- goto err_out;
+ pr2ws("%s: so not CAM, but still maybe NVME\n", __func__);
+ }
+ maybe_non_cam_nvme = true;
+ } else { /* found CAM, could be SCSI or NVME(CAM) [nda driver] */
+ struct ccb_getdev cgd;
+
+ fdc_p->cam_dev = cam_dev;
+ ret = sg_cam_get_cgd(cam_dev, &cgd, vb);
+ if (ret)
+ goto err_out;
+ switch (cgd.protocol) {
+ case PROTO_SCSI:
+ fdc_p->is_nvme_dev = false;
+ break;
+ case PROTO_NVME:
+ fdc_p->is_nvme_dev = true;
+ fdc_p->is_cam_nvme = true;
+ fdc_p->nsid = cam_dev->target_lun & UINT32_MAX;
+ break;
+ case PROTO_ATA:
+ case PROTO_ATAPI:
+ case PROTO_SATAPM:
+ case PROTO_SEMB: /* SATA Enclosure Management bridge */
+ if (vb)
+ pr2ws("%s: ATA and derivative devices not supported\n",
+ __func__);
+ ret = -EINVAL;
+ break;
+#if __FreeBSD_version > 1200058
+ case PROTO_MMCSD:
+ if (vb)
+ pr2ws("%s: MMC and SD devices not supported\n",
+ __func__);
+ ret = -EINVAL;
+ break;
+#endif
+ default:
+ if (vb)
+ pr2ws("%s: unexpected device protocol\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ goto err_out;
+ if (0 == memcpy("pass", fdc_p->devname, 4))
+ fdc_p->is_pass = true;
+ }
+ if (maybe_non_cam_nvme) {
+ first_ch = device_name[0];
+ if (('/' != first_ch) && ('.' != first_ch)) {
+ char b[PATH_MAX];
+
+ /* Step 1: if device_name is symlink, follow it */
+ s = readlink(device_name, b, sizeof(b));
+ if (s <= 0) {
+ strncpy(b, device_name, PATH_MAX - 1);
+ b[PATH_MAX - 1] = '\0';
+ }
+ /* Step 2: if no leading '/' nor '.' given, prepend '/dev/' */
+ first_ch = b[0];
+ basnam0_n = ('n' == first_ch);
+ if (('/' != first_ch) && ('.' != first_ch))
+ snprintf(dev_nm, PATH_MAX, "%s%s", "/dev/", b);
+ else
+ strcpy(dev_nm, b);
+ } else {
+ const char * cp;
+
+ strcpy(dev_nm, device_name);
+ cp = basename(dev_nm);
+ basnam0_n = ('n' == *cp);
+ strcpy(dev_nm, device_name);
+ }
+ if (stat(dev_nm, &a_stat) < 0) {
+ err = errno;
+ pr2ws("%s: unable to stat(%s): %s\n", __func__, dev_nm,
+ strerror(err));
+ ret = -err;
+ goto err_out;
+ }
+ if (! (S_ISCHR(a_stat.st_mode))) {
+ if (vb)
+ pr2ws("%s: %s is not a char device ??\n", __func__, dev_nm);
+ ret = -ENODEV;
+ goto err_out;
+ }
+ dev_fd = open(dev_nm, oflags);
+ if (dev_fd < 0) {
+ err = errno;
+ if (vb)
+ pr2ws("%s: open(%s) failed: %s (errno=%d), try SCSI/ATA\n",
+ __func__, dev_nm, strerror(err), err);
+ ret = -err;
+ goto err_out;
+ }
+ ret = nvme_get_nsid(dev_fd, &fdc_p->nsid, fdc_p->devname, DEV_IDLEN,
+ vb);
+ if (ret)
+ goto err_out;
+ if (vb > 6)
+ pr2ws("%s: nvme_dev_nm: %s, nsid=%u\n", __func__, fdc_p->devname,
+ fdc_p->nsid);
+ fdc_p->is_nvme_dev = true;
+ fdc_p->is_cam_nvme = false;
+ if (fdc_p->nsid > 0)
+ fdc_p->nvme_fd_ns = dev_fd;
+ else
+ fdc_p->nvme_fd_ctrl = dev_fd;
}
- fdc_p->cam_dev = cam_dev;
// return pointer to "file descriptor" table entry, properly offset.
- devicetable[k] = fdc_p;
- return k + FREEBSD_FDOFFSET;
+ devicetable[handle_idx] = fdc_p;
+ return handle_idx + FREEBSD_FDOFFSET;
err_out: /* ret should be negative value (negated errno) */
if (fdc_p) {
if (fdc_p->devname)
free(fdc_p->devname);
+ if (fdc_p->nvme_fd_ns >= 0)
+ close(fdc_p->nvme_fd_ns);
+ if (fdc_p->nvme_fd_ctrl >= 0)
+ close(fdc_p->nvme_fd_ctrl);
free(fdc_p);
fdc_p = NULL;
}
@@ -327,11 +434,13 @@ scsi_pt_close_device(int device_han)
}
if (fdc_p->devname)
free(fdc_p->devname);
- if (fdc_p->cam_dev)
+ if (fdc_p->cam_dev) /* N.B. can be cam_nvme devices */
cam_close_device(fdc_p->cam_dev);
- if (fdc_p->is_nvme) {
- if (fdc_p->dev_fd >= 0)
- close(fdc_p->dev_fd);
+ else if (fdc_p->is_nvme_dev) {
+ if (fdc_p->nvme_fd_ns >= 0)
+ close(fdc_p->nvme_fd_ns);
+ if (fdc_p->nvme_fd_ctrl >= 0)
+ close(fdc_p->nvme_fd_ctrl);
if (fdc_p->free_nvme_id_ctlp) {
free(fdc_p->free_nvme_id_ctlp);
fdc_p->nvme_id_ctlp = NULL;
@@ -345,10 +454,12 @@ scsi_pt_close_device(int device_han)
}
/* Assumes device_han is an "open" file handle associated with some device.
- * Returns 1 if SCSI generic pass-though device, returns 2 if secondary
- * SCSI pass-through device (in Linux a bsg device); returns 3 is char
- * NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
- * NSID), or 0 if something else (e.g. ATA block device) or device_han < 0.
+ * Returns 1 if SCSI generic pass-though device [SCSI CAM primary: nda0],
+ * returns 2 if secondary * SCSI pass-through device [SCSI CAM: pass<n>];
+ * returns 3 if non-CAM NVMe with no nsid [nvme0]; returns 4 if non-CAM
+ * NVMe device with nsid (> 0) [nvme0ns1, nvd0]; returns 5 if CAM NVMe
+ * (with or without nsid) [nda0]; or returns 0 if something else (e.g. ATA
+ * block device) or device_han < 0.
* If error, returns negated errno (operating system) value. */
int
check_pt_file_handle(int device_han, const char * device_name, int vb)
@@ -356,18 +467,26 @@ check_pt_file_handle(int device_han, const char * device_name, int vb)
struct freebsd_dev_channel *fdc_p;
int han = device_han - FREEBSD_FDOFFSET;
+ if (vb > 6)
+ pr2ws("%s: device_handle=%d, device_name: %s\n", __func__,
+ device_han, device_name);
if ((han < 0) || (han >= FREEBSD_MAXDEV))
return -ENODEV;
fdc_p = devicetable[han];
if (NULL == fdc_p)
return -ENODEV;
- if (fdc_p->is_nvme)
- return 4 - (int)fdc_p->is_char;
- else if (fdc_p->cam_dev)
- return 2 - (int)fdc_p->is_char;
+ if (fdc_p->is_nvme_dev) {
+ if (fdc_p->is_cam_nvme)
+ return 5;
+ else if (fdc_p->nsid == 0)
+ return 3;
+ else
+ return 4; /* Something like nvme0ns1 or nvd0 */
+ } else if (fdc_p->cam_dev)
+ return fdc_p->is_pass ? 2 : 1;
else {
if (vb)
- pr2ws("%s: neither SCSI nor NVMe ... hmm, dvice name: %s\n",
+ pr2ws("%s: neither SCSI nor NVMe ... hmm, device name: %s\n",
__func__, device_name);
return 0;
}
@@ -393,11 +512,9 @@ construct_scsi_pt_obj_with_fd(int dev_han, int vb)
fdc_p = get_fdc_p(ptp);
if (fdc_p) {
- ptp->is_nvme = fdc_p->is_nvme;
- ptp->cam_dev = fdc_p->cam_dev;
- ptp->dev_statp = &fdc_p->dev_stat;
+ ptp->mchanp = fdc_p;
#if (HAVE_NVME && (! IGNORE_NVME))
- sntl_init_dev_stat(ptp->dev_statp);
+ sntl_init_dev_stat(&fdc_p->dev_stat);
if (! checked_ev_dsense) {
ev_dsense = sg_get_initial_dsense();
checked_ev_dsense = true;
@@ -438,11 +555,9 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp)
void
clear_scsi_pt_obj(struct sg_pt_base * vp)
{
- bool is_nvme;
int dev_han;
struct sg_pt_freebsd_scsi * ptp;
- struct cam_device* cam_dev;
- struct sg_sntl_dev_state_t * dsp;
+ struct freebsd_dev_channel *fdc_p;
if (NULL == vp) {
pr2ws(">>>>> %s: NULL pointer given\n", __func__);
@@ -451,16 +566,12 @@ clear_scsi_pt_obj(struct sg_pt_base * vp)
if ((ptp = &vp->impl)) {
if (ptp->ccb)
cam_freeccb(ptp->ccb);
- is_nvme = ptp->is_nvme;
dev_han = ptp->dev_han;
- cam_dev = ptp->cam_dev;
- dsp = ptp->dev_statp;
+ fdc_p = ptp->mchanp;
memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi));
ptp->dxfer_dir = CAM_DIR_NONE;
ptp->dev_han = dev_han;
- ptp->is_nvme = is_nvme;
- ptp->cam_dev = cam_dev;
- ptp->dev_statp = dsp;
+ ptp->mchanp = fdc_p;
}
}
@@ -468,7 +579,6 @@ void
partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
{
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- struct freebsd_dev_channel *fdc_p;
if (NULL == ptp)
return;
@@ -481,9 +591,7 @@ partial_clear_scsi_pt_obj(struct sg_pt_base * vp)
ptp->dxfer_ilen = 0;
ptp->dxferop = NULL;
ptp->dxfer_olen = 0;
- fdc_p = get_fdc_p(ptp);
- if (fdc_p)
- fdc_p->nvme_result = 0;
+ ptp->nvme_result = 0;
}
/* Forget any previous dev_han and install the one given. May attempt to
@@ -506,8 +614,6 @@ set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb)
if (dev_han < 0) {
ptp->dev_han = -1;
ptp->dxfer_dir = CAM_DIR_NONE;
- ptp->is_nvme = false;
- ptp->cam_dev = NULL;
return 0;
}
fdc_p = get_fdc_p(ptp);
@@ -523,9 +629,7 @@ set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb)
ptp->scsi_status = 0;
ptp->dev_han = dev_han;
ptp->dxfer_dir = CAM_DIR_NONE;
- ptp->is_nvme = fdc_p->is_nvme;
- ptp->cam_dev = fdc_p->cam_dev;
- ptp->dev_statp = &fdc_p->dev_stat;
+ ptp->mchanp = fdc_p;
}
return 0;
}
@@ -671,7 +775,7 @@ set_scsi_pt_task_attr(struct sg_pt_base * vp,
void
set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
{
- if (objp) { ; } /* unused, suppress warning */
+ if (objp) { ; } /* unused, suppress warning */
if (flags) { ; } /* unused, suppress warning */
}
@@ -684,8 +788,11 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int vb)
int len;
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
struct freebsd_dev_channel *fdc_p;
+ FILE * ferrp = sg_warnings_strm ? sg_warnings_strm : stderr;
union ccb *ccb;
+ if (vb > 6)
+ pr2ws("%s: dev_han=%d, time_secs=%d\n", __func__, dev_han, time_secs);
ptp->os_err = 0;
if (ptp->in_err) {
if (vb)
@@ -716,21 +823,25 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int vb)
pr2ws("No command (cdb) given\n");
return SCSI_PT_DO_BAD_PARAMS;
}
- if (ptp->is_nvme)
- return sg_do_nvme_pt(vp, -1, vb);
- fdc_p = get_fdc_p(ptp);
+ fdc_p = ptp->mchanp;
if (NULL == fdc_p) {
- if (vb)
- pr2ws("File descriptor bad or closed??\n");
- ptp->os_err = ENODEV;
- return -ptp->os_err;
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ if (vb)
+ pr2ws("File descriptor bad or closed??\n");
+ ptp->os_err = ENODEV;
+ return -ptp->os_err;
+ }
+ ptp->mchanp = fdc_p;
}
- ptp->is_nvme = fdc_p->is_nvme;
- ptp->dev_statp = &fdc_p->dev_stat;
- if (fdc_p->is_nvme)
- return sg_do_nvme_pt(vp, -1, vb);
+#if (HAVE_NVME && (! IGNORE_NVME))
+ if (fdc_p->is_nvme_dev)
+ return sg_do_nvme_pt(ptp, -1, true /* assume Admin */, time_secs, vb);
+#endif
+ /* SCSI CAM pass-through follows */
+ ptp->is_nvme_dev = fdc_p->is_nvme_dev;
if (NULL == fdc_p->cam_dev) {
if (vb)
pr2ws("No open CAM device\n");
@@ -767,11 +878,9 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int vb)
if (cam_send_ccb(fdc_p->cam_dev, ccb) < 0) {
if (vb) {
- warn("error sending SCSI ccb");
- #if __FreeBSD_version > 500000
- cam_error_print(fdc_p->cam_dev, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
- #endif
+ pr2serr("%s: cam_send_ccb() error\n", __func__);
+ CAM_ERROR_PRINT(fdc_p->cam_dev, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, ferrp);
}
cam_freeccb(ptp->ccb);
ptp->ccb = NULL;
@@ -797,7 +906,6 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int vb)
} else
ptp->transport_err = 1;
- ptp->cam_dev = fdc_p->cam_dev; // for error processing
return 0;
}
@@ -824,8 +932,9 @@ get_scsi_pt_resid(const struct sg_pt_base * vp)
{
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- return ((NULL == ptp) || (ptp->is_nvme && ! ptp->nvme_our_sntl)) ?
- 0 : ptp->resid;
+ if ((NULL == ptp) || (NULL == ptp->mchanp))
+ return 0;
+ return ((ptp->is_nvme_dev && ! ptp->nvme_our_sntl)) ? 0 : ptp->resid;
}
void
@@ -879,14 +988,13 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp)
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
- if (ptp->is_nvme && ! ptp->nvme_our_sntl) {
- const struct freebsd_dev_channel *fdc_p;
+ const struct freebsd_dev_channel * fdc_p = ptp->mchanp;
- fdc_p = get_fdc_cp(ptp);
- if (NULL == fdc_p)
- return -1;
- return (int)fdc_p->nvme_status;
- } else
+ if (NULL == fdc_p)
+ return -1;
+ if (ptp->is_nvme_dev && ! ptp->nvme_our_sntl)
+ return (int)ptp->nvme_status;
+ else
return ptp->scsi_status;
}
return -1;
@@ -899,14 +1007,13 @@ get_pt_result(const struct sg_pt_base * vp)
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
- if (ptp->is_nvme && ! ptp->nvme_our_sntl) {
- const struct freebsd_dev_channel *fdc_p;
+ const struct freebsd_dev_channel * fdc_p = ptp->mchanp;
- fdc_p = get_fdc_cp(ptp);
- if (NULL == fdc_p)
- return -1;
- return fdc_p->nvme_result;
- } else
+ if (NULL == fdc_p)
+ return -1;
+ if (ptp->is_nvme_dev && ! ptp->nvme_our_sntl)
+ return ptp->nvme_result;
+ else
return (uint32_t)ptp->scsi_status;
}
return 0xffffffff;
@@ -983,15 +1090,15 @@ get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
b[max_b_len - 1] = '\0';
return b;
}
- if (ptp->is_nvme) {
+ if (ptp->mchanp && ptp->mchanp->is_nvme_dev) {
snprintf(b, max_b_len, "NVMe has no transport errors at present "
"but tranport_err=%d ??\n", ptp->transport_err);
return b;
}
#if __FreeBSD_version > 500000
- if (ptp->cam_dev)
- cam_error_string(ptp->cam_dev, ptp->ccb, b, max_b_len, CAM_ESF_ALL,
- CAM_EPF_ALL);
+ if (ptp->mchanp && ptp->mchanp->cam_dev)
+ cam_error_string(ptp->mchanp->cam_dev, ptp->ccb, b, max_b_len,
+ CAM_ESF_ALL, CAM_EPF_ALL);
else {
strncpy(b, "no transport error available", max_b_len);
b[max_b_len - 1] = '\0';
@@ -1017,10 +1124,7 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
errno = ENODEV;
return false;
}
- /* if unequal, cast away const and drive fdc_p value into ptp */
- if (ptp->is_nvme != fdc_p->is_nvme) /* indicates logic error */
- ((struct sg_pt_freebsd_scsi *)ptp)->is_nvme = fdc_p->is_nvme;
- return fdc_p->is_nvme;
+ return fdc_p->is_nvme_dev;
}
return false;
}
@@ -1062,7 +1166,18 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
#define SCSI_MAINT_IN_OPC 0xa3
#define SCSI_MODE_SENSE10_OPC 0x5a
#define SCSI_MODE_SELECT10_OPC 0x55
+#define SCSI_READ10_OPC 0x28
+#define SCSI_READ16_OPC 0x88
#define SCSI_READ_CAPACITY10_OPC 0x25
+#define SCSI_START_STOP_OPC 0x1b
+#define SCSI_SYNC_CACHE10_OPC 0x35
+#define SCSI_SYNC_CACHE16_OPC 0x91
+#define SCSI_VERIFY10_OPC 0x2f
+#define SCSI_VERIFY16_OPC 0x8f
+#define SCSI_WRITE10_OPC 0x2a
+#define SCSI_WRITE16_OPC 0x8a
+#define SCSI_WRITE_SAME10_OPC 0x41
+#define SCSI_WRITE_SAME16_OPC 0x93
#define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c
#define SCSI_REP_SUP_OPCS_OPC 0xc
#define SCSI_REP_SUP_TMFS_OPC 0xd
@@ -1102,6 +1217,26 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
#define MISCOMPARE_VERIFY_ASC 0x1d
#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */
#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
+#define PCIE_ERR_ASC 0x4b
+#define PCIE_UNSUPP_REQ_ASCQ 0x13
+
+/* NVMe Admin commands */
+#define SG_NVME_AD_GET_FEATURE 0xa
+#define SG_NVME_AD_SET_FEATURE 0x9
+#define SG_NVME_AD_IDENTIFY 0x6 /* similar to SCSI INQUIRY */
+#define SG_NVME_AD_DEV_SELT_TEST 0x14
+#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_FLUSH 0x0 /* SCSI SYNCHRONIZE CACHE */
+#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_WRITE_ZEROES 0x8 /* SCSI WRITE SAME */
+
+#define SG_NVME_RW_CDW12_FUA (1 << 30) /* Force Unit Access bit */
#if (HAVE_NVME && (! IGNORE_NVME))
@@ -1109,7 +1244,7 @@ static void
mk_sense_asc_ascq(struct sg_pt_freebsd_scsi * ptp, int sk, int asc, int ascq,
int vb)
{
- bool dsense = ptp->dev_statp->scsi_dsense;
+ bool dsense = ptp->mchanp ? ptp->mchanp->dev_stat.scsi_dsense : false;
int n;
uint8_t * sbp = ptp->sense;
@@ -1133,7 +1268,7 @@ mk_sense_from_nvme_status(struct sg_pt_freebsd_scsi * ptp, uint16_t sct_sc,
int vb)
{
bool ok;
- bool dsense = ptp->dev_statp->scsi_dsense;
+ bool dsense = ptp->mchanp ? ptp->mchanp->dev_stat.scsi_dsense : false;
int n;
uint8_t sstatus, sk, asc, ascq;
uint8_t * sbp = ptp->sense;
@@ -1171,7 +1306,7 @@ static void
mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb,
int in_byte, int in_bit, int vb)
{
- bool ds = ptp->dev_statp->scsi_dsense;
+ bool ds = ptp->mchanp ? ptp->mchanp->dev_stat.scsi_dsense : false;
int sl, asc, n;
uint8_t * sbp = (uint8_t *)ptp->sense;
uint8_t sks[4];
@@ -1210,50 +1345,189 @@ mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb,
((in_bit > 0) ? (0x7 & in_bit) : 0));
}
-/* Does actual ioctl(NVME_PASSTHROUGH_CMD). Returns 0 on success; negative
- * values are Unix negated errno values; positive values are NVMe status
- * (i.e. ((SCT << 8) | SC) ). */
+#if 0
+static void
+nvme_cbfcn(struct cam_periph * camperp, union ccb * ccb)
+{
+ pr2ws("%s: >>>> called, camperp=%p, ccb=%p\n", __func__, camperp, ccb);
+}
+#endif
+
+/* Does actual ioctl(NVME_PASSTHROUGH_CMD) or uses NVME(CAM) interface.
+ * Returns 0 on success; negative values are Unix negated errno values;
+ * positive values are NVMe status (i.e. ((SCT << 8) | SC) ). */
static int
-nvme_pt_low(struct freebsd_dev_channel *fdc_p, void * dxferp, uint32_t len,
- bool is_read, struct nvme_pt_command * npcp, int vb)
+nvme_pt_low(struct sg_pt_freebsd_scsi * ptp, void * dxferp, uint32_t len,
+ bool is_admin, bool is_read, struct nvme_pt_command * npcp,
+ int time_secs, int vb)
{
- int err;
+ int err, dev_fd;
uint16_t sct_sc;
uint8_t opcode;
+ struct freebsd_dev_channel *fdc_p = ptp->mchanp;
+ FILE * ferrp = sg_warnings_strm ? sg_warnings_strm : stderr;
char b[80];
- if (fdc_p->dev_fd < 0) {
- if (vb)
- pr2ws("%s: is_nvme is true but dev_fd<0, inconsistent\n",
- __func__);
- return -EINVAL;
- }
+ if (vb > 6)
+ pr2ws("%s: is_read=%d, time_secs=%d, is_cam_nvme=%d, is_admin=%d\n",
+ __func__, (int)is_read, time_secs, (int)fdc_p->is_cam_nvme,
+ (int)is_admin);
+ ptp->is_nvme_dev = fdc_p->is_nvme_dev;
npcp->buf = dxferp;
npcp->len = len;
npcp->is_read = (uint32_t)is_read;
opcode = npcp->cmd.opc;
- err = ioctl(fdc_p->dev_fd, NVME_PASSTHROUGH_CMD, npcp);
- if (err < 0)
- return -errno; /* Assume Unix error in normal place */
+ if (fdc_p->is_cam_nvme)
+ goto cam_nvme;
+
+ if (is_admin) {
+ if (fdc_p->nvme_fd_ctrl < 0) {
+ if (vb)
+ pr2ws("%s: not CAM but nvme_fd_ctrl<0, inconsistent\n",
+ __func__);
+ return -EINVAL;
+ }
+ dev_fd = fdc_p->nvme_fd_ctrl;
+ } else {
+ if (fdc_p->nvme_fd_ns < 0) {
+ if (vb)
+ pr2ws("%s: not CAM but nvme_fd_ns<0, inconsistent\n",
+ __func__);
+ return -EINVAL;
+ }
+ dev_fd = fdc_p->nvme_fd_ns;
+ }
+ err = ioctl(dev_fd, NVME_PASSTHROUGH_CMD, npcp);
+ if (err < 0) {
+ err = errno;
+ if (vb)
+ pr2ws("%s: ioctl(NVME_PASSTHROUGH_CMD) errno: %s\n", __func__,
+ strerror(err));
+ /* when that ioctl returns an error npcp->cpl is not populated */
+ return -err;
+ }
+
+#if __FreeBSD_version <= 1200058
+ sct_sc = ((npcp->cpl.status.sct << 8) | npcp->cpl.status.sc);
+#else
sct_sc = (NVME_STATUS_GET_SCT(npcp->cpl.status) << 8) |
NVME_STATUS_GET_SC(npcp->cpl.status);
- fdc_p->nvme_result = npcp->cpl.cdw0;
+#endif
+ ptp->nvme_result = npcp->cpl.cdw0;
sg_put_unaligned_le32(npcp->cpl.cdw0,
- fdc_p->cq_dw0_3 + SG_NVME_PT_CQ_RESULT);
- sg_put_unaligned_le32(npcp->cpl.rsvd1, fdc_p->cq_dw0_3 + 4);
- sg_put_unaligned_le16(npcp->cpl.sqhd, fdc_p->cq_dw0_3 + 8);
- sg_put_unaligned_le16(npcp->cpl.sqid, fdc_p->cq_dw0_3 + 10);
- sg_put_unaligned_le16(npcp->cpl.cid, fdc_p->cq_dw0_3 + 12);
+ ptp->cq_dw0_3 + SG_NVME_PT_CQ_RESULT);
+ sg_put_unaligned_le32(npcp->cpl.rsvd1, ptp->cq_dw0_3 + 4);
+ sg_put_unaligned_le16(npcp->cpl.sqhd, ptp->cq_dw0_3 + 8);
+ sg_put_unaligned_le16(npcp->cpl.sqid, ptp->cq_dw0_3 + 10);
+ sg_put_unaligned_le16(npcp->cpl.cid, ptp->cq_dw0_3 + 12);
sg_put_unaligned_le16(*((const uint16_t *)&(npcp->cpl.status)),
- fdc_p->cq_dw0_3 + SG_NVME_PT_CQ_STATUS_P);
+ ptp->cq_dw0_3 + SG_NVME_PT_CQ_STATUS_P);
if (sct_sc && (vb > 1)) {
char nam[64];
- sg_get_nvme_opcode_name(opcode, true, sizeof(nam), nam);
+ sg_get_nvme_opcode_name(opcode, is_admin, sizeof(nam), nam);
pr2ws("%s: %s [0x%x], status: %s\n", __func__, nam, opcode,
sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b));
}
return sct_sc;
+
+cam_nvme:
+ {
+ cam_status ccb_status;
+ union ccb *ccb;
+ struct ccb_nvmeio *nviop;
+
+ if (NULL == ptp->ccb) { /* re-use if we have one already */
+ if (! (ccb = cam_getccb(fdc_p->cam_dev))) {
+ if (vb)
+ pr2ws("%s: cam_getccb: failed\n", __func__);
+ ptp->os_err = ENOMEM;
+ return -ptp->os_err;
+ }
+ ptp->ccb = ccb;
+ } else
+ ccb = ptp->ccb;
+ nviop = &ccb->nvmeio;
+ CCB_CLEAR_ALL_EXCEPT_HDR(nviop);
+
+ memcpy(&nviop->cmd, &npcp->cmd, sizeof(nviop->cmd));
+ ptp->timeout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT;
+ if (is_admin)
+ cam_fill_nvmeadmin(nviop,
+ 1 /* retries */,
+ NULL,
+ is_read ? CAM_DIR_IN : CAM_DIR_OUT,
+ dxferp,
+ len,
+ ptp->timeout_ms);
+
+ else { /* NVM command set, rather than Admin */
+ if (fdc_p->nsid != npcp->cmd.nsid) {
+ if (vb)
+ pr2ws("%s: device node nsid [%u] not equal to cmd nsid "
+ "[%u]\n", __func__, fdc_p->nsid, npcp->cmd.nsid);
+ return -EINVAL;
+ }
+ cam_fill_nvmeio(nviop,
+ 1 /* retries */,
+ NULL,
+ is_read ? CAM_DIR_IN : CAM_DIR_OUT,
+ dxferp,
+ len,
+ ptp->timeout_ms);
+ }
+
+ if (cam_send_ccb(fdc_p->cam_dev, ccb) < 0) {
+ if (vb) {
+ pr2ws("%s: cam_send_ccb(NVME) %s ccb erro\n", __func__,
+ (is_admin ? "Admin" : "NVM"));
+ CAM_ERROR_PRINT(fdc_p->cam_dev, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, ferrp);
+ }
+ cam_freeccb(ptp->ccb);
+ ptp->ccb = NULL;
+ ptp->os_err = EIO;
+ return -ptp->os_err;
+ }
+ ccb_status = ccb->ccb_h.status & CAM_STATUS_MASK;
+ if (ccb_status == CAM_REQ_CMP) {
+ ptp->nvme_result = 0;
+ ptp->os_err = 0;
+ return 0;
+ }
+ /* error processing follows ... */
+ ptp->os_err = EIO;
+ if (vb) {
+ pr2ws("%s: ccb_status != CAM_REQ_CMP\n", __func__);
+ CAM_ERROR_PRINT(fdc_p->cam_dev, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, ferrp);
+ }
+#if __FreeBSD_version <= 1200058
+ sct_sc = ((nviop->cpl.status.sct << 8) | nviop->cpl.status.sc);
+#else
+ sct_sc = (NVME_STATUS_GET_SCT(nviop->cpl.status) << 8) |
+ NVME_STATUS_GET_SC(nviop->cpl.status);
+#endif
+ ptp->nvme_result = nviop->cpl.cdw0;
+ sg_put_unaligned_le32(nviop->cpl.cdw0,
+ ptp->cq_dw0_3 + SG_NVME_PT_CQ_RESULT);
+ sg_put_unaligned_le32(nviop->cpl.rsvd1, ptp->cq_dw0_3 + 4);
+ sg_put_unaligned_le16(nviop->cpl.sqhd, ptp->cq_dw0_3 + 8);
+ sg_put_unaligned_le16(nviop->cpl.sqid, ptp->cq_dw0_3 + 10);
+ sg_put_unaligned_le16(nviop->cpl.cid, ptp->cq_dw0_3 + 12);
+ sg_put_unaligned_le16(*((const uint16_t *)&(nviop->cpl.status)),
+ ptp->cq_dw0_3 + SG_NVME_PT_CQ_STATUS_P);
+ if (sct_sc && (vb > 1)) {
+ char nam[64];
+
+ sg_get_nvme_opcode_name(opcode, is_admin, sizeof(nam),
+ nam);
+ pr2ws("%s: %s [0x%x], status: %s\n", __func__, nam, opcode,
+ sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b));
+ }
+ return sct_sc ? sct_sc : ptp->os_err;
+ }
+ return 0;
}
static void
@@ -1265,7 +1539,7 @@ sntl_check_enclosure_override(struct freebsd_dev_channel * fdc_p, int vb)
if (NULL == up)
return;
nvmsr = up[253];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: enter, nvmsr=%u\n", __func__, nvmsr);
fdc_p->dev_stat.id_ctl253 = nvmsr;
switch (fdc_p->dev_stat.enclosure_override) {
@@ -1310,29 +1584,31 @@ sntl_check_enclosure_override(struct freebsd_dev_channel * fdc_p, int vb)
}
static int
-sntl_do_identify(struct freebsd_dev_channel * fdc_p, int cns, int nsid,
- int u_len, uint8_t * up, int vb)
+sntl_do_identify(struct sg_pt_freebsd_scsi * ptp, int cns, int nsid,
+ int u_len, uint8_t * up, int time_secs, int vb)
{
int err;
struct nvme_pt_command npc;
uint8_t * npc_up = (uint8_t *)&npc;
+ if (vb > 5)
+ pr2ws("%s: nsid=%d\n", __func__, nsid);
memset(npc_up, 0, sizeof(npc));
- npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */
+ npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_IDENTIFY;
sg_put_unaligned_le32(nsid, npc_up + SG_NVME_PT_NSID);
/* CNS=0x1 Identify: controller */
sg_put_unaligned_le32(cns, npc_up + SG_NVME_PT_CDW10);
sg_put_unaligned_le64((sg_uintptr_t)up, npc_up + SG_NVME_PT_ADDR);
sg_put_unaligned_le32(u_len, npc_up + SG_NVME_PT_DATA_LEN);
- err = nvme_pt_low(fdc_p, up, u_len, true, &npc, vb);
+ err = nvme_pt_low(ptp, up, u_len, true, true, &npc, time_secs, vb);
if (err) {
if (err < 0) {
if (vb > 1)
- pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__,
strerror(-err), -err);
return err;
} else { /* non-zero NVMe command status */
- fdc_p->nvme_status = err;
+ ptp->nvme_status = err;
return SG_LIB_NVME_STATUS;
}
}
@@ -1341,10 +1617,11 @@ sntl_do_identify(struct freebsd_dev_channel * fdc_p, int cns, int nsid,
/* Currently only caches associated controller response (4096 bytes) */
static int
-sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb)
+sntl_cache_identity(struct sg_pt_freebsd_scsi * ptp, int time_secs, int vb)
{
int ret;
uint32_t pg_sz = sg_get_page_size();
+ struct freebsd_dev_channel * fdc_p = ptp->mchanp;
fdc_p->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz,
&fdc_p->free_nvme_id_ctlp, vb > 3);
@@ -1352,8 +1629,8 @@ sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb)
pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
return -ENOMEM;
}
- ret = sntl_do_identify(fdc_p, 0x1 /* CNS */, 0 /* nsid */, pg_sz,
- fdc_p->nvme_id_ctlp, vb);
+ ret = sntl_do_identify(ptp, 0x1 /* CNS */, 0 /* nsid */, pg_sz,
+ fdc_p->nvme_id_ctlp, time_secs, vb);
if (0 == ret)
sntl_check_enclosure_override(fdc_p, vb);
return (ret < 0) ? sg_convert_errno(-ret) : ret;
@@ -1363,7 +1640,8 @@ static const char * nvme_scsi_vendor_str = "NVMe ";
static const uint16_t inq_resp_len = 36;
static int
-sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
+sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
+ int vb)
{
bool evpd;
bool cp_id_ctl = false;
@@ -1375,7 +1653,7 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
uint8_t * free_nvme_id_ns = NULL;
uint8_t inq_dout[256];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: starting\n", __func__);
if (0x2 & cdbp[1]) { /* Reject CmdDt=1 */
@@ -1388,9 +1666,9 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
return -EINVAL;
}
if (NULL == fdc_p->nvme_id_ctlp) {
- res = sntl_cache_identity(fdc_p, vb);
+ res = sntl_cache_identity(ptp, time_secs, vb);
if (SG_LIB_NVME_STATUS == res) {
- mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb);
return 0;
} else if (res) /* should be negative errno */
return res;
@@ -1430,7 +1708,7 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
uint8_t * npc_up = (uint8_t *)&npc;
memset(npc_up, 0, sizeof(npc));
- npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */
+ npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_IDENTIFY;
sg_put_unaligned_le32(fdc_p->nsid,
npc_up + SG_NVME_PT_NSID);
/* CNS=0x0 Identify: namespace */
@@ -1439,8 +1717,8 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
npc_up + SG_NVME_PT_ADDR);
sg_put_unaligned_le32(pg_sz,
npc_up + SG_NVME_PT_DATA_LEN);
- res = nvme_pt_low(fdc_p, nvme_id_ns, pg_sz, true, &npc,
- vb > 3);
+ res = nvme_pt_low(ptp, nvme_id_ns, pg_sz, true, true,
+ &npc, time_secs, vb > 3);
if (res) {
free(free_nvme_id_ns);
free_nvme_id_ns = NULL;
@@ -1528,7 +1806,8 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
}
static int
-sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
+sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
{
int res;
uint16_t sel_report;
@@ -1537,7 +1816,7 @@ sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
uint8_t * rl_doutp;
uint8_t * up;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: starting\n", __func__);
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
@@ -1547,9 +1826,9 @@ sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
sel_report = cdbp[2];
alloc_len = sg_get_unaligned_be32(cdbp + 6);
if (NULL == fdc_p->nvme_id_ctlp) {
- res = sntl_cache_identity(fdc_p, vb);
+ res = sntl_cache_identity(ptp, time_secs, vb);
if (SG_LIB_NVME_STATUS == res) {
- mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb);
return 0;
} else if (res)
return res;
@@ -1598,7 +1877,7 @@ sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
}
static int
-sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb)
+sntl_tur(struct sg_pt_freebsd_scsi * ptp, int time_secs, int vb)
{
int res, err;
uint32_t pow_state;
@@ -1606,7 +1885,7 @@ sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb)
uint8_t * npc_up = (uint8_t *)&npc;
struct freebsd_dev_channel * fdc_p;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: starting\n", __func__);
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
@@ -1614,32 +1893,32 @@ sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb)
return -EINVAL;
}
if (NULL == fdc_p->nvme_id_ctlp) {
- res = sntl_cache_identity(fdc_p, vb);
+ res = sntl_cache_identity(ptp, time_secs, vb);
if (SG_LIB_NVME_STATUS == res) {
- mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb);
return 0;
} else if (res)
return res;
}
memset(npc_up, 0, sizeof(npc));
- npc_up[SG_NVME_PT_OPCODE] = 0xa; /* Get feature */
+ npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_GET_FEATURE;
sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, npc_up + SG_NVME_PT_NSID);
/* SEL=0 (current), Feature=2 Power Management */
sg_put_unaligned_le32(0x2, npc_up + SG_NVME_PT_CDW10);
- err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb);
+ err = nvme_pt_low(ptp, NULL, 0, true, false, &npc, time_secs, vb);
if (err) {
if (err < 0) {
if (vb > 1)
- pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__,
strerror(-err), -err);
return err;
} else {
- fdc_p->nvme_status = err;
+ ptp->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
return 0;
}
}
- pow_state = (0x1f & fdc_p->nvme_result);
+ pow_state = (0x1f & ptp->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
#if 0 /* pow_state bounces around too much on laptop */
@@ -1651,7 +1930,8 @@ sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb)
}
static int
-sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
+sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
{
bool desc;
int res, err;
@@ -1661,7 +1941,7 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
struct freebsd_dev_channel * fdc_p;
uint8_t rs_dout[64];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: starting\n", __func__);
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
@@ -1669,9 +1949,9 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
return -EINVAL;
}
if (NULL == fdc_p->nvme_id_ctlp) {
- res = sntl_cache_identity(fdc_p, vb);
+ res = sntl_cache_identity(ptp, time_secs, vb);
if (SG_LIB_NVME_STATUS == res) {
- mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb);
return 0;
} else if (res)
return res;
@@ -1679,24 +1959,24 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
desc = !!(0x1 & cdbp[1]);
alloc_len = cdbp[4];
memset(npc_up, 0, sizeof(npc));
- npc_up[SG_NVME_PT_OPCODE] = 0xa; /* Get feature */
+ npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_GET_FEATURE;
sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, npc_up + SG_NVME_PT_NSID);
/* SEL=0 (current), Feature=2 Power Management */
sg_put_unaligned_le32(0x2, npc_up + SG_NVME_PT_CDW10);
- err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb);
+ err = nvme_pt_low(ptp, NULL, 0, true, false, &npc, time_secs, vb);
if (err) {
if (err < 0) {
if (vb > 1)
- pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__,
strerror(-err), -err);
return err;
} else {
- fdc_p->nvme_status = err;
+ ptp->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
return 0;
}
}
- pow_state = (0x1f & fdc_p->nvme_result);
+ pow_state = (0x1f & ptp->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
memset(rs_dout, 0, sizeof(rs_dout));
@@ -1716,7 +1996,8 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
}
static int
-sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
+sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
{
bool is_msense = (SCSI_MODE_SENSE10_OPC == cdbp[0]);
int res, n, len;
@@ -1724,7 +2005,7 @@ sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
struct freebsd_dev_channel * fdc_p;
struct sg_sntl_result_t sntl_result;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: mse%s\n", __func__, (is_msense ? "nse" : "lect"));
fdc_p = get_fdc_p(ptp);
if (NULL == fdc_p) {
@@ -1732,9 +2013,9 @@ sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
return -EINVAL;
}
if (NULL == fdc_p->nvme_id_ctlp) {
- res = sntl_cache_identity(fdc_p, vb);
+ res = sntl_cache_identity(ptp, time_secs, vb);
if (SG_LIB_NVME_STATUS == res) {
- mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb);
return 0;
} else if (res)
return res;
@@ -1781,7 +2062,8 @@ sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
* (SCSI Enclosure Services) use of diagnostics pages that are
* related to SES. */
static int
-sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
+sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
{
bool pf, self_test;
int err;
@@ -1795,7 +2077,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
st_cd = 0x7 & (cdbp[1] >> 5);
pf = !! (0x4 & cdbp[1]);
self_test = !! (0x10 & cdbp[1]);
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: pf=%d, self_test=%d, st_code=%d\n", __func__, (int)pf,
(int)self_test, (int)st_cd);
fdc_p = get_fdc_p(ptp);
@@ -1805,7 +2087,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
}
if (self_test || st_cd) {
memset(npc_up, 0, sizeof(npc));
- npc_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */
+ npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_DEV_SELT_TEST;
/* just this namespace (if there is one) and controller */
sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
switch (st_cd) {
@@ -1827,7 +2109,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
return 0;
}
sg_put_unaligned_le32(nvme_dst, npc_up + SG_NVME_PT_CDW10);
- err = nvme_pt_low(fdc_p, NULL, 0x0, false, &npc, vb);
+ err = nvme_pt_low(ptp, NULL, 0x0, true, false, &npc, time_secs, vb);
goto do_low;
}
alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
@@ -1877,7 +2159,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n",
__func__, dpg_cd, dpg_len);
memset(npc_up, 0, sizeof(npc));
- npc_up[SG_NVME_PT_OPCODE] = 0x1d; /* MI send; same opcode as SEND DIAG */
+ npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_MI_SEND;
sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp,
npc_up + SG_NVME_PT_ADDR);
/* NVMe 4k page size. Maybe determine this? */
@@ -1889,16 +2171,17 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
sg_put_unaligned_le32(0x9, npc_up + SG_NVME_PT_CDW11);
/* data-out length I hope */
sg_put_unaligned_le32(n, npc_up + SG_NVME_PT_CDW13);
- err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, false, &npc, vb);
+ err = nvme_pt_low(ptp, ptp->dxferp, 0x1000, true, false, &npc, time_secs,
+ vb);
do_low:
if (err) {
if (err < 0) {
if (vb > 1)
- pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n",
__func__, strerror(-err), -err);
return err;
} else {
- fdc_p->nvme_status = err;
+ ptp->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
return 0;
}
@@ -1912,7 +2195,8 @@ do_low:
* SES (SCSI Enclosure Services) use of diagnostics pages that are
* related to SES. */
static int
-sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
+sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
{
bool pcv;
int err;
@@ -1926,7 +2210,7 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
pcv = !! (0x1 & cdbp[1]);
dpg_cd = cdbp[2];
alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__,
dpg_cd, (int)pcv, alloc_len);
fdc_p = get_fdc_p(ptp);
@@ -1971,7 +2255,7 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__,
dpg_cd);
memset(npc_up, 0, sizeof(npc));
- npc_up[SG_NVME_PT_OPCODE] = 0x1e; /* MI receive */
+ npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_MI_RECEIVE;
sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp,
npc_up + SG_NVME_PT_ADDR);
/* NVMe 4k page size. Maybe determine this? */
@@ -1984,15 +2268,16 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
sg_put_unaligned_le32(dpg_cd, npc_up + SG_NVME_PT_CDW12);
/* data-in length I hope */
sg_put_unaligned_le32(n, npc_up + SG_NVME_PT_CDW13);
- err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, true, &npc, vb);
+ err = nvme_pt_low(ptp, ptp->dxferp, 0x1000, true, true, &npc, time_secs,
+ vb);
if (err) {
if (err < 0) {
if (vb > 1)
- pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n",
__func__, strerror(-err), -err);
return err;
} else {
- fdc_p->nvme_status = err;
+ ptp->nvme_status = err;
mk_sense_from_nvme_status(ptp, err, vb);
return 0;
}
@@ -2008,7 +2293,7 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
static int
sntl_rep_opcodes(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
- int vb)
+ int time_secs, int vb)
{
bool rctd;
uint8_t reporting_opts, req_opcode, supp;
@@ -2020,8 +2305,8 @@ sntl_rep_opcodes(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
uint8_t *arr;
uint8_t *free_arr;
- if (vb > 3)
- pr2ws("%s: start\n", __func__);
+ if (vb > 5)
+ pr2ws("%s: time_secs=%d\n", __func__, time_secs);
rctd = !!(cdbp[2] & 0x80); /* report command timeout desc. */
reporting_opts = cdbp[2] & 0x7;
req_opcode = cdbp[3];
@@ -2122,14 +2407,15 @@ sntl_rep_opcodes(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
}
static int
-sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
+sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
{
bool repd;
uint32_t alloc_len, len;
uint8_t arr[16];
- if (vb > 3)
- pr2ws("%s: start\n", __func__);
+ if (vb > 5)
+ pr2ws("%s: time_secs=%d\n", __func__, time_secs);
memset(arr, 0, sizeof(arr));
repd = !!(cdbp[2] & 0x80);
alloc_len = sg_get_unaligned_be32(cdbp + 6);
@@ -2152,6 +2438,324 @@ sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
return 0;
}
+static int
+sntl_rread(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool is_read10 = (SCSI_READ10_OPC == cdbp[0]);
+ bool have_fua = !!(cdbp[1] & 0x8);
+ int err;
+ uint32_t nblks_t10 = 0;
+ uint64_t lba;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
+
+ if (vb > 5)
+ pr2ws("%s: fua=%d\n", __func__, (int)have_fua);
+ fdc_p = get_fdc_p(ptp);
+ memset(&npc, 0, sizeof(npc));
+ npc.cmd.opc = SG_NVME_NVM_READ;
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+ if (is_read10) {
+ lba = sg_get_unaligned_be32(cdbp + 2);
+ nblks_t10 = sg_get_unaligned_be16(cdbp + 7);
+ } else {
+ lba = sg_get_unaligned_be64(cdbp + 2);
+ nblks_t10 = sg_get_unaligned_be32(cdbp + 10);
+ if (nblks_t10 > (UINT16_MAX + 1)) {
+ mk_sense_invalid_fld(ptp, true, 11, -1, vb);
+ return 0;
+ }
+ }
+ if (0 == nblks_t10) { /* NOP in SCSI */
+ if (vb > 4)
+ pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n",
+ __func__);
+ return 0;
+ }
+ --nblks_t10; /* crazy "0's based" counts */
+ sg_put_unaligned_le64(lba, npc_up + SG_NVME_PT_CDW10); /* fills W11 too */
+ if (have_fua)
+ nblks_t10 |= SG_NVME_RW_CDW12_FUA;
+ sg_put_unaligned_le32(nblks_t10, npc_up + SG_NVME_PT_CDW12);
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+
+ err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, true, &npc,
+ time_secs, vb);
+ if (err) {
+ if (err < 0) {
+ if (vb > 1)
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n",
+ __func__, strerror(-err), -err);
+ return err;
+ } else {
+ ptp->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ ptp->resid = 0; /* hoping */
+ return 0;
+}
+
+static int
+sntl_write(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool is_write10 = (SCSI_WRITE10_OPC == cdbp[0]);
+ bool have_fua = !!(cdbp[1] & 0x8);
+ int err;
+ uint32_t nblks_t10 = 0;
+ uint64_t lba;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
+
+ if (vb > 5)
+ pr2ws("%s: fua=%d, time_secs=%d\n", __func__, (int)have_fua,
+ time_secs);
+ fdc_p = get_fdc_p(ptp);
+ memset(&npc, 0, sizeof(npc));
+ npc.cmd.opc = SG_NVME_NVM_WRITE;
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+ if (is_write10) {
+ lba = sg_get_unaligned_be32(cdbp + 2);
+ nblks_t10 = sg_get_unaligned_be16(cdbp + 7);
+ } else {
+ lba = sg_get_unaligned_be64(cdbp + 2);
+ nblks_t10 = sg_get_unaligned_be32(cdbp + 10);
+ if (nblks_t10 > (UINT16_MAX + 1)) {
+ mk_sense_invalid_fld(ptp, true, 11, -1, vb);
+ return 0;
+ }
+ }
+ if (0 == nblks_t10) { /* NOP in SCSI */
+ if (vb > 4)
+ pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n",
+ __func__);
+ return 0;
+ }
+ --nblks_t10;
+ sg_put_unaligned_le64(lba, npc_up + SG_NVME_PT_CDW10); /* fills W11 too */
+ if (have_fua)
+ nblks_t10 |= SG_NVME_RW_CDW12_FUA;
+ sg_put_unaligned_le32(nblks_t10, npc_up + SG_NVME_PT_CDW12);
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+
+ err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, false, &npc,
+ time_secs, vb);
+ if (err) {
+ if (err < 0) {
+ if (vb > 1)
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n",
+ __func__, strerror(-err), -err);
+ return err;
+ } else {
+ ptp->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ ptp->resid = 0;
+ return 0;
+}
+
+static int
+sntl_verify(struct sg_pt_freebsd_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;
+ int err;
+ uint32_t nblks_t10 = 0;
+ uint64_t lba;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
+
+ if (vb > 5)
+ pr2ws("%s: bytchk=%d, time_secs=%d\n", __func__, bytchk, time_secs);
+ if (bytchk > 1) {
+ mk_sense_invalid_fld(ptp, true, 1, 2, vb);
+ return 0;
+ }
+ fdc_p = get_fdc_p(ptp);
+ memset(&npc, 0, sizeof(npc));
+ npc.cmd.opc = bytchk ? SG_NVME_NVM_COMPARE : SG_NVME_NVM_VERIFY;
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+ if (is_verify10) {
+ lba = sg_get_unaligned_be32(cdbp + 2);
+ nblks_t10 = sg_get_unaligned_be16(cdbp + 7);
+ } else {
+ lba = sg_get_unaligned_be64(cdbp + 2);
+ nblks_t10 = sg_get_unaligned_be32(cdbp + 10);
+ if (nblks_t10 > (UINT16_MAX + 1)) {
+ mk_sense_invalid_fld(ptp, true, 11, -1, vb);
+ return 0;
+ }
+ }
+ if (0 == nblks_t10) { /* NOP in SCSI */
+ if (vb > 4)
+ pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n",
+ __func__);
+ return 0;
+ }
+ --nblks_t10;
+ sg_put_unaligned_le64(lba, npc_up + SG_NVME_PT_CDW10); /* fills W11 too */
+ sg_put_unaligned_le32(nblks_t10, npc_up + SG_NVME_PT_CDW12);
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+
+ err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, false, &npc,
+ time_secs, vb);
+ if (err) {
+ if (err < 0) {
+ if (vb > 1)
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n",
+ __func__, strerror(-err), -err);
+ return err;
+ } else {
+ ptp->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ ptp->resid = 0;
+ return 0;
+}
+
+static int
+sntl_write_same(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool is_ws10 = (SCSI_WRITE_SAME10_OPC == cdbp[0]);
+ bool ndob = is_ws10 ? false : !!(0x1 & cdbp[1]);
+ int err;
+ int nblks_t10 = 0;
+ uint64_t lba;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
+
+ if (vb > 5)
+ pr2ws("%s: ndob=%d, time_secs=%d\n", __func__, (int)ndob, time_secs);
+ if (! ndob) {
+ int flbas, index, lbafx, lbads, lbsize;
+ uint8_t * up;
+ uint8_t * dp;
+
+ dp = ptp->dxferp;
+ up = ptp->mchanp->nvme_id_ctlp;
+ if ((dp == NULL) || (up == NULL))
+ return sg_convert_errno(ENOMEM);
+ flbas = up[26]; /* NVME FLBAS field from Identify */
+ index = 128 + (4 * (flbas & 0xf));
+ lbafx = sg_get_unaligned_le32(up + index);
+ lbads = (lbafx >> 16) & 0xff; /* bits 16 to 23 inclusive, pow2 */
+ lbsize = 1 << lbads;
+ if (! sg_all_zeros(dp, lbsize)) {
+ mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, PCIE_ERR_ASC,
+ PCIE_UNSUPP_REQ_ASCQ, vb);
+ return 0;
+ }
+ /* so given single LB full of zeros, can translate .... */
+ }
+ fdc_p = ptp->mchanp;
+ memset(&npc, 0, sizeof(npc));
+ npc.cmd.opc = SG_NVME_NVM_WRITE_ZEROES;
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+ if (is_ws10) {
+ lba = sg_get_unaligned_be32(cdbp + 2);
+ nblks_t10 = sg_get_unaligned_be16(cdbp + 7);
+ } else {
+ uint32_t num = sg_get_unaligned_be32(cdbp + 10);
+
+ lba = sg_get_unaligned_be64(cdbp + 2);
+ if (num > (UINT16_MAX + 1)) {
+ mk_sense_invalid_fld(ptp, true, 11, -1, vb);
+ return 0;
+ } else
+ nblks_t10 = num;
+ }
+ if (0 == nblks_t10) { /* NOP in SCSI */
+ if (vb > 4)
+ pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n",
+ __func__);
+ return 0;
+ }
+ --nblks_t10;
+ sg_put_unaligned_le64(lba, npc_up + SG_NVME_PT_CDW10); /* fills W11 too */
+ sg_put_unaligned_le32(nblks_t10, npc_up + SG_NVME_PT_CDW12);
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+
+ err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, false, &npc,
+ time_secs, vb);
+ if (err) {
+ if (err < 0) {
+ if (vb > 1)
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n",
+ __func__, strerror(-err), -err);
+ return err;
+ } else {
+ ptp->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ ptp->resid = 0;
+ return 0;
+}
+
+static int
+sntl_sync_cache(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool immed = !!(0x2 & cdbp[1]);
+ int err;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
+
+ if (vb > 5)
+ pr2ws("%s: immed=%d, time_secs=%d\n", __func__, (int)immed,
+ time_secs);
+ fdc_p = ptp->mchanp;
+ memset(&npc, 0, sizeof(npc));
+ npc.cmd.opc = SG_NVME_NVM_FLUSH;
+ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID);
+ if (vb > 4)
+ pr2ws("%s: immed bit, lba and num_lbs fields ignored\n", __func__);
+ err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, false, &npc,
+ time_secs, vb);
+ if (err) {
+ if (err < 0) {
+ if (vb > 1)
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n",
+ __func__, strerror(-err), -err);
+ return err;
+ } else {
+ ptp->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ ptp->resid = 0;
+ return 0;
+}
+
+static int
+sntl_start_stop(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool immed = !!(0x1 & cdbp[1]);
+
+ if (vb > 5)
+ pr2ws("%s: immed=%d, time_secs=%d, ignore\n", __func__, (int)immed,
+ time_secs);
+ if (ptp) { } /* suppress warning */
+ return 0;
+}
+
/* Note that the "Returned logical block address" (RLBA) field in the SCSI
* READ CAPACITY (10+16) command's response provides the address of the _last_
* LBA (counting origin 0) which will be one less that the "size" in the
@@ -2160,7 +2764,8 @@ sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
* (implying a 1 LB logical units size) pending further research. The LBLIB
* is the "Logical Block Length In Bytes" field in the RCAP response. */
static int
-sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
+sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
{
bool is_rcap10 = (SCSI_READ_CAPACITY10_OPC == cdbp[0]);
int res, n, len, alloc_len, dps;
@@ -2174,9 +2779,9 @@ sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
struct freebsd_dev_channel * fdc_p;
uint8_t resp[32];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: RCAP%d\n", __func__, (is_rcap10 ? 10 : 16));
- fdc_p = get_fdc_p(ptp);
+ fdc_p = ptp->mchanp;
if (NULL == fdc_p) {
pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
return -EINVAL;
@@ -2186,8 +2791,8 @@ sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
return sg_convert_errno(ENOMEM);
}
- res = sntl_do_identify(fdc_p, 0x0 /* CNS */, fdc_p->nsid, pg_sz, up,
- vb);
+ res = sntl_do_identify(ptp, 0x0 /* CNS */, fdc_p->nsid, pg_sz, up,
+ time_secs, vb);
if (res < 0) {
res = sg_convert_errno(-res);
goto fini;
@@ -2237,10 +2842,13 @@ fini:
}
/* Executes NVMe Admin command (or at least forwards it to lower layers).
+ * Depending on the device, this could be NVME(via CAM) or NVME(non-CAM).
+ * is_admin will be overridden if the SNTL functions are called.
* Returns 0 for success, negative numbers are negated 'errno' values from
* OS system calls. Positive return values are errors from this package. */
static int
-sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
+sg_do_nvme_pt(struct sg_pt_freebsd_scsi * ptp, int fd, bool is_admin,
+ int time_secs, int vb)
{
bool scsi_cdb, in_xfer;
int n, err, len, io_len;
@@ -2248,25 +2856,25 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
uint8_t * dxferp;
uint8_t * npc_up;
struct freebsd_dev_channel * fdc_p;
- struct sg_pt_freebsd_scsi * ptp = &vp->impl;
const uint8_t * cdbp;
struct nvme_pt_command npc;
npc_up = (uint8_t *)&npc;
- if (vb > 3)
- pr2ws("%s: fd=%d\n", __func__, fd);
+ if (vb > 6)
+ pr2ws("%s: fd=%d, is_admin=%d\n", __func__, fd, (int)is_admin);
if (! ptp->cdb) {
if (vb)
pr2ws("%s: No NVMe command given (set_scsi_pt_cdb())\n",
__func__);
return SCSI_PT_DO_BAD_PARAMS;
}
- fdc_p = get_fdc_p(ptp);
+ fdc_p = ptp->mchanp;
if (fd < 0) {
if (NULL == fdc_p) {
pr2ws("%s: no device handle in object or fd ?\n", __func__);
return -EINVAL;
}
+ /* no fd, but have fdc_p so that is okay */
} else {
int han = fd - FREEBSD_FDOFFSET;
@@ -2289,6 +2897,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
}
}
+ ptp->is_nvme_dev = fdc_p->is_nvme_dev;
n = ptp->cdb_len;
cdbp = (const uint8_t *)ptp->cdb;
if (vb > 3)
@@ -2296,36 +2905,52 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
scsi_cdb = sg_is_scsi_cdb(cdbp, n);
/* nvme_our_sntl is false when NVMe command (64 byte) has been given */
ptp->nvme_our_sntl = scsi_cdb;
- fdc_p->nvme_our_sntl = ptp->nvme_our_sntl;
if (scsi_cdb) {
switch (cdbp[0]) {
case SCSI_INQUIRY_OPC:
- return sntl_inq(ptp, cdbp, vb);
+ return sntl_inq(ptp, cdbp, time_secs, vb);
case SCSI_REPORT_LUNS_OPC:
- return sntl_rluns(ptp, cdbp, vb);
+ return sntl_rluns(ptp, cdbp, time_secs, vb);
case SCSI_TEST_UNIT_READY_OPC:
- return sntl_tur(ptp, vb);
+ return sntl_tur(ptp, time_secs, vb);
case SCSI_REQUEST_SENSE_OPC:
- return sntl_req_sense(ptp, cdbp, vb);
+ return sntl_req_sense(ptp, cdbp, time_secs, vb);
+ case SCSI_READ10_OPC:
+ case SCSI_READ16_OPC:
+ return sntl_rread(ptp, cdbp, time_secs, vb);
+ case SCSI_WRITE10_OPC:
+ case SCSI_WRITE16_OPC:
+ return sntl_write(ptp, cdbp, time_secs, vb);
+ case SCSI_START_STOP_OPC:
+ return sntl_start_stop(ptp, cdbp, time_secs, vb);
case SCSI_SEND_DIAGNOSTIC_OPC:
- return sntl_senddiag(ptp, cdbp, vb);
+ return sntl_senddiag(ptp, cdbp, time_secs, vb);
case SCSI_RECEIVE_DIAGNOSTIC_OPC:
- return sntl_recvdiag(ptp, cdbp, vb);
+ return sntl_recvdiag(ptp, cdbp, time_secs, vb);
case SCSI_MODE_SENSE10_OPC:
case SCSI_MODE_SELECT10_OPC:
- return sntl_mode_ss(ptp, cdbp, vb);
+ return sntl_mode_ss(ptp, cdbp, time_secs, vb);
case SCSI_READ_CAPACITY10_OPC:
- return sntl_readcap(ptp, cdbp, vb);
+ 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_WRITE_SAME10_OPC:
+ case SCSI_WRITE_SAME16_OPC:
+ return sntl_write_same(ptp, cdbp, time_secs, vb);
+ case SCSI_SYNC_CACHE10_OPC:
+ case SCSI_SYNC_CACHE16_OPC:
+ return sntl_sync_cache(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, vb);
+ return sntl_readcap(ptp, cdbp, time_secs, vb);
goto fini;
case SCSI_MAINT_IN_OPC:
- sa = 0x1f & cdbp[1]; /* service action */
+ sa = SCSI_SA_MSK & cdbp[1]; /* service action */
if (SCSI_REP_SUP_OPCS_OPC == sa)
- return sntl_rep_opcodes(ptp, cdbp, vb);
+ return sntl_rep_opcodes(ptp, cdbp, time_secs, vb);
else if (SCSI_REP_SUP_TMFS_OPC == sa)
- return sntl_rep_tmfs(ptp, cdbp, vb);
+ return sntl_rep_tmfs(ptp, cdbp, time_secs, vb);
/* fall through */
default:
fini:
@@ -2341,7 +2966,11 @@ fini:
return 0;
}
}
+
/* NVMe command given to pass-through */
+ if (vb > 4)
+ pr2ws("%s: NVMe pass-through command, admin=%d\n", __func__,
+ is_admin);
len = (int)sizeof(npc.cmd);
n = (n < len) ? n : len;
if (n < 64) {
@@ -2371,17 +3000,18 @@ fini:
sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferop,
npc_up + SG_NVME_PT_ADDR);
}
- err = nvme_pt_low(fdc_p, dxferp, io_len, in_xfer, &npc, vb);
+ err = nvme_pt_low(ptp, dxferp, io_len, is_admin, in_xfer, &npc, time_secs,
+ vb);
if (err < 0) {
if (vb > 1)
- pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n",
__func__, strerror(-err), -err);
return err;
}
sct_sc = err; /* ((SCT << 8) | SC) which may be 0 */
- fdc_p->nvme_status = sct_sc;
+ ptp->nvme_status = sct_sc;
if (ptp->sense && (ptp->sense_len > 0)) {
- uint32_t k = sizeof(fdc_p->cq_dw0_3);
+ uint32_t k = sizeof(ptp->cq_dw0_3);
if ((int)k < ptp->sense_len)
ptp->sense_resid = ptp->sense_len - (int)k;
@@ -2389,57 +3019,45 @@ fini:
k = ptp->sense_len;
ptp->sense_resid = 0;
}
- memcpy(ptp->sense, fdc_p->cq_dw0_3, k);
+ memcpy(ptp->sense, ptp->cq_dw0_3, k);
}
if (in_xfer)
ptp->resid = 0; /* Just hoping ... */
return sct_sc ? SG_LIB_NVME_STATUS : 0;
}
-#else /* if not(HAVE_NVME && (! IGNORE_NVME)) */
-
-static int
-sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
-{
- if (vb) {
- pr2ws("%s: not supported, ", __func__);
-#ifdef HAVE_NVME
- pr2ws("HAVE_NVME, ");
-#else
- pr2ws("don't HAVE_NVME, ");
-#endif
-
-#ifdef IGNORE_NVME
- pr2ws("IGNORE_NVME");
-#else
- pr2ws("don't IGNORE_NVME");
-#endif
- pr2ws("\n");
- if (NULL == vp)
- pr2ws("%s: object pointer NULL; fd=%d\n", __func__, fd);
- }
- return -ENOTTY; /* inappropriate ioctl error */
-}
-
#endif /* (HAVE_NVME && (! IGNORE_NVME)) */
+
#if (HAVE_NVME && (! IGNORE_NVME))
+/* Requires pass-through file to be open and associated with vp */
int
do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb)
{
- if (vb)
- pr2ws("%s: not supported, ", __func__);
- if (vp) { }
- if (submq) { }
- if (timeout_secs) { }
- return SCSI_PT_DO_NOT_SUPPORTED;
+ struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+ struct freebsd_dev_channel *fdc_p;
+
+ if (vb && (submq != 0))
+ pr2ws("%s: warning, uses submit queue 0\n", __func__);
+ fdc_p = ptp->mchanp;
+ if (NULL == fdc_p) {
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ if (vb > 2)
+ pr2ws("%s: no open file associated with pt object\n",
+ __func__);
+ return -EINVAL;
+ }
+ ptp->mchanp = fdc_p;
+ }
+ return sg_do_nvme_pt(ptp, -1, false, timeout_secs, vb);
}
#else /* (HAVE_NVME && (! IGNORE_NVME)) */
int
-do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose)
+do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb)
{
if (vb) {
pr2ws("%s: not supported, ", __func__);
diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c
index 6c49096b..9da8bab6 100644
--- a/lib/sg_pt_linux.c
+++ b/lib/sg_pt_linux.c
@@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
-/* sg_pt_linux version 1.51 20210102 */
+/* sg_pt_linux version 1.52 20210423 */
#include <stdio.h>
diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c
index 74453f74..094dc49c 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.15 20210102 */
+/* sg_pt_linux_nvme version 1.17 20210501 */
/* This file contains a small "SPC-only" SNTL to support the SES pass-through
* of SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS through NVME-MI
@@ -142,6 +142,7 @@
#define SG_NVME_AD_GET_FEATURE 0xa
#define SG_NVME_AD_SET_FEATURE 0x9
#define SG_NVME_AD_IDENTIFY 0x6 /* similar to SCSI INQUIRY */
+#define SG_NVME_AD_DEV_SELT_TEST 0x14
#define SG_NVME_AD_MI_RECEIVE 0x1e /* MI: Management Interface */
#define SG_NVME_AD_MI_SEND 0x1d /* hmmm, same opcode as SEND DIAG */
@@ -153,7 +154,7 @@
#define SG_NVME_NVM_WRITE 0x1
#define SG_NVME_NVM_WRITE_ZEROES 0x8 /* SCSI WRITE SAME */
-#define SG_NVME_NVM_CDW12_FUA (1 << 30) /* Force Unit Access bit */
+#define SG_NVME_RW_CDW12_FUA (1 << 30) /* Force Unit Access bit */
#if (HAVE_NVME && (! IGNORE_NVME))
@@ -318,7 +319,7 @@ sg_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
if (vb > 2) {
pr2ws("NVMe Admin command: %s\n", nam);
hex2stderr((const uint8_t *)cmdp, cmd_len, 1);
- if ((vb > 3) && (! is_read) && dp) {
+ if ((vb > 4) && (! is_read) && dp) {
uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN);
if (len > 0) {
@@ -375,7 +376,7 @@ sg_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
}
return SG_LIB_NVME_STATUS; /* == SCSI_PT_DO_NVME_STATUS */
}
- if ((vb > 3) && is_read && dp) {
+ if ((vb > 4) && is_read && dp) {
uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN);
if (len > 0) {
@@ -402,7 +403,7 @@ sntl_check_enclosure_override(struct sg_pt_linux_scsi * ptp, int vb)
if (NULL == up)
return;
nvmsr = up[253];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: enter, nvmsr=%u\n", __func__, nvmsr);
ptp->dev_stat.id_ctl253 = nvmsr;
switch (ptp->dev_stat.enclosure_override) {
@@ -528,7 +529,7 @@ sntl_inq(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
uint8_t * free_nvme_id_ns = NULL;
uint8_t inq_dout[256];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
if (0x2 & cdbp[1]) { /* Reject CmdDt=1 */
@@ -677,7 +678,7 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
uint8_t * rl_doutp;
uint8_t * up;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
sel_report = cdbp[2];
@@ -740,7 +741,7 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
int res;
uint32_t pow_state;
- if (vb > 4)
+ if (vb > 5)
pr2ws("%s: start\n", __func__);
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identify(ptp, time_secs, vb);
@@ -760,7 +761,7 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
return res;
}
pow_state = (0x1f & ptp->nvme_result);
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
#if 0 /* pow_state bounces around too much on laptop */
if (pow_state)
@@ -779,7 +780,7 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
uint32_t pow_state, alloc_len, n;
uint8_t rs_dout[64];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identify(ptp, time_secs, vb);
@@ -802,7 +803,7 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
}
ptp->io_hdr.response_len = 0;
pow_state = (0x1f & ptp->nvme_result);
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
memset(rs_dout, 0, sizeof(rs_dout));
if (pow_state)
@@ -832,7 +833,7 @@ sntl_mode_ss(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
uint8_t * bp;
struct sg_sntl_result_t sntl_result;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: mode se%s\n", __func__, (is_msense ? "nse" : "lect"));
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identify(ptp, time_secs, vb);
@@ -942,12 +943,12 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
st_cd = 0x7 & (cdbp[1] >> 5);
self_test = !! (0x4 & cdbp[1]);
pf = !! (0x10 & cdbp[1]);
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf,
(int)self_test, (int)st_cd);
if (self_test || st_cd) {
memset(cmd_up, 0, sizeof(cmd));
- cmd_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */
+ cmd_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_DEV_SELT_TEST;
/* just this namespace (if there is one) and controller */
sg_put_unaligned_le32(ptp->nvme_nsid, cmd_up + SG_NVME_PT_NSID);
switch (st_cd) {
@@ -1057,7 +1058,7 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
pcv = !! (0x1 & cdbp[1]);
dpg_cd = cdbp[2];
alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__,
dpg_cd, (int)pcv, alloc_len);
din_len = ptp->io_hdr.din_xfer_len;
@@ -1114,7 +1115,7 @@ sntl_rep_opcodes(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
uint8_t *arr;
uint8_t *free_arr;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
rctd = !!(cdbp[2] & 0x80); /* report command timeout desc. */
reporting_opts = cdbp[2] & 0x7;
@@ -1223,7 +1224,7 @@ sntl_rep_tmfs(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
uint32_t alloc_len, len;
uint8_t arr[16];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
memset(arr, 0, sizeof(arr));
repd = !!(cdbp[2] & 0x80);
@@ -1269,7 +1270,7 @@ sntl_readcap(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
uint8_t * free_up = NULL;
uint8_t resp[32];
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: RCAP%d, time_secs=%d\n", __func__,
(is_rcap10 ? 10 : 16), time_secs);
up = sg_memalign(pg_sz, pg_sz, &free_up, false);
@@ -1348,7 +1349,7 @@ do_nvm_pt_low(struct sg_pt_linux_scsi * ptp,
if (vb > 2) {
pr2ws("NVMe NVM command: %s\n", nam);
hex2stderr((const uint8_t *)cmdp, cmd_len, 1);
- if ((vb > 3) && (! is_read) && dp) {
+ if ((vb > 4) && (! is_read) && dp) {
if (dlen > 0) {
n = dlen;
if ((dlen < 512) || (vb > 5))
@@ -1403,7 +1404,7 @@ do_nvm_pt_low(struct sg_pt_linux_scsi * ptp,
}
return SG_LIB_NVME_STATUS; /* == SCSI_PT_DO_NVME_STATUS */
}
- if ((vb > 3) && is_read && dp) {
+ if ((vb > 4) && is_read && dp) {
if (dlen > 0) {
n = dlen;
if ((dlen < 1024) || (vb > 5))
@@ -1445,17 +1446,17 @@ sntl_do_nvm_cmd(struct sg_pt_linux_scsi * ptp, struct sg_nvme_user_io * iop,
}
static int
-sntl_read(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
- int time_secs, int vb)
+sntl_rread(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
{
bool is_read10 = (SCSI_READ10_OPC == cdbp[0]);
bool have_fua = !!(cdbp[1] & 0x8);
int res;
- int nblks_t10 = 0;
+ uint32_t nblks_t10 = 0;
struct sg_nvme_user_io io;
struct sg_nvme_user_io * iop = &io;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: fua=%d, time_secs=%d\n", __func__, (int)have_fua,
time_secs);
memset(iop, 0, sizeof(*iop));
@@ -1464,14 +1465,12 @@ sntl_read(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
iop->slba = sg_get_unaligned_be32(cdbp + 2);
nblks_t10 = 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 + 1)) {
+ nblks_t10 = sg_get_unaligned_be32(cdbp + 10);
+ if (nblks_t10 > (UINT16_MAX + 1)) {
mk_sense_invalid_fld(ptp, true, 11, -1, vb);
return 0;
- } else
- nblks_t10 = num;
+ }
}
if (0 == nblks_t10) { /* NOP in SCSI */
if (vb > 4)
@@ -1481,7 +1480,7 @@ sntl_read(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
}
iop->nblocks = nblks_t10 - 1; /* crazy "0's based" */
if (have_fua)
- iop->nblocks |= SG_NVME_NVM_CDW12_FUA;
+ iop->nblocks |= SG_NVME_RW_CDW12_FUA;
iop->addr = (uint64_t)ptp->io_hdr.din_xferp;
res = sntl_do_nvm_cmd(ptp, iop, ptp->io_hdr.din_xfer_len,
true /* is_read */, time_secs, vb);
@@ -1499,11 +1498,11 @@ sntl_write(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
bool is_write10 = (SCSI_WRITE10_OPC == cdbp[0]);
bool have_fua = !!(cdbp[1] & 0x8);
int res;
- int nblks_t10 = 0;
+ uint32_t nblks_t10 = 0;
struct sg_nvme_user_io io;
struct sg_nvme_user_io * iop = &io;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: fua=%d, time_secs=%d\n", __func__, (int)have_fua,
time_secs);
memset(iop, 0, sizeof(*iop));
@@ -1512,14 +1511,12 @@ sntl_write(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
iop->slba = sg_get_unaligned_be32(cdbp + 2);
nblks_t10 = 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 + 1)) {
+ nblks_t10 = sg_get_unaligned_be32(cdbp + 10);
+ if (nblks_t10 > (UINT16_MAX + 1)) {
mk_sense_invalid_fld(ptp, true, 11, -1, vb);
return 0;
- } else
- nblks_t10 = num;
+ }
}
if (0 == nblks_t10) { /* NOP in SCSI */
if (vb > 4)
@@ -1529,7 +1526,7 @@ sntl_write(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
}
iop->nblocks = nblks_t10 - 1;
if (have_fua)
- iop->nblocks |= SG_NVME_NVM_CDW12_FUA;
+ iop->nblocks |= SG_NVME_RW_CDW12_FUA;
iop->addr = (uint64_t)ptp->io_hdr.dout_xferp;
res = sntl_do_nvm_cmd(ptp, iop, ptp->io_hdr.dout_xfer_len, false,
time_secs, vb);
@@ -1548,30 +1545,28 @@ sntl_verify(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
uint8_t bytchk = (cdbp[1] >> 1) & 0x3;
uint32_t dlen = 0;
int res;
- int nblks_t10 = 0;
+ uint32_t nblks_t10 = 0;
struct sg_nvme_user_io io;
struct sg_nvme_user_io * iop = &io;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: bytchk=%d, time_secs=%d\n", __func__, bytchk, time_secs);
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;
+ iop->opcode = bytchk ? SG_NVME_NVM_COMPARE : SG_NVME_NVM_VERIFY;
if (is_verify10) {
iop->slba = sg_get_unaligned_be32(cdbp + 2);
nblks_t10 = 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 + 1)) {
+ nblks_t10 = sg_get_unaligned_be32(cdbp + 10);
+ if (nblks_t10 > (UINT16_MAX + 1)) {
mk_sense_invalid_fld(ptp, true, 11, -1, vb);
return 0;
- } else
- nblks_t10 = num;
+ }
}
if (0 == nblks_t10) { /* NOP in SCSI */
if (vb > 4)
@@ -1603,7 +1598,7 @@ sntl_write_same(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
struct sg_nvme_user_io io;
struct sg_nvme_user_io * iop = &io;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: ndob=%d, time_secs=%d\n", __func__, (int)ndob, time_secs);
if (! ndob) {
int flbas, index, lbafx, lbads, lbsize;
@@ -1673,7 +1668,7 @@ sntl_sync_cache(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
struct sg_nvme_user_io * iop = &io;
int res;
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: immed=%d, time_secs=%d\n", __func__, (int)immed,
time_secs);
memset(iop, 0, sizeof(*iop));
@@ -1694,7 +1689,7 @@ sntl_start_stop(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
{
bool immed = !!(0x1 & cdbp[1]);
- if (vb > 3)
+ if (vb > 5)
pr2ws("%s: immed=%d, time_secs=%d, ignore\n", __func__, (int)immed,
time_secs);
if (ptp) { } /* suppress warning */
@@ -1739,7 +1734,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
}
n = ptp->io_hdr.request_len;
cdbp = (const uint8_t *)(sg_uintptr_t)ptp->io_hdr.request;
- if (vb > 3)
+ if (vb > 4)
pr2ws("%s: opcode=0x%x, fd=%d (dev_fd=%d), time_secs=%d\n", __func__,
cdbp[0], fd, hold_dev_fd, time_secs);
scsi_cdb = sg_is_scsi_cdb(cdbp, n);
@@ -1757,7 +1752,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
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);
+ return sntl_rread(ptp, cdbp, time_secs, vb);
case SCSI_WRITE10_OPC:
case SCSI_WRITE16_OPC:
return sntl_write(ptp, cdbp, time_secs, vb);
@@ -1908,7 +1903,7 @@ do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb)
#else /* (HAVE_NVME && (! IGNORE_NVME)) */
int
-do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose)
+do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb)
{
if (vb) {
pr2ws("%s: not supported, ", __func__);
@@ -1923,6 +1918,7 @@ do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose)
#else
pr2ws("don't IGNORE_NVME");
#endif
+ }
if (vp) { }
if (submq) { }
if (timeout_secs) { }
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 8526c2d4..0d3e9642 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -84,7 +84,7 @@ fi
%{_libdir}/*.la
%changelog
-* Wed Apr 14 2021 - dgilbert at interlog dot com
+* Sat May 01 2021 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.47
diff --git a/src/sg_inq.c b/src/sg_inq.c
index 002f1536..7977cf66 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -51,7 +51,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * version_str = "2.10 20210328"; /* spc6r05 */
+static const char * version_str = "2.11 20210430"; /* spc6r05 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
@@ -4399,9 +4399,9 @@ main(int argc, char * argv[])
#if (HAVE_NVME && (! IGNORE_NVME))
n = check_pt_file_handle(sg_fd, op->device_name, op->verbose);
if (op->verbose > 1)
- pr2serr("check_pt_file_handle()-->%d, page_given=%d\n", n,
- op->page_given);
- if ((3 == n) || (4 == n)) { /* NVMe char or NVMe block */
+ pr2serr("check_pt_file_handle()-->%d, page_given: %s\n", n,
+ (op->page_given ? "yes" : "no"));
+ if (n > 2) { /* NVMe char or NVMe block */
op->possible_nvme = true;
if (! op->page_given) {
ret = do_nvme_identify_ctrl(sg_fd, op);
diff --git a/src/sg_raw.c b/src/sg_raw.c
index 4e94a4b1..c831a172 100644
--- a/src/sg_raw.c
+++ b/src/sg_raw.c
@@ -39,7 +39,7 @@
#include "sg_pr2serr.h"
#include "sg_unaligned.h"
-#define SG_RAW_VERSION "0.4.34 (2021-01-03)"
+#define SG_RAW_VERSION "0.4.36 (2021-04-29)"
#define DEFAULT_TIMEOUT 20
#define MIN_SCSI_CDBSZ 6
@@ -80,7 +80,7 @@ struct opts_t {
bool do_dataout;
bool do_enumerate;
bool no_sense;
- bool nvm; /* the NVMe command set containing its READ+WRITE */
+ bool do_nvm; /* the NVMe command set: NVM containing its READ+WRITE */
bool do_help;
bool verbose_given;
bool version_given;
@@ -133,17 +133,18 @@ usage()
"DEVICE but\n"
" ignores it\n"
" --help|-h Show this message and exit\n"
- " --infile=IFILE|-i IFILE Read data to send from IFILE "
- "(default:\n"
- " stdin)\n"
+ " --infile=IFILE|-i IFILE Read binary data to send (i.e. "
+ "data-out)\n"
+ " from IFILE (default: stdin)\n"
" --nosense|-n Don't display sense information\n"
" --nvm|-N command is for NVM command set (e.g. "
"Read);\n"
" default, if NVMe fd, Admin command "
"set\n"
- " --outfile=OFILE|-o OFILE Write binary data to OFILE (def: "
- "hexdump\n"
- " to stdout)\n"
+ " --outfile=OFILE|-o OFILE Write binary data from device "
+ "(i.e. data-in)\n"
+ " to OFILE (def: hexdump to "
+ "stdout)\n"
" --raw|-w interpret CF (command file) as "
"binary (def:\n"
" interpret as ASCII hex)\n"
@@ -225,7 +226,7 @@ parse_cmd_line(struct opts_t * op, int argc, char *argv[])
op->no_sense = true;
break;
case 'N':
- op->nvm = true;
+ op->do_nvm = true;
break;
case 'o':
if (op->datain_file) {
@@ -300,7 +301,7 @@ parse_cmd_line(struct opts_t * op, int argc, char *argv[])
}
if (optind >= argc) {
- pr2serr("No device specified\n\n");
+ pr2serr("No device specified\n");
return SG_LIB_SYNTAX_ERROR;
}
op->device_name = argv[optind];
@@ -379,7 +380,7 @@ parse_cmd_line(struct opts_t * op, int argc, char *argv[])
printf("Attempt to decode cdb name: %s\n", b);
} else
printf(">>> Seems to be NVMe %s command\n",
- sg_get_nvme_opcode_name(op->cdb[0], true /* admin */,
+ sg_get_nvme_opcode_name(op->cdb[0], ! op->do_nvm,
sizeof(b), b));
}
return 0;
@@ -577,6 +578,7 @@ main(int argc, char *argv[])
}
if (ret != 0) {
+ pr2serr("\n"); /* blank line before outputting usage */
usage();
goto done;
} else if (op->do_help) {
@@ -679,7 +681,7 @@ and_again:
hex2stderr(op->cdb, op->cdb_length, -1);
if (op->verbose > 1)
pr2serr(" Command name: %s\n",
- sg_get_nvme_opcode_name(op->cdb[0], true /* admin */,
+ sg_get_nvme_opcode_name(op->cdb[0], ! op->do_nvm,
b_len, b));
}
}
@@ -689,7 +691,7 @@ and_again:
(int)sizeof(sense_buffer));
set_scsi_pt_sense(ptvp, sense_buffer, sizeof(sense_buffer));
- if (op->nvm)
+ if (op->do_nvm)
ret = do_nvm_pt(ptvp, 0, op->timeout, op->verbose);
else
ret = do_scsi_pt(ptvp, -1, op->timeout, op->verbose);
@@ -732,7 +734,7 @@ and_again:
k = -ret;
pr2serr("do_scsi_pt: %s\n", safe_strerror(k));
err = get_scsi_pt_os_err(ptvp);
- if (err != k)
+ if ((err != 0) && (err != k))
pr2serr(" ... or perhaps: %s\n", safe_strerror(err));
ret = sg_convert_errno(err);
goto done;
diff --git a/suse/sg3_utils.spec b/suse/sg3_utils.spec
index 2606139f..f1b15075 100644
--- a/suse/sg3_utils.spec
+++ b/suse/sg3_utils.spec
@@ -29,9 +29,9 @@ Release: 0
Summary: A collection of tools that send SCSI commands to devices
License: GPL-2.0+ and BSD-3-Clause
Group: Hardware/Other
-Url: http://sg.danny.cz/sg/sg3_utils.html
+Url: https://sg.danny.cz/sg/sg3_utils.html
-Source: http://sg.danny.cz/sg/p/%name-%{version}.tar.xz
+Source: https://sg.danny.cz/sg/p/%name-%{version}.tar.xz
BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRequires: xz
Requires(pre): %insserv_prereq
diff --git a/testing/sg_mrq_dd.cpp b/testing/sg_mrq_dd.cpp
index 49f027ae..4a486d8b 100644
--- a/testing/sg_mrq_dd.cpp
+++ b/testing/sg_mrq_dd.cpp
@@ -30,7 +30,7 @@
*
*/
-static const char * version_str = "1.26 20210402";
+static const char * version_str = "1.27 20210424";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -133,6 +133,8 @@ using namespace std;
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
#define DEF_BLOCKS_PER_2048TRANSFER 32
+#define DEF_SDT_ICT_MS 300
+#define DEF_SDT_CRT_SEC 3
#define DEF_SCSI_CDB_SZ 10
#define MAX_SCSI_CDB_SZ 16 /* could be 32 */
#define PACK_ID_TID_MULTIPLIER (0x1000000) /* 16,777,216 */
@@ -151,7 +153,6 @@ using namespace std;
#define DEF_NUM_THREADS 4
#define MAX_NUM_THREADS 1024 /* was SG_MAX_QUEUE with v3 driver */
#define DEF_MRQ_NUM 16
-#define DEF_STALL_THRESH 4
#ifndef RAW_MAJOR
#define RAW_MAJOR 255 /*unlikely value */
@@ -191,7 +192,7 @@ struct flags_t {
bool no_dur;
bool nocreat;
bool no_waitq; /* dummy, no longer supported, just warn */
- bool order;
+ bool order_wr;
bool qhead;
bool qtail;
bool random;
@@ -255,6 +256,8 @@ struct global_collection /* one instance visible to all threads */
atomic<int> sum_of_resids;
atomic<int> reason_res;
atomic<int> most_recent_pack_id;
+ uint32_t sdt_ict; /* stall detection; initial check time (milliseconds) */
+ uint32_t sdt_crt; /* check repetition time (seconds), after first stall */
int verbose;
int dry_run;
bool mrq_eq_0; /* true when user gives mrq=0 */
@@ -861,11 +864,11 @@ usage(int pg_num)
"[dio=0|1]\n"
" [elemsz_kb=EKB] [ese=0|1] [fua=0|1|2|3] "
"[hipri=NRQS]\n"
- " [mrq=NRQS] [ofreg=OFREG] [sync=0|1] "
- "[thr=THR]\n"
- " [time=0|1|2[,TO]] [verbose=VERB] [--dry-run] "
- "[--pre-fetch]\n"
- " [--verbose] [--version]\n\n"
+ " [mrq=NRQS] [ofreg=OFREG] [sdt=SDT] "
+ "[sync=0|1]\n"
+ " [thr=THR] [time=0|1|2[,TO]] [verbose=VERB] "
+ "[--dry-run]\n"
+ " [--pre-fetch] [--verbose] [--version]\n\n"
" where: operands have the form name=value and are pecular to "
"'dd'\n"
" style commands, and options start with one or "
@@ -937,6 +940,12 @@ page2:
" ofreg OFREG is regular file or pipe to send what is "
"read from\n"
" IFILE in the first half of each shared element\n"
+ " sdt stall detection times: CRT[,ICT]. CRT: check "
+ "repetition\n"
+ " time (after first) in seconds; ICT: initial "
+ "check time\n"
+ " in milliseconds. Default: 3,300 . Use CRT=0 "
+ "to disable\n"
" sync 0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
"after copy\n"
" thr is number of threads, must be > 0, default 4, "
@@ -1141,40 +1150,38 @@ static void
sig_listen_thread(struct global_collection * clp)
{
bool stall_reported = false;
- int stall_count = 0;
int prev_pack_id = 0;
int sig_number, pack_id;
+ uint32_t ict_ms = (clp->sdt_ict ? clp->sdt_ict : DEF_SDT_ICT_MS);
struct timespec ts;
struct timespec * tsp = &ts;
- tsp->tv_sec = 0;
- tsp->tv_nsec = 300 * 1000 * 1000; /* 300 ms */
+ tsp->tv_sec = ict_ms / 1000;
+ tsp->tv_nsec = (ict_ms % 1000) * 1000 * 1000; /* DEF_SDT_ICT_MS */
while (1) {
sig_number = sigtimedwait(&signal_set, NULL, tsp);
if (shutting_down)
break;
if (sig_number < 0) {
- int err = errno;
-
- if (EAGAIN == err) { /* timeout */
- pack_id = clp->most_recent_pack_id.load();
- if (pack_id == prev_pack_id) {
- ++stall_count;
- if (0 == (stall_count % DEF_STALL_THRESH)) {
- if (! stall_reported) {
- stall_reported = true;
- pr2serr_lk("%s: stall at pack_id=%d "
- "detected\n", __func__, pack_id);
- }
- pr2serr_lk("%s: stall at pack_id=%d, dump sg/debug\n",
+ int err = errno;
+
+ /* EAGAIN implies a timeout */
+ if ((EAGAIN == err) && (clp->sdt_crt > 0)) {
+ pack_id = clp->most_recent_pack_id.load();
+ if ((pack_id > 0) && (pack_id == prev_pack_id)) {
+ if (! stall_reported) {
+ stall_reported = true;
+ tsp->tv_sec = clp->sdt_crt;
+ tsp->tv_nsec = 0;
+ pr2serr_lk("%s: first stall at pack_id=%d detected\n",
__func__, pack_id);
- system_wrapper("/usr/bin/cat /proc/scsi/sg/debug\n");
- }
- } else {
- stall_count = 0;
+ } else
+ pr2serr_lk("%s: subsequent stall at pack_id=%d\n",
+ __func__, pack_id);
+ system_wrapper("/usr/bin/cat /proc/scsi/sg/debug\n");
+ } else
prev_pack_id = pack_id;
- }
- } else
+ } else if (EAGAIN != err)
pr2serr_lk("%s: sigtimedwait() errno=%d\n", __func__, err);
}
if (SIGINT == sig_number) {
@@ -1182,7 +1189,7 @@ sig_listen_thread(struct global_collection * clp)
clp->next_count_pos.store(-1);
shutting_down.store(true);
}
- }
+ } /* end of while loop */
if (clp->verbose > 1)
pr2serr_lk("%s: exiting\n", __func__);
}
@@ -1809,7 +1816,7 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
sg_info_str(a_v4p->info, sizeof(b), b));
}
sstatus = a_v4p->device_status;
- if ((sstatus && (SAM_STAT_CONDITION_MET != sstatus)) ||
+ if ((! sg_scsi_status_is_good(sstatus)) ||
a_v4p->transport_status || a_v4p->driver_status) {
ok = false;
last_err_on_in = ! (a_v4p->flags & SGV4_FLAG_DO_ON_OTHER);
@@ -2729,7 +2736,7 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
ctl_v4.flags = SGV4_FLAG_MULTIPLE_REQS | SGV4_FLAG_SHARE;
if (! (iflagsp->coe || oflagsp->coe))
ctl_v4.flags |= SGV4_FLAG_STOP_IF;
- if ((! clp->verify) && clp->out_flags.order)
+ if ((! clp->verify) && clp->out_flags.order_wr)
ctl_v4.flags |= SGV4_FLAG_ORDERED_WR;
if (clp->mrq_hipri)
ctl_v4.flags |= SGV4_FLAG_HIPRI;
@@ -3117,9 +3124,9 @@ process_flags(const char * arg, struct flags_t * fp)
else if (0 == strcmp(cp, "null"))
;
else if (0 == strcmp(cp, "ordered"))
- fp->order = true;
+ fp->order_wr = true;
else if (0 == strcmp(cp, "order"))
- fp->order = true;
+ fp->order_wr = true;
else if (0 == strcmp(cp, "qhead"))
fp->qhead = true;
else if (0 == strcmp(cp, "qtail"))
@@ -3462,6 +3469,23 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
pr2serr("%sbad argument to 'oflag='\n", my_name);
goto syn_err;
}
+ } else if (0 == strcmp(key, "sdt")) {
+ ccp = strchr(buf, ',');
+ n = sg_get_num(buf);
+ if (n < 0) {
+ pr2serr("%sbad argument to 'sdt=CRT[,ICT]'\n", my_name);
+ goto syn_err;
+ }
+ clp->sdt_crt = n;
+ if (ccp) {
+ n = sg_get_num(ccp + 1);
+ if (n < 0) {
+ pr2serr("%sbad 2nd argument to 'sdt=CRT,ICT'\n",
+ my_name);
+ goto syn_err;
+ }
+ clp->sdt_ict = n;
+ }
} else if (0 == strcmp(key, "seek")) {
n = strlen(buf);
if (n < 1) {
@@ -3635,7 +3659,7 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
* SG_IO ioctl. So reduce it in that case. */
if ((clp->bs >= 2048) && (! bpt_given))
clp->bpt = DEF_BLOCKS_PER_2048TRANSFER;
- if (clp->in_flags.order && (! clp->out_flags.order))
+ if (clp->in_flags.order_wr && (! clp->out_flags.order_wr))
pr2serr("Warning iflag=order is ignored, use with oflag=\n");
if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) {
pr2serr("too few or too many threads requested\n");
@@ -3906,6 +3930,8 @@ main(int argc, char * argv[])
clp->dd_count = SG_COUNT_INDEFINITE;
clp->bpt = DEF_BLOCKS_PER_TRANSFER;
clp->cmd_timeout = DEF_TIMEOUT;
+ clp->sdt_ict = DEF_SDT_ICT_MS;
+ clp->sdt_crt = DEF_SDT_CRT_SEC;
clp->in_type = FT_FIFO;
/* change dd's default: if of=OFILE not given, assume /dev/null */
clp->out_type = FT_DEV_NULL;
@@ -4076,7 +4102,10 @@ main(int argc, char * argv[])
if (clp->in_flags.serial || clp->out_flags.serial)
pr2serr("serial flag ignored when both IFILE and OFILE are sg "
"devices\n");
- } else if (clp->in_flags.order)
+ if (clp->in_flags.order_wr && (num_threads > 1))
+ pr2serr("Warning: write ordering only guaranteed for single "
+ "thread\n");
+ } else if (clp->in_flags.order_wr)
pr2serr("Warning: oflag=order only active on sg->sg copies\n");
if (outregf[0]) {
diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp
index 7fb4ae06..af226958 100644
--- a/testing/sgh_dd.cpp
+++ b/testing/sgh_dd.cpp
@@ -36,7 +36,7 @@
* renamed [20181221]
*/
-static const char * version_str = "2.04 20210402";
+static const char * version_str = "2.06 20210427";
#define _XOPEN_SOURCE 600
#ifndef _GNU_SOURCE
@@ -130,6 +130,8 @@ using namespace std;
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
#define DEF_BLOCKS_PER_2048TRANSFER 32
+#define DEF_SDT_ICT_MS 300
+#define DEF_SDT_CRT_SEC 3
#define DEF_SCSI_CDBSZ 10
#define MAX_SCSI_CDBSZ 16
@@ -244,7 +246,8 @@ struct global_collection
int ofsplit;
atomic<int> dio_incomplete_count;
atomic<int> sum_of_resids;
- int vebose;
+ uint32_t sdt_ict; /* stall detection; initial check time (milliseconds) */
+ uint32_t sdt_crt; /* check repetition time (seconds), after first stall */
int fail_mask;
int verbose;
int dry_run;
@@ -914,12 +917,13 @@ usage(int pg_num)
"[fua=0|1|2|3]\n"
" [mrq=[I|O,]NRQS[,C]] [noshare=0|1] "
"[of2=OFILE2]\n"
- " [ofreg=OFREG] [ofsplit=OSP] [sync=0|1] "
- "[thr=THR]\n"
- " [time=0|1|2[,TO]] [unshare=1|0] [verbose=VERB] "
- "[--dry-run]\n"
- " [--prefetch] [-v|-vv|-vvv] [--verbose] [--verify] "
- "[--version]\n\n"
+ " [ofreg=OFREG] [ofsplit=OSP] [sdt=SDT] "
+ "[sync=0|1]\n"
+ " [thr=THR] [time=0|1|2[,TO]] [unshare=1|0] "
+ "[verbose=VERB]\n"
+ " [--dry-run] [--prefetch] [-v|-vv|-vvv] "
+ "[--verbose]\n"
+ " [--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"
@@ -995,6 +999,12 @@ page2:
" IFILE in the first half of each shared element\n"
" ofsplit split ofile write in two at block OSP (def: 0 "
"(no split))\n"
+ " sdt stall detection times: CRT[,ICT]. CRT: check "
+ "repetition\n"
+ " time (after first) in seconds; ICT: initial "
+ "check time\n"
+ " in milliseconds. Default: 3,300 . Use CRT=0 "
+ "to disable\n"
" sync 0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
"after copy\n"
" thr is number of threads, must be > 0, default 4, "
@@ -1188,22 +1198,103 @@ read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
#endif
}
+static int
+system_wrapper(const char * cmd)
+{
+ int res;
+
+ res = system(cmd);
+ if (WIFSIGNALED(res) &&
+ (WTERMSIG(res) == SIGINT || WTERMSIG(res) == SIGQUIT))
+ raise(WTERMSIG(res));
+ return WEXITSTATUS(res);
+}
+
+/* Has an infinite loop doing a timed wait for any signals in sig_set. After
+ * each timeout (300 ms) checks if the most_recent_pack_id atomic integer
+ * has changed. If not after another two timeouts announces a stall has
+ * been detected. If shutting down atomic is true breaks out of loop and
+ * shuts down this thread. Other than that, this thread is normally cancelled
+ * by the main thread, after other threads have exited. */
static void *
sig_listen_thread(void * v_clp)
{
+ bool stall_reported = false;
+ int prev_pack_id = 0;
+ int sig_number, pack_id;
+ struct timespec ts;
+ struct timespec * tsp = &ts;
struct global_collection * clp = (struct global_collection *)v_clp;
- int sig_number;
+ uint32_t ict_ms = (clp->sdt_ict ? clp->sdt_ict : DEF_SDT_ICT_MS);
+ tsp->tv_sec = ict_ms / 1000;
+ tsp->tv_nsec = (ict_ms % 1000) * 1000 * 1000; /* DEF_SDT_ICT_MS */
while (1) {
- sigwait(&signal_set, &sig_number);
+ sig_number = sigtimedwait(&signal_set, NULL, tsp);
if (shutting_down)
break;
+ if (sig_number < 0) {
+ int err = errno;
+
+ /* EAGAIN implies a timeout */
+ if ((EAGAIN == err) && (clp->sdt_crt > 0)) {
+ pack_id = mono_pack_id.load();
+ if ((pack_id > 0) && (pack_id == prev_pack_id)) {
+ if (! stall_reported) {
+ stall_reported = true;
+ tsp->tv_sec = clp->sdt_crt;
+ tsp->tv_nsec = 0;
+ pr2serr_lk("%s: first stall at pack_id=%d detected\n",
+ __func__, pack_id);
+ } else
+ pr2serr_lk("%s: subsequent stall at pack_id=%d\n",
+ __func__, pack_id);
+ system_wrapper("/usr/bin/cat /proc/scsi/sg/debug\n");
+ } else
+ prev_pack_id = pack_id;
+ } else if (EAGAIN != err)
+ pr2serr_lk("%s: sigtimedwait() errno=%d\n", __func__, err);
+ }
+ if (SIGINT == sig_number) {
+ pr2serr_lk("%sinterrupted by SIGINT\n", my_name);
+ stop_both(clp);
+ pthread_cond_broadcast(&clp->out_sync_cv);
+ }
+ } /* end of while loop */
+ if (clp->verbose > 1)
+ pr2serr_lk("%s: exiting\n", __func__);
+
+
+#if 0
+
+
+ if (EAGAIN == err) { /* timeout */
+ pack_id = mono_pack_id.load();
+ if (pack_id == prev_pack_id) {
+ if (! stall_reported) {
+ stall_reported = true;
+ tsp->tv_sec = 2;
+ tsp->tv_nsec = 0;
+ pr2serr_lk("%s: first stall at pack_id=%d "
+ "detected\n", __func__, pack_id);
+ } else
+ pr2serr_lk("%s: subsequent stall at pack_id=%d\n",
+ __func__, pack_id);
+ system_wrapper("/usr/bin/cat /proc/scsi/sg/debug\n");
+ } else
+ prev_pack_id = pack_id;
+ } else
+ pr2serr_lk("%s: sigtimedwait() errno=%d\n", __func__, err);
+ }
if (SIGINT == sig_number) {
pr2serr_lk("%sinterrupted by SIGINT\n", my_name);
stop_both(clp);
pthread_cond_broadcast(&clp->out_sync_cv);
}
}
+ if (clp->verbose > 1)
+ pr2serr_lk("%s: exiting\n", __func__);
+#endif
return NULL;
}
@@ -2321,7 +2412,7 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
sg_info_str(a_v4p->info, sizeof(b), b));
}
sstatus = a_v4p->device_status;
- if ((sstatus && (SAM_STAT_CONDITION_MET != sstatus)) ||
+ if ((! sg_scsi_status_is_good(sstatus)) ||
a_v4p->transport_status || a_v4p->driver_status) {
ok = false;
if (SAM_STAT_CHECK_CONDITION != a_v4p->device_status) {
@@ -2441,7 +2532,7 @@ chk_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
sg_info_str(a_np->info, sizeof(b), b));
}
sstatus = a_np->device_status;
- if ((sstatus && (SAM_STAT_CONDITION_MET != sstatus)) ||
+ if ((! sg_scsi_status_is_good(sstatus)) ||
a_np->transport_status || a_np->driver_status) {
ok = false;
if (SAM_STAT_CHECK_CONDITION != a_np->device_status) {
@@ -4045,6 +4136,23 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
pr2serr("%sbad argument to 'oflag='\n", my_name);
return SG_LIB_SYNTAX_ERROR;
}
+ } else if (0 == strcmp(key, "sdt")) {
+ cp = strchr(buf, ',');
+ n = sg_get_num(buf);
+ if (n < 0) {
+ pr2serr("%sbad argument to 'sdt=CRT[,ICT]'\n", my_name);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ clp->sdt_crt = n;
+ if (cp) {
+ n = sg_get_num(cp + 1);
+ if (n < 0) {
+ pr2serr("%sbad 2nd argument to 'sdt=CRT,ICT'\n",
+ my_name);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ clp->sdt_ict = n;
+ }
} else if (0 == strcmp(key, "seek")) {
clp->seek = sg_get_llnum(buf);
if (-1LL == clp->seek) {
@@ -4129,7 +4237,7 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
version_given = true;
else {
pr2serr("Unrecognized option '%s'\n", key);
- pr2serr("For more information use '--help'\n");
+ pr2serr("For more information use '--help' or '-h'\n");
return SG_LIB_SYNTAX_ERROR;
}
}
@@ -4292,6 +4400,8 @@ main(int argc, char * argv[])
clp->out2_type = FT_DEV_NULL;
clp->cdbsz_in = DEF_SCSI_CDBSZ;
clp->cdbsz_out = DEF_SCSI_CDBSZ;
+ clp->sdt_ict = DEF_SDT_ICT_MS;
+ clp->sdt_crt = DEF_SDT_CRT_SEC;
clp->nmrqs = DEF_NUM_MRQS;
clp->unshare = true;
inf[0] = '\0';
@@ -4599,7 +4709,7 @@ main(int argc, char * argv[])
if ((STDIN_FILENO == clp->infd) && (STDOUT_FILENO == clp->outfd)) {
pr2serr("Won't default both IFILE to stdin _and_ OFILE to "
"/dev/null\n");
- pr2serr("For more information use '--help'\n");
+ pr2serr("For more information use '--help' or '-h'\n");
return SG_LIB_SYNTAX_ERROR;
}
if (dd_count < 0) {