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
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

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;
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) {

View File

@ -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;

View File

@ -1,4 +1,5 @@
.\"
.\" Copyright (c) 2018 Alexander Motin <mav@FreeBSD.org>
.\" 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

View File

@ -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;

View File

@ -65,6 +65,9 @@ struct nvme_function {
#define FIRMWARE_USAGE \
" 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 \
" 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 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);