sg_pt_freebsd.c (sdparm-1.11.tgz) | : | sg_pt_freebsd.c (sdparm-1.12.tgz) | ||
---|---|---|---|---|
/* | /* | |||
* Copyright (c) 2005-2019 Douglas Gilbert. | * Copyright (c) 2005-2021 Douglas Gilbert. | |||
* All rights reserved. | * All rights reserved. | |||
* Use of this source code is governed by a BSD-style | * Use of this source code is governed by a BSD-style | |||
* license that can be found in the BSD_LICENSE file. | * license that can be found in the BSD_LICENSE file. | |||
* | * | |||
* SPDX-License-Identifier: BSD-2-Clause | * SPDX-License-Identifier: BSD-2-Clause | |||
*/ | */ | |||
/* sg_pt_freebsd version 1.35 20190210 */ | /* sg_pt_freebsd version 1.39 20210225 */ | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <stdarg.h> | #include <stdarg.h> | |||
#include <stdbool.h> | #include <stdbool.h> | |||
#include <string.h> | #include <string.h> | |||
#include <sys/types.h> | #include <sys/types.h> | |||
#include <dirent.h> | #include <dirent.h> | |||
#include <limits.h> | #include <limits.h> | |||
#include <libgen.h> /* for basename */ | #include <libgen.h> /* for basename */ | |||
skipping to change at line 58 | skipping to change at line 58 | |||
#else | #else | |||
#define NVME_CTRLR_PREFIX "/dev/nvme" | #define NVME_CTRLR_PREFIX "/dev/nvme" | |||
#define NVME_NS_PREFIX "ns" | #define NVME_NS_PREFIX "ns" | |||
#endif | #endif | |||
#define FREEBSD_MAXDEV 64 | #define FREEBSD_MAXDEV 64 | |||
#define FREEBSD_FDOFFSET 16; | #define FREEBSD_FDOFFSET 16; | |||
struct freebsd_dev_channel { | struct freebsd_dev_channel { | |||
int unitnum; // the SCSI unit number | int unitnum; // the SCSI unit number | |||
bool is_nvme; /* OS device type, if false ignore nvme_direct */ | bool is_nvme; /* OS device type, if false ignore nvme_our_sntl */ | |||
bool nvme_direct; /* false: our SNTL; true: received NVMe command */ | bool nvme_our_sntl; /* true: our SNTL; false: received NVMe command */ | |||
bool is_char; | bool is_char; | |||
uint32_t nsid; | uint32_t nsid; | |||
uint32_t nv_ctrlid; | uint32_t nv_ctrlid; | |||
int dev_fd; // for NVMe, use -1 to indicate not provided | int dev_fd; // for NVMe, use -1 to indicate not provided | |||
uint32_t nvme_result; // cdw0 from completion | uint32_t nvme_result; // cdw0 from completion | |||
uint16_t nvme_status; // from completion: ((sct << 8) | sc) | uint16_t nvme_status; // from completion: ((sct << 8) | sc) | |||
char* devname; // the device name | char* devname; // the device name | |||
struct cam_device* cam_dev; | struct cam_device* cam_dev; | |||
uint8_t * nvme_id_ctlp; | uint8_t * nvme_id_ctlp; | |||
uint8_t * free_nvme_id_ctlp; | uint8_t * free_nvme_id_ctlp; | |||
skipping to change at line 108 | skipping to change at line 108 | |||
int scsi_status; | int scsi_status; | |||
int resid; | int resid; | |||
int sense_resid; | int sense_resid; | |||
int in_err; | int in_err; | |||
int os_err; | int os_err; | |||
int transport_err; | int transport_err; | |||
int dev_han; // should be >= FREEBSD_FDOFFSET then | int dev_han; // should be >= FREEBSD_FDOFFSET then | |||
// (dev_han - FREEBSD_FDOFFSET) is the | // (dev_han - FREEBSD_FDOFFSET) is the | |||
// index into devicetable[] | // index into devicetable[] | |||
bool is_nvme; // copy of same field in fdc object | bool is_nvme; // copy of same field in fdc object | |||
bool nvme_direct; // 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 | struct sg_sntl_dev_state_t * dev_statp; // points to associated fdc | |||
}; | }; | |||
struct sg_pt_base { | struct sg_pt_base { | |||
struct sg_pt_freebsd_scsi impl; | 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); | static int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb); | |||
skipping to change at line 158 | skipping to change at line 158 | |||
} | } | |||
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed | /* 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 | * together. The 'oflags' is only used on NVMe devices. It is ignored on | |||
* SCSI and ATA devices in FreeBSD. | * SCSI and ATA devices in FreeBSD. | |||
* Returns >= 0 if successful, otherwise returns negated errno. */ | * Returns >= 0 if successful, otherwise returns negated errno. */ | |||
int | int | |||
scsi_pt_open_flags(const char * device_name, int oflags, int vb) | scsi_pt_open_flags(const char * device_name, int oflags, int vb) | |||
{ | { | |||
bool is_char, is_block, possible_nvme; | bool is_char, is_block, possible_nvme; | |||
char tmp; | char tmp, first_ch; | |||
int k, err, dev_fd, ret; | int k, err, dev_fd, ret; | |||
uint32_t nsid, nv_ctrlid; | uint32_t nsid, nv_ctrlid; | |||
ssize_t s; | ssize_t s; | |||
struct freebsd_dev_channel *fdc_p = NULL; | struct freebsd_dev_channel *fdc_p = NULL; | |||
struct cam_device* cam_dev; | struct cam_device* cam_dev; | |||
struct stat a_stat; | struct stat a_stat; | |||
char b[PATH_MAX]; | char dev_nm[PATH_MAX]; | |||
char full_path[64]; | ||||
// Search table for a free entry | // Search table for a free entry | |||
for (k = 0; k < FREEBSD_MAXDEV; k++) | for (k = 0; k < FREEBSD_MAXDEV; k++) | |||
if (! devicetable[k]) | if (! devicetable[k]) | |||
break; | break; | |||
// If no free entry found, return error. We have max allowed number | // If no free entry found, return error. We have max allowed number | |||
// of "file descriptors" already allocated. | // of "file descriptors" already allocated. | |||
if (k == FREEBSD_MAXDEV) { | if (k == FREEBSD_MAXDEV) { | |||
if (vb) | if (vb) | |||
pr2ws("too many open file descriptors (%d)\n", FREEBSD_MAXDEV); | pr2ws("too many open file descriptors (%d)\n", FREEBSD_MAXDEV); | |||
ret = -EMFILE; | ret = -EMFILE; | |||
goto err_out; | goto err_out; | |||
} | } | |||
if (stat(device_name, &a_stat) < 0) { | 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; | err = errno; | |||
pr2ws("%s: unable to stat(%s): %s\n", __func__, device_name, | pr2ws("%s: unable to stat(%s): %s\n", __func__, dev_nm, | |||
strerror(err)); | strerror(err)); | |||
ret = -err; | ret = -err; | |||
goto err_out; | goto err_out; | |||
} | } | |||
is_block = S_ISBLK(a_stat.st_mode); | is_block = S_ISBLK(a_stat.st_mode); | |||
is_char = S_ISCHR(a_stat.st_mode); | is_char = S_ISCHR(a_stat.st_mode); | |||
if (! (is_block || is_char)) { | if (! (is_block || is_char)) { | |||
if (vb) | if (vb) | |||
pr2ws("%s: %s is not char nor block device\n", __func__, | pr2ws("%s: %s is not char nor block device\n", __func__, | |||
device_name); | dev_nm); | |||
ret = -ENODEV; | ret = -ENODEV; | |||
goto err_out; | goto err_out; | |||
} | } | |||
s = readlink(device_name, b, sizeof(b)); | ||||
if (s <= 0) { | ||||
strncpy(b, device_name, PATH_MAX - 1); | ||||
b[PATH_MAX - 1] = '\0'; | ||||
} | ||||
/* Some code borrowed from smartmontools, Christian Franke */ | /* Some code borrowed from smartmontools, Christian Franke */ | |||
nsid = broadcast_nsid; | nsid = broadcast_nsid; | |||
nv_ctrlid = broadcast_nsid; | nv_ctrlid = broadcast_nsid; | |||
possible_nvme = false; | possible_nvme = false; | |||
while (true) { /* dummy loop, so can 'break' out */ | while (true) { /* dummy loop, so can 'break' out */ | |||
if(sscanf(b, NVME_CTRLR_PREFIX "%u%c", &nv_ctrlid, &tmp) == 1) { | if(sscanf(dev_nm, NVME_CTRLR_PREFIX "%u%c", &nv_ctrlid, &tmp) == 1) { | |||
if(nv_ctrlid == broadcast_nsid) | if(nv_ctrlid == broadcast_nsid) | |||
break; | break; | |||
} else if (sscanf(b, NVME_CTRLR_PREFIX "%d" NVME_NS_PREFIX "%d%c", | } else if (sscanf(dev_nm, NVME_CTRLR_PREFIX "%d" NVME_NS_PREFIX | |||
&nv_ctrlid, &nsid, &tmp) == 2) { | "%d%c", &nv_ctrlid, &nsid, &tmp) == 2) { | |||
if((nv_ctrlid == broadcast_nsid) || (nsid == broadcast_nsid)) | if((nv_ctrlid == broadcast_nsid) || (nsid == broadcast_nsid)) | |||
break; | break; | |||
} else | } else | |||
break; | break; | |||
possible_nvme = true; | possible_nvme = true; | |||
break; | break; | |||
} | } | |||
fdc_p = (struct freebsd_dev_channel *) | fdc_p = (struct freebsd_dev_channel *) | |||
calloc(1,sizeof(struct freebsd_dev_channel)); | calloc(1,sizeof(struct freebsd_dev_channel)); | |||
skipping to change at line 246 | skipping to change at line 258 | |||
if (possible_nvme) { | if (possible_nvme) { | |||
// we should always open controller, not namespace device | // we should always open controller, not namespace device | |||
snprintf(fdc_p->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d", | snprintf(fdc_p->devname, DEV_IDLEN, NVME_CTRLR_PREFIX"%d", | |||
nv_ctrlid); | nv_ctrlid); | |||
dev_fd = open(fdc_p->devname, oflags); | dev_fd = open(fdc_p->devname, oflags); | |||
if (dev_fd < 0) { | if (dev_fd < 0) { | |||
err = errno; | err = errno; | |||
if (vb) | if (vb) | |||
pr2ws("%s: open(%s) failed: %s (errno=%d), try SCSI/ATA\n", | pr2ws("%s: open(%s) failed: %s (errno=%d), try SCSI/ATA\n", | |||
__func__, full_path, strerror(err), err); | __func__, fdc_p->devname, strerror(err), err); | |||
goto scsi_ata_try; | goto scsi_ata_try; | |||
} | } | |||
fdc_p->is_nvme = true; | fdc_p->is_nvme = true; | |||
fdc_p->nvme_direct = false; | fdc_p->nvme_our_sntl = true; /* guess at this stage */ | |||
fdc_p->is_char = is_char; | fdc_p->is_char = is_char; | |||
fdc_p->nsid = (broadcast_nsid == nsid) ? 0 : nsid; | fdc_p->nsid = (broadcast_nsid == nsid) ? 0 : nsid; | |||
fdc_p->nv_ctrlid = nv_ctrlid; | fdc_p->nv_ctrlid = nv_ctrlid; | |||
fdc_p->dev_fd = dev_fd; | fdc_p->dev_fd = dev_fd; | |||
devicetable[k] = fdc_p; | devicetable[k] = fdc_p; | |||
return k + FREEBSD_FDOFFSET; | return k + FREEBSD_FDOFFSET; | |||
} | } | |||
scsi_ata_try: | scsi_ata_try: | |||
fdc_p->is_char = is_char; | fdc_p->is_char = is_char; | |||
if (cam_get_device(device_name, fdc_p->devname, DEV_IDLEN, | if (cam_get_device(dev_nm, fdc_p->devname, DEV_IDLEN, | |||
&(fdc_p->unitnum)) == -1) { | &(fdc_p->unitnum)) == -1) { | |||
if (vb) | if (vb) | |||
pr2ws("bad device name structure\n"); | pr2ws("bad device name structure\n"); | |||
errno = EINVAL; | errno = EINVAL; | |||
ret = -errno; | ret = -errno; | |||
goto err_out; | goto err_out; | |||
} | } | |||
if (vb > 4) | if (vb > 4) | |||
pr2ws("%s: cam_get_device, f->devname: %s, f->unitnum=%d\n", __func__, | pr2ws("%s: cam_get_device, f->devname: %s, f->unitnum=%d\n", __func__, | |||
fdc_p->devname, fdc_p->unitnum); | fdc_p->devname, fdc_p->unitnum); | |||
skipping to change at line 450 | skipping to change at line 462 | |||
dsp = ptp->dev_statp; | dsp = ptp->dev_statp; | |||
memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi)); | memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi)); | |||
ptp->dxfer_dir = CAM_DIR_NONE; | ptp->dxfer_dir = CAM_DIR_NONE; | |||
ptp->dev_han = dev_han; | ptp->dev_han = dev_han; | |||
ptp->is_nvme = is_nvme; | ptp->is_nvme = is_nvme; | |||
ptp->cam_dev = cam_dev; | ptp->cam_dev = cam_dev; | |||
ptp->dev_statp = dsp; | ptp->dev_statp = dsp; | |||
} | } | |||
} | } | |||
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; | ||||
ptp->in_err = 0; | ||||
ptp->os_err = 0; | ||||
ptp->transport_err = 0; | ||||
ptp->scsi_status = 0; | ||||
ptp->dxfer_dir = CAM_DIR_NONE; | ||||
ptp->dxferip = NULL; | ||||
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; | ||||
} | ||||
/* Forget any previous dev_han and install the one given. May attempt to | /* Forget any previous dev_han and install the one given. May attempt to | |||
* find file type (e.g. if pass-though) from OS so there could be an error. | * find file type (e.g. if pass-though) from OS so there could be an error. | |||
* Returns 0 for success or the same value as get_scsi_pt_os_err() | * Returns 0 for success or the same value as get_scsi_pt_os_err() | |||
* will return. dev_han should be >= 0 for a valid file handle or -1 . */ | * will return. dev_han should be >= 0 for a valid file handle or -1 . */ | |||
int | int | |||
set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb) | set_pt_file_handle(struct sg_pt_base * vp, int dev_han, int vb) | |||
{ | { | |||
struct sg_pt_freebsd_scsi * ptp; | struct sg_pt_freebsd_scsi * ptp; | |||
if (NULL == vp) { | if (NULL == vp) { | |||
skipping to change at line 509 | skipping to change at line 543 | |||
const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
return ptp ? ptp->dev_han : -1; | return ptp ? ptp->dev_han : -1; | |||
} | } | |||
void | void | |||
set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb, int cdb_len) | set_scsi_pt_cdb(struct sg_pt_base * vp, const uint8_t * cdb, int cdb_len) | |||
{ | { | |||
struct sg_pt_freebsd_scsi * ptp = &vp->impl; | struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
if (ptp->cdb) | ||||
++ptp->in_err; | ||||
ptp->cdb = (uint8_t *)cdb; | ptp->cdb = (uint8_t *)cdb; | |||
ptp->cdb_len = cdb_len; | ptp->cdb_len = cdb_len; | |||
} | } | |||
int | ||||
get_scsi_pt_cdb_len(const struct sg_pt_base * vp) | ||||
{ | ||||
const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | ||||
return ptp->cdb_len; | ||||
} | ||||
uint8_t * | ||||
get_scsi_pt_cdb_buf(const struct sg_pt_base * vp) | ||||
{ | ||||
const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | ||||
return ptp->cdb; | ||||
} | ||||
void | void | |||
set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense, | set_scsi_pt_sense(struct sg_pt_base * vp, uint8_t * sense, | |||
int max_sense_len) | int max_sense_len) | |||
{ | { | |||
struct sg_pt_freebsd_scsi * ptp = &vp->impl; | struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
if (ptp->sense) | if (sense) { | |||
++ptp->in_err; | if (max_sense_len > 0) | |||
memset(sense, 0, max_sense_len); | memset(sense, 0, max_sense_len); | |||
} | ||||
ptp->sense = sense; | ptp->sense = sense; | |||
ptp->sense_len = max_sense_len; | ptp->sense_len = max_sense_len; | |||
} | } | |||
/* Setup for data transfer from device */ | /* Setup for data transfer from device */ | |||
void | void | |||
set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp, | set_scsi_pt_data_in(struct sg_pt_base * vp, uint8_t * dxferp, | |||
int dxfer_len) | int dxfer_len) | |||
{ | { | |||
struct sg_pt_freebsd_scsi * ptp = &vp->impl; | struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
skipping to change at line 773 | skipping to change at line 822 | |||
return SCSI_PT_RESULT_STATUS; | return SCSI_PT_RESULT_STATUS; | |||
else | else | |||
return SCSI_PT_RESULT_GOOD; | return SCSI_PT_RESULT_GOOD; | |||
} | } | |||
int | int | |||
get_scsi_pt_resid(const struct sg_pt_base * vp) | get_scsi_pt_resid(const struct sg_pt_base * vp) | |||
{ | { | |||
const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
return ((NULL == ptp) || ptp->nvme_direct) ? 0 : ptp->resid; | return ((NULL == ptp) || (ptp->is_nvme && ! ptp->nvme_our_sntl)) ? | |||
0 : ptp->resid; | ||||
} | } | |||
void | void | |||
get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp, | get_pt_req_lengths(const struct sg_pt_base * vp, int * req_dinp, | |||
int * req_doutp) | int * req_doutp) | |||
{ | { | |||
const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
bool bidi = (ptp->dxfer_dir == CAM_DIR_BOTH); | bool bidi = (ptp->dxfer_dir == CAM_DIR_BOTH); | |||
if (req_dinp) { | if (req_dinp) { | |||
skipping to change at line 827 | skipping to change at line 877 | |||
/* Returns SCSI status value (from device that received the command). If an | /* Returns SCSI status value (from device that received the command). If an | |||
* NVMe command was issued directly (i.e. through do_scsi_pt() then return | * NVMe command was issued directly (i.e. through do_scsi_pt() then return | |||
* NVMe status (i.e. ((SCT << 8) | SC)). If problem returns -1. */ | * NVMe status (i.e. ((SCT << 8) | SC)). If problem returns -1. */ | |||
int | int | |||
get_scsi_pt_status_response(const struct sg_pt_base * vp) | get_scsi_pt_status_response(const struct sg_pt_base * vp) | |||
{ | { | |||
const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
if (ptp) { | if (ptp) { | |||
if (ptp->nvme_direct) { | if (ptp->is_nvme && ! ptp->nvme_our_sntl) { | |||
const struct freebsd_dev_channel *fdc_p; | const struct freebsd_dev_channel *fdc_p; | |||
fdc_p = get_fdc_cp(ptp); | fdc_p = get_fdc_cp(ptp); | |||
if (NULL == fdc_p) | if (NULL == fdc_p) | |||
return -1; | return -1; | |||
return (int)fdc_p->nvme_status; | return (int)fdc_p->nvme_status; | |||
} else | } else | |||
return ptp->scsi_status; | return ptp->scsi_status; | |||
} | } | |||
return -1; | return -1; | |||
} | } | |||
/* For NVMe command: CDW0 from completion (32 bits); for SCSI: the status */ | /* For NVMe command: CDW0 from completion (32 bits); for SCSI: the status */ | |||
uint32_t | uint32_t | |||
get_pt_result(const struct sg_pt_base * vp) | get_pt_result(const struct sg_pt_base * vp) | |||
{ | { | |||
const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
if (ptp) { | if (ptp) { | |||
if (ptp->nvme_direct) { | if (ptp->is_nvme && ! ptp->nvme_our_sntl) { | |||
const struct freebsd_dev_channel *fdc_p; | const struct freebsd_dev_channel *fdc_p; | |||
fdc_p = get_fdc_cp(ptp); | fdc_p = get_fdc_cp(ptp); | |||
if (NULL == fdc_p) | if (NULL == fdc_p) | |||
return -1; | return -1; | |||
return fdc_p->nvme_result; | return fdc_p->nvme_result; | |||
} else | } else | |||
return (uint32_t)ptp->scsi_status; | return (uint32_t)ptp->scsi_status; | |||
} | } | |||
return 0xffffffff; | return 0xffffffff; | |||
skipping to change at line 879 | skipping to change at line 929 | |||
} | } | |||
uint8_t * | uint8_t * | |||
get_scsi_pt_sense_buf(const struct sg_pt_base * vp) | get_scsi_pt_sense_buf(const struct sg_pt_base * vp) | |||
{ | { | |||
const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
return ptp->sense; | return ptp->sense; | |||
} | } | |||
/* Not impemented so return -1 . */ | /* Not implemented so return -1 . */ | |||
int | int | |||
get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused))) | get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused))) | |||
{ | { | |||
// const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | // const struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
return -1; | return -1; | |||
} | } | |||
/* If not available return 0 otherwise return number of nanoseconds that the | /* If not available return 0 otherwise return number of nanoseconds that the | |||
* lower layers (and hardware) took to execute the command just completed. */ | * lower layers (and hardware) took to execute the command just completed. */ | |||
skipping to change at line 1006 | skipping to change at line 1056 | |||
const char * cp; | const char * cp; | |||
cp = safe_strerror(ptp->os_err); | cp = safe_strerror(ptp->os_err); | |||
strncpy(b, cp, max_b_len); | strncpy(b, cp, max_b_len); | |||
if ((int)strlen(cp) >= max_b_len) | if ((int)strlen(cp) >= max_b_len) | |||
b[max_b_len - 1] = '\0'; | b[max_b_len - 1] = '\0'; | |||
return b; | return b; | |||
} | } | |||
#define SCSI_INQUIRY_OPC 0x12 | #define SCSI_INQUIRY_OPC 0x12 | |||
#define SCSI_REPORT_LUNS_OPC 0xa0 | ||||
#define SCSI_TEST_UNIT_READY_OPC 0x0 | ||||
#define SCSI_REQUEST_SENSE_OPC 0x3 | ||||
#define SCSI_SEND_DIAGNOSTIC_OPC 0x1d | ||||
#define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c | ||||
#define SCSI_MAINT_IN_OPC 0xa3 | #define SCSI_MAINT_IN_OPC 0xa3 | |||
#define SCSI_REP_SUP_OPCS_OPC 0xc | ||||
#define SCSI_REP_SUP_TMFS_OPC 0xd | ||||
#define SCSI_MODE_SENSE10_OPC 0x5a | #define SCSI_MODE_SENSE10_OPC 0x5a | |||
#define SCSI_MODE_SELECT10_OPC 0x55 | #define SCSI_MODE_SELECT10_OPC 0x55 | |||
#define SCSI_READ_CAPACITY10_OPC 0x25 | ||||
#define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c | ||||
#define SCSI_REP_SUP_OPCS_OPC 0xc | ||||
#define SCSI_REP_SUP_TMFS_OPC 0xd | ||||
#define SCSI_REPORT_LUNS_OPC 0xa0 | ||||
#define SCSI_REQUEST_SENSE_OPC 0x3 | ||||
#define SCSI_SEND_DIAGNOSTIC_OPC 0x1d | ||||
#define SCSI_TEST_UNIT_READY_OPC 0x0 | ||||
#define SCSI_SERVICE_ACT_IN_OPC 0x9e | ||||
#define SCSI_READ_CAPACITY16_SA 0x10 | ||||
#define SCSI_SA_MSK 0x1f | ||||
/* Additional Sense Code (ASC) */ | /* Additional Sense Code (ASC) */ | |||
#define NO_ADDITIONAL_SENSE 0x0 | #define NO_ADDITIONAL_SENSE 0x0 | |||
#define LOGICAL_UNIT_NOT_READY 0x4 | #define LOGICAL_UNIT_NOT_READY 0x4 | |||
#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 | #define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 | |||
#define UNRECOVERED_READ_ERR 0x11 | #define UNRECOVERED_READ_ERR 0x11 | |||
#define PARAMETER_LIST_LENGTH_ERR 0x1a | #define PARAMETER_LIST_LENGTH_ERR 0x1a | |||
#define INVALID_OPCODE 0x20 | #define INVALID_OPCODE 0x20 | |||
#define LBA_OUT_OF_RANGE 0x21 | #define LBA_OUT_OF_RANGE 0x21 | |||
#define INVALID_FIELD_IN_CDB 0x24 | #define INVALID_FIELD_IN_CDB 0x24 | |||
skipping to change at line 1252 | skipping to change at line 1306 | |||
fdc_p->dev_stat.pdt = PDT_DISK; | fdc_p->dev_stat.pdt = PDT_DISK; | |||
fdc_p->dev_stat.enc_serv = 0; | fdc_p->dev_stat.enc_serv = 0; | |||
break; | break; | |||
default: | default: | |||
pr2ws("%s: unknown enclosure_override value: %d\n", __func__, | pr2ws("%s: unknown enclosure_override value: %d\n", __func__, | |||
fdc_p->dev_stat.enclosure_override); | fdc_p->dev_stat.enclosure_override); | |||
break; | break; | |||
} | } | |||
} | } | |||
/* Currently only caches associated controller response (4096 bytes) */ | ||||
static int | static int | |||
sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb) | sntl_do_identify(struct freebsd_dev_channel * fdc_p, int cns, int nsid, | |||
int u_len, uint8_t * up, int vb) | ||||
{ | { | |||
int err; | int err; | |||
struct nvme_pt_command npc; | struct nvme_pt_command npc; | |||
uint8_t * npc_up = (uint8_t *)&npc; | uint8_t * npc_up = (uint8_t *)&npc; | |||
uint32_t pg_sz = sg_get_page_size(); | ||||
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 -ENOMEM; | ||||
} | ||||
memset(npc_up, 0, sizeof(npc)); | memset(npc_up, 0, sizeof(npc)); | |||
npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */ | npc_up[SG_NVME_PT_OPCODE] = 0x6; /* Identify */ | |||
sg_put_unaligned_le32(0x0, npc_up + SG_NVME_PT_NSID); | sg_put_unaligned_le32(nsid, npc_up + SG_NVME_PT_NSID); | |||
/* CNS=0x1 Identify: controller */ | /* CNS=0x1 Identify: controller */ | |||
sg_put_unaligned_le32(0x1, npc_up + SG_NVME_PT_CDW10); | sg_put_unaligned_le32(cns, npc_up + SG_NVME_PT_CDW10); | |||
sg_put_unaligned_le64((sg_uintptr_t)fdc_p->nvme_id_ctlp, | sg_put_unaligned_le64((sg_uintptr_t)up, npc_up + SG_NVME_PT_ADDR); | |||
npc_up + SG_NVME_PT_ADDR); | sg_put_unaligned_le32(u_len, npc_up + SG_NVME_PT_DATA_LEN); | |||
sg_put_unaligned_le32(pg_sz, npc_up + SG_NVME_PT_DATA_LEN); | err = nvme_pt_low(fdc_p, up, u_len, true, &npc, vb); | |||
err = nvme_pt_low(fdc_p, fdc_p->nvme_id_ctlp, pg_sz, true, &npc, vb); | ||||
if (err) { | if (err) { | |||
if (err < 0) { | if (err < 0) { | |||
if (vb > 1) | if (vb > 1) | |||
pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__, | pr2ws("%s: do_nvme_pt() failed: %s (errno=%d)\n", __func__, | |||
strerror(-err), -err); | strerror(-err), -err); | |||
return err; | return err; | |||
} else { /* non-zero NVMe command status */ | } else { /* non-zero NVMe command status */ | |||
fdc_p->nvme_status = err; | fdc_p->nvme_status = err; | |||
return SG_LIB_NVME_STATUS; | return SG_LIB_NVME_STATUS; | |||
} | } | |||
} | } | |||
sntl_check_enclosure_override(fdc_p, vb); | ||||
return 0; | return 0; | |||
} | } | |||
/* Currently only caches associated controller response (4096 bytes) */ | ||||
static int | ||||
sntl_cache_identity(struct freebsd_dev_channel * fdc_p, int vb) | ||||
{ | ||||
int ret; | ||||
uint32_t pg_sz = sg_get_page_size(); | ||||
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 -ENOMEM; | ||||
} | ||||
ret = sntl_do_identify(fdc_p, 0x1 /* CNS */, 0 /* nsid */, pg_sz, | ||||
fdc_p->nvme_id_ctlp, vb); | ||||
if (0 == ret) | ||||
sntl_check_enclosure_override(fdc_p, vb); | ||||
return (ret < 0) ? sg_convert_errno(-ret) : ret; | ||||
} | ||||
static const char * nvme_scsi_vendor_str = "NVMe "; | static const char * nvme_scsi_vendor_str = "NVMe "; | |||
static const uint16_t inq_resp_len = 36; | static const uint16_t inq_resp_len = 36; | |||
static int | 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 vb) | |||
{ | { | |||
bool evpd; | bool evpd; | |||
bool cp_id_ctl = false; | bool cp_id_ctl = false; | |||
int res; | int res; | |||
uint16_t n, alloc_len, pg_cd; | uint16_t n, alloc_len, pg_cd; | |||
skipping to change at line 2084 | skipping to change at line 2149 | |||
} else | } else | |||
len = 4; | len = 4; | |||
len = (len < alloc_len) ? len : alloc_len; | len = (len < alloc_len) ? len : alloc_len; | |||
ptp->resid = ptp->dxfer_len - (int)len; | ptp->resid = ptp->dxfer_len - (int)len; | |||
if (len > 0) | if (len > 0) | |||
memcpy((uint8_t *)ptp->dxferp, arr, len); | memcpy((uint8_t *)ptp->dxferp, arr, len); | |||
return 0; | 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 | ||||
* NVMe Identify command response's NSZE field. One problem is that in | ||||
* some situations NSZE can be zero: temporarily set RLBA field to 0 | ||||
* (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) | ||||
{ | ||||
bool is_rcap10 = (SCSI_READ_CAPACITY10_OPC == cdbp[0]); | ||||
int res, n, len, alloc_len, dps; | ||||
uint8_t flbas, index, lbads; /* NVMe: 2**LBADS --> Logical Block size */ | ||||
uint32_t lbafx; /* NVME: LBAF0...LBAF15, each 16 bytes */ | ||||
uint32_t pg_sz = sg_get_page_size(); | ||||
uint64_t nsze; | ||||
uint8_t * bp; | ||||
uint8_t * up; | ||||
uint8_t * free_up = NULL; | ||||
struct freebsd_dev_channel * fdc_p; | ||||
uint8_t resp[32]; | ||||
if (vb > 3) | ||||
pr2ws("%s: RCAP%d\n", __func__, (is_rcap10 ? 10 : 16)); | ||||
fdc_p = get_fdc_p(ptp); | ||||
if (NULL == fdc_p) { | ||||
pr2ws("%s: get_fdc_p() failed, no file descriptor ?\n", __func__); | ||||
return -EINVAL; | ||||
} | ||||
up = sg_memalign(pg_sz, pg_sz, &free_up, false); | ||||
if (NULL == up) { | ||||
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); | ||||
if (res < 0) { | ||||
res = sg_convert_errno(-res); | ||||
goto fini; | ||||
} | ||||
memset(resp, 0, sizeof(resp)); | ||||
nsze = sg_get_unaligned_le64(up + 0); | ||||
flbas = up[26]; /* NVME FLBAS field from Identify, want LBAF[flbas] */ | ||||
index = 128 + (4 * (flbas & 0xf)); | ||||
lbafx = sg_get_unaligned_le32(up + index); | ||||
lbads = (lbafx >> 16) & 0xff; /* bits 16 to 23 inclusive, pow2 */ | ||||
if (is_rcap10) { | ||||
alloc_len = 8; /* implicit, not in cdb */ | ||||
if (nsze > 0xffffffff) | ||||
sg_put_unaligned_be32(0xffffffff, resp + 0); | ||||
else if (0 == nsze) /* no good answer here */ | ||||
sg_put_unaligned_be32(0, resp + 0); /* SCSI RLBA field */ | ||||
else | ||||
sg_put_unaligned_be32((uint32_t)(nsze - 1), resp + 0); | ||||
sg_put_unaligned_be32(1 << lbads, resp + 4); /* SCSI LBLIB field */ | ||||
} else { | ||||
alloc_len = sg_get_unaligned_be32(cdbp + 10); | ||||
dps = up[29]; | ||||
if (0x7 & dps) { | ||||
resp[12] = 0x1; | ||||
n = (0x7 & dps) - 1; | ||||
if (n > 0) | ||||
resp[12] |= (n + n); | ||||
} | ||||
if (0 == nsze) /* no good answer here */ | ||||
sg_put_unaligned_be64(0, resp + 0); | ||||
else | ||||
sg_put_unaligned_be64(nsze - 1, resp + 0); | ||||
sg_put_unaligned_be32(1 << lbads, resp + 8); /* SCSI LBLIB field */ | ||||
} | ||||
len = ptp->dxfer_len; | ||||
bp = ptp->dxferp; | ||||
n = 32; | ||||
n = (n < alloc_len) ? n : alloc_len; | ||||
n = (n < len) ? n : len; | ||||
ptp->resid = len - n; | ||||
if (n > 0) | ||||
memcpy(bp, resp, n); | ||||
fini: | ||||
if (free_up) | ||||
free(free_up); | ||||
return res; | ||||
} | ||||
/* Executes NVMe Admin command (or at least forwards it to lower layers). | /* Executes NVMe Admin command (or at least forwards it to lower layers). | |||
* Returns 0 for success, negative numbers are negated 'errno' values from | * Returns 0 for success, negative numbers are negated 'errno' values from | |||
* OS system calls. Positive return values are errors from this package. | * OS system calls. Positive return values are errors from this package. */ | |||
* The time_secs argument is ignored. */ | ||||
static int | static int | |||
sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) | sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int vb) | |||
{ | { | |||
bool scsi_cdb, in_xfer; | bool scsi_cdb, in_xfer; | |||
int n, err, len, io_len; | int n, err, len, io_len; | |||
uint16_t sct_sc, sa; | uint16_t sct_sc, sa; | |||
uint8_t * dxferp; | uint8_t * dxferp; | |||
uint8_t * npc_up; | uint8_t * npc_up; | |||
struct freebsd_dev_channel * fdc_p; | struct freebsd_dev_channel * fdc_p; | |||
struct sg_pt_freebsd_scsi * ptp = &vp->impl; | struct sg_pt_freebsd_scsi * ptp = &vp->impl; | |||
skipping to change at line 2143 | skipping to change at line 2291 | |||
ptp->dev_han = fd; | ptp->dev_han = fd; | |||
fdc_p = devicetable[han]; | fdc_p = devicetable[han]; | |||
} | } | |||
} | } | |||
n = ptp->cdb_len; | n = ptp->cdb_len; | |||
cdbp = (const uint8_t *)ptp->cdb; | cdbp = (const uint8_t *)ptp->cdb; | |||
if (vb > 3) | if (vb > 3) | |||
pr2ws("%s: opcode=0x%x, fd=%d\n", __func__, cdbp[0], fd); | pr2ws("%s: opcode=0x%x, fd=%d\n", __func__, cdbp[0], fd); | |||
scsi_cdb = sg_is_scsi_cdb(cdbp, n); | scsi_cdb = sg_is_scsi_cdb(cdbp, n); | |||
/* nvme_direct is true when NVMe command (64 byte) has been given */ | /* nvme_our_sntl is false when NVMe command (64 byte) has been given */ | |||
ptp->nvme_direct = ! scsi_cdb; | ptp->nvme_our_sntl = scsi_cdb; | |||
fdc_p->nvme_direct = ptp->nvme_direct; | fdc_p->nvme_our_sntl = ptp->nvme_our_sntl; | |||
if (scsi_cdb) { | if (scsi_cdb) { | |||
switch (cdbp[0]) { | switch (cdbp[0]) { | |||
case SCSI_INQUIRY_OPC: | case SCSI_INQUIRY_OPC: | |||
return sntl_inq(ptp, cdbp, vb); | return sntl_inq(ptp, cdbp, vb); | |||
case SCSI_REPORT_LUNS_OPC: | case SCSI_REPORT_LUNS_OPC: | |||
return sntl_rluns(ptp, cdbp, vb); | return sntl_rluns(ptp, cdbp, vb); | |||
case SCSI_TEST_UNIT_READY_OPC: | case SCSI_TEST_UNIT_READY_OPC: | |||
return sntl_tur(ptp, vb); | return sntl_tur(ptp, vb); | |||
case SCSI_REQUEST_SENSE_OPC: | case SCSI_REQUEST_SENSE_OPC: | |||
return sntl_req_sense(ptp, cdbp, vb); | return sntl_req_sense(ptp, cdbp, vb); | |||
case SCSI_SEND_DIAGNOSTIC_OPC: | case SCSI_SEND_DIAGNOSTIC_OPC: | |||
return sntl_senddiag(ptp, cdbp, vb); | return sntl_senddiag(ptp, cdbp, vb); | |||
case SCSI_RECEIVE_DIAGNOSTIC_OPC: | case SCSI_RECEIVE_DIAGNOSTIC_OPC: | |||
return sntl_recvdiag(ptp, cdbp, vb); | return sntl_recvdiag(ptp, cdbp, vb); | |||
case SCSI_MODE_SENSE10_OPC: | case SCSI_MODE_SENSE10_OPC: | |||
case SCSI_MODE_SELECT10_OPC: | case SCSI_MODE_SELECT10_OPC: | |||
return sntl_mode_ss(ptp, cdbp, vb); | return sntl_mode_ss(ptp, cdbp, vb); | |||
case SCSI_READ_CAPACITY10_OPC: | ||||
return sntl_readcap(ptp, cdbp, vb); | ||||
case SCSI_SERVICE_ACT_IN_OPC: | ||||
if (SCSI_READ_CAPACITY16_SA == (cdbp[1] & SCSI_SA_MSK)) | ||||
return sntl_readcap(ptp, cdbp, vb); | ||||
goto fini; | ||||
case SCSI_MAINT_IN_OPC: | case SCSI_MAINT_IN_OPC: | |||
sa = 0x1f & cdbp[1]; /* service action */ | sa = 0x1f & cdbp[1]; /* service action */ | |||
if (SCSI_REP_SUP_OPCS_OPC == sa) | if (SCSI_REP_SUP_OPCS_OPC == sa) | |||
return sntl_rep_opcodes(ptp, cdbp, vb); | return sntl_rep_opcodes(ptp, cdbp, vb); | |||
else if (SCSI_REP_SUP_TMFS_OPC == sa) | else if (SCSI_REP_SUP_TMFS_OPC == sa) | |||
return sntl_rep_tmfs(ptp, cdbp, vb); | return sntl_rep_tmfs(ptp, cdbp, vb); | |||
/* fall through */ | /* fall through */ | |||
default: | default: | |||
fini: | ||||
if (vb > 2) { | if (vb > 2) { | |||
char b[64]; | char b[64]; | |||
sg_get_command_name(cdbp, -1, sizeof(b), b); | sg_get_command_name(cdbp, -1, sizeof(b), b); | |||
pr2ws("%s: no translation to NVMe for SCSI %s command\n", | pr2ws("%s: no translation to NVMe for SCSI %s command\n", | |||
__func__, b); | __func__, b); | |||
} | } | |||
mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE, | mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE, | |||
0, vb); | 0, vb); | |||
return 0; | return 0; | |||
skipping to change at line 2264 | skipping to change at line 2419 | |||
pr2ws("don't IGNORE_NVME"); | pr2ws("don't IGNORE_NVME"); | |||
#endif | #endif | |||
pr2ws("\n"); | pr2ws("\n"); | |||
if (NULL == vp) | if (NULL == vp) | |||
pr2ws("%s: object pointer NULL; fd=%d\n", __func__, fd); | pr2ws("%s: object pointer NULL; fd=%d\n", __func__, fd); | |||
} | } | |||
return -ENOTTY; /* inappropriate ioctl error */ | return -ENOTTY; /* inappropriate ioctl error */ | |||
} | } | |||
#endif /* (HAVE_NVME && (! IGNORE_NVME)) */ | #endif /* (HAVE_NVME && (! IGNORE_NVME)) */ | |||
#if (HAVE_NVME && (! IGNORE_NVME)) | ||||
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; | ||||
} | ||||
#else /* (HAVE_NVME && (! IGNORE_NVME)) */ | ||||
int | ||||
do_nvm_pt(struct sg_pt_base * vp, int submq, int timeout_secs, int verbose) | ||||
{ | ||||
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 | ||||
} | ||||
if (vp) { } | ||||
if (submq) { } | ||||
if (timeout_secs) { } | ||||
return SCSI_PT_DO_NOT_SUPPORTED; | ||||
} | ||||
#endif /* (HAVE_NVME && (! IGNORE_NVME)) */ | ||||
End of changes. 40 change blocks. | ||||
59 lines changed or deleted | 214 lines changed or added |