Page MenuHomeFreeBSD

D59536.id186281.diff
No OneTemporary

D59536.id186281.diff

diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile
--- a/sbin/camcontrol/Makefile
+++ b/sbin/camcontrol/Makefile
@@ -18,7 +18,7 @@
SRCS+= identify_ext.c
SRCS+= nc_util.c
.PATH: ${SRCTOP}/sys/dev/nvme
-SRCS+= nvme_util.c nvres.c nvformat.c
+SRCS+= nvme_util.c nvres.c nvformat.c nvfeature.c
# This is verboten
.if ${MACHINE_CPUARCH} == "arm"
WARNS?= 3
diff --git a/sbin/camcontrol/camcontrol.h b/sbin/camcontrol/camcontrol.h
--- a/sbin/camcontrol/camcontrol.h
+++ b/sbin/camcontrol/camcontrol.h
@@ -82,6 +82,9 @@
int nvmeformat(struct cam_device *device, int argc, char **argv,
char *combinedopt, int retry_count, int timeout,
int verbosemode, int err_recover);
+int nvmefeature(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout,
+ int verbosemode, int err_recover);
int camxferrate(struct cam_device *device);
int fwdownload(struct cam_device *device, int argc, char **argv,
char *combinedopt, int printerrors, int task_attr,
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -418,6 +418,15 @@
.Op Fl m Ar mset
.Op Fl p Ar pi
.Nm
+.Ic nvfeature
+.Op device id
+.Op generic args
+.Aq Fl a Cm get | set
+.Op Fl d Ar data
+.Fl f Ar feature
+.Op Fl s Ar arg
+.Op Fl U
+.Nm
.Ic devtype
.Op device id
.Nm
@@ -2887,6 +2896,174 @@
Other types are allowed up to 7, but the only values currently defined in
the NVMe 1.4 specification are 0-3.
.El
+.It Ic nvfeature
+Issue NVMe Get Features and Set Features commands.
+.Bl -tag -width 10n
+.It Fl a Ar action
+Specify the action to perform.
+.Bl -tag -width 8n
+.It Cm get
+Issue a Get Features command.
+.It Cm set
+Issue a Set Features command.
+.El
+.It Fl d Ar data
+Specify the data for the feature bing set.
+.It Fl f Ar feature
+Specify the feature to get or set.
+.Bl -tag -width 15n
+.It Cm arbitration
+(not yet implemented)
+.Pp
+Command arbitration.
+.It Cm powermgmt
+(not yet implemented)
+.Pp
+Power management.
+.It Cm lbaranget
+(not yet implemented)
+.Pp
+LBA range type.
+.It Cm tempthresh
+(not yet implemented)
+.Pp
+Temperature threshold.
+.It Cm erecover
+(not yet implemented)
+.Pp
+Error recovery.
+.It Cm volatilewc
+(not yet implemented)
+.Pp
+Volatile write cache.
+.It Cm nqueues
+(not yet implemented)
+.Pp
+Number of queues.
+.It Cm intrcoalesc
+(not yet implemented)
+.Pp
+Interrupt coalescing.
+.It Cm intrvectcfg
+(not yet implemented)
+.Pp
+Interrupt vector configuration.
+.It Cm wratomicity
+(not yet implemented)
+.Pp
+Write atomicity normal.
+.It Cm asyncevcfg
+(not yet implemented)
+.Pp
+Asynchronous event configuration.
+.It Cm autopwrstate
+(not yet implemented)
+.Pp
+Autonomous power state transition.
+.It Cm hostmem
+(not yet implemented)
+.Pp
+Host memory buffer.
+.It Cm timestamp
+(not yet implemented)
+.Pp
+Timestamp.
+.It Cm keepalive
+(not yet implemented)
+.Pp
+Keep alive timer.
+.It Cm hctherm
+(not yet implemented)
+.Pp
+Host controlled thermal management.
+.It Cm noppwrstate
+(not yet implemented)
+.Pp
+Non-operational power state config.
+.It Cm rdreclvl
+(not yet implemented)
+.Pp
+Read recovery level config.
+.It Cm prdlatencycfg
+(not yet implemented)
+.Pp
+Predictable latency mode config.
+.It Cm prdlatencywin
+(not yet implemented)
+.Pp
+Predictable latency mode window.
+.It Cm lbastatinfattrs
+(not yet implemented)
+.Pp
+LBA status information attributes.
+.It Cm hostbehavior
+(not yet implemented)
+.Pp
+Host behavior support.
+.It Cm sanitizecfg
+(not yet implemented)
+.Pp
+Sanitize config.
+.It Cm egroupevcfg
+(not yet implemented)
+.Pp
+Endurance group event configuration.
+.It Cm swprogmarker
+(not yet implemented)
+.Pp
+Software progress marker.
+.It Cm hostid
+Host identifier.
+The hostid value is specified with
+.Fl d Ar data
+as a UUID or an integer in decimal, hex, or octal.
+.It Cm rsvnotmask
+(not yet implemented)
+.Pp
+Reservation notification mask.
+.It Cm rsvpersist
+(not yet implemented)
+.Pp
+Reservation persistence.
+.It Cm nswpcfg
+(not yet implemented)
+.Pp
+Namespace write protection config.
+.It Cm raw
+(not yet implemented)
+.Pp
+Raw feature identified by id and represented in hex.
+.El
+.It Fl s Ar arg
+Specifies an optional, numeric argument for feature get and set.
+Not all features or implementations support these options.
+.Bl -tag -width 4n
+.It Cm get
+When getting a feature value, specifies the feature select mask as follows:
+.Bl -tag -width 4n
+.It Cm 0x0
+Get the feature's current value.
+.It Cm 0x1
+Get the feature's default value.
+.It Cm 0x2
+Get the feature's saved value.
+.It Cm 0x3
+Get the feature's capability mask.
+.El
+.It Cm set
+When setting a feature value, specifies the feature save flag as follows:
+.Bl -tag -width 4n
+.It Cm 0x0
+Do not set the feature's save flag; this modifies the current feature value
+if the feature's capabilities allow.
+.It Cm 0x1
+Set the feature's save flag; this modifies the saved and current feature
+values if the feature's capabilities allow.
+.El
+.El
+.It Fl U
+Output the hostid feature as a UUID.
+.El
.It Ic devtype
Print out the device type for specified device.
.Bl -tag -width 10n
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -114,7 +114,8 @@
CAM_CMD_DEPOP,
CAM_CMD_REQSENSE,
CAM_CMD_NVRES,
- CAM_CMD_NVFORMAT
+ CAM_CMD_NVFORMAT,
+ CAM_CMD_NVFEATURE
} cam_cmd;
typedef enum {
@@ -238,6 +239,7 @@
{"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"},
{"nvres", CAM_CMD_NVRES, CAM_ARG_NONE, "a:eik:p:A:K:T:"},
{"nvformat", CAM_CMD_NVFORMAT, CAM_ARG_NONE, "e:Gf:l:m:p:"},
+ {"nvfeature", CAM_CMD_NVFEATURE, CAM_ARG_NONE, "a:d:f:s:U"},
{"depop", CAM_CMD_DEPOP, CAM_ARG_NONE, "ac:de:ls"},
{"sense", CAM_CMD_REQSENSE, CAM_ARG_NONE, "Dx"},
{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@@ -10165,6 +10167,7 @@
" camcontrol nvres [dev_id][generic args]<-A action> <-a action>\n"
" camcontrol nvformat [dev_id][generic args][-e <user|crypto|num>]\n"
" [-G][-f format][-l pil][-m mset][-p pi]\n"
+" camcontrol nvfeature [dev_id][generic args]<-a action> <-f feature>\n"
" camcontrol devtype [dev_id]\n"
" camcontrol depop [dev_id] [-d | -l | -r] [-e element] [-c capacity]\n"
" camcontrol mmcsdcmd [dev_id] [[-c mmc_opcode] [-a mmc_arg]\n"
@@ -10222,6 +10225,7 @@
"timestamp report or set the device's timestamp\n"
"nvres send NVMe reservation commands\n"
"nvformat send NVMe format command\n"
+"nvfeature send NVMe set / get features commands\n"
"devtype report the type of device\n"
"depop manage drive storage elements\n"
"mmcsdcmd send the given MMC command, needs -c and -a as well\n"
@@ -10445,6 +10449,13 @@
"-l pil Protection Information Location, 1 = start, 0 = end\n"
"-m mset metadata setting, 0 = separate buffer, 1 ext. LBA\n"
"-p pi Protection Information type\n"
+"nvfeature arguments:\n"
+"-a get | set specify get or set features command\n"
+"-d data specify the data for the feature to set\n"
+"-f feature specify the feature to get or set\n"
+"-s num get: select 0=current, 1=default, 2=saved, 3=capabilities\n"
+" set: save flag, 1 = persist feature across reset\n"
+"-U output the hostid attribute as a UUID\n"
"depop arguments:\n"
"-d remove an element from service\n"
"-l list status of all elements of drive\n"
@@ -10890,6 +10901,12 @@
arglist & CAM_ARG_VERBOSE,
arglist & CAM_ARG_ERR_RECOVER);
break;
+ case CAM_CMD_NVFEATURE:
+ error = nvmefeature(cam_dev, argc, argv, combinedopt,
+ retry_count, timeout,
+ arglist & CAM_ARG_VERBOSE,
+ arglist & CAM_ARG_ERR_RECOVER);
+ break;
case CAM_CMD_DEPOP:
error = depop(cam_dev, argc, argv, combinedopt,
task_attr, retry_count, timeout,
diff --git a/sbin/camcontrol/nvfeature.c b/sbin/camcontrol/nvfeature.c
new file mode 100644
--- /dev/null
+++ b/sbin/camcontrol/nvfeature.c
@@ -0,0 +1,541 @@
+/*-
+ * Copyright (c) 2020-2021 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: Reid Linnemann (Spectra Logic Corporation)
+ */
+/*
+ * NVMe Feature support for camcontrol(8).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/endian.h>
+#include <sys/stdint.h>
+#include <sys/types.h>
+#include <sys/sbuf.h>
+
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <uuid.h>
+
+#include <cam/cam.h>
+#include <cam/nvme/nvme_all.h>
+#include <cam/cam_ccb.h>
+#include <dev/nvme/nvme.h>
+#include <camlib.h>
+
+#include "camcontrol.h"
+
+
+#include <cam/scsi/scsi_all.h>
+
+static struct scsi_nv nvfeature_actions[] = {
+ {"get", NVME_OPC_GET_FEATURES},
+ {"set", NVME_OPC_SET_FEATURES}
+};
+
+static struct scsi_nv nvfeature_features[] = {
+ {"arbitration", NVME_FEAT_ARBITRATION},
+ {"powermgmt", NVME_FEAT_POWER_MANAGEMENT},
+ {"lbaranget", NVME_FEAT_LBA_RANGE_TYPE},
+ {"tempthresh", NVME_FEAT_TEMPERATURE_THRESHOLD},
+ {"erecover", NVME_FEAT_ERROR_RECOVERY},
+ {"volatilewc", NVME_FEAT_VOLATILE_WRITE_CACHE},
+ {"nqueues", NVME_FEAT_NUMBER_OF_QUEUES},
+ {"intrcoalesc", NVME_FEAT_INTERRUPT_COALESCING},
+ {"intrvectcfg", NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION},
+ {"wratomicity", NVME_FEAT_WRITE_ATOMICITY},
+ {"asyncevcfg", NVME_FEAT_ASYNC_EVENT_CONFIGURATION},
+ {"autopwrstate", NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION},
+ {"hostmem", NVME_FEAT_HOST_MEMORY_BUFFER},
+ {"timestamp", NVME_FEAT_TIMESTAMP},
+ {"keepalive", NVME_FEAT_KEEP_ALIVE_TIMER},
+ {"hctherm", NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT},
+ {"noppwrstate", NVME_FEAT_NON_OP_POWER_STATE_CONFIG},
+ {"rdreclvl", NVME_FEAT_READ_RECOVERY_LEVEL_CONFIG},
+ {"prdlatencycfg", NVME_FEAT_PREDICTABLE_LATENCY_MODE_CONFIG},
+ {"prdlatencywin", NVME_FEAT_PREDICTABLE_LATENCY_MODE_WINDOW},
+ {"lbastatinfattrs", NVME_FEAT_LBA_STATUS_INFORMATION_ATTRIBUTES},
+ {"hostbehavior", NVME_FEAT_HOST_BEHAVIOR_SUPPORT},
+ {"sanitizecfg", NVME_FEAT_SANITIZE_CONFIG},
+ {"egroupevcfg", NVME_FEAT_ENDURANCE_GROUP_EVENT_CONFIGURATION},
+ {"swprogmarker", NVME_FEAT_SOFTWARE_PROGRESS_MARKER},
+ {"hostid", NVME_FEAT_HOST_IDENTIFIER},
+ {"rsvnotmask", NVME_FEAT_RESERVATION_NOTIFICATION_MASK},
+ {"rsvpersist", NVME_FEAT_RESERVATION_PERSISTENCE},
+ {"nswpcfg", NVME_FEAT_NAMESPACE_WRITE_PROTECTION_CONFIG},
+ {"raw", 0} /* raw feature on commandline */
+};
+
+
+/*
+ * If bit 4 is set to '1' in the Optional NVM Command Support (ONCS) field
+ * of the Identify Controller data then feature get supports these
+ * select values
+ */
+enum nvme_feature_select {
+ NVME_FEAT_SEL_CURRENT = 0x0,
+ NVME_FEAT_SEL_DEFAULT = 0x1,
+ NVME_FEAT_SEL_SAVED = 0x2,
+ NVME_FEAT_SEL_CAPABILITIES = 0x3,
+};
+
+static const char *nvfeature_select_to_str[] = {
+ [NVME_FEAT_SEL_CURRENT] = "current",
+ [NVME_FEAT_SEL_DEFAULT] = "default",
+ [NVME_FEAT_SEL_SAVED] = "saved",
+ [NVME_FEAT_SEL_CAPABILITIES] = "capabilities",
+};
+
+/*
+ * Get feature command completion when select is NVME_FEAT_SELECT_CAPABILITIES
+ */
+enum nvme_feature_capabilities {
+ NVME_FEAT_CAPS_SAVEABLE = 0x1,
+ NVME_FEAT_CAPS_NS_SPECIFIC = 0x2,
+ NVME_FEAT_CAPS_CHANGEABLE = 0x4,
+};
+
+static const char *nvfeature_caps_to_str[] = {
+ [NVME_FEAT_CAPS_SAVEABLE] = "saveable",
+ [NVME_FEAT_CAPS_NS_SPECIFIC] = "ns-specific",
+ [NVME_FEAT_CAPS_CHANGEABLE] = "changeable",
+};
+
+static int
+nvfeature_validate_select(int sel)
+{
+ switch (sel) {
+ case NVME_FEAT_SEL_CURRENT:
+ case NVME_FEAT_SEL_DEFAULT:
+ case NVME_FEAT_SEL_SAVED:
+ case NVME_FEAT_SEL_CAPABILITIES:
+ return 1;
+
+ default:
+ warnx("%s: feature select value 0x%x must be one of "
+ "0x%x(%s), 0x%x(%s), "
+ "0x%x(%s), or 0x%x(%s)", __func__, sel,
+ NVME_FEAT_SEL_CURRENT,
+ nvfeature_select_to_str[NVME_FEAT_SEL_CURRENT],
+ NVME_FEAT_SEL_DEFAULT,
+ nvfeature_select_to_str[NVME_FEAT_SEL_DEFAULT],
+ NVME_FEAT_SEL_SAVED,
+ nvfeature_select_to_str[NVME_FEAT_SEL_SAVED],
+ NVME_FEAT_SEL_CAPABILITIES,
+ nvfeature_select_to_str[NVME_FEAT_SEL_CAPABILITIES]);
+ break;
+ }
+ return 0;
+}
+
+static int
+nvfeature_validate_save(int save)
+{
+ if (save < 0 || save > 1) {
+ warnx("%s: feature save value must either 0 or 1", __func__);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+nvfeature_print_caps(union ccb *ccb, const char *msg)
+{
+ uint32_t cdw0 = le32toh(ccb->nvmeio.cpl.cdw0);
+ int p = 0;
+ int b = 1;
+
+ printf("%s 0x%.4x", msg, cdw0);
+ if (cdw0 & NVME_FEAT_CAPS_CHANGEABLE) {
+ printf("%s%s",
+ b ? " [" : "",
+ nvfeature_caps_to_str[NVME_FEAT_CAPS_CHANGEABLE]);
+ p++;
+ b = 0;
+ }
+ if (cdw0 & NVME_FEAT_CAPS_NS_SPECIFIC) {
+ printf("%s%s%s",
+ b ? " [" : "",
+ p ? " | " : "",
+ nvfeature_caps_to_str[NVME_FEAT_CAPS_NS_SPECIFIC]);
+ p++;
+ b = 0;
+ }
+ if (cdw0 & NVME_FEAT_CAPS_SAVEABLE) {
+ printf("%s%s%s",
+ b ? " [" : "",
+ p ? " | " : "",
+ nvfeature_caps_to_str[NVME_FEAT_CAPS_SAVEABLE]);
+ p++;
+ b = 0;
+ }
+ if (b == 0)
+ printf("]\n");
+ else
+ printf("\n");
+ return 0;
+}
+
+static int
+nvfeature_print_hostid(struct cam_device *device, uint8_t *hostid,
+ int sel, int output_uuid)
+{
+ int error = 0;
+ char *hostid_str = NULL;
+
+ if (output_uuid == 1) {
+ uint32_t status = 0;
+ uint8_t xhostid[16];
+
+ /* XXX: uint8_t to 'uuid_t *' increases required alignment
+ * from 1 to 4 [-Werror,-Wcast-align]
+ */
+ memcpy(xhostid, hostid, sizeof(xhostid));
+ nvme_le128tobe(xhostid);
+
+ uuid_to_string((uuid_t *)xhostid, &hostid_str, &status);
+ if (status != uuid_s_ok) {
+ warn("failed to convert host ID to UUID\n");
+ error = 1;
+ goto bailout;
+ }
+ } else {
+ int exhid;
+ struct sbuf *hostid_sbuf;
+ struct nvme_controller_data cdata;
+
+ error = nvme_get_data(device, CDAI_TYPE_NVME_CNTRL,
+ &cdata, sizeof(cdata));
+ if (error != 0) {
+ goto bailout;
+ }
+ exhid = cdata.ctratt & NVME_CD_CTRATT_EXHID;
+
+ hostid_sbuf = sbuf_new(NULL, NULL, 64, SBUF_FIXEDLEN);
+ if (hostid_sbuf == NULL) {
+ warn("error allocating sbuf");
+ error = 1;
+ goto bailout;
+ }
+
+ if (exhid) {
+ nvme_le128toh(hostid);
+ } else {
+ hostid[0] = le64dec((void*)hostid);
+ }
+ nvme_hostid_sbuf(hostid_sbuf, hostid, exhid);
+ sbuf_finish(hostid_sbuf);
+ hostid_str = strndup(sbuf_data(hostid_sbuf), 64);
+ sbuf_delete(hostid_sbuf);
+ }
+ printf("Host Identifier (%s): %s\n",
+ nvfeature_select_to_str[sel], hostid_str);
+
+bailout:
+ if (hostid_str != NULL)
+ free(hostid_str);
+
+ return error;
+}
+
+static int
+nvmefeature_get_hostid(struct cam_device *device, int sel, int verbosemode,
+ int err_recover, int output_uuid, int retry_count,
+ int timeout)
+{
+ union ccb *ccb = NULL;
+ uint8_t hostid[16];
+ uint8_t *data = NULL;
+ uint32_t datalen = 0;
+ int error = 0;
+
+ if (! nvfeature_validate_select(sel)) {
+ error = 1;
+ goto bailout;
+ }
+
+ ccb = cam_getccb(device);
+ if (ccb == NULL) {
+ warnx("%s: error allocating CCB", __func__);
+ error = 1;
+ goto bailout;
+ }
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+ /* Getting capabilities does not output as uuid nor
+ * is the data buffer (hostid) used/needed
+ */
+ if (sel == NVME_FEAT_SEL_CAPABILITIES) {
+ output_uuid = 0;
+ } else {
+ bzero(hostid, sizeof(hostid));
+ data = hostid;
+ datalen = sizeof(hostid);
+ }
+ nvme_get_feature_hostid(/*nvmeio*/ &ccb->nvmeio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*sel*/ sel,
+ /*uuid_idx*/ 0,
+ /*hostid*/ data,
+ /*dxfer_len*/ datalen,
+ /*timeout*/ timeout);
+
+ if (err_recover != 0)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ warnx("%s: error sending NVMe Get Feature", __func__);
+ error = 1;
+ goto bailout;
+ }
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (verbosemode != 0) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ error = 1;
+ goto bailout;
+ }
+
+ if (sel == NVME_FEAT_SEL_CAPABILITIES) {
+ error = nvfeature_print_caps(ccb, "Host ID capabilities:");
+ } else {
+ error = nvfeature_print_hostid(device, hostid, sel,
+ output_uuid);
+ }
+
+bailout:
+ if (ccb != NULL)
+ free(ccb);
+
+ return error;
+}
+
+static int
+nvmefeature_set_hostid(struct cam_device *device, int save, int verbosemode,
+ int err_recover, char *hostid_str,
+ int retry_count, int timeout)
+{
+ uint8_t data[16];
+ uuid_t hostid;
+ union ccb *ccb = NULL;
+ uint32_t status = 0;
+ int exhid = 0;
+ int error = 0;
+
+
+ if (! nvfeature_validate_save(save)) {
+ error = 1;
+ goto bailout;
+ }
+
+ if (hostid_str == NULL) {
+ warnx("%s: no host id provided", __func__);
+ error = 1;
+ goto bailout;
+ }
+
+ ccb = cam_getccb(device);
+ if (ccb == NULL) {
+ warnx("%s: error allocating CCB", __func__);
+ error = 1;
+ goto bailout;
+ }
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+ uuid_from_string(hostid_str, &hostid, &status);
+ if (status == uuid_s_ok) {
+ exhid = 1;
+ uuid_enc_be((void *)data, &hostid);
+ } else {
+ char *end;
+ uintmax_t id = strtoumax((const char *)hostid_str, &end, 0);
+ if (*end != '\0') {
+ warnx("%s: error parsing hostid value", __func__);
+ error = 1;
+ goto bailout;
+ }
+ ((uint64_t*)data)[0] = htole64(id);
+ }
+ nvme_set_feature_hostid(/*nvmeio*/ &ccb->nvmeio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*save*/ save,
+ /*exhid*/ exhid,
+ /*data_ptr*/ data,
+ /*dxfer_len*/ sizeof(data),
+ /*timeout*/ timeout);
+ if (err_recover != 0)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ warnx("error sending NVMe Set Feature");
+ error = 1;
+ goto bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (verbosemode != 0) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ error = 1;
+ goto bailout;
+ }
+
+bailout:
+ if (ccb != NULL) {
+ cam_freeccb(ccb);
+ }
+ return error;
+}
+
+int
+nvmefeature(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout,
+ int verbosemode, int err_recover)
+{
+ int error = 0;
+ int s = 0;
+ struct scsi_nv *action = NULL, *feature = NULL;
+ char *data_str = NULL;
+ char *endptr = NULL;
+ int output_uuid = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch (c) {
+ case 'a':{
+ scsi_nv_status status;
+ int table_entry = 0;
+
+ status = scsi_get_nv(nvfeature_actions,
+ sizeof(nvfeature_actions) /
+ sizeof(nvfeature_actions[0]),
+ optarg, &table_entry,
+ SCSI_NV_FLAG_IG_CASE);
+ if (status == SCSI_NV_FOUND)
+ action = &nvfeature_actions[table_entry];
+ else {
+ warnx("%s: %s nvfeature operation %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ }
+ case 'd':
+ data_str = strndup((const char *)optarg, 64);
+ break;
+ case 'f': {
+ scsi_nv_status status;
+ int table_entry = 0;
+
+ status = scsi_get_nv(nvfeature_features,
+ sizeof(nvfeature_features) /
+ sizeof(nvfeature_features[0]),
+ optarg, &table_entry,
+ SCSI_NV_FLAG_IG_CASE);
+ if (status == SCSI_NV_FOUND)
+ feature = &nvfeature_features[table_entry];
+ else {
+ warnx("%s: %s nvfeature feature %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ }
+ case 's':
+ s = strtol(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("%s: invalid -s \"%s\", must be a number",
+ __func__, optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ case 'U':
+ output_uuid = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (action->value) {
+ case NVME_OPC_SET_FEATURES:
+ switch (feature->value) {
+ case NVME_FEAT_HOST_IDENTIFIER:
+ error = nvmefeature_set_hostid(device, /*save*/s,
+ verbosemode, err_recover, data_str,
+ retry_count, timeout);
+ break;
+ default:
+ warnx("%s: set feature %s not yet implemented",
+ __func__, feature->name);
+ error = 1;
+ }
+ break;
+
+ case NVME_OPC_GET_FEATURES:
+ switch (feature->value) {
+ case NVME_FEAT_HOST_IDENTIFIER:
+ error = nvmefeature_get_hostid(device, /*select*/s,
+ verbosemode, err_recover, output_uuid,
+ retry_count, timeout);
+ break;
+ default:
+ warnx("%s: get feature %s not yet implemented",
+ __func__, feature->name);
+ error = 1;
+
+ }
+ break;
+ default:
+ error = 1;
+ goto bailout;
+ }
+bailout:
+ if (data_str != NULL) {
+ free(data_str);
+ }
+ return error;
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Sep 23, 4:37 PM (5 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39480767
Default Alt Text
D59536.id186281.diff (21 KB)

Event Timeline