diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2021-05-02 01:09:24 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2021-05-02 01:09:24 +0000 |
commit | 0791cf6b3cd25a054d12c42425cbb8ffee7e598a (patch) | |
tree | e50a551f783363a35d07893dd9db836d60080e0b /lib | |
parent | 28400776770cf5453f196690fcd24e353c5826e0 (diff) | |
download | sg3_utils-0791cf6b3cd25a054d12c42425cbb8ffee7e598a.tar.gz |
major rework of lib/sg_pt_freebsd.c; sg_raw: fix prints of NVMe NVM command names
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@896 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'lib')
-rw-r--r-- | lib/sg_lib.c | 13 | ||||
-rw-r--r-- | lib/sg_lib_data.c | 2 | ||||
-rw-r--r-- | lib/sg_pt_freebsd.c | 1316 | ||||
-rw-r--r-- | lib/sg_pt_linux.c | 2 | ||||
-rw-r--r-- | lib/sg_pt_linux_nvme.c | 100 |
5 files changed, 1030 insertions, 403 deletions
diff --git a/lib/sg_lib.c b/lib/sg_lib.c index 1860e69a..780a3a4d 100644 --- a/lib/sg_lib.c +++ b/lib/sg_lib.c @@ -261,6 +261,19 @@ static const struct sg_lib_simple_value_name_t sstatus_str_arr[] = { {0xffff, NULL}, }; +bool +sg_scsi_status_is_good(int sstatus) +{ + sstatus &= 0xfe; + switch (sstatus) { + case SAM_STAT_GOOD: + case SAM_STAT_CONDITION_MET: + return true; + default: + return false; + } +} + void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff) { diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c index eb18be62..f8bcd67e 100644 --- a/lib/sg_lib_data.c +++ b/lib/sg_lib_data.c @@ -19,7 +19,7 @@ #include "sg_lib_data.h" -const char * sg_lib_version_str = "2.79 20210304"; +const char * sg_lib_version_str = "2.80 20210419"; /* spc6r05, sbc4r22, zbc2r09 */ diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c index 4a3845f6..65399f55 100644 --- a/lib/sg_pt_freebsd.c +++ b/lib/sg_pt_freebsd.c @@ -7,7 +7,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ -/* sg_pt_freebsd version 1.39 20210225 */ +/* sg_pt_freebsd version 1.40 20210501 */ #include <stdio.h> #include <stdlib.h> @@ -46,30 +46,36 @@ #if (HAVE_NVME && (! IGNORE_NVME)) #include "freebsd_nvme_ioctl.h" #else -#define NVME_CTRLR_PREFIX "/dev/nvme" -#define NVME_NS_PREFIX "ns" +#define NVME_CTRLR_PREFIX "/dev/nvme" +#define NVME_NS_PREFIX "ns" #endif +#define SG_NVME_NVD_PREFIX "/dev/nvd" /* >= FreeBSD 9.2 */ +#define SG_NVME_NDA_PREFIX "/dev/nda" /* >= FreeBSD 12.0, CAM compatible */ #define FREEBSD_MAXDEV 64 #define FREEBSD_FDOFFSET 16; +#if __FreeBSD_version > 500000 +#define CAM_ERROR_PRINT(a, b, c, d, e) cam_error_print(a, b, c, d, e); +#else +#define CAM_ERROR_PRINT(a, b, c, d, e) +#endif -struct freebsd_dev_channel { - int unitnum; // the SCSI unit number - bool is_nvme; /* OS device type, if false ignore nvme_our_sntl */ - bool nvme_our_sntl; /* true: our SNTL; false: received NVMe command */ - bool is_char; + +struct freebsd_dev_channel { /* one instance per open file descriptor */ + bool is_nvme_dev; /* true if NVMe device attached, else SCSI */ + bool is_cam_nvme; /* NVMe via /dev/nda<n> or /dev/pass<n> devices */ + bool is_pass; /* CAM passthrough device (i.e. 'pass<n>') */ + int unitnum; /* the SCSI unit number, NVMe controller id? */ uint32_t nsid; - uint32_t nv_ctrlid; - int dev_fd; // for NVMe, use -1 to indicate not provided - uint32_t nvme_result; // cdw0 from completion - uint16_t nvme_status; // from completion: ((sct << 8) | sc) + // uint32_t nv_ctrlid; /* unitnum seems to have this role */ + int nvme_fd_ns; // for non-CAM NVMe, use -1 to indicate not provided + int nvme_fd_ctrl; // open("/dev/nvme<n>") if needed */ char* devname; // the device name struct cam_device* cam_dev; uint8_t * nvme_id_ctlp; uint8_t * free_nvme_id_ctlp; - uint8_t cq_dw0_3[16]; struct sg_sntl_dev_state_t dev_stat; // owner }; @@ -79,8 +85,7 @@ static struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV]; #define DEF_TIMEOUT 60000 /* 60,000 milliseconds (60 seconds) */ -struct sg_pt_freebsd_scsi { - struct cam_device* cam_dev; // copy held for error processing +struct sg_pt_freebsd_scsi { /* context of one SCSI/NVME command (pt object) */ union ccb *ccb; uint8_t * cdb; int cdb_len; @@ -95,7 +100,9 @@ struct sg_pt_freebsd_scsi { uint32_t dxfer_ilen; uint32_t dxfer_olen; uint32_t mdxfer_len; - bool mdxfer_out; + uint32_t nvme_result; // cdw0 from completion + uint16_t nvme_status; // from completion: ((sct << 8) | sc) + uint8_t cq_dw0_3[16]; int timeout_ms; int scsi_status; int resid; @@ -106,18 +113,22 @@ struct sg_pt_freebsd_scsi { int dev_han; // should be >= FREEBSD_FDOFFSET then // (dev_han - FREEBSD_FDOFFSET) is the // index into devicetable[] - bool is_nvme; // copy of same field in fdc object - bool nvme_our_sntl; // copy of same field in fdc object - struct sg_sntl_dev_state_t * dev_statp; // points to associated fdc + bool mdxfer_out; + bool is_nvme_dev; /* copied from owning mchanp */ + bool nvme_our_sntl; /* true: our SNTL; false: received NVMe command */ + struct freebsd_dev_channel * mchanp; /* associated device info */ }; struct sg_pt_base { struct sg_pt_freebsd_scsi impl; }; -static const uint32_t broadcast_nsid = SG_NVME_BROADCAST_NSID; +// static const uint32_t broadcast_nsid = SG_NVME_BROADCAST_NSID; -static int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb); +#if (HAVE_NVME && (! IGNORE_NVME)) +static int sg_do_nvme_pt(struct sg_pt_freebsd_scsi * ptp, int fd, + bool is_admin, int timeout_secs, int vb); +#endif @@ -141,6 +152,32 @@ get_fdc_cp(const struct sg_pt_freebsd_scsi * ptp) return devicetable[han]; } +/* This works with /dev/nvme*, /dev/nvd* and /dev/nda* but not /dev/pass* */ +static int +nvme_get_nsid(int fd, uint32_t *nsid, char *b, int blen, int vb) +{ + struct nvme_get_nsid gnsid; + int n_cdev = sizeof(gnsid.cdev); + + if (ioctl(fd, NVME_GET_NSID, &gnsid) < 0) { + int err = errno; + + if (vb > 2) + pr2ws("%s: ioctl(NVME_GET_NSID) failed, errno=%d", __func__, err); + return -err; + } + if (n_cdev < blen) { + strncpy(b, gnsid.cdev, n_cdev); + b[n_cdev] = '\0'; + } else { + strncpy(b, gnsid.cdev, blen); + b[blen - 1] = '\0'; + } + if (nsid != NULL) + *nsid = gnsid.nsid; + return 0; +} + /* Returns >= 0 if successful. If error in Unix returns negated errno. */ int scsi_pt_open_device(const char * device_name, bool read_only, int vb) @@ -151,6 +188,43 @@ scsi_pt_open_device(const char * device_name, bool read_only, int vb) return scsi_pt_open_flags(device_name, oflags, vb); } +/* Get a get device CCB for the specified device, borrowed from camdd.c */ +int +sg_cam_get_cgd(struct cam_device *device, struct ccb_getdev *cgd, int vb) +{ + union ccb *ccb; + FILE * ferrp = sg_warnings_strm ? sg_warnings_strm : stderr; + int retval = 0; + + ccb = cam_getccb(device); + if (ccb == NULL) { + if (vb) + pr2ws("%s: couldn't allocate CCB", __func__); + return -ENOMEM; + } + CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cgd); + ccb->ccb_h.func_code = XPT_GDEV_TYPE; + + if (cam_send_ccb(device, ccb) < 0) { + if (vb) { + pr2ws("%s: error sending Get Device Information CCB", __func__); + CAM_ERROR_PRINT(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, ferrp); + } + retval = -ENODEV; + goto bailout; + } + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if (vb) + CAM_ERROR_PRINT(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, ferrp); + retval = -ENODEV; + goto bailout; + } + bcopy(&ccb->cgd, cgd, sizeof(struct ccb_getdev)); +bailout: + cam_freeccb(ccb); + return retval; +} + /* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed * together. The 'oflags' is only used on NVMe devices. It is ignored on * SCSI and ATA devices in FreeBSD. @@ -158,16 +232,19 @@ scsi_pt_open_device(const char * device_name, bool read_only, int vb) int scsi_pt_open_flags(const char * device_name, int oflags, int vb) { - bool is_char, is_block, possible_nvme; - char tmp, first_ch; - int k, err, dev_fd, ret; - uint32_t nsid, nv_ctrlid; + bool maybe_non_cam_nvme = false; + bool basnam0_n; + char first_ch; + int k, err, dev_fd, ret, handle_idx; ssize_t s; struct freebsd_dev_channel *fdc_p = NULL; struct cam_device* cam_dev; struct stat a_stat; char dev_nm[PATH_MAX]; + if (vb > 6) + pr2ws("%s: device_name=%s, oflags=0x%x\n", __func__, device_name, + oflags); // Search table for a free entry for (k = 0; k < FREEBSD_MAXDEV; k++) if (! devicetable[k]) @@ -181,59 +258,7 @@ scsi_pt_open_flags(const char * device_name, int oflags, int vb) ret = -EMFILE; goto err_out; } - first_ch = device_name[0]; - if (('/' != first_ch) && ('.' != first_ch)) { - char b[PATH_MAX]; - - /* Step 1: if device_name is symlink, follow it */ - s = readlink(device_name, b, sizeof(b)); - if (s <= 0) { - strncpy(b, device_name, PATH_MAX - 1); - b[PATH_MAX - 1] = '\0'; - } - /* Step 2: if no leading '/' nor '.' given, prepend '/dev/' */ - first_ch = b[0]; - if (('/' != first_ch) && ('.' != first_ch)) - snprintf(dev_nm, PATH_MAX, "%s%s", "/dev/", b); - else - strcpy(dev_nm, b); - } else - strcpy(dev_nm, device_name); - if (stat(dev_nm, &a_stat) < 0) { - err = errno; - pr2ws("%s: unable to stat(%s): %s\n", __func__, dev_nm, - strerror(err)); - ret = -err; - goto err_out; - } - is_block = S_ISBLK(a_stat.st_mode); - is_char = S_ISCHR(a_stat.st_mode); - if (! (is_block || is_char)) { - if (vb) - pr2ws("%s: %s is not char nor block device\n", __func__, - dev_nm); - ret = -ENODEV; - goto err_out; - } - - /* Some code borrowed from smartmontools, Christian Franke */ - nsid = broadcast_nsid; - nv_ctrlid = broadcast_nsid; - possible_nvme = false; - while (true) { /* dummy loop, so can 'break' out */ - if(sscanf(dev_nm, NVME_CTRLR_PREFIX "%u%c", &nv_ctrlid, &tmp) == 1) { - if(nv_ctrlid == broadcast_nsid) - break; - } else if (sscanf(dev_nm, NVME_CTRLR_PREFIX "%d" NVME_NS_PREFIX - "%d%c", &nv_ctrlid, &nsid, &tmp) == 2) { - if((nv_ctrlid == broadcast_nsid) || (nsid == broadcast_nsid)) - break; - } else - break; - possible_nvme = true; - break; - } - + handle_idx = k; fdc_p = (struct freebsd_dev_channel *) calloc(1,sizeof(struct freebsd_dev_channel)); if (fdc_p == NULL) { @@ -241,68 +266,150 @@ scsi_pt_open_flags(const char * device_name, int oflags, int vb) ret = -ENOMEM; goto err_out; } - fdc_p->dev_fd = -1; -#if (HAVE_NVME && (! IGNORE_NVME)) - sntl_init_dev_stat(&fdc_p->dev_stat); -#endif + fdc_p->nvme_fd_ns = -1; + fdc_p->nvme_fd_ctrl = -1; if (! (fdc_p->devname = (char *)calloc(1, DEV_IDLEN+1))) { ret = -ENOMEM; goto err_out; } - - if (possible_nvme) { - // we should always open controller, not namespace device - snprintf(fdc_p->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d", - nv_ctrlid); - dev_fd = open(fdc_p->devname, oflags); - if (dev_fd < 0) { - err = errno; - if (vb) - pr2ws("%s: open(%s) failed: %s (errno=%d), try SCSI/ATA\n", - __func__, fdc_p->devname, strerror(err), err); - goto scsi_ata_try; - } - fdc_p->is_nvme = true; - fdc_p->nvme_our_sntl = true; /* guess at this stage */ - fdc_p->is_char = is_char; - fdc_p->nsid = (broadcast_nsid == nsid) ? 0 : nsid; - fdc_p->nv_ctrlid = nv_ctrlid; - fdc_p->dev_fd = dev_fd; - devicetable[k] = fdc_p; - return k + FREEBSD_FDOFFSET; - } - -scsi_ata_try: - fdc_p->is_char = is_char; - if (cam_get_device(dev_nm, fdc_p->devname, DEV_IDLEN, + /* Don't know yet whether device_name is a SCSI, NVME(non-CAM) or + * NVME(CAM) device. Start by assuming it is CAM. */ + if (cam_get_device(device_name, fdc_p->devname, DEV_IDLEN, &(fdc_p->unitnum)) == -1) { - if (vb) - pr2ws("bad device name structure\n"); - errno = EINVAL; - ret = -errno; + if (vb > 3) + pr2ws("%s: cam_get_device(%s) fails, should work for SCSI and " + "NVMe devices\n", __func__, device_name, errno); + ret = -EINVAL; goto err_out; - } - if (vb > 4) - pr2ws("%s: cam_get_device, f->devname: %s, f->unitnum=%d\n", __func__, - fdc_p->devname, fdc_p->unitnum); + } else if (vb > 6) + pr2ws("%s: cam_get_device() works, devname=%s unit=%u\n", __func__, + fdc_p->devname, fdc_p->unitnum); if (! (cam_dev = cam_open_spec_device(fdc_p->devname, fdc_p->unitnum, O_RDWR, NULL))) { - if (vb) + if (vb > 6) { pr2ws("cam_open_spec_device: %s\n", cam_errbuf); - errno = EPERM; /* permissions or not CAM device (NVMe ?) */ - ret = -errno; - goto err_out; + pr2ws("%s: so not CAM, but still maybe NVME\n", __func__); + } + maybe_non_cam_nvme = true; + } else { /* found CAM, could be SCSI or NVME(CAM) [nda driver] */ + struct ccb_getdev cgd; + + fdc_p->cam_dev = cam_dev; + ret = sg_cam_get_cgd(cam_dev, &cgd, vb); + if (ret) + goto err_out; + switch (cgd.protocol) { + case PROTO_SCSI: + fdc_p->is_nvme_dev = false; + break; + case PROTO_NVME: + fdc_p->is_nvme_dev = true; + fdc_p->is_cam_nvme = true; + fdc_p->nsid = cam_dev->target_lun & UINT32_MAX; + break; + case PROTO_ATA: + case PROTO_ATAPI: + case PROTO_SATAPM: + case PROTO_SEMB: /* SATA Enclosure Management bridge */ + if (vb) + pr2ws("%s: ATA and derivative devices not supported\n", + __func__); + ret = -EINVAL; + break; +#if __FreeBSD_version > 1200058 + case PROTO_MMCSD: + if (vb) + pr2ws("%s: MMC and SD devices not supported\n", + __func__); + ret = -EINVAL; + break; +#endif + default: + if (vb) + pr2ws("%s: unexpected device protocol\n", __func__); + ret = -EINVAL; + break; + } + if (ret) + goto err_out; + if (0 == memcpy("pass", fdc_p->devname, 4)) + fdc_p->is_pass = true; + } + if (maybe_non_cam_nvme) { + first_ch = device_name[0]; + if (('/' != first_ch) && ('.' != first_ch)) { + char b[PATH_MAX]; + + /* Step 1: if device_name is symlink, follow it */ + s = readlink(device_name, b, sizeof(b)); + if (s <= 0) { + strncpy(b, device_name, PATH_MAX - 1); + b[PATH_MAX - 1] = '\0'; + } + /* Step 2: if no leading '/' nor '.' given, prepend '/dev/' */ + first_ch = b[0]; + basnam0_n = ('n' == first_ch); + if (('/' != first_ch) && ('.' != first_ch)) + snprintf(dev_nm, PATH_MAX, "%s%s", "/dev/", b); + else + strcpy(dev_nm, b); + } else { + const char * cp; + + strcpy(dev_nm, device_name); + cp = basename(dev_nm); + basnam0_n = ('n' == *cp); + strcpy(dev_nm, device_name); + } + if (stat(dev_nm, &a_stat) < 0) { + err = errno; + pr2ws("%s: unable to stat(%s): %s\n", __func__, dev_nm, + strerror(err)); + ret = -err; + goto err_out; + } + if (! (S_ISCHR(a_stat.st_mode))) { + if (vb) + pr2ws("%s: %s is not a char device ??\n", __func__, dev_nm); + ret = -ENODEV; + goto err_out; + } + dev_fd = open(dev_nm, oflags); + if (dev_fd < 0) { + err = errno; + if (vb) + pr2ws("%s: open(%s) failed: %s (errno=%d), try SCSI/ATA\n", + __func__, dev_nm, strerror(err), err); + ret = -err; + goto err_out; + } + ret = nvme_get_nsid(dev_fd, &fdc_p->nsid, fdc_p->devname, DEV_IDLEN, + vb); + if (ret) + goto err_out; + if (vb > 6) + pr2ws("%s: nvme_dev_nm: %s, nsid=%u\n", __func__, fdc_p->devname, + fdc_p->nsid); + fdc_p->is_nvme_dev = true; + fdc_p->is_cam_nvme = false; + if (fdc_p->nsid > 0) + fdc_p->nvme_fd_ns = dev_fd; + else + fdc_p->nvme_fd_ctrl = dev_fd; } - fdc_p->cam_dev = cam_dev; // return pointer to "file descriptor" table entry, properly offset. - devicetable[k] = fdc_p; - return k + FREEBSD_FDOFFSET; + devicetable[handle_idx] = fdc_p; + return handle_idx + FREEBSD_FDOFFSET; err_out: /* ret should be negative value (negated errno) */ if (fdc_p) { if (fdc_p->devname) free(fdc_p->devname); + if (fdc_p->nvme_fd_ns >= 0) + close(fdc_p->nvme_fd_ns); + if (fdc_p->nvme_fd_ctrl >= 0) + close(fdc_p->nvme_fd_ctrl); free(fdc_p); fdc_p = NULL; } @@ -327,11 +434,13 @@ scsi_pt_close_device(int device_han) } if (fdc_p->devname) free(fdc_p->devname); - if (fdc_p->cam_dev) + if (fdc_p->cam_dev) /* N.B. can be cam_nvme devices */ cam_close_device(fdc_p->cam_dev); - if (fdc_p->is_nvme) { - if (fdc_p->dev_fd >= 0) - close(fdc_p->dev_fd); + else if (fdc_p->is_nvme_dev) { + if (fdc_p->nvme_fd_ns >= 0) + close(fdc_p->nvme_fd_ns); + if (fdc_p->nvme_fd_ctrl >= 0) + close(fdc_p->nvme_fd_ctrl); if (fdc_p->free_nvme_id_ctlp) { free(fdc_p->free_nvme_id_ctlp); fdc_p->nvme_id_ctlp = NULL; @@ -345,10 +454,12 @@ scsi_pt_close_device(int device_han) } /* Assumes device_han is an "open" file handle associated with some device. - * Returns 1 if SCSI generic pass-though device, returns 2 if secondary - * SCSI pass-through device (in Linux a bsg device); returns 3 is char - * NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes - * NSID), or 0 if something else (e.g. ATA block device) or device_han < 0. + * Returns 1 if SCSI generic pass-though device [SCSI CAM primary: nda0], + * returns 2 if secondary * SCSI pass-through device [SCSI CAM: pass<n>]; + * returns 3 if non-CAM NVMe with no nsid [nvme0]; returns 4 if non-CAM + * NVMe device with nsid (> 0) [nvme0ns1, nvd0]; returns 5 if CAM NVMe + * (with or without nsid) [nda0]; or returns 0 if something else (e.g. ATA + * block device) or device_han < 0. * If error, returns negated errno (operating system) value. */ int check_pt_file_handle(int device_han, const char * device_name, int vb) @@ -356,18 +467,26 @@ check_pt_file_handle(int device_han, const char * device_name, int vb) struct freebsd_dev_channel *fdc_p; int han = device_han - FREEBSD_FDOFFSET; + if (vb > 6) + pr2ws("%s: device_handle=%d, device_name: %s\n", __func__, + device_han, device_name); if ((han < 0) || (han >= FREEBSD_MAXDEV)) return -ENODEV; fdc_p = devicetable[han]; if (NULL == fdc_p) return -ENODEV; - if (fdc_p->is_nvme) - return 4 - (int)fdc_p->is_char; - else if (fdc_p->cam_dev) - return 2 - (int)fdc_p->is_char; + if (fdc_p->is_nvme_dev) { + if (fdc_p->is_cam_nvme) + return 5; + else if (fdc_p->nsid == 0) + return 3; + else + return 4; /* Something like nvme0ns1 or nvd0 */ + } else if (fdc_p->cam_dev) + return fdc_p->is_pass ? 2 : 1; else { if (vb) - pr2ws("%s: neither SCSI nor NVMe ... hmm, dvice name: %s\n", + pr2ws("%s: neither SCSI nor NVMe ... hmm, device name: %s\n", __func__, device_name); return 0; } @@ -393,11 +512,9 @@ construct_scsi_pt_obj_with_fd(int dev_han, int vb) fdc_p = get_fdc_p(ptp); if (fdc_p) { - ptp->is_nvme = fdc_p->is_nvme; - ptp->cam_dev = fdc_p->cam_dev; - ptp->dev_statp = &fdc_p->dev_stat; + ptp->mchanp = fdc_p; #if (HAVE_NVME && (! IGNORE_NVME)) - sntl_init_dev_stat(ptp->dev_statp); + sntl_init_dev_stat(&fdc_p->dev_stat); if (! checked_ev_dsense) { ev_dsense = sg_get_initial_dsense(); checked_ev_dsense = true; @@ -438,11 +555,9 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp) void clear_scsi_pt_obj(struct sg_pt_base * vp) { - bool is_nvme; int dev_han; struct sg_pt_freebsd_scsi * ptp; - struct cam_device* cam_dev; - struct sg_sntl_dev_state_t * dsp; + struct freebsd_dev_channel *fdc_p; if (NULL == vp) { pr2ws(">>>>> %s: NULL pointer given\n", __func__); @@ -451,16 +566,12 @@ clear_scsi_pt_obj(struct sg_pt_base * vp) if ((ptp = &vp->impl)) { if (ptp->ccb) cam_freeccb(ptp->ccb); - is_nvme = ptp->is_nvme; dev_han = ptp->dev_han; - cam_dev = ptp->cam_dev; - dsp = ptp->dev_statp; + fdc_p = ptp->mchanp; memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi)); ptp->dxfer_dir = CAM_DIR_NONE; ptp->dev_han = dev_han; - ptp->is_nvme = is_nvme; - ptp->cam_dev = cam_dev; - ptp->dev_statp = dsp; + ptp->mchanp = fdc_p; } } @@ -468,7 +579,6 @@ void partial_clear_scsi_pt_obj(struct sg_pt_base * vp) { struct sg_pt_freebsd_scsi * ptp = &vp->impl; - struct freebsd_dev_channel *fdc_p; if (NULL == ptp) return; @@ -481,9 +591,7 @@ partial_clear_scsi_pt_obj(struct sg_pt_base * vp) ptp->dxfer_ilen = 0; ptp->dxferop = NULL; ptp->dxfer_olen = 0; - fdc_p = get_fdc_p(ptp); - if (fdc_p) - fdc_p->nvme_result = 0; + ptp->nvme_result = 0; } /* Forget any previous dev_han and install the one given. May attempt to @@ -506,8 +614,6 @@ set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb) if (dev_han < 0) { ptp->dev_han = -1; ptp->dxfer_dir = CAM_DIR_NONE; - ptp->is_nvme = false; - ptp->cam_dev = NULL; return 0; } fdc_p = get_fdc_p(ptp); @@ -523,9 +629,7 @@ set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb) ptp->scsi_status = 0; ptp->dev_han = dev_han; ptp->dxfer_dir = CAM_DIR_NONE; - ptp->is_nvme = fdc_p->is_nvme; - ptp->cam_dev = fdc_p->cam_dev; - ptp->dev_statp = &fdc_p->dev_stat; + ptp->mchanp = fdc_p; } return 0; } @@ -671,7 +775,7 @@ set_scsi_pt_task_attr(struct sg_pt_base * vp, void set_scsi_pt_flags(struct sg_pt_base * objp, int flags) { - if (objp) { ; } /* unused, suppress warning */ + if (objp) { ; } /* unused, suppress warning */ if (flags) { ; } /* unused, suppress warning */ } @@ -684,8 +788,11 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int vb) int len; struct sg_pt_freebsd_scsi * ptp = &vp->impl; struct freebsd_dev_channel *fdc_p; + FILE * ferrp = sg_warnings_strm ? sg_warnings_strm : stderr; union ccb *ccb; + if (vb > 6) + pr2ws("%s: dev_han=%d, time_secs=%d\n", __func__, dev_han, time_secs); ptp->os_err = 0; if (ptp->in_err) { if (vb) @@ -716,21 +823,25 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int vb) pr2ws("No command (cdb) given\n"); return SCSI_PT_DO_BAD_PARAMS; } - if (ptp->is_nvme) - return sg_do_nvme_pt(vp, -1, vb); - fdc_p = get_fdc_p(ptp); + fdc_p = ptp->mchanp; if (NULL == fdc_p) { - if (vb) - pr2ws("File descriptor bad or closed??\n"); - ptp->os_err = ENODEV; - return -ptp->os_err; + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + if (vb) + pr2ws("File descriptor bad or closed??\n"); + ptp->os_err = ENODEV; + return -ptp->os_err; + } + ptp->mchanp = fdc_p; } - ptp->is_nvme = fdc_p->is_nvme; - ptp->dev_statp = &fdc_p->dev_stat; - if (fdc_p->is_nvme) - return sg_do_nvme_pt(vp, -1, vb); +#if (HAVE_NVME && (! IGNORE_NVME)) + if (fdc_p->is_nvme_dev) + return sg_do_nvme_pt(ptp, -1, true /* assume Admin */, time_secs, vb); +#endif + /* SCSI CAM pass-through follows */ + ptp->is_nvme_dev = fdc_p->is_nvme_dev; if (NULL == fdc_p->cam_dev) { if (vb) pr2ws("No open CAM device\n"); @@ -767,11 +878,9 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int vb) if (cam_send_ccb(fdc_p->cam_dev, ccb) < 0) { if (vb) { - warn("error sending SCSI ccb"); - #if __FreeBSD_version > 500000 - cam_error_print(fdc_p->cam_dev, ccb, CAM_ESF_ALL, - CAM_EPF_ALL, stderr); - #endif + pr2serr("%s: cam_send_ccb() error\n", __func__); + CAM_ERROR_PRINT(fdc_p->cam_dev, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, ferrp); } cam_freeccb(ptp->ccb); ptp->ccb = NULL; @@ -797,7 +906,6 @@ do_scsi_pt(struct sg_pt_base * vp, int dev_han, int time_secs, int vb) } else ptp->transport_err = 1; - ptp->cam_dev = fdc_p->cam_dev; // for error processing return 0; } @@ -824,8 +932,9 @@ get_scsi_pt_resid(const struct sg_pt_base * vp) { const struct sg_pt_freebsd_scsi * ptp = &vp->impl; - return ((NULL == ptp) || (ptp->is_nvme && ! ptp->nvme_our_sntl)) ? - 0 : ptp->resid; + if ((NULL == ptp) || (NULL == ptp->mchanp)) + return 0; + return ((ptp->is_nvme_dev && ! ptp->nvme_our_sntl)) ? 0 : ptp->resid; } void @@ -879,14 +988,13 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp) const struct sg_pt_freebsd_scsi * ptp = &vp->impl; if (ptp) { - if (ptp->is_nvme && ! ptp->nvme_our_sntl) { - const struct freebsd_dev_channel *fdc_p; + const struct freebsd_dev_channel * fdc_p = ptp->mchanp; - fdc_p = get_fdc_cp(ptp); - if (NULL == fdc_p) - return -1; - return (int)fdc_p->nvme_status; - } else + if (NULL == fdc_p) + return -1; + if (ptp->is_nvme_dev && ! ptp->nvme_our_sntl) + return (int)ptp->nvme_status; + else return ptp->scsi_status; } return -1; @@ -899,14 +1007,13 @@ get_pt_result(const struct sg_pt_base * vp) const struct sg_pt_freebsd_scsi * ptp = &vp->impl; if (ptp) { - if (ptp->is_nvme && ! ptp->nvme_our_sntl) { - const struct freebsd_dev_channel *fdc_p; + const struct freebsd_dev_channel * fdc_p = ptp->mchanp; - fdc_p = get_fdc_cp(ptp); - if (NULL == fdc_p) - return -1; - return fdc_p->nvme_result; - } else + if (NULL == fdc_p) + return -1; + if (ptp->is_nvme_dev && ! ptp->nvme_our_sntl) + return ptp->nvme_result; + else return (uint32_t)ptp->scsi_status; } return 0xffffffff; @@ -983,15 +1090,15 @@ get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, b[max_b_len - 1] = '\0'; return b; } - if (ptp->is_nvme) { + if (ptp->mchanp && ptp->mchanp->is_nvme_dev) { snprintf(b, max_b_len, "NVMe has no transport errors at present " "but tranport_err=%d ??\n", ptp->transport_err); return b; } #if __FreeBSD_version > 500000 - if (ptp->cam_dev) - cam_error_string(ptp->cam_dev, ptp->ccb, b, max_b_len, CAM_ESF_ALL, - CAM_EPF_ALL); + if (ptp->mchanp && ptp->mchanp->cam_dev) + cam_error_string(ptp->mchanp->cam_dev, ptp->ccb, b, max_b_len, + CAM_ESF_ALL, CAM_EPF_ALL); else { strncpy(b, "no transport error available", max_b_len); b[max_b_len - 1] = '\0'; @@ -1017,10 +1124,7 @@ pt_device_is_nvme(const struct sg_pt_base * vp) errno = ENODEV; return false; } - /* if unequal, cast away const and drive fdc_p value into ptp */ - if (ptp->is_nvme != fdc_p->is_nvme) /* indicates logic error */ - ((struct sg_pt_freebsd_scsi *)ptp)->is_nvme = fdc_p->is_nvme; - return fdc_p->is_nvme; + return fdc_p->is_nvme_dev; } return false; } @@ -1062,7 +1166,18 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) #define SCSI_MAINT_IN_OPC 0xa3 #define SCSI_MODE_SENSE10_OPC 0x5a #define SCSI_MODE_SELECT10_OPC 0x55 +#define SCSI_READ10_OPC 0x28 +#define SCSI_READ16_OPC 0x88 #define SCSI_READ_CAPACITY10_OPC 0x25 +#define SCSI_START_STOP_OPC 0x1b +#define SCSI_SYNC_CACHE10_OPC 0x35 +#define SCSI_SYNC_CACHE16_OPC 0x91 +#define SCSI_VERIFY10_OPC 0x2f +#define SCSI_VERIFY16_OPC 0x8f +#define SCSI_WRITE10_OPC 0x2a +#define SCSI_WRITE16_OPC 0x8a +#define SCSI_WRITE_SAME10_OPC 0x41 +#define SCSI_WRITE_SAME16_OPC 0x93 #define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c #define SCSI_REP_SUP_OPCS_OPC 0xc #define SCSI_REP_SUP_TMFS_OPC 0xd @@ -1102,6 +1217,26 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) #define MISCOMPARE_VERIFY_ASC 0x1d #define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */ #define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16 +#define PCIE_ERR_ASC 0x4b +#define PCIE_UNSUPP_REQ_ASCQ 0x13 + +/* NVMe Admin commands */ +#define SG_NVME_AD_GET_FEATURE 0xa +#define SG_NVME_AD_SET_FEATURE 0x9 +#define SG_NVME_AD_IDENTIFY 0x6 /* similar to SCSI INQUIRY */ +#define SG_NVME_AD_DEV_SELT_TEST 0x14 +#define SG_NVME_AD_MI_RECEIVE 0x1e /* MI: Management Interface */ +#define SG_NVME_AD_MI_SEND 0x1d /* hmmm, same opcode as SEND DIAG */ + +/* NVMe NVM (Non-Volatile Memory) commands */ +#define SG_NVME_NVM_FLUSH 0x0 /* SCSI SYNCHRONIZE CACHE */ +#define SG_NVME_NVM_COMPARE 0x5 /* SCSI VERIFY(BYTCHK=1) */ +#define SG_NVME_NVM_READ 0x2 +#define SG_NVME_NVM_VERIFY 0xc /* SCSI VERIFY(BYTCHK=0) */ +#define SG_NVME_NVM_WRITE 0x1 +#define SG_NVME_NVM_WRITE_ZEROES 0x8 /* SCSI WRITE SAME */ + +#define SG_NVME_RW_CDW12_FUA (1 << 30) /* Force Unit Access bit */ #if (HAVE_NVME && (! IGNORE_NVME)) @@ -1109,7 +1244,7 @@ static void mk_sense_asc_ascq(struct sg_pt_freebsd_scsi * ptp, int sk, int asc, int ascq, int vb) { - bool dsense = ptp->dev_statp->scsi_dsense; + bool dsense = ptp->mchanp ? ptp->mchanp->dev_stat.scsi_dsense : false; int n; uint8_t * sbp = ptp->sense; @@ -1133,7 +1268,7 @@ mk_sense_from_nvme_status(struct sg_pt_freebsd_scsi * ptp, uint16_t sct_sc, int vb) { bool ok; - bool dsense = ptp->dev_statp->scsi_dsense; + bool dsense = ptp->mchanp ? ptp->mchanp->dev_stat.scsi_dsense : false; int n; uint8_t sstatus, sk, asc, ascq; uint8_t * sbp = ptp->sense; @@ -1171,7 +1306,7 @@ static void mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb, int in_byte, int in_bit, int vb) { - bool ds = ptp->dev_statp->scsi_dsense; + bool ds = ptp->mchanp ? ptp->mchanp->dev_stat.scsi_dsense : false; int sl, asc, n; uint8_t * sbp = (uint8_t *)ptp->sense; uint8_t sks[4]; @@ -1210,50 +1345,189 @@ mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb, ((in_bit > 0) ? (0x7 & in_bit) : 0)); } -/* Does actual ioctl(NVME_PASSTHROUGH_CMD). Returns 0 on success; negative - * values are Unix negated errno values; positive values are NVMe status - * (i.e. ((SCT << 8) | SC) ). */ +#if 0 +static void +nvme_cbfcn(struct cam_periph * camperp, union ccb * ccb) +{ + pr2ws("%s: >>>> called, camperp=%p, ccb=%p\n", __func__, camperp, ccb); +} +#endif + +/* Does actual ioctl(NVME_PASSTHROUGH_CMD) or uses NVME(CAM) interface. + * Returns 0 on success; negative values are Unix negated errno values; + * positive values are NVMe status (i.e. ((SCT << 8) | SC) ). */ static int -nvme_pt_low(struct freebsd_dev_channel *fdc_p, void * dxferp, uint32_t len, - bool is_read, struct nvme_pt_command * npcp, int vb) +nvme_pt_low(struct sg_pt_freebsd_scsi * ptp, void * dxferp, uint32_t len, + bool is_admin, bool is_read, struct nvme_pt_command * npcp, + int time_secs, int vb) { - int err; + int err, dev_fd; uint16_t sct_sc; uint8_t opcode; + struct freebsd_dev_channel *fdc_p = ptp->mchanp; + FILE * ferrp = sg_warnings_strm ? sg_warnings_strm : stderr; char b[80]; - if (fdc_p->dev_fd < 0) { - if (vb) - pr2ws("%s: is_nvme is true but dev_fd<0, inconsistent\n", - __func__); - return -EINVAL; - } + if (vb > 6) + pr2ws("%s: is_read=%d, time_secs=%d, is_cam_nvme=%d, is_admin=%d\n", + __func__, (int)is_read, time_secs, (int)fdc_p->is_cam_nvme, + (int)is_admin); + ptp->is_nvme_dev = fdc_p->is_nvme_dev; npcp->buf = dxferp; npcp->len = len; npcp->is_read = (uint32_t)is_read; opcode = npcp->cmd.opc; - err = ioctl(fdc_p->dev_fd, NVME_PASSTHROUGH_CMD, npcp); - if (err < 0) - return -errno; /* Assume Unix error in normal place */ + if (fdc_p->is_cam_nvme) + goto cam_nvme; + + if (is_admin) { + if (fdc_p->nvme_fd_ctrl < 0) { + if (vb) + pr2ws("%s: not CAM but nvme_fd_ctrl<0, inconsistent\n", + __func__); + return -EINVAL; + } + dev_fd = fdc_p->nvme_fd_ctrl; + } else { + if (fdc_p->nvme_fd_ns < 0) { + if (vb) + pr2ws("%s: not CAM but nvme_fd_ns<0, inconsistent\n", + __func__); + return -EINVAL; + } + dev_fd = fdc_p->nvme_fd_ns; + } + err = ioctl(dev_fd, NVME_PASSTHROUGH_CMD, npcp); + if (err < 0) { + err = errno; + if (vb) + pr2ws("%s: ioctl(NVME_PASSTHROUGH_CMD) errno: %s\n", __func__, + strerror(err)); + /* when that ioctl returns an error npcp->cpl is not populated */ + return -err; + } + +#if __FreeBSD_version <= 1200058 + sct_sc = ((npcp->cpl.status.sct << 8) | npcp->cpl.status.sc); +#else sct_sc = (NVME_STATUS_GET_SCT(npcp->cpl.status) << 8) | NVME_STATUS_GET_SC(npcp->cpl.status); - fdc_p->nvme_result = npcp->cpl.cdw0; +#endif + ptp->nvme_result = npcp->cpl.cdw0; sg_put_unaligned_le32(npcp->cpl.cdw0, - fdc_p->cq_dw0_3 + SG_NVME_PT_CQ_RESULT); - sg_put_unaligned_le32(npcp->cpl.rsvd1, fdc_p->cq_dw0_3 + 4); - sg_put_unaligned_le16(npcp->cpl.sqhd, fdc_p->cq_dw0_3 + 8); - sg_put_unaligned_le16(npcp->cpl.sqid, fdc_p->cq_dw0_3 + 10); - sg_put_unaligned_le16(npcp->cpl.cid, fdc_p->cq_dw0_3 + 12); + ptp->cq_dw0_3 + SG_NVME_PT_CQ_RESULT); + sg_put_unaligned_le32(npcp->cpl.rsvd1, ptp->cq_dw0_3 + 4); + sg_put_unaligned_le16(npcp->cpl.sqhd, ptp->cq_dw0_3 + 8); + sg_put_unaligned_le16(npcp->cpl.sqid, ptp->cq_dw0_3 + 10); + sg_put_unaligned_le16(npcp->cpl.cid, ptp->cq_dw0_3 + 12); sg_put_unaligned_le16(*((const uint16_t *)&(npcp->cpl.status)), - fdc_p->cq_dw0_3 + SG_NVME_PT_CQ_STATUS_P); + ptp->cq_dw0_3 + SG_NVME_PT_CQ_STATUS_P); if (sct_sc && (vb > 1)) { char nam[64]; - sg_get_nvme_opcode_name(opcode, true, sizeof(nam), nam); + sg_get_nvme_opcode_name(opcode, is_admin, sizeof(nam), nam); pr2ws("%s: %s [0x%x], status: %s\n", __func__, nam, opcode, sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b)); } return sct_sc; + +cam_nvme: + { + cam_status ccb_status; + union ccb *ccb; + struct ccb_nvmeio *nviop; + + if (NULL == ptp->ccb) { /* re-use if we have one already */ + if (! (ccb = cam_getccb(fdc_p->cam_dev))) { + if (vb) + pr2ws("%s: cam_getccb: failed\n", __func__); + ptp->os_err = ENOMEM; + return -ptp->os_err; + } + ptp->ccb = ccb; + } else + ccb = ptp->ccb; + nviop = &ccb->nvmeio; + CCB_CLEAR_ALL_EXCEPT_HDR(nviop); + + memcpy(&nviop->cmd, &npcp->cmd, sizeof(nviop->cmd)); + ptp->timeout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT; + if (is_admin) + cam_fill_nvmeadmin(nviop, + 1 /* retries */, + NULL, + is_read ? CAM_DIR_IN : CAM_DIR_OUT, + dxferp, + len, + ptp->timeout_ms); + + else { /* NVM command set, rather than Admin */ + if (fdc_p->nsid != npcp->cmd.nsid) { + if (vb) + pr2ws("%s: device node nsid [%u] not equal to cmd nsid " + "[%u]\n", __func__, fdc_p->nsid, npcp->cmd.nsid); + return -EINVAL; + } + cam_fill_nvmeio(nviop, + 1 /* retries */, + NULL, + is_read ? CAM_DIR_IN : CAM_DIR_OUT, + dxferp, + len, + ptp->timeout_ms); + } + + if (cam_send_ccb(fdc_p->cam_dev, ccb) < 0) { + if (vb) { + pr2ws("%s: cam_send_ccb(NVME) %s ccb erro\n", __func__, + (is_admin ? "Admin" : "NVM")); + CAM_ERROR_PRINT(fdc_p->cam_dev, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, ferrp); + } + cam_freeccb(ptp->ccb); + ptp->ccb = NULL; + ptp->os_err = EIO; + return -ptp->os_err; + } + ccb_status = ccb->ccb_h.status & CAM_STATUS_MASK; + if (ccb_status == CAM_REQ_CMP) { + ptp->nvme_result = 0; + ptp->os_err = 0; + return 0; + } + /* error processing follows ... */ + ptp->os_err = EIO; + if (vb) { + pr2ws("%s: ccb_status != CAM_REQ_CMP\n", __func__); + CAM_ERROR_PRINT(fdc_p->cam_dev, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, ferrp); + } +#if __FreeBSD_version <= 1200058 + sct_sc = ((nviop->cpl.status.sct << 8) | nviop->cpl.status.sc); +#else + sct_sc = (NVME_STATUS_GET_SCT(nviop->cpl.status) << 8) | + NVME_STATUS_GET_SC(nviop->cpl.status); +#endif + ptp->nvme_result = nviop->cpl.cdw0; + sg_put_unaligned_le32(nviop->cpl.cdw0, + ptp->cq_dw0_3 + SG_NVME_PT_CQ_RESULT); + sg_put_unaligned_le32(nviop->cpl.rsvd1, ptp->cq_dw0_3 + 4); + sg_put_unaligned_le16(nviop->cpl.sqhd, ptp->cq_dw0_3 + 8); + sg_put_unaligned_le16(nviop->cpl.sqid, ptp->cq_dw0_3 + 10); + sg_put_unaligned_le16(nviop->cpl.cid, ptp->cq_dw0_3 + 12); + sg_put_unaligned_le16(*((const uint16_t *)&(nviop->cpl.status)), + ptp->cq_dw0_3 + SG_NVME_PT_CQ_STATUS_P); + if (sct_sc && (vb > 1)) { + char nam[64]; + + sg_get_nvme_opcode_name(opcode, is_admin, sizeof(nam), + nam); + pr2ws("%s: %s [0x%x], status: %s\n", __func__, nam, opcode, + sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b)); + } + return sct_sc ? sct_sc : ptp->os_err; + } + return 0; } static void @@ -1265,7 +1539,7 @@ sntl_check_enclosure_override(struct freebsd_dev_channel * fdc_p, int vb) if (NULL == up) return; nvmsr = up[253]; - if (vb > 3) + if (vb > 5) pr2ws("%s: enter, nvmsr=%u\n", __func__, nvmsr); fdc_p->dev_stat.id_ctl253 = nvmsr; switch (fdc_p->dev_stat.enclosure_override) { @@ -1310,29 +1584,31 @@ sntl_check_enclosure_override(struct freebsd_dev_channel * fdc_p, int vb) } static int -sntl_do_identify(struct freebsd_dev_channel * fdc_p, int cns, int nsid, - int u_len, uint8_t * up, int vb) +sntl_do_identify(struct sg_pt_freebsd_scsi * ptp, int cns, int nsid, + int u_len, uint8_t * up, int time_secs, int vb) { int err; struct nvme_pt_command npc; uint8_t * npc_up = (uint8_t *)&npc; + if (vb > 5) + pr2ws("%s: nsid=%d\n", __func__, nsid); memset(npc_up, 0, sizeof(npc)); - npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */ + npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_IDENTIFY; sg_put_unaligned_le32(nsid, npc_up + SG_NVME_PT_NSID); /* CNS=0x1 Identify: controller */ sg_put_unaligned_le32(cns, npc_up + SG_NVME_PT_CDW10); sg_put_unaligned_le64((sg_uintptr_t)up, npc_up + SG_NVME_PT_ADDR); sg_put_unaligned_le32(u_len, npc_up + SG_NVME_PT_DATA_LEN); - err = nvme_pt_low(fdc_p, up, u_len, true, &npc, vb); + err = nvme_pt_low(ptp, up, u_len, true, true, &npc, time_secs, vb); if (err) { if (err < 0) { if (vb > 1) - pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__, + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__, strerror(-err), -err); return err; } else { /* non-zero NVMe command status */ - fdc_p->nvme_status = err; + ptp->nvme_status = err; return SG_LIB_NVME_STATUS; } } @@ -1341,10 +1617,11 @@ sntl_do_identify(struct freebsd_dev_channel * fdc_p, int cns, int nsid, /* Currently only caches associated controller response (4096 bytes) */ static int -sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb) +sntl_cache_identity(struct sg_pt_freebsd_scsi * ptp, int time_secs, int vb) { int ret; uint32_t pg_sz = sg_get_page_size(); + struct freebsd_dev_channel * fdc_p = ptp->mchanp; fdc_p->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz, &fdc_p->free_nvme_id_ctlp, vb > 3); @@ -1352,8 +1629,8 @@ sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb) pr2ws("%s: sg_memalign() failed to get memory\n", __func__); return -ENOMEM; } - ret = sntl_do_identify(fdc_p, 0x1 /* CNS */, 0 /* nsid */, pg_sz, - fdc_p->nvme_id_ctlp, vb); + ret = sntl_do_identify(ptp, 0x1 /* CNS */, 0 /* nsid */, pg_sz, + fdc_p->nvme_id_ctlp, time_secs, vb); if (0 == ret) sntl_check_enclosure_override(fdc_p, vb); return (ret < 0) ? sg_convert_errno(-ret) : ret; @@ -1363,7 +1640,8 @@ static const char * nvme_scsi_vendor_str = "NVMe "; static const uint16_t inq_resp_len = 36; static int -sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs, + int vb) { bool evpd; bool cp_id_ctl = false; @@ -1375,7 +1653,7 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) uint8_t * free_nvme_id_ns = NULL; uint8_t inq_dout[256]; - if (vb > 3) + if (vb > 5) pr2ws("%s: starting\n", __func__); if (0x2 & cdbp[1]) { /* Reject CmdDt=1 */ @@ -1388,9 +1666,9 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) return -EINVAL; } if (NULL == fdc_p->nvme_id_ctlp) { - res = sntl_cache_identity(fdc_p, vb); + res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { - mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb); return 0; } else if (res) /* should be negative errno */ return res; @@ -1430,7 +1708,7 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) uint8_t * npc_up = (uint8_t *)&npc; memset(npc_up, 0, sizeof(npc)); - npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */ + npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_IDENTIFY; sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); /* CNS=0x0 Identify: namespace */ @@ -1439,8 +1717,8 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) npc_up + SG_NVME_PT_ADDR); sg_put_unaligned_le32(pg_sz, npc_up + SG_NVME_PT_DATA_LEN); - res = nvme_pt_low(fdc_p, nvme_id_ns, pg_sz, true, &npc, - vb > 3); + res = nvme_pt_low(ptp, nvme_id_ns, pg_sz, true, true, + &npc, time_secs, vb > 3); if (res) { free(free_nvme_id_ns); free_nvme_id_ns = NULL; @@ -1528,7 +1806,8 @@ sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) } static int -sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) { int res; uint16_t sel_report; @@ -1537,7 +1816,7 @@ sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) uint8_t * rl_doutp; uint8_t * up; - if (vb > 3) + if (vb > 5) pr2ws("%s: starting\n", __func__); fdc_p = get_fdc_p(ptp); if (NULL == fdc_p) { @@ -1547,9 +1826,9 @@ sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) sel_report = cdbp[2]; alloc_len = sg_get_unaligned_be32(cdbp + 6); if (NULL == fdc_p->nvme_id_ctlp) { - res = sntl_cache_identity(fdc_p, vb); + res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { - mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb); return 0; } else if (res) return res; @@ -1598,7 +1877,7 @@ sntl_rluns(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) } static int -sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb) +sntl_tur(struct sg_pt_freebsd_scsi * ptp, int time_secs, int vb) { int res, err; uint32_t pow_state; @@ -1606,7 +1885,7 @@ sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb) uint8_t * npc_up = (uint8_t *)&npc; struct freebsd_dev_channel * fdc_p; - if (vb > 3) + if (vb > 5) pr2ws("%s: starting\n", __func__); fdc_p = get_fdc_p(ptp); if (NULL == fdc_p) { @@ -1614,32 +1893,32 @@ sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb) return -EINVAL; } if (NULL == fdc_p->nvme_id_ctlp) { - res = sntl_cache_identity(fdc_p, vb); + res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { - mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb); return 0; } else if (res) return res; } memset(npc_up, 0, sizeof(npc)); - npc_up[SG_NVME_PT_OPCODE] = 0xa; /* Get feature */ + npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_GET_FEATURE; sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, npc_up + SG_NVME_PT_NSID); /* SEL=0 (current), Feature=2 Power Management */ sg_put_unaligned_le32(0x2, npc_up + SG_NVME_PT_CDW10); - err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb); + err = nvme_pt_low(ptp, NULL, 0, true, false, &npc, time_secs, vb); if (err) { if (err < 0) { if (vb > 1) - pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__, + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__, strerror(-err), -err); return err; } else { - fdc_p->nvme_status = err; + ptp->nvme_status = err; mk_sense_from_nvme_status(ptp, err, vb); return 0; } } - pow_state = (0x1f & fdc_p->nvme_result); + pow_state = (0x1f & ptp->nvme_result); if (vb > 3) pr2ws("%s: pow_state=%u\n", __func__, pow_state); #if 0 /* pow_state bounces around too much on laptop */ @@ -1651,7 +1930,8 @@ sntl_tur(struct sg_pt_freebsd_scsi * ptp, int vb) } static int -sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) { bool desc; int res, err; @@ -1661,7 +1941,7 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) struct freebsd_dev_channel * fdc_p; uint8_t rs_dout[64]; - if (vb > 3) + if (vb > 5) pr2ws("%s: starting\n", __func__); fdc_p = get_fdc_p(ptp); if (NULL == fdc_p) { @@ -1669,9 +1949,9 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) return -EINVAL; } if (NULL == fdc_p->nvme_id_ctlp) { - res = sntl_cache_identity(fdc_p, vb); + res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { - mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb); return 0; } else if (res) return res; @@ -1679,24 +1959,24 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) desc = !!(0x1 & cdbp[1]); alloc_len = cdbp[4]; memset(npc_up, 0, sizeof(npc)); - npc_up[SG_NVME_PT_OPCODE] = 0xa; /* Get feature */ + npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_GET_FEATURE; sg_put_unaligned_le32(SG_NVME_BROADCAST_NSID, npc_up + SG_NVME_PT_NSID); /* SEL=0 (current), Feature=2 Power Management */ sg_put_unaligned_le32(0x2, npc_up + SG_NVME_PT_CDW10); - err = nvme_pt_low(fdc_p, NULL, 0, false, &npc, vb); + err = nvme_pt_low(ptp, NULL, 0, true, false, &npc, time_secs, vb); if (err) { if (err < 0) { if (vb > 1) - pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__, + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__, strerror(-err), -err); return err; } else { - fdc_p->nvme_status = err; + ptp->nvme_status = err; mk_sense_from_nvme_status(ptp, err, vb); return 0; } } - pow_state = (0x1f & fdc_p->nvme_result); + pow_state = (0x1f & ptp->nvme_result); if (vb > 3) pr2ws("%s: pow_state=%u\n", __func__, pow_state); memset(rs_dout, 0, sizeof(rs_dout)); @@ -1716,7 +1996,8 @@ sntl_req_sense(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) } static int -sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) { bool is_msense = (SCSI_MODE_SENSE10_OPC == cdbp[0]); int res, n, len; @@ -1724,7 +2005,7 @@ sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) struct freebsd_dev_channel * fdc_p; struct sg_sntl_result_t sntl_result; - if (vb > 3) + if (vb > 5) pr2ws("%s: mse%s\n", __func__, (is_msense ? "nse" : "lect")); fdc_p = get_fdc_p(ptp); if (NULL == fdc_p) { @@ -1732,9 +2013,9 @@ sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) return -EINVAL; } if (NULL == fdc_p->nvme_id_ctlp) { - res = sntl_cache_identity(fdc_p, vb); + res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { - mk_sense_from_nvme_status(ptp, fdc_p->nvme_status, vb); + mk_sense_from_nvme_status(ptp, ptp->nvme_status, vb); return 0; } else if (res) return res; @@ -1781,7 +2062,8 @@ sntl_mode_ss(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) * (SCSI Enclosure Services) use of diagnostics pages that are * related to SES. */ static int -sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) { bool pf, self_test; int err; @@ -1795,7 +2077,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) st_cd = 0x7 & (cdbp[1] >> 5); pf = !! (0x4 & cdbp[1]); self_test = !! (0x10 & cdbp[1]); - if (vb > 3) + if (vb > 5) pr2ws("%s: pf=%d, self_test=%d, st_code=%d\n", __func__, (int)pf, (int)self_test, (int)st_cd); fdc_p = get_fdc_p(ptp); @@ -1805,7 +2087,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) } if (self_test || st_cd) { memset(npc_up, 0, sizeof(npc)); - npc_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */ + npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_DEV_SELT_TEST; /* just this namespace (if there is one) and controller */ sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); switch (st_cd) { @@ -1827,7 +2109,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) return 0; } sg_put_unaligned_le32(nvme_dst, npc_up + SG_NVME_PT_CDW10); - err = nvme_pt_low(fdc_p, NULL, 0x0, false, &npc, vb); + err = nvme_pt_low(ptp, NULL, 0x0, true, false, &npc, time_secs, vb); goto do_low; } alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ @@ -1877,7 +2159,7 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n", __func__, dpg_cd, dpg_len); memset(npc_up, 0, sizeof(npc)); - npc_up[SG_NVME_PT_OPCODE] = 0x1d; /* MI send; same opcode as SEND DIAG */ + npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_MI_SEND; sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp, npc_up + SG_NVME_PT_ADDR); /* NVMe 4k page size. Maybe determine this? */ @@ -1889,16 +2171,17 @@ sntl_senddiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) sg_put_unaligned_le32(0x9, npc_up + SG_NVME_PT_CDW11); /* data-out length I hope */ sg_put_unaligned_le32(n, npc_up + SG_NVME_PT_CDW13); - err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, false, &npc, vb); + err = nvme_pt_low(ptp, ptp->dxferp, 0x1000, true, false, &npc, time_secs, + vb); do_low: if (err) { if (err < 0) { if (vb > 1) - pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__, strerror(-err), -err); return err; } else { - fdc_p->nvme_status = err; + ptp->nvme_status = err; mk_sense_from_nvme_status(ptp, err, vb); return 0; } @@ -1912,7 +2195,8 @@ do_low: * SES (SCSI Enclosure Services) use of diagnostics pages that are * related to SES. */ static int -sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) { bool pcv; int err; @@ -1926,7 +2210,7 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) pcv = !! (0x1 & cdbp[1]); dpg_cd = cdbp[2]; alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ - if (vb > 3) + if (vb > 5) pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__, dpg_cd, (int)pcv, alloc_len); fdc_p = get_fdc_p(ptp); @@ -1971,7 +2255,7 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__, dpg_cd); memset(npc_up, 0, sizeof(npc)); - npc_up[SG_NVME_PT_OPCODE] = 0x1e; /* MI receive */ + npc_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_MI_RECEIVE; sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferp, npc_up + SG_NVME_PT_ADDR); /* NVMe 4k page size. Maybe determine this? */ @@ -1984,15 +2268,16 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) sg_put_unaligned_le32(dpg_cd, npc_up + SG_NVME_PT_CDW12); /* data-in length I hope */ sg_put_unaligned_le32(n, npc_up + SG_NVME_PT_CDW13); - err = nvme_pt_low(fdc_p, ptp->dxferp, 0x1000, true, &npc, vb); + err = nvme_pt_low(ptp, ptp->dxferp, 0x1000, true, true, &npc, time_secs, + vb); if (err) { if (err < 0) { if (vb > 1) - pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__, strerror(-err), -err); return err; } else { - fdc_p->nvme_status = err; + ptp->nvme_status = err; mk_sense_from_nvme_status(ptp, err, vb); return 0; } @@ -2008,7 +2293,7 @@ sntl_recvdiag(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) static int sntl_rep_opcodes(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, - int vb) + int time_secs, int vb) { bool rctd; uint8_t reporting_opts, req_opcode, supp; @@ -2020,8 +2305,8 @@ sntl_rep_opcodes(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, uint8_t *arr; uint8_t *free_arr; - if (vb > 3) - pr2ws("%s: start\n", __func__); + if (vb > 5) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); rctd = !!(cdbp[2] & 0x80); /* report command timeout desc. */ reporting_opts = cdbp[2] & 0x7; req_opcode = cdbp[3]; @@ -2122,14 +2407,15 @@ sntl_rep_opcodes(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, } static int -sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) { bool repd; uint32_t alloc_len, len; uint8_t arr[16]; - if (vb > 3) - pr2ws("%s: start\n", __func__); + if (vb > 5) + pr2ws("%s: time_secs=%d\n", __func__, time_secs); memset(arr, 0, sizeof(arr)); repd = !!(cdbp[2] & 0x80); alloc_len = sg_get_unaligned_be32(cdbp + 6); @@ -2152,6 +2438,324 @@ sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) return 0; } +static int +sntl_rread(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_read10 = (SCSI_READ10_OPC == cdbp[0]); + bool have_fua = !!(cdbp[1] & 0x8); + int err; + uint32_t nblks_t10 = 0; + uint64_t lba; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + + if (vb > 5) + pr2ws("%s: fua=%d\n", __func__, (int)have_fua); + fdc_p = get_fdc_p(ptp); + memset(&npc, 0, sizeof(npc)); + npc.cmd.opc = SG_NVME_NVM_READ; + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + if (is_read10) { + lba = sg_get_unaligned_be32(cdbp + 2); + nblks_t10 = sg_get_unaligned_be16(cdbp + 7); + } else { + lba = sg_get_unaligned_be64(cdbp + 2); + nblks_t10 = sg_get_unaligned_be32(cdbp + 10); + if (nblks_t10 > (UINT16_MAX + 1)) { + mk_sense_invalid_fld(ptp, true, 11, -1, vb); + return 0; + } + } + if (0 == nblks_t10) { /* NOP in SCSI */ + if (vb > 4) + pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n", + __func__); + return 0; + } + --nblks_t10; /* crazy "0's based" counts */ + sg_put_unaligned_le64(lba, npc_up + SG_NVME_PT_CDW10); /* fills W11 too */ + if (have_fua) + nblks_t10 |= SG_NVME_RW_CDW12_FUA; + sg_put_unaligned_le32(nblks_t10, npc_up + SG_NVME_PT_CDW12); + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + + err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, true, &npc, + time_secs, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", + __func__, strerror(-err), -err); + return err; + } else { + ptp->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + ptp->resid = 0; /* hoping */ + return 0; +} + +static int +sntl_write(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_write10 = (SCSI_WRITE10_OPC == cdbp[0]); + bool have_fua = !!(cdbp[1] & 0x8); + int err; + uint32_t nblks_t10 = 0; + uint64_t lba; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + + if (vb > 5) + pr2ws("%s: fua=%d, time_secs=%d\n", __func__, (int)have_fua, + time_secs); + fdc_p = get_fdc_p(ptp); + memset(&npc, 0, sizeof(npc)); + npc.cmd.opc = SG_NVME_NVM_WRITE; + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + if (is_write10) { + lba = sg_get_unaligned_be32(cdbp + 2); + nblks_t10 = sg_get_unaligned_be16(cdbp + 7); + } else { + lba = sg_get_unaligned_be64(cdbp + 2); + nblks_t10 = sg_get_unaligned_be32(cdbp + 10); + if (nblks_t10 > (UINT16_MAX + 1)) { + mk_sense_invalid_fld(ptp, true, 11, -1, vb); + return 0; + } + } + if (0 == nblks_t10) { /* NOP in SCSI */ + if (vb > 4) + pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n", + __func__); + return 0; + } + --nblks_t10; + sg_put_unaligned_le64(lba, npc_up + SG_NVME_PT_CDW10); /* fills W11 too */ + if (have_fua) + nblks_t10 |= SG_NVME_RW_CDW12_FUA; + sg_put_unaligned_le32(nblks_t10, npc_up + SG_NVME_PT_CDW12); + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + + err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, false, &npc, + time_secs, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", + __func__, strerror(-err), -err); + return err; + } else { + ptp->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + ptp->resid = 0; + return 0; +} + +static int +sntl_verify(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_verify10 = (SCSI_VERIFY10_OPC == cdbp[0]); + uint8_t bytchk = (cdbp[1] >> 1) & 0x3; + int err; + uint32_t nblks_t10 = 0; + uint64_t lba; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + + if (vb > 5) + pr2ws("%s: bytchk=%d, time_secs=%d\n", __func__, bytchk, time_secs); + if (bytchk > 1) { + mk_sense_invalid_fld(ptp, true, 1, 2, vb); + return 0; + } + fdc_p = get_fdc_p(ptp); + memset(&npc, 0, sizeof(npc)); + npc.cmd.opc = bytchk ? SG_NVME_NVM_COMPARE : SG_NVME_NVM_VERIFY; + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + if (is_verify10) { + lba = sg_get_unaligned_be32(cdbp + 2); + nblks_t10 = sg_get_unaligned_be16(cdbp + 7); + } else { + lba = sg_get_unaligned_be64(cdbp + 2); + nblks_t10 = sg_get_unaligned_be32(cdbp + 10); + if (nblks_t10 > (UINT16_MAX + 1)) { + mk_sense_invalid_fld(ptp, true, 11, -1, vb); + return 0; + } + } + if (0 == nblks_t10) { /* NOP in SCSI */ + if (vb > 4) + pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n", + __func__); + return 0; + } + --nblks_t10; + sg_put_unaligned_le64(lba, npc_up + SG_NVME_PT_CDW10); /* fills W11 too */ + sg_put_unaligned_le32(nblks_t10, npc_up + SG_NVME_PT_CDW12); + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + + err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, false, &npc, + time_secs, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", + __func__, strerror(-err), -err); + return err; + } else { + ptp->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + ptp->resid = 0; + return 0; +} + +static int +sntl_write_same(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool is_ws10 = (SCSI_WRITE_SAME10_OPC == cdbp[0]); + bool ndob = is_ws10 ? false : !!(0x1 & cdbp[1]); + int err; + int nblks_t10 = 0; + uint64_t lba; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + + if (vb > 5) + pr2ws("%s: ndob=%d, time_secs=%d\n", __func__, (int)ndob, time_secs); + if (! ndob) { + int flbas, index, lbafx, lbads, lbsize; + uint8_t * up; + uint8_t * dp; + + dp = ptp->dxferp; + up = ptp->mchanp->nvme_id_ctlp; + if ((dp == NULL) || (up == NULL)) + return sg_convert_errno(ENOMEM); + flbas = up[26]; /* NVME FLBAS field from Identify */ + index = 128 + (4 * (flbas & 0xf)); + lbafx = sg_get_unaligned_le32(up + index); + lbads = (lbafx >> 16) & 0xff; /* bits 16 to 23 inclusive, pow2 */ + lbsize = 1 << lbads; + if (! sg_all_zeros(dp, lbsize)) { + mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, PCIE_ERR_ASC, + PCIE_UNSUPP_REQ_ASCQ, vb); + return 0; + } + /* so given single LB full of zeros, can translate .... */ + } + fdc_p = ptp->mchanp; + memset(&npc, 0, sizeof(npc)); + npc.cmd.opc = SG_NVME_NVM_WRITE_ZEROES; + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + if (is_ws10) { + lba = sg_get_unaligned_be32(cdbp + 2); + nblks_t10 = sg_get_unaligned_be16(cdbp + 7); + } else { + uint32_t num = sg_get_unaligned_be32(cdbp + 10); + + lba = sg_get_unaligned_be64(cdbp + 2); + if (num > (UINT16_MAX + 1)) { + mk_sense_invalid_fld(ptp, true, 11, -1, vb); + return 0; + } else + nblks_t10 = num; + } + if (0 == nblks_t10) { /* NOP in SCSI */ + if (vb > 4) + pr2ws("%s: nblks_t10 is 0, a NOP in SCSI, can't map to NVMe\n", + __func__); + return 0; + } + --nblks_t10; + sg_put_unaligned_le64(lba, npc_up + SG_NVME_PT_CDW10); /* fills W11 too */ + sg_put_unaligned_le32(nblks_t10, npc_up + SG_NVME_PT_CDW12); + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + + err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, false, &npc, + time_secs, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", + __func__, strerror(-err), -err); + return err; + } else { + ptp->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + ptp->resid = 0; + return 0; +} + +static int +sntl_sync_cache(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool immed = !!(0x2 & cdbp[1]); + int err; + struct nvme_pt_command npc; + uint8_t * npc_up = (uint8_t *)&npc; + struct freebsd_dev_channel * fdc_p; + + if (vb > 5) + pr2ws("%s: immed=%d, time_secs=%d\n", __func__, (int)immed, + time_secs); + fdc_p = ptp->mchanp; + memset(&npc, 0, sizeof(npc)); + npc.cmd.opc = SG_NVME_NVM_FLUSH; + sg_put_unaligned_le32(fdc_p->nsid, npc_up + SG_NVME_PT_NSID); + if (vb > 4) + pr2ws("%s: immed bit, lba and num_lbs fields ignored\n", __func__); + err = nvme_pt_low(ptp, ptp->dxferp, ptp->dxfer_len, false, false, &npc, + time_secs, vb); + if (err) { + if (err < 0) { + if (vb > 1) + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", + __func__, strerror(-err), -err); + return err; + } else { + ptp->nvme_status = err; + mk_sense_from_nvme_status(ptp, err, vb); + return 0; + } + } + ptp->resid = 0; + return 0; +} + +static int +sntl_start_stop(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) +{ + bool immed = !!(0x1 & cdbp[1]); + + if (vb > 5) + pr2ws("%s: immed=%d, time_secs=%d, ignore\n", __func__, (int)immed, + time_secs); + if (ptp) { } /* suppress warning */ + return 0; +} + /* Note that the "Returned logical block address" (RLBA) field in the SCSI * READ CAPACITY (10+16) command's response provides the address of the _last_ * LBA (counting origin 0) which will be one less that the "size" in the @@ -2160,7 +2764,8 @@ sntl_rep_tmfs(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) * (implying a 1 LB logical units size) pending further research. The LBLIB * is the "Logical Block Length In Bytes" field in the RCAP response. */ static int -sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) +sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) { bool is_rcap10 = (SCSI_READ_CAPACITY10_OPC == cdbp[0]); int res, n, len, alloc_len, dps; @@ -2174,9 +2779,9 @@ sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) struct freebsd_dev_channel * fdc_p; uint8_t resp[32]; - if (vb > 3) + if (vb > 5) pr2ws("%s: RCAP%d\n", __func__, (is_rcap10 ? 10 : 16)); - fdc_p = get_fdc_p(ptp); + fdc_p = ptp->mchanp; if (NULL == fdc_p) { pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); return -EINVAL; @@ -2186,8 +2791,8 @@ sntl_readcap(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int vb) pr2ws("%s: sg_memalign() failed to get memory\n", __func__); return sg_convert_errno(ENOMEM); } - res = sntl_do_identify(fdc_p, 0x0 /* CNS */, fdc_p->nsid, pg_sz, up, - vb); + res = sntl_do_identify(ptp, 0x0 /* CNS */, fdc_p->nsid, pg_sz, up, + time_secs, vb); if (res < 0) { res = sg_convert_errno(-res); goto fini; @@ -2237,10 +2842,13 @@ fini: } /* Executes NVMe Admin command (or at least forwards it to lower layers). + * Depending on the device, this could be NVME(via CAM) or NVME(non-CAM). + * is_admin will be overridden if the SNTL functions are called. * Returns 0 for success, negative numbers are negated 'errno' values from * OS system calls. Positive return values are errors from this package. */ static int -sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) +sg_do_nvme_pt(struct sg_pt_freebsd_scsi * ptp, int fd, bool is_admin, + int time_secs, int vb) { bool scsi_cdb, in_xfer; int n, err, len, io_len; @@ -2248,25 +2856,25 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) uint8_t * dxferp; uint8_t * npc_up; struct freebsd_dev_channel * fdc_p; - struct sg_pt_freebsd_scsi * ptp = &vp->impl; const uint8_t * cdbp; struct nvme_pt_command npc; npc_up = (uint8_t *)&npc; - if (vb > 3) - pr2ws("%s: fd=%d\n", __func__, fd); + if (vb > 6) + pr2ws("%s: fd=%d, is_admin=%d\n", __func__, fd, (int)is_admin); if (! ptp->cdb) { if (vb) pr2ws("%s: No NVMe command given (set_scsi_pt_cdb())\n", __func__); return SCSI_PT_DO_BAD_PARAMS; } - fdc_p = get_fdc_p(ptp); + fdc_p = ptp->mchanp; if (fd < 0) { if (NULL == fdc_p) { pr2ws("%s: no device handle in object or fd ?\n", __func__); return -EINVAL; } + /* no fd, but have fdc_p so that is okay */ } else { int han = fd - FREEBSD_FDOFFSET; @@ -2289,6 +2897,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) } } + ptp->is_nvme_dev = fdc_p->is_nvme_dev; n = ptp->cdb_len; cdbp = (const uint8_t *)ptp->cdb; if (vb > 3) @@ -2296,36 +2905,52 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) scsi_cdb = sg_is_scsi_cdb(cdbp, n); /* nvme_our_sntl is false when NVMe command (64 byte) has been given */ ptp->nvme_our_sntl = scsi_cdb; - fdc_p->nvme_our_sntl = ptp->nvme_our_sntl; if (scsi_cdb) { switch (cdbp[0]) { case SCSI_INQUIRY_OPC: - return sntl_inq(ptp, cdbp, vb); + return sntl_inq(ptp, cdbp, time_secs, vb); case SCSI_REPORT_LUNS_OPC: - return sntl_rluns(ptp, cdbp, vb); + return sntl_rluns(ptp, cdbp, time_secs, vb); case SCSI_TEST_UNIT_READY_OPC: - return sntl_tur(ptp, vb); + return sntl_tur(ptp, time_secs, vb); case SCSI_REQUEST_SENSE_OPC: - return sntl_req_sense(ptp, cdbp, vb); + return sntl_req_sense(ptp, cdbp, time_secs, vb); + case SCSI_READ10_OPC: + case SCSI_READ16_OPC: + return sntl_rread(ptp, cdbp, time_secs, vb); + case SCSI_WRITE10_OPC: + case SCSI_WRITE16_OPC: + return sntl_write(ptp, cdbp, time_secs, vb); + case SCSI_START_STOP_OPC: + return sntl_start_stop(ptp, cdbp, time_secs, vb); case SCSI_SEND_DIAGNOSTIC_OPC: - return sntl_senddiag(ptp, cdbp, vb); + return sntl_senddiag(ptp, cdbp, time_secs, vb); case SCSI_RECEIVE_DIAGNOSTIC_OPC: - return sntl_recvdiag(ptp, cdbp, vb); + return sntl_recvdiag(ptp, cdbp, time_secs, vb); case SCSI_MODE_SENSE10_OPC: case SCSI_MODE_SELECT10_OPC: - return sntl_mode_ss(ptp, cdbp, vb); + return sntl_mode_ss(ptp, cdbp, time_secs, vb); case SCSI_READ_CAPACITY10_OPC: - return sntl_readcap(ptp, cdbp, vb); + return sntl_readcap(ptp, cdbp, time_secs, vb); + case SCSI_VERIFY10_OPC: + case SCSI_VERIFY16_OPC: + return sntl_verify(ptp, cdbp, time_secs, vb); + case SCSI_WRITE_SAME10_OPC: + case SCSI_WRITE_SAME16_OPC: + return sntl_write_same(ptp, cdbp, time_secs, vb); + case SCSI_SYNC_CACHE10_OPC: + case SCSI_SYNC_CACHE16_OPC: + return sntl_sync_cache(ptp, cdbp, time_secs, vb); case SCSI_SERVICE_ACT_IN_OPC: if (SCSI_READ_CAPACITY16_SA == (cdbp[1] & SCSI_SA_MSK)) - return sntl_readcap(ptp, cdbp, vb); + return sntl_readcap(ptp, cdbp, time_secs, vb); goto fini; case SCSI_MAINT_IN_OPC: - sa = 0x1f & cdbp[1]; /* service action */ + sa = SCSI_SA_MSK & cdbp[1]; /* service action */ if (SCSI_REP_SUP_OPCS_OPC == sa) - return sntl_rep_opcodes(ptp, cdbp, vb); + return sntl_rep_opcodes(ptp, cdbp, time_secs, vb); else if (SCSI_REP_SUP_TMFS_OPC == sa) - return sntl_rep_tmfs(ptp, cdbp, vb); + return sntl_rep_tmfs(ptp, cdbp, time_secs, vb); /* fall through */ default: fini: @@ -2341,7 +2966,11 @@ fini: return 0; } } + /* NVMe command given to pass-through */ + if (vb > 4) + pr2ws("%s: NVMe pass-through command, admin=%d\n", __func__, + is_admin); len = (int)sizeof(npc.cmd); n = (n < len) ? n : len; if (n < 64) { @@ -2371,17 +3000,18 @@ fini: sg_put_unaligned_le64((sg_uintptr_t)ptp->dxferop, npc_up + SG_NVME_PT_ADDR); } - err = nvme_pt_low(fdc_p, dxferp, io_len, in_xfer, &npc, vb); + err = nvme_pt_low(ptp, dxferp, io_len, is_admin, in_xfer, &npc, time_secs, + vb); if (err < 0) { if (vb > 1) - pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", + pr2ws("%s: nvme_pt_low() failed: %s (errno=%d)\n", __func__, strerror(-err), -err); return err; } sct_sc = err; /* ((SCT << 8) | SC) which may be 0 */ - fdc_p->nvme_status = sct_sc; + ptp->nvme_status = sct_sc; if (ptp->sense && (ptp->sense_len > 0)) { - uint32_t k = sizeof(fdc_p->cq_dw0_3); + uint32_t k = sizeof(ptp->cq_dw0_3); if ((int)k < ptp->sense_len) ptp->sense_resid = ptp->sense_len - (int)k; @@ -2389,57 +3019,45 @@ fini: k = ptp->sense_len; ptp->sense_resid = 0; } - memcpy(ptp->sense, fdc_p->cq_dw0_3, k); + memcpy(ptp->sense, ptp->cq_dw0_3, k); } if (in_xfer) ptp->resid = 0; /* Just hoping ... */ return sct_sc ? SG_LIB_NVME_STATUS : 0; } -#else /* if not(HAVE_NVME && (! IGNORE_NVME)) */ - -static int -sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) -{ - if (vb) { - pr2ws("%s: not supported, ", __func__); -#ifdef HAVE_NVME - pr2ws("HAVE_NVME, "); -#else - pr2ws("don't HAVE_NVME, "); -#endif - -#ifdef IGNORE_NVME - pr2ws("IGNORE_NVME"); -#else - pr2ws("don't IGNORE_NVME"); -#endif - pr2ws("\n"); - if (NULL == vp) - pr2ws("%s: object pointer NULL; fd=%d\n", __func__, fd); - } - return -ENOTTY; /* inappropriate ioctl error */ -} - #endif /* (HAVE_NVME && (! IGNORE_NVME)) */ + #if (HAVE_NVME && (! IGNORE_NVME)) +/* Requires pass-through file to be open and associated with vp */ int do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb) { - if (vb) - pr2ws("%s: not supported, ", __func__); - if (vp) { } - if (submq) { } - if (timeout_secs) { } - return SCSI_PT_DO_NOT_SUPPORTED; + struct sg_pt_freebsd_scsi * ptp = &vp->impl; + struct freebsd_dev_channel *fdc_p; + + if (vb && (submq != 0)) + pr2ws("%s: warning, uses submit queue 0\n", __func__); + fdc_p = ptp->mchanp; + if (NULL == fdc_p) { + fdc_p = get_fdc_p(ptp); + if (NULL == fdc_p) { + if (vb > 2) + pr2ws("%s: no open file associated with pt object\n", + __func__); + return -EINVAL; + } + ptp->mchanp = fdc_p; + } + return sg_do_nvme_pt(ptp, -1, false, timeout_secs, vb); } #else /* (HAVE_NVME && (! IGNORE_NVME)) */ int -do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb) { if (vb) { pr2ws("%s: not supported, ", __func__); diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c index 6c49096b..9da8bab6 100644 --- a/lib/sg_pt_linux.c +++ b/lib/sg_pt_linux.c @@ -7,7 +7,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ -/* sg_pt_linux version 1.51 20210102 */ +/* sg_pt_linux version 1.52 20210423 */ #include <stdio.h> diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c index 74453f74..094dc49c 100644 --- a/lib/sg_pt_linux_nvme.c +++ b/lib/sg_pt_linux_nvme.c @@ -41,7 +41,7 @@ * MA 02110-1301, USA. */ -/* sg_pt_linux_nvme version 1.15 20210102 */ +/* sg_pt_linux_nvme version 1.17 20210501 */ /* This file contains a small "SPC-only" SNTL to support the SES pass-through * of SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS through NVME-MI @@ -142,6 +142,7 @@ #define SG_NVME_AD_GET_FEATURE 0xa #define SG_NVME_AD_SET_FEATURE 0x9 #define SG_NVME_AD_IDENTIFY 0x6 /* similar to SCSI INQUIRY */ +#define SG_NVME_AD_DEV_SELT_TEST 0x14 #define SG_NVME_AD_MI_RECEIVE 0x1e /* MI: Management Interface */ #define SG_NVME_AD_MI_SEND 0x1d /* hmmm, same opcode as SEND DIAG */ @@ -153,7 +154,7 @@ #define SG_NVME_NVM_WRITE 0x1 #define SG_NVME_NVM_WRITE_ZEROES 0x8 /* SCSI WRITE SAME */ -#define SG_NVME_NVM_CDW12_FUA (1 << 30) /* Force Unit Access bit */ +#define SG_NVME_RW_CDW12_FUA (1 << 30) /* Force Unit Access bit */ #if (HAVE_NVME && (! IGNORE_NVME)) @@ -318,7 +319,7 @@ sg_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp, if (vb > 2) { pr2ws("NVMe Admin command: %s\n", nam); hex2stderr((const uint8_t *)cmdp, cmd_len, 1); - if ((vb > 3) && (! is_read) && dp) { + if ((vb > 4) && (! is_read) && dp) { uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN); if (len > 0) { @@ -375,7 +376,7 @@ sg_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp, } return SG_LIB_NVME_STATUS; /* == SCSI_PT_DO_NVME_STATUS */ } - if ((vb > 3) && is_read && dp) { + if ((vb > 4) && is_read && dp) { uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN); if (len > 0) { @@ -402,7 +403,7 @@ sntl_check_enclosure_override(struct sg_pt_linux_scsi * ptp, int vb) if (NULL == up) return; nvmsr = up[253]; - if (vb > 3) + if (vb > 5) pr2ws("%s: enter, nvmsr=%u\n", __func__, nvmsr); ptp->dev_stat.id_ctl253 = nvmsr; switch (ptp->dev_stat.enclosure_override) { @@ -528,7 +529,7 @@ sntl_inq(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, uint8_t * free_nvme_id_ns = NULL; uint8_t inq_dout[256]; - if (vb > 3) + if (vb > 5) pr2ws("%s: time_secs=%d\n", __func__, time_secs); if (0x2 & cdbp[1]) { /* Reject CmdDt=1 */ @@ -677,7 +678,7 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, uint8_t * rl_doutp; uint8_t * up; - if (vb > 3) + if (vb > 5) pr2ws("%s: time_secs=%d\n", __func__, time_secs); sel_report = cdbp[2]; @@ -740,7 +741,7 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb) int res; uint32_t pow_state; - if (vb > 4) + if (vb > 5) pr2ws("%s: start\n", __func__); if (NULL == ptp->nvme_id_ctlp) { res = sntl_cache_identify(ptp, time_secs, vb); @@ -760,7 +761,7 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb) return res; } pow_state = (0x1f & ptp->nvme_result); - if (vb > 3) + if (vb > 5) pr2ws("%s: pow_state=%u\n", __func__, pow_state); #if 0 /* pow_state bounces around too much on laptop */ if (pow_state) @@ -779,7 +780,7 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, uint32_t pow_state, alloc_len, n; uint8_t rs_dout[64]; - if (vb > 3) + if (vb > 5) pr2ws("%s: time_secs=%d\n", __func__, time_secs); if (NULL == ptp->nvme_id_ctlp) { res = sntl_cache_identify(ptp, time_secs, vb); @@ -802,7 +803,7 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, } ptp->io_hdr.response_len = 0; pow_state = (0x1f & ptp->nvme_result); - if (vb > 3) + if (vb > 5) pr2ws("%s: pow_state=%u\n", __func__, pow_state); memset(rs_dout, 0, sizeof(rs_dout)); if (pow_state) @@ -832,7 +833,7 @@ sntl_mode_ss(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, uint8_t * bp; struct sg_sntl_result_t sntl_result; - if (vb > 3) + if (vb > 5) pr2ws("%s: mode se%s\n", __func__, (is_msense ? "nse" : "lect")); if (NULL == ptp->nvme_id_ctlp) { res = sntl_cache_identify(ptp, time_secs, vb); @@ -942,12 +943,12 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, st_cd = 0x7 & (cdbp[1] >> 5); self_test = !! (0x4 & cdbp[1]); pf = !! (0x10 & cdbp[1]); - if (vb > 3) + if (vb > 5) pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf, (int)self_test, (int)st_cd); if (self_test || st_cd) { memset(cmd_up, 0, sizeof(cmd)); - cmd_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */ + cmd_up[SG_NVME_PT_OPCODE] = SG_NVME_AD_DEV_SELT_TEST; /* just this namespace (if there is one) and controller */ sg_put_unaligned_le32(ptp->nvme_nsid, cmd_up + SG_NVME_PT_NSID); switch (st_cd) { @@ -1057,7 +1058,7 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, pcv = !! (0x1 & cdbp[1]); dpg_cd = cdbp[2]; alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ - if (vb > 3) + if (vb > 5) pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__, dpg_cd, (int)pcv, alloc_len); din_len = ptp->io_hdr.din_xfer_len; @@ -1114,7 +1115,7 @@ sntl_rep_opcodes(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, uint8_t *arr; uint8_t *free_arr; - if (vb > 3) + if (vb > 5) pr2ws("%s: time_secs=%d\n", __func__, time_secs); rctd = !!(cdbp[2] & 0x80); /* report command timeout desc. */ reporting_opts = cdbp[2] & 0x7; @@ -1223,7 +1224,7 @@ sntl_rep_tmfs(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, uint32_t alloc_len, len; uint8_t arr[16]; - if (vb > 3) + if (vb > 5) pr2ws("%s: time_secs=%d\n", __func__, time_secs); memset(arr, 0, sizeof(arr)); repd = !!(cdbp[2] & 0x80); @@ -1269,7 +1270,7 @@ sntl_readcap(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, uint8_t * free_up = NULL; uint8_t resp[32]; - if (vb > 3) + if (vb > 5) pr2ws("%s: RCAP%d, time_secs=%d\n", __func__, (is_rcap10 ? 10 : 16), time_secs); up = sg_memalign(pg_sz, pg_sz, &free_up, false); @@ -1348,7 +1349,7 @@ do_nvm_pt_low(struct sg_pt_linux_scsi * ptp, if (vb > 2) { pr2ws("NVMe NVM command: %s\n", nam); hex2stderr((const uint8_t *)cmdp, cmd_len, 1); - if ((vb > 3) && (! is_read) && dp) { + if ((vb > 4) && (! is_read) && dp) { if (dlen > 0) { n = dlen; if ((dlen < 512) || (vb > 5)) @@ -1403,7 +1404,7 @@ do_nvm_pt_low(struct sg_pt_linux_scsi * ptp, } return SG_LIB_NVME_STATUS; /* == SCSI_PT_DO_NVME_STATUS */ } - if ((vb > 3) && is_read && dp) { + if ((vb > 4) && is_read && dp) { if (dlen > 0) { n = dlen; if ((dlen < 1024) || (vb > 5)) @@ -1445,17 +1446,17 @@ sntl_do_nvm_cmd(struct sg_pt_linux_scsi * ptp, struct sg_nvme_user_io * iop, } static int -sntl_read(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, - int time_secs, int vb) +sntl_rread(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, + int time_secs, int vb) { bool is_read10 = (SCSI_READ10_OPC == cdbp[0]); bool have_fua = !!(cdbp[1] & 0x8); int res; - int nblks_t10 = 0; + uint32_t nblks_t10 = 0; struct sg_nvme_user_io io; struct sg_nvme_user_io * iop = &io; - if (vb > 3) + if (vb > 5) pr2ws("%s: fua=%d, time_secs=%d\n", __func__, (int)have_fua, time_secs); memset(iop, 0, sizeof(*iop)); @@ -1464,14 +1465,12 @@ sntl_read(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, iop->slba = sg_get_unaligned_be32(cdbp + 2); nblks_t10 = sg_get_unaligned_be16(cdbp + 7); } else { - uint32_t num = sg_get_unaligned_be32(cdbp + 10); - iop->slba = sg_get_unaligned_be64(cdbp + 2); - if (num > (UINT16_MAX + 1)) { + nblks_t10 = sg_get_unaligned_be32(cdbp + 10); + if (nblks_t10 > (UINT16_MAX + 1)) { mk_sense_invalid_fld(ptp, true, 11, -1, vb); return 0; - } else - nblks_t10 = num; + } } if (0 == nblks_t10) { /* NOP in SCSI */ if (vb > 4) @@ -1481,7 +1480,7 @@ sntl_read(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, } iop->nblocks = nblks_t10 - 1; /* crazy "0's based" */ if (have_fua) - iop->nblocks |= SG_NVME_NVM_CDW12_FUA; + iop->nblocks |= SG_NVME_RW_CDW12_FUA; iop->addr = (uint64_t)ptp->io_hdr.din_xferp; res = sntl_do_nvm_cmd(ptp, iop, ptp->io_hdr.din_xfer_len, true /* is_read */, time_secs, vb); @@ -1499,11 +1498,11 @@ sntl_write(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, bool is_write10 = (SCSI_WRITE10_OPC == cdbp[0]); bool have_fua = !!(cdbp[1] & 0x8); int res; - int nblks_t10 = 0; + uint32_t nblks_t10 = 0; struct sg_nvme_user_io io; struct sg_nvme_user_io * iop = &io; - if (vb > 3) + if (vb > 5) pr2ws("%s: fua=%d, time_secs=%d\n", __func__, (int)have_fua, time_secs); memset(iop, 0, sizeof(*iop)); @@ -1512,14 +1511,12 @@ sntl_write(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, iop->slba = sg_get_unaligned_be32(cdbp + 2); nblks_t10 = sg_get_unaligned_be16(cdbp + 7); } else { - uint32_t num = sg_get_unaligned_be32(cdbp + 10); - iop->slba = sg_get_unaligned_be64(cdbp + 2); - if (num > (UINT16_MAX + 1)) { + nblks_t10 = sg_get_unaligned_be32(cdbp + 10); + if (nblks_t10 > (UINT16_MAX + 1)) { mk_sense_invalid_fld(ptp, true, 11, -1, vb); return 0; - } else - nblks_t10 = num; + } } if (0 == nblks_t10) { /* NOP in SCSI */ if (vb > 4) @@ -1529,7 +1526,7 @@ sntl_write(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, } iop->nblocks = nblks_t10 - 1; if (have_fua) - iop->nblocks |= SG_NVME_NVM_CDW12_FUA; + iop->nblocks |= SG_NVME_RW_CDW12_FUA; iop->addr = (uint64_t)ptp->io_hdr.dout_xferp; res = sntl_do_nvm_cmd(ptp, iop, ptp->io_hdr.dout_xfer_len, false, time_secs, vb); @@ -1548,30 +1545,28 @@ sntl_verify(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, uint8_t bytchk = (cdbp[1] >> 1) & 0x3; uint32_t dlen = 0; int res; - int nblks_t10 = 0; + uint32_t nblks_t10 = 0; struct sg_nvme_user_io io; struct sg_nvme_user_io * iop = &io; - if (vb > 3) + if (vb > 5) pr2ws("%s: bytchk=%d, time_secs=%d\n", __func__, bytchk, time_secs); if (bytchk > 1) { mk_sense_invalid_fld(ptp, true, 1, 2, vb); return 0; } memset(iop, 0, sizeof(*iop)); - iop->opcode = bytchk ? SG_NVME_NVM_COMPARE : SG_NVME_NVM_WRITE; + iop->opcode = bytchk ? SG_NVME_NVM_COMPARE : SG_NVME_NVM_VERIFY; if (is_verify10) { iop->slba = sg_get_unaligned_be32(cdbp + 2); nblks_t10 = sg_get_unaligned_be16(cdbp + 7); } else { - uint32_t num = sg_get_unaligned_be32(cdbp + 10); - iop->slba = sg_get_unaligned_be64(cdbp + 2); - if (num > (UINT16_MAX + 1)) { + nblks_t10 = sg_get_unaligned_be32(cdbp + 10); + if (nblks_t10 > (UINT16_MAX + 1)) { mk_sense_invalid_fld(ptp, true, 11, -1, vb); return 0; - } else - nblks_t10 = num; + } } if (0 == nblks_t10) { /* NOP in SCSI */ if (vb > 4) @@ -1603,7 +1598,7 @@ sntl_write_same(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, struct sg_nvme_user_io io; struct sg_nvme_user_io * iop = &io; - if (vb > 3) + if (vb > 5) pr2ws("%s: ndob=%d, time_secs=%d\n", __func__, (int)ndob, time_secs); if (! ndob) { int flbas, index, lbafx, lbads, lbsize; @@ -1673,7 +1668,7 @@ sntl_sync_cache(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, struct sg_nvme_user_io * iop = &io; int res; - if (vb > 3) + if (vb > 5) pr2ws("%s: immed=%d, time_secs=%d\n", __func__, (int)immed, time_secs); memset(iop, 0, sizeof(*iop)); @@ -1694,7 +1689,7 @@ sntl_start_stop(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, { bool immed = !!(0x1 & cdbp[1]); - if (vb > 3) + if (vb > 5) pr2ws("%s: immed=%d, time_secs=%d, ignore\n", __func__, (int)immed, time_secs); if (ptp) { } /* suppress warning */ @@ -1739,7 +1734,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb) } n = ptp->io_hdr.request_len; cdbp = (const uint8_t *)(sg_uintptr_t)ptp->io_hdr.request; - if (vb > 3) + if (vb > 4) pr2ws("%s: opcode=0x%x, fd=%d (dev_fd=%d), time_secs=%d\n", __func__, cdbp[0], fd, hold_dev_fd, time_secs); scsi_cdb = sg_is_scsi_cdb(cdbp, n); @@ -1757,7 +1752,7 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb) return sntl_req_sense(ptp, cdbp, time_secs, vb); case SCSI_READ10_OPC: case SCSI_READ16_OPC: - return sntl_read(ptp, cdbp, time_secs, vb); + return sntl_rread(ptp, cdbp, time_secs, vb); case SCSI_WRITE10_OPC: case SCSI_WRITE16_OPC: return sntl_write(ptp, cdbp, time_secs, vb); @@ -1908,7 +1903,7 @@ do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb) #else /* (HAVE_NVME && (! IGNORE_NVME)) */ int -do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) +do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int vb) { if (vb) { pr2ws("%s: not supported, ", __func__); @@ -1923,6 +1918,7 @@ do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) #else pr2ws("don't IGNORE_NVME"); #endif + } if (vp) { } if (submq) { } if (timeout_secs) { } |