diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2017-12-29 18:23:19 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2017-12-29 18:23:19 +0000 |
commit | b29ef85867d5347e8fed4c603986964868cba801 (patch) | |
tree | 6c39b1855f97dc416307229654713d9aed4c8d54 /lib | |
parent | 766b77d70276eef4a6ae92217d1ffbcde819a88b (diff) | |
download | sg3_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.c | 189 | ||||
-rw-r--r-- | lib/sg_lib_data.c | 177 | ||||
-rw-r--r-- | lib/sg_pt_freebsd.c | 955 | ||||
-rw-r--r-- | lib/sg_pt_linux.c | 13 | ||||
-rw-r--r-- | lib/sg_pt_linux_nvme.c | 328 |
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); } |