diff options
| -rw-r--r-- | sbin/camcontrol/Makefile | 2 | ||||
| -rw-r--r-- | sbin/camcontrol/camcontrol.8 | 182 | ||||
| -rw-r--r-- | sbin/camcontrol/camcontrol.c | 33 | ||||
| -rw-r--r-- | sbin/camcontrol/camcontrol.h | 3 | ||||
| -rw-r--r-- | sbin/camcontrol/nvfeature.c | 539 |
5 files changed, 757 insertions, 2 deletions
diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile index 15b51dcd76df..5ca678a42e15 100644 --- a/sbin/camcontrol/Makefile +++ b/sbin/camcontrol/Makefile @@ -18,7 +18,7 @@ CFLAGS+= -I${SRCTOP}/sbin/nvmecontrol 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.8 b/sbin/camcontrol/camcontrol.8 index 3452d8f5e513..9ecc7e6872a1 100644 --- a/sbin/camcontrol/camcontrol.8 +++ b/sbin/camcontrol/camcontrol.8 @@ -375,6 +375,15 @@ .Op generic args .Ao Fl r Oo Ns Fl f Ar format | Fl m | Fl U Oc | Fl s Ao Fl f Ar format Fl T Ar time | Fl U Ac Ac .Nm +.Ic feature +.Op device id +.Op generic args +.Aq Fl a Cm get | set +.Fl f Ar feature +.Op Fl d Ar data +.Op Fl s Ar arg +.Op Fl U +.Nm .Ic devtype .Op device id .Nm @@ -2809,6 +2818,179 @@ option. Set the timestamp to the host system's time in UTC. .El .El +.It Ic feature +Issue +.Tn NVMe +Get Features and Set Features commands. +Only supported for +.Tn NVMe +devices. +.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 being 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 index 168804fa9a6e..28a2d39606a6 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -112,7 +112,8 @@ typedef enum { CAM_CMD_DEVTYPE, CAM_CMD_AMA, CAM_CMD_DEPOP, - CAM_CMD_REQSENSE + CAM_CMD_REQSENSE, + CAM_CMD_FEATURE } cam_cmd; typedef enum { @@ -234,6 +235,7 @@ static struct camcontrol_opts option_table[] = { {"zone", CAM_CMD_ZONE, CAM_ARG_NONE, "ac:l:No:P:"}, {"epc", CAM_CMD_EPC, CAM_ARG_NONE, "c:dDeHp:Pr:sS:T:"}, {"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"}, + {"feature", CAM_CMD_FEATURE, 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}, @@ -10160,6 +10162,8 @@ usage(int printlong) " [-S power_src] [-T timer]\n" " camcontrol timestamp [dev_id][generic_args] <-r [-f format|-m|-U]>|\n" " <-s <-f format -T time | -U >>\n" +" camcontrol feature [dev_id][generic args]<-a action> <-f feature>\n" +" [-d data][-s arg][-U]\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" @@ -10215,6 +10219,7 @@ usage(int printlong) "zone manage Zoned Block (Shingled) devices\n" "epc send ATA Extended Power Conditions commands\n" "timestamp report or set the device's timestamp\n" +"feature get or set NVMe device features\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" @@ -10437,6 +10442,13 @@ usage(int printlong) "-f format the format of the time string passed into strptime(3)\n" "-T time the time value passed into strptime(3)\n" "-U set the timestamp of the device to UTC time\n" +"feature 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" @@ -10899,6 +10911,25 @@ main(int argc, char **argv) task_attr, retry_count, timeout, arglist & CAM_ARG_VERBOSE); break; + case CAM_CMD_FEATURE: { + struct ccb_pathinq cpi; + + if (get_cpi(cam_dev, &cpi) != 0) { + warnx("couldn't get CPI"); + error = 1; + break; + } + if (cpi.protocol != PROTO_NVME) { + warnx("the feature subcommand is only supported for " + "NVMe devices"); + error = 1; + break; + } + 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/camcontrol.h b/sbin/camcontrol/camcontrol.h index 86950badced4..122a62a1bf98 100644 --- a/sbin/camcontrol/camcontrol.h +++ b/sbin/camcontrol/camcontrol.h @@ -83,6 +83,9 @@ int nvmepersist(struct cam_device *device, int argc, char **argv, 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/nvfeature.c b/sbin/camcontrol/nvfeature.c new file mode 100644 index 000000000000..f288365657f9 --- /dev/null +++ b/sbin/camcontrol/nvfeature.c @@ -0,0 +1,539 @@ +/*- + * 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/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; + uuid_t uuid; + + uuid_dec_be(hostid, &uuid); + uuid_to_string(&uuid, &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; + } + + 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] = { 0 }; + 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; + } + le64enc(data, id); + } + nvme_set_feature_hostid(/*nvmeio*/ &ccb->nvmeio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*save*/ save, + /*exhid*/ exhid, + /*data_ptr*/ data, + /*dxfer_len*/ exhid ? 16 : 8, + /*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; + } + } + + if (action == NULL) { + warnx("%s: you must specify an action with -a", __func__); + error = 1; + goto bailout; + } + if (feature == NULL) { + warnx("%s: you must specify a feature with -f", __func__); + error = 1; + goto bailout; + } + + 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; +} |
