Add nvmecontrol format subcommand.

It allows to change namespace parameters, such as block size, metadata,
protection information, etc. and/or erase the data.

MFC after:	2 weeks
Sponsored by:	iXsystems, Inc.
This commit is contained in:
mav 2018-03-13 03:02:09 +00:00
parent bb71ae0672
commit d37531263a
7 changed files with 225 additions and 11 deletions

View File

@ -2,7 +2,7 @@
PACKAGE=runtime PACKAGE=runtime
PROG= nvmecontrol 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 perftest.c reset.c nvme_util.c power.c util.c wdc.c
MAN= nvmecontrol.8 MAN= nvmecontrol.8

175
sbin/nvmecontrol/format.c Normal file
View File

@ -0,0 +1,175 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (C) 2018 Alexander Motin <mav@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/ioccom.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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);
}

View File

@ -384,8 +384,9 @@ identify_ns(int argc, char *argv[])
{ {
struct nvme_namespace_data nsdata; struct nvme_namespace_data nsdata;
char path[64]; char path[64];
int ch, fd, hexflag = 0, hexlength, nsid; int ch, fd, hexflag = 0, hexlength;
int verboseflag = 0; int verboseflag = 0;
uint32_t nsid;
while ((ch = getopt(argc, argv, "vx")) != -1) { while ((ch = getopt(argc, argv, "vx")) != -1) {
switch ((char)ch) { switch ((char)ch) {

View File

@ -99,7 +99,7 @@ get_log_buffer(uint32_t size)
} }
void 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) uint32_t payload_size)
{ {
struct nvme_pt_command pt; struct nvme_pt_command pt;
@ -909,13 +909,13 @@ logpage_help(void)
void void
logpage(int argc, char *argv[]) logpage(int argc, char *argv[])
{ {
int fd, nsid; int fd;
int log_page = 0, pageflag = false; int log_page = 0, pageflag = false;
int binflag = false, hexflag = false, ns_specified; int binflag = false, hexflag = false, ns_specified;
int opt; int opt;
char *p; char *p;
char cname[64]; char cname[64];
uint32_t size; uint32_t nsid, size;
void *buf; void *buf;
const char *vendor = NULL; const char *vendor = NULL;
struct logpage_function *f; struct logpage_function *f;

View File

@ -1,4 +1,5 @@
.\" .\"
.\" Copyright (c) 2018 Alexander Motin <mav@FreeBSD.org>
.\" Copyright (c) 2012 Intel Corporation .\" Copyright (c) 2012 Intel Corporation
.\" All rights reserved. .\" All rights reserved.
.\" .\"
@ -33,7 +34,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd February 24, 2017 .Dd March 12, 2018
.Dt NVMECONTROL 8 .Dt NVMECONTROL 8
.Os .Os
.Sh NAME .Sh NAME
@ -47,6 +48,7 @@
.Op Fl v .Op Fl v
.Op Fl x .Op Fl x
.Aq device id .Aq device id
.Aq namespace id
.Nm .Nm
.Ic perftest .Ic perftest
.Aq Fl n Ar num_threads .Aq Fl n Ar num_threads
@ -73,6 +75,16 @@
.Op Fl a .Op Fl a
.Aq device id .Aq device id
.Nm .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 .Ic power
.Op Fl l .Op Fl l
.Op Fl p power_state .Op Fl p power_state
@ -117,6 +129,27 @@ will list all valid vendors and pages.
will print the page as hex. will print the page as hex.
.Fl b .Fl b
will print the binary data for the page. 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 .Ss wdc
The various wdc command retrieve log data from the wdc/hgst drives. The various wdc command retrieve log data from the wdc/hgst drives.
The The

View File

@ -55,6 +55,7 @@ static struct nvme_function funcs[] = {
{"reset", reset, RESET_USAGE}, {"reset", reset, RESET_USAGE},
{"logpage", logpage, LOGPAGE_USAGE}, {"logpage", logpage, LOGPAGE_USAGE},
{"firmware", firmware, FIRMWARE_USAGE}, {"firmware", firmware, FIRMWARE_USAGE},
{"format", format, FORMAT_USAGE},
{"power", power, POWER_USAGE}, {"power", power, POWER_USAGE},
{"wdc", wdc, WDC_USAGE}, {"wdc", wdc, WDC_USAGE},
{NULL, NULL, NULL}, {NULL, NULL, NULL},
@ -163,7 +164,7 @@ read_controller_data(int fd, struct nvme_controller_data *cdata)
} }
void 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; struct nvme_pt_command pt;
@ -214,7 +215,7 @@ open_dev(const char *str, int *fd, int show_error, int exit_on_error)
} }
void 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; char *nsloc;

View File

@ -65,6 +65,9 @@ struct nvme_function {
#define FIRMWARE_USAGE \ #define FIRMWARE_USAGE \
" nvmecontrol firmware [-s slot] [-f path_to_firmware] [-a] <controller id>\n" " nvmecontrol firmware [-s slot] [-f path_to_firmware] [-a] <controller id>\n"
#define FORMAT_USAGE \
" nvmecontrol format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] <controller id|namespace id>\n"
#define POWER_USAGE \ #define POWER_USAGE \
" nvmecontrol power [-l] [-p new-state [-w workload-hint]] <controller id>\n" " nvmecontrol power [-l] [-p new-state [-w workload-hint]] <controller id>\n"
@ -77,15 +80,16 @@ void perftest(int argc, char *argv[]);
void reset(int argc, char *argv[]); void reset(int argc, char *argv[]);
void logpage(int argc, char *argv[]); void logpage(int argc, char *argv[]);
void firmware(int argc, char *argv[]); void firmware(int argc, char *argv[]);
void format(int argc, char *argv[]);
void power(int argc, char *argv[]); void power(int argc, char *argv[]);
void wdc(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); 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_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 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); uint32_t payload_size);
void gen_usage(struct nvme_function *); void gen_usage(struct nvme_function *);
void dispatch(int argc, char *argv[], struct nvme_function *f); void dispatch(int argc, char *argv[], struct nvme_function *f);