diff --git a/sbin/nvmecontrol/Makefile b/sbin/nvmecontrol/Makefile index f79a7a2d27e5..436298d191a4 100644 --- a/sbin/nvmecontrol/Makefile +++ b/sbin/nvmecontrol/Makefile @@ -2,7 +2,7 @@ PACKAGE=runtime PROG= nvmecontrol -SRCS= nvmecontrol.c devlist.c firmware.c identify.c logpage.c \ +SRCS= nvmecontrol.c devlist.c firmware.c format.c identify.c logpage.c \ perftest.c reset.c nvme_util.c power.c util.c wdc.c MAN= nvmecontrol.8 diff --git a/sbin/nvmecontrol/format.c b/sbin/nvmecontrol/format.c new file mode 100644 index 000000000000..78a6da7a2b38 --- /dev/null +++ b/sbin/nvmecontrol/format.c @@ -0,0 +1,175 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2018 Alexander Motin + * 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvmecontrol.h" + +static void +format_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, FORMAT_USAGE); + exit(1); +} + +void +format(int argc, char *argv[]) +{ + struct nvme_controller_data cd; + struct nvme_namespace_data nsd; + struct nvme_pt_command pt; + char path[64]; + char *target; + uint32_t nsid; + int ch, fd; + int lbaf = -1, mset = -1, pi = -1, pil = -1, ses = 0; + + if (argc < 2) + format_usage(); + + while ((ch = getopt(argc, argv, "f:m:p:l:EC")) != -1) { + switch ((char)ch) { + case 'f': + lbaf = strtol(optarg, NULL, 0); + break; + case 'm': + mset = strtol(optarg, NULL, 0); + break; + case 'p': + pi = strtol(optarg, NULL, 0); + break; + case 'l': + pil = strtol(optarg, NULL, 0); + break; + case 'E': + ses = 1; + break; + case 'C': + ses = 2; + break; + default: + format_usage(); + } + } + + /* Check that a controller or namespace was specified. */ + if (optind >= argc) + format_usage(); + target = argv[optind]; + + /* + * Check if the specified device node exists before continuing. + * This is a cleaner check for cases where the correct controller + * is specified, but an invalid namespace on that controller. + */ + open_dev(target, &fd, 1, 1); + + /* + * If device node contains "ns", we consider it a namespace, + * otherwise, consider it a controller. + */ + if (strstr(target, NVME_NS_PREFIX) == NULL) { + nsid = NVME_GLOBAL_NAMESPACE_TAG; + + /* We have no previous parameters, so use defaults. */ + if (lbaf < 0) + lbaf = 0; + if (mset < 0) + mset = 0; + if (pi < 0) + pi = 0; + if (pil < 0) + pil = 0; + } else { + parse_ns_str(target, path, &nsid); + + /* + * We send FORMAT commands to the controller, not the namespace, + * since it is an admin cmd. The namespace ID will be specified + * in the command itself. So parse the namespace's device node + * string to get the controller substring and namespace ID. + */ + close(fd); + open_dev(path, &fd, 1, 1); + + /* Check that controller can execute this command. */ + read_controller_data(fd, &cd); + if (((cd.fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) + & NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) && ses == 0) { + fprintf(stderr, "H/w doesn't support per-NS format\n"); + exit(1); + } else if (((cd.fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) + & NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) && ses != 0) { + fprintf(stderr, "H/w doesn't support per-NS erase\n"); + exit(1); + } + + /* Try to keep previous namespace parameters. */ + read_namespace_data(fd, nsid, &nsd); + if (lbaf < 0) + lbaf = (nsd.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) + & NVME_NS_DATA_FLBAS_FORMAT_MASK; + if (mset < 0) + mset = (nsd.flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT) + & NVME_NS_DATA_FLBAS_EXTENDED_MASK; + if (pi < 0) + pi = (nsd.dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) + & NVME_NS_DATA_DPS_MD_START_MASK; + if (pil < 0) + pil = (nsd.dps >> NVME_NS_DATA_DPS_PIT_SHIFT) + & NVME_NS_DATA_DPS_PIT_MASK; + } + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc_fuse = NVME_CMD_SET_OPC(NVME_OPC_FORMAT_NVM); + pt.cmd.nsid = htole32(nsid); + pt.cmd.cdw10 = htole32((ses << 9) + (pil << 8) + (pi << 5) + + (mset << 4) + lbaf); + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(1, "format request failed"); + + if (nvme_completion_is_error(&pt.cpl)) + errx(1, "format request returned error"); + close(fd); + exit(0); +} diff --git a/sbin/nvmecontrol/identify.c b/sbin/nvmecontrol/identify.c index 279cd1873af1..8c11e2bad148 100644 --- a/sbin/nvmecontrol/identify.c +++ b/sbin/nvmecontrol/identify.c @@ -384,8 +384,9 @@ identify_ns(int argc, char *argv[]) { struct nvme_namespace_data nsdata; char path[64]; - int ch, fd, hexflag = 0, hexlength, nsid; + int ch, fd, hexflag = 0, hexlength; int verboseflag = 0; + uint32_t nsid; while ((ch = getopt(argc, argv, "vx")) != -1) { switch ((char)ch) { diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index 390da9f546b6..e617bcd921ce 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -99,7 +99,7 @@ get_log_buffer(uint32_t size) } void -read_logpage(int fd, uint8_t log_page, int nsid, void *payload, +read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size) { struct nvme_pt_command pt; @@ -909,13 +909,13 @@ logpage_help(void) void logpage(int argc, char *argv[]) { - int fd, nsid; + int fd; int log_page = 0, pageflag = false; int binflag = false, hexflag = false, ns_specified; int opt; char *p; char cname[64]; - uint32_t size; + uint32_t nsid, size; void *buf; const char *vendor = NULL; struct logpage_function *f; diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8 index 9967ed809cdd..07bbf05b02ca 100644 --- a/sbin/nvmecontrol/nvmecontrol.8 +++ b/sbin/nvmecontrol/nvmecontrol.8 @@ -1,4 +1,5 @@ .\" +.\" Copyright (c) 2018 Alexander Motin .\" Copyright (c) 2012 Intel Corporation .\" All rights reserved. .\" @@ -33,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 24, 2017 +.Dd March 12, 2018 .Dt NVMECONTROL 8 .Os .Sh NAME @@ -47,6 +48,7 @@ .Op Fl v .Op Fl x .Aq device id +.Aq namespace id .Nm .Ic perftest .Aq Fl n Ar num_threads @@ -73,6 +75,16 @@ .Op Fl a .Aq device id .Nm +.Ic format +.Op Fl f Ar fmt +.Op Fl m Ar mset +.Op Fl o Ar pi +.Op Fl l Ar pil +.Op Fl E +.Op Fl C +.Aq device id +.Aq namespace id +.Nm .Ic power .Op Fl l .Op Fl p power_state @@ -117,6 +129,27 @@ will list all valid vendors and pages. will print the page as hex. .Fl b will print the binary data for the page. +.Ss format +Format either specified namespace, or all namespaces of specified controller, +using specified parameters: +.Ar fmt +LBA Format, +.Ar mset +Metadata Settings, +.Ar pi +Protection Information, +.Ar pil +Protection Information Location. +When formatting specific namespace, existing values are used as defaults. +When formatting all namespaces, all parameters should be specified. +Some controllers may not support formatting or erasing specific or all +namespaces. +Option +.Fl E +enables User Data Erase during format. +Option +.Fl C +enables Cryptographic Erase during format. .Ss wdc The various wdc command retrieve log data from the wdc/hgst drives. The diff --git a/sbin/nvmecontrol/nvmecontrol.c b/sbin/nvmecontrol/nvmecontrol.c index 0b31ee314876..b07a2d00074f 100644 --- a/sbin/nvmecontrol/nvmecontrol.c +++ b/sbin/nvmecontrol/nvmecontrol.c @@ -55,6 +55,7 @@ static struct nvme_function funcs[] = { {"reset", reset, RESET_USAGE}, {"logpage", logpage, LOGPAGE_USAGE}, {"firmware", firmware, FIRMWARE_USAGE}, + {"format", format, FORMAT_USAGE}, {"power", power, POWER_USAGE}, {"wdc", wdc, WDC_USAGE}, {NULL, NULL, NULL}, @@ -163,7 +164,7 @@ read_controller_data(int fd, struct nvme_controller_data *cdata) } void -read_namespace_data(int fd, int nsid, struct nvme_namespace_data *nsdata) +read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata) { struct nvme_pt_command pt; @@ -214,7 +215,7 @@ open_dev(const char *str, int *fd, int show_error, int exit_on_error) } void -parse_ns_str(const char *ns_str, char *ctrlr_str, int *nsid) +parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid) { char *nsloc; diff --git a/sbin/nvmecontrol/nvmecontrol.h b/sbin/nvmecontrol/nvmecontrol.h index d3f6badf0fce..59345f370523 100644 --- a/sbin/nvmecontrol/nvmecontrol.h +++ b/sbin/nvmecontrol/nvmecontrol.h @@ -65,6 +65,9 @@ struct nvme_function { #define FIRMWARE_USAGE \ " nvmecontrol firmware [-s slot] [-f path_to_firmware] [-a] \n" +#define FORMAT_USAGE \ +" nvmecontrol format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] \n" + #define POWER_USAGE \ " nvmecontrol power [-l] [-p new-state [-w workload-hint]] \n" @@ -77,15 +80,16 @@ void perftest(int argc, char *argv[]); void reset(int argc, char *argv[]); void logpage(int argc, char *argv[]); void firmware(int argc, char *argv[]); +void format(int argc, char *argv[]); void power(int argc, char *argv[]); void wdc(int argc, char *argv[]); int open_dev(const char *str, int *fd, int show_error, int exit_on_error); -void parse_ns_str(const char *ns_str, char *ctrlr_str, int *nsid); +void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid); void read_controller_data(int fd, struct nvme_controller_data *cdata); -void read_namespace_data(int fd, int nsid, struct nvme_namespace_data *nsdata); +void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata); void print_hex(void *data, uint32_t length); -void read_logpage(int fd, uint8_t log_page, int nsid, void *payload, +void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size); void gen_usage(struct nvme_function *); void dispatch(int argc, char *argv[], struct nvme_function *f);