aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2017-12-29 18:23:19 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2017-12-29 18:23:19 +0000
commitb29ef85867d5347e8fed4c603986964868cba801 (patch)
tree6c39b1855f97dc416307229654713d9aed4c8d54 /lib
parent766b77d70276eef4a6ae92217d1ffbcde819a88b (diff)
downloadsg3_utils-b29ef85867d5347e8fed4c603986964868cba801.tar.gz
sg_ses: further NVMe support work; decode array status dpage (obsolete); build: add SG_LIB_ANDROID
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@738 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'lib')
-rw-r--r--lib/sg_lib.c189
-rw-r--r--lib/sg_lib_data.c177
-rw-r--r--lib/sg_pt_freebsd.c955
-rw-r--r--lib/sg_pt_linux.c13
-rw-r--r--lib/sg_pt_linux_nvme.c328
5 files changed, 1142 insertions, 520 deletions
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index 003f1547..5055c414 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -353,6 +353,42 @@ sg_get_sense_info_fld(const unsigned char * sbp, int sb_len,
}
}
+/* Returns true if fixed format or command specific information descriptor
+ * is found in the descriptor sense; else false. If available the command
+ * specific information field (4 byte integer in fixed format, 8 byte
+ * integer in descriptor format) is written out via 'cmd_spec_outp'.
+ * Handles both fixed and descriptor sense formats. */
+bool
+sg_get_sense_cmd_spec_fld(const unsigned char * sbp, int sb_len,
+ uint64_t * cmd_spec_outp)
+{
+ const unsigned char * bp;
+
+ if (cmd_spec_outp)
+ *cmd_spec_outp = 0;
+ if (sb_len < 7)
+ return false;
+ switch (sbp[0] & 0x7f) {
+ case 0x70:
+ case 0x71:
+ if (cmd_spec_outp)
+ *cmd_spec_outp = sg_get_unaligned_be32(sbp + 8);
+ return true;
+ case 0x72:
+ case 0x73:
+ bp = sg_scsi_sense_desc_find(sbp, sb_len,
+ 1 /* command specific info desc */);
+ if (bp && (0xa == bp[1])) {
+ if (cmd_spec_outp)
+ *cmd_spec_outp = sg_get_unaligned_be64(bp + 4);
+ return true;
+ } else
+ return false;
+ default:
+ return false;
+ }
+}
+
/* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
* In descriptor format if the stream commands descriptor not found
* then returns false. Writes true or false corresponding to these bits to
@@ -2342,6 +2378,153 @@ sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len, char * buff,
return buff;
}
+/* This is a heuristic that takes into account the command bytes and length
+ * to decide whether the presented unstructured sequence of bytes could be
+ * a SCSI command. If so it returns true otherwise false. Vendor specific
+ * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
+ * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
+ * only SCSI commands considered above 16 bytes of length are the Variable
+ * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e).
+ * Both have an inbuilt length field which can be cross checked with clen.
+ * No NVMe commands (64 bytes long plus some extra added by some OSes) have
+ * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
+ * structures that are sent across the wire. The FIS register structure is
+ * used to move a command from a SATA host to device, but the ATA 'command'
+ * is not the first byte. So it is harder to say what will happen if a
+ * FIS structure is presented as a SCSI command, hopfully there is a low
+ * probability this function will yield true in that case. */
+bool
+sg_is_scsi_cdb(const uint8_t * cdbp, int clen)
+{
+ int ilen, sa;
+ uint8_t opcode;
+ uint8_t top3bits;
+
+ if (clen < 6)
+ return false;
+ opcode = cdbp[0];
+ top3bits = opcode >> 5;
+ if (0x3 == top3bits) {
+ if ((clen < 12) || (clen % 4))
+ return false; /* must be modulo 4 and 12 or more bytes */
+ switch (opcode) {
+ case 0x7e: /* Extended cdb (XCDB) */
+ ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
+ return (ilen == clen);
+ case 0x7f: /* Variable Length cdb */
+ ilen = 8 + cdbp[7];
+ sa = sg_get_unaligned_be16(cdbp + 8);
+ /* service action (sa) 0x0 is reserved */
+ return ((ilen == clen) && sa);
+ default:
+ return false;
+ }
+ } else if (clen <= 16) {
+ switch (clen) {
+ case 6:
+ if (top3bits > 0x5) /* vendor */
+ return true;
+ return (0x0 == top3bits); /* 6 byte cdb */
+ case 10:
+ if (top3bits > 0x5) /* vendor */
+ return true;
+ return ((0x1 == top3bits) || (0x2 == top3bits)); /* 10 byte cdb */
+ case 16:
+ if (top3bits > 0x5) /* vendor */
+ return true;
+ return (0x4 == top3bits); /* 16 byte cdb */
+ case 12:
+ if (top3bits > 0x5) /* vendor */
+ return true;
+ return (0x5 == top3bits); /* 12 byte cdb */
+ default:
+ return false;
+ }
+ }
+ /* NVMe probably falls out here, clen > 16 and (opcode < 0x60 or
+ * opcode > 0x7f). */
+ return false;
+}
+
+/* Yield string associated with NVMe command status value in sct_sc. It
+ * expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25
+ * are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC).
+ * Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found
+ * a string of the form "Reserved [0x<sct_sc_in_hex>]" is generated.
+ * Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/
+char *
+sg_get_nvme_cmd_status_str(uint16_t sct_sc, int b_len, char * b)
+{
+ int k;
+ uint16_t s = 0x3ff & sct_sc;
+ const struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr;
+
+ if ((b_len <= 0) || (NULL == b))
+ return b;
+ else if (1 == b_len) {
+ b[0] = '\0';
+ return b;
+ }
+ for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) {
+ if (s == (uint16_t)vp->value) {
+ strncpy(b, vp->name, b_len);
+ b[b_len - 1] = '\0';
+ return b;
+ }
+ }
+ if (k >= 1000)
+ pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n",
+ __func__);
+ snprintf(b, b_len, "Reserved [0x%x]", sct_sc);
+ return b;
+}
+
+/* Attempts to map NVMe status value (SCT and SC) to SCSI status, sense_key,
+ * asc and ascq tuple. If successful returns true and writes to non-NULL
+ * pointer arguments; otherwise returns false. */
+bool
+sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p,
+ uint8_t * asc_p, uint8_t * ascq_p)
+{
+ int k, ind;
+ uint16_t s = 0x3ff & sct_sc;
+ struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr;
+ struct sg_lib_4tuple_u8 * mp = sg_lib_scsi_status_sense_arr;
+
+ for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) {
+ if (s == (uint16_t)vp->value)
+ break;
+ }
+ if (k >= 1000) {
+ pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n",
+ __func__);
+ return false;
+ }
+ if (NULL == vp->name)
+ return false;
+ ind = vp->peri_dev_type;
+
+
+ for (k = 0; (0xff != mp->t2) && k < 1000; ++k, ++mp)
+ ; /* count entries for valid index range */
+ if (k >= 1000) {
+ pr2ws("%s: where is sentinel for sg_lib_scsi_status_sense_arr ??\n",
+ __func__);
+ return false;
+ } else if (ind >= k)
+ return false;
+ mp = sg_lib_scsi_status_sense_arr + ind;
+ if (status_p)
+ *status_p = mp->t1;
+ if (sk_p)
+ *sk_p = mp->t2;
+ if (asc_p)
+ *asc_p = mp->t3;
+ if (ascq_p)
+ *ascq_p = mp->t4;
+ return true;
+}
+
/* safe_strerror() contributed by Clayton Weaver <cgweav at email dot com>
* Allows for situation in which strerror() is given a wild value (or the
* C library is incomplete) and returns NULL. Still not thread safe.
@@ -2586,7 +2769,8 @@ sg_is_big_endian()
the most significant byte */
}
-bool sg_all_zeros(const uint8_t * bp, int b_len)
+bool
+sg_all_zeros(const uint8_t * bp, int b_len)
{
if ((NULL == bp) || (b_len <= 0))
return false;
@@ -2597,7 +2781,8 @@ bool sg_all_zeros(const uint8_t * bp, int b_len)
return true;
}
-bool sg_all_ffs(const uint8_t * bp, int b_len)
+bool
+sg_all_ffs(const uint8_t * bp, int b_len)
{
if ((NULL == bp) || (b_len <= 0))
return false;
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index 76964d8c..b6362a50 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -17,7 +17,7 @@
#endif
-const char * sg_lib_version_str = "2.34 20171219";/* spc5r17, sbc4r15 */
+const char * sg_lib_version_str = "2.35 20171227";/* spc5r17, sbc4r15 */
/* indexed by pdt; those that map to own index do not decay */
@@ -1506,3 +1506,178 @@ struct sg_lib_value_name_t sg_lib_scsi_feature_sets[] =
{SCSI_FS_SBC_DRIVE_MAINT_2016, PDT_DISK, "Drive maintenance 2016"},
{0x0, 0, NULL}, /* 0x0 is reserved sfs; trailing sentinel */
};
+
+/* .value is completion queue's DW3 as follows: ((DW3 >> 17) & 0x3ff)
+ * .peri_dev_type is an index for the sg_lib_scsi_status_sense_arr[]
+ * .name is taken from NVMe 1.3a document, section 4.6.1.2.1 with less
+ * capitalization.
+ * NVMe term bits 31:17 of DW3 in the completion field as the "Status
+ * Field" (SF). Bit 31 is "Do not retry" (DNR) and bit 30 is "More" (M).
+ * Bits 29:28 are reserved, bit 27:25 are the "Status Code Type" (SCT)
+ * and bits 24:17 are the Status Code (SC). This table is in ascending
+ * order of its .value field so a binary search could be done on it. */
+#ifdef SG_SCSI_STRINGS
+struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] =
+{
+ /* Generic command status values, Status Code Type (SCT): 0h
+ * Lowest 8 bits are the Status Code (SC), in this case:
+ * 00h - 7Fh: Applicable to Admin Command Set, or across multiple
+ * command sets
+ * 80h - BFh: I/O Command Set Specific status codes
+ * c0h - FFh: I/O Vendor Specific status codes */
+ {0x0, 0, "Successful completion"},
+ {0x1, 1, "Invalid command opcode"},
+ {0x2, 2, "Invalid field in command"},
+ {0x3, 2, "Command id conflict"},
+ {0x4, 3, "Data transfer error"},
+ {0x5, 4, "Command aborted due to power loss notication"},
+ {0x6, 5, "Internal error"},
+ {0x7, 6, "Command abort requested"},
+ {0x8, 6, "Command aborted due to SQ deletion"},
+ {0x9, 6, "Command aborted due to failed fused command"},
+ {0xa, 6, "Command aborted due to missing fused command"},
+ {0xb, 7, "Invalid namespace or format"},
+ {0xc, 5, "Command sequence error"},
+ {0xd, 5, "Invalid SGL segment descriptor"},
+ {0xe, 5, "Invalid number of SGL descriptors"},
+ {0xf, 5, "Data SGL length invalid"},
+ {0x10, 5, "Matadata SGL length invalid"},
+ {0x11, 5, "SGL descriptor type invalid"},
+ {0x12, 5, "Invalid use of controller memory buffer"},
+ {0x13, 5, "PRP offset invalid"},
+ {0x14, 2, "Atomic write unit exceeded"},
+ {0x15, 8, "Operation denied"},
+ {0x16, 5, "SGL offset invalid"},
+ {0x17, 5, "Reserved [0x17]"},
+ {0x18, 5, "Host identifier inconsistent format"},
+ {0x19, 5, "Keep alive timeout expired"},
+ {0x1a, 5, "Keep alive timeout invalid"},
+ {0x1b, 6, "Command aborted due to Preempt and Abort"},
+ {0x1c, 10, "Sanitize failed"},
+ {0x1d, 11, "Sanitize in progress"},
+ {0x1e, 5, "SGL data block granularity invalid"},
+ {0x1f, 5, "Command not supported for queue in CMB"},
+
+ /* Generic command status values, NVM (I/O) Command Set */
+ {0x80, 12, "LBA out of range"},
+ {0x81, 3, "Capacity exceeded"},
+ {0x82, 13, "Namespace not ready"},
+ {0x83, 14, "Reservation conflict"},
+ {0x84, 15, "Format in progress"},
+ /* 0xc0 - 0xff: vendor specific */
+
+ /* Command specific status values, Status Code Type (SCT): 1h */
+ {0x100, 5, "Completion queue invalid"},
+ {0x101, 5, "Invalid queue identifier"},
+ {0x102, 5, "Invalid queue size"},
+ {0x103, 5, "Abort command limit exceeded"},
+ {0x104, 5, "Reserved [0x104]"},
+ {0x105, 5, "Asynchronous event request limit exceeded"},
+ {0x106, 5, "Invalid firmware slot"},
+ {0x107, 5, "Invalid firmware image"},
+ {0x108, 5, "Invalid interrupt vector"},
+ {0x109, 5, "Invalid log page"},
+ {0x10a,16, "Invalid format"},
+ {0x10b, 5, "Firmware activation requires conventional reset"},
+ {0x10c, 5, "Invalid queue deletion"},
+ {0x10d, 5, "Feature identifier not saveable"},
+ {0x10e, 5, "Feature not changeable"},
+ {0x10f, 5, "Feature not namespace specific"},
+ {0x110, 5, "Firmware activation requires NVM subsystem reset"},
+ {0x111, 5, "Firmware activation requires reset"},
+ {0x112, 5, "Firmware activation requires maximum time violation"},
+ {0x113, 5, "Firmware activation prohibited"},
+ {0x114, 5, "Overlapping range"},
+ {0x115, 5, "Namespace insufficient capacity"},
+ {0x116, 5, "Namespace identifier unavailable"},
+ {0x117, 5, "Reserved [0x107]"},
+ {0x118, 5, "Namespace already attached"},
+ {0x119, 5, "Namespace is private"},
+ {0x11a, 5, "Namespace not attached"},
+ {0x11b, 3, "Thin provisioning not supported"},
+ {0x11c, 3, "Controller list invalid"},
+ {0x11d,17, "Device self-test in progress"},
+ {0x11e,18, "Boot partition write prohibited"},
+ {0x11f, 5, "Invalid controller identifier"},
+ {0x120, 5, "Invalid secondary controller state"},
+ {0x121, 5, "Invalid number of controller resorces"},
+ {0x122, 5, "Invalid resorce identifier"},
+
+ /* Command specific status values, Status Code Type (SCT): 1h
+ * for NVM (I/O) Command Set */
+ {0x180, 2, "Conflicting attributes"},
+ {0x181,19, "Invalid protection information"},
+ {0x182,18, "Attempted write to read only range"},
+ /* 0x1c0 - 0x1ff: vendor specific */
+
+ /* Media and Data Integrity error values, Status Code Type (SCT): 2h */
+ {0x280,20, "Write fault"},
+ {0x281,21, "Unrecovered read error"},
+ {0x282,22, "End-to-end guard check error"},
+ {0x283,23, "End-to-end application tag check error"},
+ {0x284,24, "End-to-end reference tag check error"},
+ {0x285,25, "Compare failure"},
+ {0x286, 8, "Access denied"},
+ {0x287,26, "Deallocated or unwritten logical block"},
+ /* 0x2c0 - 0x2ff: vendor specific */
+
+ /* Leave this Sentinel value at end of this array */
+ {0x3ff, 0, NULL},
+};
+
+/* The sg_lib_nvme_cmd_status_arr[n].peri_dev_type field is an index
+ * to this array. It allows an NVMe status (error) value to be mapped
+ * to this SCSI tuple: status, sense_key, additional sense code (asc) and
+ * asc qualifier (ascq). For brevity SAM_STAT_CHECK_CONDITION is written
+ * as 0x2. */
+struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] =
+{
+ {SAM_STAT_GOOD, SPC_SK_NO_SENSE, 0, 0}, /* it's all good */ /* 0 */
+ {SAM_STAT_CHECK_CONDITION, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x0},/* opcode */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x24, 0x0}, /* field in cdb */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x0, 0x0},
+ {SAM_STAT_TASK_ABORTED, SPC_SK_ABORTED_COMMAND, 0xb, 0x8},
+ {0x2, SPC_SK_HARDWARE_ERROR, 0x44, 0x0}, /* internal error */ /* 5 */
+ {SAM_STAT_TASK_ABORTED, SPC_SK_ABORTED_COMMAND, 0x0, 0x0},
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x9}, /* invalid LU */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x2}, /* access denied */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x2c, 0x0}, /* cmd sequence error */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x31, 0x3}, /* sanitize failed */ /* 10 */
+ {0x2, SPC_SK_NOT_READY, 0x4, 0x1b}, /* sanitize in progress */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x21, 0x0}, /* LBA out of range */
+ {0x2, SPC_SK_NOT_READY, 0x4, 0x0}, /* not reportable; 0x1: becoming */
+ {SAM_STAT_RESERVATION_CONFLICT, 0x0, 0x0, 0x0},
+ {0x2, SPC_SK_NOT_READY, 0x4, 0x4}, /* format in progress */ /* 15 */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x31, 0x1}, /* format failed */
+ {0x2, SPC_SK_NOT_READY, 0x4, 0x9}, /* self-test in progress */
+ {0x2, SPC_SK_DATA_PROTECT, 0x27, 0x0}, /* write prohibited */
+ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x10, 0x5}, /* protection info */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x3, 0x0}, /* periph dev w fault */ /* 20 */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x11, 0x0}, /* unrecoc rd */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x1}, /* PI guard */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x2}, /* PI app tag */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x2}, /* PI app tag */
+ {0x2, SPC_SK_MISCOMPARE, 0x1d, 0x0}, /* during verify */ /* 25 */
+ {0x2, SPC_SK_MEDIUM_ERROR, 0x21, 0x6}, /* read invalid data */
+
+ /* Leave this Sentinel value at end of this array */
+ {0xff, 0xff, 0xff, 0xff},
+};
+
+
+#else /* no SG_SCSI_STRINGS define in config.sys */
+struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] =
+{
+
+ /* Leave this Sentinel value at end of this array */
+ {0x3ff, 0, NULL},
+};
+
+struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] =
+{
+
+ /* Leave this Sentinel value at end of this array */
+ {0xff, 0xff, 0xff, 0xff},
+};
+
+#endif /* SG_SCSI_STRINGS */
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index 6fa3f310..0d2e38f9 100644
--- a/lib/sg_pt_freebsd.c
+++ b/lib/sg_pt_freebsd.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_freebsd version 1.19 20171218 */
+/* sg_pt_freebsd version 1.20 20171227 */
#include <stdio.h>
#include <stdlib.h>
@@ -18,6 +18,8 @@
#include <libgen.h> /* for basename */
#include <fcntl.h>
#include <errno.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h> /* from PRIx macros */
#include <err.h>
#include <camlib.h>
#include <cam/scsi/scsi_message.h>
@@ -51,8 +53,12 @@ struct freebsd_dev_channel {
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)
char* devname; // the device name
struct cam_device* cam_dev;
+ uint8_t * nvme_id_ctlp;
+ uint8_t * free_nvme_id_ctlp;
};
// Private table of open devices: guaranteed zero on startup since
@@ -79,14 +85,17 @@ struct sg_pt_freebsd_scsi {
uint32_t mdxfer_len;
bool mdxfer_out;
bool scsi_dsense;
+ int timeout_ms;
int scsi_status;
int resid;
int sense_resid;
int in_err;
int os_err;
int transport_err;
- int dev_han; // -1 if not provided
- uint32_t nvme_result; // from completion
+ 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
};
struct sg_pt_base {
@@ -102,6 +111,10 @@ static int pr2ws(const char * fmt, ...)
static int pr2ws(const char * fmt, ...);
#endif
+static int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb);
+static struct freebsd_dev_channel *
+ get_fdc_p(struct sg_pt_freebsd_scsi * ptp);
+
static int
pr2ws(const char * fmt, ...)
@@ -120,38 +133,6 @@ static inline bool is_aligned(const void * pointer, size_t byte_count)
return (sg_uintptr_t)pointer % byte_count == 0;
}
-/* The web claims that all NVMe commands are 64 bytes long. Believe it until
- * contradicted. The only SCSI commands that can be longer than 16 bytes are
- * the Variable Length Commands (opcode 0x7f) and the XCDB wrapped commands
- * (opcode 0x7e). Both have an inbuilt length field which can be cross
- * checked with clen. */
-static bool
-is_scsi_command(const uint8_t * cdbp, int clen)
-{
- int ilen, sa;
-
- if (clen <= 16)
- return true;
- if (0 == (clen % 4)) {
- if (0x7f == cdbp[0]) {
- ilen = 8 + cdbp[7];
- sa = sg_get_unaligned_be16(cdbp + 8);
- if ((ilen == clen) && sa)
- return true;
- } else if (0x7e == cdbp[0]) {
- ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
- if (ilen == clen)
- return true;
- }
- }
- if ((clen >= 64) && (clen <= 72))
- return false;
- pr2ws("%s: irregular command, assume NVMe:\n", __func__);
- dStrHexErr((const char *)cdbp, clen, 1);
- return false;
-}
-
-
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
int
scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
@@ -174,7 +155,7 @@ scsi_pt_open_flags(const char * device_name, int oflags, int verbose)
int k, err, dev_fd, ret;
uint32_t nsid, nv_ctrlid;
ssize_t s;
- struct freebsd_dev_channel *fdchan = NULL;
+ struct freebsd_dev_channel *fdc_p = NULL;
struct cam_device* cam_dev;
struct stat a_stat;
char b[PATH_MAX];
@@ -233,24 +214,24 @@ scsi_pt_open_flags(const char * device_name, int oflags, int verbose)
break;
}
- fdchan = (struct freebsd_dev_channel *)
+ fdc_p = (struct freebsd_dev_channel *)
calloc(1,sizeof(struct freebsd_dev_channel));
- if (fdchan == NULL) {
+ if (fdc_p == NULL) {
// errno already set by call to calloc()
ret = -ENOMEM;
goto err_out;
}
- fdchan->dev_fd = -1;
- if (! (fdchan->devname = (char *)calloc(1, DEV_IDLEN+1))) {
+ fdc_p->dev_fd = -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(fdchan->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d",
+ snprintf(fdc_p->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d",
nv_ctrlid);
- dev_fd = open(fdchan->devname, oflags);
+ dev_fd = open(fdc_p->devname, oflags);
if (dev_fd < 0) {
err = errno;
if (verbose)
@@ -258,19 +239,19 @@ scsi_pt_open_flags(const char * device_name, int oflags, int verbose)
__func__, full_path, strerror(err), err);
goto scsi_ata_try;
}
- fdchan->is_nvme = true;
- fdchan->is_char = is_char;
- fdchan->nsid = (broadcast_nsid == nsid) ? 0 : nsid;
- fdchan->nv_ctrlid = nv_ctrlid;
- fdchan->dev_fd = dev_fd;
- devicetable[k] = fdchan;
+ fdc_p->is_nvme = true;
+ 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:
- fdchan->is_char = is_char;
- if (cam_get_device(device_name, fdchan->devname, DEV_IDLEN,
- &(fdchan->unitnum)) == -1) {
+ fdc_p->is_char = is_char;
+ if (cam_get_device(device_name, fdc_p->devname, DEV_IDLEN,
+ &(fdc_p->unitnum)) == -1) {
if (verbose)
pr2ws("bad device name structure\n");
errno = EINVAL;
@@ -279,27 +260,27 @@ scsi_ata_try:
}
if (verbose > 4)
pr2ws("%s: cam_get_device, f->devname: %s, f->unitnum=%d\n", __func__,
- fdchan->devname, fdchan->unitnum);
+ fdc_p->devname, fdc_p->unitnum);
- if (! (cam_dev = cam_open_spec_device(fdchan->devname,
- fdchan->unitnum, O_RDWR, NULL))) {
+ if (! (cam_dev = cam_open_spec_device(fdc_p->devname,
+ fdc_p->unitnum, O_RDWR, NULL))) {
if (verbose)
pr2ws("cam_open_spec_device: %s\n", cam_errbuf);
errno = EPERM; /* permissions or not CAM device (NVMe ?) */
ret = -errno;
goto err_out;
}
- fdchan->cam_dev = cam_dev;
+ fdc_p->cam_dev = cam_dev;
// return pointer to "file descriptor" table entry, properly offset.
- devicetable[k] = fdchan;
+ devicetable[k] = fdc_p;
return k + FREEBSD_FDOFFSET;
err_out: /* ret should be negative value (negated errno) */
- if (fdchan) {
- if (fdchan->devname)
- free(fdchan->devname);
- free(fdchan);
- fdchan = NULL;
+ if (fdc_p) {
+ if (fdc_p->devname)
+ free(fdc_p->devname);
+ free(fdc_p);
+ fdc_p = NULL;
}
return ret;
}
@@ -308,27 +289,32 @@ err_out: /* ret should be negative value (negated errno) */
int
scsi_pt_close_device(int device_han)
{
- struct freebsd_dev_channel *fdchan;
+ struct freebsd_dev_channel *fdc_p;
int han = device_han - FREEBSD_FDOFFSET;
if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
errno = ENODEV;
return -errno;
}
- fdchan = devicetable[han];
- if (NULL == fdchan) {
+ fdc_p = devicetable[han];
+ if (NULL == fdc_p) {
errno = ENODEV;
return -errno;
}
- if (fdchan->devname)
- free(fdchan->devname);
- if (fdchan->cam_dev)
- cam_close_device(fdchan->cam_dev);
- if (fdchan->is_nvme) {
- if (fdchan->dev_fd >= 0)
- close(fdchan->dev_fd);
+ if (fdc_p->devname)
+ free(fdc_p->devname);
+ if (fdc_p->cam_dev)
+ cam_close_device(fdc_p->cam_dev);
+ if (fdc_p->is_nvme) {
+ if (fdc_p->dev_fd >= 0)
+ close(fdc_p->dev_fd);
+ if (fdc_p->free_nvme_id_ctlp) {
+ free(fdc_p->free_nvme_id_ctlp);
+ fdc_p->nvme_id_ctlp = NULL;
+ fdc_p->free_nvme_id_ctlp = NULL;
+ }
}
- free(fdchan);
+ free(fdc_p);
devicetable[han] = NULL;
errno = 0;
return 0;
@@ -343,22 +329,22 @@ scsi_pt_close_device(int device_han)
int
check_pt_file_handle(int device_han, const char * device_name, int verbose)
{
- struct freebsd_dev_channel *fdchan;
+ struct freebsd_dev_channel *fdc_p;
int han = device_han - FREEBSD_FDOFFSET;
if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
errno = ENODEV;
return -errno;
}
- fdchan = devicetable[han];
- if (NULL == fdchan) {
+ fdc_p = devicetable[han];
+ if (NULL == fdc_p) {
errno = ENODEV;
return -errno;
}
- if (fdchan->is_nvme)
- return 4 - (int)fdchan->is_char;
- else if (fdchan->cam_dev)
- return 2 - (int)fdchan->is_char;
+ 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;
else {
if (device_name) { }
if (verbose) { }
@@ -380,7 +366,12 @@ construct_scsi_pt_obj_with_fd(int dev_han, int verbose)
ptp = (struct sg_pt_freebsd_scsi *)
calloc(1, sizeof(struct sg_pt_freebsd_scsi));
if (ptp) {
+ struct freebsd_dev_channel *fdc_p;
+
memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi));
+ fdc_p = get_fdc_p(ptp);
+ if (fdc_p)
+ ptp->is_nvme = fdc_p->is_nvme;
ptp->dxfer_dir = CAM_DIR_NONE;
ptp->dev_han = (dev_han < 0) ? -1 : dev_han;
} else if (verbose)
@@ -407,19 +398,43 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp)
}
}
+static struct freebsd_dev_channel *
+get_fdc_p(struct sg_pt_freebsd_scsi * ptp)
+{
+ int han = ptp->dev_han - FREEBSD_FDOFFSET;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV))
+ return NULL;
+ return devicetable[han];
+}
+
+static const struct freebsd_dev_channel *
+get_fdc_cp(const struct sg_pt_freebsd_scsi * ptp)
+{
+ int han = ptp->dev_han - FREEBSD_FDOFFSET;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV))
+ return NULL;
+ return devicetable[han];
+}
+
+
void
clear_scsi_pt_obj(struct sg_pt_base * vp)
{
+ bool is_nvme;
int dev_han;
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
if (ptp->ccb)
cam_freeccb(ptp->ccb);
+ is_nvme = ptp->is_nvme;
dev_han = ptp->dev_han;
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;
}
}
@@ -481,11 +496,11 @@ set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
if (ptp->dxferip)
++ptp->in_err;
+ ptp->dxferip = dxferp;
+ ptp->dxfer_ilen = dxfer_len;
if (dxfer_len > 0) {
ptp->dxferp = dxferp;
- ptp->dxferip = dxferp;
ptp->dxfer_len = dxfer_len;
- ptp->dxfer_ilen = dxfer_len;
ptp->dxfer_dir = CAM_DIR_IN;
}
}
@@ -499,11 +514,11 @@ set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
if (ptp->dxferop)
++ptp->in_err;
+ ptp->dxferop = (unsigned char *)dxferp;
+ ptp->dxfer_olen = dxfer_len;
if (dxfer_len > 0) {
ptp->dxferp = (unsigned char *)dxferp;
- ptp->dxferop = (unsigned char *)dxferp;
ptp->dxfer_len = dxfer_len;
- ptp->dxfer_olen = dxfer_len;
ptp->dxfer_dir = CAM_DIR_OUT;
}
}
@@ -516,11 +531,10 @@ set_pt_metadata_xfer(struct sg_pt_base * vp, unsigned char * mdxferp,
if (ptp->mdxferp)
++ptp->in_err;
- if (mdxfer_len > 0) {
- ptp->mdxferp = mdxferp;
- ptp->mdxfer_len = mdxfer_len;
+ ptp->mdxferp = mdxferp;
+ ptp->mdxfer_len = mdxfer_len;
+ if (mdxfer_len > 0)
ptp->mdxfer_out = out_true;
- }
}
void
@@ -563,16 +577,43 @@ set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
if (flags) { ; } /* unused, suppress warning */
}
+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)
+{
+ int err, status;
+ uint8_t opcode;
+ 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;
+ }
+ 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;
+ status = ((npcp->cpl.status.sct << 8) | npcp->cpl.status.sc);
+ if (status && vb)
+ pr2ws("%s: opcode=0x%x, status: %s\n", __func__, opcode,
+ sg_get_nvme_cmd_status_str(status, sizeof(b), b));
+ return status;
+}
+
/* Executes SCSI command (or at least forwards it to lower layers).
* Clears os_err field prior to active call (whose result may set it
* again). */
int
do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
{
- int n, len, timout_ms;
- int han;
+ int len;
struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- struct freebsd_dev_channel *fdchan;
+ struct freebsd_dev_channel *fdc_p;
union ccb *ccb;
ptp->os_err = 0;
@@ -598,78 +639,34 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
} else
ptp->dev_han = dev_han;
}
- han = ptp->dev_han - FREEBSD_FDOFFSET;
if (NULL == ptp->cdb) {
if (verbose)
pr2ws("No command (cdb) given\n");
return SCSI_PT_DO_BAD_PARAMS;
}
+ if (ptp->is_nvme)
+ return sg_do_nvme_pt(vp, -1, verbose);
- if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
- if (verbose)
- pr2ws("Bad file handle\n");
- ptp->os_err = ENODEV;
- return -ptp->os_err;
- }
- fdchan = devicetable[han];
- if (NULL == fdchan) {
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
if (verbose)
- pr2ws("File descriptor closed??\n");
+ pr2ws("File descriptor bad or closed??\n");
ptp->os_err = ENODEV;
return -ptp->os_err;
}
- if (fdchan->is_nvme) {
- int err;
- struct nvme_pt_command npc;
+ ptp->is_nvme = fdc_p->is_nvme;
+ if (fdc_p->is_nvme)
+ return sg_do_nvme_pt(vp, -1, verbose);
- if (fdchan->dev_fd < 0) {
- if (verbose)
- pr2ws("%s: is_nvme is true but dev_fd<0, inconsistent\n",
- __func__);
- ptp->os_err = EINVAL;
- return -ptp->os_err;
- }
- memset(&npc, 0, sizeof(npc));
- n = ptp->cdb_len;
- len = (int)sizeof(npc.cmd);
- n = (len < n) ? len : n;
- if (n < 8) {
- if (verbose)
- pr2ws("%s: cdb_len=%d too short\n", __func__, n);
- return SCSI_PT_DO_BAD_PARAMS;
- }
- memcpy(&npc.cmd, ptp->cdb, ptp->cdb_len);
- npc.buf = ptp->dxferp;
- npc.len = ptp->dxfer_len;
- npc.is_read = (CAM_DIR_IN == ptp->dxfer_dir);
- if ((0 == npc.is_read) && (CAM_DIR_OUT == ptp->dxfer_dir))
- npc.len = 0; /* don't want write by accident */
- err = ioctl(fdchan->dev_fd, NVME_PASSTHROUGH_CMD, &npc);
- if (err < 0) {
- ptp->os_err = errno;
- if (verbose > 3)
- pr2ws("%s: ioctl(NVME_PASSTHROUGH_CMD) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -ptp->os_err;
- }
- ptp->nvme_result = npc.cpl.cdw0;
- if (ptp->sense_len > 0) {
- n = (int)sizeof(npc.cpl);
- n = ptp->sense_len < n ? ptp->sense_len : n;
- memcpy(ptp->sense, &npc.cpl, n);
- }
- return 0;
- }
- if (NULL == fdchan->cam_dev) {
+ if (NULL == fdc_p->cam_dev) {
if (verbose)
pr2ws("No open CAM device\n");
return SCSI_PT_DO_BAD_PARAMS;
}
if (NULL == ptp->ccb) { /* re-use if we have one already */
- if (! (ccb = cam_getccb(fdchan->cam_dev))) {
+ if (! (ccb = cam_getccb(fdc_p->cam_dev))) {
if (verbose)
pr2ws("cam_getccb: failed\n");
ptp->os_err = ENOMEM;
@@ -683,7 +680,7 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
bzero(&(&ccb->ccb_h)[1],
sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
- timout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT;
+ ptp->timeout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT;
cam_fill_csio(&ccb->csio,
/* retries */ 1,
/* cbfcnp */ NULL,
@@ -693,14 +690,14 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
/* datalen */ ptp->dxfer_len,
/* senselen */ ptp->sense_len,
/* cdblen */ ptp->cdb_len,
- /* timeout (millisecs) */ timout_ms);
+ /* timeout (millisecs) */ ptp->timeout_ms);
memcpy(ccb->csio.cdb_io.cdb_bytes, ptp->cdb, ptp->cdb_len);
- if (cam_send_ccb(fdchan->cam_dev, ccb) < 0) {
+ if (cam_send_ccb(fdc_p->cam_dev, ccb) < 0) {
if (verbose) {
warn("error sending SCSI ccb");
#if __FreeBSD_version > 500000
- cam_error_print(fdchan->cam_dev, ccb, CAM_ESF_ALL,
+ cam_error_print(fdc_p->cam_dev, ccb, CAM_ESF_ALL,
CAM_EPF_ALL, stderr);
#endif
}
@@ -728,7 +725,7 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int verbose)
} else
ptp->transport_err = 1;
- ptp->cam_dev = fdchan->cam_dev; // for error processing
+ ptp->cam_dev = fdc_p->cam_dev; // for error processing
return 0;
}
@@ -755,7 +752,7 @@ get_scsi_pt_resid(const struct sg_pt_base * vp)
{
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
- return ptp->resid;
+ return ptp->is_nvme ? 0 : ptp->resid;
}
int
@@ -764,35 +761,35 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp)
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
- int han = ptp->dev_han - FREEBSD_FDOFFSET;
- struct freebsd_dev_channel *fdchan;
+ if (ptp->is_nvme) {
+ const struct freebsd_dev_channel *fdc_p;
- if ((han < 0) || (han >= FREEBSD_MAXDEV))
- return -1;
- fdchan = devicetable[han];
- if (NULL == fdchan)
- return -1;
- return fdchan->is_nvme ? (int)ptp->nvme_result : ptp->scsi_status;
+ fdc_p = get_fdc_cp(ptp);
+ if (NULL == fdc_p)
+ return -1;
+ return (int)fdc_p->nvme_status;
+ } else
+ return ptp->scsi_status;
}
return -1;
}
+/* For NVMe, CDW0 from completion (32 bits), for SCSI the status */
uint32_t
get_pt_result(const struct sg_pt_base * vp)
{
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp) {
- int han = ptp->dev_han - FREEBSD_FDOFFSET;
- struct freebsd_dev_channel *fdchan;
+ if (ptp->is_nvme) {
+ const struct freebsd_dev_channel *fdc_p;
- if ((han < 0) || (han >= FREEBSD_MAXDEV))
- return -1;
- fdchan = devicetable[han];
- if (NULL == fdchan)
- return -1;
- return fdchan->is_nvme ? ptp->nvme_result :
- (uint32_t)ptp->scsi_status;
+ fdc_p = get_fdc_cp(ptp);
+ if (NULL == fdc_p)
+ return -1;
+ return fdc_p->nvme_result;
+ } else
+ return (uint32_t)ptp->scsi_status;
}
return -1;
}
@@ -843,6 +840,11 @@ 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) {
+ 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,
@@ -864,19 +866,17 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp && (ptp->dev_han >= 0)) {
- int han = ptp->dev_han - FREEBSD_FDOFFSET;
- struct freebsd_dev_channel *fdchan;
+ const struct freebsd_dev_channel *fdc_p;
- if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
+ fdc_p = get_fdc_cp(ptp);
+ if (NULL == fdc_p) {
errno = ENODEV;
return false;
}
- fdchan = devicetable[han];
- if (NULL == fdchan) {
- errno = ENODEV;
- return false;
- }
- return fdchan->is_nvme;
+ /* if unequal, cast away const and drive fdc_p value into ptp */
+ if (ptp->is_nvme != fdc_p->is_nvme)
+ ((struct sg_pt_freebsd_scsi *)ptp)->is_nvme = fdc_p->is_nvme;
+ return fdc_p->is_nvme;
}
return false;
}
@@ -890,15 +890,12 @@ get_pt_nvme_nsid(const struct sg_pt_base * vp)
const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
if (ptp && (ptp->dev_han >= 0)) {
- int han = ptp->dev_han - FREEBSD_FDOFFSET;
- struct freebsd_dev_channel *fdchan;
+ const struct freebsd_dev_channel *fdc_p;
- if ((han < 0) || (han >= FREEBSD_MAXDEV))
- return 0;
- fdchan = devicetable[han];
- if (NULL == fdchan)
+ fdc_p = get_fdc_cp(ptp);
+ if (NULL == fdc_p)
return 0;
- return fdchan->nsid;
+ return fdc_p->nsid;
}
return 0;
}
@@ -953,7 +950,6 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */
#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
-
static void
build_sense_buffer(bool desc, uint8_t *buf, uint8_t skey, uint8_t asc,
uint8_t ascq)
@@ -993,8 +989,41 @@ mk_sense_asc_ascq(struct sg_pt_freebsd_scsi * ptp, int sk, int asc, int ascq,
memset(sbp, 0, n);
build_sense_buffer(dsense, sbp, sk, asc, ascq);
if (vb > 3)
- pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x%x]\n", __func__, asc,
- ascq);
+ pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__,
+ sk, asc, ascq);
+}
+
+static void
+mk_sense_from_nvme_status(struct sg_pt_freebsd_scsi * ptp, uint16_t sct_sc,
+ int vb)
+{
+ bool ok;
+ bool dsense = ptp->scsi_dsense;
+ int n;
+ uint8_t sstatus, sk, asc, ascq;
+ uint8_t * sbp = ptp->sense;
+
+ ok = sg_nvme_status2scsi(sct_sc, &sstatus, &sk, &asc, &ascq);
+ if (! ok) { /* can't find a mapping to a SCSI error, so ... */
+ sstatus = SAM_STAT_CHECK_CONDITION;
+ sk = SPC_SK_ILLEGAL_REQUEST;
+ asc = 0xb;
+ ascq = 0x0; /* asc: "WARNING" purposely vague */
+ }
+
+ ptp->scsi_status = sstatus;
+ n = ptp->sense_len;
+ if ((n < 8) || ((! dsense) && (n < 14))) {
+ pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, n);
+ return;
+ } else
+ ptp->sense_resid = ptp->sense_len -
+ (dsense ? 8 : ((n < 18) ? n : 18));
+ memset(sbp, 0, n);
+ build_sense_buffer(dsense, sbp, sk, asc, ascq);
+ if (vb > 3)
+ pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__,
+ sk, asc, ascq);
}
/* Set in_bit to -1 to indicate no bit position of invalid field */
@@ -1040,73 +1069,40 @@ mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb,
__func__, asc, in_cdb ? 'C' : 'D', in_byte, in_bit);
}
-#if 0
static int
-do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
- struct sg_nvme_passthru_cmd *cmdp, int time_secs,
- bool cp_cmd_out2resp, int vb)
-{
- const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd);
- uint32_t n;
-
- cmdp->timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (vb > 2) {
- pr2ws("NVMe command:\n");
- dStrHex((const char *)cmdp, cmd_len, 1);
- }
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp) < 0) {
- ptp->os_err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)\n",
- __func__, strerror(ptp->os_err), ptp->os_err);
- return -ptp->os_err;
- } else
- ptp->os_err = 0;
- ptp->nvme_result = cmdp->result;
- if (cp_cmd_out2resp) {
- n = ptp->io_hdr.max_response_len;
- if ((n > 0) && ptp->io_hdr.response) {
- n = (n < cmd_len) ? n : cmd_len;
- memcpy((uint8_t *)ptp->io_hdr.response, cmdp, n);
- ptp->io_hdr.response_len = n;
- } else
- ptp->io_hdr.response_len = 0;
- } else
- ptp->io_hdr.response_len = 0;
-
- if (vb > 2)
- pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmdp->timeout_ms,
- cmdp->result);
- return 0;
-}
-#endif
-
-static int
-sntl_cache_identity(struct sg_pt_freebsd_scsi * ptp, int time_secs, int vb)
+sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb)
{
int err;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
uint32_t pg_sz = sg_get_page_size();
- ptp->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp,
- vb > 3);
- if (NULL == ptp->nvme_id_ctlp) {
+ fdc_p->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz,
+ &fdc_p->free_nvme_id_ctlp, vb > 3);
+ if (NULL == fdc_p->nvme_id_ctlp) {
pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
return SG_LIB_OS_BASE_ERR + ENOMEM;
}
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x6; /* Identify */
- cmd.cdw10 = 0x1; /* CNS=0x1 Identify controller */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->nvme_id_ctlp;
- cmd.data_len = pg_sz;
- cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)"
- "\n", __func__, strerror(err), err);
- ptp->os_err = err;
- return -err;
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */
+ sg_put_unaligned_le32(0x0, npc_up + SG_NVME_PT_NSID);
+ /* CNS=0x1 Identify: controller */
+ sg_put_unaligned_le32(0x1, npc_up + SG_NVME_PT_CDW10);
+ sg_put_unaligned_le64((sg_uintptr_t)fdc_p->nvme_id_ctlp,
+ npc_up + SG_NVME_PT_ADDR);
+ sg_put_unaligned_le32(pg_sz, npc_up + SG_NVME_PT_DATA_LEN);
+ err = nvme_pt_low(fdc_p, fdc_p->nvme_id_ctlp, pg_sz, true, &npc, vb);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ return SG_LIB_NVME_STATUS;
+ }
}
return 0;
}
@@ -1115,24 +1111,32 @@ 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 time_secs,
- int vb)
+sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
bool evpd;
int res;
uint16_t k, n, alloc_len, pg_cd;
+ struct freebsd_dev_channel * fdc_p;
uint8_t inq_dout[128];
if (vb > 3)
- pr2ws("%s: time_secs=%d\n", __func__, time_secs);
+ pr2ws("%s: starting\n", __func__);
if (0x2 & cdbp[1]) {
mk_sense_invalid_fld(ptp, true, 1, 1, vb);
return 0;
}
- if (NULL == ptp->nvme_id_ctlp) {
- res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ if (NULL == fdc_p->nvme_id_ctlp) {
+ res = sntl_cache_identity(fdc_p, vb);
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ return 0;
+ } else if (res)
return res;
}
memset(inq_dout, 0, sizeof(inq_dout));
@@ -1154,7 +1158,7 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
/* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */
inq_dout[1] = pg_cd;
sg_put_unaligned_be16(20, inq_dout + 2);
- memcpy(inq_dout + 4, ptp->nvme_id_ctlp + 4, 20); /* SN */
+ memcpy(inq_dout + 4, fdc_p->nvme_id_ctlp + 4, 20); /* SN */
n = 24;
break;
case 0x83:
@@ -1166,14 +1170,15 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
* dated 20150624 confuses this with SCSI name string
* descriptor, desig_id=8 */
memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8);
- memcpy(inq_dout + 16, ptp->nvme_id_ctlp + 24, 40); /* MN */
+ memcpy(inq_dout + 16, fdc_p->nvme_id_ctlp + 24, 40); /* MN */
for (k = 40; k > 0; --k) {
if (' ' == inq_dout[16 + k - 1])
inq_dout[16 + k - 1] = '_'; /* convert trailing spaces */
else
break;
}
- memcpy(inq_dout + 16 + k + 1, ptp->nvme_id_ctlp + 4, 20); /* SN */
+ /* SN */
+ memcpy(inq_dout + 16 + k + 1, fdc_p->nvme_id_ctlp + 4, 20);
n = 16 + k + 1 + 20;
inq_dout[7] = 8 + k + 1 + 20;
sg_put_unaligned_be16(n - 4, inq_dout + 2);
@@ -1184,9 +1189,9 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
}
if (alloc_len > 0) {
n = (alloc_len < n) ? alloc_len : n;
- n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ n = (n < ptp->dxfer_len) ? n : ptp->dxfer_len;
if (n > 0)
- memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n);
+ memcpy((uint8_t *)ptp->dxferp, inq_dout, n);
}
} else { /* Standard INQUIRY response */
/* inq_dout[0] = (PQ=0)<<5 | (PDT=0); pdt=0 --> SBC; 0xd --> SES */
@@ -1196,39 +1201,46 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
inq_dout[6] = 0x40; /* ENCSERV=1 */
inq_dout[7] = 0x2; /* CMDQUE=1 */
memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8); /* NVMe not Intel */
- memcpy(inq_dout + 16, ptp->nvme_id_ctlp + 24, 16); /* Prod <-- MN */
- memcpy(inq_dout + 32, ptp->nvme_id_ctlp + 64, 4); /* Rev <-- FR */
+ memcpy(inq_dout + 16, fdc_p->nvme_id_ctlp + 24, 16);/* Prod <-- MN */
+ memcpy(inq_dout + 32, fdc_p->nvme_id_ctlp + 64, 4); /* Rev <-- FR */
if (alloc_len > 0) {
n = (alloc_len < inq_resp_len) ? alloc_len : inq_resp_len;
- n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ n = (n < ptp->dxfer_len) ? n : ptp->dxfer_len;
if (n > 0)
- memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n);
+ memcpy((uint8_t *)ptp->dxferp, inq_dout, n);
}
}
return 0;
}
static int
-sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
- int vb)
+sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
int res;
uint16_t sel_report;
uint32_t alloc_len, k, n, num, max_nsid;
+ struct freebsd_dev_channel * fdc_p;
uint8_t * rl_doutp;
uint8_t * up;
if (vb > 3)
- pr2ws("%s: time_secs=%d\n", __func__, time_secs);
-
+ pr2ws("%s: starting\n", __func__);
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
sel_report = cdbp[2];
alloc_len = sg_get_unaligned_be32(cdbp + 6);
- if (NULL == ptp->nvme_id_ctlp) {
- res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (NULL == fdc_p->nvme_id_ctlp) {
+ res = sntl_cache_identity(fdc_p, vb);
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ return 0;
+ } else if (res)
return res;
}
- max_nsid = sg_get_unaligned_le32(ptp->nvme_id_ctlp + 516);
+ max_nsid = sg_get_unaligned_le32(fdc_p->nvme_id_ctlp + 516);
switch (sel_report) {
case 0:
case 2:
@@ -1240,7 +1252,7 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
num = 0;
break;
case 0x11:
- num = (1 == ptp->nvme_nsid) ? max_nsid : 0;
+ num = (1 == fdc_p->nsid) ? max_nsid : 0;
break;
default:
if (vb > 1)
@@ -1261,10 +1273,10 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
n+= 8;
if (alloc_len > 0) {
n = (alloc_len < n) ? alloc_len : n;
- n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ n = (n < (uint32_t)ptp->dxfer_len) ? n : (uint32_t)ptp->dxfer_len;
if (n > 0) {
- memcpy((uint8_t *)ptp->io_hdr.din_xferp, rl_doutp, n);
- ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n;
+ memcpy((uint8_t *)ptp->dxferp, rl_doutp, n);
+ ptp->resid = ptp->dxfer_len - (int)n;
}
}
res = 0;
@@ -1273,33 +1285,49 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
}
static int
-sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
+sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb)
{
int res, err;
uint32_t pow_state;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
if (vb > 3)
- pr2ws("%s: time_secs=%d\n", __func__, time_secs);
- if (NULL == ptp->nvme_id_ctlp) {
- res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ pr2ws("%s: starting\n", __func__);
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ if (NULL == fdc_p->nvme_id_ctlp) {
+ res = sntl_cache_identity(fdc_p, vb);
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ return 0;
+ } else if (res)
return res;
}
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0xa; /* Get feature */
- cmd.nsid = SG_NVME_BROADCAST_NSID;
- cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
- cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_ADMIN(Get feature)) failed: %s (errno=%d)"
- "\n", __func__, strerror(err), err);
- ptp->os_err = err;
- return -err;
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0xa; /* 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);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
}
- pow_state = (0x1f & cmd.result);
+ pow_state = (0x1f & fdc_p->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
#if 0 /* pow_state bounces around too much on laptop */
@@ -1311,38 +1339,53 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
}
static int
-sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
- int time_secs, int vb)
+sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
bool desc;
int res, err;
uint32_t pow_state, alloc_len, n;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
uint8_t rs_dout[64];
if (vb > 3)
- pr2ws("%s: time_secs=%d\n", __func__, time_secs);
- if (NULL == ptp->nvme_id_ctlp) {
- res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ pr2ws("%s: starting\n", __func__);
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ if (NULL == fdc_p->nvme_id_ctlp) {
+ res = sntl_cache_identity(fdc_p, vb);
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb);
+ return 0;
+ } else if (res)
return res;
}
desc = !!(0x1 & cdbp[1]);
alloc_len = cdbp[4];
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0xa; /* Get feature */
- cmd.nsid = SG_NVME_BROADCAST_NSID;
- cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
- cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_ADMIN(Get feature)) failed: %s (errno=%d)"
- "\n", __func__, strerror(err), err);
- ptp->os_err = err;
- return -err;
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0xa; /* 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);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__,
+ strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
}
- pow_state = (0x1f & cmd.result);
+ pow_state = (0x1f & fdc_p->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
memset(rs_dout, 0, sizeof(rs_dout));
@@ -1354,10 +1397,10 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
NO_ADDITIONAL_SENSE, 0);
n = desc ? 8 : 18;
n = (n < alloc_len) ? n : alloc_len;
- n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ n = (n < (uint32_t)ptp->dxfer_len) ? n : (uint32_t)ptp->dxfer_len;
if (n > 0) {
- memcpy((uint8_t *)ptp->io_hdr.din_xferp, rs_dout, n);
- ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n;
+ memcpy((uint8_t *)ptp->dxferp, rs_dout, n);
+ ptp->resid = ptp->dxfer_len - (int)n;
}
return 0;
}
@@ -1368,26 +1411,57 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
* (SCSI Enclosure Services) use of diagnostics pages that are
* related to SES. */
static int
-sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
- int time_secs, int vb)
+sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
bool pf, self_test;
+ int err;
uint8_t st_cd, dpg_cd;
- uint32_t alloc_len, n, dout_len, dpg_len;
+ uint32_t alloc_len, n, dout_len, dpg_len, nvme_dst;
uint32_t pg_sz = sg_get_page_size();
const uint8_t * dop;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
st_cd = 0x7 & (cdbp[1] >> 5);
pf = !! (0x4 & cdbp[1]);
self_test = !! (0x10 & cdbp[1]);
if (vb > 3)
- pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf,
+ pr2ws("%s: pf=%d, self_test=%d, st_code=%d\n", __func__, (int)pf,
(int)self_test, (int)st_cd);
- if (self_test)
- return 0; /* NVMe has no self-test, just say OK */
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ if (self_test || st_cd) {
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-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) {
+ case 0: /* Here if self_test is set, do short self-test */
+ case 1: /* Background short */
+ case 5: /* Foreground short */
+ nvme_dst = 1;
+ break;
+ case 2: /* Background extended */
+ case 6: /* Foreground extended */
+ nvme_dst = 2;
+ break;
+ case 4: /* Abort self-test */
+ nvme_dst = 0xf;
+ break;
+ default:
+ pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ sg_put_unaligned_le32(nvme_dst, npc_up + SG_NVME_PT_CDW10);
+ err = nvme_pt_low(fdc_p, NULL, 0x0, false, &npc, vb);
+ goto do_low;
+ }
alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
- dout_len = ptp->io_hdr.dout_xfer_len;
+ dout_len = ptp->dxfer_len;
if (pf) {
if (0 == alloc_len) {
mk_sense_invalid_fld(ptp, true, 3, 7, vb);
@@ -1417,11 +1491,11 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
}
n = dout_len;
n = (n < alloc_len) ? n : alloc_len;
- dop = (const uint8_t *)ptp->io_hdr.dout_xferp;
+ dop = (const uint8_t *)ptp->dxferp;
if (! is_aligned(dop, pg_sz)) { /* caller best use sg_memalign(,pg_sz) */
if (vb)
pr2ws("%s: dout [0x%" PRIx64 "] not page aligned\n", __func__,
- (uint64_t)ptp->io_hdr.dout_xferp);
+ (uint64_t)ptp->dxferp);
return SCSI_PT_DO_BAD_PARAMS;
}
dpg_cd = dop[0];
@@ -1432,15 +1506,35 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
if (vb)
pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n",
__func__, dpg_cd, dpg_len);
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x1d; /* MI send; hmmm same opcode as SEND DIAG */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
- cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
- /* dout_len > 0x1000, is this a problem?? */
- cmd.cdw10 = 0x0804; /* NVMe Message Header */
- cmd.cdw11 = 0x9; /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */
- cmd.cdw13 = n;
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, false, vb);
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0x1d; /* MI send; same opcode as SEND DIAG */
+ sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp,
+ npc_up + SG_NVME_PT_ADDR);
+ /* NVMe 4k page size. Maybe determine this? */
+ /* dout_len > 0x1000, is this a problem?? */
+ sg_put_unaligned_le32(0x1000, npc_up + SG_NVME_PT_DATA_LEN);
+ /* NVMe Message Header */
+ sg_put_unaligned_le32(0x0804, npc_up + SG_NVME_PT_CDW10);
+ /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */
+ 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);
+do_low:
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ __func__, strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ return 0;
}
/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1)
@@ -1449,16 +1543,17 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
* SES (SCSI Enclosure Services) use of diagnostics pages that are
* related to SES. */
static int
-sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
- int time_secs, int vb)
+sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb)
{
bool pcv;
- int res;
+ int err;
uint8_t dpg_cd;
uint32_t alloc_len, n, din_len;
uint32_t pg_sz = sg_get_page_size();
const uint8_t * dip;
- struct sg_nvme_passthru_cmd cmd;
+ struct nvme_pt_command npc;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
pcv = !! (0x1 & cdbp[1]);
dpg_cd = cdbp[2];
@@ -1466,7 +1561,12 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
if (vb > 3)
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;
+ fdc_p = get_fdc_p(ptp);
+ if (NULL == fdc_p) {
+ pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__);
+ return SG_LIB_OS_BASE_ERR + EINVAL;
+ }
+ din_len = ptp->dxfer_len;
if (pcv) {
if (0 == alloc_len) {
/* T10 says not an error, hmmm */
@@ -1491,91 +1591,127 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
}
n = din_len;
n = (n < alloc_len) ? n : alloc_len;
- dip = (const uint8_t *)ptp->io_hdr.din_xferp;
+ dip = (const uint8_t *)ptp->dxferp;
if (! is_aligned(dip, pg_sz)) { /* caller best use sg_memalign(,pg_sz) */
if (vb)
pr2ws("%s: din [0x%" PRIx64 "] not page aligned\n", __func__,
- (uint64_t)ptp->io_hdr.din_xferp);
+ (uint64_t)ptp->dxferp);
return SCSI_PT_DO_BAD_PARAMS;
}
if (vb)
pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__,
dpg_cd);
- memset(&cmd, 0, sizeof(cmd));
- cmd.opcode = 0x1e; /* MI receive */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
- cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
- /* din_len > 0x1000, is this a problem?? */
- cmd.cdw10 = 0x0804; /* NVMe Message Header */
- cmd.cdw11 = 0x8; /* nvme_mi_ses_receive */
- cmd.cdw12 = dpg_cd;
- cmd.cdw13 = n;
- res = do_nvme_admin_cmd(ptp, &cmd, time_secs, false, vb);
- ptp->io_hdr.din_resid = din_len - n;
- return res;
+ memset(npc_up, 0, sizeof(npc));
+ npc_up[SG_NVME_PT_OPCODE] = 0x1e; /* MI receive */
+ sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp,
+ npc_up + SG_NVME_PT_ADDR);
+ /* NVMe 4k page size. Maybe determine this? */
+ /* dout_len > 0x1000, is this a problem?? */
+ sg_put_unaligned_le32(0x1000, npc_up + SG_NVME_PT_DATA_LEN);
+ /* NVMe Message Header */
+ sg_put_unaligned_le32(0x0804, npc_up + SG_NVME_PT_CDW10);
+ /* nvme_mi_ses_receive */
+ sg_put_unaligned_le32(0x8, npc_up + SG_NVME_PT_CDW11);
+ 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);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ __func__, strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ ptp->resid = din_len - n;
+ return 0;
}
/* Executes NVMe Admin command (or at least forwards it to lower layers).
* Returns 0 for success, negative numbers are negated 'errno' values from
* OS system calls. Positive return values are errors from this package.
- * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds
- * is used. */
-int
-sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
+ * The time_secs argument is ignored. */
+static int
+sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb)
{
- bool scsi_cmd;
- int n, len;
- struct sg_pt_linux_scsi * ptp = &vp->impl;
- struct sg_nvme_passthru_cmd cmd;
+ bool scsi_cmd, in_xfer;
+ int n, err, len, io_len;
+ struct nvme_pt_command npc;
+ uint8_t * dxferp;
+ uint8_t * npc_up = (uint8_t *)&npc;
+ struct freebsd_dev_channel * fdc_p;
+ struct sg_pt_freebsd_scsi * ptp = &vp->impl;
const uint8_t * cdbp;
- if (! ptp->io_hdr.request) {
+ if (vb > 3)
+ pr2ws("%s: fd=%d\n", __func__, fd);
+ if (! ptp->cdb) {
if (vb)
- pr2ws("No NVMe command given (set_scsi_pt_cdb())\n");
+ pr2ws("%s: No NVMe command given (set_scsi_pt_cdb())\n",
+ __func__);
return SCSI_PT_DO_BAD_PARAMS;
}
- if (fd >= 0) {
- if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
- if (vb)
- pr2ws("%s: file descriptor given to create() and here "
- "differ\n", __func__);
+ fdc_p = get_fdc_p(ptp);
+ if (fd < 0) {
+ if (NULL == fdc_p) {
+ pr2ws("%s: no device handle in object or fd ?\n", __func__);
+ return SG_LIB_FILE_ERROR;
+ }
+ } else {
+ int han = fd - FREEBSD_FDOFFSET;
+
+ if ((han < 0) || (han >= FREEBSD_MAXDEV)) {
+ pr2ws("%s: argument 'fd' is bad\n", __func__);
return SCSI_PT_DO_BAD_PARAMS;
}
- ptp->dev_fd = fd;
- } else if (ptp->dev_fd < 0) {
- if (vb)
- pr2ws("%s: invalid file descriptors\n", __func__);
- return SCSI_PT_DO_BAD_PARAMS;
+ if (NULL == devicetable[han]) {
+ pr2ws("%s: argument 'fd' is bad (2)\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ if (fdc_p && (fdc_p != devicetable[han])) {
+ pr2ws("%s: different device handle in object and fd ?\n",
+ __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ if (NULL == fdc_p) {
+ ptp->dev_han = fd;
+ fdc_p = devicetable[han];
+ }
}
- n = ptp->io_hdr.request_len;
- cdbp = (const uint8_t *)ptp->io_hdr.request;
+
+ n = ptp->cdb_len;
+ cdbp = (const uint8_t *)ptp->cdb;
if (vb > 3)
- pr2ws("%s: opcode=0x%x, fd=%d, time_secs=%d\n", __func__, cdbp[0],
- fd, time_secs);
- scsi_cmd = is_scsi_command(cdbp, n);
+ pr2ws("%s: opcode=0x%x, fd=%d\n", __func__, cdbp[0], fd);
+ scsi_cmd = sg_is_scsi_cdb(cdbp, n);
if (scsi_cmd) {
switch (cdbp[0]) {
case SCSI_INQUIRY_OPC:
- return sntl_inq(ptp, cdbp, time_secs, vb);
+ return sntl_inq(ptp, cdbp, vb);
case SCSI_REPORT_LUNS_OPC:
- return sntl_rluns(ptp, cdbp, time_secs, vb);
+ return sntl_rluns(ptp, cdbp, vb);
case SCSI_TEST_UNIT_READY_OPC:
- return sntl_tur(ptp, time_secs, vb);
+ return sntl_tur(ptp, vb);
case SCSI_REQUEST_SENSE_OPC:
- return sntl_req_sense(ptp, cdbp, time_secs, vb);
+ return sntl_req_sense(ptp, cdbp, vb);
case SCSI_SEND_DIAGNOSTIC_OPC:
- return sntl_senddiag(ptp, cdbp, time_secs, vb);
+ return sntl_senddiag(ptp, cdbp, vb);
case SCSI_RECEIVE_DIAGNOSTIC_OPC:
- return sntl_recvdiag(ptp, cdbp, time_secs, vb);
-// xxxxxxxxxx
+ return sntl_recvdiag(ptp, cdbp, vb);
default:
mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE,
0, vb);
return 0;
}
}
- len = (int)sizeof(cmd);
+ len = (int)sizeof(npc.cmd);
n = (n < len) ? n : len;
if (n < 64) {
if (vb)
@@ -1583,15 +1719,42 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
n);
return SCSI_PT_DO_BAD_PARAMS;
}
- memcpy(&cmd, (const uint8_t *)ptp->io_hdr.request, n);
- if (n < len) /* zero out rest of 'cmd' */
- memset((unsigned char *)&cmd + n, 0, len - n);
- if (ptp->io_hdr.din_xfer_len > 0) {
- cmd.data_len = ptp->io_hdr.din_xfer_len;
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
- } else if (ptp->io_hdr.dout_xfer_len > 0) {
- cmd.data_len = ptp->io_hdr.dout_xfer_len;
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
- }
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, true, vb);
+ memcpy(npc_up, (const uint8_t *)ptp->cdb, n);
+ if (n < len) /* zero out rest of 'npc' */
+ memset(npc_up + n, 0, len - n);
+ in_xfer = false;
+ io_len = 0;
+ dxferp = NULL;
+ if (ptp->dxfer_ilen > 0) {
+ in_xfer = true;
+ io_len = ptp->dxfer_ilen;
+ dxferp = ptp->dxferip;
+ sg_put_unaligned_le32(ptp->dxfer_ilen, npc_up + SG_NVME_PT_DATA_LEN);
+ sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferip,
+ npc_up + SG_NVME_PT_ADDR);
+ } else if (ptp->dxfer_olen > 0) {
+ in_xfer = false;
+ io_len = ptp->dxfer_olen;
+ dxferp = ptp->dxferop;
+ sg_put_unaligned_le32(ptp->dxfer_olen, npc_up + SG_NVME_PT_DATA_LEN);
+ 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);
+ if (err) {
+ if (err < 0) {
+ err = -err;
+ if (vb > 1)
+ pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n",
+ __func__, strerror(err), err);
+ return SG_LIB_OS_BASE_ERR + err;
+ } else {
+ fdc_p->nvme_status = err;
+ mk_sense_from_nvme_status(ptp, err, vb);
+ return 0;
+ }
+ }
+ if (in_xfer)
+ ptp->resid = 0; /* Just hoping ... */
+ return 0;
}
diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c
index 91c4c250..3512ea1e 100644
--- a/lib/sg_pt_linux.c
+++ b/lib/sg_pt_linux.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_linux version 1.32 20171207 */
+/* sg_pt_linux version 1.33 20171227 */
#include <stdio.h>
@@ -360,6 +360,12 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose)
{
int fd;
+bool ok;
+char b[512];
+ok = sg_get_nvme_char_devname(device_name, sizeof(b), b);
+pr2ws("%s: sg_get_nvme_char_devname() --> ok=%s\n", __func__, ok ? "true" : "false");
+if (ok)
+pr2ws("\t trimmed devname: %s\n", b);
if (! sg_bsg_nvme_char_major_checked) {
sg_bsg_nvme_char_major_checked = true;
sg_find_bsg_nvme_char_major(verbose);
@@ -433,6 +439,11 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
+ if (ptp->free_nvme_id_ctlp) {
+ free(ptp->free_nvme_id_ctlp);
+ ptp->free_nvme_id_ctlp = NULL;
+ ptp->nvme_id_ctlp = NULL;
+ }
if (ptp)
free(ptp);
}
diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c
index c18a854a..a3d092d6 100644
--- a/lib/sg_pt_linux_nvme.c
+++ b/lib/sg_pt_linux_nvme.c
@@ -136,35 +136,32 @@ pr2ws(const char * fmt, ...)
return n;
}
-/* The web claims that all NVMe commands are 64 bytes long. Believe it until
- * contradicted. The only SCSI commands that can be longer than 16 bytes are
- * the Variable Length Commands (opcode 0x7f) and the XCDB wrapped commands
- * (opcode 0x7e). Both have an inbuilt length field which can be cross
- * checked with clen. */
-static bool
-is_scsi_command(const uint8_t * cdbp, int clen)
+/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5)
+ * to the name of its associated char device (e.g. /dev/nvme0). If this
+ * occurs true is returned and the char device name is placed in 'b' (as
+ * long as b_len is sufficient). Otherwise false is returned. */
+ bool
+sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len,
+ char * b)
{
- int ilen, sa;
-
- if (clen <= 16)
- return true;
- if (0 == (clen % 4)) {
- if (0x7f == cdbp[0]) {
- ilen = 8 + cdbp[7];
- sa = sg_get_unaligned_be16(cdbp + 8);
- if ((ilen == clen) && sa)
- return true;
- } else if (0x7e == cdbp[0]) {
- ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
- if (ilen == clen)
- return true;
- }
- }
- if ((clen >= 64) && (clen <= 72))
- return false;
- pr2ws("%s: irregular command, assume NVMe:\n", __func__);
- dStrHexErr((const char *)cdbp, clen, 1);
- return false;
+ uint32_t n, tlen;
+ const char * cp;
+ char buff[8];
+
+ if ((NULL == b) || (b_len < 5))
+ return false; /* degenerate cases */
+ cp = strstr(nvme_block_devname, "nvme");
+ if (NULL == cp)
+ return false; /* expected to find "nvme" in given name */
+ if (1 != sscanf(cp, "nvme%u", &n))
+ return false; /* didn't find valid "nvme<number>" */
+ snprintf(buff, sizeof(buff), "%u", n);
+ tlen = (cp - nvme_block_devname) + 4 + strlen(buff);
+ if ((tlen + 1) > b_len)
+ return false; /* b isn't long enough to fit output */
+ memcpy(b, nvme_block_devname, tlen);
+ b[tlen] = '\0';
+ return true;
}
static void
@@ -210,6 +207,37 @@ mk_sense_asc_ascq(struct sg_pt_linux_scsi * ptp, int sk, int asc, int ascq,
ascq);
}
+static void
+mk_sense_from_nvme_status(struct sg_pt_linux_scsi * ptp, int vb)
+{
+ bool ok;
+ bool dsense = ptp->scsi_dsense;
+ int n;
+ uint8_t sstatus, sk, asc, ascq;
+ uint8_t * sbp = (uint8_t *)ptp->io_hdr.response;
+
+ ok = sg_nvme_status2scsi(ptp->nvme_status, &sstatus, &sk, &asc, &ascq);
+ if (! ok) { /* can't find a mapping to a SCSI error, so ... */
+ sstatus = SAM_STAT_CHECK_CONDITION;
+ sk = SPC_SK_ILLEGAL_REQUEST;
+ asc = 0xb;
+ ascq = 0x0; /* asc: "WARNING" purposely vague */
+ }
+
+ ptp->io_hdr.device_status = sstatus;
+ n = ptp->io_hdr.max_response_len;
+ if ((n < 8) || ((! dsense) && (n < 14))) {
+ pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, n);
+ return;
+ } else
+ ptp->io_hdr.response_len = (dsense ? 8 : ((n < 18) ? n : 18));
+ memset(sbp, 0, n);
+ build_sense_buffer(dsense, sbp, sk, asc, ascq);
+ if (vb > 3)
+ pr2ws("%s: [status, sense_key,asc,ascq]: [0x%x, 0x%x,0x%x,0x%x]\n",
+ __func__, sstatus, sk, asc, ascq);
+}
+
/* Set in_bit to -1 to indicate no bit position of invalid field */
static void
mk_sense_invalid_fld(struct sg_pt_linux_scsi * ptp, bool in_cdb, int in_byte,
@@ -253,53 +281,94 @@ mk_sense_invalid_fld(struct sg_pt_linux_scsi * ptp, bool in_cdb, int in_byte,
__func__, asc, in_cdb ? 'C' : 'D', in_byte, in_bit);
}
+/* Returns 0 for success. Returns SG_LIB_NVME_STATUS if there is non-zero
+ * NVMe status (from the completion queue) with the value in
+ * ptp->nvme_status. If Unix error from ioctl add equivalent errno value to
+ * SG_LIB_OS_BASE_ERR. Should not return negative values. CDW0 from
+ * the completion queue is placed in ptp->nvme_result on success. */
static int
do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
- struct sg_nvme_passthru_cmd *cmdp, int time_secs, int vb)
+ struct sg_nvme_passthru_cmd *cmdp, const void * dp,
+ bool is_read, int time_secs, int vb)
{
const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd);
int res;
+ uint32_t n;
+ const uint8_t * up = ((const uint8_t *)cmdp) + SG_NVME_PT_OPCODE;
cmdp->timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
if (vb > 2) {
pr2ws("NVMe command:\n");
- dStrHex((const char *)cmdp, cmd_len, 1);
+ dStrHexErr((const char *)cmdp, cmd_len, 1);
+ if ((vb > 3) && (! is_read) && dp) {
+ uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN);
+
+ if (len > 0) {
+ n = len;
+ if ((len < 512) || (vb > 5))
+ pr2ws("\nData-out buffer (%u bytes):\n", n);
+ else {
+ pr2ws("\nData-out buffer (first 512 of %u bytes):\n", n);
+ n = 512;
+ }
+ dStrHexErr((const char *)dp, n, 0);
+ }
+ }
}
res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp);
if (0 != res) {
if (res < 0) { /* OS error (errno negated) */
- ptp->os_err = -res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -res;
+ res = (-res & 0x3ff); /* clear DNR and More, if present */
+ ptp->os_err = res;
+ if (vb > 3) {
+ pr2ws("%s: ioctl opcode=0x%x failed: %s "
+ "(errno=%d)\n", __func__, *up, strerror(res), res);
+ }
+ return SG_LIB_OS_BASE_ERR + res;
} else { /* NVMe errors are positive return values */
ptp->nvme_status = res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD) failed: NVMe status "
- "(SF) 0x%x\n", __func__, res);
+ if (vb > 2) {
+ char b[80];
+
+ pr2ws("%s: ioctl opcode=0x%x failed: NVMe status: %s "
+ "[0x%x]\n", __func__, *up,
+ sg_get_nvme_cmd_status_str(res, sizeof(b), b), res);
+ }
return SG_LIB_NVME_STATUS;
}
} else {
ptp->os_err = 0;
ptp->nvme_status = 0;
+ if ((vb > 3) && is_read && dp) {
+ uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN);
+
+ if (len > 0) {
+ n = len;
+ if ((len < 1024) || (vb > 5))
+ pr2ws("\nData-in buffer (%u bytes):\n", n);
+ else {
+ pr2ws("\nData-in buffer (first 1024 of %u bytes):\n", n);
+ n = 1024;
+ }
+ dStrHexErr((const char *)dp, n, 0);
+ }
+ }
}
ptp->nvme_result = cmdp->result;
- ptp->io_hdr.response_len = 0;
return 0;
}
+/* Returns 0 on success; otherwise a positive value is returned */
static int
sntl_cache_identity(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
{
- int res;
struct sg_nvme_passthru_cmd cmd;
uint32_t pg_sz = sg_get_page_size();
+ void * vp;
- ptp->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp,
- vb > 3);
- if (NULL == ptp->nvme_id_ctlp) {
+ vp = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp, vb > 3);
+ ptp->nvme_id_ctlp = vp;
+ if (NULL == vp) {
pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
return SG_LIB_OS_BASE_ERR + ENOMEM;
}
@@ -308,30 +377,7 @@ sntl_cache_identity(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
cmd.cdw10 = 0x1; /* CNS=0x1 Identify controller */
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->nvme_id_ctlp;
cmd.data_len = pg_sz;
- cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
- if (0 != res) {
- if (res < 0) { /* OS error (errno negated) */
- ptp->os_err = -res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD(Identify)) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -res;
- } else { /* NVMe errors are positive return values */
- ptp->nvme_status = res;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: NVMe status "
- "(SF) 0x%x\n", __func__, res);
- return SG_LIB_NVME_STATUS;
- }
- } else {
- ptp->os_err = 0;
- ptp->nvme_status = 0;
- }
- ptp->nvme_result = cmd.result;
- ptp->io_hdr.response_len = 0;
- return 0;
+ return do_nvme_admin_cmd(ptp, &cmd, vp, true, time_secs, vb);
}
static const char * nvme_scsi_vendor_str = "NVMe ";
@@ -355,7 +401,10 @@ sntl_inq(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
}
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else if (res)
return res;
}
memset(inq_dout, 0, sizeof(inq_dout));
@@ -448,7 +497,10 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
alloc_len = sg_get_unaligned_be32(cdbp + 6);
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else if (res)
return res;
}
max_nsid = sg_get_unaligned_le32(ptp->nvme_id_ctlp + 516);
@@ -502,11 +554,14 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
uint32_t pow_state;
struct sg_nvme_passthru_cmd cmd;
- if (vb > 3)
+ if (vb > 4)
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else if (res)
return res;
}
memset(&cmd, 0, sizeof(cmd));
@@ -514,28 +569,17 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
cmd.nsid = SG_NVME_BROADCAST_NSID;
cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb);
if (0 != res) {
- if (res < 0) { /* OS error (errno negated) */
- ptp->os_err = -res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD(Get feature)) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -res;
- } else { /* NVMe errors are positive return values */
- ptp->nvme_status = res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN_CMD(Get feature)) failed: NVMe "
- "status (SF) 0x%x\n", __func__, res);
- return SG_LIB_NVME_STATUS;
- }
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else
+ return res;
} else {
ptp->os_err = 0;
ptp->nvme_status = 0;
}
- ptp->nvme_result = cmd.result;
- ptp->io_hdr.response_len = 0;
pow_state = (0x1f & ptp->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
@@ -561,7 +605,10 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
pr2ws("%s: time_secs=%d\n", __func__, time_secs);
if (NULL == ptp->nvme_id_ctlp) {
res = sntl_cache_identity(ptp, time_secs, vb);
- if (res)
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else if (res)
return res;
}
desc = !!(0x1 & cdbp[1]);
@@ -571,38 +618,28 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
cmd.nsid = SG_NVME_BROADCAST_NSID;
cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb);
if (0 != res) {
- if (res < 0) { /* OS error (errno negated) */
- ptp->os_err = -res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN(Get feature)) failed: %s "
- "(errno=%d)\n", __func__, strerror(ptp->os_err),
- ptp->os_err);
- return -res;
- } else { /* NVMe errors are positive return values */
- ptp->nvme_status = res;
- if (vb > 2)
- pr2ws("%s: ioctl(ADMIN(Get feature)) failed: NVMe "
- "status (SF) 0x%x\n", __func__, res);
- return SG_LIB_NVME_STATUS;
- }
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else
+ return res;
} else {
ptp->os_err = 0;
ptp->nvme_status = 0;
}
- ptp->nvme_result = cmd.result;
ptp->io_hdr.response_len = 0;
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));
if (pow_state)
- build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
- LOW_POWER_COND_ON_ASC, 0);
+ build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
+ LOW_POWER_COND_ON_ASC, 0);
else
- build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
- NO_ADDITIONAL_SENSE, 0);
+ build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
+ NO_ADDITIONAL_SENSE, 0);
n = desc ? 8 : 18;
n = (n < alloc_len) ? n : alloc_len;
n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
@@ -623,11 +660,13 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
int time_secs, int vb)
{
bool pf, self_test;
+ int res;
uint8_t st_cd, dpg_cd;
- uint32_t alloc_len, n, dout_len, dpg_len;
+ uint32_t alloc_len, n, dout_len, dpg_len, nvme_dst;
uint32_t pg_sz = sg_get_page_size();
const uint8_t * dop;
struct sg_nvme_passthru_cmd cmd;
+ uint8_t * cmd_up = (uint8_t *)&cmd;
st_cd = 0x7 & (cdbp[1] >> 5);
self_test = !! (0x4 & cdbp[1]);
@@ -635,8 +674,38 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
if (vb > 3)
pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf,
(int)self_test, (int)st_cd);
- if (self_test)
- return 0; /* NVMe has no self-test, just say OK */
+ if (self_test || st_cd) {
+ memset(cmd_up, 0, sizeof(cmd));
+ cmd_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-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) {
+ case 0: /* Here if self_test is set, do short self-test */
+ case 1: /* Background short */
+ case 5: /* Foreground short */
+ nvme_dst = 1;
+ break;
+ case 2: /* Background extended */
+ case 6: /* Foreground extended */
+ nvme_dst = 2;
+ break;
+ case 4: /* Abort self-test */
+ nvme_dst = 0xf;
+ break;
+ default:
+ pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ sg_put_unaligned_le32(nvme_dst, cmd_up + SG_NVME_PT_CDW10);
+ res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb);
+ if (0 != res) {
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else
+ return res;
+ }
+ }
alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
dout_len = ptp->io_hdr.dout_xfer_len;
if (pf) {
@@ -685,13 +754,20 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
__func__, dpg_cd, dpg_len);
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = 0x1d; /* MI send; hmmm same opcode as SEND DIAG */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
+ cmd.addr = (uint64_t)(sg_uintptr_t)dop;
cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
/* dout_len > 0x1000, is this a problem?? */
cmd.cdw10 = 0x0804; /* NVMe Message Header */
cmd.cdw11 = 0x9; /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */
cmd.cdw13 = n;
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
+ res = do_nvme_admin_cmd(ptp, &cmd, dop, false, time_secs, vb);
+ if (0 != res) {
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ }
+ }
+ return res;
}
/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1)
@@ -733,14 +809,21 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
dpg_cd);
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = 0x1e; /* MI receive */
- cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
+ cmd.addr = (uint64_t)(sg_uintptr_t)dip;
cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
/* din_len > 0x1000, is this a problem?? */
cmd.cdw10 = 0x0804; /* NVMe Message Header */
cmd.cdw11 = 0x8; /* nvme_mi_ses_receive */
cmd.cdw12 = dpg_cd;
cmd.cdw13 = n;
- res = do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
+ res = do_nvme_admin_cmd(ptp, &cmd, dip, true, time_secs, vb);
+ if (0 != res) {
+ if (SG_LIB_NVME_STATUS == res) {
+ mk_sense_from_nvme_status(ptp, vb);
+ return 0;
+ } else
+ return res;
+ }
ptp->io_hdr.din_resid = din_len - n;
return res;
}
@@ -754,10 +837,12 @@ int
sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
{
bool scsi_cmd;
+ bool is_read = false;
int n, len;
struct sg_pt_linux_scsi * ptp = &vp->impl;
struct sg_nvme_passthru_cmd cmd;
const uint8_t * cdbp;
+ void * dp = NULL;
if (! ptp->io_hdr.request) {
if (vb)
@@ -782,7 +867,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
if (vb > 3)
pr2ws("%s: opcode=0x%x, fd=%d, time_secs=%d\n", __func__, cdbp[0],
fd, time_secs);
- scsi_cmd = is_scsi_command(cdbp, n);
+ scsi_cmd = sg_is_scsi_cdb(cdbp, n);
if (scsi_cmd) {
switch (cdbp[0]) {
case SCSI_INQUIRY_OPC:
@@ -797,7 +882,6 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
return sntl_senddiag(ptp, cdbp, time_secs, vb);
case SCSI_RECEIVE_DIAGNOSTIC_OPC:
return sntl_recvdiag(ptp, cdbp, time_secs, vb);
-// xxxxxxxxxx
default:
mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE,
0, vb);
@@ -817,10 +901,14 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
memset((unsigned char *)&cmd + n, 0, len - n);
if (ptp->io_hdr.din_xfer_len > 0) {
cmd.data_len = ptp->io_hdr.din_xfer_len;
+ dp = (void *)ptp->io_hdr.din_xferp;
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
+ is_read = true;
} else if (ptp->io_hdr.dout_xfer_len > 0) {
cmd.data_len = ptp->io_hdr.dout_xfer_len;
+ dp = (void *)ptp->io_hdr.dout_xferp;
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
+ is_read = false;
}
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
+ return do_nvme_admin_cmd(ptp, &cmd, dp, is_read, time_secs, vb);
}