mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-06-05 11:35:50 +08:00
parent
e7291f7ac3
commit
94a7b59e06
345
freebsd/sbin/nvmecontrol/comnd.c
Normal file
345
freebsd/sbin/nvmecontrol/comnd.c
Normal file
@ -0,0 +1,345 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2019 Netflix, Inc
|
||||
*
|
||||
* 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 <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <libutil.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "comnd.h"
|
||||
|
||||
static struct cmd top;
|
||||
|
||||
static void
|
||||
print_tree(const struct cmd *f)
|
||||
{
|
||||
|
||||
if (f->parent != NULL)
|
||||
print_tree(f->parent);
|
||||
if (f->name != NULL)
|
||||
fprintf(stderr, " %s", f->name);
|
||||
}
|
||||
|
||||
static void
|
||||
print_usage(const struct cmd *f)
|
||||
{
|
||||
|
||||
fprintf(stderr, " %s", getprogname());
|
||||
print_tree(f->parent);
|
||||
fprintf(stderr, " %-15s - %s\n", f->name, f->descr);
|
||||
}
|
||||
|
||||
static void
|
||||
gen_usage(const struct cmd *t)
|
||||
{
|
||||
struct cmd *walker;
|
||||
|
||||
fprintf(stderr, "usage:\n");
|
||||
SLIST_FOREACH(walker, &t->subcmd, link) {
|
||||
print_usage(walker);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
cmd_dispatch(int argc, char *argv[], const struct cmd *t)
|
||||
{
|
||||
struct cmd *walker;
|
||||
|
||||
if (t == NULL)
|
||||
t = ⊤
|
||||
|
||||
if (argv[1] == NULL) {
|
||||
gen_usage(t);
|
||||
return (1);
|
||||
}
|
||||
SLIST_FOREACH(walker, &t->subcmd, link) {
|
||||
if (strcmp(argv[1], walker->name) == 0) {
|
||||
walker->fn(walker, argc-1, &argv[1]);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Unknown command: %s\n", argv[1]);
|
||||
gen_usage(t);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
arg_suffix(char *buf, size_t len, arg_type at)
|
||||
{
|
||||
switch (at) {
|
||||
case arg_none:
|
||||
break;
|
||||
case arg_string:
|
||||
strlcat(buf, "=<STRING>", len);
|
||||
break;
|
||||
case arg_path:
|
||||
strlcat(buf, "=<FILE>", len);
|
||||
break;
|
||||
default:
|
||||
strlcat(buf, "=<NUM>", len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
arg_help(int argc __unused, char * const *argv, const struct cmd *f)
|
||||
{
|
||||
int i;
|
||||
char buf[31];
|
||||
const struct opts *opts = f->opts;
|
||||
const struct args *args = f->args;
|
||||
|
||||
// XXX walk up the cmd list...
|
||||
if (argv[optind])
|
||||
fprintf(stderr, "Unknown argument: %s\n", argv[optind]);
|
||||
fprintf(stderr, "Usage:\n %s", getprogname());
|
||||
print_tree(f);
|
||||
if (opts)
|
||||
fprintf(stderr, " <args>");
|
||||
if (args) {
|
||||
while (args->descr != NULL) {
|
||||
fprintf(stderr, " %s", args->descr);
|
||||
args++;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n\n%s\n", f->descr);
|
||||
if (opts != NULL) {
|
||||
fprintf(stderr, "Options:\n");
|
||||
for (i = 0; opts[i].long_arg != NULL; i++) {
|
||||
*buf = '\0';
|
||||
if (isprint(opts[i].short_arg)) {
|
||||
snprintf(buf, sizeof(buf), " -%c, ", opts[i].short_arg);
|
||||
} else {
|
||||
strlcpy(buf, " ", sizeof(buf));
|
||||
}
|
||||
strlcat(buf, "--", sizeof(buf));
|
||||
strlcat(buf, opts[i].long_arg, sizeof(buf));
|
||||
arg_suffix(buf, sizeof(buf), opts[i].at);
|
||||
fprintf(stderr, "%-30.30s - %s\n", buf, opts[i].descr);
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
find_long(struct option *lopts, int ch)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; lopts[i].val != ch && lopts[i].name != NULL; i++)
|
||||
continue;
|
||||
return (i);
|
||||
}
|
||||
|
||||
int
|
||||
arg_parse(int argc, char * const * argv, const struct cmd *f)
|
||||
{
|
||||
int i, n, idx, ch;
|
||||
uint64_t v;
|
||||
struct option *lopts;
|
||||
char *shortopts, *p;
|
||||
const struct opts *opts = f->opts;
|
||||
const struct args *args = f->args;
|
||||
|
||||
if (opts == NULL)
|
||||
n = 0;
|
||||
else
|
||||
for (n = 0; opts[n].long_arg != NULL;)
|
||||
n++;
|
||||
lopts = malloc((n + 2) * sizeof(struct option));
|
||||
if (lopts == NULL)
|
||||
err(1, "option memory");
|
||||
p = shortopts = malloc((n + 3) * sizeof(char));
|
||||
if (shortopts == NULL)
|
||||
err(1, "shortopts memory");
|
||||
idx = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
lopts[i].name = opts[i].long_arg;
|
||||
lopts[i].has_arg = opts[i].at == arg_none ? no_argument : required_argument;
|
||||
lopts[i].flag = NULL;
|
||||
lopts[i].val = opts[i].short_arg;
|
||||
if (isprint(opts[i].short_arg)) {
|
||||
*p++ = opts[i].short_arg;
|
||||
if (lopts[i].has_arg)
|
||||
*p++ = ':';
|
||||
}
|
||||
}
|
||||
lopts[n].name = "help";
|
||||
lopts[n].has_arg = no_argument;
|
||||
lopts[n].flag = NULL;
|
||||
lopts[n].val = '?';
|
||||
*p++ = '?';
|
||||
*p++ = '\0';
|
||||
memset(lopts + n + 1, 0, sizeof(struct option));
|
||||
while ((ch = getopt_long(argc, argv, shortopts, lopts, &idx)) != -1) {
|
||||
/*
|
||||
* If ch != 0, we've found a short option, and we have to
|
||||
* look it up lopts table. Otherwise idx is valid.
|
||||
*/
|
||||
if (ch != 0)
|
||||
idx = find_long(lopts, ch);
|
||||
if (idx == n)
|
||||
arg_help(argc, argv, f);
|
||||
switch (opts[idx].at) {
|
||||
case arg_none:
|
||||
*(bool *)opts[idx].ptr = true;
|
||||
break;
|
||||
case arg_string:
|
||||
case arg_path:
|
||||
*(const char **)opts[idx].ptr = optarg;
|
||||
break;
|
||||
case arg_uint8:
|
||||
v = strtoul(optarg, NULL, 0);
|
||||
if (v > 0xff)
|
||||
goto bad_arg;
|
||||
*(uint8_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
case arg_uint16:
|
||||
v = strtoul(optarg, NULL, 0);
|
||||
if (v > 0xffff)
|
||||
goto bad_arg;
|
||||
*(uint16_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
case arg_uint32:
|
||||
v = strtoul(optarg, NULL, 0);
|
||||
if (v > 0xffffffffu)
|
||||
goto bad_arg;
|
||||
*(uint32_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
case arg_uint64:
|
||||
v = strtoul(optarg, NULL, 0);
|
||||
if (v > 0xffffffffffffffffull)
|
||||
goto bad_arg;
|
||||
*(uint64_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
case arg_size:
|
||||
if (expand_number(optarg, &v) < 0)
|
||||
goto bad_arg;
|
||||
*(uint64_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (args) {
|
||||
while (args->descr) {
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "Missing arg %s\n", args->descr);
|
||||
arg_help(argc, argv, f);
|
||||
free(lopts);
|
||||
free(shortopts);
|
||||
return (1);
|
||||
}
|
||||
*(char **)args->ptr = argv[optind++];
|
||||
args++;
|
||||
}
|
||||
}
|
||||
free(lopts);
|
||||
free(shortopts);
|
||||
return (0);
|
||||
bad_arg:
|
||||
fprintf(stderr, "Bad value to --%s: %s\n", opts[idx].long_arg, optarg);
|
||||
free(lopts);
|
||||
free(shortopts);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads all the .so's from the specified directory.
|
||||
*/
|
||||
void
|
||||
cmd_load_dir(const char *dir __unused, cmd_load_cb_t cb __unused, void *argp __unused)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *dent;
|
||||
char *path = NULL;
|
||||
void *h;
|
||||
|
||||
d = opendir(dir);
|
||||
if (d == NULL)
|
||||
return;
|
||||
for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
|
||||
if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0)
|
||||
continue;
|
||||
asprintf(&path, "%s/%s", dir, dent->d_name);
|
||||
if (path == NULL)
|
||||
err(1, "Can't malloc for path, giving up.");
|
||||
if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL)
|
||||
warnx("Can't load %s: %s", path, dlerror());
|
||||
else {
|
||||
if (cb != NULL)
|
||||
cb(argp, h);
|
||||
}
|
||||
free(path);
|
||||
path = NULL;
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
void
|
||||
cmd_register(struct cmd *up, struct cmd *cmd)
|
||||
{
|
||||
struct cmd *walker, *last;
|
||||
|
||||
if (up == NULL)
|
||||
up = ⊤
|
||||
SLIST_INIT(&cmd->subcmd);
|
||||
cmd->parent = up;
|
||||
last = NULL;
|
||||
SLIST_FOREACH(walker, &up->subcmd, link) {
|
||||
if (strcmp(walker->name, cmd->name) > 0)
|
||||
break;
|
||||
last = walker;
|
||||
}
|
||||
if (last == NULL) {
|
||||
SLIST_INSERT_HEAD(&up->subcmd, cmd, link);
|
||||
} else {
|
||||
SLIST_INSERT_AFTER(last, cmd, link);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cmd_init(void)
|
||||
{
|
||||
|
||||
}
|
102
freebsd/sbin/nvmecontrol/comnd.h
Normal file
102
freebsd/sbin/nvmecontrol/comnd.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Netflix, Inc
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef COMND_H
|
||||
#define COMND_H
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/linker_set.h>
|
||||
|
||||
/*
|
||||
* Regularized parsing of simple arguments built on top of getopt_long.
|
||||
*/
|
||||
|
||||
typedef enum arg_type {
|
||||
arg_none = 0,
|
||||
arg_uint8,
|
||||
arg_uint16,
|
||||
arg_uint32,
|
||||
arg_uint64,
|
||||
arg_size,
|
||||
arg_string,
|
||||
arg_path,
|
||||
} arg_type;
|
||||
|
||||
// XXX need to change to offsetof for opts and args.
|
||||
// we then need to allocate ctx and pass that into the cmd
|
||||
// stuff. this will be a little tricky and we may need to expand
|
||||
// arg_type stuff.
|
||||
|
||||
struct opts {
|
||||
const char *long_arg;
|
||||
int short_arg;
|
||||
arg_type at;
|
||||
void *ptr; // XXXX change to offset of
|
||||
const char *descr;
|
||||
};
|
||||
|
||||
// XXX TDB: subcommand vs actual argument. maybe with subcmd?
|
||||
// XXX TBD: do we need parsing callback functions?
|
||||
struct args {
|
||||
arg_type at;
|
||||
void *ptr; // XXXX change to offset of
|
||||
const char *descr;
|
||||
};
|
||||
|
||||
typedef void (cmd_load_cb_t)(void *, void *);
|
||||
struct cmd;
|
||||
typedef void (cmd_fn_t)(const struct cmd *nf, int argc, char *argv[]);
|
||||
|
||||
struct cmd {
|
||||
SLIST_ENTRY(cmd) link;
|
||||
const char *name;
|
||||
cmd_fn_t *fn;
|
||||
size_t ctx_size;
|
||||
const struct opts *opts;
|
||||
const struct args *args;
|
||||
const char *descr;
|
||||
SLIST_HEAD(,cmd) subcmd;
|
||||
struct cmd *parent;
|
||||
};
|
||||
|
||||
void cmd_register(struct cmd *, struct cmd *);
|
||||
#define CMD_COMMAND(c) \
|
||||
static void cmd_register_##c(void) __attribute__((constructor)); \
|
||||
static void cmd_register_##c(void) { cmd_register(NULL, &c); }
|
||||
#define CMD_SUBCOMMAND(c,sc) \
|
||||
static void cmd_register_##c_##sc(void) __attribute__((constructor)); \
|
||||
static void cmd_register_##c_##sc(void) { cmd_register(&c, &sc); }
|
||||
|
||||
int arg_parse(int argc, char * const *argv, const struct cmd *f);
|
||||
void arg_help(int argc, char * const *argv, const struct cmd *f);
|
||||
void cmd_init(void);
|
||||
void cmd_load_dir(const char *dir, cmd_load_cb_t *cb, void *argp);
|
||||
int cmd_dispatch(int argc, char *argv[], const struct cmd *);
|
||||
|
||||
#endif /* COMND_H */
|
131
freebsd/sbin/nvmecontrol/devlist.c
Normal file
131
freebsd/sbin/nvmecontrol/devlist.c
Normal file
@ -0,0 +1,131 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel 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.
|
||||
* 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 <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
#include "comnd.h"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
#define NVME_MAX_UNIT 256
|
||||
|
||||
static cmd_fn_t devlist;
|
||||
|
||||
static struct cmd devlist_cmd = {
|
||||
.name = "devlist",
|
||||
.fn = devlist,
|
||||
.descr = "List NVMe controllers and namespaces"
|
||||
};
|
||||
|
||||
CMD_COMMAND(devlist_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static inline uint32_t
|
||||
ns_get_sector_size(struct nvme_namespace_data *nsdata)
|
||||
{
|
||||
uint8_t flbas_fmt, lbads;
|
||||
|
||||
flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) &
|
||||
NVME_NS_DATA_FLBAS_FORMAT_MASK;
|
||||
lbads = (nsdata->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) &
|
||||
NVME_NS_DATA_LBAF_LBADS_MASK;
|
||||
|
||||
return (1 << lbads);
|
||||
}
|
||||
|
||||
static void
|
||||
devlist(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_controller_data cdata;
|
||||
struct nvme_namespace_data nsdata;
|
||||
char name[64];
|
||||
uint8_t mn[64];
|
||||
uint32_t i;
|
||||
int ctrlr, fd, found, ret;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
ctrlr = -1;
|
||||
found = 0;
|
||||
|
||||
while (ctrlr < NVME_MAX_UNIT) {
|
||||
ctrlr++;
|
||||
sprintf(name, "%s%d", NVME_CTRLR_PREFIX, ctrlr);
|
||||
|
||||
ret = open_dev(name, &fd, 0, 0);
|
||||
|
||||
if (ret == EACCES) {
|
||||
warnx("could not open "_PATH_DEV"%s\n", name);
|
||||
continue;
|
||||
} else if (ret != 0)
|
||||
continue;
|
||||
|
||||
found++;
|
||||
read_controller_data(fd, &cdata);
|
||||
nvme_strvis(mn, cdata.mn, sizeof(mn), NVME_MODEL_NUMBER_LENGTH);
|
||||
printf("%6s: %s\n", name, mn);
|
||||
|
||||
for (i = 0; i < cdata.nn; i++) {
|
||||
read_namespace_data(fd, i + 1, &nsdata);
|
||||
if (nsdata.nsze == 0)
|
||||
continue;
|
||||
sprintf(name, "%s%d%s%d", NVME_CTRLR_PREFIX, ctrlr,
|
||||
NVME_NS_PREFIX, i + 1);
|
||||
printf(" %10s (%lldMB)\n",
|
||||
name,
|
||||
nsdata.nsze *
|
||||
(long long)ns_get_sector_size(&nsdata) /
|
||||
1024 / 1024);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (found == 0)
|
||||
printf("No NVMe controllers found.\n");
|
||||
|
||||
exit(1);
|
||||
}
|
363
freebsd/sbin/nvmecontrol/firmware.c
Normal file
363
freebsd/sbin/nvmecontrol/firmware.c
Normal file
@ -0,0 +1,363 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2013 EMC Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel 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.
|
||||
* 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 <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t firmware;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
static struct options {
|
||||
bool activate;
|
||||
uint32_t slot;
|
||||
const char *fw_img;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.activate = false,
|
||||
.slot = NONE,
|
||||
.fw_img = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts firmware_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("activate", 'a', arg_none, opt, activate,
|
||||
"Attempt to activate firmware"),
|
||||
OPT("slot", 's', arg_uint32, opt, slot,
|
||||
"Slot to activate and/or download firmware to"),
|
||||
OPT("firmware", 'f', arg_path, opt, fw_img,
|
||||
"Firmware image to download"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args firmware_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd firmware_cmd = {
|
||||
.name = "firmware",
|
||||
.fn = firmware,
|
||||
.descr = "Download firmware image to controller",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = firmware_opts,
|
||||
.args = firmware_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(firmware_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static int
|
||||
slot_has_valid_firmware(int fd, int slot)
|
||||
{
|
||||
struct nvme_firmware_page fw;
|
||||
int has_fw = false;
|
||||
|
||||
read_logpage(fd, NVME_LOG_FIRMWARE_SLOT,
|
||||
NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &fw, sizeof(fw));
|
||||
|
||||
if (fw.revision[slot-1] != 0LLU)
|
||||
has_fw = true;
|
||||
|
||||
return (has_fw);
|
||||
}
|
||||
|
||||
static void
|
||||
read_image_file(const char *path, void **buf, int32_t *size)
|
||||
{
|
||||
struct stat sb;
|
||||
int32_t filesize;
|
||||
int fd;
|
||||
|
||||
*size = 0;
|
||||
*buf = NULL;
|
||||
|
||||
if ((fd = open(path, O_RDONLY)) < 0)
|
||||
err(1, "unable to open '%s'", path);
|
||||
if (fstat(fd, &sb) < 0)
|
||||
err(1, "unable to stat '%s'", path);
|
||||
|
||||
/*
|
||||
* The NVMe spec does not explicitly state a maximum firmware image
|
||||
* size, although one can be inferred from the dword size limitation
|
||||
* for the size and offset fields in the Firmware Image Download
|
||||
* command.
|
||||
*
|
||||
* Technically, the max is UINT32_MAX * sizeof(uint32_t), since the
|
||||
* size and offsets are specified in terms of dwords (not bytes), but
|
||||
* realistically INT32_MAX is sufficient here and simplifies matters
|
||||
* a bit.
|
||||
*/
|
||||
if (sb.st_size > INT32_MAX)
|
||||
errx(1, "size of file '%s' is too large (%jd bytes)",
|
||||
path, (intmax_t)sb.st_size);
|
||||
filesize = (int32_t)sb.st_size;
|
||||
if ((*buf = malloc(filesize)) == NULL)
|
||||
errx(1, "unable to malloc %d bytes", filesize);
|
||||
if ((*size = read(fd, *buf, filesize)) < 0)
|
||||
err(1, "error reading '%s'", path);
|
||||
/* XXX assuming no short reads */
|
||||
if (*size != filesize)
|
||||
errx(1,
|
||||
"error reading '%s' (read %d bytes, requested %d bytes)",
|
||||
path, *size, filesize);
|
||||
}
|
||||
|
||||
static void
|
||||
update_firmware(int fd, uint8_t *payload, int32_t payload_size)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
int32_t off, resid, size;
|
||||
void *chunk;
|
||||
|
||||
off = 0;
|
||||
resid = payload_size;
|
||||
|
||||
if ((chunk = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE)) == NULL)
|
||||
errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE);
|
||||
|
||||
while (resid > 0) {
|
||||
size = (resid >= NVME_MAX_XFER_SIZE) ?
|
||||
NVME_MAX_XFER_SIZE : resid;
|
||||
memcpy(chunk, payload + off, size);
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
|
||||
pt.cmd.cdw10 = htole32((size / sizeof(uint32_t)) - 1);
|
||||
pt.cmd.cdw11 = htole32(off / sizeof(uint32_t));
|
||||
pt.buf = chunk;
|
||||
pt.len = size;
|
||||
pt.is_read = 0;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "firmware download request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "firmware download request returned error");
|
||||
|
||||
resid -= size;
|
||||
off += size;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
activate_firmware(int fd, int slot, int activate_action)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
uint16_t sct, sc;
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE;
|
||||
pt.cmd.cdw10 = htole32((activate_action << 3) | slot);
|
||||
pt.is_read = 0;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "firmware activate request failed");
|
||||
|
||||
sct = NVME_STATUS_GET_SCT(pt.cpl.status);
|
||||
sc = NVME_STATUS_GET_SC(pt.cpl.status);
|
||||
|
||||
if (sct == NVME_SCT_COMMAND_SPECIFIC &&
|
||||
sc == NVME_SC_FIRMWARE_REQUIRES_RESET)
|
||||
return 1;
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "firmware activate request returned error");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
firmware(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
int fd = -1;
|
||||
int activate_action, reboot_required;
|
||||
char prompt[64];
|
||||
void *buf = NULL;
|
||||
int32_t size = 0, nsid;
|
||||
uint16_t oacs_fw;
|
||||
uint8_t fw_slot1_ro, fw_num_slots;
|
||||
struct nvme_controller_data cdata;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
if (opt.slot == 0) {
|
||||
fprintf(stderr,
|
||||
"0 is not a valid slot number. "
|
||||
"Slot numbers start at 1.\n");
|
||||
arg_help(argc, argv, f);
|
||||
} else if (opt.slot > 7 && opt.slot != NONE) {
|
||||
fprintf(stderr,
|
||||
"Slot number %s specified which is "
|
||||
"greater than max allowed slot number of "
|
||||
"7.\n", optarg);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
if (!opt.activate && opt.fw_img == NULL) {
|
||||
fprintf(stderr,
|
||||
"Neither a replace ([-f path_to_firmware]) nor "
|
||||
"activate ([-a]) firmware image action\n"
|
||||
"was specified.\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
if (opt.activate && opt.fw_img == NULL && opt.slot == 0) {
|
||||
fprintf(stderr,
|
||||
"Slot number to activate not specified.\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
|
||||
/* Check that a controller (and not a namespace) was specified. */
|
||||
get_nsid(fd, NULL, &nsid);
|
||||
if (nsid != 0) {
|
||||
close(fd);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
read_controller_data(fd, &cdata);
|
||||
|
||||
oacs_fw = (cdata.oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
|
||||
|
||||
if (oacs_fw == 0)
|
||||
errx(1,
|
||||
"controller does not support firmware activate/download");
|
||||
|
||||
fw_slot1_ro = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) &
|
||||
NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK;
|
||||
|
||||
if (opt.fw_img && opt.slot == 1 && fw_slot1_ro)
|
||||
errx(1, "slot %d is marked as read only", opt.slot);
|
||||
|
||||
fw_num_slots = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
|
||||
NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
|
||||
|
||||
if (opt.slot > fw_num_slots)
|
||||
errx(1,
|
||||
"slot %d specified but controller only supports %d slots",
|
||||
opt.slot, fw_num_slots);
|
||||
|
||||
if (opt.activate && opt.fw_img == NULL &&
|
||||
!slot_has_valid_firmware(fd, opt.slot))
|
||||
errx(1,
|
||||
"slot %d does not contain valid firmware,\n"
|
||||
"try 'nvmecontrol logpage -p 3 %s' to get a list "
|
||||
"of available images\n",
|
||||
opt.slot, opt.dev);
|
||||
|
||||
if (opt.fw_img)
|
||||
read_image_file(opt.fw_img, &buf, &size);
|
||||
|
||||
if (opt.fw_img != NULL&& opt.activate)
|
||||
printf("You are about to download and activate "
|
||||
"firmware image (%s) to controller %s.\n"
|
||||
"This may damage your controller and/or "
|
||||
"overwrite an existing firmware image.\n",
|
||||
opt.fw_img, opt.dev);
|
||||
else if (opt.activate)
|
||||
printf("You are about to activate a new firmware "
|
||||
"image on controller %s.\n"
|
||||
"This may damage your controller.\n",
|
||||
opt.dev);
|
||||
else if (opt.fw_img != NULL)
|
||||
printf("You are about to download firmware image "
|
||||
"(%s) to controller %s.\n"
|
||||
"This may damage your controller and/or "
|
||||
"overwrite an existing firmware image.\n",
|
||||
opt.fw_img, opt.dev);
|
||||
|
||||
printf("Are you sure you want to continue? (yes/no) ");
|
||||
while (1) {
|
||||
fgets(prompt, sizeof(prompt), stdin);
|
||||
if (strncasecmp(prompt, "yes", 3) == 0)
|
||||
break;
|
||||
if (strncasecmp(prompt, "no", 2) == 0)
|
||||
exit(1);
|
||||
printf("Please answer \"yes\" or \"no\". ");
|
||||
}
|
||||
|
||||
if (opt.fw_img != NULL) {
|
||||
update_firmware(fd, buf, size);
|
||||
if (opt.activate)
|
||||
activate_action = NVME_AA_REPLACE_ACTIVATE;
|
||||
else
|
||||
activate_action = NVME_AA_REPLACE_NO_ACTIVATE;
|
||||
} else {
|
||||
activate_action = NVME_AA_ACTIVATE;
|
||||
}
|
||||
|
||||
reboot_required = activate_firmware(fd, opt.slot, activate_action);
|
||||
|
||||
if (opt.activate) {
|
||||
if (reboot_required) {
|
||||
printf("New firmware image activated but requires "
|
||||
"conventional reset (i.e. reboot) to "
|
||||
"complete activation.\n");
|
||||
} else {
|
||||
printf("New firmware image activated and will take "
|
||||
"effect after next controller reset.\n"
|
||||
"Controller reset can be initiated via "
|
||||
"'nvmecontrol reset %s'\n",
|
||||
opt.dev);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
222
freebsd/sbin/nvmecontrol/format.c
Normal file
222
freebsd/sbin/nvmecontrol/format.c
Normal file
@ -0,0 +1,222 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
#define SES_NONE 0
|
||||
#define SES_USER 1
|
||||
#define SES_CRYPTO 2
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t format;
|
||||
|
||||
static struct options {
|
||||
uint32_t lbaf;
|
||||
uint32_t ms;
|
||||
uint32_t pi;
|
||||
uint32_t pil;
|
||||
uint32_t ses;
|
||||
bool Eflag;
|
||||
bool Cflag;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.lbaf = NONE,
|
||||
.ms = NONE,
|
||||
.pi = NONE,
|
||||
.pil = NONE,
|
||||
.ses = SES_NONE,
|
||||
.Eflag = false,
|
||||
.Cflag = false,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts format_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("crypto", 'C', arg_none, opt, Cflag,
|
||||
"Crptographic erase"),
|
||||
OPT("erase", 'E', arg_none, opt, Eflag,
|
||||
"User data erase"),
|
||||
OPT("lbaf", 'f', arg_uint32, opt, lbaf,
|
||||
"LBA Format to apply to the media"),
|
||||
OPT("ms", 'm', arg_uint32, opt, ms,
|
||||
"Metadata settings"),
|
||||
OPT("pi", 'p', arg_uint32, opt, pi,
|
||||
"Protective information"),
|
||||
OPT("pil", 'l', arg_uint32, opt, pil,
|
||||
"Protective information location"),
|
||||
OPT("ses", 's', arg_uint32, opt, ses,
|
||||
"Secure erase settings"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args format_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id|namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd format_cmd = {
|
||||
.name = "format",
|
||||
.fn = format,
|
||||
.descr = "Format/erase one or all the namespaces",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = format_opts,
|
||||
.args = format_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(format_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static void
|
||||
format(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_controller_data cd;
|
||||
struct nvme_namespace_data nsd;
|
||||
struct nvme_pt_command pt;
|
||||
char *path;
|
||||
const char *target;
|
||||
uint32_t nsid;
|
||||
int lbaf, ms, pi, pil, ses, fd;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
if ((int)opt.Eflag + opt.Cflag + (opt.ses != SES_NONE) > 1) {
|
||||
fprintf(stderr,
|
||||
"Only one of -E, -C or -s may be specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
target = opt.dev;
|
||||
lbaf = opt.lbaf;
|
||||
ms = opt.ms;
|
||||
pi = opt.pi;
|
||||
pil = opt.pil;
|
||||
if (opt.Eflag)
|
||||
ses = SES_USER;
|
||||
else if (opt.Cflag)
|
||||
ses = SES_CRYPTO;
|
||||
else
|
||||
ses = opt.ses;
|
||||
|
||||
open_dev(target, &fd, 1, 1);
|
||||
get_nsid(fd, &path, &nsid);
|
||||
if (nsid == 0) {
|
||||
nsid = NVME_GLOBAL_NAMESPACE_TAG;
|
||||
} else {
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
free(path);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
read_controller_data(fd, &cd);
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_FORMAT_MASK) == 0)
|
||||
errx(1, "controller does not support format");
|
||||
if (((cd.fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) == 0 && ses == SES_CRYPTO)
|
||||
errx(1, "controller does not support cryptographic erase");
|
||||
|
||||
if (nsid != NVME_GLOBAL_NAMESPACE_TAG) {
|
||||
if (((cd.fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) && ses == SES_NONE)
|
||||
errx(1, "controller does not support per-NS format");
|
||||
if (((cd.fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) && ses != SES_NONE)
|
||||
errx(1, "controller does not support per-NS erase");
|
||||
|
||||
/* 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 (lbaf > nsd.nlbaf)
|
||||
errx(1, "LBA format is out of range");
|
||||
if (ms < 0)
|
||||
ms = (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;
|
||||
} else {
|
||||
|
||||
/* We have no previous parameters, so default to zeroes. */
|
||||
if (lbaf < 0)
|
||||
lbaf = 0;
|
||||
if (ms < 0)
|
||||
ms = 0;
|
||||
if (pi < 0)
|
||||
pi = 0;
|
||||
if (pil < 0)
|
||||
pil = 0;
|
||||
}
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_FORMAT_NVM;
|
||||
pt.cmd.nsid = htole32(nsid);
|
||||
pt.cmd.cdw10 = htole32((ses << 9) + (pil << 8) + (pi << 5) +
|
||||
(ms << 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);
|
||||
}
|
280
freebsd/sbin/nvmecontrol/identify.c
Normal file
280
freebsd/sbin/nvmecontrol/identify.c
Normal file
@ -0,0 +1,280 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel Corporation
|
||||
* All rights reserved.
|
||||
* Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
#include "nvmecontrol_ext.h"
|
||||
|
||||
#define NONE 0xfffffffeu
|
||||
|
||||
static struct options {
|
||||
bool hex;
|
||||
bool verbose;
|
||||
const char *dev;
|
||||
uint32_t nsid;
|
||||
} opt = {
|
||||
.hex = false,
|
||||
.verbose = false,
|
||||
.dev = NULL,
|
||||
.nsid = NONE,
|
||||
};
|
||||
|
||||
void
|
||||
print_namespace(struct nvme_namespace_data *nsdata)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t lbaf, lbads, ms, rp;
|
||||
uint8_t thin_prov, ptype;
|
||||
uint8_t flbas_fmt, t;
|
||||
|
||||
thin_prov = (nsdata->nsfeat >> NVME_NS_DATA_NSFEAT_THIN_PROV_SHIFT) &
|
||||
NVME_NS_DATA_NSFEAT_THIN_PROV_MASK;
|
||||
|
||||
flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) &
|
||||
NVME_NS_DATA_FLBAS_FORMAT_MASK;
|
||||
|
||||
printf("Size (in LBAs): %lld (%lldM)\n",
|
||||
(long long)nsdata->nsze,
|
||||
(long long)nsdata->nsze / 1024 / 1024);
|
||||
printf("Capacity (in LBAs): %lld (%lldM)\n",
|
||||
(long long)nsdata->ncap,
|
||||
(long long)nsdata->ncap / 1024 / 1024);
|
||||
printf("Utilization (in LBAs): %lld (%lldM)\n",
|
||||
(long long)nsdata->nuse,
|
||||
(long long)nsdata->nuse / 1024 / 1024);
|
||||
printf("Thin Provisioning: %s\n",
|
||||
thin_prov ? "Supported" : "Not Supported");
|
||||
printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1);
|
||||
printf("Current LBA Format: LBA Format #%02d\n", flbas_fmt);
|
||||
printf("Data Protection Caps: %s%s%s%s%s%s\n",
|
||||
(nsdata->dpc == 0) ? "Not Supported" : "",
|
||||
((nsdata->dpc >> NVME_NS_DATA_DPC_MD_END_SHIFT) &
|
||||
NVME_NS_DATA_DPC_MD_END_MASK) ? "Last Bytes, " : "",
|
||||
((nsdata->dpc >> NVME_NS_DATA_DPC_MD_START_SHIFT) &
|
||||
NVME_NS_DATA_DPC_MD_START_MASK) ? "First Bytes, " : "",
|
||||
((nsdata->dpc >> NVME_NS_DATA_DPC_PIT3_SHIFT) &
|
||||
NVME_NS_DATA_DPC_PIT3_MASK) ? "Type 3, " : "",
|
||||
((nsdata->dpc >> NVME_NS_DATA_DPC_PIT2_SHIFT) &
|
||||
NVME_NS_DATA_DPC_PIT2_MASK) ? "Type 2, " : "",
|
||||
((nsdata->dpc >> NVME_NS_DATA_DPC_PIT2_MASK) &
|
||||
NVME_NS_DATA_DPC_PIT1_MASK) ? "Type 1" : "");
|
||||
printf("Data Protection Settings: ");
|
||||
ptype = (nsdata->dps >> NVME_NS_DATA_DPS_PIT_SHIFT) &
|
||||
NVME_NS_DATA_DPS_PIT_MASK;
|
||||
if (ptype) {
|
||||
printf("Type %d, %s Bytes\n", ptype,
|
||||
((nsdata->dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) &
|
||||
NVME_NS_DATA_DPS_MD_START_MASK) ? "First" : "Last");
|
||||
} else {
|
||||
printf("Not Enabled\n");
|
||||
}
|
||||
printf("Multi-Path I/O Capabilities: %s%s\n",
|
||||
(nsdata->nmic == 0) ? "Not Supported" : "",
|
||||
((nsdata->nmic >> NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) &
|
||||
NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK) ? "May be shared" : "");
|
||||
printf("Reservation Capabilities: %s%s%s%s%s%s%s%s%s\n",
|
||||
(nsdata->rescap == 0) ? "Not Supported" : "",
|
||||
((nsdata->rescap >> NVME_NS_DATA_RESCAP_IEKEY13_SHIFT) &
|
||||
NVME_NS_DATA_RESCAP_IEKEY13_MASK) ? "IEKEY13, " : "",
|
||||
((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_AR_SHIFT) &
|
||||
NVME_NS_DATA_RESCAP_EX_AC_AR_MASK) ? "EX_AC_AR, " : "",
|
||||
((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_AR_SHIFT) &
|
||||
NVME_NS_DATA_RESCAP_WR_EX_AR_MASK) ? "WR_EX_AR, " : "",
|
||||
((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_RO_SHIFT) &
|
||||
NVME_NS_DATA_RESCAP_EX_AC_RO_MASK) ? "EX_AC_RO, " : "",
|
||||
((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_RO_SHIFT) &
|
||||
NVME_NS_DATA_RESCAP_WR_EX_RO_MASK) ? "WR_EX_RO, " : "",
|
||||
((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_SHIFT) &
|
||||
NVME_NS_DATA_RESCAP_EX_AC_MASK) ? "EX_AC, " : "",
|
||||
((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_SHIFT) &
|
||||
NVME_NS_DATA_RESCAP_WR_EX_MASK) ? "WR_EX, " : "",
|
||||
((nsdata->rescap >> NVME_NS_DATA_RESCAP_PTPL_SHIFT) &
|
||||
NVME_NS_DATA_RESCAP_PTPL_MASK) ? "PTPL" : "");
|
||||
printf("Format Progress Indicator: ");
|
||||
if ((nsdata->fpi >> NVME_NS_DATA_FPI_SUPP_SHIFT) &
|
||||
NVME_NS_DATA_FPI_SUPP_MASK) {
|
||||
printf("%u%% remains\n",
|
||||
(nsdata->fpi >> NVME_NS_DATA_FPI_PERC_SHIFT) &
|
||||
NVME_NS_DATA_FPI_PERC_MASK);
|
||||
} else
|
||||
printf("Not Supported\n");
|
||||
t = (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_READ_SHIFT) &
|
||||
NVME_NS_DATA_DLFEAT_READ_MASK;
|
||||
printf("Deallocate Logical Block: Read %s%s%s\n",
|
||||
(t == NVME_NS_DATA_DLFEAT_READ_NR) ? "Not Reported" :
|
||||
(t == NVME_NS_DATA_DLFEAT_READ_00) ? "00h" :
|
||||
(t == NVME_NS_DATA_DLFEAT_READ_FF) ? "FFh" : "Unknown",
|
||||
(nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_DWZ_SHIFT) &
|
||||
NVME_NS_DATA_DLFEAT_DWZ_MASK ? ", Write Zero" : "",
|
||||
(nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_GCRC_SHIFT) &
|
||||
NVME_NS_DATA_DLFEAT_GCRC_MASK ? ", Guard CRC" : "");
|
||||
printf("Optimal I/O Boundary (LBAs): %u\n", nsdata->noiob);
|
||||
printf("Globally Unique Identifier: ");
|
||||
for (i = 0; i < sizeof(nsdata->nguid); i++)
|
||||
printf("%02x", nsdata->nguid[i]);
|
||||
printf("\n");
|
||||
printf("IEEE EUI64: ");
|
||||
for (i = 0; i < sizeof(nsdata->eui64); i++)
|
||||
printf("%02x", nsdata->eui64[i]);
|
||||
printf("\n");
|
||||
for (i = 0; i <= nsdata->nlbaf; i++) {
|
||||
lbaf = nsdata->lbaf[i];
|
||||
lbads = (lbaf >> NVME_NS_DATA_LBAF_LBADS_SHIFT) &
|
||||
NVME_NS_DATA_LBAF_LBADS_MASK;
|
||||
ms = (lbaf >> NVME_NS_DATA_LBAF_MS_SHIFT) &
|
||||
NVME_NS_DATA_LBAF_MS_MASK;
|
||||
rp = (lbaf >> NVME_NS_DATA_LBAF_RP_SHIFT) &
|
||||
NVME_NS_DATA_LBAF_RP_MASK;
|
||||
printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d"
|
||||
" Performance: %s\n",
|
||||
i, 1 << lbads, ms, (rp == 0) ? "Best" :
|
||||
(rp == 1) ? "Better" : (rp == 2) ? "Good" : "Degraded");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
identify_ctrlr(int fd)
|
||||
{
|
||||
struct nvme_controller_data cdata;
|
||||
int hexlength;
|
||||
|
||||
read_controller_data(fd, &cdata);
|
||||
close(fd);
|
||||
|
||||
if (opt.hex) {
|
||||
if (opt.verbose)
|
||||
hexlength = sizeof(struct nvme_controller_data);
|
||||
else
|
||||
hexlength = offsetof(struct nvme_controller_data,
|
||||
reserved8);
|
||||
print_hex(&cdata, hexlength);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
nvme_print_controller(&cdata);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
identify_ns(int fd, uint32_t nsid)
|
||||
{
|
||||
struct nvme_namespace_data nsdata;
|
||||
int hexlength;
|
||||
|
||||
read_namespace_data(fd, nsid, &nsdata);
|
||||
close(fd);
|
||||
|
||||
if (opt.hex) {
|
||||
if (opt.verbose)
|
||||
hexlength = sizeof(struct nvme_namespace_data);
|
||||
else
|
||||
hexlength = offsetof(struct nvme_namespace_data,
|
||||
reserved6);
|
||||
print_hex(&nsdata, hexlength);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
print_namespace(&nsdata);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
identify(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
char *path;
|
||||
int fd;
|
||||
uint32_t nsid;
|
||||
|
||||
arg_parse(argc, argv, f);
|
||||
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
get_nsid(fd, &path, &nsid);
|
||||
if (nsid != 0) {
|
||||
/*
|
||||
* We got namespace device, but we need to send IDENTIFY
|
||||
* commands to the controller, not the namespace, since it
|
||||
* is an admin cmd. The namespace ID will be specified in
|
||||
* the IDENTIFY command itself.
|
||||
*/
|
||||
close(fd);
|
||||
open_dev(path, &fd, 1, 1);
|
||||
}
|
||||
free(path);
|
||||
if (opt.nsid != NONE)
|
||||
nsid = opt.nsid;
|
||||
|
||||
if (nsid == 0)
|
||||
identify_ctrlr(fd);
|
||||
else
|
||||
identify_ns(fd, nsid);
|
||||
}
|
||||
|
||||
static const struct opts identify_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("hex", 'x', arg_none, opt, hex,
|
||||
"Print identiy information in hex"),
|
||||
OPT("verbose", 'v', arg_none, opt, verbose,
|
||||
"More verbosity: print entire identify table"),
|
||||
OPT("nsid", 'n', arg_uint32, opt, nsid,
|
||||
"Namespace ID to use if not in device name"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args identify_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id|namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd identify_cmd = {
|
||||
.name = "identify",
|
||||
.fn = identify,
|
||||
.descr = "Print summary of the IDENTIFY information",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = identify_opts,
|
||||
.args = identify_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(identify_cmd);
|
249
freebsd/sbin/nvmecontrol/identify_ext.c
Normal file
249
freebsd/sbin/nvmecontrol/identify_ext.c
Normal file
@ -0,0 +1,249 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel Corporation
|
||||
* All rights reserved.
|
||||
* Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
|
||||
*
|
||||
* 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 <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"
|
||||
#include "nvmecontrol_ext.h"
|
||||
|
||||
void
|
||||
nvme_print_controller(struct nvme_controller_data *cdata)
|
||||
{
|
||||
uint8_t str[128];
|
||||
char cbuf[UINT128_DIG + 1];
|
||||
uint16_t oncs, oacs;
|
||||
uint8_t compare, write_unc, dsm, t;
|
||||
uint8_t security, fmt, fw, nsmgmt;
|
||||
uint8_t fw_slot1_ro, fw_num_slots;
|
||||
uint8_t ns_smart;
|
||||
uint8_t sqes_max, sqes_min;
|
||||
uint8_t cqes_max, cqes_min;
|
||||
|
||||
oncs = cdata->oncs;
|
||||
compare = (oncs >> NVME_CTRLR_DATA_ONCS_COMPARE_SHIFT) &
|
||||
NVME_CTRLR_DATA_ONCS_COMPARE_MASK;
|
||||
write_unc = (oncs >> NVME_CTRLR_DATA_ONCS_WRITE_UNC_SHIFT) &
|
||||
NVME_CTRLR_DATA_ONCS_WRITE_UNC_MASK;
|
||||
dsm = (oncs >> NVME_CTRLR_DATA_ONCS_DSM_SHIFT) &
|
||||
NVME_CTRLR_DATA_ONCS_DSM_MASK;
|
||||
|
||||
oacs = cdata->oacs;
|
||||
security = (oacs >> NVME_CTRLR_DATA_OACS_SECURITY_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_SECURITY_MASK;
|
||||
fmt = (oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_FORMAT_MASK;
|
||||
fw = (oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
|
||||
nsmgmt = (oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK;
|
||||
|
||||
fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
|
||||
NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
|
||||
fw_slot1_ro = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) &
|
||||
NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK;
|
||||
|
||||
ns_smart = (cdata->lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
|
||||
NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
|
||||
|
||||
sqes_min = (cdata->sqes >> NVME_CTRLR_DATA_SQES_MIN_SHIFT) &
|
||||
NVME_CTRLR_DATA_SQES_MIN_MASK;
|
||||
sqes_max = (cdata->sqes >> NVME_CTRLR_DATA_SQES_MAX_SHIFT) &
|
||||
NVME_CTRLR_DATA_SQES_MAX_MASK;
|
||||
|
||||
cqes_min = (cdata->cqes >> NVME_CTRLR_DATA_CQES_MIN_SHIFT) &
|
||||
NVME_CTRLR_DATA_CQES_MIN_MASK;
|
||||
cqes_max = (cdata->cqes >> NVME_CTRLR_DATA_CQES_MAX_SHIFT) &
|
||||
NVME_CTRLR_DATA_CQES_MAX_MASK;
|
||||
|
||||
printf("Controller Capabilities/Features\n");
|
||||
printf("================================\n");
|
||||
printf("Vendor ID: %04x\n", cdata->vid);
|
||||
printf("Subsystem Vendor ID: %04x\n", cdata->ssvid);
|
||||
nvme_strvis(str, cdata->sn, sizeof(str), NVME_SERIAL_NUMBER_LENGTH);
|
||||
printf("Serial Number: %s\n", str);
|
||||
nvme_strvis(str, cdata->mn, sizeof(str), NVME_MODEL_NUMBER_LENGTH);
|
||||
printf("Model Number: %s\n", str);
|
||||
nvme_strvis(str, cdata->fr, sizeof(str), NVME_FIRMWARE_REVISION_LENGTH);
|
||||
printf("Firmware Version: %s\n", str);
|
||||
printf("Recommended Arb Burst: %d\n", cdata->rab);
|
||||
printf("IEEE OUI Identifier: %02x %02x %02x\n",
|
||||
cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]);
|
||||
printf("Multi-Path I/O Capabilities: %s%s%s%s%s\n",
|
||||
(cdata->mic == 0) ? "Not Supported" : "",
|
||||
((cdata->mic >> NVME_CTRLR_DATA_MIC_ANAR_SHIFT) &
|
||||
NVME_CTRLR_DATA_MIC_SRIOVVF_MASK) ? "Asymmetric, " : "",
|
||||
((cdata->mic >> NVME_CTRLR_DATA_MIC_SRIOVVF_SHIFT) &
|
||||
NVME_CTRLR_DATA_MIC_SRIOVVF_MASK) ? "SR-IOV VF, " : "",
|
||||
((cdata->mic >> NVME_CTRLR_DATA_MIC_MCTRLRS_SHIFT) &
|
||||
NVME_CTRLR_DATA_MIC_MCTRLRS_MASK) ? "Multiple controllers, " : "",
|
||||
((cdata->mic >> NVME_CTRLR_DATA_MIC_MPORTS_SHIFT) &
|
||||
NVME_CTRLR_DATA_MIC_MPORTS_MASK) ? "Multiple ports" : "");
|
||||
/* TODO: Use CAP.MPSMIN to determine true memory page size. */
|
||||
printf("Max Data Transfer Size: ");
|
||||
if (cdata->mdts == 0)
|
||||
printf("Unlimited\n");
|
||||
else
|
||||
printf("%ld\n", PAGE_SIZE * (1L << cdata->mdts));
|
||||
printf("Controller ID: 0x%04x\n", cdata->ctrlr_id);
|
||||
printf("Version: %d.%d.%d\n",
|
||||
(cdata->ver >> 16) & 0xffff, (cdata->ver >> 8) & 0xff,
|
||||
cdata->ver & 0xff);
|
||||
printf("\n");
|
||||
|
||||
printf("Admin Command Set Attributes\n");
|
||||
printf("============================\n");
|
||||
printf("Security Send/Receive: %s\n",
|
||||
security ? "Supported" : "Not Supported");
|
||||
printf("Format NVM: %s\n",
|
||||
fmt ? "Supported" : "Not Supported");
|
||||
printf("Firmware Activate/Download: %s\n",
|
||||
fw ? "Supported" : "Not Supported");
|
||||
printf("Namespace Managment: %s\n",
|
||||
nsmgmt ? "Supported" : "Not Supported");
|
||||
printf("Device Self-test: %sSupported\n",
|
||||
((oacs >> NVME_CTRLR_DATA_OACS_SELFTEST_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_SELFTEST_MASK) ? "" : "Not ");
|
||||
printf("Directives: %sSupported\n",
|
||||
((oacs >> NVME_CTRLR_DATA_OACS_DIRECTIVES_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_DIRECTIVES_MASK) ? "" : "Not ");
|
||||
printf("NVMe-MI Send/Receive: %sSupported\n",
|
||||
((oacs >> NVME_CTRLR_DATA_OACS_NVMEMI_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NVMEMI_MASK) ? "" : "Not ");
|
||||
printf("Virtualization Management: %sSupported\n",
|
||||
((oacs >> NVME_CTRLR_DATA_OACS_VM_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_VM_MASK) ? "" : "Not ");
|
||||
printf("Doorbell Buffer Config: %sSupported\n",
|
||||
((oacs >> NVME_CTRLR_DATA_OACS_DBBUFFER_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_DBBUFFER_MASK) ? "" : "Not ");
|
||||
printf("Get LBA Status: %sSupported\n",
|
||||
((oacs >> NVME_CTRLR_DATA_OACS_GETLBA_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_GETLBA_MASK) ? "" : "Not ");
|
||||
printf("Sanitize: ");
|
||||
if (cdata->sanicap != 0) {
|
||||
printf("%s%s%s\n",
|
||||
((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) &
|
||||
NVME_CTRLR_DATA_SANICAP_CES_MASK) ? "crypto, " : "",
|
||||
((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) &
|
||||
NVME_CTRLR_DATA_SANICAP_BES_MASK) ? "block, " : "",
|
||||
((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) &
|
||||
NVME_CTRLR_DATA_SANICAP_OWS_MASK) ? "overwrite" : "");
|
||||
} else {
|
||||
printf("Not Supported\n");
|
||||
}
|
||||
printf("Abort Command Limit: %d\n", cdata->acl+1);
|
||||
printf("Async Event Request Limit: %d\n", cdata->aerl+1);
|
||||
printf("Number of Firmware Slots: ");
|
||||
if (fw != 0)
|
||||
printf("%d\n", fw_num_slots);
|
||||
else
|
||||
printf("N/A\n");
|
||||
printf("Firmware Slot 1 Read-Only: ");
|
||||
if (fw != 0)
|
||||
printf("%s\n", fw_slot1_ro ? "Yes" : "No");
|
||||
else
|
||||
printf("N/A\n");
|
||||
printf("Per-Namespace SMART Log: %s\n",
|
||||
ns_smart ? "Yes" : "No");
|
||||
printf("Error Log Page Entries: %d\n", cdata->elpe+1);
|
||||
printf("Number of Power States: %d\n", cdata->npss+1);
|
||||
|
||||
printf("\n");
|
||||
printf("NVM Command Set Attributes\n");
|
||||
printf("==========================\n");
|
||||
printf("Submission Queue Entry Size\n");
|
||||
printf(" Max: %d\n", 1 << sqes_max);
|
||||
printf(" Min: %d\n", 1 << sqes_min);
|
||||
printf("Completion Queue Entry Size\n");
|
||||
printf(" Max: %d\n", 1 << cqes_max);
|
||||
printf(" Min: %d\n", 1 << cqes_min);
|
||||
printf("Number of Namespaces: %d\n", cdata->nn);
|
||||
printf("Compare Command: %s\n",
|
||||
compare ? "Supported" : "Not Supported");
|
||||
printf("Write Uncorrectable Command: %s\n",
|
||||
write_unc ? "Supported" : "Not Supported");
|
||||
printf("Dataset Management Command: %s\n",
|
||||
dsm ? "Supported" : "Not Supported");
|
||||
printf("Write Zeroes Command: %sSupported\n",
|
||||
((oncs >> NVME_CTRLR_DATA_ONCS_WRZERO_SHIFT) &
|
||||
NVME_CTRLR_DATA_ONCS_WRZERO_MASK) ? "" : "Not ");
|
||||
printf("Save Features: %sSupported\n",
|
||||
((oncs >> NVME_CTRLR_DATA_ONCS_SAVEFEAT_SHIFT) &
|
||||
NVME_CTRLR_DATA_ONCS_SAVEFEAT_MASK) ? "" : "Not ");
|
||||
printf("Reservations: %sSupported\n",
|
||||
((oncs >> NVME_CTRLR_DATA_ONCS_RESERV_SHIFT) &
|
||||
NVME_CTRLR_DATA_ONCS_RESERV_MASK) ? "" : "Not ");
|
||||
printf("Timestamp feature: %sSupported\n",
|
||||
((oncs >> NVME_CTRLR_DATA_ONCS_TIMESTAMP_SHIFT) &
|
||||
NVME_CTRLR_DATA_ONCS_TIMESTAMP_MASK) ? "" : "Not ");
|
||||
printf("Verify feature: %sSupported\n",
|
||||
((oncs >> NVME_CTRLR_DATA_ONCS_VERIFY_SHIFT) &
|
||||
NVME_CTRLR_DATA_ONCS_VERIFY_MASK) ? "" : "Not ");
|
||||
printf("Fused Operation Support: %s%s\n",
|
||||
(cdata->fuses == 0) ? "Not Supported" : "",
|
||||
((cdata->fuses >> NVME_CTRLR_DATA_FUSES_CNW_SHIFT) &
|
||||
NVME_CTRLR_DATA_FUSES_CNW_MASK) ? "Compare and Write" : "");
|
||||
printf("Format NVM Attributes: %s%s Erase, %s Format\n",
|
||||
((cdata->fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) ? "Crypto Erase, " : "",
|
||||
((cdata->fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) ? "All-NVM" : "Per-NS",
|
||||
((cdata->fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) ? "All-NVM" : "Per-NS");
|
||||
t = (cdata->vwc >> NVME_CTRLR_DATA_VWC_ALL_SHIFT) &
|
||||
NVME_CTRLR_DATA_VWC_ALL_MASK;
|
||||
printf("Volatile Write Cache: %s%s\n",
|
||||
((cdata->vwc >> NVME_CTRLR_DATA_VWC_PRESENT_SHIFT) &
|
||||
NVME_CTRLR_DATA_VWC_PRESENT_MASK) ? "Present" : "Not Present",
|
||||
(t == NVME_CTRLR_DATA_VWC_ALL_NO) ? ", no flush all" :
|
||||
(t == NVME_CTRLR_DATA_VWC_ALL_YES) ? ", flush all" : "");
|
||||
|
||||
if (nsmgmt) {
|
||||
printf("\n");
|
||||
printf("Namespace Drive Attributes\n");
|
||||
printf("==========================\n");
|
||||
printf("NVM total cap: %s\n",
|
||||
uint128_to_str(to128(cdata->untncap.tnvmcap), cbuf, sizeof(cbuf)));
|
||||
printf("NVM unallocated cap: %s\n",
|
||||
uint128_to_str(to128(cdata->untncap.unvmcap), cbuf, sizeof(cbuf)));
|
||||
}
|
||||
}
|
757
freebsd/sbin/nvmecontrol/logpage.c
Normal file
757
freebsd/sbin/nvmecontrol/logpage.c
Normal file
@ -0,0 +1,757 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2013 EMC Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel Corporation
|
||||
* All rights reserved.
|
||||
* Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/endian.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t logpage;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
static struct options {
|
||||
bool binary;
|
||||
bool hex;
|
||||
uint32_t page;
|
||||
uint8_t lsp;
|
||||
uint16_t lsi;
|
||||
bool rae;
|
||||
const char *vendor;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.binary = false,
|
||||
.hex = false,
|
||||
.page = NONE,
|
||||
.lsp = 0,
|
||||
.lsi = 0,
|
||||
.rae = false,
|
||||
.vendor = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts logpage_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("binary", 'b', arg_none, opt, binary,
|
||||
"Dump the log page as binary"),
|
||||
OPT("hex", 'x', arg_none, opt, hex,
|
||||
"Dump the log page as hex"),
|
||||
OPT("page", 'p', arg_uint32, opt, page,
|
||||
"Page to dump"),
|
||||
OPT("lsp", 'f', arg_uint8, opt, lsp,
|
||||
"Log Specific Field"),
|
||||
OPT("lsi", 'i', arg_uint16, opt, lsp,
|
||||
"Log Specific Identifier"),
|
||||
OPT("rae", 'r', arg_none, opt, rae,
|
||||
"Retain Asynchronous Event"),
|
||||
OPT("vendor", 'v', arg_string, opt, vendor,
|
||||
"Vendor specific formatting"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args logpage_args[] = {
|
||||
{ arg_string, &opt.dev, "<controller id|namespace id>" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd logpage_cmd = {
|
||||
.name = "logpage",
|
||||
.fn = logpage,
|
||||
.descr = "Print logpages in human-readable form",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = logpage_opts,
|
||||
.args = logpage_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(logpage_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
#define MAX_FW_SLOTS (7)
|
||||
|
||||
static SLIST_HEAD(,logpage_function) logpages;
|
||||
|
||||
static int
|
||||
logpage_compare(struct logpage_function *a, struct logpage_function *b)
|
||||
{
|
||||
int c;
|
||||
|
||||
if ((a->vendor == NULL) != (b->vendor == NULL))
|
||||
return (a->vendor == NULL ? -1 : 1);
|
||||
if (a->vendor != NULL) {
|
||||
c = strcmp(a->vendor, b->vendor);
|
||||
if (c != 0)
|
||||
return (c);
|
||||
}
|
||||
return ((int)a->log_page - (int)b->log_page);
|
||||
}
|
||||
|
||||
void
|
||||
logpage_register(struct logpage_function *p)
|
||||
{
|
||||
struct logpage_function *l, *a;
|
||||
|
||||
a = NULL;
|
||||
l = SLIST_FIRST(&logpages);
|
||||
while (l != NULL) {
|
||||
if (logpage_compare(l, p) > 0)
|
||||
break;
|
||||
a = l;
|
||||
l = SLIST_NEXT(l, link);
|
||||
}
|
||||
if (a == NULL)
|
||||
SLIST_INSERT_HEAD(&logpages, p, link);
|
||||
else
|
||||
SLIST_INSERT_AFTER(a, p, link);
|
||||
}
|
||||
|
||||
const char *
|
||||
kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
|
||||
{
|
||||
static char bad[32];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < kv_count; i++, kv++)
|
||||
if (kv->key == key)
|
||||
return kv->name;
|
||||
snprintf(bad, sizeof(bad), "Attribute %#x", key);
|
||||
return bad;
|
||||
}
|
||||
|
||||
static void
|
||||
print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
|
||||
{
|
||||
|
||||
print_hex(data, length);
|
||||
}
|
||||
|
||||
static void
|
||||
print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
|
||||
{
|
||||
|
||||
write(STDOUT_FILENO, data, length);
|
||||
}
|
||||
|
||||
static void *
|
||||
get_log_buffer(uint32_t size)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
if ((buf = malloc(size)) == NULL)
|
||||
errx(1, "unable to malloc %u bytes", size);
|
||||
|
||||
memset(buf, 0, size);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
void
|
||||
read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp,
|
||||
uint16_t lsi, uint8_t rae, void *payload, uint32_t payload_size)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_error_information_entry *err_entry;
|
||||
u_int i, err_pages, numd;
|
||||
|
||||
numd = payload_size / sizeof(uint32_t) - 1;
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_GET_LOG_PAGE;
|
||||
pt.cmd.nsid = htole32(nsid);
|
||||
pt.cmd.cdw10 = htole32(
|
||||
(numd << 16) | /* NUMDL */
|
||||
(rae << 15) | /* RAE */
|
||||
(lsp << 8) | /* LSP */
|
||||
log_page); /* LID */
|
||||
pt.cmd.cdw11 = htole32(
|
||||
((uint32_t)lsi << 16) | /* LSI */
|
||||
(numd >> 16)); /* NUMDU */
|
||||
pt.cmd.cdw12 = 0; /* LPOL */
|
||||
pt.cmd.cdw13 = 0; /* LPOU */
|
||||
pt.cmd.cdw14 = 0; /* UUID Index */
|
||||
pt.buf = payload;
|
||||
pt.len = payload_size;
|
||||
pt.is_read = 1;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "get log page request failed");
|
||||
|
||||
/* Convert data to host endian */
|
||||
switch (log_page) {
|
||||
case NVME_LOG_ERROR:
|
||||
err_entry = (struct nvme_error_information_entry *)payload;
|
||||
err_pages = payload_size / sizeof(struct nvme_error_information_entry);
|
||||
for (i = 0; i < err_pages; i++)
|
||||
nvme_error_information_entry_swapbytes(err_entry++);
|
||||
break;
|
||||
case NVME_LOG_HEALTH_INFORMATION:
|
||||
nvme_health_information_page_swapbytes(
|
||||
(struct nvme_health_information_page *)payload);
|
||||
break;
|
||||
case NVME_LOG_FIRMWARE_SLOT:
|
||||
nvme_firmware_page_swapbytes(
|
||||
(struct nvme_firmware_page *)payload);
|
||||
break;
|
||||
case NVME_LOG_CHANGED_NAMESPACE:
|
||||
nvme_ns_list_swapbytes((struct nvme_ns_list *)payload);
|
||||
break;
|
||||
case NVME_LOG_COMMAND_EFFECT:
|
||||
nvme_command_effects_page_swapbytes(
|
||||
(struct nvme_command_effects_page *)payload);
|
||||
break;
|
||||
case NVME_LOG_RES_NOTIFICATION:
|
||||
nvme_res_notification_page_swapbytes(
|
||||
(struct nvme_res_notification_page *)payload);
|
||||
break;
|
||||
case NVME_LOG_SANITIZE_STATUS:
|
||||
nvme_sanitize_status_page_swapbytes(
|
||||
(struct nvme_sanitize_status_page *)payload);
|
||||
break;
|
||||
case INTEL_LOG_TEMP_STATS:
|
||||
intel_log_temp_stats_swapbytes(
|
||||
(struct intel_log_temp_stats *)payload);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "get log page request returned error");
|
||||
}
|
||||
|
||||
static void
|
||||
print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
|
||||
{
|
||||
int i, nentries;
|
||||
uint16_t status;
|
||||
uint8_t p, sc, sct, m, dnr;
|
||||
struct nvme_error_information_entry *entry = buf;
|
||||
|
||||
printf("Error Information Log\n");
|
||||
printf("=====================\n");
|
||||
|
||||
if (entry->error_count == 0) {
|
||||
printf("No error entries found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nentries = size/sizeof(struct nvme_error_information_entry);
|
||||
for (i = 0; i < nentries; i++, entry++) {
|
||||
if (entry->error_count == 0)
|
||||
break;
|
||||
|
||||
status = entry->status;
|
||||
|
||||
p = NVME_STATUS_GET_P(status);
|
||||
sc = NVME_STATUS_GET_SC(status);
|
||||
sct = NVME_STATUS_GET_SCT(status);
|
||||
m = NVME_STATUS_GET_M(status);
|
||||
dnr = NVME_STATUS_GET_DNR(status);
|
||||
|
||||
printf("Entry %02d\n", i + 1);
|
||||
printf("=========\n");
|
||||
printf(" Error count: %ju\n", entry->error_count);
|
||||
printf(" Submission queue ID: %u\n", entry->sqid);
|
||||
printf(" Command ID: %u\n", entry->cid);
|
||||
/* TODO: Export nvme_status_string structures from kernel? */
|
||||
printf(" Status:\n");
|
||||
printf(" Phase tag: %d\n", p);
|
||||
printf(" Status code: %d\n", sc);
|
||||
printf(" Status code type: %d\n", sct);
|
||||
printf(" More: %d\n", m);
|
||||
printf(" DNR: %d\n", dnr);
|
||||
printf(" Error location: %u\n", entry->error_location);
|
||||
printf(" LBA: %ju\n", entry->lba);
|
||||
printf(" Namespace ID: %u\n", entry->nsid);
|
||||
printf(" Vendor specific info: %u\n", entry->vendor_specific);
|
||||
printf(" Transport type: %u\n", entry->trtype);
|
||||
printf(" Command specific info:%ju\n", entry->csi);
|
||||
printf(" Transport specific: %u\n", entry->ttsi);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_temp(uint16_t t)
|
||||
{
|
||||
printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
|
||||
{
|
||||
struct nvme_health_information_page *health = buf;
|
||||
char cbuf[UINT128_DIG + 1];
|
||||
uint8_t warning;
|
||||
int i;
|
||||
|
||||
warning = health->critical_warning;
|
||||
|
||||
printf("SMART/Health Information Log\n");
|
||||
printf("============================\n");
|
||||
|
||||
printf("Critical Warning State: 0x%02x\n", warning);
|
||||
printf(" Available spare: %d\n",
|
||||
!!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE));
|
||||
printf(" Temperature: %d\n",
|
||||
!!(warning & NVME_CRIT_WARN_ST_TEMPERATURE));
|
||||
printf(" Device reliability: %d\n",
|
||||
!!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY));
|
||||
printf(" Read only: %d\n",
|
||||
!!(warning & NVME_CRIT_WARN_ST_READ_ONLY));
|
||||
printf(" Volatile memory backup: %d\n",
|
||||
!!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP));
|
||||
printf("Temperature: ");
|
||||
print_temp(health->temperature);
|
||||
printf("Available spare: %u\n",
|
||||
health->available_spare);
|
||||
printf("Available spare threshold: %u\n",
|
||||
health->available_spare_threshold);
|
||||
printf("Percentage used: %u\n",
|
||||
health->percentage_used);
|
||||
|
||||
printf("Data units (512,000 byte) read: %s\n",
|
||||
uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf)));
|
||||
printf("Data units written: %s\n",
|
||||
uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf)));
|
||||
printf("Host read commands: %s\n",
|
||||
uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf)));
|
||||
printf("Host write commands: %s\n",
|
||||
uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf)));
|
||||
printf("Controller busy time (minutes): %s\n",
|
||||
uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf)));
|
||||
printf("Power cycles: %s\n",
|
||||
uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf)));
|
||||
printf("Power on hours: %s\n",
|
||||
uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf)));
|
||||
printf("Unsafe shutdowns: %s\n",
|
||||
uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf)));
|
||||
printf("Media errors: %s\n",
|
||||
uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf)));
|
||||
printf("No. error info log entries: %s\n",
|
||||
uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf)));
|
||||
|
||||
printf("Warning Temp Composite Time: %d\n", health->warning_temp_time);
|
||||
printf("Error Temp Composite Time: %d\n", health->error_temp_time);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (health->temp_sensor[i] == 0)
|
||||
continue;
|
||||
printf("Temperature Sensor %d: ", i + 1);
|
||||
print_temp(health->temp_sensor[i]);
|
||||
}
|
||||
printf("Temperature 1 Transition Count: %d\n", health->tmt1tc);
|
||||
printf("Temperature 2 Transition Count: %d\n", health->tmt2tc);
|
||||
printf("Total Time For Temperature 1: %d\n", health->ttftmt1);
|
||||
printf("Total Time For Temperature 2: %d\n", health->ttftmt2);
|
||||
}
|
||||
|
||||
static void
|
||||
print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused)
|
||||
{
|
||||
int i, slots;
|
||||
const char *status;
|
||||
struct nvme_firmware_page *fw = buf;
|
||||
uint8_t afi_slot;
|
||||
uint16_t oacs_fw;
|
||||
uint8_t fw_num_slots;
|
||||
|
||||
afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT;
|
||||
afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK;
|
||||
|
||||
oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
|
||||
fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
|
||||
NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
|
||||
|
||||
printf("Firmware Slot Log\n");
|
||||
printf("=================\n");
|
||||
|
||||
if (oacs_fw == 0)
|
||||
slots = 1;
|
||||
else
|
||||
slots = MIN(fw_num_slots, MAX_FW_SLOTS);
|
||||
|
||||
for (i = 0; i < slots; i++) {
|
||||
printf("Slot %d: ", i + 1);
|
||||
if (afi_slot == i + 1)
|
||||
status = " Active";
|
||||
else
|
||||
status = "Inactive";
|
||||
|
||||
if (fw->revision[i] == 0LLU)
|
||||
printf("Empty\n");
|
||||
else
|
||||
if (isprint(*(char *)&fw->revision[i]))
|
||||
printf("[%s] %.8s\n", status,
|
||||
(char *)&fw->revision[i]);
|
||||
else
|
||||
printf("[%s] %016jx\n", status,
|
||||
fw->revision[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_log_ns(const struct nvme_controller_data *cdata __unused, void *buf,
|
||||
uint32_t size __unused)
|
||||
{
|
||||
struct nvme_ns_list *nsl;
|
||||
u_int i;
|
||||
|
||||
nsl = (struct nvme_ns_list *)buf;
|
||||
printf("Changed Namespace List\n");
|
||||
printf("======================\n");
|
||||
|
||||
for (i = 0; i < nitems(nsl->ns) && nsl->ns[i] != 0; i++) {
|
||||
printf("%08x\n", nsl->ns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_log_command_effects(const struct nvme_controller_data *cdata __unused,
|
||||
void *buf, uint32_t size __unused)
|
||||
{
|
||||
struct nvme_command_effects_page *ce;
|
||||
u_int i;
|
||||
uint32_t s;
|
||||
|
||||
ce = (struct nvme_command_effects_page *)buf;
|
||||
printf("Commands Supported and Effects\n");
|
||||
printf("==============================\n");
|
||||
printf(" Command\tLBCC\tNCC\tNIC\tCCC\tCSE\tUUID\n");
|
||||
|
||||
for (i = 0; i < 255; i++) {
|
||||
s = ce->acs[i];
|
||||
if (((s >> NVME_CE_PAGE_CSUP_SHIFT) &
|
||||
NVME_CE_PAGE_CSUP_MASK) == 0)
|
||||
continue;
|
||||
printf("Admin\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i,
|
||||
((s >> NVME_CE_PAGE_LBCC_SHIFT) &
|
||||
NVME_CE_PAGE_LBCC_MASK) ? "Yes" : "No",
|
||||
((s >> NVME_CE_PAGE_NCC_SHIFT) &
|
||||
NVME_CE_PAGE_NCC_MASK) ? "Yes" : "No",
|
||||
((s >> NVME_CE_PAGE_NIC_SHIFT) &
|
||||
NVME_CE_PAGE_NIC_MASK) ? "Yes" : "No",
|
||||
((s >> NVME_CE_PAGE_CCC_SHIFT) &
|
||||
NVME_CE_PAGE_CCC_MASK) ? "Yes" : "No",
|
||||
((s >> NVME_CE_PAGE_CSE_SHIFT) &
|
||||
NVME_CE_PAGE_CSE_MASK),
|
||||
((s >> NVME_CE_PAGE_UUID_SHIFT) &
|
||||
NVME_CE_PAGE_UUID_MASK) ? "Yes" : "No");
|
||||
}
|
||||
for (i = 0; i < 255; i++) {
|
||||
s = ce->iocs[i];
|
||||
if (((s >> NVME_CE_PAGE_CSUP_SHIFT) &
|
||||
NVME_CE_PAGE_CSUP_MASK) == 0)
|
||||
continue;
|
||||
printf("I/O\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i,
|
||||
((s >> NVME_CE_PAGE_LBCC_SHIFT) &
|
||||
NVME_CE_PAGE_LBCC_MASK) ? "Yes" : "No",
|
||||
((s >> NVME_CE_PAGE_NCC_SHIFT) &
|
||||
NVME_CE_PAGE_NCC_MASK) ? "Yes" : "No",
|
||||
((s >> NVME_CE_PAGE_NIC_SHIFT) &
|
||||
NVME_CE_PAGE_NIC_MASK) ? "Yes" : "No",
|
||||
((s >> NVME_CE_PAGE_CCC_SHIFT) &
|
||||
NVME_CE_PAGE_CCC_MASK) ? "Yes" : "No",
|
||||
((s >> NVME_CE_PAGE_CSE_SHIFT) &
|
||||
NVME_CE_PAGE_CSE_MASK),
|
||||
((s >> NVME_CE_PAGE_UUID_SHIFT) &
|
||||
NVME_CE_PAGE_UUID_MASK) ? "Yes" : "No");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_log_res_notification(const struct nvme_controller_data *cdata __unused,
|
||||
void *buf, uint32_t size __unused)
|
||||
{
|
||||
struct nvme_res_notification_page *rn;
|
||||
|
||||
rn = (struct nvme_res_notification_page *)buf;
|
||||
printf("Reservation Notification\n");
|
||||
printf("========================\n");
|
||||
|
||||
printf("Log Page Count: %ju\n", rn->log_page_count);
|
||||
printf("Log Page Type: ");
|
||||
switch (rn->log_page_type) {
|
||||
case 0:
|
||||
printf("Empty Log Page\n");
|
||||
break;
|
||||
case 1:
|
||||
printf("Registration Preempted\n");
|
||||
break;
|
||||
case 2:
|
||||
printf("Reservation Released\n");
|
||||
break;
|
||||
case 3:
|
||||
printf("Reservation Preempted\n");
|
||||
break;
|
||||
default:
|
||||
printf("Unknown %x\n", rn->log_page_type);
|
||||
break;
|
||||
};
|
||||
printf("Number of Available Log Pages: %d\n", rn->available_log_pages);
|
||||
printf("Namespace ID: 0x%x\n", rn->nsid);
|
||||
}
|
||||
|
||||
static void
|
||||
print_log_sanitize_status(const struct nvme_controller_data *cdata __unused,
|
||||
void *buf, uint32_t size __unused)
|
||||
{
|
||||
struct nvme_sanitize_status_page *ss;
|
||||
u_int p;
|
||||
|
||||
ss = (struct nvme_sanitize_status_page *)buf;
|
||||
printf("Sanitize Status\n");
|
||||
printf("===============\n");
|
||||
|
||||
printf("Sanitize Progress: %u%% (%u/65535)\n",
|
||||
(ss->sprog * 100 + 32768) / 65536, ss->sprog);
|
||||
printf("Sanitize Status: ");
|
||||
switch ((ss->sstat >> NVME_SS_PAGE_SSTAT_STATUS_SHIFT) &
|
||||
NVME_SS_PAGE_SSTAT_STATUS_MASK) {
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_NEVER:
|
||||
printf("Never sanitized");
|
||||
break;
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_COMPLETED:
|
||||
printf("Completed");
|
||||
break;
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_INPROG:
|
||||
printf("In Progress");
|
||||
break;
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_FAILED:
|
||||
printf("Failed");
|
||||
break;
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_COMPLETEDWD:
|
||||
printf("Completed with deallocation");
|
||||
break;
|
||||
default:
|
||||
printf("Unknown");
|
||||
break;
|
||||
}
|
||||
p = (ss->sstat & NVME_SS_PAGE_SSTAT_PASSES_SHIFT) >>
|
||||
NVME_SS_PAGE_SSTAT_PASSES_MASK;
|
||||
if (p > 0)
|
||||
printf(", %d passes", p);
|
||||
if ((ss->sstat & NVME_SS_PAGE_SSTAT_GDE_SHIFT) >>
|
||||
NVME_SS_PAGE_SSTAT_GDE_MASK)
|
||||
printf(", Global Data Erased");
|
||||
printf("\n");
|
||||
printf("Sanitize Command Dword 10: 0x%x\n", ss->scdw10);
|
||||
printf("Time For Overwrite: %u sec\n", ss->etfo);
|
||||
printf("Time For Block Erase: %u sec\n", ss->etfbe);
|
||||
printf("Time For Crypto Erase: %u sec\n", ss->etfce);
|
||||
printf("Time For Overwrite No-Deallocate: %u sec\n", ss->etfownd);
|
||||
printf("Time For Block Erase No-Deallocate: %u sec\n", ss->etfbewnd);
|
||||
printf("Time For Crypto Erase No-Deallocate: %u sec\n", ss->etfcewnd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Table of log page printer / sizing.
|
||||
*
|
||||
* Make sure you keep all the pages of one vendor together so -v help
|
||||
* lists all the vendors pages.
|
||||
*/
|
||||
NVME_LOGPAGE(error,
|
||||
NVME_LOG_ERROR, NULL, "Drive Error Log",
|
||||
print_log_error, 0);
|
||||
NVME_LOGPAGE(health,
|
||||
NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data",
|
||||
print_log_health, sizeof(struct nvme_health_information_page));
|
||||
NVME_LOGPAGE(fw,
|
||||
NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information",
|
||||
print_log_firmware, sizeof(struct nvme_firmware_page));
|
||||
NVME_LOGPAGE(ns,
|
||||
NVME_LOG_CHANGED_NAMESPACE, NULL, "Changed Namespace List",
|
||||
print_log_ns, sizeof(struct nvme_ns_list));
|
||||
NVME_LOGPAGE(ce,
|
||||
NVME_LOG_COMMAND_EFFECT, NULL, "Commands Supported and Effects",
|
||||
print_log_command_effects, sizeof(struct nvme_command_effects_page));
|
||||
NVME_LOGPAGE(dst,
|
||||
NVME_LOG_DEVICE_SELF_TEST, NULL, "Device Self-test",
|
||||
NULL, 564);
|
||||
NVME_LOGPAGE(thi,
|
||||
NVME_LOG_TELEMETRY_HOST_INITIATED, NULL, "Telemetry Host-Initiated",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(tci,
|
||||
NVME_LOG_TELEMETRY_CONTROLLER_INITIATED, NULL, "Telemetry Controller-Initiated",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(egi,
|
||||
NVME_LOG_ENDURANCE_GROUP_INFORMATION, NULL, "Endurance Group Information",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(plpns,
|
||||
NVME_LOG_PREDICTABLE_LATENCY_PER_NVM_SET, NULL, "Predictable Latency Per NVM Set",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(ple,
|
||||
NVME_LOG_PREDICTABLE_LATENCY_EVENT_AGGREGATE, NULL, "Predictable Latency Event Aggregate",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(ana,
|
||||
NVME_LOG_ASYMMETRIC_NAMESPAVE_ACCESS, NULL, "Asymmetric Namespace Access",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(pel,
|
||||
NVME_LOG_PERSISTENT_EVENT_LOG, NULL, "Persistent Event Log",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(lbasi,
|
||||
NVME_LOG_LBA_STATUS_INFORMATION, NULL, "LBA Status Information",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(egea,
|
||||
NVME_LOG_ENDURANCE_GROUP_EVENT_AGGREGATE, NULL, "Endurance Group Event Aggregate",
|
||||
NULL, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(res_notification,
|
||||
NVME_LOG_RES_NOTIFICATION, NULL, "Reservation Notification",
|
||||
print_log_res_notification, sizeof(struct nvme_res_notification_page));
|
||||
NVME_LOGPAGE(sanitize_status,
|
||||
NVME_LOG_SANITIZE_STATUS, NULL, "Sanitize Status",
|
||||
print_log_sanitize_status, sizeof(struct nvme_sanitize_status_page));
|
||||
|
||||
static void
|
||||
logpage_help(void)
|
||||
{
|
||||
const struct logpage_function *f;
|
||||
const char *v;
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
|
||||
fprintf(stderr, "-------- ---------- ----------\n");
|
||||
SLIST_FOREACH(f, &logpages, link) {
|
||||
v = f->vendor == NULL ? "-" : f->vendor;
|
||||
fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
logpage(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
char *path;
|
||||
uint32_t nsid, size;
|
||||
void *buf;
|
||||
const struct logpage_function *lpf;
|
||||
struct nvme_controller_data cdata;
|
||||
print_fn_t print_fn;
|
||||
uint8_t ns_smart;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (opt.hex && opt.binary) {
|
||||
fprintf(stderr,
|
||||
"Can't specify both binary and hex\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
if (opt.vendor != NULL && strcmp(opt.vendor, "help") == 0)
|
||||
logpage_help();
|
||||
if (opt.page == NONE) {
|
||||
fprintf(stderr, "Missing page_id (-p).\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
get_nsid(fd, &path, &nsid);
|
||||
if (nsid == 0) {
|
||||
nsid = NVME_GLOBAL_NAMESPACE_TAG;
|
||||
} else {
|
||||
close(fd);
|
||||
open_dev(path, &fd, 1, 1);
|
||||
}
|
||||
free(path);
|
||||
|
||||
read_controller_data(fd, &cdata);
|
||||
|
||||
ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
|
||||
NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
|
||||
|
||||
/*
|
||||
* The log page attribtues indicate whether or not the controller
|
||||
* supports the SMART/Health information log page on a per
|
||||
* namespace basis.
|
||||
*/
|
||||
if (nsid != NVME_GLOBAL_NAMESPACE_TAG) {
|
||||
if (opt.page != NVME_LOG_HEALTH_INFORMATION)
|
||||
errx(1, "log page %d valid only at controller level",
|
||||
opt.page);
|
||||
if (ns_smart == 0)
|
||||
errx(1,
|
||||
"controller does not support per namespace "
|
||||
"smart/health information");
|
||||
}
|
||||
|
||||
print_fn = print_log_hex;
|
||||
size = DEFAULT_SIZE;
|
||||
if (opt.binary)
|
||||
print_fn = print_bin;
|
||||
if (!opt.binary && !opt.hex) {
|
||||
/*
|
||||
* See if there is a pretty print function for the specified log
|
||||
* page. If one isn't found, we just revert to the default
|
||||
* (print_hex). If there was a vendor specified by the user, and
|
||||
* the page is vendor specific, don't match the print function
|
||||
* unless the vendors match.
|
||||
*/
|
||||
SLIST_FOREACH(lpf, &logpages, link) {
|
||||
if (lpf->vendor != NULL && opt.vendor != NULL &&
|
||||
strcmp(lpf->vendor, opt.vendor) != 0)
|
||||
continue;
|
||||
if (opt.page != lpf->log_page)
|
||||
continue;
|
||||
if (lpf->print_fn != NULL)
|
||||
print_fn = lpf->print_fn;
|
||||
size = lpf->size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt.page == NVME_LOG_ERROR) {
|
||||
size = sizeof(struct nvme_error_information_entry);
|
||||
size *= (cdata.elpe + 1);
|
||||
}
|
||||
|
||||
/* Read the log page */
|
||||
buf = get_log_buffer(size);
|
||||
read_logpage(fd, opt.page, nsid, opt.lsp, opt.lsi, opt.rae, buf, size);
|
||||
print_fn(&cdata, buf, size);
|
||||
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
197
freebsd/sbin/nvmecontrol/modules/intel/intel.c
Normal file
197
freebsd/sbin/nvmecontrol/modules/intel/intel.c
Normal file
@ -0,0 +1,197 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2013 EMC Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel 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.
|
||||
* 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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/endian.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
/*
|
||||
* Intel specific log pages from
|
||||
* http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
|
||||
*
|
||||
* Though the version as of this date has a typo for the size of log page 0xca,
|
||||
* offset 147: it is only 1 byte, not 6.
|
||||
*/
|
||||
static void
|
||||
print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
|
||||
{
|
||||
struct intel_log_temp_stats *temp = buf;
|
||||
|
||||
printf("Intel Temperature Log\n");
|
||||
printf("=====================\n");
|
||||
|
||||
printf("Current: ");
|
||||
print_temp(temp->current);
|
||||
printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last);
|
||||
printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life);
|
||||
printf("Max Temperature ");
|
||||
print_temp(temp->max_temp);
|
||||
printf("Min Temperature ");
|
||||
print_temp(temp->min_temp);
|
||||
printf("Max Operating Temperature ");
|
||||
print_temp(temp->max_oper_temp);
|
||||
printf("Min Operating Temperature ");
|
||||
print_temp(temp->min_oper_temp);
|
||||
printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Format from Table 22, section 5.7 IO Command Latency Statistics.
|
||||
* Read and write stats pages have identical encoding.
|
||||
*/
|
||||
static void
|
||||
print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
|
||||
{
|
||||
const char *walker = buf;
|
||||
int i;
|
||||
|
||||
printf("Major: %d\n", le16dec(walker + 0));
|
||||
printf("Minor: %d\n", le16dec(walker + 2));
|
||||
for (i = 0; i < 32; i++)
|
||||
printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4));
|
||||
for (i = 1; i < 32; i++)
|
||||
printf("%4dms-%4dms: %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4));
|
||||
for (i = 1; i < 32; i++)
|
||||
printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4));
|
||||
}
|
||||
|
||||
static void
|
||||
print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
|
||||
{
|
||||
|
||||
printf("Intel Read Latency Log\n");
|
||||
printf("======================\n");
|
||||
print_intel_read_write_lat_log(cdata, buf, size);
|
||||
}
|
||||
|
||||
static void
|
||||
print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
|
||||
{
|
||||
|
||||
printf("Intel Write Latency Log\n");
|
||||
printf("=======================\n");
|
||||
print_intel_read_write_lat_log(cdata, buf, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Table 19. 5.4 SMART Attributes. Others also implement this and some extra data not documented.
|
||||
*/
|
||||
void
|
||||
print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
|
||||
{
|
||||
uint8_t *walker = buf;
|
||||
uint8_t *end = walker + 150;
|
||||
const char *name;
|
||||
uint64_t raw;
|
||||
uint8_t normalized;
|
||||
|
||||
static struct kv_name kv[] =
|
||||
{
|
||||
{ 0xab, "Program Fail Count" },
|
||||
{ 0xac, "Erase Fail Count" },
|
||||
{ 0xad, "Wear Leveling Count" },
|
||||
{ 0xb8, "End to End Error Count" },
|
||||
{ 0xc7, "CRC Error Count" },
|
||||
{ 0xe2, "Timed: Media Wear" },
|
||||
{ 0xe3, "Timed: Host Read %" },
|
||||
{ 0xe4, "Timed: Elapsed Time" },
|
||||
{ 0xea, "Thermal Throttle Status" },
|
||||
{ 0xf0, "Retry Buffer Overflows" },
|
||||
{ 0xf3, "PLL Lock Loss Count" },
|
||||
{ 0xf4, "NAND Bytes Written" },
|
||||
{ 0xf5, "Host Bytes Written" },
|
||||
};
|
||||
|
||||
printf("Additional SMART Data Log\n");
|
||||
printf("=========================\n");
|
||||
/*
|
||||
* walker[0] = Key
|
||||
* walker[1,2] = reserved
|
||||
* walker[3] = Normalized Value
|
||||
* walker[4] = reserved
|
||||
* walker[5..10] = Little Endian Raw value
|
||||
* (or other represenations)
|
||||
* walker[11] = reserved
|
||||
*/
|
||||
while (walker < end) {
|
||||
name = kv_lookup(kv, nitems(kv), *walker);
|
||||
normalized = walker[3];
|
||||
raw = le48dec(walker + 5);
|
||||
switch (*walker){
|
||||
case 0:
|
||||
break;
|
||||
case 0xad:
|
||||
printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized,
|
||||
le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9));
|
||||
break;
|
||||
case 0xe2:
|
||||
printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
|
||||
break;
|
||||
case 0xea:
|
||||
printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6));
|
||||
break;
|
||||
default:
|
||||
printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
|
||||
break;
|
||||
}
|
||||
walker += 12;
|
||||
}
|
||||
}
|
||||
|
||||
NVME_LOGPAGE(intel_temp,
|
||||
INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats",
|
||||
print_intel_temp_stats, sizeof(struct intel_log_temp_stats));
|
||||
NVME_LOGPAGE(intel_rlat,
|
||||
INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies",
|
||||
print_intel_read_lat_log, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(intel_wlat,
|
||||
INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies",
|
||||
print_intel_write_lat_log, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(intel_smart,
|
||||
INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data",
|
||||
print_intel_add_smart, DEFAULT_SIZE);
|
627
freebsd/sbin/nvmecontrol/modules/wdc/wdc.c
Normal file
627
freebsd/sbin/nvmecontrol/modules/wdc/wdc.c
Normal file
@ -0,0 +1,627 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2017 Netflix, Inc
|
||||
* 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 <sys/endian.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"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t wdc;
|
||||
static cmd_fn_t wdc_cap_diag;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
#define NONE64 0xffffffffffffffffull
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
#define OPT_END { NULL, 0, arg_none, NULL, NULL }
|
||||
|
||||
static struct cmd wdc_cmd = {
|
||||
.name = "wdc", .fn = wdc, .descr = "wdc vendor specific commands", .ctx_size = 0, .opts = NULL, .args = NULL,
|
||||
};
|
||||
|
||||
CMD_COMMAND(wdc_cmd);
|
||||
|
||||
static struct options
|
||||
{
|
||||
const char *template;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.template = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts opts[] = {
|
||||
OPT("template", 'o', arg_string, opt, template,
|
||||
"Template for paths to use for different logs"),
|
||||
OPT_END
|
||||
};
|
||||
|
||||
static const struct args args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd cap_diag_cmd = {
|
||||
.name = "cap-diag",
|
||||
.fn = wdc_cap_diag,
|
||||
.descr = "Retrieve the cap-diag logs from the drive",
|
||||
.ctx_size = sizeof(struct options),
|
||||
.opts = opts,
|
||||
.args = args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(wdc_cmd, cap_diag_cmd);
|
||||
|
||||
#define WDC_NVME_TOC_SIZE 8
|
||||
|
||||
#define WDC_NVME_CAP_DIAG_OPCODE 0xe6
|
||||
#define WDC_NVME_CAP_DIAG_CMD 0x0000
|
||||
|
||||
static void
|
||||
wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix)
|
||||
{
|
||||
struct nvme_controller_data cdata;
|
||||
char sn[NVME_SERIAL_NUMBER_LENGTH + 1];
|
||||
char *walker;
|
||||
|
||||
len -= strlen(buf);
|
||||
buf += strlen(buf);
|
||||
read_controller_data(fd, &cdata);
|
||||
memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH);
|
||||
walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1;
|
||||
while (walker > sn && *walker == ' ')
|
||||
walker--;
|
||||
*++walker = '\0';
|
||||
snprintf(buf, len, "%s%s.bin", sn, suffix);
|
||||
}
|
||||
|
||||
static void
|
||||
wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd,
|
||||
uint8_t *buffer, size_t buflen)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = opcode;
|
||||
pt.cmd.cdw10 = htole32(len / sizeof(uint32_t)); /* - 1 like all the others ??? */
|
||||
pt.cmd.cdw11 = htole32(off / sizeof(uint32_t));
|
||||
pt.cmd.cdw12 = htole32(cmd);
|
||||
pt.buf = buffer;
|
||||
pt.len = buflen;
|
||||
pt.is_read = 1;
|
||||
// printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n",
|
||||
// (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen);
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "wdc_get_data request failed");
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "wdc_get_data request returned error");
|
||||
}
|
||||
|
||||
static void
|
||||
wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode,
|
||||
uint32_t cmd, int len_off)
|
||||
{
|
||||
int first;
|
||||
int fd2;
|
||||
uint8_t *buf;
|
||||
uint32_t len, offset;
|
||||
size_t resid;
|
||||
|
||||
wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix);
|
||||
|
||||
/* XXX overwrite protection? */
|
||||
fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd2 < 0)
|
||||
err(1, "open %s", tmpl);
|
||||
buf = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE);
|
||||
if (buf == NULL)
|
||||
errx(1, "Can't get buffer to read dump");
|
||||
offset = 0;
|
||||
len = NVME_MAX_XFER_SIZE;
|
||||
first = 1;
|
||||
|
||||
do {
|
||||
resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len;
|
||||
wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid);
|
||||
|
||||
if (first) {
|
||||
len = be32dec(buf + len_off);
|
||||
if (len == 0)
|
||||
errx(1, "No data for %s", suffix);
|
||||
if (memcmp("E6LG", buf, 4) != 0)
|
||||
printf("Expected header of E6LG, found '%4.4s' instead\n",
|
||||
buf);
|
||||
printf("Dumping %d bytes of version %d.%d log to %s\n", len,
|
||||
buf[8], buf[9], tmpl);
|
||||
/*
|
||||
* Adjust amount to dump if total dump < 1MB,
|
||||
* though it likely doesn't matter to the WDC
|
||||
* analysis tools.
|
||||
*/
|
||||
if (resid > len)
|
||||
resid = len;
|
||||
first = 0;
|
||||
}
|
||||
if (write(fd2, buf, resid) != (ssize_t)resid)
|
||||
err(1, "write");
|
||||
offset += resid;
|
||||
len -= resid;
|
||||
} while (len > 0);
|
||||
free(buf);
|
||||
close(fd2);
|
||||
}
|
||||
|
||||
static void
|
||||
wdc_cap_diag(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
char tmpl[MAXPATHLEN];
|
||||
int fd;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (opt.template == NULL) {
|
||||
fprintf(stderr, "Missing template arg.\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
strlcpy(tmpl, opt.template, sizeof(tmpl));
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
wdc_do_dump(fd, tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE,
|
||||
WDC_NVME_CAP_DIAG_CMD, 4);
|
||||
|
||||
close(fd);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
wdc(const struct cmd *nf __unused, int argc, char *argv[])
|
||||
{
|
||||
|
||||
cmd_dispatch(argc, argv, &wdc_cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* HGST's 0xc1 page. This is a grab bag of additional data. Please see
|
||||
* https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
|
||||
* https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
|
||||
* Appendix A for details
|
||||
*/
|
||||
|
||||
typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
|
||||
struct subpage_print
|
||||
{
|
||||
uint16_t key;
|
||||
subprint_fn_t fn;
|
||||
};
|
||||
|
||||
static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
|
||||
|
||||
static struct subpage_print hgst_subpage[] = {
|
||||
{ 0x02, print_hgst_info_write_errors },
|
||||
{ 0x03, print_hgst_info_read_errors },
|
||||
{ 0x05, print_hgst_info_verify_errors },
|
||||
{ 0x10, print_hgst_info_self_test },
|
||||
{ 0x15, print_hgst_info_background_scan },
|
||||
{ 0x30, print_hgst_info_erase_errors },
|
||||
{ 0x31, print_hgst_info_erase_counts },
|
||||
{ 0x32, print_hgst_info_temp_history },
|
||||
{ 0x37, print_hgst_info_ssd_perf },
|
||||
{ 0x38, print_hgst_info_firmware_load },
|
||||
};
|
||||
|
||||
/* Print a subpage that is basically just key value pairs */
|
||||
static void
|
||||
print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
|
||||
const struct kv_name *kv, size_t kv_count)
|
||||
{
|
||||
uint8_t *wsp, *esp;
|
||||
uint16_t ptype;
|
||||
uint8_t plen;
|
||||
uint64_t param;
|
||||
int i;
|
||||
|
||||
wsp = buf;
|
||||
esp = wsp + size;
|
||||
while (wsp < esp) {
|
||||
ptype = le16dec(wsp);
|
||||
wsp += 2;
|
||||
wsp++; /* Flags, just ignore */
|
||||
plen = *wsp++;
|
||||
param = 0;
|
||||
for (i = 0; i < plen; i++)
|
||||
param |= (uint64_t)*wsp++ << (i * 8);
|
||||
printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
|
||||
{
|
||||
static struct kv_name kv[] =
|
||||
{
|
||||
{ 0x0000, "Corrected Without Delay" },
|
||||
{ 0x0001, "Corrected Maybe Delayed" },
|
||||
{ 0x0002, "Re-Writes" },
|
||||
{ 0x0003, "Errors Corrected" },
|
||||
{ 0x0004, "Correct Algorithm Used" },
|
||||
{ 0x0005, "Bytes Processed" },
|
||||
{ 0x0006, "Uncorrected Errors" },
|
||||
{ 0x8000, "Flash Write Commands" },
|
||||
{ 0x8001, "HGST Special" },
|
||||
};
|
||||
|
||||
printf("Write Errors Subpage:\n");
|
||||
print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
|
||||
{
|
||||
static struct kv_name kv[] =
|
||||
{
|
||||
{ 0x0000, "Corrected Without Delay" },
|
||||
{ 0x0001, "Corrected Maybe Delayed" },
|
||||
{ 0x0002, "Re-Reads" },
|
||||
{ 0x0003, "Errors Corrected" },
|
||||
{ 0x0004, "Correct Algorithm Used" },
|
||||
{ 0x0005, "Bytes Processed" },
|
||||
{ 0x0006, "Uncorrected Errors" },
|
||||
{ 0x8000, "Flash Read Commands" },
|
||||
{ 0x8001, "XOR Recovered" },
|
||||
{ 0x8002, "Total Corrected Bits" },
|
||||
};
|
||||
|
||||
printf("Read Errors Subpage:\n");
|
||||
print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
|
||||
{
|
||||
static struct kv_name kv[] =
|
||||
{
|
||||
{ 0x0000, "Corrected Without Delay" },
|
||||
{ 0x0001, "Corrected Maybe Delayed" },
|
||||
{ 0x0002, "Re-Reads" },
|
||||
{ 0x0003, "Errors Corrected" },
|
||||
{ 0x0004, "Correct Algorithm Used" },
|
||||
{ 0x0005, "Bytes Processed" },
|
||||
{ 0x0006, "Uncorrected Errors" },
|
||||
{ 0x8000, "Commands Processed" },
|
||||
};
|
||||
|
||||
printf("Verify Errors Subpage:\n");
|
||||
print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t *walker = buf;
|
||||
uint16_t code, hrs;
|
||||
uint32_t lba;
|
||||
|
||||
printf("Self Test Subpage:\n");
|
||||
for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */
|
||||
code = le16dec(walker);
|
||||
walker += 2;
|
||||
walker++; /* Ignore fixed flags */
|
||||
if (*walker == 0) /* Last entry is zero length */
|
||||
break;
|
||||
if (*walker++ != 0x10) {
|
||||
printf("Bad length for self test report\n");
|
||||
return;
|
||||
}
|
||||
printf(" %-30s: %d\n", "Recent Test", code);
|
||||
printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
|
||||
printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
|
||||
walker++;
|
||||
printf(" %-28s: %#x\n", "Self-Test Number", *walker++);
|
||||
hrs = le16dec(walker);
|
||||
walker += 2;
|
||||
lba = le32dec(walker);
|
||||
walker += 4;
|
||||
printf(" %-28s: %u\n", "Total Power On Hrs", hrs);
|
||||
printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba);
|
||||
printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
|
||||
printf(" %-28s: %#x\n", "Additional Sense Code", *walker++);
|
||||
printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
|
||||
printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
|
||||
{
|
||||
uint8_t *walker = buf;
|
||||
uint8_t status;
|
||||
uint16_t code, nscan, progress;
|
||||
uint32_t pom, nand;
|
||||
|
||||
printf("Background Media Scan Subpage:\n");
|
||||
/* Decode the header */
|
||||
code = le16dec(walker);
|
||||
walker += 2;
|
||||
walker++; /* Ignore fixed flags */
|
||||
if (*walker++ != 0x10) {
|
||||
printf("Bad length for background scan header\n");
|
||||
return;
|
||||
}
|
||||
if (code != 0) {
|
||||
printf("Expceted code 0, found code %#x\n", code);
|
||||
return;
|
||||
}
|
||||
pom = le32dec(walker);
|
||||
walker += 4;
|
||||
walker++; /* Reserved */
|
||||
status = *walker++;
|
||||
nscan = le16dec(walker);
|
||||
walker += 2;
|
||||
progress = le16dec(walker);
|
||||
walker += 2;
|
||||
walker += 6; /* Reserved */
|
||||
printf(" %-30s: %d\n", "Power On Minutes", pom);
|
||||
printf(" %-30s: %x (%s)\n", "BMS Status", status,
|
||||
status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown")));
|
||||
printf(" %-30s: %d\n", "Number of BMS", nscan);
|
||||
printf(" %-30s: %d\n", "Progress Current BMS", progress);
|
||||
/* Report retirements */
|
||||
if (walker - (uint8_t *)buf != 20) {
|
||||
printf("Coding error, offset not 20\n");
|
||||
return;
|
||||
}
|
||||
size -= 20;
|
||||
printf(" %-30s: %d\n", "BMS retirements", size / 0x18);
|
||||
while (size > 0) {
|
||||
code = le16dec(walker);
|
||||
walker += 2;
|
||||
walker++;
|
||||
if (*walker++ != 0x14) {
|
||||
printf("Bad length parameter\n");
|
||||
return;
|
||||
}
|
||||
pom = le32dec(walker);
|
||||
walker += 4;
|
||||
/*
|
||||
* Spec sheet says the following are hard coded, if true, just
|
||||
* print the NAND retirement.
|
||||
*/
|
||||
if (walker[0] == 0x41 &&
|
||||
walker[1] == 0x0b &&
|
||||
walker[2] == 0x01 &&
|
||||
walker[3] == 0x00 &&
|
||||
walker[4] == 0x00 &&
|
||||
walker[5] == 0x00 &&
|
||||
walker[6] == 0x00 &&
|
||||
walker[7] == 0x00) {
|
||||
walker += 8;
|
||||
walker += 4; /* Skip reserved */
|
||||
nand = le32dec(walker);
|
||||
walker += 4;
|
||||
printf(" %-30s: %d\n", "Retirement number", code);
|
||||
printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
|
||||
} else {
|
||||
printf("Parameter %#x entry corrupt\n", code);
|
||||
walker += 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
|
||||
{
|
||||
static struct kv_name kv[] =
|
||||
{
|
||||
{ 0x0000, "Corrected Without Delay" },
|
||||
{ 0x0001, "Corrected Maybe Delayed" },
|
||||
{ 0x0002, "Re-Erase" },
|
||||
{ 0x0003, "Errors Corrected" },
|
||||
{ 0x0004, "Correct Algorithm Used" },
|
||||
{ 0x0005, "Bytes Processed" },
|
||||
{ 0x0006, "Uncorrected Errors" },
|
||||
{ 0x8000, "Flash Erase Commands" },
|
||||
{ 0x8001, "Mfg Defect Count" },
|
||||
{ 0x8002, "Grown Defect Count" },
|
||||
{ 0x8003, "Erase Count -- User" },
|
||||
{ 0x8004, "Erase Count -- System" },
|
||||
};
|
||||
|
||||
printf("Erase Errors Subpage:\n");
|
||||
print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
|
||||
{
|
||||
/* My drive doesn't export this -- so not coding up */
|
||||
printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
|
||||
{
|
||||
uint8_t *walker = buf;
|
||||
uint32_t min;
|
||||
|
||||
printf("Temperature History:\n");
|
||||
printf(" %-30s: %d C\n", "Current Temperature", *walker++);
|
||||
printf(" %-30s: %d C\n", "Reference Temperature", *walker++);
|
||||
printf(" %-30s: %d C\n", "Maximum Temperature", *walker++);
|
||||
printf(" %-30s: %d C\n", "Minimum Temperature", *walker++);
|
||||
min = le32dec(walker);
|
||||
walker += 4;
|
||||
printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
|
||||
min = le32dec(walker);
|
||||
walker += 4;
|
||||
printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60);
|
||||
min = le32dec(walker);
|
||||
walker += 4;
|
||||
printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused)
|
||||
{
|
||||
uint8_t *walker = buf;
|
||||
uint64_t val;
|
||||
|
||||
printf("SSD Performance Subpage Type %d:\n", res);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Read Commands", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Read Blocks", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Read Commands Stalled", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Write Commands", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Write Blocks", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Write Odd End Commands", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "Host Write Commands Stalled", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "NAND Read Commands", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "NAND Read Blocks", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "NAND Write Commands", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "NAND Write Blocks", val);
|
||||
val = le64dec(walker);
|
||||
walker += 8;
|
||||
printf(" %-30s: %ju\n", "NAND Read Before Writes", val);
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
|
||||
{
|
||||
uint8_t *walker = buf;
|
||||
|
||||
printf("Firmware Load Subpage:\n");
|
||||
printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker));
|
||||
}
|
||||
|
||||
static void
|
||||
kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nsp; i++, sp++) {
|
||||
if (sp->key == subtype) {
|
||||
sp->fn(buf, subtype, res, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf("No handler for page type %x\n", subtype);
|
||||
}
|
||||
|
||||
static void
|
||||
print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
|
||||
{
|
||||
uint8_t *walker, *end, *subpage;
|
||||
int pages;
|
||||
uint16_t len;
|
||||
uint8_t subtype, res;
|
||||
|
||||
printf("HGST Extra Info Log\n");
|
||||
printf("===================\n");
|
||||
|
||||
walker = buf;
|
||||
pages = *walker++;
|
||||
walker++;
|
||||
len = le16dec(walker);
|
||||
walker += 2;
|
||||
end = walker + len; /* Length is exclusive of this header */
|
||||
|
||||
while (walker < end) {
|
||||
subpage = walker + 4;
|
||||
subtype = *walker++ & 0x3f; /* subtype */
|
||||
res = *walker++; /* Reserved */
|
||||
len = le16dec(walker);
|
||||
walker += len + 2; /* Length, not incl header */
|
||||
if (walker > end) {
|
||||
printf("Ooops! Off the end of the list\n");
|
||||
break;
|
||||
}
|
||||
kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage));
|
||||
}
|
||||
}
|
||||
|
||||
NVME_LOGPAGE(hgst_info,
|
||||
HGST_INFO_LOG, "hgst", "Detailed Health/SMART",
|
||||
print_hgst_info_log, DEFAULT_SIZE);
|
||||
NVME_LOGPAGE(wdc_info,
|
||||
HGST_INFO_LOG, "wdc", "Detailed Health/SMART",
|
||||
print_hgst_info_log, DEFAULT_SIZE);
|
61
freebsd/sbin/nvmecontrol/nc_util.c
Normal file
61
freebsd/sbin/nvmecontrol/nc_util.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2017 Netflix, Inc
|
||||
* 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/endian.h>
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
char *
|
||||
uint128_to_str(uint128_t u, char *buf, size_t buflen)
|
||||
{
|
||||
char *end = buf + buflen - 1;
|
||||
|
||||
*end-- = '\0';
|
||||
if (u == 0)
|
||||
*end-- = '0';
|
||||
while (u && end >= buf) {
|
||||
*end-- = u % 10 + '0';
|
||||
u /= 10;
|
||||
}
|
||||
end++;
|
||||
if (u != 0)
|
||||
return NULL;
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
/* "Missing" from endian.h */
|
||||
uint64_t
|
||||
le48dec(const void *pp)
|
||||
{
|
||||
uint8_t const *p = (uint8_t const *)pp;
|
||||
|
||||
return (((uint64_t)le16dec(p + 4) << 32) | le32dec(p));
|
||||
}
|
886
freebsd/sbin/nvmecontrol/ns.c
Normal file
886
freebsd/sbin/nvmecontrol/ns.c
Normal file
@ -0,0 +1,886 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2017 Netflix, Inc
|
||||
* Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
|
||||
*
|
||||
* 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, immediately at the beginning of the file.
|
||||
* 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 ``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 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 <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t ns;
|
||||
static cmd_fn_t nsactive;
|
||||
static cmd_fn_t nsallocated;
|
||||
static cmd_fn_t nscontrollers;
|
||||
static cmd_fn_t nscreate;
|
||||
static cmd_fn_t nsdelete;
|
||||
static cmd_fn_t nsattach;
|
||||
static cmd_fn_t nsdetach;
|
||||
static cmd_fn_t nsattached;
|
||||
static cmd_fn_t nsidentify;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
#define NONE64 0xffffffffffffffffull
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
#define OPT_END { NULL, 0, arg_none, NULL, NULL }
|
||||
|
||||
static struct cmd ns_cmd = {
|
||||
.name = "ns",
|
||||
.fn = ns,
|
||||
.descr = "Namespace management commands",
|
||||
.ctx_size = 0,
|
||||
.opts = NULL,
|
||||
.args = NULL,
|
||||
};
|
||||
|
||||
CMD_COMMAND(ns_cmd);
|
||||
|
||||
static struct active_options {
|
||||
const char *dev;
|
||||
} active_opt = {
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct args active_args[] = {
|
||||
{ arg_string, &active_opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd active_cmd = {
|
||||
.name = "active",
|
||||
.fn = nsactive,
|
||||
.descr = "List active (attached) namespaces",
|
||||
.ctx_size = sizeof(active_opt),
|
||||
.opts = NULL,
|
||||
.args = active_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, active_cmd);
|
||||
|
||||
static struct cmd allocated_cmd = {
|
||||
.name = "allocated",
|
||||
.fn = nsallocated,
|
||||
.descr = "List allocated (created) namespaces",
|
||||
.ctx_size = sizeof(active_opt),
|
||||
.opts = NULL,
|
||||
.args = active_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, allocated_cmd);
|
||||
|
||||
static struct controllers_options {
|
||||
const char *dev;
|
||||
} controllers_opt = {
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct args controllers_args[] = {
|
||||
{ arg_string, &controllers_opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd controllers_cmd = {
|
||||
.name = "controllers",
|
||||
.fn = nscontrollers,
|
||||
.descr = "List all controllers in NVM subsystem",
|
||||
.ctx_size = sizeof(controllers_opt),
|
||||
.opts = NULL,
|
||||
.args = controllers_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, controllers_cmd);
|
||||
|
||||
static struct create_options {
|
||||
uint64_t nsze;
|
||||
uint64_t cap;
|
||||
uint32_t lbaf;
|
||||
uint32_t mset;
|
||||
uint32_t nmic;
|
||||
uint32_t pi;
|
||||
uint32_t pil;
|
||||
uint32_t flbas;
|
||||
uint32_t dps;
|
||||
// uint32_t block_size;
|
||||
const char *dev;
|
||||
} create_opt = {
|
||||
.nsze = NONE64,
|
||||
.cap = NONE64,
|
||||
.lbaf = NONE,
|
||||
.mset = NONE,
|
||||
.nmic = NONE,
|
||||
.pi = NONE,
|
||||
.pil = NONE,
|
||||
.flbas = NONE,
|
||||
.dps = NONE,
|
||||
.dev = NULL,
|
||||
// .block_size = NONE,
|
||||
};
|
||||
|
||||
static const struct opts create_opts[] = {
|
||||
OPT("nsze", 's', arg_uint64, create_opt, nsze,
|
||||
"The namespace size"),
|
||||
OPT("ncap", 'c', arg_uint64, create_opt, cap,
|
||||
"The capacity of the namespace (<= ns size)"),
|
||||
OPT("lbaf", 'f', arg_uint32, create_opt, lbaf,
|
||||
"The FMT field of the FLBAS"),
|
||||
OPT("mset", 'm', arg_uint32, create_opt, mset,
|
||||
"The MSET field of the FLBAS"),
|
||||
OPT("nmic", 'n', arg_uint32, create_opt, nmic,
|
||||
"Namespace multipath and sharing capabilities"),
|
||||
OPT("pi", 'p', arg_uint32, create_opt, pi,
|
||||
"PI field of FLBAS"),
|
||||
OPT("pil", 'l', arg_uint32, create_opt, pil,
|
||||
"PIL field of FLBAS"),
|
||||
OPT("flbas", 'L', arg_uint32, create_opt, flbas,
|
||||
"Namespace formatted logical block size setting"),
|
||||
OPT("dps", 'd', arg_uint32, create_opt, dps,
|
||||
"Data protection settings"),
|
||||
// OPT("block-size", 'b', arg_uint32, create_opt, block_size,
|
||||
// "Blocksize of the namespace"),
|
||||
OPT_END
|
||||
};
|
||||
|
||||
static const struct args create_args[] = {
|
||||
{ arg_string, &create_opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd create_cmd = {
|
||||
.name = "create",
|
||||
.fn = nscreate,
|
||||
.descr = "Create a namespace",
|
||||
.ctx_size = sizeof(create_opt),
|
||||
.opts = create_opts,
|
||||
.args = create_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, create_cmd);
|
||||
|
||||
static struct delete_options {
|
||||
uint32_t nsid;
|
||||
const char *dev;
|
||||
} delete_opt = {
|
||||
.nsid = NONE,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts delete_opts[] = {
|
||||
OPT("namespace-id", 'n', arg_uint32, delete_opt, nsid,
|
||||
"The namespace ID to delete"),
|
||||
OPT_END
|
||||
};
|
||||
|
||||
static const struct args delete_args[] = {
|
||||
{ arg_string, &delete_opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd delete_cmd = {
|
||||
.name = "delete",
|
||||
.fn = nsdelete,
|
||||
.descr = "Delete a namespace",
|
||||
.ctx_size = sizeof(delete_opt),
|
||||
.opts = delete_opts,
|
||||
.args = delete_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, delete_cmd);
|
||||
|
||||
static struct attach_options {
|
||||
uint32_t nsid;
|
||||
uint32_t ctrlrid;
|
||||
const char *dev;
|
||||
} attach_opt = {
|
||||
.nsid = NONE,
|
||||
.ctrlrid = NONE - 1,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts attach_opts[] = {
|
||||
OPT("namespace-id", 'n', arg_uint32, attach_opt, nsid,
|
||||
"The namespace ID to attach"),
|
||||
OPT("controller", 'c', arg_uint32, attach_opt, ctrlrid,
|
||||
"The controller ID to attach"),
|
||||
OPT_END
|
||||
};
|
||||
|
||||
static const struct args attach_args[] = {
|
||||
{ arg_string, &attach_opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd attach_cmd = {
|
||||
.name = "attach",
|
||||
.fn = nsattach,
|
||||
.descr = "Attach a controller to a namespace",
|
||||
.ctx_size = sizeof(attach_opt),
|
||||
.opts = attach_opts,
|
||||
.args = attach_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, attach_cmd);
|
||||
|
||||
static struct attached_options {
|
||||
uint32_t nsid;
|
||||
const char *dev;
|
||||
} attached_opt = {
|
||||
.nsid = NONE,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts attached_opts[] = {
|
||||
OPT("namespace-id", 'n', arg_uint32, attached_opt, nsid,
|
||||
"The namespace ID to request attached controllers"),
|
||||
OPT_END
|
||||
};
|
||||
|
||||
static const struct args attached_args[] = {
|
||||
{ arg_string, &attached_opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd attached_cmd = {
|
||||
.name = "attached",
|
||||
.fn = nsattached,
|
||||
.descr = "List controllers attached to a namespace",
|
||||
.ctx_size = sizeof(attached_opt),
|
||||
.opts = attached_opts,
|
||||
.args = attached_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, attached_cmd);
|
||||
|
||||
static struct detach_options {
|
||||
uint32_t nsid;
|
||||
uint32_t ctrlrid;
|
||||
const char *dev;
|
||||
} detach_opt = {
|
||||
.nsid = NONE,
|
||||
.ctrlrid = NONE - 1,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts detach_opts[] = {
|
||||
OPT("namespace-id", 'n', arg_uint32, detach_opt, nsid,
|
||||
"The namespace ID to detach"),
|
||||
OPT("controller", 'c', arg_uint32, detach_opt, ctrlrid,
|
||||
"The controller ID to detach"),
|
||||
OPT_END
|
||||
};
|
||||
|
||||
static const struct args detach_args[] = {
|
||||
{ arg_string, &detach_opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd detach_cmd = {
|
||||
.name = "detach",
|
||||
.fn = nsdetach,
|
||||
.descr = "Detach a controller from a namespace",
|
||||
.ctx_size = sizeof(detach_opt),
|
||||
.opts = detach_opts,
|
||||
.args = detach_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, detach_cmd);
|
||||
|
||||
static struct identify_options {
|
||||
bool hex;
|
||||
bool verbose;
|
||||
const char *dev;
|
||||
uint32_t nsid;
|
||||
} identify_opt = {
|
||||
.hex = false,
|
||||
.verbose = false,
|
||||
.dev = NULL,
|
||||
.nsid = NONE,
|
||||
};
|
||||
|
||||
static const struct opts identify_opts[] = {
|
||||
OPT("hex", 'x', arg_none, identify_opt, hex,
|
||||
"Print identiy information in hex"),
|
||||
OPT("verbose", 'v', arg_none, identify_opt, verbose,
|
||||
"More verbosity: print entire identify table"),
|
||||
OPT("nsid", 'n', arg_uint32, identify_opt, nsid,
|
||||
"The namespace ID to print IDENTIFY for"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct args identify_args[] = {
|
||||
{ arg_string, &identify_opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd identify_cmd = {
|
||||
.name = "identify",
|
||||
.fn = nsidentify,
|
||||
.descr = "Print IDENTIFY for allocated namespace",
|
||||
.ctx_size = sizeof(identify_opt),
|
||||
.opts = identify_opts,
|
||||
.args = identify_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, identify_cmd);
|
||||
|
||||
/* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */
|
||||
|
||||
struct ns_result_str {
|
||||
uint16_t res;
|
||||
const char * str;
|
||||
};
|
||||
|
||||
static struct ns_result_str ns_result[] = {
|
||||
{ 0x2, "Invalid Field"},
|
||||
{ 0xa, "Invalid Format"},
|
||||
{ 0xb, "Invalid Namespace or format"},
|
||||
{ 0x15, "Namespace insufficent capacity"},
|
||||
{ 0x16, "Namespace ID unavaliable"},
|
||||
{ 0x18, "Namespace already attached"},
|
||||
{ 0x19, "Namespace is private"},
|
||||
{ 0x1a, "Namespace is not attached"},
|
||||
{ 0x1b, "Thin provisioning not supported"},
|
||||
{ 0x1c, "Controller list invalid"},
|
||||
{ 0x24, "ANA Group Identifier Invalid"},
|
||||
{ 0x25, "ANA Attach Failed"},
|
||||
{ 0xFFFF, "Unknown"}
|
||||
};
|
||||
|
||||
static const char *
|
||||
get_res_str(uint16_t res)
|
||||
{
|
||||
struct ns_result_str *t = ns_result;
|
||||
|
||||
while (t->res != 0xFFFF) {
|
||||
if (t->res == res)
|
||||
return (t->str);
|
||||
t++;
|
||||
}
|
||||
return t->str;
|
||||
}
|
||||
|
||||
static void
|
||||
nsactive(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
int fd, i;
|
||||
uint32_t list[1024];
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
open_dev(active_opt.dev, &fd, 1, 1);
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.nsid = htole32(0);
|
||||
pt.cmd.cdw10 = htole32(0x02);
|
||||
pt.buf = list;
|
||||
pt.len = sizeof(list);
|
||||
pt.is_read = 1;
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
|
||||
printf("Active namespaces:\n");
|
||||
for (i = 0; list[i] != 0; i++)
|
||||
printf("%10d\n", le32toh(list[i]));
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
nsallocated(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int fd, i;
|
||||
uint32_t list[1024];
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
open_dev(active_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.nsid = htole32(0);
|
||||
pt.cmd.cdw10 = htole32(0x10);
|
||||
pt.buf = list;
|
||||
pt.len = sizeof(list);
|
||||
pt.is_read = 1;
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
|
||||
printf("Allocated namespaces:\n");
|
||||
for (i = 0; list[i] != 0; i++)
|
||||
printf("%10d\n", le32toh(list[i]));
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
nscontrollers(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int fd, i, n;
|
||||
uint16_t clist[2048];
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
open_dev(controllers_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.cdw10 = htole32(0x13);
|
||||
pt.buf = clist;
|
||||
pt.len = sizeof(clist);
|
||||
pt.is_read = 1;
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
|
||||
n = le16toh(clist[0]);
|
||||
printf("NVM subsystem includes %d controller(s):\n", n);
|
||||
for (i = 0; i < n; i++)
|
||||
printf(" 0x%04x\n", le16toh(clist[i + 1]));
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* NS MGMT Command specific status values:
|
||||
* 0xa = Invalid Format
|
||||
* 0x15 = Namespace Insuffience capacity
|
||||
* 0x16 = Namespace ID unavailable (number namespaces exceeded)
|
||||
* 0xb = Thin Provisioning Not supported
|
||||
*/
|
||||
static void
|
||||
nscreate(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
struct nvme_namespace_data nsdata;
|
||||
int fd, result;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
if (create_opt.cap == NONE64)
|
||||
create_opt.cap = create_opt.nsze;
|
||||
if (create_opt.nsze == NONE64) {
|
||||
fprintf(stderr,
|
||||
"Size not specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
open_dev(create_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
/* Allow namespaces sharing if Multi-Path I/O is supported. */
|
||||
if (create_opt.nmic == NONE) {
|
||||
create_opt.nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK <<
|
||||
NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) : 0;
|
||||
}
|
||||
|
||||
memset(&nsdata, 0, sizeof(nsdata));
|
||||
nsdata.nsze = create_opt.nsze;
|
||||
nsdata.ncap = create_opt.cap;
|
||||
if (create_opt.flbas == NONE)
|
||||
nsdata.flbas = ((create_opt.lbaf & NVME_NS_DATA_FLBAS_FORMAT_MASK)
|
||||
<< NVME_NS_DATA_FLBAS_FORMAT_SHIFT) |
|
||||
((create_opt.mset & NVME_NS_DATA_FLBAS_EXTENDED_MASK)
|
||||
<< NVME_NS_DATA_FLBAS_EXTENDED_SHIFT);
|
||||
else
|
||||
nsdata.flbas = create_opt.flbas;
|
||||
if (create_opt.dps == NONE)
|
||||
nsdata.dps = ((create_opt.pi & NVME_NS_DATA_DPS_MD_START_MASK)
|
||||
<< NVME_NS_DATA_DPS_MD_START_SHIFT) |
|
||||
((create_opt.pil & NVME_NS_DATA_DPS_PIT_MASK)
|
||||
<< NVME_NS_DATA_DPS_PIT_SHIFT);
|
||||
else
|
||||
nsdata.dps = create_opt.dps;
|
||||
nsdata.nmic = create_opt.nmic;
|
||||
nvme_namespace_data_swapbytes(&nsdata);
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT;
|
||||
pt.cmd.cdw10 = htole32(0); /* create */
|
||||
pt.buf = &nsdata;
|
||||
pt.len = sizeof(struct nvme_namespace_data);
|
||||
pt.is_read = 0; /* passthrough writes data to ctrlr */
|
||||
if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
|
||||
errx(1, "ioctl request to %s failed: %d", argv[optind], result);
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl)) {
|
||||
errx(1, "namespace creation failed: %s",
|
||||
get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
|
||||
NVME_STATUS_SC_MASK));
|
||||
}
|
||||
printf("namespace %d created\n", pt.cpl.cdw0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
nsdelete(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int fd, result;
|
||||
char buf[2];
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (delete_opt.nsid == NONE) {
|
||||
fprintf(stderr,
|
||||
"No NSID specified");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
open_dev(delete_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT;
|
||||
pt.cmd.cdw10 = htole32(1); /* delete */
|
||||
pt.buf = buf;
|
||||
pt.len = sizeof(buf);
|
||||
pt.is_read = 1;
|
||||
pt.cmd.nsid = delete_opt.nsid;
|
||||
|
||||
if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
|
||||
errx(1, "ioctl request to %s failed: %d", delete_opt.dev, result);
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl)) {
|
||||
errx(1, "namespace deletion failed: %s",
|
||||
get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
|
||||
NVME_STATUS_SC_MASK));
|
||||
}
|
||||
printf("namespace %d deleted\n", delete_opt.nsid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach and Detach use Dword 10, and a controller list (section 4.9)
|
||||
* This struct is 4096 bytes in size.
|
||||
* 0h = attach
|
||||
* 1h = detach
|
||||
*
|
||||
* Result values for both attach/detach:
|
||||
*
|
||||
* Completion 18h = Already attached
|
||||
* 19h = NS is private and already attached to a controller
|
||||
* 1Ah = Not attached, request could not be completed
|
||||
* 1Ch = Controller list invalid.
|
||||
*
|
||||
* 0x2 Invalid Field can occur if ctrlrid d.n.e in system.
|
||||
*/
|
||||
static void
|
||||
nsattach(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int fd, result;
|
||||
uint16_t clist[2048];
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (attach_opt.nsid == NONE) {
|
||||
fprintf(stderr, "No valid NSID specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
open_dev(attach_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
if (attach_opt.ctrlrid == NONE) {
|
||||
/* Get full list of controllers to attach to. */
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.cdw10 = htole32(0x13);
|
||||
pt.buf = clist;
|
||||
pt.len = sizeof(clist);
|
||||
pt.is_read = 1;
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
} else {
|
||||
/* By default attach to this controller. */
|
||||
if (attach_opt.ctrlrid == NONE - 1)
|
||||
attach_opt.ctrlrid = cd.ctrlr_id;
|
||||
memset(&clist, 0, sizeof(clist));
|
||||
clist[0] = htole16(1);
|
||||
clist[1] = htole16(attach_opt.ctrlrid);
|
||||
}
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
|
||||
pt.cmd.cdw10 = htole32(0); /* attach */
|
||||
pt.cmd.nsid = attach_opt.nsid;
|
||||
pt.buf = &clist;
|
||||
pt.len = sizeof(clist);
|
||||
|
||||
if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
|
||||
errx(1, "ioctl request to %s failed: %d", attach_opt.dev, result);
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl)) {
|
||||
errx(1, "namespace attach failed: %s",
|
||||
get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
|
||||
NVME_STATUS_SC_MASK));
|
||||
}
|
||||
printf("namespace %d attached\n", attach_opt.nsid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
nsdetach(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int fd, result;
|
||||
uint16_t clist[2048];
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (detach_opt.nsid == NONE) {
|
||||
fprintf(stderr, "No valid NSID specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
open_dev(detach_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
if (detach_opt.ctrlrid == NONE) {
|
||||
/* Get list of controllers this namespace attached to. */
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.nsid = htole32(detach_opt.nsid);
|
||||
pt.cmd.cdw10 = htole32(0x12);
|
||||
pt.buf = clist;
|
||||
pt.len = sizeof(clist);
|
||||
pt.is_read = 1;
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
if (clist[0] == 0) {
|
||||
detach_opt.ctrlrid = cd.ctrlr_id;
|
||||
memset(&clist, 0, sizeof(clist));
|
||||
clist[0] = htole16(1);
|
||||
clist[1] = htole16(detach_opt.ctrlrid);
|
||||
}
|
||||
} else {
|
||||
/* By default detach from this controller. */
|
||||
if (detach_opt.ctrlrid == NONE - 1)
|
||||
detach_opt.ctrlrid = cd.ctrlr_id;
|
||||
memset(&clist, 0, sizeof(clist));
|
||||
clist[0] = htole16(1);
|
||||
clist[1] = htole16(detach_opt.ctrlrid);
|
||||
}
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
|
||||
pt.cmd.cdw10 = htole32(1); /* detach */
|
||||
pt.cmd.nsid = detach_opt.nsid;
|
||||
pt.buf = &clist;
|
||||
pt.len = sizeof(clist);
|
||||
|
||||
if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
|
||||
errx(1, "ioctl request to %s failed: %d", argv[optind], result);
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl)) {
|
||||
errx(1, "namespace detach failed: %s",
|
||||
get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
|
||||
NVME_STATUS_SC_MASK));
|
||||
}
|
||||
printf("namespace %d detached\n", detach_opt.nsid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
nsattached(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int fd, i, n;
|
||||
uint16_t clist[2048];
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (attached_opt.nsid == NONE) {
|
||||
fprintf(stderr, "No valid NSID specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
open_dev(attached_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.nsid = htole32(attached_opt.nsid);
|
||||
pt.cmd.cdw10 = htole32(0x12);
|
||||
pt.buf = clist;
|
||||
pt.len = sizeof(clist);
|
||||
pt.is_read = 1;
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
|
||||
n = le16toh(clist[0]);
|
||||
printf("Attached %d controller(s):\n", n);
|
||||
for (i = 0; i < n; i++)
|
||||
printf(" 0x%04x\n", le16toh(clist[i + 1]));
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
nsidentify(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
struct nvme_namespace_data nsdata;
|
||||
uint8_t *data;
|
||||
int fd;
|
||||
u_int i;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (identify_opt.nsid == NONE) {
|
||||
fprintf(stderr, "No valid NSID specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
open_dev(identify_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.nsid = htole32(identify_opt.nsid);
|
||||
pt.cmd.cdw10 = htole32(0x11);
|
||||
pt.buf = &nsdata;
|
||||
pt.len = sizeof(nsdata);
|
||||
pt.is_read = 1;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
|
||||
close(fd);
|
||||
|
||||
data = (uint8_t *)&nsdata;
|
||||
for (i = 0; i < sizeof(nsdata); i++) {
|
||||
if (data[i] != 0)
|
||||
break;
|
||||
}
|
||||
if (i == sizeof(nsdata))
|
||||
errx(1, "namespace %d is not allocated", identify_opt.nsid);
|
||||
|
||||
/* Convert data to host endian */
|
||||
nvme_namespace_data_swapbytes(&nsdata);
|
||||
|
||||
if (identify_opt.hex) {
|
||||
i = sizeof(struct nvme_namespace_data);
|
||||
if (!identify_opt.verbose) {
|
||||
for (; i > 384; i--) {
|
||||
if (data[i - 1] != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
print_hex(&nsdata, i);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
print_namespace(&nsdata);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
ns(const struct cmd *nf __unused, int argc, char *argv[])
|
||||
{
|
||||
|
||||
cmd_dispatch(argc, argv, &ns_cmd);
|
||||
}
|
82
freebsd/sbin/nvmecontrol/nsid.c
Normal file
82
freebsd/sbin/nvmecontrol/nsid.c
Normal file
@ -0,0 +1,82 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2019 Alexander Motin <mav@FreeBSD.org>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
#include "comnd.h"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t gnsid;
|
||||
|
||||
static struct nsid_options {
|
||||
const char *dev;
|
||||
} nsid_opt = {
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct args nsid_args[] = {
|
||||
{ arg_string, &nsid_opt.dev, "namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd nsid_cmd = {
|
||||
.name = "nsid",
|
||||
.fn = gnsid,
|
||||
.descr = "Get controller and NSID for namespace",
|
||||
.ctx_size = sizeof(nsid_opt),
|
||||
.opts = NULL,
|
||||
.args = nsid_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(nsid_cmd);
|
||||
|
||||
static void
|
||||
gnsid(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
char *path;
|
||||
int fd;
|
||||
uint32_t nsid;
|
||||
|
||||
arg_parse(argc, argv, f);
|
||||
|
||||
open_dev(nsid_opt.dev, &fd, 1, 1);
|
||||
get_nsid(fd, &path, &nsid);
|
||||
close(fd);
|
||||
printf("%s\t%u\n", path, nsid);
|
||||
free(path);
|
||||
}
|
190
freebsd/sbin/nvmecontrol/nvmecontrol.c
Normal file
190
freebsd/sbin/nvmecontrol/nvmecontrol.c
Normal file
@ -0,0 +1,190 @@
|
||||
#include <machine/rtems-bsd-kernel-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel 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.
|
||||
* 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 <sys/stat.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dlfcn.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
static void
|
||||
print_bytes(void *data, uint32_t length)
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint8_t *p, *end;
|
||||
|
||||
end = (uint8_t *)data + length;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
p = (uint8_t *)data + (i*16);
|
||||
printf("%03x: ", i*16);
|
||||
for (j = 0; j < 16 && p < end; j++)
|
||||
printf("%02x ", *p++);
|
||||
if (p >= end)
|
||||
break;
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_dwords(void *data, uint32_t length)
|
||||
{
|
||||
uint32_t *p;
|
||||
uint32_t i, j;
|
||||
|
||||
p = (uint32_t *)data;
|
||||
length /= sizeof(uint32_t);
|
||||
|
||||
for (i = 0; i < length; i+=8) {
|
||||
printf("%03x: ", i*4);
|
||||
for (j = 0; j < 8; j++)
|
||||
printf("%08x ", p[i+j]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
print_hex(void *data, uint32_t length)
|
||||
{
|
||||
if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0)
|
||||
print_dwords(data, length);
|
||||
else
|
||||
print_bytes(data, length);
|
||||
}
|
||||
|
||||
void
|
||||
read_controller_data(int fd, struct nvme_controller_data *cdata)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.cdw10 = htole32(1);
|
||||
pt.buf = cdata;
|
||||
pt.len = sizeof(*cdata);
|
||||
pt.is_read = 1;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
|
||||
/* Convert data to host endian */
|
||||
nvme_controller_data_swapbytes(cdata);
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
}
|
||||
|
||||
void
|
||||
read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
pt.cmd.nsid = htole32(nsid);
|
||||
pt.cmd.cdw10 = htole32(0);
|
||||
pt.buf = nsdata;
|
||||
pt.len = sizeof(*nsdata);
|
||||
pt.is_read = 1;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "identify request failed");
|
||||
|
||||
/* Convert data to host endian */
|
||||
nvme_namespace_data_swapbytes(nsdata);
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
}
|
||||
|
||||
int
|
||||
open_dev(const char *str, int *fd, int show_error, int exit_on_error)
|
||||
{
|
||||
char full_path[64];
|
||||
|
||||
snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str);
|
||||
*fd = open(full_path, O_RDWR);
|
||||
if (*fd < 0) {
|
||||
if (show_error)
|
||||
warn("could not open %s", full_path);
|
||||
if (exit_on_error)
|
||||
exit(1);
|
||||
else
|
||||
return (errno);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
get_nsid(int fd, char **ctrlr_str, uint32_t *nsid)
|
||||
{
|
||||
struct nvme_get_nsid gnsid;
|
||||
|
||||
if (ioctl(fd, NVME_GET_NSID, &gnsid) < 0)
|
||||
err(1, "NVME_GET_NSID ioctl failed");
|
||||
if (ctrlr_str != NULL)
|
||||
*ctrlr_str = strndup(gnsid.cdev, sizeof(gnsid.cdev));
|
||||
if (nsid != NULL)
|
||||
*nsid = gnsid.nsid;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
cmd_init();
|
||||
|
||||
cmd_load_dir("/lib/nvmecontrol", NULL, NULL);
|
||||
cmd_load_dir("/usr/local/lib/nvmecontrol", NULL, NULL);
|
||||
|
||||
cmd_dispatch(argc, argv, NULL);
|
||||
|
||||
return (0);
|
||||
}
|
104
freebsd/sbin/nvmecontrol/nvmecontrol.h
Normal file
104
freebsd/sbin/nvmecontrol/nvmecontrol.h
Normal file
@ -0,0 +1,104 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel 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.
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __NVMECONTROL_H__
|
||||
#define __NVMECONTROL_H__
|
||||
|
||||
#include <dev/nvme/nvme.h>
|
||||
#include "comnd.h"
|
||||
|
||||
typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size);
|
||||
|
||||
struct logpage_function {
|
||||
SLIST_ENTRY(logpage_function) link;
|
||||
uint8_t log_page;
|
||||
const char *vendor;
|
||||
const char *name;
|
||||
print_fn_t print_fn;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \
|
||||
static struct logpage_function unique ## _lpf = { \
|
||||
.log_page = lp, \
|
||||
.vendor = vend, \
|
||||
.name = nam, \
|
||||
.print_fn = fn, \
|
||||
.size = sz, \
|
||||
} ; \
|
||||
static void logpage_reg_##unique(void) __attribute__((constructor)); \
|
||||
static void logpage_reg_##unique(void) { logpage_register(&unique##_lpf); }
|
||||
|
||||
#define DEFAULT_SIZE (4096)
|
||||
struct kv_name {
|
||||
uint32_t key;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key);
|
||||
|
||||
void logpage_register(struct logpage_function *p);
|
||||
#define NVME_CTRLR_PREFIX "nvme"
|
||||
#define NVME_NS_PREFIX "ns"
|
||||
|
||||
int open_dev(const char *str, int *fd, int show_error, int exit_on_error);
|
||||
void get_nsid(int fd, char **ctrlr_str, uint32_t *nsid);
|
||||
void read_controller_data(int fd, struct nvme_controller_data *cdata);
|
||||
void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata);
|
||||
void print_hex(void *data, uint32_t length);
|
||||
void print_namespace(struct nvme_namespace_data *nsdata);
|
||||
void read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp,
|
||||
uint16_t lsi, uint8_t rae, void *payload, uint32_t payload_size);
|
||||
void print_temp(uint16_t t);
|
||||
void print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused);
|
||||
|
||||
/* Utility Routines */
|
||||
/*
|
||||
* 128-bit integer augments to standard values. On i386 this
|
||||
* doesn't exist, so we use 64-bit values. So, on 32-bit i386,
|
||||
* you'll get truncated values until someone implement 128bit
|
||||
* ints in sofware.
|
||||
*/
|
||||
#define UINT128_DIG 39
|
||||
#ifdef __i386__
|
||||
typedef uint64_t uint128_t;
|
||||
#else
|
||||
typedef __uint128_t uint128_t;
|
||||
#endif
|
||||
|
||||
static __inline uint128_t
|
||||
to128(void *p)
|
||||
{
|
||||
return *(uint128_t *)p;
|
||||
}
|
||||
|
||||
uint64_t le48dec(const void *pp);
|
||||
char * uint128_to_str(uint128_t u, char *buf, size_t buflen);
|
||||
#endif
|
30
freebsd/sbin/nvmecontrol/nvmecontrol_ext.h
Normal file
30
freebsd/sbin/nvmecontrol/nvmecontrol_ext.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2018 Netflix
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
void nvme_print_controller(struct nvme_controller_data *cdata);
|
291
freebsd/sbin/nvmecontrol/passthru.c
Normal file
291
freebsd/sbin/nvmecontrol/passthru.c
Normal file
@ -0,0 +1,291 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel 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.
|
||||
* 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 <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
#include "comnd.h"
|
||||
|
||||
static struct options {
|
||||
uint8_t opcode;
|
||||
uint8_t flags;
|
||||
uint16_t rsvd;
|
||||
uint32_t nsid;
|
||||
uint32_t data_len;
|
||||
uint32_t metadata_len;
|
||||
uint32_t timeout;
|
||||
uint32_t cdw2;
|
||||
uint32_t cdw3;
|
||||
uint32_t cdw10;
|
||||
uint32_t cdw11;
|
||||
uint32_t cdw12;
|
||||
uint32_t cdw13;
|
||||
uint32_t cdw14;
|
||||
uint32_t cdw15;
|
||||
const char *ifn;
|
||||
bool binary;
|
||||
bool show_command;
|
||||
bool dry_run;
|
||||
bool read;
|
||||
bool write;
|
||||
uint8_t prefill;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.binary = false,
|
||||
.cdw10 = 0,
|
||||
.cdw11 = 0,
|
||||
.cdw12 = 0,
|
||||
.cdw13 = 0,
|
||||
.cdw14 = 0,
|
||||
.cdw15 = 0,
|
||||
.cdw2 = 0,
|
||||
.cdw3 = 0,
|
||||
.data_len = 0,
|
||||
.dry_run = false,
|
||||
.flags = 0,
|
||||
.ifn = "",
|
||||
.metadata_len = 0,
|
||||
.nsid = 0,
|
||||
.opcode = 0,
|
||||
.prefill = 0,
|
||||
.read = false,
|
||||
.rsvd = 0,
|
||||
.show_command = false,
|
||||
.timeout = 0,
|
||||
.write = false,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Argument names and short names selected to match the nvme-cli program
|
||||
* so vendor-siupplied formulas work out of the box on FreeBSD with a simple
|
||||
* s/nvme/nvmecontrol/.
|
||||
*/
|
||||
#define ARG(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
|
||||
static struct opts opts[] = {
|
||||
ARG("opcode", 'o', arg_uint8, opt, opcode,
|
||||
"NVMe command opcode (required)"),
|
||||
ARG("cdw2", '2', arg_uint32, opt, cdw2,
|
||||
"Command dword 2 value"),
|
||||
ARG("cdw3", '3', arg_uint32, opt, cdw3,
|
||||
"Command dword 3 value"),
|
||||
ARG("cdw10", '4', arg_uint32, opt, cdw10,
|
||||
"Command dword 10 value"),
|
||||
ARG("cdw11", '5', arg_uint32, opt, cdw11,
|
||||
"Command dword 11 value"),
|
||||
ARG("cdw12", '6', arg_uint32, opt, cdw12,
|
||||
"Command dword 12 value"),
|
||||
ARG("cdw13", '7', arg_uint32, opt, cdw13,
|
||||
"Command dword 13 value"),
|
||||
ARG("cdw14", '8', arg_uint32, opt, cdw14,
|
||||
"Command dword 14 value"),
|
||||
ARG("cdw15", '9', arg_uint32, opt, cdw15,
|
||||
"Command dword 15 value"),
|
||||
ARG("data-len", 'l', arg_uint32, opt, data_len,
|
||||
"Length of data for I/O (bytes)"),
|
||||
ARG("metadata-len", 'm', arg_uint32, opt, metadata_len,
|
||||
"Length of metadata segment (bytes) (igored)"),
|
||||
ARG("flags", 'f', arg_uint8, opt, flags,
|
||||
"NVMe command flags"),
|
||||
ARG("input-file", 'i', arg_path, opt, ifn,
|
||||
"Input file to send (default stdin)"),
|
||||
ARG("namespace-id", 'n', arg_uint32, opt, nsid,
|
||||
"Namespace id (ignored on FreeBSD)"),
|
||||
ARG("prefill", 'p', arg_uint8, opt, prefill,
|
||||
"Value to prefill payload with"),
|
||||
ARG("rsvd", 'R', arg_uint16, opt, rsvd,
|
||||
"Reserved field value"),
|
||||
ARG("timeout", 't', arg_uint32, opt, timeout,
|
||||
"Command timeout (ms)"),
|
||||
ARG("raw-binary", 'b', arg_none, opt, binary,
|
||||
"Output in binary format"),
|
||||
ARG("dry-run", 'd', arg_none, opt, dry_run,
|
||||
"Don't actually execute the command"),
|
||||
ARG("read", 'r', arg_none, opt, read,
|
||||
"Command reads data from device"),
|
||||
ARG("show-command", 's', arg_none, opt, show_command,
|
||||
"Show all the command values on stdout"),
|
||||
ARG("write", 'w', arg_none, opt, write,
|
||||
"Command writes data to device"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct args args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id|namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static void
|
||||
passthru(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
int fd = -1, ifd = -1;
|
||||
void *data = NULL, *metadata = NULL;
|
||||
struct nvme_pt_command pt;
|
||||
|
||||
arg_parse(argc, argv, f);
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
|
||||
if (opt.read && opt.write)
|
||||
errx(1, "need exactly one of --read or --write");
|
||||
if (opt.data_len != 0 && !opt.read && !opt.write)
|
||||
errx(1, "need exactly one of --read or --write");
|
||||
if (*opt.ifn && (ifd = open(opt.ifn, O_RDONLY)) == -1) {
|
||||
warn("open %s", opt.ifn);
|
||||
goto cleanup;
|
||||
}
|
||||
#if notyet /* No support in kernel for this */
|
||||
if (opt.metadata_len != 0) {
|
||||
if (posix_memalign(&metadata, getpagesize(), opt.metadata_len)) {
|
||||
warn("can't allocate %d bytes for metadata", metadata_len);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (opt.metadata_len != 0)
|
||||
errx(1, "metadata not supported on FreeBSD");
|
||||
#endif
|
||||
if (opt.data_len) {
|
||||
if (posix_memalign(&data, getpagesize(), opt.data_len)) {
|
||||
warn("can't allocate %d bytes for data", opt.data_len);
|
||||
goto cleanup;
|
||||
}
|
||||
memset(data, opt.prefill, opt.data_len);
|
||||
if (opt.write && read(ifd, data, opt.data_len) < 0) {
|
||||
warn("read %s", *opt.ifn ? opt.ifn : "stdin");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if (opt.show_command) {
|
||||
fprintf(stderr, "opcode : %#02x\n", opt.opcode);
|
||||
fprintf(stderr, "flags : %#02x\n", opt.flags);
|
||||
fprintf(stderr, "rsvd1 : %#04x\n", opt.rsvd);
|
||||
fprintf(stderr, "nsid : %#04x\n", opt.nsid);
|
||||
fprintf(stderr, "cdw2 : %#08x\n", opt.cdw2);
|
||||
fprintf(stderr, "cdw3 : %#08x\n", opt.cdw3);
|
||||
fprintf(stderr, "data_len : %#08x\n", opt.data_len);
|
||||
fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len);
|
||||
fprintf(stderr, "data : %p\n", data);
|
||||
fprintf(stderr, "metadata : %p\n", metadata);
|
||||
fprintf(stderr, "cdw10 : %#08x\n", opt.cdw10);
|
||||
fprintf(stderr, "cdw11 : %#08x\n", opt.cdw11);
|
||||
fprintf(stderr, "cdw12 : %#08x\n", opt.cdw12);
|
||||
fprintf(stderr, "cdw13 : %#08x\n", opt.cdw13);
|
||||
fprintf(stderr, "cdw14 : %#08x\n", opt.cdw14);
|
||||
fprintf(stderr, "cdw15 : %#08x\n", opt.cdw15);
|
||||
fprintf(stderr, "timeout_ms : %d\n", opt.timeout);
|
||||
}
|
||||
if (opt.dry_run) {
|
||||
errno = 0;
|
||||
warn("Doing a dry-run, no actual I/O");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = opt.opcode;
|
||||
pt.cmd.fuse = opt.flags;
|
||||
pt.cmd.cid = htole16(opt.rsvd);
|
||||
pt.cmd.nsid = opt.nsid; /* XXX note: kernel overrides this */
|
||||
pt.cmd.rsvd2 = htole32(opt.cdw2);
|
||||
pt.cmd.rsvd3 = htole32(opt.cdw3);
|
||||
pt.cmd.cdw10 = htole32(opt.cdw10);
|
||||
pt.cmd.cdw11 = htole32(opt.cdw11);
|
||||
pt.cmd.cdw12 = htole32(opt.cdw12);
|
||||
pt.cmd.cdw13 = htole32(opt.cdw13);
|
||||
pt.cmd.cdw14 = htole32(opt.cdw14);
|
||||
pt.cmd.cdw15 = htole32(opt.cdw15);
|
||||
pt.buf = data;
|
||||
pt.len = opt.data_len;
|
||||
pt.is_read = opt.read;
|
||||
|
||||
errno = 0;
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "passthrough request failed");
|
||||
/* XXX report status */
|
||||
if (opt.read) {
|
||||
if (opt.binary)
|
||||
write(STDOUT_FILENO, data, opt.data_len);
|
||||
else {
|
||||
/* print status here */
|
||||
print_hex(data, opt.data_len);
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
if (errno)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
admin_passthru(const struct cmd *nf, int argc, char *argv[])
|
||||
{
|
||||
|
||||
passthru(nf, argc, argv);
|
||||
}
|
||||
|
||||
static void
|
||||
io_passthru(const struct cmd *nf, int argc, char *argv[])
|
||||
{
|
||||
|
||||
passthru(nf, argc, argv);
|
||||
}
|
||||
|
||||
static struct cmd admin_pass_cmd = {
|
||||
.name = "admin-passthru",
|
||||
.fn = admin_passthru,
|
||||
.ctx_size = sizeof(struct options),
|
||||
.opts = opts,
|
||||
.args = args,
|
||||
.descr = "Send a pass through Admin command to the specified device",
|
||||
};
|
||||
|
||||
static struct cmd io_pass_cmd = {
|
||||
.name = "io-passthru",
|
||||
.fn = io_passthru,
|
||||
.ctx_size = sizeof(struct options),
|
||||
.opts = opts,
|
||||
.args = args,
|
||||
.descr = "Send a pass through Admin command to the specified device",
|
||||
};
|
||||
|
||||
CMD_COMMAND(admin_pass_cmd);
|
||||
CMD_COMMAND(io_pass_cmd);
|
188
freebsd/sbin/nvmecontrol/perftest.c
Normal file
188
freebsd/sbin/nvmecontrol/perftest.c
Normal file
@ -0,0 +1,188 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel 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.
|
||||
* 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 <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t perftest;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
static struct options {
|
||||
bool perthread;
|
||||
uint32_t threads;
|
||||
uint32_t size;
|
||||
uint32_t time;
|
||||
const char *op;
|
||||
const char *intr;
|
||||
const char *flags;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.perthread = false,
|
||||
.threads = 0,
|
||||
.size = 0,
|
||||
.time = 0,
|
||||
.op = NULL,
|
||||
.intr = NULL,
|
||||
.flags = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
|
||||
static const struct opts perftest_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("perthread", 'p', arg_none, opt, perthread,
|
||||
"Report per-thread results"),
|
||||
OPT("threads", 'n', arg_uint32, opt, threads,
|
||||
"Number of threads to run"),
|
||||
OPT("size", 's', arg_uint32, opt, size,
|
||||
"Size of the test"),
|
||||
OPT("time", 't', arg_uint32, opt, time,
|
||||
"How long to run the test in seconds"),
|
||||
OPT("operation", 'o', arg_string, opt, op,
|
||||
"Operation type: 'read' or 'write'"),
|
||||
OPT("interrupt", 'i', arg_string, opt, intr,
|
||||
"Interrupt mode: 'intr' or 'wait'"),
|
||||
OPT("flags", 'f', arg_string, opt, flags,
|
||||
"Turn on testing flags: refthread"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args perftest_args[] = {
|
||||
{ arg_string, &opt.dev, "namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd perftest_cmd = {
|
||||
.name = "perftest",
|
||||
.fn = perftest,
|
||||
.descr = "Perform low-level performance testing",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = perftest_opts,
|
||||
.args = perftest_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(perftest_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static void
|
||||
print_perftest(struct nvme_io_test *io_test, bool perthread)
|
||||
{
|
||||
uint64_t io_completed = 0, iops, mbps;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < io_test->num_threads; i++)
|
||||
io_completed += io_test->io_completed[i];
|
||||
|
||||
iops = io_completed/io_test->time;
|
||||
mbps = iops * io_test->size / (1024*1024);
|
||||
|
||||
printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7ju MB/s: %4ju\n",
|
||||
io_test->num_threads, io_test->size,
|
||||
io_test->opc == NVME_OPC_READ ? "READ" : "WRITE",
|
||||
io_test->time, (uintmax_t)iops, (uintmax_t)mbps);
|
||||
|
||||
if (perthread)
|
||||
for (i = 0; i < io_test->num_threads; i++)
|
||||
printf("\t%3d: %8ju IO/s\n", i,
|
||||
(uintmax_t)io_test->io_completed[i]/io_test->time);
|
||||
}
|
||||
|
||||
static void
|
||||
perftest(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_io_test io_test;
|
||||
int fd;
|
||||
u_long ioctl_cmd = NVME_IO_TEST;
|
||||
|
||||
memset(&io_test, 0, sizeof(io_test));
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
if (opt.flags == NULL || opt.op == NULL)
|
||||
arg_help(argc, argv, f);
|
||||
if (strcmp(opt.flags, "refthread") == 0)
|
||||
io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
|
||||
if (opt.intr != NULL) {
|
||||
if (strcmp(opt.intr, "bio") == 0 ||
|
||||
strcmp(opt.intr, "wait") == 0)
|
||||
ioctl_cmd = NVME_BIO_TEST;
|
||||
else if (strcmp(opt.intr, "io") == 0 ||
|
||||
strcmp(opt.intr, "intr") == 0)
|
||||
ioctl_cmd = NVME_IO_TEST;
|
||||
else {
|
||||
fprintf(stderr, "Unknown interrupt test type %s\n", opt.intr);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
}
|
||||
if (opt.threads <= 0 || opt.threads > 128) {
|
||||
fprintf(stderr, "Bad number of threads %d\n", opt.threads);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
if (strcasecmp(opt.op, "read") == 0)
|
||||
io_test.opc = NVME_OPC_READ;
|
||||
else if (strcasecmp(opt.op, "write") == 0)
|
||||
io_test.opc = NVME_OPC_WRITE;
|
||||
else {
|
||||
fprintf(stderr, "\"%s\" not valid opcode.\n", opt.op);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
if (opt.time == 0) {
|
||||
fprintf(stderr, "No time speciifed\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
io_test.time = opt.time;
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
if (ioctl(fd, ioctl_cmd, &io_test) < 0)
|
||||
err(1, "ioctl NVME_IO_TEST failed");
|
||||
|
||||
close(fd);
|
||||
print_perftest(&io_test, opt.perthread);
|
||||
exit(0);
|
||||
}
|
203
freebsd/sbin/nvmecontrol/power.c
Normal file
203
freebsd/sbin/nvmecontrol/power.c
Normal file
@ -0,0 +1,203 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2016 Netflix, Inc
|
||||
* 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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
_Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY,
|
||||
"nvme_power_state size wrong");
|
||||
|
||||
#define POWER_NONE 0xffffffffu
|
||||
|
||||
static struct options {
|
||||
bool list;
|
||||
uint32_t power;
|
||||
uint32_t workload;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.list = false,
|
||||
.power = POWER_NONE,
|
||||
.workload = 0,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static void
|
||||
power_list_one(int i, struct nvme_power_state *nps)
|
||||
{
|
||||
int mpower, apower, ipower;
|
||||
uint8_t mps, nops, aps, apw;
|
||||
|
||||
mps = (nps->mps_nops >> NVME_PWR_ST_MPS_SHIFT) &
|
||||
NVME_PWR_ST_MPS_MASK;
|
||||
nops = (nps->mps_nops >> NVME_PWR_ST_NOPS_SHIFT) &
|
||||
NVME_PWR_ST_NOPS_MASK;
|
||||
apw = (nps->apw_aps >> NVME_PWR_ST_APW_SHIFT) &
|
||||
NVME_PWR_ST_APW_MASK;
|
||||
aps = (nps->apw_aps >> NVME_PWR_ST_APS_SHIFT) &
|
||||
NVME_PWR_ST_APS_MASK;
|
||||
|
||||
mpower = nps->mp;
|
||||
if (mps == 0)
|
||||
mpower *= 100;
|
||||
ipower = nps->idlp;
|
||||
if (nps->ips == 1)
|
||||
ipower *= 100;
|
||||
apower = nps->actp;
|
||||
if (aps == 1)
|
||||
apower *= 100;
|
||||
printf("%2d: %2d.%04dW%c %3d.%03dms %3d.%03dms %2d %2d %2d %2d %2d.%04dW %2d.%04dW %d\n",
|
||||
i, mpower / 10000, mpower % 10000,
|
||||
nops ? '*' : ' ', nps->enlat / 1000, nps->enlat % 1000,
|
||||
nps->exlat / 1000, nps->exlat % 1000, nps->rrt, nps->rrl,
|
||||
nps->rwt, nps->rwl, ipower / 10000, ipower % 10000,
|
||||
apower / 10000, apower % 10000, apw);
|
||||
}
|
||||
|
||||
static void
|
||||
power_list(struct nvme_controller_data *cdata)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("\nPower States Supported: %d\n\n", cdata->npss + 1);
|
||||
printf(" # Max pwr Enter Lat Exit Lat RT RL WT WL Idle Pwr Act Pwr Workloadd\n");
|
||||
printf("-- -------- --------- --------- -- -- -- -- -------- -------- --\n");
|
||||
for (i = 0; i <= cdata->npss; i++)
|
||||
power_list_one(i, &cdata->power_state[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
power_set(int fd, int power_val, int workload, int perm)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
uint32_t p;
|
||||
|
||||
p = perm ? (1u << 31) : 0;
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_SET_FEATURES;
|
||||
pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT | p);
|
||||
pt.cmd.cdw11 = htole32(power_val | (workload << 5));
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "set feature power mgmt request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "set feature power mgmt request returned error");
|
||||
}
|
||||
|
||||
static void
|
||||
power_show(int fd)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_GET_FEATURES;
|
||||
pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT);
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "set feature power mgmt request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "set feature power mgmt request returned error");
|
||||
|
||||
printf("Current Power Mode is %d\n", pt.cpl.cdw0);
|
||||
}
|
||||
|
||||
static void
|
||||
power(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_controller_data cdata;
|
||||
int fd;
|
||||
|
||||
arg_parse(argc, argv, f);
|
||||
|
||||
if (opt.list && opt.power != POWER_NONE) {
|
||||
fprintf(stderr, "Can't set power and list power states\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
|
||||
if (opt.list) {
|
||||
read_controller_data(fd, &cdata);
|
||||
power_list(&cdata);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opt.power != POWER_NONE) {
|
||||
power_set(fd, opt.power, opt.workload, 0);
|
||||
goto out;
|
||||
}
|
||||
power_show(fd);
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static const struct opts power_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("list", 'l', arg_none, opt, list,
|
||||
"List the valid power states"),
|
||||
OPT("power", 'p', arg_uint32, opt, power,
|
||||
"Set the power state"),
|
||||
OPT("workload", 'w', arg_uint32, opt, workload,
|
||||
"Set the workload"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args power_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd power_cmd = {
|
||||
.name = "power",
|
||||
.fn = power,
|
||||
.descr = "Manage power states for the drive",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = power_opts,
|
||||
.args = power_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(power_cmd);
|
78
freebsd/sbin/nvmecontrol/reset.c
Normal file
78
freebsd/sbin/nvmecontrol/reset.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2012-2013 Intel 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.
|
||||
* 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 <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
static struct options {
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.dev = NULL
|
||||
};
|
||||
|
||||
static const struct args args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static void
|
||||
reset(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
|
||||
arg_parse(argc, argv, f);
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
|
||||
if (ioctl(fd, NVME_RESET_CONTROLLER) < 0)
|
||||
err(1, "reset request to %s failed", argv[optind]);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static struct cmd reset_cmd = {
|
||||
.name = "reset",
|
||||
.fn = reset,
|
||||
.descr = "Perform a controller-level reset",
|
||||
.args = args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(reset_cmd);
|
444
freebsd/sbin/nvmecontrol/resv.c
Normal file
444
freebsd/sbin/nvmecontrol/resv.c
Normal file
@ -0,0 +1,444 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2019 Alexander Motin <mav@FreeBSD.org>
|
||||
*
|
||||
* 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, immediately at the beginning of the file.
|
||||
* 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 ``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 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 <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t resv;
|
||||
static cmd_fn_t resvacquire;
|
||||
static cmd_fn_t resvregister;
|
||||
static cmd_fn_t resvrelease;
|
||||
static cmd_fn_t resvreport;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
#define NONE64 0xffffffffffffffffull
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
#define OPT_END { NULL, 0, arg_none, NULL, NULL }
|
||||
|
||||
static struct cmd resv_cmd = {
|
||||
.name = "resv",
|
||||
.fn = resv,
|
||||
.descr = "Reservation commands",
|
||||
.ctx_size = 0,
|
||||
.opts = NULL,
|
||||
.args = NULL,
|
||||
};
|
||||
|
||||
CMD_COMMAND(resv_cmd);
|
||||
|
||||
static struct acquire_options {
|
||||
uint64_t crkey;
|
||||
uint64_t prkey;
|
||||
uint8_t rtype;
|
||||
uint8_t racqa;
|
||||
const char *dev;
|
||||
} acquire_opt = {
|
||||
.crkey = 0,
|
||||
.prkey = 0,
|
||||
.rtype = 0,
|
||||
.racqa = 0,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts acquire_opts[] = {
|
||||
OPT("crkey", 'c', arg_uint64, acquire_opt, crkey,
|
||||
"Current Reservation Key"),
|
||||
OPT("prkey", 'p', arg_uint64, acquire_opt, prkey,
|
||||
"Preempt Reservation Key"),
|
||||
OPT("rtype", 't', arg_uint8, acquire_opt, rtype,
|
||||
"Reservation Type"),
|
||||
OPT("racqa", 'a', arg_uint8, acquire_opt, racqa,
|
||||
"Acquire Action (0=acq, 1=pre, 2=pre+ab)"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct args acquire_args[] = {
|
||||
{ arg_string, &acquire_opt.dev, "namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd acquire_cmd = {
|
||||
.name = "acquire",
|
||||
.fn = resvacquire,
|
||||
.descr = "Acquire/preempt reservation",
|
||||
.ctx_size = sizeof(acquire_opt),
|
||||
.opts = acquire_opts,
|
||||
.args = acquire_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(resv_cmd, acquire_cmd);
|
||||
|
||||
static struct register_options {
|
||||
uint64_t crkey;
|
||||
uint64_t nrkey;
|
||||
uint8_t rrega;
|
||||
bool iekey;
|
||||
uint8_t cptpl;
|
||||
const char *dev;
|
||||
} register_opt = {
|
||||
.crkey = 0,
|
||||
.nrkey = 0,
|
||||
.rrega = 0,
|
||||
.iekey = false,
|
||||
.cptpl = 0,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts register_opts[] = {
|
||||
OPT("crkey", 'c', arg_uint64, register_opt, crkey,
|
||||
"Current Reservation Key"),
|
||||
OPT("nrkey", 'k', arg_uint64, register_opt, nrkey,
|
||||
"New Reservation Key"),
|
||||
OPT("rrega", 'r', arg_uint8, register_opt, rrega,
|
||||
"Register Action (0=reg, 1=unreg, 2=replace)"),
|
||||
OPT("iekey", 'i', arg_none, register_opt, iekey,
|
||||
"Ignore Existing Key"),
|
||||
OPT("cptpl", 'p', arg_uint8, register_opt, cptpl,
|
||||
"Change Persist Through Power Loss State"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct args register_args[] = {
|
||||
{ arg_string, ®ister_opt.dev, "namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd register_cmd = {
|
||||
.name = "register",
|
||||
.fn = resvregister,
|
||||
.descr = "Register/unregister reservation",
|
||||
.ctx_size = sizeof(register_opt),
|
||||
.opts = register_opts,
|
||||
.args = register_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(resv_cmd, register_cmd);
|
||||
|
||||
static struct release_options {
|
||||
uint64_t crkey;
|
||||
uint8_t rtype;
|
||||
uint8_t rrela;
|
||||
const char *dev;
|
||||
} release_opt = {
|
||||
.crkey = 0,
|
||||
.rtype = 0,
|
||||
.rrela = 0,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts release_opts[] = {
|
||||
OPT("crkey", 'c', arg_uint64, release_opt, crkey,
|
||||
"Current Reservation Key"),
|
||||
OPT("rtype", 't', arg_uint8, release_opt, rtype,
|
||||
"Reservation Type"),
|
||||
OPT("rrela", 'a', arg_uint8, release_opt, rrela,
|
||||
"Release Action (0=release, 1=clear)"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct args release_args[] = {
|
||||
{ arg_string, &release_opt.dev, "namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd release_cmd = {
|
||||
.name = "release",
|
||||
.fn = resvrelease,
|
||||
.descr = "Release/clear reservation",
|
||||
.ctx_size = sizeof(release_opt),
|
||||
.opts = release_opts,
|
||||
.args = release_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(resv_cmd, release_cmd);
|
||||
|
||||
static struct report_options {
|
||||
bool hex;
|
||||
bool verbose;
|
||||
bool eds;
|
||||
const char *dev;
|
||||
} report_opt = {
|
||||
.hex = false,
|
||||
.verbose = false,
|
||||
.eds = false,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts report_opts[] = {
|
||||
OPT("hex", 'x', arg_none, report_opt, hex,
|
||||
"Print reservation status in hex"),
|
||||
OPT("verbose", 'v', arg_none, report_opt, verbose,
|
||||
"More verbosity"),
|
||||
OPT("eds", 'e', arg_none, report_opt, eds,
|
||||
"Extended Data Structure"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
|
||||
static const struct args report_args[] = {
|
||||
{ arg_string, &report_opt.dev, "namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd report_cmd = {
|
||||
.name = "report",
|
||||
.fn = resvreport,
|
||||
.descr = "Print reservation status",
|
||||
.ctx_size = sizeof(report_opt),
|
||||
.opts = report_opts,
|
||||
.args = report_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(resv_cmd, report_cmd);
|
||||
|
||||
/* handles NVME_OPC_RESERVATION_* NVM commands */
|
||||
|
||||
static void
|
||||
resvacquire(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
uint64_t data[2];
|
||||
int fd;
|
||||
uint32_t nsid;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
open_dev(acquire_opt.dev, &fd, 1, 1);
|
||||
get_nsid(fd, NULL, &nsid);
|
||||
if (nsid == 0) {
|
||||
fprintf(stderr, "This command require namespace-id\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
data[0] = htole64(acquire_opt.crkey);
|
||||
data[1] = htole64(acquire_opt.prkey);
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_RESERVATION_ACQUIRE;
|
||||
pt.cmd.cdw10 = htole32((acquire_opt.racqa & 7) |
|
||||
(acquire_opt.rtype << 8));
|
||||
pt.buf = &data;
|
||||
pt.len = sizeof(data);
|
||||
pt.is_read = 0;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "acquire request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "acquire request returned error");
|
||||
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
resvregister(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
uint64_t data[2];
|
||||
int fd;
|
||||
uint32_t nsid;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
open_dev(register_opt.dev, &fd, 1, 1);
|
||||
get_nsid(fd, NULL, &nsid);
|
||||
if (nsid == 0) {
|
||||
fprintf(stderr, "This command require namespace-id\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
data[0] = htole64(register_opt.crkey);
|
||||
data[1] = htole64(register_opt.nrkey);
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_RESERVATION_REGISTER;
|
||||
pt.cmd.cdw10 = htole32((register_opt.rrega & 7) |
|
||||
(register_opt.iekey << 3) | (register_opt.cptpl << 30));
|
||||
pt.buf = &data;
|
||||
pt.len = sizeof(data);
|
||||
pt.is_read = 0;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "register request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "register request returned error");
|
||||
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
resvrelease(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
uint64_t data[1];
|
||||
int fd;
|
||||
uint32_t nsid;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
open_dev(release_opt.dev, &fd, 1, 1);
|
||||
get_nsid(fd, NULL, &nsid);
|
||||
if (nsid == 0) {
|
||||
fprintf(stderr, "This command require namespace-id\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
data[0] = htole64(release_opt.crkey);
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_RESERVATION_RELEASE;
|
||||
pt.cmd.cdw10 = htole32((release_opt.rrela & 7) |
|
||||
(release_opt.rtype << 8));
|
||||
pt.buf = &data;
|
||||
pt.len = sizeof(data);
|
||||
pt.is_read = 0;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "release request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "release request returned error");
|
||||
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
resvreport(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_resv_status *s;
|
||||
struct nvme_resv_status_ext *e;
|
||||
uint8_t data[4096] __aligned(4);
|
||||
int fd;
|
||||
u_int i, n;
|
||||
uint32_t nsid;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
open_dev(report_opt.dev, &fd, 1, 1);
|
||||
get_nsid(fd, NULL, &nsid);
|
||||
if (nsid == 0) {
|
||||
fprintf(stderr, "This command require namespace-id\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
bzero(data, sizeof(data));
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_RESERVATION_REPORT;
|
||||
pt.cmd.cdw10 = htole32(sizeof(data) / 4 - 1);
|
||||
pt.cmd.cdw11 = htole32(report_opt.eds); /* EDS */
|
||||
pt.buf = &data;
|
||||
pt.len = sizeof(data);
|
||||
pt.is_read = 1;
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "report request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "report request returned error");
|
||||
|
||||
close(fd);
|
||||
|
||||
if (report_opt.eds)
|
||||
nvme_resv_status_ext_swapbytes((void *)data, sizeof(data));
|
||||
else
|
||||
nvme_resv_status_swapbytes((void *)data, sizeof(data));
|
||||
|
||||
if (report_opt.hex) {
|
||||
i = sizeof(data);
|
||||
if (!report_opt.verbose) {
|
||||
for (; i > 64; i--) {
|
||||
if (data[i - 1] != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
print_hex(&data, i);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
s = (struct nvme_resv_status *)data;
|
||||
n = (s->regctl[1] << 8) | s->regctl[0];
|
||||
printf("Generation: %u\n", s->gen);
|
||||
printf("Reservation Type: %u\n", s->rtype);
|
||||
printf("Number of Registered Controllers: %u\n", n);
|
||||
printf("Persist Through Power Loss State: %u\n", s->ptpls);
|
||||
if (report_opt.eds) {
|
||||
e = (struct nvme_resv_status_ext *)data;
|
||||
n = MIN(n, (sizeof(data) - sizeof(e)) / sizeof(e->ctrlr[0]));
|
||||
for (i = 0; i < n; i++) {
|
||||
printf("Controller ID: 0x%04x\n",
|
||||
e->ctrlr[i].ctrlr_id);
|
||||
printf(" Reservation Status: %u\n",
|
||||
e->ctrlr[i].rcsts);
|
||||
printf(" Reservation Key: 0x%08jx\n",
|
||||
e->ctrlr[i].rkey);
|
||||
printf(" Host Identifier: 0x%08jx%08jx\n",
|
||||
e->ctrlr[i].hostid[0], e->ctrlr[i].hostid[1]);
|
||||
}
|
||||
} else {
|
||||
n = MIN(n, (sizeof(data) - sizeof(s)) / sizeof(s->ctrlr[0]));
|
||||
for (i = 0; i < n; i++) {
|
||||
printf("Controller ID: 0x%04x\n",
|
||||
s->ctrlr[i].ctrlr_id);
|
||||
printf(" Reservation Status: %u\n",
|
||||
s->ctrlr[i].rcsts);
|
||||
printf(" Host Identifier: 0x%08jx\n",
|
||||
s->ctrlr[i].hostid);
|
||||
printf(" Reservation Key: 0x%08jx\n",
|
||||
s->ctrlr[i].rkey);
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
resv(const struct cmd *nf __unused, int argc, char *argv[])
|
||||
{
|
||||
|
||||
cmd_dispatch(argc, argv, &resv_cmd);
|
||||
}
|
224
freebsd/sbin/nvmecontrol/sanitize.c
Normal file
224
freebsd/sbin/nvmecontrol/sanitize.c
Normal file
@ -0,0 +1,224 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2019 Alexander Motin <mav@FreeBSD.org>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t sanitize;
|
||||
|
||||
static struct options {
|
||||
bool ause;
|
||||
bool ndas;
|
||||
bool oipbp;
|
||||
bool reportonly;
|
||||
uint8_t owpass;
|
||||
uint32_t ovrpat;
|
||||
const char *sanact;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.ause = false,
|
||||
.ndas = false,
|
||||
.oipbp = false,
|
||||
.reportonly = false,
|
||||
.owpass = 1,
|
||||
.ovrpat = 0,
|
||||
.sanact = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts sanitize_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("ause", 'U', arg_none, opt, ause,
|
||||
"Allow Unrestricted Sanitize Exit"),
|
||||
OPT("ndas", 'd', arg_none, opt, ndas,
|
||||
"No Deallocate After Sanitize"),
|
||||
OPT("oipbp", 'I', arg_none, opt, oipbp,
|
||||
"Overwrite Invert Pattern Between Passes"),
|
||||
OPT("reportonly", 'r', arg_none, opt, reportonly,
|
||||
"Report previous sanitize status"),
|
||||
OPT("owpass", 'c', arg_uint8, opt, owpass,
|
||||
"Overwrite Pass Count"),
|
||||
OPT("ovrpat", 'p', arg_uint32, opt, ovrpat,
|
||||
"Overwrite Pattern"),
|
||||
OPT("sanact", 'a', arg_string, opt, sanact,
|
||||
"Sanitize Action (block, overwrite, crypto)"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args sanitize_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd sanitize_cmd = {
|
||||
.name = "sanitize",
|
||||
.fn = sanitize,
|
||||
.descr = "Sanitize NVM subsystem",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = sanitize_opts,
|
||||
.args = sanitize_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(sanitize_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static void
|
||||
sanitize(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_controller_data cd;
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_sanitize_status_page ss;
|
||||
char *path;
|
||||
uint32_t nsid;
|
||||
int sanact = 0, fd, delay = 1;
|
||||
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
if (opt.sanact == NULL) {
|
||||
if (!opt.reportonly) {
|
||||
fprintf(stderr, "Sanitize Action is not specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
} else {
|
||||
if (strcmp(opt.sanact, "exitfailure") == 0)
|
||||
sanact = 1;
|
||||
else if (strcmp(opt.sanact, "block") == 0)
|
||||
sanact = 2;
|
||||
else if (strcmp(opt.sanact, "overwrite") == 0)
|
||||
sanact = 3;
|
||||
else if (strcmp(opt.sanact, "crypto") == 0)
|
||||
sanact = 4;
|
||||
else {
|
||||
fprintf(stderr, "Incorrect Sanitize Action value\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
}
|
||||
if (opt.owpass == 0 || opt.owpass > 16) {
|
||||
fprintf(stderr, "Incorrect Overwrite Pass Count value\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
get_nsid(fd, &path, &nsid);
|
||||
if (nsid != 0) {
|
||||
close(fd);
|
||||
open_dev(path, &fd, 1, 1);
|
||||
}
|
||||
free(path);
|
||||
|
||||
if (opt.reportonly)
|
||||
goto wait;
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
read_controller_data(fd, &cd);
|
||||
if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) &
|
||||
NVME_CTRLR_DATA_SANICAP_BES_MASK) == 0 && sanact == 2)
|
||||
errx(1, "controller does not support Block Erase");
|
||||
if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) &
|
||||
NVME_CTRLR_DATA_SANICAP_OWS_MASK) == 0 && sanact == 3)
|
||||
errx(1, "controller does not support Overwrite");
|
||||
if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) &
|
||||
NVME_CTRLR_DATA_SANICAP_CES_MASK) == 0 && sanact == 4)
|
||||
errx(1, "controller does not support Crypto Erase");
|
||||
|
||||
/*
|
||||
* If controller supports only one namespace, we may sanitize it.
|
||||
* If there can be more, make user explicit in his commands.
|
||||
*/
|
||||
if (nsid != 0 && cd.nn > 1)
|
||||
errx(1, "can't sanitize one of namespaces, specify controller");
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_SANITIZE;
|
||||
pt.cmd.cdw10 = htole32((opt.ndas << 9) | (opt.oipbp << 8) |
|
||||
((opt.owpass & 0xf) << 4) | (opt.ause << 3) | sanact);
|
||||
pt.cmd.cdw11 = htole32(opt.ovrpat);
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "sanitize request failed");
|
||||
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "sanitize request returned error");
|
||||
|
||||
wait:
|
||||
read_logpage(fd, NVME_LOG_SANITIZE_STATUS,
|
||||
NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &ss, sizeof(ss));
|
||||
switch ((ss.sstat >> NVME_SS_PAGE_SSTAT_STATUS_SHIFT) &
|
||||
NVME_SS_PAGE_SSTAT_STATUS_MASK) {
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_NEVER:
|
||||
printf("Never sanitized");
|
||||
break;
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_COMPLETED:
|
||||
printf("Sanitize completed");
|
||||
break;
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_INPROG:
|
||||
printf("Sanitize in progress: %u%% (%u/65535)\r",
|
||||
(ss.sprog * 100 + 32768) / 65536, ss.sprog);
|
||||
fflush(stdout);
|
||||
if (delay < 16)
|
||||
delay++;
|
||||
sleep(delay);
|
||||
goto wait;
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_FAILED:
|
||||
printf("Sanitize failed");
|
||||
break;
|
||||
case NVME_SS_PAGE_SSTAT_STATUS_COMPLETEDWD:
|
||||
printf("Sanitize completed with deallocation");
|
||||
break;
|
||||
default:
|
||||
printf("Sanitize status unknown");
|
||||
break;
|
||||
}
|
||||
if (delay > 1)
|
||||
printf(" ");
|
||||
printf("\n");
|
||||
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
198
freebsd/sbin/nvmecontrol/wdc.c
Normal file
198
freebsd/sbin/nvmecontrol/wdc.c
Normal file
@ -0,0 +1,198 @@
|
||||
#include <machine/rtems-bsd-user-space.h>
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2017 Netflix, Inc
|
||||
* 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 <sys/endian.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"
|
||||
|
||||
#define WDC_NVME_TOC_SIZE 8
|
||||
|
||||
#define WDC_NVME_CAP_DIAG_OPCODE 0xe6
|
||||
#define WDC_NVME_CAP_DIAG_CMD 0x0000
|
||||
|
||||
static void wdc_cap_diag(int argc, char *argv[]);
|
||||
|
||||
#define WDC_CAP_DIAG_USAGE "\tnvmecontrol wdc cap-diag [-o path-template]\n"
|
||||
|
||||
static struct nvme_function wdc_funcs[] = {
|
||||
{"cap-diag", wdc_cap_diag, WDC_CAP_DIAG_USAGE},
|
||||
{NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
static void
|
||||
wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix)
|
||||
{
|
||||
struct nvme_controller_data cdata;
|
||||
char sn[NVME_SERIAL_NUMBER_LENGTH + 1];
|
||||
char *walker;
|
||||
|
||||
len -= strlen(buf);
|
||||
buf += strlen(buf);
|
||||
read_controller_data(fd, &cdata);
|
||||
memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH);
|
||||
walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1;
|
||||
while (walker > sn && *walker == ' ')
|
||||
walker--;
|
||||
*++walker = '\0';
|
||||
snprintf(buf, len, "%s%s.bin", sn, suffix);
|
||||
}
|
||||
|
||||
static void
|
||||
wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd,
|
||||
uint8_t *buffer, size_t buflen)
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = opcode;
|
||||
pt.cmd.cdw10 = htole32(len / sizeof(uint32_t)); /* - 1 like all the others ??? */
|
||||
pt.cmd.cdw11 = htole32(off / sizeof(uint32_t));
|
||||
pt.cmd.cdw12 = htole32(cmd);
|
||||
pt.buf = buffer;
|
||||
pt.len = buflen;
|
||||
pt.is_read = 1;
|
||||
// printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n",
|
||||
// (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen);
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "wdc_get_data request failed");
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "wdc_get_data request returned error");
|
||||
}
|
||||
|
||||
static void
|
||||
wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode,
|
||||
uint32_t cmd, int len_off)
|
||||
{
|
||||
int first;
|
||||
int fd2;
|
||||
uint8_t *buf;
|
||||
uint32_t len, offset;
|
||||
size_t resid;
|
||||
|
||||
wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix);
|
||||
|
||||
/* XXX overwrite protection? */
|
||||
fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd2 < 0)
|
||||
err(1, "open %s", tmpl);
|
||||
buf = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE);
|
||||
if (buf == NULL)
|
||||
errx(1, "Can't get buffer to read dump");
|
||||
offset = 0;
|
||||
len = NVME_MAX_XFER_SIZE;
|
||||
first = 1;
|
||||
|
||||
do {
|
||||
resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len;
|
||||
wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid);
|
||||
|
||||
if (first) {
|
||||
len = be32dec(buf + len_off);
|
||||
if (len == 0)
|
||||
errx(1, "No data for %s", suffix);
|
||||
if (memcmp("E6LG", buf, 4) != 0)
|
||||
printf("Expected header of E6LG, found '%4.4s' instead\n",
|
||||
buf);
|
||||
printf("Dumping %d bytes of version %d.%d log to %s\n", len,
|
||||
buf[8], buf[9], tmpl);
|
||||
/*
|
||||
* Adjust amount to dump if total dump < 1MB,
|
||||
* though it likely doesn't matter to the WDC
|
||||
* analysis tools.
|
||||
*/
|
||||
if (resid > len)
|
||||
resid = len;
|
||||
first = 0;
|
||||
}
|
||||
if (write(fd2, buf, resid) != (ssize_t)resid)
|
||||
err(1, "write");
|
||||
offset += resid;
|
||||
len -= resid;
|
||||
} while (len > 0);
|
||||
free(buf);
|
||||
close(fd2);
|
||||
}
|
||||
|
||||
static void
|
||||
wdc_cap_diag_usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage:\n");
|
||||
fprintf(stderr, WDC_CAP_DIAG_USAGE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
wdc_cap_diag(int argc, char *argv[])
|
||||
{
|
||||
char path_tmpl[MAXPATHLEN];
|
||||
int ch, fd;
|
||||
|
||||
path_tmpl[0] = '\0';
|
||||
while ((ch = getopt(argc, argv, "o:")) != -1) {
|
||||
switch ((char)ch) {
|
||||
case 'o':
|
||||
strlcpy(path_tmpl, optarg, MAXPATHLEN);
|
||||
break;
|
||||
default:
|
||||
wdc_cap_diag_usage();
|
||||
}
|
||||
}
|
||||
/* Check that a controller was specified. */
|
||||
if (optind >= argc)
|
||||
wdc_cap_diag_usage();
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
|
||||
wdc_do_dump(fd, path_tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE,
|
||||
WDC_NVME_CAP_DIAG_CMD, 4);
|
||||
|
||||
close(fd);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
wdc(int argc, char *argv[])
|
||||
{
|
||||
|
||||
dispatch(argc, argv, wdc_funcs);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user