"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "lib/sg_pt_freebsd.c" between
sdparm-1.11.tgz and sdparm-1.12.tgz

About: sdparm let you access SCSI modes pages, read VPD pages, send simple SCSI commands (similar functionality for SCSI disks like "hdparm" for ATA disks).

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

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)