2025-02-16 12:16:06 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2025-02-16 11:09:01 +01:00
|
|
|
/*
|
|
|
|
* nvme.c -- NVM-Express command line utility.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2014-2015, Intel Corporation.
|
|
|
|
*
|
|
|
|
* Written by Keith Busch <kbusch@kernel.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This program uses NVMe IOCTLs to run native nvme commands to a device.
|
|
|
|
*/
|
2025-02-16 12:15:30 +01:00
|
|
|
#include "config.h"
|
|
|
|
#include "nvme/tree.h"
|
|
|
|
#include "nvme/types.h"
|
2025-02-16 11:09:01 +01:00
|
|
|
#include <errno.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <libgen.h>
|
2025-02-16 12:15:30 +01:00
|
|
|
#include <zlib.h>
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
#ifdef CONFIG_LIBHUGETLBFS
|
2025-02-16 11:09:01 +01:00
|
|
|
#include <hugetlbfs.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
#if HAVE_SYS_RANDOM
|
|
|
|
#include <sys/random.h>
|
|
|
|
#endif
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
#include "common.h"
|
2025-02-16 12:15:30 +01:00
|
|
|
#include "nvme.h"
|
|
|
|
#include "libnvme.h"
|
2025-02-16 11:09:01 +01:00
|
|
|
#include "nvme-print.h"
|
|
|
|
#include "plugin.h"
|
2025-02-16 12:15:30 +01:00
|
|
|
#include "util/base64.h"
|
2025-02-16 12:17:56 +01:00
|
|
|
#include "nvme-wrap.h"
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
#include "util/argconfig.h"
|
2025-02-16 11:09:01 +01:00
|
|
|
#include "fabrics.h"
|
|
|
|
|
|
|
|
#define CREATE_CMD
|
|
|
|
#include "nvme-builtin.h"
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct feat_cfg {
|
|
|
|
enum nvme_features_id feature_id;
|
|
|
|
__u32 namespace_id;
|
|
|
|
enum nvme_get_features_sel sel;
|
|
|
|
__u32 cdw11;
|
|
|
|
__u8 uuid_index;
|
|
|
|
__u32 data_len;
|
|
|
|
bool raw_binary;
|
|
|
|
bool human_readable;
|
|
|
|
};
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static const char nvme_version_string[] = NVME_VERSION;
|
|
|
|
|
|
|
|
static struct plugin builtin = {
|
|
|
|
.commands = commands,
|
|
|
|
.name = NULL,
|
|
|
|
.desc = NULL,
|
|
|
|
.next = NULL,
|
|
|
|
.tail = &builtin,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct program nvme = {
|
|
|
|
.name = "nvme",
|
|
|
|
.version = nvme_version_string,
|
|
|
|
.usage = "<command> [<device>] [<args>]",
|
|
|
|
.desc = "The '<device>' may be either an NVMe character "\
|
2025-02-16 12:17:56 +01:00
|
|
|
"device (ex: /dev/nvme0), an nvme block device "\
|
|
|
|
"(ex: /dev/nvme0n1), or a mctp address in the form "\
|
|
|
|
"mctp:<net>,<eid>[:ctrl-id]",
|
2025-02-16 11:09:01 +01:00
|
|
|
.extensions = &builtin,
|
|
|
|
};
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *output_format = "Output format: normal|json|binary";
|
2025-02-16 11:09:01 +01:00
|
|
|
static const char *output_format_no_binary = "Output format: normal|json";
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
static void *__nvme_alloc(size_t len, bool *huge) {
|
2025-02-16 11:09:01 +01:00
|
|
|
void *p;
|
|
|
|
|
|
|
|
if (!posix_memalign(&p, getpagesize(), len)) {
|
|
|
|
*huge = false;
|
|
|
|
memset(p, 0, len);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define HUGE_MIN 0x80000
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
#ifdef CONFIG_LIBHUGETLBFS
|
2025-02-16 11:30:13 +01:00
|
|
|
void nvme_free(void *p, bool huge)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
if (huge) {
|
|
|
|
if (p)
|
|
|
|
free_hugepage_region(p);
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
else
|
|
|
|
free(p);
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
void *nvme_alloc(size_t len, bool *huge)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
void *p;
|
|
|
|
|
|
|
|
if (len < HUGE_MIN)
|
|
|
|
return __nvme_alloc(len, huge);
|
|
|
|
|
|
|
|
p = get_hugepage_region(len, GHR_DEFAULT);
|
|
|
|
if (!p)
|
|
|
|
return __nvme_alloc(len, huge);
|
|
|
|
|
|
|
|
*huge = true;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
#else
|
2025-02-16 11:30:13 +01:00
|
|
|
void nvme_free(void *p, bool huge)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
free(p);
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
void *nvme_alloc(size_t len, bool *huge)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
return __nvme_alloc(len, huge);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *nvme_strerror(int errnum)
|
|
|
|
{
|
|
|
|
if (errnum >= ENVME_CONNECT_RESOLVE)
|
|
|
|
return nvme_errno_to_string(errnum);
|
|
|
|
return strerror(errnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
int map_log_level(int verbose, bool quiet)
|
|
|
|
{
|
|
|
|
int log_level;
|
|
|
|
|
|
|
|
switch (verbose) {
|
|
|
|
case 0:
|
|
|
|
log_level = LOG_WARNING;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
log_level = LOG_NOTICE;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
log_level = LOG_INFO;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_level = LOG_DEBUG;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (quiet)
|
|
|
|
log_level = LOG_ERR;
|
|
|
|
|
|
|
|
return log_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t getrandom_bytes(void *buf, size_t buflen)
|
|
|
|
{
|
|
|
|
#if HAVE_SYS_RANDOM
|
|
|
|
return getrandom(buf, buflen, GRND_NONBLOCK);
|
|
|
|
#else
|
|
|
|
ssize_t result;
|
|
|
|
int fd, err = 0;
|
|
|
|
|
|
|
|
fd = open("/dev/urandom", O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return fd;
|
|
|
|
result = read(fd, buf, buflen);
|
|
|
|
if (result < 0)
|
|
|
|
err = errno;
|
|
|
|
close(fd);
|
|
|
|
errno = err;
|
|
|
|
return result;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static bool is_chardev(struct nvme_dev *dev)
|
2025-02-16 11:30:13 +01:00
|
|
|
{
|
2025-02-16 12:17:56 +01:00
|
|
|
return S_ISCHR(dev->direct.stat.st_mode);
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static bool is_blkdev(struct nvme_dev *dev)
|
2025-02-16 11:30:13 +01:00
|
|
|
{
|
2025-02-16 12:17:56 +01:00
|
|
|
return S_ISBLK(dev->direct.stat.st_mode);
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int open_dev_direct(struct nvme_dev **devp, char *devstr, int flags)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
dev = calloc(1, sizeof(*dev));
|
|
|
|
if (!dev)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dev->type = NVME_DEV_DIRECT;
|
|
|
|
dev->name = basename(devstr);
|
|
|
|
err = open(devstr, flags);
|
|
|
|
if (err < 0) {
|
|
|
|
perror(devstr);
|
|
|
|
goto err_free;
|
|
|
|
}
|
|
|
|
dev->direct.fd = err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = fstat(dev_fd(dev), &dev->direct.stat);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:17:56 +01:00
|
|
|
perror(devstr);
|
|
|
|
goto err_close;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
if (!is_chardev(dev) && !is_blkdev(dev)) {
|
|
|
|
fprintf(stderr, "%s is not a block or character device\n",
|
|
|
|
devstr);
|
|
|
|
err = -ENODEV;
|
|
|
|
goto err_close;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
*devp = dev;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_close:
|
|
|
|
close(dev_fd(dev));
|
|
|
|
err_free:
|
|
|
|
free(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int parse_mi_dev(char *dev, unsigned int *net, uint8_t *eid,
|
|
|
|
unsigned int *ctrl)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* <net>,<eid>:<ctrl-id> form */
|
|
|
|
rc = sscanf(dev, "mctp:%u,%hhu:%u", net, eid, ctrl);
|
|
|
|
if (rc == 3)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* <net>,<eid> form, implicit ctrl-id = 0 */
|
|
|
|
*ctrl = 0;
|
|
|
|
rc = sscanf(dev, "mctp:%u,%hhu", net, eid);
|
|
|
|
if (rc == 2)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int open_dev_mi_mctp(struct nvme_dev **devp, char *devstr)
|
|
|
|
{
|
|
|
|
unsigned int net, ctrl_id;
|
|
|
|
struct nvme_dev *dev;
|
|
|
|
unsigned char eid;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = parse_mi_dev(devstr, &net, &eid, &ctrl_id);
|
|
|
|
if (rc) {
|
|
|
|
fprintf(stderr, "invalid device specifier '%s'\n", devstr);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = calloc(1, sizeof(*dev));
|
|
|
|
if (!dev)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dev->type = NVME_DEV_MI;
|
|
|
|
dev->name = devstr;
|
|
|
|
|
|
|
|
/* todo: verbose argument */
|
|
|
|
dev->mi.root = nvme_mi_create_root(stderr, LOG_WARNING);
|
|
|
|
if (!dev->mi.root)
|
|
|
|
goto err_free;
|
|
|
|
|
|
|
|
dev->mi.ep = nvme_mi_open_mctp(dev->mi.root, net, eid);
|
|
|
|
if (!dev->mi.ep)
|
|
|
|
goto err_free_root;
|
|
|
|
|
|
|
|
dev->mi.ctrl = nvme_mi_init_ctrl(dev->mi.ep, ctrl_id);
|
|
|
|
if (!dev->mi.ctrl)
|
|
|
|
goto err_close_ep;
|
|
|
|
|
|
|
|
*devp = dev;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_close_ep:
|
|
|
|
nvme_mi_close(dev->mi.ep);
|
|
|
|
err_free_root:
|
|
|
|
nvme_mi_free_root(dev->mi.root);
|
|
|
|
err_free:
|
|
|
|
free(dev);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int check_arg_dev(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if (optind >= argc) {
|
|
|
|
errno = EINVAL;
|
|
|
|
perror(argv[0]);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int get_dev(struct nvme_dev **dev, int argc, char **argv, int flags)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:17:56 +01:00
|
|
|
char *devname;
|
2025-02-16 11:09:01 +01:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = check_arg_dev(argc, argv);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
devname = argv[optind];
|
|
|
|
|
|
|
|
if (!strncmp(devname, "mctp:", strlen("mctp:")))
|
|
|
|
ret = open_dev_mi_mctp(dev, devname);
|
|
|
|
else
|
|
|
|
ret = open_dev_direct(dev, devname, flags);
|
|
|
|
|
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
int parse_and_open(struct nvme_dev **dev, int argc, char **argv,
|
|
|
|
const char *desc,
|
|
|
|
const struct argconfig_commandline_options *opts)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
ret = get_dev(dev, argc, argv, O_RDONLY);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (ret < 0)
|
|
|
|
argconfig_print_help(desc, opts);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
int open_exclusive(struct nvme_dev **dev, int argc, char **argv,
|
|
|
|
int ignore_exclusive)
|
2025-02-16 12:15:30 +01:00
|
|
|
{
|
2025-02-16 12:17:56 +01:00
|
|
|
int flags = O_RDONLY;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (!ignore_exclusive)
|
|
|
|
flags |= O_EXCL;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
return get_dev(dev, argc, argv, flags);
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
enum nvme_print_flags validate_output_format(const char *format)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
if (!format)
|
|
|
|
return -EINVAL;
|
|
|
|
if (!strcmp(format, "normal"))
|
|
|
|
return NORMAL;
|
|
|
|
if (!strcmp(format, "json"))
|
|
|
|
return JSON;
|
|
|
|
if (!strcmp(format, "binary"))
|
|
|
|
return BINARY;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
void dev_close(struct nvme_dev *dev)
|
|
|
|
{
|
|
|
|
switch (dev->type) {
|
|
|
|
case NVME_DEV_DIRECT:
|
|
|
|
close(dev_fd(dev));
|
|
|
|
break;
|
|
|
|
case NVME_DEV_MI:
|
|
|
|
nvme_mi_close(dev->mi.ep);
|
|
|
|
nvme_mi_free_root(dev->mi.root);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free(dev);
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int get_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
struct nvme_smart_log smart_log;
|
|
|
|
const char *desc = "Retrieve SMART log for the given device "\
|
|
|
|
"(or optionally a namespace) in either decoded format "\
|
|
|
|
"(default) or binary.";
|
|
|
|
const char *namespace = "(optional) desired namespace";
|
|
|
|
const char *raw = "output in binary format";
|
|
|
|
const char *human_readable = "show info in readable format";
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
|
|
|
bool human_readable;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = NVME_NSID_ALL,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
|
|
|
goto close_fd;
|
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_smart(dev, cfg.namespace_id, true,
|
|
|
|
&smart_log);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_smart_log(&smart_log, cfg.namespace_id,
|
|
|
|
dev->name, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "smart log: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
close_fd:
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_ana_log(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve ANA log for the given device in " \
|
|
|
|
"decoded format (default), json or binary.";
|
2025-02-16 12:16:06 +01:00
|
|
|
const char *groups = "Return ANA groups only.";
|
2025-02-16 11:09:01 +01:00
|
|
|
void *ana_log;
|
|
|
|
size_t ana_log_len;
|
|
|
|
struct nvme_id_ctrl ctrl;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:15:30 +01:00
|
|
|
enum nvme_log_ana_lsp lsp;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:16:06 +01:00
|
|
|
bool groups;
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:16:06 +01:00
|
|
|
.groups = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:16:06 +01:00
|
|
|
OPT_FLAG("groups", 'g', &cfg.groups, groups),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
|
|
|
goto close_fd;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ctrl(dev, &ctrl);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "ERROR : nvme_identify_ctrl() failed: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_fd;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
ana_log_len = sizeof(struct nvme_ana_log) +
|
2025-02-16 11:09:01 +01:00
|
|
|
le32_to_cpu(ctrl.nanagrpid) * sizeof(struct nvme_ana_group_desc);
|
|
|
|
if (!(ctrl.anacap & (1 << 6)))
|
|
|
|
ana_log_len += le32_to_cpu(ctrl.mnan) * sizeof(__le32);
|
|
|
|
|
|
|
|
ana_log = malloc(ana_log_len);
|
|
|
|
if (!ana_log) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_fd;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
lsp = cfg.groups ? NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY :
|
|
|
|
NVME_LOG_ANA_LSP_RGO_NAMESPACES;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_ana(dev, lsp, true, 0, ana_log_len, ana_log);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err) {
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_ana_log(ana_log, dev->name, flags, ana_log_len);
|
2025-02-16 11:09:01 +01:00
|
|
|
} else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "ana-log: %s", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
free(ana_log);
|
|
|
|
close_fd:
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static int get_telemetry_log(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
const char *desc = "Retrieve telemetry log and write to binary file";
|
|
|
|
const char *fname = "File name to save raw binary, includes header";
|
|
|
|
const char *hgen = "Have the host tell the controller to generate the report";
|
|
|
|
const char *cgen = "Gather report generated by the controller.";
|
2025-02-16 12:13:36 +01:00
|
|
|
const char *dgen = "Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. Valid options are 1, 2, 3, 4.";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_telemetry_log *log;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err = 0, output;
|
2025-02-16 12:15:30 +01:00
|
|
|
size_t total_size;
|
|
|
|
__u8 *data_ptr = NULL;
|
|
|
|
int data_written = 0, data_remaining = 0;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *file_name;
|
|
|
|
__u32 host_gen;
|
|
|
|
bool ctrl_init;
|
|
|
|
int data_area;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.file_name = NULL,
|
|
|
|
.host_gen = 1,
|
|
|
|
.ctrl_init = false,
|
|
|
|
.data_area = 3,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FILE("output-file", 'o', &cfg.file_name, fname),
|
|
|
|
OPT_UINT("host-generate", 'g', &cfg.host_gen, hgen),
|
|
|
|
OPT_FLAG("controller-init", 'c', &cfg.ctrl_init, cgen),
|
|
|
|
OPT_UINT("data-area", 'd', &cfg.data_area, dgen),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (!cfg.file_name) {
|
|
|
|
fprintf(stderr, "Please provide an output file!\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cfg.host_gen = !!cfg.host_gen;
|
|
|
|
output = open(cfg.file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
|
|
if (output < 0) {
|
|
|
|
fprintf(stderr, "Failed to open output file %s: %s!\n",
|
|
|
|
cfg.file_name, strerror(errno));
|
|
|
|
err = output;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.ctrl_init)
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_ctrl_telemetry(dev_fd(dev), true, &log,
|
|
|
|
cfg.data_area, &total_size);
|
2025-02-16 12:15:30 +01:00
|
|
|
else if (cfg.host_gen)
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_new_host_telemetry(dev_fd(dev), &log,
|
|
|
|
cfg.data_area, &total_size);
|
2025-02-16 12:15:30 +01:00
|
|
|
else
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_host_telemetry(dev_fd(dev), &log,
|
|
|
|
cfg.data_area, &total_size);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (err < 0) {
|
|
|
|
fprintf(stderr, "get-telemetry-log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
} else if (err > 0) {
|
|
|
|
nvme_show_status(err);
|
|
|
|
fprintf(stderr, "Failed to acquire telemetry log %d!\n", err);
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_output;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
data_written = 0;
|
|
|
|
data_remaining = total_size;
|
|
|
|
data_ptr = (__u8 *)log;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
while (data_remaining) {
|
|
|
|
data_written = write(output, data_ptr, data_remaining);
|
|
|
|
if (data_written < 0) {
|
|
|
|
data_remaining = data_written;
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
2025-02-16 12:15:30 +01:00
|
|
|
} else if (data_written <= data_remaining) {
|
|
|
|
data_remaining -= data_written;
|
|
|
|
data_ptr += data_written;
|
|
|
|
} else {
|
|
|
|
/* Unexpected overwrite */
|
|
|
|
fprintf(stderr, "Failure: Unexpected telemetry log overwrite - data_remaining = 0x%x, data_written = 0x%x\n",
|
|
|
|
data_remaining, data_written);
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (fsync(output) < 0) {
|
|
|
|
fprintf(stderr, "ERROR : %s: : fsync : %s\n", __func__, strerror(errno));
|
|
|
|
return -1;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
free(log);
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
close_output:
|
|
|
|
close(output);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_endurance_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
struct nvme_endurance_group_log endurance_log;
|
|
|
|
const char *desc = "Retrieves endurance groups log page and prints the log.";
|
|
|
|
const char *group_id = "The endurance group identifier";
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
__u16 group_id;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.group_id = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SHRT("group-id", 'g', &cfg.group_id, group_id),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_endurance_group(dev, cfg.group_id,
|
|
|
|
&endurance_log);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_endurance_log(&endurance_log, cfg.group_id,
|
|
|
|
dev->name, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "endurance log: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int collect_effects_log(struct nvme_dev *dev, enum nvme_csi csi,
|
2025-02-16 12:16:06 +01:00
|
|
|
struct list_head *list, int flags)
|
2025-02-16 12:15:30 +01:00
|
|
|
{
|
2025-02-16 12:16:06 +01:00
|
|
|
nvme_effects_log_node_t *node;
|
2025-02-16 12:15:30 +01:00
|
|
|
int err;
|
2025-02-16 12:16:06 +01:00
|
|
|
|
|
|
|
node = malloc(sizeof(nvme_effects_log_node_t));
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!node)
|
2025-02-16 12:16:06 +01:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
node->csi = csi;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_cmd_effects(dev, csi, &node->effects);
|
2025-02-16 12:16:44 +01:00
|
|
|
if (err) {
|
|
|
|
free(node);
|
2025-02-16 12:16:06 +01:00
|
|
|
return err;
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
2025-02-16 12:16:44 +01:00
|
|
|
list_add(list, &node->node);
|
|
|
|
return 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_effects_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve command effects log page and print the table.";
|
|
|
|
const char *raw = "show log in binary format";
|
|
|
|
const char *human_readable = "show log in readable format";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *csi = "";
|
|
|
|
struct list_head log_pages;
|
|
|
|
nvme_effects_log_node_t *node;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
void *bar = NULL;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
int err = -1;
|
2025-02-16 11:09:01 +01:00
|
|
|
enum nvme_print_flags flags;
|
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
bool human_readable;
|
|
|
|
bool raw_binary;
|
|
|
|
int csi;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.human_readable = false,
|
|
|
|
.raw_binary = false,
|
|
|
|
.csi = -1,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_INT("csi", 'c', &cfg.csi, csi),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
list_head_init(&log_pages);
|
|
|
|
|
|
|
|
if (cfg.csi < 0) {
|
|
|
|
nvme_root_t nvme_root;
|
|
|
|
uint64_t cap;
|
|
|
|
int nvme_command_set_supported;
|
|
|
|
int other_command_sets_supported;
|
|
|
|
nvme_root = nvme_scan(NULL);
|
2025-02-16 12:17:56 +01:00
|
|
|
bar = mmap_registers(nvme_root, dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_free_tree(nvme_root);
|
|
|
|
|
|
|
|
if (!bar) {
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
cap = mmio_read64(bar + NVME_REG_CAP);
|
|
|
|
munmap(bar, getpagesize());
|
|
|
|
|
|
|
|
nvme_command_set_supported = NVME_CAP_CSS(cap) & NVME_CAP_CSS_NVM;
|
|
|
|
other_command_sets_supported = NVME_CAP_CSS(cap) & NVME_CAP_CSS_CSI;
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
if (nvme_command_set_supported)
|
2025-02-16 12:17:56 +01:00
|
|
|
err = collect_effects_log(dev, NVME_CSI_NVM,
|
2025-02-16 12:16:06 +01:00
|
|
|
&log_pages, flags);
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
if (!err && other_command_sets_supported)
|
2025-02-16 12:17:56 +01:00
|
|
|
err = collect_effects_log(dev, NVME_CSI_ZNS,
|
2025-02-16 12:16:06 +01:00
|
|
|
&log_pages, flags);
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
} else {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = collect_effects_log(dev, cfg.csi, &log_pages, flags);
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_print_effects_log_pages(&log_pages, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
perror("effects log page");
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
while ((node = list_pop(&log_pages, nvme_effects_log_node_t, node))) {
|
2025-02-16 12:15:30 +01:00
|
|
|
free(node);
|
2025-02-16 12:17:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:13:36 +01:00
|
|
|
static int get_supported_log_pages(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve supported logs and print the table.";
|
|
|
|
const char *verbose = "Increase output verbosity";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_supported_log_pages supports;
|
2025-02-16 12:13:36 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
bool verbose;
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.verbose = false
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:13:36 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
if (cfg.verbose)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_supported_log_pages(dev, false, &supports);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_supported_log(&supports, dev->name, flags);
|
2025-02-16 12:13:36 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "supported log pages: %s",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:13:36 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:13:36 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int get_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve specified number of "\
|
|
|
|
"error log entries from a given device "\
|
|
|
|
"in either decoded format (default) or binary.";
|
|
|
|
const char *log_entries = "number of entries to retrieve";
|
|
|
|
const char *raw = "dump in binary format";
|
|
|
|
struct nvme_error_log_page *err_log;
|
|
|
|
struct nvme_id_ctrl ctrl;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 log_entries;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.log_entries = 64,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
|
|
|
if (!cfg.log_entries) {
|
|
|
|
fprintf(stderr, "non-zero log-entries is required param\n");
|
2025-02-16 12:10:50 +01:00
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ctrl(dev, &ctrl);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
|
|
|
perror("identify controller");
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
} else if (err) {
|
|
|
|
fprintf(stderr, "could not identify controller\n");
|
2025-02-16 12:10:50 +01:00
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cfg.log_entries = min(cfg.log_entries, ctrl.elpe + 1);
|
|
|
|
err_log = calloc(cfg.log_entries, sizeof(struct nvme_error_log_page));
|
|
|
|
if (!err_log) {
|
2025-02-16 12:10:50 +01:00
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_error(dev, cfg.log_entries, false, err_log);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_error_log(err_log, cfg.log_entries,
|
|
|
|
dev->name, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
perror("error log");
|
|
|
|
free(err_log);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_fw_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve the firmware log for the "\
|
|
|
|
"specified device in either decoded format (default) or binary.";
|
|
|
|
const char *raw = "use binary output";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_firmware_slot fw_log;
|
2025-02-16 11:09:01 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_fw_slot(dev, false, &fw_log);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_fw_log(&fw_log, dev->name, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "fw log: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_changed_ns_list_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve Changed Namespaces log for the given device "\
|
|
|
|
"in either decoded format "\
|
|
|
|
"(default) or binary.";
|
|
|
|
const char *raw = "output in binary format";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_ns_list changed_ns_list_log;
|
2025-02-16 11:09:01 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_changed_ns_list(dev, true,
|
|
|
|
&changed_ns_list_log);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_changed_ns_list_log(&changed_ns_list_log,
|
|
|
|
dev->name, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "changed ns list log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
static int get_pred_lat_per_nvmset_log(int argc, char **argv,
|
|
|
|
struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve Predictable latency per nvm set log "\
|
|
|
|
"page and prints it for the given device in either decoded " \
|
|
|
|
"format(default),json or binary.";
|
|
|
|
const char *nvmset_id = "NVM Set Identifier";
|
|
|
|
const char *raw = "use binary output";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_nvmset_predictable_lat_log plpns_log;
|
2025-02-16 11:30:13 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 nvmset_id;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.nvmset_id = 1,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_SHRT("nvmset-id", 'i', &cfg.nvmset_id, nvmset_id),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_predictable_lat_nvmset(dev, cfg.nvmset_id,
|
|
|
|
&plpns_log);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_predictable_latency_per_nvmset(&plpns_log,
|
2025-02-16 12:17:56 +01:00
|
|
|
cfg.nvmset_id, dev->name, flags);
|
2025-02-16 11:30:13 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "predictable latency per nvm set: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_pred_lat_event_agg_log(int argc, char **argv,
|
|
|
|
struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve Predictable Latency Event" \
|
|
|
|
"Aggregate Log page and prints it, for the given" \
|
|
|
|
"device in either decoded format(default)," \
|
|
|
|
"json or binary.";
|
|
|
|
const char *log_entries = "Number of pending NVM Set" \
|
|
|
|
"log Entries list";
|
|
|
|
const char *rae = "Retain an Asynchronous Event";
|
|
|
|
const char *raw = "use binary output";
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_id_ctrl ctrl;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
__u32 log_size;
|
2025-02-16 12:15:30 +01:00
|
|
|
void *pea_log;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u64 log_entries;
|
|
|
|
bool rae;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.log_entries = 2044,
|
|
|
|
.rae = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries),
|
|
|
|
OPT_FLAG("rae", 'r', &cfg.rae, rae),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
|
|
|
if (!cfg.log_entries) {
|
|
|
|
fprintf(stderr, "non-zero log-entries is required param\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ctrl(dev, &ctrl);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify controller: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
} else if (err) {
|
|
|
|
nvme_show_status(err);
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cfg.log_entries = min(cfg.log_entries, le32_to_cpu(ctrl.nsetidmax));
|
|
|
|
log_size = sizeof(__u64) + cfg.log_entries * sizeof(__u16);
|
|
|
|
pea_log = calloc(log_size, 1);
|
|
|
|
if (!pea_log) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_predictable_lat_event(dev, cfg.rae, 0,
|
|
|
|
log_size, pea_log);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_predictable_latency_event_agg_log(pea_log, cfg.log_entries,
|
2025-02-16 12:17:56 +01:00
|
|
|
log_size, dev->name, flags);
|
2025-02-16 11:30:13 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "predictable latency event aggregate log page: %s",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
free(pea_log);
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_persistent_event_log(int argc, char **argv,
|
|
|
|
struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve Persistent Event log info for"\
|
|
|
|
" the given device in either decoded format(default),"\
|
|
|
|
" json or binary.";
|
|
|
|
const char *action = "action the controller shall take during"\
|
|
|
|
" processing this persistent log page command.";
|
|
|
|
const char *log_len = "number of bytes to retrieve";
|
|
|
|
const char *raw = "use binary output";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_persistent_event_log *pevent, *pevent_collected;
|
2025-02-16 11:30:13 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:15:30 +01:00
|
|
|
void *pevent_log_info;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
bool huge;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 action;
|
|
|
|
__u32 log_len;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.action = 0xff,
|
|
|
|
.log_len = 0,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("action", 'a', &cfg.action, action),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_UINT("log_len", 'l', &cfg.log_len, log_len),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
pevent = calloc(sizeof(*pevent), 1);
|
|
|
|
if (!pevent) {
|
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_persistent_event(dev, cfg.action,
|
|
|
|
sizeof(*pevent), pevent);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "persistent event log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
goto free_pevent;
|
2025-02-16 11:30:13 +01:00
|
|
|
} else if (err) {
|
|
|
|
nvme_show_status(err);
|
2025-02-16 12:15:30 +01:00
|
|
|
goto free_pevent;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.action == NVME_PEVENT_LOG_RELEASE_CTX) {
|
|
|
|
printf("Releasing Persistent Event Log Context\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
goto free_pevent;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.log_len && cfg.action != NVME_PEVENT_LOG_EST_CTX_AND_READ) {
|
2025-02-16 12:15:30 +01:00
|
|
|
cfg.log_len = le64_to_cpu(pevent->tll);
|
2025-02-16 11:30:13 +01:00
|
|
|
} else if (!cfg.log_len && cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) {
|
|
|
|
printf("Establishing Persistent Event Log Context\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
goto free_pevent;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2025-02-16 12:16:06 +01:00
|
|
|
* if header already read with context establish action 0x1,
|
2025-02-16 11:30:13 +01:00
|
|
|
* action shall not be 0x1 again in the subsequent request,
|
|
|
|
* until the current context is released by issuing action
|
|
|
|
* with 0x2, otherwise throws command sequence error, make
|
|
|
|
* it as zero to read the log page
|
|
|
|
*/
|
|
|
|
if (cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ)
|
|
|
|
cfg.action = NVME_PEVENT_LOG_READ;
|
|
|
|
|
|
|
|
pevent_log_info = nvme_alloc(cfg.log_len, &huge);
|
|
|
|
if (!pevent_log_info) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto free_pevent;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_persistent_event(dev, cfg.action,
|
|
|
|
cfg.log_len, pevent_log_info);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (!err) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_persistent_event(dev, cfg.action,
|
|
|
|
sizeof(*pevent),
|
|
|
|
pevent);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "persistent event log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:13:36 +01:00
|
|
|
goto free;
|
|
|
|
} else if (err) {
|
|
|
|
nvme_show_status(err);
|
|
|
|
goto free;
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
pevent_collected = pevent_log_info;
|
|
|
|
if (pevent_collected->gen_number != pevent->gen_number) {
|
2025-02-16 12:13:36 +01:00
|
|
|
printf("Collected Persistent Event Log may be invalid, "\
|
2025-02-16 12:16:06 +01:00
|
|
|
"Re-read the log is required\n");
|
2025-02-16 12:13:36 +01:00
|
|
|
goto free;
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_show_persistent_event_log(pevent_log_info, cfg.action,
|
2025-02-16 12:17:56 +01:00
|
|
|
cfg.log_len, dev->name, flags);
|
2025-02-16 12:15:30 +01:00
|
|
|
} else if (err > 0)
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "persistent event log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:13:36 +01:00
|
|
|
free:
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_free(pevent_log_info, huge);
|
2025-02-16 12:15:30 +01:00
|
|
|
free_pevent:
|
|
|
|
free(pevent);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_endurance_event_agg_log(int argc, char **argv,
|
|
|
|
struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve Retrieve Predictable Latency " \
|
|
|
|
"Event Aggregate page and prints it, for the given " \
|
|
|
|
"device in either decoded format(default), " \
|
|
|
|
"json or binary.";
|
|
|
|
const char *log_entries = "Number of pending Endurance Group " \
|
|
|
|
"Event log Entries list";
|
|
|
|
const char *rae = "Retain an Asynchronous Event";
|
|
|
|
const char *raw = "use binary output";
|
|
|
|
void *endurance_log;
|
|
|
|
struct nvme_id_ctrl ctrl;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
__u32 log_size;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u64 log_entries;
|
|
|
|
bool rae;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.log_entries = 2044,
|
|
|
|
.rae = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("log-entries", 'e', &cfg.log_entries, log_entries),
|
|
|
|
OPT_FLAG("rae", 'r', &cfg.rae, rae),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
|
|
|
if (!cfg.log_entries) {
|
|
|
|
fprintf(stderr, "non-zero log-entries is required param\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ctrl(dev, &ctrl);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify controller: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
} else if (err) {
|
|
|
|
fprintf(stderr, "could not identify controller\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENODEV;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cfg.log_entries = min(cfg.log_entries, le16_to_cpu(ctrl.endgidmax));
|
|
|
|
log_size = sizeof(__u64) + cfg.log_entries * sizeof(__u16);
|
|
|
|
endurance_log = calloc(log_size, 1);
|
|
|
|
if (!endurance_log) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_endurance_grp_evt(dev, cfg.rae, 0, log_size,
|
|
|
|
endurance_log);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_endurance_group_event_agg_log(endurance_log, cfg.log_entries,
|
2025-02-16 12:17:56 +01:00
|
|
|
log_size, dev->name, flags);
|
2025-02-16 11:30:13 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "endurance group event aggregate log page: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
free(endurance_log);
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_lba_status_log(int argc, char **argv,
|
|
|
|
struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve Get LBA Status Info Log " \
|
|
|
|
"and prints it, for the given device in either " \
|
|
|
|
"decoded format(default),json or binary.";
|
|
|
|
const char *rae = "Retain an Asynchronous Event";
|
|
|
|
void *lab_status;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
__u32 lslplen;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
bool rae;
|
|
|
|
char *output_format;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.rae = false,
|
|
|
|
.output_format = "normal",
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FLAG("rae", 'r', &cfg.rae, rae),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_lba_status(dev, true, 0, sizeof(__u32),
|
|
|
|
&lslplen);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "lba status log page: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
} else if (err) {
|
|
|
|
nvme_show_status(err);
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
lab_status = calloc(lslplen, 1);
|
|
|
|
if (!lab_status) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_lba_status(dev, cfg.rae, 0, lslplen,
|
|
|
|
lab_status);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_lba_status_log(lab_status, lslplen, dev->name, flags);
|
2025-02-16 11:30:13 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "lba status log page: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
free(lab_status);
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_resv_notif_log(int argc, char **argv,
|
|
|
|
struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
|
|
|
|
const char *desc = "Retrieve Reservation Notification " \
|
|
|
|
"log page and prints it, for the given " \
|
|
|
|
"device in either decoded format(default), " \
|
|
|
|
"json or binary.";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_resv_notification_log resv;
|
2025-02-16 11:30:13 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_reservation(dev, false, &resv);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_resv_notif_log(&resv, dev->name, flags);
|
2025-02-16 11:30:13 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "resv notifi log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:13:36 +01:00
|
|
|
static int get_boot_part_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve Boot Partition " \
|
|
|
|
"log page and prints it, for the given " \
|
|
|
|
"device in either decoded format(default), " \
|
|
|
|
"json or binary.";
|
|
|
|
const char *lsp = "log specific field";
|
|
|
|
const char *fname = "boot partition data output file name";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_boot_partition boot;
|
2025-02-16 12:13:36 +01:00
|
|
|
__u8 *bp_log;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err = -1, output = 0;
|
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
__u32 bpsz = 0;
|
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 lsp;
|
|
|
|
char *file_name;
|
|
|
|
char *output_format;
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.lsp = 0,
|
|
|
|
.output_format = "normal",
|
|
|
|
.file_name = NULL,
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_BYTE("lsp", 's', &cfg.lsp, lsp),
|
|
|
|
OPT_FILE("output-file", 'f', &cfg.file_name, fname),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:13:36 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
if (!cfg.file_name) {
|
|
|
|
fprintf(stderr, "Please provide an output file!\n");
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.lsp > 128) {
|
|
|
|
fprintf(stderr, "invalid lsp param: %u\n", cfg.lsp);
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
output = open(cfg.file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
|
|
if (output < 0) {
|
|
|
|
fprintf(stderr, "Failed to open output file %s: %s!\n",
|
|
|
|
cfg.file_name, strerror(errno));
|
|
|
|
err = output;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_boot_partition(dev, false, cfg.lsp,
|
|
|
|
sizeof(boot), &boot);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "boot partition log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:13:36 +01:00
|
|
|
goto close_output;
|
|
|
|
} else if (err) {
|
|
|
|
nvme_show_status(err);
|
|
|
|
goto close_output;
|
|
|
|
}
|
|
|
|
|
|
|
|
bpsz = (boot.bpinfo & 0x7fff) * 128 * 1024;
|
|
|
|
bp_log = calloc(sizeof(boot) + bpsz, 1);
|
|
|
|
if (!bp_log) {
|
|
|
|
err = -1;
|
|
|
|
goto close_output;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_boot_partition(dev, false, cfg.lsp,
|
|
|
|
sizeof(boot) + bpsz,
|
|
|
|
(struct nvme_boot_partition *)bp_log);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_boot_part_log(&bp_log, dev->name, flags,
|
|
|
|
sizeof(boot) + bpsz);
|
2025-02-16 12:13:36 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "boot partition log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
err = write(output, (void *) bp_log + sizeof(boot), bpsz);
|
|
|
|
if (err != bpsz) {
|
|
|
|
fprintf(stderr, "Failed to flush all data to file!\n");
|
|
|
|
} else {
|
|
|
|
printf("Data flushed into file %s\n", cfg.file_name);
|
|
|
|
}
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
free(bp_log);
|
|
|
|
|
|
|
|
close_output:
|
|
|
|
close(output);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:13:36 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static int get_media_unit_stat_log(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *desc = "Retrieve the configuration and wear of media units and print it";
|
|
|
|
const char *domainid = "Domain Identifier";
|
|
|
|
const char *raw = "use binary output";
|
|
|
|
struct nvme_media_unit_stat_log mus;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
struct config {
|
|
|
|
__u16 domainid;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.domainid = 0,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("domain-id", 'd', &cfg.domainid, domainid),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:15:30 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_media_unit_stat(dev, cfg.domainid, &mus);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_media_unit_stat_log(&mus, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "media unit status log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
ret:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_supp_cap_config_log(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve the list of Supported Capacity Configuration Descriptors";
|
|
|
|
const char *domainid = "Domain Identifier";
|
|
|
|
const char *raw = "use binary output";
|
|
|
|
struct nvme_supported_cap_config_list_log cap_log;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
struct config {
|
|
|
|
__u16 domainid;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.domainid = 0,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("domain-id", 'd', &cfg.domainid, domainid),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:15:30 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_support_cap_config_list(dev, cfg.domainid,
|
|
|
|
&cap_log);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_supported_cap_config_log(&cap_log, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
perror("supported capacity configuration list log");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
ret:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve desired number of bytes "\
|
|
|
|
"from a given log on a specified device in either "\
|
|
|
|
"hex-dump (default) or binary format";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *log_id = "identifier of log to retrieve";
|
|
|
|
const char *log_len = "how many bytes to retrieve";
|
|
|
|
const char *aen = "result of the aen, use to override log id";
|
|
|
|
const char *lsp = "log specific field";
|
|
|
|
const char *lpo = "log page offset specifies the location within a log page from where to start returning data";
|
|
|
|
const char *lsi = "log specific identifier specifies an identifier that is required for a particular log page";
|
|
|
|
const char *rae = "retain an asynchronous event";
|
|
|
|
const char *raw = "output in raw format";
|
|
|
|
const char *uuid_index = "UUID index";
|
|
|
|
const char *csi = "command set identifier";
|
|
|
|
const char *offset_type = "offset type";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
unsigned char *log;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u8 log_id;
|
|
|
|
__u32 log_len;
|
|
|
|
__u32 aen;
|
|
|
|
__u64 lpo;
|
|
|
|
__u8 lsp;
|
|
|
|
__u16 lsi;
|
|
|
|
bool rae;
|
|
|
|
__u8 uuid_index;
|
|
|
|
bool raw_binary;
|
|
|
|
__u8 csi;
|
|
|
|
bool ot;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = NVME_NSID_ALL,
|
|
|
|
.log_id = 0xff,
|
|
|
|
.log_len = 0,
|
|
|
|
.aen = 0,
|
|
|
|
.lpo = NVME_LOG_LPO_NONE,
|
|
|
|
.lsp = NVME_LOG_LSP_NONE,
|
|
|
|
.lsi = NVME_LOG_LSI_NONE,
|
|
|
|
.rae = false,
|
|
|
|
.uuid_index = NVME_UUID_NONE,
|
|
|
|
.raw_binary = false,
|
|
|
|
.csi = NVME_CSI_NVM,
|
|
|
|
.ot = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("log-id", 'i', &cfg.log_id, log_id),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_UINT("log-len", 'l', &cfg.log_len, log_len),
|
|
|
|
OPT_UINT("aen", 'a', &cfg.aen, aen),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SUFFIX("lpo", 'o', &cfg.lpo, lpo),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("lsp", 's', &cfg.lsp, lsp),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SHRT("lsi", 'S', &cfg.lsi, lsi),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FLAG("rae", 'r', &cfg.rae, rae),
|
|
|
|
OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("csi", 'y', &cfg.csi, csi),
|
|
|
|
OPT_FLAG("ot", 'O', &cfg.ot, offset_type),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.aen) {
|
|
|
|
cfg.log_len = 4096;
|
|
|
|
cfg.log_id = (cfg.aen >> 16) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.log_len) {
|
2025-02-16 12:15:30 +01:00
|
|
|
perror("non-zero log-len is required param\n");
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
if (cfg.lsp > 128) {
|
2025-02-16 12:15:30 +01:00
|
|
|
perror("invalid lsp param\n");
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
if (cfg.uuid_index > 128) {
|
2025-02-16 12:15:30 +01:00
|
|
|
perror("invalid uuid index param\n");
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:10:50 +01:00
|
|
|
|
|
|
|
log = malloc(cfg.log_len);
|
|
|
|
if (!log) {
|
|
|
|
perror("could not alloc buffer for log\n");
|
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_get_log_args args = {
|
|
|
|
.args_size = sizeof(args),
|
|
|
|
.lid = cfg.log_id,
|
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.lpo = cfg.lpo,
|
|
|
|
.lsp = cfg.lsp,
|
|
|
|
.lsi = cfg.lsi,
|
|
|
|
.rae = cfg.rae,
|
|
|
|
.uuidx = cfg.uuid_index,
|
|
|
|
.csi = cfg.csi,
|
|
|
|
.ot = cfg.ot,
|
|
|
|
.len = cfg.log_len,
|
|
|
|
.log = log,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log(dev, &args);
|
2025-02-16 12:10:50 +01:00
|
|
|
if (!err) {
|
|
|
|
if (!cfg.raw_binary) {
|
|
|
|
printf("Device:%s log-id:%d namespace-id:%#x\n",
|
2025-02-16 12:17:56 +01:00
|
|
|
dev->name, cfg.log_id,
|
2025-02-16 12:15:30 +01:00
|
|
|
cfg.namespace_id);
|
2025-02-16 12:10:50 +01:00
|
|
|
d(log, cfg.log_len, 16, 1);
|
|
|
|
} else
|
|
|
|
d_raw((unsigned char *)log, cfg.log_len);
|
|
|
|
} else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "log page: %s\n", nvme_strerror(errno));
|
2025-02-16 12:10:50 +01:00
|
|
|
free(log);
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sanitize_log(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve sanitize log and show it.";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *rae = "Retain an Asynchronous Event";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *raw = "show log in binary format";
|
|
|
|
const char *human_readable = "show log in readable format";
|
|
|
|
struct nvme_sanitize_log_page sanitize_log;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
bool rae;
|
|
|
|
char *output_format;
|
|
|
|
bool human_readable;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.rae = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
.human_readable = false,
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_FLAG("rae", 'r', &cfg.rae, rae),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_sanitize(dev, cfg.rae, &sanitize_log);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_sanitize_log(&sanitize_log, dev->name, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "sanitize status log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:13:36 +01:00
|
|
|
static int get_fid_support_effects_log(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve FID Support and Effects log and show it.";
|
|
|
|
const char *human_readable = "show log in readable format";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_fid_supported_effects_log fid_support_log;
|
2025-02-16 12:13:36 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
bool human_readable;
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.human_readable = false,
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:13:36 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_fid_supported_effects(dev, false,
|
|
|
|
&fid_support_log);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_fid_support_effects_log(&fid_support_log,
|
|
|
|
dev->name, flags);
|
2025-02-16 12:13:36 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "fid support effects log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:13:36 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_mi_cmd_support_effects_log(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve NVMe-MI Command Support and Effects log and show it.";
|
|
|
|
const char *human_readable = "show log in readable format";
|
|
|
|
struct nvme_mi_cmd_supported_effects_log mi_cmd_support_log;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
struct config {
|
|
|
|
char *output_format;
|
|
|
|
bool human_readable;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.output_format = "normal",
|
|
|
|
.human_readable = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:15:30 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_mi_cmd_supported_effects(dev, false,
|
|
|
|
&mi_cmd_support_log);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!err)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_mi_cmd_support_effects_log(&mi_cmd_support_log,
|
|
|
|
dev->name, flags);
|
2025-02-16 12:15:30 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "mi command support effects log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
ret:
|
|
|
|
return err;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Show controller list information for the subsystem the "\
|
|
|
|
"given device is part of, or optionally controllers attached to a specific namespace.";
|
|
|
|
const char *controller = "controller to display";
|
|
|
|
const char *namespace_id = "optional namespace attached to controller";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_ctrl_list *cntlist;
|
2025-02-16 12:10:50 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 cntid;
|
|
|
|
__u32 namespace_id;
|
|
|
|
char *output_format;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.cntid = 0,
|
|
|
|
.namespace_id = NVME_NSID_NONE,
|
|
|
|
.output_format = "normal",
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SHRT("cntid", 'c', &cfg.cntid, controller),
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (posix_memalign((void *)&cntlist, getpagesize(), 0x1000)) {
|
|
|
|
fprintf(stderr, "can not allocate controller list payload\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.namespace_id == NVME_NSID_NONE)
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ctrl_list(dev, cfg.cntid, cntlist);
|
|
|
|
else
|
|
|
|
err = nvme_cli_identify_nsid_ctrl_list(dev, cfg.namespace_id,
|
|
|
|
cfg.cntid, cntlist);
|
2025-02-16 12:10:50 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_list_ctrl(cntlist, flags);
|
|
|
|
else if (err > 0)
|
2025-02-16 11:09:01 +01:00
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "id controller list: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
free(cntlist);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int list_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "For the specified controller handle, show the "\
|
|
|
|
"namespace list in the associated NVMe subsystem, optionally starting with a given nsid.";
|
|
|
|
const char *namespace_id = "first nsid returned list should start from";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *csi = "I/O command set identifier";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *all = "show all namespaces in the subsystem, whether attached or inactive";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_ns_list ns_list;
|
2025-02-16 12:10:50 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
int csi;
|
|
|
|
bool all;
|
|
|
|
char *output_format;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 1,
|
|
|
|
.csi = -1,
|
|
|
|
.all = false,
|
2025-02-16 12:10:50 +01:00
|
|
|
.output_format = "normal",
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_INT("csi", 'y', &cfg.csi, csi),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_FLAG("all", 'a', &cfg.all, all),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
if (flags != JSON && flags != NORMAL) {
|
2025-02-16 11:09:01 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
fprintf(stderr, "invalid nsid parameter\n");
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:16:44 +01:00
|
|
|
struct nvme_identify_args args = {
|
|
|
|
.args_size = sizeof(args),
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.data = &ns_list,
|
|
|
|
.nsid = cfg.namespace_id - 1.
|
|
|
|
};
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.csi < 0) {
|
2025-02-16 12:16:44 +01:00
|
|
|
args.cns = cfg.all ? NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST :
|
|
|
|
NVME_IDENTIFY_CNS_NS_ACTIVE_LIST;
|
2025-02-16 12:15:30 +01:00
|
|
|
} else {
|
2025-02-16 12:16:44 +01:00
|
|
|
args.cns = cfg.all ? NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST :
|
|
|
|
NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST;
|
|
|
|
args.csi = cfg.csi;
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify(dev, &args);
|
2025-02-16 12:16:44 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_list_ns(&ns_list, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "id namespace list: %s",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
ret:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int id_ns_lba_format(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify Namespace command to the given "\
|
|
|
|
"device, returns capability field properties of the specified "\
|
|
|
|
"LBA Format index in various formats.";
|
|
|
|
const char *lba_format_index = "The index into the LBA Format list "\
|
|
|
|
"identifying the LBA Format capabilities that are to be returned";
|
|
|
|
const char *uuid_index = "UUID index";
|
|
|
|
const char *verbose = "Increase output verbosity";
|
|
|
|
enum nvme_print_flags flags;
|
|
|
|
struct nvme_id_ns ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
struct config {
|
|
|
|
__u16 lba_format_index;
|
|
|
|
__u8 uuid_index;
|
|
|
|
bool verbose;
|
|
|
|
char *output_format;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.lba_format_index = 0,
|
|
|
|
.uuid_index = NVME_UUID_NONE,
|
|
|
|
.verbose = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("lba-format-index", 'i', &cfg.lba_format_index, lba_format_index),
|
|
|
|
OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index),
|
|
|
|
OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:15:30 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (cfg.verbose)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_ns_csi_user_data_format(dev_fd(dev),
|
|
|
|
cfg.lba_format_index,
|
2025-02-16 12:15:30 +01:00
|
|
|
cfg.uuid_index, NVME_CSI_NVM, &ns);
|
2025-02-16 12:10:50 +01:00
|
|
|
if (!err)
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_id_ns(&ns, 0, cfg.lba_format_index, true, flags);
|
2025-02-16 12:10:50 +01:00
|
|
|
else if (err > 0)
|
2025-02-16 11:09:01 +01:00
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
perror("identify namespace for specific LBA format");
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
|
|
|
return nvme_status_to_errno(err, false);
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:13:36 +01:00
|
|
|
static int id_endurance_grp_list(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Show endurance group list information for the given endurance "\
|
|
|
|
"group id";
|
|
|
|
const char *endurance_grp_id = "Endurance Group ID";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_id_endurance_group_list *endgrp_list;
|
2025-02-16 12:13:36 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 endgrp_id;
|
|
|
|
char *output_format;
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.endgrp_id = 0,
|
|
|
|
.output_format = "normal",
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_SHRT("endgrp-id", 'i', &cfg.endgrp_id, endurance_grp_id),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:13:36 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
if (flags != JSON && flags != NORMAL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
fprintf(stderr, "invalid output format\n");
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (posix_memalign((void *)&endgrp_list, getpagesize(), 0x1000)) {
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_endurance_group_list(dev_fd(dev), cfg.endgrp_id,
|
|
|
|
endgrp_list);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_endurance_group_list(endgrp_list, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "Id endurance group list: %s",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
free(endgrp_list);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:13:36 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Delete the given namespace by "\
|
|
|
|
"sending a namespace management command to "\
|
|
|
|
"the provided device. All controllers should be detached from "\
|
|
|
|
"the namespace prior to namespace deletion. A namespace ID "\
|
|
|
|
"becomes inactive when that namespace is detached or, if "\
|
|
|
|
"the namespace is not already inactive, once deleted.";
|
|
|
|
const char *namespace_id = "namespace to delete";
|
|
|
|
const char *timeout = "timeout value, in milliseconds";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
|
|
|
__u32 namespace_id;
|
|
|
|
__u32 timeout;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.namespace_id = 0,
|
2025-02-16 12:15:30 +01:00
|
|
|
.timeout = 120000,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_UINT("timeout", 't', &cfg.timeout, timeout),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_ns_mgmt_delete(dev, cfg.namespace_id);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
printf("%s: Success, deleted nsid:%d\n", cmd->name,
|
|
|
|
cfg.namespace_id);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "delete namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, struct command *cmd)
|
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_ctrl_list cntlist;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err, num, i, list[2048];
|
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
__u16 ctrlist[2048];
|
|
|
|
|
|
|
|
const char *namespace_id = "namespace to attach";
|
|
|
|
const char *cont = "optional comma-sep controller id list";
|
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
char *cntlist;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.cntlist = "",
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_LIST("controllers", 'c', &cfg.cntlist, cont),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
|
|
|
fprintf(stderr, "%s: namespace-id parameter required\n",
|
|
|
|
cmd->name);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
num = argconfig_parse_comma_sep_array(cfg.cntlist, list, 2047);
|
|
|
|
if (!num) {
|
|
|
|
fprintf(stderr, "warning: empty controller-id list will result in no actual change in namespace attachment\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num == -1) {
|
|
|
|
fprintf(stderr, "%s: controller id list is malformed\n",
|
|
|
|
cmd->name);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++)
|
2025-02-16 12:15:30 +01:00
|
|
|
ctrlist[i] = (__u16)list[i];
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_init_ctrl_list(&cntlist, num, ctrlist);
|
|
|
|
|
|
|
|
if (attach)
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_ns_attach_ctrls(dev, cfg.namespace_id,
|
|
|
|
&cntlist);
|
2025-02-16 12:15:30 +01:00
|
|
|
else
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_ns_detach_ctrls(dev, cfg.namespace_id,
|
|
|
|
&cntlist);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
if (!err)
|
|
|
|
printf("%s: Success, nsid:%d\n", cmd->name, cfg.namespace_id);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
perror(attach ? "attach namespace" : "detach namespace");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int attach_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Attach the given namespace to the "\
|
|
|
|
"given controller or comma-sep list of controllers. ID of the "\
|
|
|
|
"given namespace becomes active upon attachment to a "\
|
|
|
|
"controller. A namespace must be attached to a controller "\
|
|
|
|
"before IO commands may be directed to that namespace.";
|
|
|
|
return nvme_attach_ns(argc, argv, 1, desc, cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int detach_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Detach the given namespace from the "\
|
|
|
|
"given controller; de-activates the given namespace's ID. A "\
|
|
|
|
"namespace must be attached to a controller before IO "\
|
|
|
|
"commands may be directed to that namespace.";
|
|
|
|
return nvme_attach_ns(argc, argv, 0, desc, cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send a namespace management command "\
|
|
|
|
"to the specified device to create a namespace with the given "\
|
|
|
|
"parameters. The next available namespace ID is used for the "\
|
|
|
|
"create operation. Note that create-ns does not attach the "\
|
|
|
|
"namespace to a controller, the attach-ns command is needed.";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *nsze = "size of ns (NSZE)";
|
|
|
|
const char *ncap = "capacity of ns (NCAP)";
|
|
|
|
const char *flbas = "Formatted LBA size (FLBAS), if entering this "\
|
|
|
|
"value ignore \'block-size\' field";
|
|
|
|
const char *dps = "data protection settings (DPS)";
|
|
|
|
const char *nmic = "multipath and sharing capabilities (NMIC)";
|
|
|
|
const char *anagrpid = "ANA Group Identifier (ANAGRPID)";
|
|
|
|
const char *nvmsetid = "NVM Set Identifier (NVMSETID)";
|
|
|
|
const char *csi = "command set identifier (CSI)";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *lbstm = "logical block storage tag mask (LBSTM)";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *timeout = "timeout value, in milliseconds";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *bs = "target block size, specify only if \'FLBAS\' "\
|
|
|
|
"value not entered";
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct nvme_id_ns ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = 0, i;
|
2025-02-16 11:09:01 +01:00
|
|
|
__u32 nsid;
|
|
|
|
|
|
|
|
struct config {
|
|
|
|
__u64 nsze;
|
|
|
|
__u64 ncap;
|
|
|
|
__u8 flbas;
|
|
|
|
__u8 dps;
|
|
|
|
__u8 nmic;
|
|
|
|
__u32 anagrpid;
|
|
|
|
__u16 nvmsetid;
|
|
|
|
__u64 bs;
|
|
|
|
__u32 timeout;
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 csi;
|
|
|
|
__u64 lbstm;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsze = 0,
|
|
|
|
.ncap = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
.flbas = 0xff,
|
2025-02-16 12:15:30 +01:00
|
|
|
.dps = 0,
|
|
|
|
.nmic = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
.anagrpid = 0,
|
|
|
|
.nvmsetid = 0,
|
|
|
|
.bs = 0x00,
|
2025-02-16 12:15:30 +01:00
|
|
|
.timeout = 120000,
|
|
|
|
.csi = 0,
|
|
|
|
.lbstm = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_SUFFIX("nsze", 's', &cfg.nsze, nsze),
|
|
|
|
OPT_SUFFIX("ncap", 'c', &cfg.ncap, ncap),
|
|
|
|
OPT_BYTE("flbas", 'f', &cfg.flbas, flbas),
|
|
|
|
OPT_BYTE("dps", 'd', &cfg.dps, dps),
|
|
|
|
OPT_BYTE("nmic", 'm', &cfg.nmic, nmic),
|
|
|
|
OPT_UINT("anagrp-id", 'a', &cfg.anagrpid, anagrpid),
|
|
|
|
OPT_UINT("nvmset-id", 'i', &cfg.nvmsetid, nvmsetid),
|
|
|
|
OPT_SUFFIX("block-size", 'b', &cfg.bs, bs),
|
|
|
|
OPT_UINT("timeout", 't', &cfg.timeout, timeout),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_BYTE("csi", 'y', &cfg.csi, csi),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_SUFFIX("lbstm", 'l', &cfg.lbstm, lbstm),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.flbas != 0xff && cfg.bs != 0x00) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Invalid specification of both FLBAS and Block Size, please specify only one\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.bs) {
|
|
|
|
if ((cfg.bs & (~cfg.bs + 1)) != cfg.bs) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Invalid value for block size (%"PRIu64"). Block size must be a power of two\n",
|
|
|
|
(uint64_t)cfg.bs);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ns(dev, NVME_NSID_ALL, &ns);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err) {
|
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify-namespace: %s",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else {
|
|
|
|
fprintf(stderr, "identify failed\n");
|
|
|
|
nvme_show_status(err);
|
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:16:06 +01:00
|
|
|
for (i = 0; i <= ns.nlbaf; ++i) {
|
2025-02-16 11:09:01 +01:00
|
|
|
if ((1 << ns.lbaf[i].ds) == cfg.bs && ns.lbaf[i].ms == 0) {
|
|
|
|
cfg.flbas = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (cfg.flbas == 0xff) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"FLBAS corresponding to block size %"PRIu64" not found\n",
|
|
|
|
(uint64_t)cfg.bs);
|
|
|
|
fprintf(stderr,
|
|
|
|
"Please correct block size, or specify FLBAS directly\n");
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_id_ns ns2 = {
|
|
|
|
.nsze = cpu_to_le64(cfg.nsze),
|
|
|
|
.ncap = cpu_to_le64(cfg.ncap),
|
|
|
|
.flbas = cfg.flbas,
|
|
|
|
.dps = cfg.dps,
|
|
|
|
.nmic = cfg.nmic,
|
|
|
|
.anagrpid = cpu_to_le32(cfg.anagrpid),
|
|
|
|
.nvmsetid = cpu_to_le16(cfg.nvmsetid),
|
|
|
|
.lbstm = cpu_to_le64(cfg.lbstm),
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_ns_mgmt_create(dev, &ns2, &nsid, cfg.timeout, cfg.csi);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
printf("%s: Success, created nsid:%d\n", cmd->name, nsid);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "create namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nvme_match_device_filter(nvme_subsystem_t s,
|
|
|
|
nvme_ctrl_t c, nvme_ns_t ns, void *f_args)
|
|
|
|
{
|
|
|
|
int ret, instance, nsid, s_num;
|
|
|
|
char *devname = f_args;
|
|
|
|
|
|
|
|
if (!devname || !strlen(devname))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
ret = sscanf(devname, "nvme%dn%d", &instance, &nsid);
|
|
|
|
if (ret != 2)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (s) {
|
|
|
|
ret = sscanf(nvme_subsystem_get_name(s), "nvme-subsys%d",
|
|
|
|
&s_num);
|
|
|
|
if (ret == 1 && s_num == instance)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (c) {
|
|
|
|
s = nvme_ctrl_get_subsystem(c);
|
|
|
|
|
|
|
|
ret = sscanf(nvme_subsystem_get_name(s), "nvme-subsys%d",
|
|
|
|
&s_num);
|
|
|
|
if (ret == 1 && s_num == instance)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (ns) {
|
|
|
|
if (!strcmp(devname, nvme_ns_get_name(ns)))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int list_subsys(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_root_t r = NULL;
|
2025-02-16 11:09:01 +01:00
|
|
|
enum nvme_print_flags flags;
|
|
|
|
const char *desc = "Retrieve information for subsystems";
|
|
|
|
const char *verbose = "Increase output verbosity";
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_scan_filter_t filter = NULL;
|
2025-02-16 12:17:56 +01:00
|
|
|
char *devname;
|
2025-02-16 12:15:30 +01:00
|
|
|
int err;
|
2025-02-16 12:16:06 +01:00
|
|
|
int nsid = NVME_NSID_ALL;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
int verbose;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.verbose = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_INCR("verbose", 'v', &cfg.verbose, verbose),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err < 0)
|
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
devname = NULL;
|
2025-02-16 12:15:30 +01:00
|
|
|
if (optind < argc)
|
2025-02-16 12:17:56 +01:00
|
|
|
devname = basename(argv[optind++]);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
goto ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (flags != JSON && flags != NORMAL) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
|
|
|
goto ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.verbose)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
|
|
|
|
if (!r) {
|
2025-02-16 12:17:56 +01:00
|
|
|
if (devname)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr,
|
|
|
|
"Failed to scan nvme subsystem for %s\n",
|
2025-02-16 12:17:56 +01:00
|
|
|
devname);
|
2025-02-16 12:15:30 +01:00
|
|
|
else
|
|
|
|
fprintf(stderr, "Failed to scan nvme subsystem\n");
|
|
|
|
err = -errno;
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
if (devname) {
|
2025-02-16 12:16:06 +01:00
|
|
|
int subsys_num;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
if (sscanf(devname, "nvme%dn%d", &subsys_num, &nsid) != 2) {
|
|
|
|
fprintf(stderr, "Invalid device name %s\n", devname);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
filter = nvme_match_device_filter;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_scan_topology(r, filter, (void *)devname);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
goto ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
nvme_show_subsystem_list(r, nsid != NVME_NSID_ALL, flags);
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
if (r)
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int list(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve basic information for all NVMe namespaces";
|
|
|
|
const char *verbose = "Increase output verbosity";
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_root_t r;
|
|
|
|
int err = 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
bool verbose;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.verbose = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary),
|
|
|
|
OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
|
|
|
return err;
|
|
|
|
if (flags != JSON && flags != NORMAL) {
|
|
|
|
fprintf(stderr, "Invalid output format\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (cfg.verbose)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
|
|
|
|
if (!r) {
|
|
|
|
fprintf(stderr, "Failed to create topology root: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
err = nvme_scan_topology(r, NULL, NULL);
|
|
|
|
if (err < 0) {
|
2025-02-16 12:16:06 +01:00
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
2025-02-16 11:09:01 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_list_items(r, flags);
|
|
|
|
nvme_free_tree(r);
|
|
|
|
|
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int __id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin,
|
|
|
|
void (*vs)(__u8 *vs, struct json_object *root))
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify Controller command to "\
|
|
|
|
"the given device and report information about the specified "\
|
|
|
|
"controller in human-readable or "\
|
|
|
|
"binary format. May also return vendor-specific "\
|
|
|
|
"controller attributes in hex-dump if requested.";
|
|
|
|
const char *vendor_specific = "dump binary vendor field";
|
|
|
|
const char *raw = "show identify in binary format";
|
|
|
|
const char *human_readable = "show identify in readable format";
|
|
|
|
enum nvme_print_flags flags;
|
|
|
|
struct nvme_id_ctrl ctrl;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
bool vendor_specific;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
|
|
|
bool human_readable;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.vendor_specific = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FLAG("vendor-specific", 'v', &cfg.vendor_specific, vendor_specific),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
if (cfg.vendor_specific)
|
|
|
|
flags |= VS;
|
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ctrl(dev, &ctrl);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_id_ctrl(&ctrl, flags, vs);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify controller: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
return __id_ctrl(argc, argv, cmd, plugin, NULL);
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
static int nvm_id_ctrl(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify Controller NVM Command Set "\
|
|
|
|
"command to the given device and report information about "\
|
|
|
|
"the specified controller in various formats.";
|
|
|
|
enum nvme_print_flags flags;
|
|
|
|
struct nvme_id_ctrl_nvm ctrl_nvm;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_nvm_identify_ctrl(dev_fd(dev), &ctrl_nvm);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_id_ctrl_nvm(&ctrl_nvm, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "nvm identify controller: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
ret:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvm_id_ns(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify Namespace NVM Command Set "\
|
|
|
|
"command to the given device and report information about "\
|
|
|
|
"the specified namespace in various formats.";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
const char *uuid_index = "UUID index";
|
|
|
|
const char *verbose = "Increase output verbosity";
|
|
|
|
enum nvme_print_flags flags;
|
|
|
|
struct nvme_nvm_id_ns id_ns;
|
|
|
|
struct nvme_id_ns ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
struct config {
|
|
|
|
__u32 namespace_id;
|
|
|
|
__u8 uuid_index;
|
|
|
|
char *output_format;
|
|
|
|
bool verbose;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.namespace_id = 0,
|
|
|
|
.uuid_index = NVME_UUID_NONE,
|
|
|
|
.output_format = "normal",
|
|
|
|
.verbose = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:15:30 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (cfg.verbose)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (err < 0) {
|
|
|
|
perror("get-namespace-id");
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ns(dev, cfg.namespace_id, &ns);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (err) {
|
|
|
|
nvme_show_status(err);
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_ns_csi(dev_fd(dev), cfg.namespace_id,
|
|
|
|
cfg.uuid_index,
|
2025-02-16 12:15:30 +01:00
|
|
|
NVME_CSI_NVM, &id_ns);
|
|
|
|
if (!err)
|
|
|
|
nvme_show_nvm_id_ns(&id_ns, cfg.namespace_id, &ns, 0, false, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
perror("nvm identify namespace");
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
ret:
|
|
|
|
return nvme_status_to_errno(err, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvm_id_ns_lba_format(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an NVM Command Set specific Identify Namespace "
|
|
|
|
"command to the given device, returns capability field properties of "
|
|
|
|
"the specified LBA Format index in the specified namespace in various "
|
|
|
|
"formats.";
|
|
|
|
const char *lba_format_index = "The index into the LBA Format list "\
|
|
|
|
"identifying the LBA Format capabilities that are to be returned";
|
|
|
|
const char *uuid_index = "UUID index";
|
|
|
|
const char *verbose = "Increase output verbosity";
|
|
|
|
enum nvme_print_flags flags;
|
|
|
|
struct nvme_id_ns ns;
|
|
|
|
struct nvme_nvm_id_ns nvm_ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
struct config {
|
|
|
|
__u16 lba_format_index;
|
|
|
|
__u8 uuid_index;
|
|
|
|
bool verbose;
|
|
|
|
char *output_format;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.lba_format_index = 0,
|
|
|
|
.uuid_index = NVME_UUID_NONE,
|
|
|
|
.verbose = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("lba-format-index", 'i', &cfg.lba_format_index, lba_format_index),
|
|
|
|
OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index),
|
|
|
|
OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:15:30 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (cfg.verbose)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ns(dev, NVME_NSID_ALL, &ns);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (err) {
|
|
|
|
ns.nlbaf = NVME_FEAT_LBA_RANGE_MAX - 1;
|
|
|
|
ns.nulbaf = 0;
|
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_iocs_ns_csi_user_data_format(dev_fd(dev),
|
|
|
|
cfg.lba_format_index,
|
2025-02-16 12:15:30 +01:00
|
|
|
cfg.uuid_index, NVME_CSI_NVM, &nvm_ns);
|
|
|
|
if (!err)
|
|
|
|
nvme_show_nvm_id_ns(&nvm_ns, 0, &ns, cfg.lba_format_index, true,
|
|
|
|
flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
perror("NVM identify namespace for specific LBA format");
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
|
|
|
return nvme_status_to_errno(err, false);
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send Namespace Identification Descriptors command to the "\
|
|
|
|
"given device, returns the namespace identification descriptors "\
|
|
|
|
"of the specific namespace in either human-readable or binary format.";
|
|
|
|
const char *raw = "show descriptors in binary format";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
void *nsdescs;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (posix_memalign(&nsdescs, getpagesize(), 0x1000)) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_ns_descs(dev_fd(dev), cfg.namespace_id, nsdescs);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_id_ns_descs(nsdescs, cfg.namespace_id, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
free(nsdescs);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify Namespace command to the "\
|
|
|
|
"given device, returns properties of the specified namespace "\
|
|
|
|
"in either human-readable or binary format. Can also return "\
|
|
|
|
"binary vendor-specific namespace attributes.";
|
2025-02-16 12:16:06 +01:00
|
|
|
const char *force = "Return this namespace, even if not attached (1.2 devices only)";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *vendor_specific = "dump binary vendor fields";
|
|
|
|
const char *raw = "show identify in binary format";
|
|
|
|
const char *human_readable = "show identify in readable format";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
|
|
|
|
enum nvme_print_flags flags;
|
|
|
|
struct nvme_id_ns ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
bool force;
|
|
|
|
bool vendor_specific;
|
|
|
|
bool raw_binary;
|
|
|
|
char *output_format;
|
|
|
|
bool human_readable;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.force = false,
|
|
|
|
.vendor_specific = false,
|
|
|
|
.raw_binary = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("force", 0, &cfg.force, force),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FLAG("vendor-specific", 'v', &cfg.vendor_specific, vendor_specific),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
if (cfg.vendor_specific)
|
|
|
|
flags |= VS;
|
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.force)
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_allocated_ns(dev,
|
|
|
|
cfg.namespace_id, &ns);
|
2025-02-16 12:15:30 +01:00
|
|
|
else
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ns(dev, cfg.namespace_id, &ns);
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_id_ns(&ns, cfg.namespace_id, 0, false, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:13:36 +01:00
|
|
|
static int cmd_set_independent_id_ns(int argc, char **argv,
|
|
|
|
struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an I/O Command Set Independent Identify "\
|
|
|
|
"Namespace command to the given device, returns properties of the "\
|
|
|
|
"specified namespace in human-readable or binary or json format.";
|
|
|
|
const char *raw = "show identify in binary format";
|
|
|
|
const char *human_readable = "show identify in readable format";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_id_independent_id_ns ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
bool raw_binary;
|
|
|
|
char *output_format;
|
|
|
|
bool human_readable;
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.raw_binary = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
.human_readable = false,
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:13:36 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = cfg.namespace_id = nvme_get_nsid(dev_fd(dev),
|
|
|
|
&cfg.namespace_id);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (err < 0) {
|
|
|
|
perror("get-namespace-id");
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_independent_identify_ns(dev_fd(dev),
|
|
|
|
cfg.namespace_id, &ns);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_cmd_set_independent_id_ns(&ns, cfg.namespace_id, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "I/O command set independent identify namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:13:36 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify Namespace Granularity List command to the "\
|
|
|
|
"given device, returns namespace granularity list "\
|
|
|
|
"in either human-readable or binary format.";
|
|
|
|
|
|
|
|
struct nvme_id_ns_granularity_list *granularity_list;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
if (posix_memalign((void *)&granularity_list, getpagesize(), NVME_IDENTIFY_DATA_SIZE)) {
|
|
|
|
fprintf(stderr, "can not allocate granularity list payload\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_ns_granularity(dev_fd(dev), granularity_list);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_id_ns_granularity_list(granularity_list, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify namespace granularity: %s\n", nvme_strerror(errno));
|
2025-02-16 12:16:06 +01:00
|
|
|
free(granularity_list);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int id_nvmset(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify NVM Set List command to the "\
|
|
|
|
"given device, returns entries for NVM Set identifiers greater "\
|
|
|
|
"than or equal to the value specified CDW11.NVMSETID "\
|
|
|
|
"in either binary format or json format";
|
|
|
|
const char *nvmset_id = "NVM Set Identify value";
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_id_nvmset_list nvmset;
|
2025-02-16 11:09:01 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 nvmset_id;
|
|
|
|
char *output_format;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.nvmset_id = 0,
|
|
|
|
.output_format = "normal",
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SHRT("nvmset_id", 'i', &cfg.nvmset_id, nvmset_id),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_nvmset_list(dev_fd(dev), cfg.nvmset_id, &nvmset);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_id_nvmset(&nvmset, cfg.nvmset_id, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify nvm set list: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int id_uuid(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify UUID List command to the "\
|
|
|
|
"given device, returns list of supported Vendor Specific UUIDs "\
|
|
|
|
"in either human-readable or binary format.";
|
|
|
|
const char *raw = "show uuid in binary format";
|
|
|
|
const char *human_readable = "show uuid in readable format";
|
|
|
|
|
|
|
|
struct nvme_id_uuid_list uuid_list;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
|
|
|
bool human_readable;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:10:50 +01:00
|
|
|
goto ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_uuid(dev_fd(dev), &uuid_list);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_id_uuid_list(&uuid_list, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify UUID list: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:10:50 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
static int id_iocs(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send an Identify Command Set Data command to the "\
|
|
|
|
"given device, returns properties of the specified controller "\
|
|
|
|
"in either human-readable or binary format.";
|
|
|
|
const char *controller_id = "identifier of desired controller";
|
|
|
|
struct nvme_id_iocs iocs;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 cntid;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.cntid = 0xffff,
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_SHRT("controller-id", 'c', &cfg.cntid, controller_id),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_iocs(dev_fd(dev), cfg.cntid, &iocs);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err) {
|
|
|
|
printf("NVMe Identify I/O Command Set:\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_id_iocs(&iocs);
|
2025-02-16 11:30:13 +01:00
|
|
|
} else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "NVMe Identify I/O Command Set: %s\n", nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 12:10:50 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int id_domain(int argc, char **argv, struct command *cmd, struct plugin *plugin) {
|
|
|
|
const char *desc = "Send an Identify Domain List command to the "\
|
|
|
|
"given device, returns properties of the specified domain "\
|
|
|
|
"in either normal|json|binary format.";
|
|
|
|
const char *domain_id = "identifier of desired domain";
|
|
|
|
struct nvme_id_domain_list id_domain;
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 12:10:50 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 dom_id;
|
|
|
|
char *output_format;
|
2025-02-16 12:10:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.dom_id = 0xffff,
|
|
|
|
.output_format = "normal",
|
2025-02-16 12:10:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_SHRT("dom-id", 'd', &cfg.dom_id, domain_id),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:10:50 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_domain_list(dev_fd(dev), cfg.dom_id, &id_domain);
|
2025-02-16 12:10:50 +01:00
|
|
|
if (!err) {
|
|
|
|
printf("NVMe Identify command for Domain List is successful:\n");
|
|
|
|
printf("NVMe Identify Domain List:\n");
|
|
|
|
nvme_show_id_domain_list(&id_domain, flags);
|
|
|
|
} else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "NVMe Identify Domain List: %s\n", nvme_strerror(errno));
|
2025-02-16 12:10:50 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int get_ns_id(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *desc = "Get namespace ID of a the block device.";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
unsigned int nsid;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err = 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &nsid);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (err < 0) {
|
|
|
|
fprintf(stderr, "get namespace ID: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
err = errno;
|
|
|
|
goto close_fd;
|
|
|
|
}
|
|
|
|
err = 0;
|
2025-02-16 12:17:56 +01:00
|
|
|
printf("%s: namespace-id:%d\n", dev->name, nsid);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
close_fd:
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int virtual_mgmt(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "The Virtualization Management command is supported by primary controllers "\
|
|
|
|
"that support the Virtualization Enhancements capability. This command is used for:\n"\
|
|
|
|
" 1. Modifying Flexible Resource allocation for the primary controller\n"\
|
|
|
|
" 2. Assigning Flexible Resources for secondary controllers\n"\
|
|
|
|
" 3. Setting the Online and Offline state for secondary controllers";
|
|
|
|
const char *cntlid = "Controller Identifier(CNTLID)";
|
|
|
|
const char *rt = "Resource Type(RT): [0,1]\n"\
|
|
|
|
"0h: VQ Resources\n"\
|
|
|
|
"1h: VI Resources";
|
|
|
|
const char *act = "Action(ACT): [1,7,8,9]\n"\
|
|
|
|
"1h: Primary Flexible\n"\
|
|
|
|
"7h: Secondary Offline\n"\
|
|
|
|
"8h: Secondary Assign\n"\
|
|
|
|
"9h: Secondary Online";
|
|
|
|
const char *nr = "Number of Controller Resources(NR)";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 result;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 cntlid;
|
|
|
|
__u8 rt;
|
|
|
|
__u8 act;
|
|
|
|
__u16 nr;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.cntlid = 0,
|
|
|
|
.rt = 0,
|
|
|
|
.act = 0,
|
|
|
|
.nr = 0,
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("cntlid", 'c', &cfg.cntlid, cntlid),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("rt", 'r', &cfg.rt, rt),
|
|
|
|
OPT_BYTE("act", 'a', &cfg.act, act),
|
|
|
|
OPT_SHRT("nr", 'n', &cfg.nr, nr),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_virtual_mgmt_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.act = cfg.act,
|
|
|
|
.rt = cfg.rt,
|
|
|
|
.cntlid = cfg.cntlid,
|
|
|
|
.nr = cfg.nr,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = &result,
|
|
|
|
};
|
|
|
|
err = nvme_virtual_mgmt(&args);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err) {
|
|
|
|
printf("success, Number of Controller Resources Modified "\
|
|
|
|
"(NRM):%#x\n", result);
|
|
|
|
} else if (err > 0) {
|
|
|
|
nvme_show_status(err);
|
|
|
|
} else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "virt-mgmt: %s\n", nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int primary_ctrl_caps(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *cntlid = "Controller ID";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *desc = "Send an Identify Primary Controller Capabilities "\
|
|
|
|
"command to the given device and report the information in a "\
|
|
|
|
"decoded format (default), json or binary.";
|
|
|
|
const char *human_readable = "show info in readable format";
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_primary_ctrl_cap caps;
|
2025-02-16 11:30:13 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 cntlid;
|
|
|
|
char *output_format;
|
|
|
|
bool human_readable;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.cntlid = 0,
|
|
|
|
.output_format = "normal",
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_UINT("cntlid", 'c', &cfg.cntlid, cntlid),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_primary_ctrl(dev_fd(dev), cfg.cntlid, &caps);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!err)
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_primary_ctrl_cap(&caps, flags);
|
2025-02-16 11:30:13 +01:00
|
|
|
else if (err > 0)
|
2025-02-16 11:09:01 +01:00
|
|
|
nvme_show_status(err);
|
2025-02-16 11:30:13 +01:00
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify primary controller capabilities: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int list_secondary_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Show secondary controller list associated with the primary controller "\
|
|
|
|
"of the given device.";
|
|
|
|
const char *controller = "lowest controller identifier to display";
|
|
|
|
const char *namespace_id = "optional namespace attached to controller";
|
|
|
|
const char *num_entries = "number of entries to retrieve";
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_secondary_ctrl_list *sc_list;
|
2025-02-16 11:09:01 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 cntid;
|
|
|
|
__u32 namespace_id;
|
|
|
|
__u32 num_entries;
|
|
|
|
char *output_format;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.cntid = 0,
|
|
|
|
.namespace_id = 0,
|
|
|
|
.num_entries = ARRAY_SIZE(sc_list->sc_entry),
|
|
|
|
.output_format = "normal",
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_SHRT("cntid", 'c', &cfg.cntid, controller),
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_UINT("num-entries", 'e', &cfg.num_entries, num_entries),
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
if (!cfg.num_entries) {
|
|
|
|
fprintf(stderr, "non-zero num-entries is required param\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (posix_memalign((void *)&sc_list, getpagesize(), sizeof(*sc_list))) {
|
|
|
|
fprintf(stderr, "can not allocate controller list payload\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_secondary_ctrl_list(dev_fd(dev), cfg.namespace_id,
|
|
|
|
cfg.cntid, sc_list);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_list_secondary_ctrl(sc_list, cfg.num_entries, flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "id secondary controller list: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
free(sc_list);
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_err:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int device_self_test(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Implementing the device self-test feature"\
|
|
|
|
" which provides the necessary log to determine the state of the device";
|
|
|
|
const char *namespace_id = "Indicate the namespace in which the device self-test"\
|
|
|
|
" has to be carried out";
|
|
|
|
const char * self_test_code = "This field specifies the action taken by the device self-test command : "\
|
|
|
|
"\n1h Start a short device self-test operation\n"\
|
|
|
|
"2h Start a extended device self-test operation\n"\
|
|
|
|
"eh Start a vendor specific device self-test operation\n"\
|
|
|
|
"fh abort the device self-test operation\n";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u8 stc;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = NVME_NSID_ALL,
|
|
|
|
.stc = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("self-test-code", 's', &cfg.stc, self_test_code),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_dev_self_test_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.stc = cfg.stc,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_dev_self_test(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err) {
|
2025-02-16 11:30:13 +01:00
|
|
|
if (cfg.stc == 0xf)
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("Aborting device self-test operation\n");
|
2025-02-16 11:30:13 +01:00
|
|
|
else if (cfg.stc == 0x2)
|
|
|
|
printf("Extended Device self-test started\n");
|
|
|
|
else if (cfg.stc == 0x1)
|
|
|
|
printf("Short Device self-test started\n");
|
2025-02-16 11:09:01 +01:00
|
|
|
} else if (err > 0) {
|
|
|
|
nvme_show_status(err);
|
|
|
|
} else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "Device self-test: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int self_test_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Retrieve the self-test log for the given device and given test "\
|
|
|
|
"(or optionally a namespace) in either decoded format "\
|
|
|
|
"(default) or binary.";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *dst_entries = "Indicate how many DST log entries to be retrieved, "\
|
|
|
|
"by default all the 20 entries will be retrieved";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *verbose = "Increase output verbosity";
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_self_test_log log;
|
2025-02-16 11:09:01 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 dst_entries;
|
|
|
|
char *output_format;
|
|
|
|
bool verbose;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.dst_entries = NVME_LOG_ST_MAX_RESULTS,
|
|
|
|
.output_format = "normal",
|
|
|
|
.verbose = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("dst-entries", 'e', &cfg.dst_entries, dst_entries),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.verbose)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_get_log_device_self_test(dev, &log);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_self_test_log(&log, cfg.dst_entries, 0,
|
2025-02-16 12:17:56 +01:00
|
|
|
dev->name, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "self test log: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int get_feature_id(struct nvme_dev *dev, struct feat_cfg *cfg,
|
|
|
|
void **buf, __u32 *result)
|
2025-02-16 12:15:30 +01:00
|
|
|
{
|
|
|
|
if (!cfg->data_len)
|
|
|
|
nvme_get_feature_length(cfg->feature_id, cfg->cdw11,
|
|
|
|
&cfg->data_len);
|
|
|
|
|
|
|
|
/* check for Extended Host Identifier */
|
|
|
|
if (cfg->feature_id == NVME_FEAT_FID_HOST_ID && (cfg->cdw11 & 0x1))
|
|
|
|
cfg->data_len = 16;
|
|
|
|
|
|
|
|
if (cfg->sel == 3)
|
|
|
|
cfg->data_len = 0;
|
|
|
|
|
|
|
|
if (cfg->data_len) {
|
|
|
|
if (posix_memalign(buf, getpagesize(), cfg->data_len)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memset(*buf, 0, cfg->data_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nvme_get_features_args args = {
|
|
|
|
.args_size = sizeof(args),
|
|
|
|
.fid = cfg->feature_id,
|
|
|
|
.nsid = cfg->namespace_id,
|
|
|
|
.sel = cfg->sel,
|
|
|
|
.cdw11 = cfg->cdw11,
|
|
|
|
.uuidx = cfg->uuid_index,
|
|
|
|
.data_len = cfg->data_len,
|
|
|
|
.data = *buf,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = result,
|
|
|
|
};
|
2025-02-16 12:17:56 +01:00
|
|
|
return nvme_cli_get_features(dev, &args);
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void get_feature_id_print(struct feat_cfg cfg, int err, __u32 result,
|
|
|
|
void *buf)
|
|
|
|
{
|
|
|
|
if (!err) {
|
|
|
|
if (!cfg.raw_binary || !buf) {
|
|
|
|
printf("get-feature:%#0*x (%s), %s value:%#0*x\n",
|
|
|
|
cfg.feature_id ? 4 : 2, cfg.feature_id,
|
|
|
|
nvme_feature_to_string(cfg.feature_id),
|
|
|
|
nvme_select_to_string(cfg.sel), result ? 10 : 8,
|
|
|
|
result);
|
|
|
|
if (cfg.sel == 3)
|
|
|
|
nvme_show_select_result(result);
|
|
|
|
else if (cfg.human_readable)
|
|
|
|
nvme_feature_show_fields(cfg.feature_id, result,
|
|
|
|
buf);
|
|
|
|
else if (buf)
|
|
|
|
d(buf, cfg.data_len, 16, 1);
|
|
|
|
} else if (buf) {
|
|
|
|
d_raw(buf, cfg.data_len);
|
|
|
|
}
|
|
|
|
} else if (err > 0) {
|
2025-02-16 12:17:56 +01:00
|
|
|
if (!nvme_status_equals(err, NVME_STATUS_TYPE_NVME,
|
|
|
|
NVME_SC_INVALID_FIELD))
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_status(err);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "get-feature: %s\n", nvme_strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int get_feature_id_changed(struct nvme_dev *dev, struct feat_cfg cfg,
|
|
|
|
bool changed)
|
2025-02-16 12:15:30 +01:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
int err_def = 0;
|
|
|
|
__u32 result;
|
|
|
|
__u32 result_def;
|
|
|
|
void *buf = NULL;
|
|
|
|
void *buf_def = NULL;
|
|
|
|
|
|
|
|
if (changed)
|
|
|
|
cfg.sel = 0;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = get_feature_id(dev, &cfg, &buf, &result);
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (!err && changed) {
|
|
|
|
cfg.sel = 1;
|
2025-02-16 12:17:56 +01:00
|
|
|
err_def = get_feature_id(dev, &cfg, &buf_def, &result_def);
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (changed)
|
|
|
|
cfg.sel = 8;
|
|
|
|
|
|
|
|
if (err || !changed || err_def || result != result_def ||
|
|
|
|
(buf && buf_def && !strcmp(buf, buf_def)))
|
|
|
|
get_feature_id_print(cfg, err, result, buf);
|
|
|
|
|
|
|
|
free(buf);
|
|
|
|
free(buf_def);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int get_feature_ids(struct nvme_dev *dev, struct feat_cfg cfg)
|
2025-02-16 12:15:30 +01:00
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
int i;
|
|
|
|
int feat_max = 0x100;
|
|
|
|
int feat_num = 0;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
if (cfg.sel == 8)
|
|
|
|
changed = true;
|
|
|
|
|
|
|
|
if (cfg.feature_id)
|
|
|
|
feat_max = cfg.feature_id + 1;
|
|
|
|
|
|
|
|
for (i = cfg.feature_id; i < feat_max; i++, feat_num++) {
|
|
|
|
cfg.feature_id = i;
|
2025-02-16 12:17:56 +01:00
|
|
|
err = get_feature_id_changed(dev, cfg, changed);
|
|
|
|
if (err && !nvme_status_equals(err, NVME_STATUS_TYPE_NVME,
|
|
|
|
NVME_SC_INVALID_FIELD))
|
2025-02-16 12:15:30 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
if (feat_num == 1 && nvme_status_equals(err, NVME_STATUS_TYPE_NVME,
|
|
|
|
NVME_SC_INVALID_FIELD))
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_status(err);
|
|
|
|
|
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static int get_feature(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
const char *desc = "Read operating parameters of the "\
|
|
|
|
"specified controller. Operating parameters are grouped "\
|
|
|
|
"and identified by Feature Identifiers; each Feature "\
|
|
|
|
"Identifier contains one or more attributes that may affect "\
|
2025-02-16 12:16:06 +01:00
|
|
|
"behavior of the feature. Each Feature has three possible "\
|
2025-02-16 11:09:01 +01:00
|
|
|
"settings: default, saveable, and current. If a Feature is "\
|
|
|
|
"saveable, it may be modified by set-feature. Default values "\
|
|
|
|
"are vendor-specific and not changeable. Use set-feature to "\
|
|
|
|
"change saveable Features.";
|
|
|
|
const char *raw = "show feature in binary format";
|
|
|
|
const char *feature_id = "feature identifier";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
const char *sel = "[0-3,8]: current/default/saved/supported/changed";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *data_len = "buffer len if data is returned through host memory buffer";
|
|
|
|
const char *cdw11 = "dword 11 for interrupt vector config";
|
|
|
|
const char *human_readable = "show feature in readable format";
|
2025-02-16 12:10:50 +01:00
|
|
|
const char *uuid_index = "specify uuid index";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct feat_cfg cfg = {
|
|
|
|
.feature_id = 0,
|
|
|
|
.namespace_id = 0,
|
|
|
|
.sel = 0,
|
|
|
|
.data_len = 0,
|
|
|
|
.raw_binary = false,
|
|
|
|
.cdw11 = 0,
|
|
|
|
.uuid_index = 0,
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("feature-id", 'f', &cfg.feature_id, feature_id),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("sel", 's', &cfg.sel, sel),
|
|
|
|
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
|
|
|
if (errno != ENOTTY) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
cfg.namespace_id = NVME_NSID_ALL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.sel > 8) {
|
2025-02-16 11:09:01 +01:00
|
|
|
fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.uuid_index > 128) {
|
|
|
|
fprintf(stderr, "invalid uuid index param: %u\n", cfg.uuid_index);
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = get_feature_ids(dev, cfg);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fw_download(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Copy all or part of a firmware image to "\
|
|
|
|
"a controller for future update. Optionally, specify how "\
|
|
|
|
"many KiB of the firmware to transfer at once. The offset will "\
|
|
|
|
"start at 0 and automatically adjust based on xfer size "\
|
|
|
|
"unless fw is split across multiple files. May be submitted "\
|
|
|
|
"while outstanding commands exist on the Admin and IO "\
|
|
|
|
"Submission Queues. Activate downloaded firmware with "\
|
|
|
|
"fw-activate, and then reset the device to apply the downloaded firmware.";
|
|
|
|
const char *fw = "firmware file (required)";
|
|
|
|
const char *xfer = "transfer chunksize limit";
|
|
|
|
const char *offset = "starting dword offset, default 0";
|
|
|
|
unsigned int fw_size;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
void *fw_buf, *buf;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err, fw_fd = -1;
|
|
|
|
struct stat sb;
|
2025-02-16 11:09:01 +01:00
|
|
|
bool huge;
|
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *fw;
|
|
|
|
__u32 xfer;
|
|
|
|
__u32 offset;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.fw = "",
|
|
|
|
.xfer = 4096,
|
|
|
|
.offset = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FILE("fw", 'f', &cfg.fw, fw),
|
|
|
|
OPT_UINT("xfer", 'x', &cfg.xfer, xfer),
|
|
|
|
OPT_UINT("offset", 'o', &cfg.offset, offset),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
fw_fd = open(cfg.fw, O_RDONLY);
|
|
|
|
cfg.offset <<= 2;
|
|
|
|
if (fw_fd < 0) {
|
|
|
|
fprintf(stderr, "Failed to open firmware file %s: %s\n",
|
|
|
|
cfg.fw, strerror(errno));
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
err = fstat(fw_fd, &sb);
|
|
|
|
if (err < 0) {
|
|
|
|
perror("fstat");
|
|
|
|
goto close_fw_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
fw_size = sb.st_size;
|
2025-02-16 11:30:13 +01:00
|
|
|
if ((fw_size & 0x3) || (fw_size == 0)) {
|
2025-02-16 11:09:01 +01:00
|
|
|
fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_fw_fd;
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
if (cfg.xfer == 0 || cfg.xfer % 4096)
|
|
|
|
cfg.xfer = 4096;
|
|
|
|
|
|
|
|
if (cfg.xfer < HUGE_MIN)
|
|
|
|
fw_buf = __nvme_alloc(fw_size, &huge);
|
|
|
|
else
|
|
|
|
fw_buf = nvme_alloc(fw_size, &huge);
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!fw_buf) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_fw_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = fw_buf;
|
|
|
|
if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size))) {
|
|
|
|
err = -errno;
|
|
|
|
fprintf(stderr, "read :%s :%s\n", cfg.fw, strerror(errno));
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fw_size > 0) {
|
|
|
|
cfg.xfer = min(cfg.xfer, fw_size);
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_fw_download_args args = {
|
|
|
|
.args_size = sizeof(args),
|
|
|
|
.offset = cfg.offset,
|
|
|
|
.data_len = cfg.xfer,
|
|
|
|
.data = fw_buf,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_fw_download(dev, &args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "fw-download: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
|
|
|
} else if (err != 0) {
|
|
|
|
nvme_show_status(err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fw_buf += cfg.xfer;
|
|
|
|
fw_size -= cfg.xfer;
|
|
|
|
cfg.offset += cfg.xfer;
|
|
|
|
}
|
|
|
|
if (!err)
|
|
|
|
printf("Firmware download success\n");
|
|
|
|
|
|
|
|
free:
|
|
|
|
nvme_free(buf, huge);
|
|
|
|
close_fw_fd:
|
|
|
|
close(fw_fd);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
static char *nvme_fw_status_reset_type(__u16 status)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 11:30:13 +01:00
|
|
|
switch (status & 0x7ff) {
|
2025-02-16 11:09:01 +01:00
|
|
|
case NVME_SC_FW_NEEDS_CONV_RESET: return "conventional";
|
|
|
|
case NVME_SC_FW_NEEDS_SUBSYS_RESET: return "subsystem";
|
|
|
|
case NVME_SC_FW_NEEDS_RESET: return "any controller";
|
|
|
|
default: return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fw_commit(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Verify downloaded firmware image and "\
|
|
|
|
"commit to specific firmware slot. Device is not automatically "\
|
|
|
|
"reset following firmware activation. A reset may be issued "\
|
|
|
|
"with an 'echo 1 > /sys/class/nvme/nvmeX/reset_controller'. "\
|
|
|
|
"Ensure nvmeX is the device you just activated before reset.";
|
|
|
|
const char *slot = "[0-7]: firmware slot for commit action";
|
|
|
|
const char *action = "[0-7]: commit action";
|
|
|
|
const char *bpid = "[0,1]: boot partition identifier, if applicable (default: 0)";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
__u32 result;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 slot;
|
|
|
|
__u8 action;
|
|
|
|
__u8 bpid;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.slot = 0,
|
|
|
|
.action = 0,
|
|
|
|
.bpid = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_BYTE("slot", 's', &cfg.slot, slot),
|
|
|
|
OPT_BYTE("action", 'a', &cfg.action, action),
|
|
|
|
OPT_BYTE("bpid", 'b', &cfg.bpid, bpid),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.slot > 7) {
|
|
|
|
fprintf(stderr, "invalid slot:%d\n", cfg.slot);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.action > 7 || cfg.action == 4 || cfg.action == 5) {
|
|
|
|
fprintf(stderr, "invalid action:%d\n", cfg.action);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.bpid > 1) {
|
|
|
|
fprintf(stderr, "invalid boot partition id:%d\n", cfg.bpid);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_fw_commit_args args = {
|
|
|
|
.args_size = sizeof(args),
|
|
|
|
.slot = cfg.slot,
|
|
|
|
.action = cfg.action,
|
|
|
|
.bpid = cfg.bpid,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = &result,
|
|
|
|
};
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_fw_commit(dev, &args);
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "fw-commit: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
else if (err != 0) {
|
|
|
|
__u32 val = nvme_status_get_value(err);
|
|
|
|
int type = nvme_status_get_type(err);
|
|
|
|
|
|
|
|
if (type == NVME_STATUS_TYPE_NVME) {
|
|
|
|
switch (val & 0x7ff) {
|
|
|
|
case NVME_SC_FW_NEEDS_CONV_RESET:
|
|
|
|
case NVME_SC_FW_NEEDS_SUBSYS_RESET:
|
|
|
|
case NVME_SC_FW_NEEDS_RESET:
|
|
|
|
printf("Success activating firmware action:%d slot:%d",
|
|
|
|
cfg.action, cfg.slot);
|
|
|
|
if (cfg.action == 6 || cfg.action == 7)
|
|
|
|
printf(" bpid:%d", cfg.bpid);
|
|
|
|
printf(", but firmware requires %s reset\n",
|
|
|
|
nvme_fw_status_reset_type(val));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nvme_show_status(err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2025-02-16 11:09:01 +01:00
|
|
|
nvme_show_status(err);
|
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
} else {
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("Success committing firmware action:%d slot:%d",
|
|
|
|
cfg.action, cfg.slot);
|
|
|
|
if (cfg.action == 6 || cfg.action == 7)
|
|
|
|
printf(" bpid:%d", cfg.bpid);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
if (err >= 0) {
|
|
|
|
printf("Multiple Update Detected (MUD) Value: %u\n", result);
|
|
|
|
if (result & 0x1)
|
|
|
|
printf("Detected an overlapping firmware/boot partition image update command "\
|
|
|
|
"sequence due to processing a command from a Management Endpoint");
|
|
|
|
if ((result >> 1) & 0x1)
|
|
|
|
printf("Detected an overlapping firmware/boot partition image update command "\
|
|
|
|
"sequence due to processing a command from an Admin SQ on a controller");
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int subsystem_reset(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Resets the NVMe subsystem\n";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_subsystem_reset(dev_fd(dev));
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
|
|
|
if (errno == ENOTTY)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Subsystem-reset: NVM Subsystem Reset not supported.\n");
|
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "Subsystem-reset: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int reset(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Resets the NVMe controller\n";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_ctrl_reset(dev_fd(dev));
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "Reset: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ns_rescan(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Rescans the NVMe namespaces\n";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_ns_rescan(dev_fd(dev));
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "Namespace Rescan");
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sanitize(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send a sanitize command.";
|
|
|
|
const char *no_dealloc_desc = "No deallocate after sanitize.";
|
|
|
|
const char *oipbp_desc = "Overwrite invert pattern between passes.";
|
|
|
|
const char *owpass_desc = "Overwrite pass count.";
|
|
|
|
const char *ause_desc = "Allow unrestricted sanitize exit.";
|
|
|
|
const char *sanact_desc = "Sanitize action.";
|
|
|
|
const char *ovrpat_desc = "Overwrite pattern.";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
bool no_dealloc;
|
|
|
|
bool oipbp;
|
|
|
|
__u8 owpass;
|
|
|
|
bool ause;
|
|
|
|
__u8 sanact;
|
|
|
|
__u32 ovrpat;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.no_dealloc = false,
|
|
|
|
.oipbp = false,
|
|
|
|
.owpass = 0,
|
|
|
|
.ause = false,
|
|
|
|
.sanact = 0,
|
|
|
|
.ovrpat = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FLAG("no-dealloc", 'd', &cfg.no_dealloc, no_dealloc_desc),
|
|
|
|
OPT_FLAG("oipbp", 'i', &cfg.oipbp, oipbp_desc),
|
|
|
|
OPT_BYTE("owpass", 'n', &cfg.owpass, owpass_desc),
|
|
|
|
OPT_FLAG("ause", 'u', &cfg.ause, ause_desc),
|
|
|
|
OPT_BYTE("sanact", 'a', &cfg.sanact, sanact_desc),
|
|
|
|
OPT_UINT("ovrpat", 'p', &cfg.ovrpat, ovrpat_desc),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
switch (cfg.sanact) {
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_SANITIZE_SANACT_EXIT_FAILURE:
|
|
|
|
case NVME_SANITIZE_SANACT_START_BLOCK_ERASE:
|
|
|
|
case NVME_SANITIZE_SANACT_START_OVERWRITE:
|
|
|
|
case NVME_SANITIZE_SANACT_START_CRYPTO_ERASE:
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Invalid Sanitize Action\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.sanact == NVME_SANITIZE_SANACT_EXIT_FAILURE) {
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.ause || cfg.no_dealloc) {
|
|
|
|
fprintf(stderr, "SANACT is Exit Failure Mode\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.sanact == NVME_SANITIZE_SANACT_START_OVERWRITE) {
|
|
|
|
if (cfg.owpass > 16) {
|
|
|
|
fprintf(stderr, "OWPASS out of range [0-16]\n");
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (cfg.owpass || cfg.oipbp || cfg.ovrpat) {
|
|
|
|
fprintf(stderr, "SANACT is not Overwrite\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_sanitize_nvm_args args = {
|
|
|
|
.args_size = sizeof(args),
|
|
|
|
.sanact = cfg.sanact,
|
|
|
|
.ause = cfg.ause,
|
|
|
|
.owpass = cfg.owpass,
|
|
|
|
.oipbp = cfg.oipbp,
|
|
|
|
.nodas = cfg.no_dealloc,
|
|
|
|
.ovrpat = cfg.ovrpat,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_sanitize_nvm(dev, &args);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (err < 0)
|
|
|
|
fprintf(stderr, "sanitize: %s\n", nvme_strerror(errno));
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
ret:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvme_get_properties(int fd, void **pbar)
|
|
|
|
{
|
|
|
|
int offset, err, size = getpagesize();
|
|
|
|
__u64 value;
|
|
|
|
|
|
|
|
*pbar = malloc(size);
|
|
|
|
if (!*pbar) {
|
|
|
|
fprintf(stderr, "malloc: %s\n", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(*pbar, 0xff, size);
|
|
|
|
for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ;) {
|
|
|
|
struct nvme_get_property_args args = {
|
|
|
|
.args_size = sizeof(args),
|
|
|
|
.fd = fd,
|
|
|
|
.offset = offset,
|
|
|
|
.value = &value,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
};
|
2025-02-16 12:17:56 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
err = nvme_get_property(&args);
|
2025-02-16 12:17:56 +01:00
|
|
|
if (nvme_status_equals(err, NVME_STATUS_TYPE_NVME,
|
|
|
|
NVME_SC_INVALID_FIELD)) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = 0;
|
|
|
|
value = -1;
|
|
|
|
} else if (err) {
|
|
|
|
free(*pbar);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (nvme_is_64bit_reg(offset)) {
|
|
|
|
*(uint64_t *)(*pbar + offset) = value;
|
|
|
|
offset += 8;
|
|
|
|
} else {
|
|
|
|
*(uint32_t *)(*pbar + offset) = value;
|
|
|
|
offset += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev)
|
2025-02-16 12:15:30 +01:00
|
|
|
{
|
|
|
|
nvme_ctrl_t c = NULL;
|
|
|
|
nvme_ns_t n = NULL;
|
|
|
|
|
|
|
|
char path[512];
|
|
|
|
void *membase;
|
|
|
|
int fd;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
c = nvme_scan_ctrl(r, dev->name);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (c) {
|
|
|
|
snprintf(path, sizeof(path), "%s/device/resource0",
|
|
|
|
nvme_ctrl_get_sysfs_dir(c));
|
|
|
|
nvme_free_ctrl(c);
|
|
|
|
} else {
|
2025-02-16 12:17:56 +01:00
|
|
|
n = nvme_scan_namespace(dev->name);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!n) {
|
2025-02-16 12:17:56 +01:00
|
|
|
fprintf(stderr, "Unable to find %s\n", dev->name);
|
2025-02-16 12:15:30 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
snprintf(path, sizeof(path), "%s/device/device/resource0",
|
|
|
|
nvme_ns_get_sysfs_dir(n));
|
|
|
|
nvme_free_ns(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "%s did not find a pci resource, open failed %s\n",
|
2025-02-16 12:17:56 +01:00
|
|
|
dev->name, strerror(errno));
|
2025-02-16 12:15:30 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
membase = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0);
|
|
|
|
if (membase == MAP_FAILED) {
|
2025-02-16 12:17:56 +01:00
|
|
|
fprintf(stderr, "%s failed to map. ", dev->name);
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "Did your kernel enable CONFIG_IO_STRICT_DEVMEM?\n");
|
|
|
|
membase = NULL;
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
close(fd);
|
2025-02-16 12:15:30 +01:00
|
|
|
return membase;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int show_registers(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Reads and shows the defined NVMe controller registers "\
|
|
|
|
"in binary or human-readable format";
|
|
|
|
const char *human_readable = "show info in readable format in case of "\
|
|
|
|
"output_format == normal";
|
|
|
|
|
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
bool fabrics = true;
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_root_t r;
|
2025-02-16 11:09:01 +01:00
|
|
|
void *bar;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *output_format;
|
|
|
|
bool human_readable;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.output_format = "normal",
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
r = nvme_scan(NULL);
|
2025-02-16 11:09:01 +01:00
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_properties(dev_fd(dev), &bar);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err) {
|
2025-02-16 12:17:56 +01:00
|
|
|
bar = mmap_registers(r, dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
fabrics = false;
|
|
|
|
if (bar)
|
|
|
|
err = 0;
|
|
|
|
}
|
|
|
|
if (!bar)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
nvme_show_ctrl_registers(bar, fabrics, flags);
|
|
|
|
if (fabrics)
|
|
|
|
free(bar);
|
|
|
|
else
|
|
|
|
munmap(bar, getpagesize());
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_free_tree(r);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_property(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Reads and shows the defined NVMe controller property "\
|
|
|
|
"for NVMe over Fabric. Property offset must be one of:\n"
|
|
|
|
"CAP=0x0, VS=0x8, CC=0x14, CSTS=0x1c, NSSR=0x20";
|
|
|
|
const char *offset = "offset of the requested property";
|
|
|
|
const char *human_readable = "show property in readable format";
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
__u64 value;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
int offset;
|
|
|
|
bool human_readable;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.offset = -1,
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("offset", 'o', &cfg.offset, offset),
|
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.offset == -1) {
|
|
|
|
fprintf(stderr, "offset required param\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_get_property_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.offset = cfg.offset,
|
|
|
|
.value = &value,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
};
|
|
|
|
err = nvme_get_property(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-property: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
} else if (!err) {
|
|
|
|
nvme_show_single_property(cfg.offset, value, cfg.human_readable);
|
|
|
|
} else if (err > 0) {
|
|
|
|
nvme_show_status(err);
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int set_property(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Writes and shows the defined NVMe controller property "\
|
2025-02-16 12:16:06 +01:00
|
|
|
"for NVMe over Fabric";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *offset = "the offset of the property";
|
|
|
|
const char *value = "the value of the property to be set";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
int offset;
|
|
|
|
int value;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.offset = -1,
|
|
|
|
.value = -1,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("offset", 'o', &cfg.offset, offset),
|
|
|
|
OPT_UINT("value", 'v', &cfg.value, value),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.offset == -1) {
|
|
|
|
fprintf(stderr, "offset required param\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.value == -1) {
|
|
|
|
fprintf(stderr, "value required param\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_set_property_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.offset = cfg.offset,
|
|
|
|
.value = cfg.value,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_set_property(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "set-property: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
} else if (!err) {
|
|
|
|
printf("set-property: %02x (%s), value: %#08x\n", cfg.offset,
|
|
|
|
nvme_register_to_string(cfg.offset), cfg.value);
|
|
|
|
} else if (err > 0) {
|
|
|
|
nvme_show_status(err);
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int format(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Re-format a specified namespace on the "\
|
|
|
|
"given device. Can erase all data in namespace (user "\
|
|
|
|
"data erase) or delete data encryption key if specified. "\
|
|
|
|
"Can also be used to change LBAF to change the namespaces reported physical block format.";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *lbaf = "LBA format to apply (required)";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *ses = "[0-2]: secure erase";
|
|
|
|
const char *pil = "[0-1]: protection info location last/first 8 bytes of metadata";
|
|
|
|
const char *pi = "[0-3]: protection info off/Type 1/Type 2/Type 3";
|
|
|
|
const char *ms = "[0-1]: extended format off/on";
|
|
|
|
const char *reset = "Automatically reset the controller after successful format";
|
|
|
|
const char *timeout = "timeout value, in milliseconds";
|
|
|
|
const char *bs = "target block size";
|
|
|
|
const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command";
|
|
|
|
struct nvme_id_ns ns;
|
|
|
|
struct nvme_id_ctrl ctrl;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
__u8 prev_lbaf = 0;
|
2025-02-16 12:17:56 +01:00
|
|
|
int block_size;
|
|
|
|
int err, i;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u32 timeout;
|
|
|
|
__u8 lbaf;
|
|
|
|
__u8 ses;
|
|
|
|
__u8 pi;
|
|
|
|
__u8 pil;
|
|
|
|
__u8 ms;
|
|
|
|
bool reset;
|
|
|
|
bool force;
|
|
|
|
__u64 bs;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.timeout = 600000,
|
|
|
|
.lbaf = 0xff,
|
|
|
|
.ses = 0,
|
|
|
|
.pi = 0,
|
|
|
|
.pil = 0,
|
|
|
|
.ms = 0,
|
|
|
|
.reset = false,
|
|
|
|
.force = false,
|
|
|
|
.bs = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_UINT("timeout", 't', &cfg.timeout, timeout),
|
|
|
|
OPT_BYTE("lbaf", 'l', &cfg.lbaf, lbaf),
|
|
|
|
OPT_BYTE("ses", 's', &cfg.ses, ses),
|
|
|
|
OPT_BYTE("pi", 'i', &cfg.pi, pi),
|
|
|
|
OPT_BYTE("pil", 'p', &cfg.pil, pil),
|
|
|
|
OPT_BYTE("ms", 'm', &cfg.ms, ms),
|
|
|
|
OPT_FLAG("reset", 'r', &cfg.reset, reset),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("force", 0, &cfg.force, force),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_SUFFIX("block-size", 'b', &cfg.bs, bs),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = open_exclusive(&dev, argc, argv, cfg.force);
|
|
|
|
if (err) {
|
2025-02-16 12:15:30 +01:00
|
|
|
if (errno == EBUSY) {
|
|
|
|
fprintf(stderr, "Failed to open %s.\n",
|
|
|
|
basename(argv[optind]));
|
|
|
|
fprintf(stderr,
|
|
|
|
"Namespace is currently busy.\n");
|
|
|
|
if (!cfg.force)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Use the force [--force] option to ignore that.\n");
|
|
|
|
} else {
|
|
|
|
argconfig_print_help(desc, opts);
|
|
|
|
}
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.lbaf != 0xff && cfg.bs !=0) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Invalid specification of both LBAF and Block Size, please specify only one\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.bs) {
|
|
|
|
if ((cfg.bs & (~cfg.bs + 1)) != cfg.bs) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Invalid value for block size (%"PRIu64"), must be a power of two\n",
|
|
|
|
(uint64_t) cfg.bs);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ctrl(dev, &ctrl);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify-ctrl: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((ctrl.fna & 1) == 1) {
|
|
|
|
/*
|
|
|
|
* FNA bit 0 set to 1: all namespaces ... shall be configured with the same
|
|
|
|
* attributes and a format (excluding secure erase) of any namespace results in a
|
|
|
|
* format of all namespaces.
|
|
|
|
*/
|
|
|
|
cfg.namespace_id = NVME_NSID_ALL;
|
2025-02-16 11:30:13 +01:00
|
|
|
} else if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.namespace_id == 0) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Invalid namespace ID, "
|
|
|
|
"specify a namespace to format or use '-n 0xffffffff' "
|
|
|
|
"to format all namespaces on this controller.\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.namespace_id != NVME_NSID_ALL) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ns(dev, cfg.namespace_id, &ns);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err) {
|
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify-namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else {
|
|
|
|
fprintf(stderr, "identify failed\n");
|
|
|
|
nvme_show_status(err);
|
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &prev_lbaf);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
if (cfg.bs) {
|
2025-02-16 12:16:06 +01:00
|
|
|
for (i = 0; i < ns.nlbaf; ++i) {
|
2025-02-16 11:09:01 +01:00
|
|
|
if ((1ULL << ns.lbaf[i].ds) == cfg.bs &&
|
|
|
|
ns.lbaf[i].ms == 0) {
|
|
|
|
cfg.lbaf = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cfg.lbaf == 0xff) {
|
|
|
|
fprintf(stderr,
|
2025-02-16 11:30:13 +01:00
|
|
|
"LBAF corresponding to given block size %"PRIu64" not found\n",
|
|
|
|
(uint64_t)cfg.bs);
|
2025-02-16 11:09:01 +01:00
|
|
|
fprintf(stderr,
|
|
|
|
"Please correct block size, or specify LBAF directly\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
} else if (cfg.lbaf == 0xff)
|
|
|
|
cfg.lbaf = prev_lbaf;
|
2025-02-16 11:30:13 +01:00
|
|
|
} else {
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.lbaf == 0xff) cfg.lbaf = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ses & pi checks set to 7 for forward-compatibility */
|
|
|
|
if (cfg.ses > 7) {
|
|
|
|
fprintf(stderr, "invalid secure erase settings:%d\n", cfg.ses);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:16:06 +01:00
|
|
|
if (cfg.lbaf > 63) {
|
2025-02-16 11:09:01 +01:00
|
|
|
fprintf(stderr, "invalid lbaf:%d\n", cfg.lbaf);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.pi > 7) {
|
|
|
|
fprintf(stderr, "invalid pi:%d\n", cfg.pi);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.pil > 1) {
|
|
|
|
fprintf(stderr, "invalid pil:%d\n", cfg.pil);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.ms > 1) {
|
|
|
|
fprintf(stderr, "invalid ms:%d\n", cfg.ms);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.force) {
|
|
|
|
fprintf(stderr, "You are about to format %s, namespace %#x%s.\n",
|
2025-02-16 12:17:56 +01:00
|
|
|
dev->name, cfg.namespace_id,
|
2025-02-16 11:09:01 +01:00
|
|
|
cfg.namespace_id == NVME_NSID_ALL ? "(ALL namespaces)" : "");
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_show_relatives(dev->name);
|
2025-02-16 11:09:01 +01:00
|
|
|
fprintf(stderr, "WARNING: Format may irrevocably delete this device's data.\n"
|
|
|
|
"You have 10 seconds to press Ctrl-C to cancel this operation.\n\n"
|
2025-02-16 12:15:30 +01:00
|
|
|
"Use the force [--force] option to suppress this warning.\n");
|
2025-02-16 11:09:01 +01:00
|
|
|
sleep(10);
|
|
|
|
fprintf(stderr, "Sending format operation ... \n");
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_format_nvm_args args = {
|
|
|
|
.args_size = sizeof(args),
|
|
|
|
.nsid = cfg.namespace_id,
|
2025-02-16 12:16:06 +01:00
|
|
|
.lbafu = (cfg.lbaf & NVME_NS_FLBAS_HIGHER_MASK) >> 4,
|
|
|
|
.lbaf = cfg.lbaf & NVME_NS_FLBAS_LOWER_MASK,
|
2025-02-16 12:15:30 +01:00
|
|
|
.mset = cfg.ms,
|
|
|
|
.pi = cfg.pi,
|
|
|
|
.pil = cfg.pil,
|
|
|
|
.ses = cfg.ses,
|
|
|
|
.timeout = cfg.timeout,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_format_nvm(dev, &args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "format: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else {
|
|
|
|
printf("Success formatting namespace:%x\n", cfg.namespace_id);
|
2025-02-16 12:17:56 +01:00
|
|
|
if (dev->type == NVME_DEV_DIRECT && cfg.lbaf != prev_lbaf){
|
|
|
|
if (is_chardev(dev)) {
|
|
|
|
if (ioctl(dev_fd(dev), NVME_IOCTL_RESCAN) < 0) {
|
2025-02-16 11:30:13 +01:00
|
|
|
fprintf(stderr, "failed to rescan namespaces\n");
|
|
|
|
err = -errno;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
block_size = 1 << ns.lbaf[cfg.lbaf].ds;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If block size has been changed by the format
|
|
|
|
* command up there, we should notify it to
|
|
|
|
* kernel blkdev to update its own block size
|
|
|
|
* to the given one because blkdev will not
|
|
|
|
* update by itself without re-opening fd.
|
|
|
|
*/
|
2025-02-16 12:17:56 +01:00
|
|
|
if (ioctl(dev_fd(dev), BLKBSZSET, &block_size) < 0) {
|
2025-02-16 11:30:13 +01:00
|
|
|
fprintf(stderr, "failed to set block size to %d\n",
|
|
|
|
block_size);
|
|
|
|
err = -errno;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
if (ioctl(dev_fd(dev), BLKRRPART) < 0) {
|
2025-02-16 11:30:13 +01:00
|
|
|
fprintf(stderr, "failed to re-read partition table\n");
|
|
|
|
err = -errno;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
if (dev->type == NVME_DEV_DIRECT && cfg.reset && is_chardev(dev))
|
|
|
|
nvme_ctrl_reset(dev_fd(dev));
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
#define STRTOUL_AUTO_BASE (0)
|
|
|
|
#define NVME_FEAT_TIMESTAMP_DATA_SIZE (6)
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int set_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Modify the saveable or changeable "\
|
|
|
|
"current operating parameters of the controller. Operating "\
|
|
|
|
"parameters are grouped and identified by Feature "\
|
|
|
|
"Identifiers. Feature settings can be applied to the entire "\
|
|
|
|
"controller and all associated namespaces, or to only a few "\
|
|
|
|
"namespace(s) associated with the controller. Default values "\
|
|
|
|
"for each Feature are vendor-specific and may not be modified."\
|
|
|
|
"Use get-feature to determine which Features are supported by "\
|
|
|
|
"the controller and are saveable/changeable.";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *feature_id = "feature identifier (required)";
|
|
|
|
const char *data_len = "buffer length if data required";
|
|
|
|
const char *data = "optional file for feature data (default stdin)";
|
|
|
|
const char *value = "new value of feature (required)";
|
|
|
|
const char *cdw12 = "feature cdw12, if used";
|
|
|
|
const char *save = "specifies that the controller shall save the attribute";
|
2025-02-16 12:10:50 +01:00
|
|
|
const char *uuid_index = "specify uuid index";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:15:30 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
__u32 result;
|
|
|
|
void *buf = NULL;
|
2025-02-16 12:17:56 +01:00
|
|
|
int ffd = STDIN_FILENO;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u8 feature_id;
|
|
|
|
__u64 value;
|
|
|
|
__u32 cdw12;
|
|
|
|
__u8 uuid_index;
|
|
|
|
__u32 data_len;
|
|
|
|
char *file;
|
|
|
|
bool save;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.feature_id = 0,
|
|
|
|
.value = 0,
|
|
|
|
.uuid_index = 0,
|
|
|
|
.data_len = 0,
|
|
|
|
.file = "",
|
|
|
|
.save = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("feature-id", 'f', &cfg.feature_id, feature_id),
|
|
|
|
OPT_SUFFIX("value", 'v', &cfg.value, value),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_UINT("cdw12", 'c', &cfg.cdw12, cdw12),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
|
|
|
|
OPT_FILE("data", 'd', &cfg.file, data),
|
|
|
|
OPT_FLAG("save", 's', &cfg.save, save),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
|
|
|
if (errno != ENOTTY) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cfg.namespace_id = NVME_NSID_ALL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!cfg.feature_id) {
|
|
|
|
fprintf(stderr, "feature-id required param\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
if (cfg.uuid_index > 128) {
|
|
|
|
fprintf(stderr, "invalid uuid index param: %u\n", cfg.uuid_index);
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.data_len)
|
2025-02-16 12:17:56 +01:00
|
|
|
nvme_cli_get_feature_length2(cfg.feature_id, cfg.value,
|
|
|
|
NVME_DATA_TFR_HOST_TO_CTRL,
|
|
|
|
&cfg.data_len);
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.data_len) {
|
|
|
|
if (posix_memalign(&buf, getpagesize(), cfg.data_len)) {
|
|
|
|
fprintf(stderr, "can not allocate feature payload\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
memset(buf, 0, cfg.data_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf) {
|
2025-02-16 12:10:50 +01:00
|
|
|
/*
|
|
|
|
* Use the '-v' value for the timestamp feature if provided as
|
|
|
|
* a convenience since it can often fit in 4-bytes. The user
|
|
|
|
* should use the buffer method if the value exceeds this
|
|
|
|
* length.
|
|
|
|
*/
|
2025-02-16 12:15:30 +01:00
|
|
|
if (NVME_FEAT_FID_TIMESTAMP == cfg.feature_id && cfg.value) {
|
2025-02-16 12:10:50 +01:00
|
|
|
memcpy(buf, &cfg.value, NVME_FEAT_TIMESTAMP_DATA_SIZE);
|
|
|
|
} else {
|
|
|
|
if (strlen(cfg.file)) {
|
|
|
|
ffd = open(cfg.file, O_RDONLY);
|
|
|
|
if (ffd <= 0) {
|
|
|
|
fprintf(stderr, "Failed to open file %s: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
cfg.file, strerror(errno));
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:10:50 +01:00
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = read(ffd, (void *)buf, cfg.data_len);
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
fprintf(stderr, "failed to read data buffer from input"
|
|
|
|
" file: %s\n", strerror(errno));
|
|
|
|
goto close_ffd;
|
|
|
|
}
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_set_features_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.fid = cfg.feature_id,
|
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.cdw11 = cfg.value,
|
|
|
|
.cdw12 = cfg.cdw12,
|
|
|
|
.save = cfg.save,
|
|
|
|
.uuidx = cfg.uuid_index,
|
|
|
|
.cdw15 = 0,
|
|
|
|
.data_len = cfg.data_len,
|
|
|
|
.data = buf,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = &result,
|
|
|
|
};
|
|
|
|
err = nvme_set_features(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "set-feature: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
} else if (!err) {
|
2025-02-16 12:10:50 +01:00
|
|
|
printf("set-feature:%#0*x (%s), value:%#0*"PRIx64", cdw12:%#0*x, save:%#x\n",
|
|
|
|
cfg.feature_id ? 4 : 2, cfg.feature_id,
|
|
|
|
nvme_feature_to_string(cfg.feature_id),
|
|
|
|
cfg.value ? 10 : 8, (uint64_t)cfg.value,
|
|
|
|
cfg.cdw12 ? 10 : 8, cfg.cdw12, cfg.save);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.feature_id == NVME_FEAT_FID_LBA_STS_INTERVAL) {
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_show_lba_status_info(result);
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
if (buf) {
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.feature_id == NVME_FEAT_FID_LBA_RANGE)
|
2025-02-16 11:09:01 +01:00
|
|
|
nvme_show_lba_range((struct nvme_lba_range_type *)buf,
|
|
|
|
result);
|
|
|
|
else
|
|
|
|
d(buf, cfg.data_len, 16, 1);
|
|
|
|
}
|
|
|
|
} else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
|
|
|
|
close_ffd:
|
2025-02-16 12:16:06 +01:00
|
|
|
if (ffd != STDIN_FILENO)
|
|
|
|
close(ffd);
|
2025-02-16 11:09:01 +01:00
|
|
|
free:
|
2025-02-16 11:30:13 +01:00
|
|
|
free(buf);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sec_send(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
const char *desc = "Transfer security protocol data to "\
|
|
|
|
"a controller. Security Receives for the same protocol should be "\
|
|
|
|
"performed after Security Sends. The security protocol field "\
|
|
|
|
"associates Security Sends (security-send) and Security Receives "\
|
|
|
|
"(security-recv).";
|
|
|
|
const char *file = "transfer payload";
|
|
|
|
const char *secp = "security protocol (cf. SPC-4)";
|
|
|
|
const char *spsp = "security-protocol-specific (cf. SPC-4)";
|
|
|
|
const char *tl = "transfer length (cf. SPC-4)";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *nssf = "NVMe Security Specific Field";
|
2025-02-16 12:17:56 +01:00
|
|
|
int err, sec_fd = STDIN_FILENO;
|
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
void *sec_buf;
|
|
|
|
unsigned int sec_size;
|
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
char *file;
|
|
|
|
__u8 nssf;
|
|
|
|
__u8 secp;
|
|
|
|
__u16 spsp;
|
|
|
|
__u32 tl;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.file = "",
|
|
|
|
.nssf = 0,
|
|
|
|
.secp = 0,
|
|
|
|
.spsp = 0,
|
|
|
|
.tl = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_FILE("file", 'f', &cfg.file, file),
|
|
|
|
OPT_BYTE("nssf", 'N', &cfg.nssf, nssf),
|
|
|
|
OPT_BYTE("secp", 'p', &cfg.secp, secp),
|
|
|
|
OPT_SHRT("spsp", 's', &cfg.spsp, spsp),
|
|
|
|
OPT_UINT("tl", 't', &cfg.tl, tl),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
if (cfg.tl == 0) {
|
|
|
|
fprintf(stderr, "--tl unspecified or zero\n");
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
if ((cfg.tl & 3) != 0)
|
|
|
|
fprintf(stderr, "WARNING: --tl not dword aligned; unaligned bytes may be truncated\n");
|
|
|
|
|
|
|
|
if (strlen(cfg.file) == 0) {
|
|
|
|
sec_fd = STDIN_FILENO;
|
|
|
|
sec_size = cfg.tl;
|
|
|
|
} else {
|
|
|
|
sec_fd = open(cfg.file, O_RDONLY);
|
|
|
|
if (sec_fd < 0) {
|
|
|
|
fprintf(stderr, "Failed to open %s: %s\n",
|
|
|
|
cfg.file, strerror(errno));
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
err = fstat(sec_fd, &sb);
|
|
|
|
if (err < 0) {
|
|
|
|
perror("fstat");
|
|
|
|
goto close_sec_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
sec_size = cfg.tl > sb.st_size ? cfg.tl : sb.st_size;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (posix_memalign(&sec_buf, getpagesize(), cfg.tl)) {
|
|
|
|
fprintf(stderr, "No memory for security size:%d\n", cfg.tl);
|
|
|
|
err = -ENOMEM;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_sec_fd;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
memset(sec_buf, 0, cfg.tl); // ensure zero fill if buf_size > sec_size
|
2025-02-16 12:10:50 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
err = read(sec_fd, sec_buf, sec_size);
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
fprintf(stderr, "Failed to read data from security file"
|
|
|
|
" %s with %s\n", cfg.file, strerror(errno));
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_security_send_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.nssf = cfg.nssf,
|
|
|
|
.spsp0 = cfg.spsp & 0xff,
|
|
|
|
.spsp1 = cfg.spsp >> 8,
|
|
|
|
.secp = cfg.secp,
|
|
|
|
.tl = cfg.tl,
|
|
|
|
.data_len = cfg.tl,
|
|
|
|
.data = sec_buf,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_security_send(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "security-send: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_show_status(err);
|
2025-02-16 11:09:01 +01:00
|
|
|
else
|
2025-02-16 11:30:13 +01:00
|
|
|
printf("NVME Security Send Command Success\n");
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
free:
|
|
|
|
free(sec_buf);
|
|
|
|
close_sec_fd:
|
|
|
|
close(sec_fd);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dir_send(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Set directive parameters of the "\
|
|
|
|
"specified directive type.";
|
|
|
|
const char *raw = "show directive in binary format";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
const char *data_len = "buffer len (if) data is returned";
|
|
|
|
const char *dtype = "directive type";
|
|
|
|
const char *dspec = "directive specification associated with directive type";
|
|
|
|
const char *doper = "directive operation";
|
|
|
|
const char *endir = "directive enable";
|
|
|
|
const char *ttype = "target directive type to be enabled/disabled";
|
|
|
|
const char *human_readable = "show directive in readable format";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *input = "write/send file (default stdin)";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
__u32 result;
|
|
|
|
__u32 dw12 = 0;
|
|
|
|
void *buf = NULL;
|
|
|
|
int ffd = STDIN_FILENO;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u32 data_len;
|
|
|
|
__u8 dtype;
|
|
|
|
__u8 ttype;
|
|
|
|
__u16 dspec;
|
|
|
|
__u8 doper;
|
|
|
|
__u16 endir;
|
|
|
|
bool human_readable;
|
|
|
|
bool raw_binary;
|
|
|
|
char *file;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 1,
|
|
|
|
.data_len = 0,
|
|
|
|
.dtype = 0,
|
|
|
|
.ttype = 0,
|
|
|
|
.dspec = 0,
|
|
|
|
.doper = 0,
|
|
|
|
.endir = 1,
|
|
|
|
.human_readable = false,
|
|
|
|
.raw_binary = false,
|
|
|
|
.file = "",
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
|
|
|
|
OPT_BYTE("dir-type", 'D', &cfg.dtype, dtype),
|
|
|
|
OPT_BYTE("target-dir", 'T', &cfg.ttype, ttype),
|
|
|
|
OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec),
|
|
|
|
OPT_BYTE("dir-oper", 'O', &cfg.doper, doper),
|
|
|
|
OPT_SHRT("endir", 'e', &cfg.endir, endir),
|
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FILE("input-file", 'i', &cfg.file, input),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
switch (cfg.dtype) {
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_DTYPE_IDENTIFY:
|
2025-02-16 11:09:01 +01:00
|
|
|
switch (cfg.doper) {
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR:
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!cfg.ttype) {
|
|
|
|
fprintf(stderr, "target-dir required param\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
dw12 = cfg.ttype << 8 | cfg.endir;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "invalid directive operations for Identify Directives\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
break;
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_DTYPE_STREAMS:
|
2025-02-16 11:09:01 +01:00
|
|
|
switch (cfg.doper) {
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER:
|
|
|
|
case NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE:
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "invalid directive operations for Streams Directives\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "invalid directive type\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cfg.data_len) {
|
|
|
|
if (posix_memalign(&buf, getpagesize(), cfg.data_len)) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
memset(buf, 0, cfg.data_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf) {
|
|
|
|
if (strlen(cfg.file)) {
|
|
|
|
ffd = open(cfg.file, O_RDONLY);
|
|
|
|
if (ffd <= 0) {
|
|
|
|
fprintf(stderr, "Failed to open file %s: %s\n",
|
|
|
|
cfg.file, strerror(errno));
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = read(ffd, (void *)buf, cfg.data_len);
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
fprintf(stderr, "failed to read data buffer from input"
|
|
|
|
" file %s\n", strerror(errno));
|
|
|
|
goto close_ffd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_directive_send_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.dspec = cfg.dspec,
|
|
|
|
.doper = cfg.doper,
|
|
|
|
.dtype = cfg.dtype,
|
|
|
|
.cdw12 = dw12,
|
|
|
|
.data_len = cfg.data_len,
|
|
|
|
.data = buf,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = &result,
|
|
|
|
};
|
|
|
|
err = nvme_directive_send(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "dir-send: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_ffd;
|
|
|
|
}
|
|
|
|
if (!err) {
|
|
|
|
printf("dir-send: type %#x, operation %#x, spec_val %#x, nsid %#x, result %#x \n",
|
|
|
|
cfg.dtype, cfg.doper, cfg.dspec, cfg.namespace_id, result);
|
|
|
|
if (buf) {
|
|
|
|
if (!cfg.raw_binary)
|
|
|
|
d(buf, cfg.data_len, 16, 1);
|
|
|
|
else
|
|
|
|
d_raw(buf, cfg.data_len);
|
|
|
|
}
|
2025-02-16 11:30:13 +01:00
|
|
|
} else if (err > 0)
|
2025-02-16 11:09:01 +01:00
|
|
|
nvme_show_status(err);
|
|
|
|
|
|
|
|
close_ffd:
|
|
|
|
close(ffd);
|
|
|
|
free:
|
2025-02-16 11:30:13 +01:00
|
|
|
free(buf);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int write_uncor(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "The Write Uncorrectable command is used to set a "\
|
|
|
|
"range of logical blocks to invalid.";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *start_block = "64-bit LBA of first block to access";
|
|
|
|
const char *block_count = "number of blocks (zeroes based) on device to access";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 start_block;
|
|
|
|
__u16 block_count;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.start_block = 0,
|
|
|
|
.block_count = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block),
|
|
|
|
OPT_SHRT("block-count", 'c', &cfg.block_count, block_count),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_io_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.slba = cfg.start_block,
|
|
|
|
.nlb = cfg.block_count,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_write_uncorrectable(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "write uncorrectable: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVME Write Uncorrectable Success\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
static int invalid_tags(__u64 storage_tag, __u64 ref_tag, __u8 sts, __u8 pif)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
if (sts < 64 && storage_tag >= (1LL << sts)) {
|
|
|
|
fprintf(stderr, "Storage tag larger than storage tag size\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (pif) {
|
|
|
|
case 0:
|
|
|
|
if (ref_tag >= (1LL << (32 - sts)))
|
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (sts > 16 && ref_tag >= (1LL << (80 - sts)))
|
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (sts > 0 && ref_tag >= (1LL << (64 - sts)))
|
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Invalid PIF\n");
|
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
fprintf(stderr, "Reference tag larger than allowed by PIF\n");
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int write_zeroes(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
__u16 control = 0;
|
2025-02-16 12:16:06 +01:00
|
|
|
__u8 lba_index, sts = 0, pif = 0;
|
|
|
|
struct nvme_id_ns ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 12:16:06 +01:00
|
|
|
struct nvme_nvm_id_ns nvm_ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 12:16:06 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *desc = "The Write Zeroes command is used to set a "\
|
|
|
|
"range of logical blocks to zero.";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *start_block = "64-bit LBA of first block to access";
|
|
|
|
const char *block_count = "number of blocks (zeroes based) on device to access";
|
|
|
|
const char *limited_retry = "limit media access attempts";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *force_unit_access = "force device to commit data before command completes";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *prinfo = "PI and check field";
|
2025-02-16 12:16:06 +01:00
|
|
|
const char *ref_tag = "reference tag for end-to-end PI";
|
|
|
|
const char *app_tag_mask = "app tag mask for end-to-end PI";
|
|
|
|
const char *app_tag = "app tag for end-to-end PI";
|
|
|
|
const char *storage_tag = "storage tag for end-to-end PI";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *deac = "Set DEAC bit, requesting controller to deallocate specified logical blocks";
|
2025-02-16 12:10:50 +01:00
|
|
|
const char *storage_tag_check = "This bit specifies the Storage Tag field shall be checked as "\
|
|
|
|
"part of end-to-end data protection processing";
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 start_block;
|
|
|
|
__u16 block_count;
|
|
|
|
bool deac;
|
|
|
|
bool limited_retry;
|
|
|
|
bool force_unit_access;
|
|
|
|
__u8 prinfo;
|
2025-02-16 12:16:06 +01:00
|
|
|
__u64 ref_tag;
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 app_tag_mask;
|
|
|
|
__u16 app_tag;
|
|
|
|
__u64 storage_tag;
|
|
|
|
bool storage_tag_check;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.start_block = 0,
|
|
|
|
.block_count = 0,
|
|
|
|
.deac = false,
|
|
|
|
.limited_retry = false,
|
|
|
|
.force_unit_access = false,
|
|
|
|
.prinfo = 0,
|
|
|
|
.ref_tag = 0,
|
|
|
|
.app_tag_mask = 0,
|
|
|
|
.app_tag = 0,
|
|
|
|
.storage_tag = 0,
|
|
|
|
.storage_tag_check = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block),
|
|
|
|
OPT_SHRT("block-count", 'c', &cfg.block_count, block_count),
|
|
|
|
OPT_FLAG("deac", 'd', &cfg.deac, deac),
|
|
|
|
OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo),
|
2025-02-16 12:16:06 +01:00
|
|
|
OPT_SUFFIX("ref-tag", 'r', &cfg.ref_tag, ref_tag),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_SHRT("app-tag-mask", 'm', &cfg.app_tag_mask, app_tag_mask),
|
|
|
|
OPT_SHRT("app-tag", 'a', &cfg.app_tag, app_tag),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SUFFIX("storage-tag", 'S', &cfg.storage_tag, storage_tag),
|
|
|
|
OPT_FLAG("storage-tag-check", 'C', &cfg.storage_tag_check, storage_tag_check),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.prinfo > 0xf) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
control |= (cfg.prinfo << 10);
|
|
|
|
if (cfg.limited_retry)
|
2025-02-16 12:15:30 +01:00
|
|
|
control |= NVME_IO_LR;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.force_unit_access)
|
2025-02-16 12:15:30 +01:00
|
|
|
control |= NVME_IO_FUA;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.deac)
|
2025-02-16 12:15:30 +01:00
|
|
|
control |= NVME_IO_DEAC;
|
2025-02-16 12:10:50 +01:00
|
|
|
if (cfg.storage_tag_check)
|
2025-02-16 12:16:06 +01:00
|
|
|
control |= NVME_IO_STC;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ns(dev, cfg.namespace_id, &ns);
|
2025-02-16 12:16:06 +01:00
|
|
|
if (err) {
|
|
|
|
nvme_show_status(err);
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:16:06 +01:00
|
|
|
} else if (err < 0) {
|
|
|
|
fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:16:06 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_ns_csi(dev_fd(dev), cfg.namespace_id, 0,
|
|
|
|
NVME_CSI_NVM, &nvm_ns);
|
2025-02-16 12:16:06 +01:00
|
|
|
if (!err) {
|
|
|
|
nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index);
|
|
|
|
sts = nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_STS_MASK;
|
|
|
|
pif = (nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_PIF_MASK) >> 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (invalid_tags(cfg.storage_tag, cfg.ref_tag, sts, pif)) {
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:16:06 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_io_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.slba = cfg.start_block,
|
|
|
|
.nlb = cfg.block_count,
|
|
|
|
.control = control,
|
2025-02-16 12:16:06 +01:00
|
|
|
.reftag_u64 = cfg.ref_tag,
|
2025-02-16 12:15:30 +01:00
|
|
|
.apptag = cfg.app_tag,
|
|
|
|
.appmask = cfg.app_tag_mask,
|
2025-02-16 12:16:06 +01:00
|
|
|
.sts = sts,
|
|
|
|
.pif = pif,
|
2025-02-16 12:15:30 +01:00
|
|
|
.storage_tag = cfg.storage_tag,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_write_zeros(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "write-zeroes: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVME Write Zeroes Success\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dsm(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "The Dataset Management command is used by the host to "\
|
|
|
|
"indicate attributes for ranges of logical blocks. This includes attributes "\
|
|
|
|
"for discarding unused blocks, data read and write frequency, access size, and other "\
|
|
|
|
"information that may be used to optimize performance and reliability.";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
const char *blocks = "Comma separated list of the number of blocks in each range";
|
|
|
|
const char *starting_blocks = "Comma separated list of the starting block in each range";
|
|
|
|
const char *context_attrs = "Comma separated list of the context attributes in each range";
|
|
|
|
const char *ad = "Attribute Deallocate";
|
|
|
|
const char *idw = "Attribute Integral Dataset for Write";
|
|
|
|
const char *idr = "Attribute Integral Dataset for Read";
|
|
|
|
const char *cdw11 = "All the command DWORD 11 attributes. Use instead of specifying individual attributes";
|
|
|
|
|
|
|
|
uint16_t nr, nc, nb, ns;
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 ctx_attrs[256] = {0,};
|
|
|
|
__u32 nlbs[256] = {0,};
|
|
|
|
__u64 slbas[256] = {0,};
|
|
|
|
struct nvme_dsm_range dsm[256];
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
char *ctx_attrs;
|
|
|
|
char *blocks;
|
|
|
|
char *slbas;
|
|
|
|
bool ad;
|
|
|
|
bool idw;
|
|
|
|
bool idr;
|
|
|
|
__u32 cdw11;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.ctx_attrs = "",
|
|
|
|
.blocks = "",
|
|
|
|
.slbas = "",
|
|
|
|
.ad = false,
|
|
|
|
.idw = false,
|
|
|
|
.idr = false,
|
|
|
|
.cdw11 = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_LIST("ctx-attrs", 'a', &cfg.ctx_attrs, context_attrs),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_LIST("blocks", 'b', &cfg.blocks, blocks),
|
|
|
|
OPT_LIST("slbs", 's', &cfg.slbas, starting_blocks),
|
|
|
|
OPT_FLAG("ad", 'd', &cfg.ad, ad),
|
|
|
|
OPT_FLAG("idw", 'w', &cfg.idw, idw),
|
|
|
|
OPT_FLAG("idr", 'r', &cfg.idr, idr),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nc = argconfig_parse_comma_sep_array(cfg.ctx_attrs, (int *)ctx_attrs, ARRAY_SIZE(ctx_attrs));
|
|
|
|
nb = argconfig_parse_comma_sep_array(cfg.blocks, (int *)nlbs, ARRAY_SIZE(nlbs));
|
|
|
|
ns = argconfig_parse_comma_sep_array_long(cfg.slbas, (unsigned long long *)slbas, ARRAY_SIZE(slbas));
|
2025-02-16 11:09:01 +01:00
|
|
|
nr = max(nc, max(nb, ns));
|
|
|
|
if (!nr || nr > 256) {
|
|
|
|
fprintf(stderr, "No range definition provided\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!cfg.cdw11)
|
|
|
|
cfg.cdw11 = (cfg.ad << 2) | (cfg.idw << 1) | (cfg.idr << 0);
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_init_dsm_range(dsm, ctx_attrs, nlbs, slbas, nr);
|
|
|
|
struct nvme_dsm_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.attrs = cfg.cdw11,
|
|
|
|
.nr_ranges = nr,
|
|
|
|
.dsm = dsm,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_dsm(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "data-set management: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVMe DSM: success\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:30:13 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int copy(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "The Copy command is used by the host to copy data "
|
|
|
|
"from one or more source logical block ranges to a "
|
|
|
|
"single consecutive destination logical block "
|
|
|
|
"range.";
|
|
|
|
|
|
|
|
const char *d_nsid = "identifier of desired namespace";
|
|
|
|
const char *d_sdlba = "64-bit addr of first destination logical block";
|
|
|
|
const char *d_slbas = "64-bit addr of first block per range (comma-separated list)";
|
|
|
|
const char *d_nlbs = "number of blocks per range (comma-separated list, zeroes-based values)";
|
|
|
|
const char *d_lr = "limited retry";
|
|
|
|
const char *d_fua = "force unit access";
|
|
|
|
const char *d_prinfor = "protection information and check field (read part)";
|
|
|
|
const char *d_prinfow = "protection information and check field (write part)";
|
|
|
|
const char *d_ilbrt = "initial lba reference tag (write part)";
|
|
|
|
const char *d_eilbrts = "expected lba reference tags (read part, comma-separated list)";
|
|
|
|
const char *d_lbat = "lba application tag (write part)";
|
|
|
|
const char *d_elbats = "expected lba application tags (read part, comma-separated list)";
|
|
|
|
const char *d_lbatm = "lba application tag mask (write part)";
|
|
|
|
const char *d_elbatms = "expected lba application tag masks (read part, comma-separated list)";
|
|
|
|
const char *d_dtype = "directive type (write part)";
|
|
|
|
const char *d_dspec = "directive specific (write part)";
|
|
|
|
const char *d_format = "source range entry format";
|
|
|
|
|
|
|
|
uint16_t nr, nb, ns, nrts, natms, nats;
|
2025-02-16 12:15:30 +01:00
|
|
|
__u16 nlbs[128] = { 0 };
|
2025-02-16 11:30:13 +01:00
|
|
|
unsigned long long slbas[128] = {0,};
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 12:16:06 +01:00
|
|
|
|
|
|
|
union {
|
|
|
|
__u32 f0[128];
|
|
|
|
__u64 f1[101];
|
|
|
|
} eilbrts;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 elbatms[128] = { 0 };
|
|
|
|
__u32 elbats[128] = { 0 };
|
2025-02-16 12:16:06 +01:00
|
|
|
|
|
|
|
union {
|
|
|
|
struct nvme_copy_range f0[128];
|
|
|
|
struct nvme_copy_range_f1 f1[101];
|
|
|
|
} copy;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 sdlba;
|
|
|
|
char *slbas;
|
|
|
|
char *nlbs;
|
|
|
|
bool lr;
|
|
|
|
bool fua;
|
|
|
|
__u8 prinfow;
|
|
|
|
__u8 prinfor;
|
2025-02-16 12:16:06 +01:00
|
|
|
__u64 ilbrt;
|
2025-02-16 12:15:30 +01:00
|
|
|
char *eilbrts;
|
|
|
|
__u16 lbat;
|
|
|
|
char *elbats;
|
2025-02-16 12:16:06 +01:00
|
|
|
__u16 lbatm;
|
|
|
|
char *elbatms;
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 dtype;
|
|
|
|
__u16 dspec;
|
|
|
|
__u8 format;
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.sdlba = 0,
|
|
|
|
.slbas = "",
|
|
|
|
.nlbs = "",
|
|
|
|
.lr = false,
|
|
|
|
.fua = false,
|
|
|
|
.prinfow = 0,
|
|
|
|
.prinfor = 0,
|
|
|
|
.ilbrt = 0,
|
|
|
|
.eilbrts = "",
|
|
|
|
.lbat = 0,
|
|
|
|
.elbats = "",
|
|
|
|
.lbatm = 0,
|
|
|
|
.elbatms = "",
|
|
|
|
.dtype = 0,
|
|
|
|
.dspec = 0,
|
|
|
|
.format = 0,
|
2025-02-16 11:30:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, d_nsid),
|
|
|
|
OPT_SUFFIX("sdlba", 'd', &cfg.sdlba, d_sdlba),
|
|
|
|
OPT_LIST("slbs", 's', &cfg.slbas, d_slbas),
|
|
|
|
OPT_LIST("blocks", 'b', &cfg.nlbs, d_nlbs),
|
|
|
|
OPT_FLAG("limited-retry", 'l', &cfg.lr, d_lr),
|
|
|
|
OPT_FLAG("force-unit-access", 'f', &cfg.fua, d_fua),
|
|
|
|
OPT_BYTE("prinfow", 'p', &cfg.prinfow, d_prinfow),
|
|
|
|
OPT_BYTE("prinfor", 'P', &cfg.prinfor, d_prinfor),
|
2025-02-16 12:16:06 +01:00
|
|
|
OPT_SUFFIX("ref-tag", 'r', &cfg.ilbrt, d_ilbrt),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_LIST("expected-ref-tags", 'R', &cfg.eilbrts, d_eilbrts),
|
|
|
|
OPT_SHRT("app-tag", 'a', &cfg.lbat, d_lbat),
|
|
|
|
OPT_LIST("expected-app-tags", 'A', &cfg.elbats, d_elbats),
|
|
|
|
OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, d_lbatm),
|
|
|
|
OPT_LIST("expected-app-tag-masks", 'M', &cfg.elbatms, d_elbatms),
|
|
|
|
OPT_BYTE("dir-type", 'T', &cfg.dtype, d_dtype),
|
|
|
|
OPT_SHRT("dir-spec", 'S', &cfg.dspec, d_dspec),
|
|
|
|
OPT_BYTE("format", 'F', &cfg.format, d_format),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:30:13 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
nb = argconfig_parse_comma_sep_array_short(cfg.nlbs, nlbs, ARRAY_SIZE(nlbs));
|
2025-02-16 11:30:13 +01:00
|
|
|
ns = argconfig_parse_comma_sep_array_long(cfg.slbas, slbas, ARRAY_SIZE(slbas));
|
2025-02-16 12:16:06 +01:00
|
|
|
|
|
|
|
if (cfg.format == 0)
|
|
|
|
nrts = argconfig_parse_comma_sep_array(cfg.eilbrts, (int *)eilbrts.f0, ARRAY_SIZE(eilbrts.f0));
|
|
|
|
else if (cfg.format == 1)
|
2025-02-16 12:17:56 +01:00
|
|
|
nrts = argconfig_parse_comma_sep_array_long(cfg.eilbrts, (unsigned long long *)eilbrts.f1, ARRAY_SIZE(eilbrts.f1));
|
2025-02-16 12:16:06 +01:00
|
|
|
else {
|
|
|
|
fprintf(stderr, "invalid format\n");
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:16:06 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
natms = argconfig_parse_comma_sep_array(cfg.elbatms, (int *)elbatms, ARRAY_SIZE(elbatms));
|
|
|
|
nats = argconfig_parse_comma_sep_array(cfg.elbats, (int *)elbats, ARRAY_SIZE(elbats));
|
2025-02-16 11:30:13 +01:00
|
|
|
|
|
|
|
nr = max(nb, max(ns, max(nrts, max(natms, nats))));
|
2025-02-16 12:16:06 +01:00
|
|
|
if (!nr || nr > 128 || (cfg.format == 1 && nr > 101)) {
|
2025-02-16 11:30:13 +01:00
|
|
|
fprintf(stderr, "invalid range\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
if (cfg.format == 0)
|
|
|
|
nvme_init_copy_range(copy.f0, nlbs, (__u64 *)slbas,
|
|
|
|
eilbrts.f0, elbatms, elbats, nr);
|
|
|
|
else if (cfg.format == 1)
|
|
|
|
nvme_init_copy_range_f1(copy.f1, nlbs, (__u64 *)slbas,
|
|
|
|
eilbrts.f1, elbatms, elbats, nr);
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
struct nvme_copy_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
2025-02-16 12:16:06 +01:00
|
|
|
.copy = copy.f0,
|
2025-02-16 12:15:30 +01:00
|
|
|
.sdlba = cfg.sdlba,
|
|
|
|
.nr = nr,
|
|
|
|
.prinfor = cfg.prinfor,
|
|
|
|
.prinfow = cfg.prinfow,
|
|
|
|
.dtype = cfg.dtype,
|
|
|
|
.dspec = cfg.dspec,
|
|
|
|
.format = cfg.format,
|
|
|
|
.lr = cfg.lr,
|
|
|
|
.fua = cfg.fua,
|
2025-02-16 12:16:06 +01:00
|
|
|
.ilbrt_u64 = cfg.ilbrt,
|
2025-02-16 12:15:30 +01:00
|
|
|
.lbatm = cfg.lbatm,
|
|
|
|
.lbat = cfg.lbat,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_copy(&args);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "NVMe Copy: %s\n", nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVMe Copy: success\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int flush(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Commit data and metadata associated with "\
|
|
|
|
"given namespaces to nonvolatile media. Applies to all commands "\
|
|
|
|
"finished before the flush was submitted. Additional data may also be "\
|
|
|
|
"flushed by the controller, from any namespace, depending on controller and "\
|
|
|
|
"associated namespace status.";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_flush(dev_fd(dev), cfg.namespace_id);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "flush: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVMe Flush: success\n");
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int resv_acquire(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Obtain a reservation on a given "\
|
|
|
|
"namespace. Only one reservation is allowed at a time on a "\
|
|
|
|
"given namespace, though multiple controllers may register "\
|
|
|
|
"with that namespace. Namespace reservation will abort with "\
|
|
|
|
"status Reservation Conflict if the given namespace is "\
|
|
|
|
"already reserved.";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
const char *crkey = "current reservation key";
|
|
|
|
const char *prkey = "pre-empt reservation key";
|
|
|
|
const char *rtype = "reservation type";
|
2025-02-16 12:10:50 +01:00
|
|
|
const char *racqa = "reservation acquire action";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *iekey = "ignore existing res. key";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 crkey;
|
|
|
|
__u64 prkey;
|
|
|
|
__u8 rtype;
|
|
|
|
__u8 racqa;
|
|
|
|
bool iekey;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.crkey = 0,
|
|
|
|
.prkey = 0,
|
|
|
|
.rtype = 0,
|
|
|
|
.racqa = 0,
|
|
|
|
.iekey = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey),
|
|
|
|
OPT_SUFFIX("prkey", 'p', &cfg.prkey, prkey),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("rtype", 't', &cfg.rtype, rtype),
|
|
|
|
OPT_BYTE("racqa", 'a', &cfg.racqa, racqa),
|
|
|
|
OPT_FLAG("iekey", 'i', &cfg.iekey, iekey),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cfg.racqa > 7) {
|
|
|
|
fprintf(stderr, "invalid racqa:%d\n", cfg.racqa);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_resv_acquire_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.rtype = cfg.rtype,
|
|
|
|
.racqa = cfg.racqa,
|
|
|
|
.iekey = !!cfg.iekey,
|
|
|
|
.crkey = cfg.crkey,
|
|
|
|
.nrkey = cfg.prkey,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_resv_acquire(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "reservation acquire: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVME Reservation Acquire success\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int resv_register(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Register, de-register, or "\
|
|
|
|
"replace a controller's reservation on a given namespace. "\
|
|
|
|
"Only one reservation at a time is allowed on any namespace.";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
const char *crkey = "current reservation key";
|
|
|
|
const char *iekey = "ignore existing res. key";
|
|
|
|
const char *nrkey = "new reservation key";
|
|
|
|
const char *rrega = "reservation registration action";
|
|
|
|
const char *cptpl = "change persistence through power loss setting";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 crkey;
|
|
|
|
__u64 nrkey;
|
|
|
|
__u8 rrega;
|
|
|
|
__u8 cptpl;
|
|
|
|
bool iekey;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.crkey = 0,
|
|
|
|
.nrkey = 0,
|
|
|
|
.rrega = 0,
|
|
|
|
.cptpl = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey),
|
|
|
|
OPT_SUFFIX("nrkey", 'k', &cfg.nrkey, nrkey),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("rrega", 'r', &cfg.rrega, rrega),
|
|
|
|
OPT_BYTE("cptpl", 'p', &cfg.cptpl, cptpl),
|
|
|
|
OPT_FLAG("iekey", 'i', &cfg.iekey, iekey),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cfg.cptpl > 3) {
|
|
|
|
fprintf(stderr, "invalid cptpl:%d\n", cfg.cptpl);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.rrega > 7) {
|
|
|
|
fprintf(stderr, "invalid rrega:%d\n", cfg.rrega);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_resv_register_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.rrega = cfg.rrega,
|
|
|
|
.cptpl = cfg.cptpl,
|
|
|
|
.iekey = !!cfg.iekey,
|
|
|
|
.crkey = cfg.crkey,
|
|
|
|
.nrkey = cfg.nrkey,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_resv_register(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "reservation register: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVME Reservation success\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int resv_release(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Releases reservation held on a "\
|
|
|
|
"namespace by the given controller. If rtype != current reservation"\
|
|
|
|
"type, release will fails. If the given controller holds no "\
|
|
|
|
"reservation on the namespace or is not the namespace's current "\
|
|
|
|
"reservation holder, the release command completes with no "\
|
|
|
|
"effect. If the reservation type is not Write Exclusive or "\
|
|
|
|
"Exclusive Access, all registrants on the namespace except "\
|
|
|
|
"the issuing controller are notified.";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *crkey = "current reservation key";
|
|
|
|
const char *iekey = "ignore existing res. key";
|
|
|
|
const char *rtype = "reservation type";
|
|
|
|
const char *rrela = "reservation release action";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 crkey;
|
|
|
|
__u8 rtype;
|
|
|
|
__u8 rrela;
|
|
|
|
__u8 iekey;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.crkey = 0,
|
|
|
|
.rtype = 0,
|
|
|
|
.rrela = 0,
|
|
|
|
.iekey = 0,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SUFFIX("crkey", 'c', &cfg.crkey, crkey),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("rtype", 't', &cfg.rtype, rtype),
|
|
|
|
OPT_BYTE("rrela", 'a', &cfg.rrela, rrela),
|
|
|
|
OPT_FLAG("iekey", 'i', &cfg.iekey, iekey),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cfg.rrela > 7) {
|
|
|
|
fprintf(stderr, "invalid rrela:%d\n", cfg.rrela);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_resv_release_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.rtype = cfg.rtype,
|
|
|
|
.rrela = cfg.rrela,
|
|
|
|
.iekey = !!cfg.iekey,
|
|
|
|
.crkey = cfg.crkey,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_resv_release(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "reservation release: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVME Reservation Release success\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int resv_report(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Returns Reservation Status data "\
|
|
|
|
"structure describing any existing reservations on and the "\
|
|
|
|
"status of a given namespace. Namespace Reservation Status "\
|
|
|
|
"depends on the number of controllers registered for that "\
|
|
|
|
"namespace.";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
const char *numd = "number of dwords to transfer";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *eds = "request extended data structure";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *raw = "dump output in binary format";
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_resv_status *status;
|
2025-02-16 11:09:01 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err, size;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u32 numd;
|
|
|
|
__u8 eds;
|
|
|
|
char *output_format;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.numd = 0,
|
|
|
|
.eds = false,
|
|
|
|
.output_format = "normal",
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_UINT("numd", 'd', &cfg.numd, numd),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("eds", 'e', &cfg.eds, eds),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.numd || cfg.numd >= (0x1000 >> 2))
|
|
|
|
cfg.numd = (0x1000 >> 2) - 1;
|
|
|
|
if (cfg.numd < 3)
|
|
|
|
cfg.numd = 3;
|
|
|
|
|
|
|
|
size = (cfg.numd + 1) << 2;
|
|
|
|
|
|
|
|
if (posix_memalign((void **)&status, getpagesize(), size)) {
|
|
|
|
fprintf(stderr, "No memory for resv report:%d\n", size);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
memset(status, 0, size);
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_resv_report_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.eds = cfg.eds,
|
|
|
|
.len = size,
|
|
|
|
.report = status,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_resv_report(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_show_resv_report(status, size, cfg.eds, flags);
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err > 0)
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_show_status(err);
|
2025-02-16 11:09:01 +01:00
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "reservation report: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
free(status);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
unsigned long long elapsed_utime(struct timeval start_time,
|
2025-02-16 11:09:01 +01:00
|
|
|
struct timeval end_time)
|
|
|
|
{
|
|
|
|
unsigned long long err = (end_time.tv_sec - start_time.tv_sec) * 1000000 +
|
|
|
|
(end_time.tv_usec - start_time.tv_usec);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int submit_io(int opcode, char *command, const char *desc,
|
|
|
|
int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct timeval start_time, end_time;
|
|
|
|
void *buffer, *mbuffer = NULL;
|
2025-02-16 12:15:30 +01:00
|
|
|
int err = 0;
|
2025-02-16 12:17:56 +01:00
|
|
|
int dfd, mfd;
|
2025-02-16 11:09:01 +01:00
|
|
|
int flags = opcode & 1 ? O_RDONLY : O_WRONLY | O_CREAT;
|
|
|
|
int mode = S_IRUSR | S_IWUSR |S_IRGRP | S_IWGRP| S_IROTH;
|
|
|
|
__u16 control = 0;
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 dsmgmt = 0;
|
2025-02-16 11:30:13 +01:00
|
|
|
int logical_block_size = 0;
|
2025-02-16 12:16:06 +01:00
|
|
|
unsigned long long buffer_size = 0, mbuffer_size = 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
bool huge;
|
2025-02-16 11:30:13 +01:00
|
|
|
struct nvme_id_ns ns;
|
2025-02-16 12:16:06 +01:00
|
|
|
struct nvme_nvm_id_ns nvm_ns;
|
|
|
|
__u8 lba_index, ms = 0, sts = 0, pif = 0;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *namespace_id = "Identifier of desired namespace";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *start_block = "64-bit addr of first block to access";
|
|
|
|
const char *block_count = "number of blocks (zeroes based) on device to access";
|
|
|
|
const char *data_size = "size of data in bytes";
|
|
|
|
const char *metadata_size = "size of metadata in bytes";
|
2025-02-16 12:16:06 +01:00
|
|
|
const char *ref_tag = "reference tag for end-to-end PI";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *data = "data file";
|
|
|
|
const char *metadata = "metadata file";
|
|
|
|
const char *prinfo = "PI and check field";
|
|
|
|
const char *app_tag_mask = "app tag mask (for end to end PI)";
|
|
|
|
const char *app_tag = "app tag (for end to end PI)";
|
|
|
|
const char *limited_retry = "limit num. media access attempts";
|
|
|
|
const char *latency = "output latency statistics";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *force_unit_access = "force device to commit data before command completes";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *show = "show command before sending";
|
|
|
|
const char *dry = "show command instead of sending";
|
|
|
|
const char *dtype = "directive type (for write-only)";
|
|
|
|
const char *dspec = "directive specific (for write-only)";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *dsm = "dataset management attributes (lower 8 bits)";
|
2025-02-16 12:10:50 +01:00
|
|
|
const char *storage_tag_check = "This bit specifies the Storage Tag field shall be " \
|
|
|
|
"checked as part of end-to-end data protection processing";
|
2025-02-16 12:16:06 +01:00
|
|
|
const char *storage_tag = "storage tag for end-to-end PI";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *force = "The \"I know what I'm doing\" flag, do not enforce exclusive access for write";
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 start_block;
|
|
|
|
__u16 block_count;
|
|
|
|
__u64 data_size;
|
|
|
|
__u64 metadata_size;
|
2025-02-16 12:16:06 +01:00
|
|
|
__u64 ref_tag;
|
2025-02-16 12:15:30 +01:00
|
|
|
char *data;
|
|
|
|
char *metadata;
|
|
|
|
__u8 prinfo;
|
|
|
|
__u16 app_tag_mask;
|
|
|
|
__u16 app_tag;
|
|
|
|
__u64 storage_tag;
|
|
|
|
bool limited_retry;
|
|
|
|
bool force_unit_access;
|
|
|
|
bool storage_tag_check;
|
|
|
|
__u8 dtype;
|
|
|
|
__u16 dspec;
|
|
|
|
__u8 dsmgmt;
|
|
|
|
bool show;
|
|
|
|
bool dry_run;
|
|
|
|
bool latency;
|
|
|
|
bool force;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:10:50 +01:00
|
|
|
.namespace_id = 0,
|
2025-02-16 12:15:30 +01:00
|
|
|
.start_block = 0,
|
|
|
|
.block_count = 0,
|
|
|
|
.data_size = 0,
|
|
|
|
.metadata_size = 0,
|
|
|
|
.ref_tag = 0,
|
|
|
|
.data = "",
|
|
|
|
.metadata = "",
|
|
|
|
.prinfo = 0,
|
|
|
|
.app_tag_mask = 0,
|
|
|
|
.app_tag = 0,
|
2025-02-16 12:10:50 +01:00
|
|
|
.storage_tag = 0,
|
2025-02-16 12:15:30 +01:00
|
|
|
.limited_retry = false,
|
|
|
|
.force_unit_access = false,
|
|
|
|
.storage_tag_check = false,
|
|
|
|
.dtype = 0,
|
|
|
|
.dspec = 0,
|
|
|
|
.dsmgmt = 0,
|
|
|
|
.show = false,
|
|
|
|
.dry_run = false,
|
|
|
|
.latency = false,
|
|
|
|
.force = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block),
|
|
|
|
OPT_SHRT("block-count", 'c', &cfg.block_count, block_count),
|
|
|
|
OPT_SUFFIX("data-size", 'z', &cfg.data_size, data_size),
|
|
|
|
OPT_SUFFIX("metadata-size", 'y', &cfg.metadata_size, metadata_size),
|
2025-02-16 12:16:06 +01:00
|
|
|
OPT_SUFFIX("ref-tag", 'r', &cfg.ref_tag, ref_tag),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FILE("data", 'd', &cfg.data, data),
|
|
|
|
OPT_FILE("metadata", 'M', &cfg.metadata, metadata),
|
|
|
|
OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo),
|
|
|
|
OPT_SHRT("app-tag-mask", 'm', &cfg.app_tag_mask, app_tag_mask),
|
|
|
|
OPT_SHRT("app-tag", 'a', &cfg.app_tag, app_tag),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SUFFIX("storage-tag", 'g', &cfg.storage_tag, storage_tag),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_FLAG("storage-tag-check", 'C', &cfg.storage_tag_check, storage_tag_check),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("dir-type", 'T', &cfg.dtype, dtype),
|
|
|
|
OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_BYTE("dsm", 'D', &cfg.dsmgmt, dsm),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FLAG("show-command", 'v', &cfg.show, show),
|
|
|
|
OPT_FLAG("dry-run", 'w', &cfg.dry_run, dry),
|
|
|
|
OPT_FLAG("latency", 't', &cfg.latency, latency),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("force", 0, &cfg.force, force),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (opcode != nvme_cmd_write) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:15:30 +01:00
|
|
|
goto ret;
|
|
|
|
} else {
|
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err)
|
|
|
|
goto ret;
|
2025-02-16 12:17:56 +01:00
|
|
|
err = open_exclusive(&dev, argc, argv, cfg.force);
|
|
|
|
if (err) {
|
2025-02-16 12:15:30 +01:00
|
|
|
if (errno == EBUSY) {
|
|
|
|
fprintf(stderr, "Failed to open %s.\n",
|
|
|
|
basename(argv[optind]));
|
|
|
|
fprintf(stderr,
|
|
|
|
"Namespace is currently busy.\n");
|
|
|
|
if (!cfg.force)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Use the force [--force] option to ignore that.\n");
|
|
|
|
} else {
|
|
|
|
argconfig_print_help(desc, opts);
|
|
|
|
}
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 12:10:50 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
dfd = mfd = opcode & 1 ? STDIN_FILENO : STDOUT_FILENO;
|
|
|
|
if (cfg.prinfo > 0xf) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dsmgmt = cfg.dsmgmt;
|
|
|
|
control |= (cfg.prinfo << 10);
|
|
|
|
if (cfg.limited_retry)
|
2025-02-16 12:15:30 +01:00
|
|
|
control |= NVME_IO_LR;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.force_unit_access)
|
2025-02-16 12:15:30 +01:00
|
|
|
control |= NVME_IO_FUA;
|
2025-02-16 12:10:50 +01:00
|
|
|
if (cfg.storage_tag_check)
|
2025-02-16 12:16:06 +01:00
|
|
|
control |= NVME_IO_STC;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.dtype) {
|
|
|
|
if (cfg.dtype > 0xf) {
|
|
|
|
fprintf(stderr, "Invalid directive type, %x\n",
|
|
|
|
cfg.dtype);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
control |= cfg.dtype << 4;
|
|
|
|
dsmgmt |= ((__u32)cfg.dspec) << 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen(cfg.data)) {
|
|
|
|
dfd = open(cfg.data, flags, mode);
|
|
|
|
if (dfd < 0) {
|
|
|
|
perror(cfg.data);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (strlen(cfg.metadata)) {
|
|
|
|
mfd = open(cfg.metadata, flags, mode);
|
|
|
|
if (mfd < 0) {
|
|
|
|
perror(cfg.metadata);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_dfd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.data_size) {
|
|
|
|
fprintf(stderr, "data size not provided\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_mfd;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
if (nvme_get_logical_block_size(dev_fd(dev), cfg.namespace_id,
|
2025-02-16 12:15:30 +01:00
|
|
|
&logical_block_size) < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_mfd;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
buffer_size = ((long long)cfg.block_count + 1) * logical_block_size;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.data_size < buffer_size) {
|
|
|
|
fprintf(stderr, "Rounding data size to fit block count (%lld bytes)\n",
|
|
|
|
buffer_size);
|
|
|
|
} else {
|
|
|
|
buffer_size = cfg.data_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer = nvme_alloc(buffer_size, &huge);
|
|
|
|
if (!buffer) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto close_mfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.metadata_size) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ns(dev, cfg.namespace_id, &ns);
|
2025-02-16 12:16:06 +01:00
|
|
|
if (err > 0) {
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_show_status(err);
|
|
|
|
goto free_buffer;
|
|
|
|
} else if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 11:30:13 +01:00
|
|
|
goto free_buffer;
|
|
|
|
}
|
2025-02-16 12:16:06 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index);
|
2025-02-16 11:30:13 +01:00
|
|
|
ms = ns.lbaf[lba_index].ms;
|
2025-02-16 12:16:06 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_ns_csi(dev_fd(dev), 1, 0, NVME_CSI_NVM,
|
|
|
|
&nvm_ns);
|
2025-02-16 12:16:06 +01:00
|
|
|
if (!err) {
|
|
|
|
sts = nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_STS_MASK;
|
|
|
|
pif = (nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_PIF_MASK) >> 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
mbuffer_size = ((unsigned long long)cfg.block_count + 1) * ms;
|
2025-02-16 11:30:13 +01:00
|
|
|
if (ms && cfg.metadata_size < mbuffer_size) {
|
|
|
|
fprintf(stderr, "Rounding metadata size to fit block count (%lld bytes)\n",
|
|
|
|
mbuffer_size);
|
|
|
|
} else {
|
|
|
|
mbuffer_size = cfg.metadata_size;
|
|
|
|
}
|
|
|
|
mbuffer = malloc(mbuffer_size);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!mbuffer) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto free_buffer;
|
|
|
|
}
|
2025-02-16 11:30:13 +01:00
|
|
|
memset(mbuffer, 0, mbuffer_size);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
if (invalid_tags(cfg.storage_tag, cfg.ref_tag, sts, pif)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto free_buffer;
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if ((opcode & 1)) {
|
|
|
|
err = read(dfd, (void *)buffer, cfg.data_size);
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
fprintf(stderr, "failed to read data buffer from input"
|
|
|
|
" file %s\n", strerror(errno));
|
|
|
|
goto free_mbuffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((opcode & 1) && cfg.metadata_size) {
|
2025-02-16 11:30:13 +01:00
|
|
|
err = read(mfd, (void *)mbuffer, mbuffer_size);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
fprintf(stderr, "failed to read meta-data buffer from"
|
|
|
|
" input file %s\n", strerror(errno));
|
|
|
|
goto free_mbuffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.show || cfg.dry_run) {
|
|
|
|
printf("opcode : %02x\n", opcode);
|
|
|
|
printf("nsid : %02x\n", cfg.namespace_id);
|
|
|
|
printf("flags : %02x\n", 0);
|
|
|
|
printf("control : %04x\n", control);
|
|
|
|
printf("nblocks : %04x\n", cfg.block_count);
|
|
|
|
printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)mbuffer);
|
|
|
|
printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)buffer);
|
|
|
|
printf("slba : %"PRIx64"\n", (uint64_t)cfg.start_block);
|
|
|
|
printf("dsmgmt : %08x\n", dsmgmt);
|
2025-02-16 12:16:06 +01:00
|
|
|
printf("reftag : %"PRIx64"\n", (uint64_t)cfg.ref_tag);
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("apptag : %04x\n", cfg.app_tag);
|
|
|
|
printf("appmask : %04x\n", cfg.app_tag_mask);
|
2025-02-16 12:10:50 +01:00
|
|
|
printf("storagetagcheck : %04x\n", cfg.storage_tag_check);
|
|
|
|
printf("storagetag : %"PRIx64"\n", (uint64_t)cfg.storage_tag);
|
2025-02-16 12:16:06 +01:00
|
|
|
printf("pif : %02x\n", pif);
|
|
|
|
printf("sts : %02x\n", sts);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
if (cfg.dry_run)
|
|
|
|
goto free_mbuffer;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_io_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.slba = cfg.start_block,
|
|
|
|
.nlb = cfg.block_count,
|
|
|
|
.control = control,
|
|
|
|
.dsm = cfg.dsmgmt,
|
2025-02-16 12:16:06 +01:00
|
|
|
.sts = sts,
|
|
|
|
.pif = pif,
|
2025-02-16 12:15:30 +01:00
|
|
|
.dspec = cfg.dspec,
|
2025-02-16 12:16:06 +01:00
|
|
|
.reftag_u64 = cfg.ref_tag,
|
2025-02-16 12:15:30 +01:00
|
|
|
.apptag = cfg.app_tag,
|
|
|
|
.appmask = cfg.app_tag_mask,
|
|
|
|
.storage_tag = cfg.storage_tag,
|
|
|
|
.data_len = buffer_size,
|
|
|
|
.data = buffer,
|
|
|
|
.metadata_len = cfg.metadata_size,
|
|
|
|
.metadata = mbuffer,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
2025-02-16 11:09:01 +01:00
|
|
|
gettimeofday(&start_time, NULL);
|
2025-02-16 12:16:06 +01:00
|
|
|
err = nvme_io(&args, opcode);
|
2025-02-16 11:09:01 +01:00
|
|
|
gettimeofday(&end_time, NULL);
|
|
|
|
if (cfg.latency)
|
|
|
|
printf(" latency: %s: %llu us\n",
|
|
|
|
command, elapsed_utime(start_time, end_time));
|
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "submit-io: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else {
|
|
|
|
if (!(opcode & 1) && write(dfd, (void *)buffer, cfg.data_size) < 0) {
|
|
|
|
fprintf(stderr, "write: %s: failed to write buffer to output file\n",
|
|
|
|
strerror(errno));
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
} else if (!(opcode & 1) && cfg.metadata_size &&
|
2025-02-16 11:30:13 +01:00
|
|
|
write(mfd, (void *)mbuffer, mbuffer_size) < 0) {
|
2025-02-16 11:09:01 +01:00
|
|
|
fprintf(stderr, "write: %s: failed to write meta-data buffer to output file\n",
|
|
|
|
strerror(errno));
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
} else
|
|
|
|
fprintf(stderr, "%s: Success\n", command);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_mbuffer:
|
2025-02-16 11:30:13 +01:00
|
|
|
free(mbuffer);
|
2025-02-16 11:09:01 +01:00
|
|
|
free_buffer:
|
|
|
|
nvme_free(buffer, huge);
|
|
|
|
close_mfd:
|
|
|
|
if (strlen(cfg.metadata))
|
|
|
|
close(mfd);
|
|
|
|
close_dfd:
|
|
|
|
close(dfd);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int compare(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Compare specified logical blocks on "\
|
|
|
|
"device with specified data buffer; return failure if buffer "\
|
|
|
|
"and block(s) are dissimilar";
|
|
|
|
return submit_io(nvme_cmd_compare, "compare", desc, argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Copy specified logical blocks on the given "\
|
|
|
|
"device to specified data buffer (default buffer is stdout).";
|
|
|
|
return submit_io(nvme_cmd_read, "read", desc, argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int write_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Copy from provided data buffer (default "\
|
|
|
|
"buffer is stdin) to specified logical blocks on the given "\
|
|
|
|
"device.";
|
|
|
|
return submit_io(nvme_cmd_write, "write", desc, argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int verify_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
__u16 control = 0;
|
2025-02-16 12:16:06 +01:00
|
|
|
__u8 lba_index, sts = 0, pif = 0;
|
|
|
|
struct nvme_id_ns ns;
|
|
|
|
struct nvme_nvm_id_ns nvm_ns;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err;
|
2025-02-16 12:16:06 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *desc = "Verify specified logical blocks on the given device.";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *start_block = "64-bit LBA of first block to access";
|
|
|
|
const char *block_count = "number of blocks (zeroes based) on device to access";
|
|
|
|
const char *limited_retry = "limit media access attempts";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *force_unit_access = "force device to commit cached data before performing the verify operation";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *prinfo = "PI and check field";
|
2025-02-16 12:16:06 +01:00
|
|
|
const char *ref_tag = "reference tag for end-to-end PI";
|
|
|
|
const char *app_tag_mask = "app tag mask for end-to-end PI";
|
|
|
|
const char *app_tag = "app tag for end-to-end PI";
|
|
|
|
const char *storage_tag = "storage tag for end-to-end PI";
|
2025-02-16 12:10:50 +01:00
|
|
|
const char *storage_tag_check = "This bit specifies the Storage Tag field shall "\
|
|
|
|
"be checked as part of Verify operation";
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 start_block;
|
|
|
|
__u16 block_count;
|
|
|
|
bool limited_retry;
|
|
|
|
bool force_unit_access;
|
|
|
|
__u8 prinfo;
|
|
|
|
__u32 ref_tag;
|
|
|
|
__u16 app_tag;
|
|
|
|
__u16 app_tag_mask;
|
|
|
|
__u64 storage_tag;
|
|
|
|
bool storage_tag_check;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.start_block = 0,
|
|
|
|
.block_count = 0,
|
|
|
|
.limited_retry = false,
|
|
|
|
.force_unit_access = false,
|
|
|
|
.prinfo = 0,
|
|
|
|
.ref_tag = 0,
|
|
|
|
.app_tag = 0,
|
|
|
|
.app_tag_mask = 0,
|
|
|
|
.storage_tag = 0,
|
|
|
|
.storage_tag_check = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_SUFFIX("start-block", 's', &cfg.start_block, start_block),
|
|
|
|
OPT_SHRT("block-count", 'c', &cfg.block_count, block_count),
|
|
|
|
OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FLAG("force-unit-access", 'f', &cfg.force_unit_access, force_unit_access),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo),
|
2025-02-16 12:16:06 +01:00
|
|
|
OPT_SUFFIX("ref-tag", 'r', &cfg.ref_tag, ref_tag),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_SHRT("app-tag", 'a', &cfg.app_tag, app_tag),
|
|
|
|
OPT_SHRT("app-tag-mask", 'm', &cfg.app_tag_mask, app_tag_mask),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_SUFFIX("storage-tag", 'S', &cfg.storage_tag, storage_tag),
|
|
|
|
OPT_FLAG("storage-tag-check", 'C', &cfg.storage_tag_check, storage_tag_check),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:10:50 +01:00
|
|
|
goto ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
if (cfg.prinfo > 0xf) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
control |= (cfg.prinfo << 10);
|
|
|
|
if (cfg.limited_retry)
|
2025-02-16 12:15:30 +01:00
|
|
|
control |= NVME_IO_LR;
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.force_unit_access)
|
2025-02-16 12:15:30 +01:00
|
|
|
control |= NVME_IO_FUA;
|
2025-02-16 12:10:50 +01:00
|
|
|
if (cfg.storage_tag_check)
|
2025-02-16 12:16:06 +01:00
|
|
|
control |= NVME_IO_STC;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
if (!cfg.namespace_id) {
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (err < 0) {
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_cli_identify_ns(dev, cfg.namespace_id, &ns);
|
2025-02-16 12:16:06 +01:00
|
|
|
if (err) {
|
|
|
|
nvme_show_status(err);
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:16:06 +01:00
|
|
|
} else if (err < 0) {
|
|
|
|
fprintf(stderr, "identify namespace: %s\n", nvme_strerror(errno));
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:16:06 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_identify_ns_csi(dev_fd(dev), cfg.namespace_id, 0,
|
|
|
|
NVME_CSI_NVM, &nvm_ns);
|
2025-02-16 12:16:06 +01:00
|
|
|
if (!err) {
|
|
|
|
nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index);
|
|
|
|
sts = nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_STS_MASK;
|
|
|
|
pif = (nvm_ns.elbaf[lba_index] & NVME_NVM_ELBAF_PIF_MASK) >> 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (invalid_tags(cfg.storage_tag, cfg.ref_tag, sts, pif)) {
|
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:16:06 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_io_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.slba = cfg.start_block,
|
|
|
|
.nlb = cfg.block_count,
|
|
|
|
.control = control,
|
2025-02-16 12:16:06 +01:00
|
|
|
.reftag_u64 = cfg.ref_tag,
|
2025-02-16 12:15:30 +01:00
|
|
|
.apptag = cfg.app_tag,
|
|
|
|
.appmask = cfg.app_tag_mask,
|
2025-02-16 12:16:06 +01:00
|
|
|
.sts = sts,
|
|
|
|
.pif = pif,
|
2025-02-16 12:15:30 +01:00
|
|
|
.storage_tag = cfg.storage_tag,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_verify(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "verify: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("NVME Verify Success\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:10:50 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sec_recv(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Obtain results of one or more "\
|
|
|
|
"previously submitted security-sends. Results, and association "\
|
|
|
|
"between Security Send and Receive, depend on the security "\
|
|
|
|
"protocol field as they are defined by the security protocol "\
|
|
|
|
"used. A Security Receive must follow a Security Send made with "\
|
|
|
|
"the same security protocol.";
|
|
|
|
const char *size = "size of buffer (prints to stdout on success)";
|
|
|
|
const char *secp = "security protocol (cf. SPC-4)";
|
|
|
|
const char *spsp = "security-protocol-specific (cf. SPC-4)";
|
|
|
|
const char *al = "allocation length (cf. SPC-4)";
|
|
|
|
const char *raw = "dump output in binary format";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *nssf = "NVMe Security Specific Field";
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
void *sec_buf = NULL;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u32 size;
|
|
|
|
__u8 nssf;
|
|
|
|
__u8 secp;
|
|
|
|
__u16 spsp;
|
|
|
|
__u32 al;
|
|
|
|
bool raw_binary;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.size = 0,
|
|
|
|
.nssf = 0,
|
|
|
|
.secp = 0,
|
|
|
|
.spsp = 0,
|
|
|
|
.al = 0,
|
|
|
|
.raw_binary = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_UINT("size", 'x', &cfg.size, size),
|
|
|
|
OPT_BYTE("nssf", 'N', &cfg.nssf, nssf),
|
|
|
|
OPT_BYTE("secp", 'p', &cfg.secp, secp),
|
|
|
|
OPT_SHRT("spsp", 's', &cfg.spsp, spsp),
|
|
|
|
OPT_UINT("al", 't', &cfg.al, al),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.size) {
|
|
|
|
if (posix_memalign(&sec_buf, getpagesize(), cfg.size)) {
|
|
|
|
fprintf(stderr, "No memory for security size:%d\n",
|
|
|
|
cfg.size);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_security_receive_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.nssf = cfg.nssf,
|
|
|
|
.spsp0 = cfg.spsp & 0xff,
|
|
|
|
.spsp1 = cfg.spsp >> 8,
|
|
|
|
.secp = cfg.secp,
|
|
|
|
.al = cfg.al,
|
|
|
|
.data_len = cfg.size,
|
|
|
|
.data = sec_buf,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_security_receive(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "security receive: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err != 0)
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_show_status(err);
|
2025-02-16 11:09:01 +01:00
|
|
|
else {
|
2025-02-16 11:30:13 +01:00
|
|
|
printf("NVME Security Receive Command Success\n");
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!cfg.raw_binary) {
|
|
|
|
d(sec_buf, cfg.size, 16, 1);
|
|
|
|
} else if (cfg.size)
|
|
|
|
d_raw((unsigned char *)sec_buf, cfg.size);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(sec_buf);
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_lba_status(int argc, char **argv, struct command *cmd,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Information about potentially unrecoverable LBAs.";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *namespace_id = "Desired Namespace";
|
2025-02-16 11:09:01 +01:00
|
|
|
const char *slba = "Starting LBA(SLBA) in 64-bit address of the first"\
|
|
|
|
" logical block addressed by this command";
|
|
|
|
const char *mndw = "Maximum Number of Dwords(MNDW) specifies maximum"\
|
|
|
|
" number of dwords to return";
|
2025-02-16 12:10:50 +01:00
|
|
|
const char *atype = "Action Type(ATYPE) specifies the mechanism"\
|
2025-02-16 11:09:01 +01:00
|
|
|
" the controller uses in determining the LBA"\
|
|
|
|
" Status Descriptors to return.";
|
|
|
|
const char *rl = "Range Length(RL) specifies the length of the range"\
|
|
|
|
" of contiguous LBAs beginning at SLBA";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *timeout = "timeout value, in milliseconds";
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
enum nvme_print_flags flags;
|
|
|
|
unsigned long buf_len;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
void *buf;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u64 slba;
|
|
|
|
__u32 mndw;
|
|
|
|
__u8 atype;
|
|
|
|
__u16 rl;
|
|
|
|
__u32 timeout;
|
|
|
|
char *output_format;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 0,
|
|
|
|
.slba = 0,
|
|
|
|
.mndw = 0,
|
|
|
|
.atype = 0,
|
|
|
|
.rl = 0,
|
|
|
|
.timeout = 0,
|
|
|
|
.output_format = "normal",
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_SUFFIX("start-lba", 's', &cfg.slba, slba),
|
|
|
|
OPT_UINT("max-dw", 'm', &cfg.mndw, mndw),
|
|
|
|
OPT_BYTE("action", 'a', &cfg.atype, atype),
|
|
|
|
OPT_SHRT("range-len", 'l', &cfg.rl, rl),
|
2025-02-16 11:30:13 +01:00
|
|
|
OPT_UINT("timeout", 't', &cfg.timeout, timeout),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
if (!cfg.atype) {
|
|
|
|
fprintf(stderr, "action type (--action) has to be given\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
buf_len = (cfg.mndw + 1) * 4;
|
|
|
|
buf = calloc(1, buf_len);
|
|
|
|
if (!buf) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_get_lba_status_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.slba = cfg.slba,
|
|
|
|
.mndw = cfg.mndw,
|
|
|
|
.rl = cfg.rl,
|
|
|
|
.atype = cfg.atype,
|
|
|
|
.lbas = buf,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_get_lba_status(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_show_lba_status(buf, buf_len, flags);
|
|
|
|
else if (err > 0)
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_show_status(err);
|
2025-02-16 11:09:01 +01:00
|
|
|
else
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "get lba status: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
free(buf);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
err:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
static int capacity_mgmt(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Host software uses the Capacity Management command to "\
|
|
|
|
"configure Endurance Groups and NVM Sets in an NVM subsystem by either " \
|
|
|
|
"selecting one of a set of supported configurations or by specifying the "\
|
|
|
|
"capacity of the Endurance Group or NVM Set to be created";
|
|
|
|
const char *operation = "Operation to be performed by the controller";
|
|
|
|
const char *element_id = "Value specific to the value of the Operation field.";
|
|
|
|
const char *cap_lower = "Least significant 32 bits of the capacity in bytes of the "\
|
|
|
|
"Endurance Group or NVM Set to be created";
|
|
|
|
const char *cap_upper = "Most significant 32 bits of the capacity in bytes of the "\
|
|
|
|
"Endurance Group or NVM Set to be created";
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:10:50 +01:00
|
|
|
__u32 result;
|
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 operation;
|
|
|
|
__u16 element_id;
|
|
|
|
__u32 dw11;
|
|
|
|
__u32 dw12;
|
2025-02-16 12:10:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.operation = 0xff,
|
|
|
|
.element_id = 0xffff,
|
|
|
|
.dw11 = 0,
|
|
|
|
.dw12 = 0,
|
2025-02-16 12:10:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_BYTE("operation", 'o', &cfg.operation, operation),
|
|
|
|
OPT_SHRT("element-id", 'i', &cfg.element_id, element_id),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_UINT("cap-lower", 'l', &cfg.dw11, cap_lower),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_UINT("cap-upper", 'u', &cfg.dw12, cap_upper),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:10:50 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.operation > 0xf) {
|
|
|
|
fprintf(stderr, "invalid operation field: %u\n", cfg.operation);
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_capacity_mgmt_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.op = cfg.operation,
|
|
|
|
.element_id = cfg.element_id,
|
|
|
|
.cdw11 = cfg.dw11,
|
|
|
|
.cdw12 = cfg.dw12,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = &result,
|
|
|
|
};
|
|
|
|
err = nvme_capacity_mgmt(&args);
|
2025-02-16 12:10:50 +01:00
|
|
|
if (!err) {
|
|
|
|
printf("Capacity Management Command is Success\n");
|
|
|
|
if (cfg.operation == 1) {
|
|
|
|
printf("Created Element Identifier for Endurance Group is: %u\n", result);
|
|
|
|
} else if (cfg.operation == 3) {
|
|
|
|
printf("Created Element Identifier for NVM Set is: %u\n", result);
|
|
|
|
}
|
|
|
|
} else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "capacity management: %s\n", nvme_strerror(errno));
|
2025-02-16 12:10:50 +01:00
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:10:50 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int dir_receive(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Read directive parameters of the "\
|
|
|
|
"specified directive type.";
|
|
|
|
const char *raw = "show directive in binary format";
|
|
|
|
const char *namespace_id = "identifier of desired namespace";
|
|
|
|
const char *data_len = "buffer len (if) data is returned";
|
|
|
|
const char *dtype = "directive type";
|
|
|
|
const char *dspec = "directive specification associated with directive type";
|
|
|
|
const char *doper = "directive operation";
|
|
|
|
const char *nsr = "namespace stream requested";
|
|
|
|
const char *human_readable = "show directive in readable format";
|
|
|
|
|
|
|
|
enum nvme_print_flags flags = NORMAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
__u32 result;
|
|
|
|
__u32 dw12 = 0;
|
|
|
|
void *buf = NULL;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u32 namespace_id;
|
|
|
|
__u32 data_len;
|
|
|
|
bool raw_binary;
|
|
|
|
__u8 dtype;
|
|
|
|
__u16 dspec;
|
|
|
|
__u8 doper;
|
|
|
|
__u16 nsr; /* dw12 for NVME_DIR_ST_RCVOP_STATUS */
|
|
|
|
bool human_readable;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.namespace_id = 1,
|
|
|
|
.data_len = 0,
|
|
|
|
.raw_binary = false,
|
|
|
|
.dtype = 0,
|
|
|
|
.dspec = 0,
|
|
|
|
.doper = 0,
|
|
|
|
.nsr = 0,
|
|
|
|
.human_readable = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
|
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
|
|
|
|
OPT_BYTE("dir-type", 'D', &cfg.dtype, dtype),
|
|
|
|
OPT_SHRT("dir-spec", 'S', &cfg.dspec, dspec),
|
|
|
|
OPT_BYTE("dir-oper", 'O', &cfg.doper, doper),
|
|
|
|
OPT_SHRT("req-resource", 'r', &cfg.nsr, nsr),
|
|
|
|
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
|
|
|
if (cfg.human_readable)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
if (cfg.raw_binary)
|
|
|
|
flags = BINARY;
|
|
|
|
|
|
|
|
switch (cfg.dtype) {
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_DTYPE_IDENTIFY:
|
2025-02-16 11:09:01 +01:00
|
|
|
switch (cfg.doper) {
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM:
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!cfg.data_len)
|
|
|
|
cfg.data_len = 4096;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "invalid directive operations for Identify Directives\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
break;
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_DTYPE_STREAMS:
|
2025-02-16 11:09:01 +01:00
|
|
|
switch (cfg.doper) {
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM:
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!cfg.data_len)
|
|
|
|
cfg.data_len = 32;
|
|
|
|
break;
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS:
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!cfg.data_len)
|
|
|
|
cfg.data_len = 128 * 1024;
|
|
|
|
break;
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE:
|
2025-02-16 11:09:01 +01:00
|
|
|
dw12 = cfg.nsr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "invalid directive operations for Streams Directives\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "invalid directive type\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.data_len) {
|
|
|
|
if (posix_memalign(&buf, getpagesize(), cfg.data_len)) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
memset(buf, 0, cfg.data_len);
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_directive_recv_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.nsid = cfg.namespace_id,
|
|
|
|
.dspec = cfg.dspec,
|
|
|
|
.doper = cfg.doper,
|
|
|
|
.dtype = cfg.dtype,
|
|
|
|
.cdw12 = dw12,
|
|
|
|
.data_len = cfg.data_len,
|
|
|
|
.data = buf,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = &result,
|
|
|
|
};
|
|
|
|
err = nvme_directive_recv(&args);
|
2025-02-16 11:09:01 +01:00
|
|
|
if (!err)
|
|
|
|
nvme_directive_show(cfg.dtype, cfg.doper, cfg.dspec,
|
|
|
|
cfg.namespace_id, result, buf, cfg.data_len,
|
|
|
|
flags);
|
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "dir-receive: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
free(buf);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
/* rpmb_cmd_option is defined in nvme-rpmb.c */
|
|
|
|
extern int rpmb_cmd_option(int, char **, struct command *, struct plugin *);
|
|
|
|
static int rpmb_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 11:30:13 +01:00
|
|
|
return rpmb_cmd_option(argc, argv, cmd, plugin);
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:13:36 +01:00
|
|
|
static int lockdown_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "The Lockdown command is used to control the "\
|
|
|
|
"Command and Feature Lockdown capability which configures the "\
|
|
|
|
"prohibition or allowance of execution of the specified command "\
|
|
|
|
"or Set Features command targeting a specific Feature Identifier.";
|
|
|
|
const char *ofi_desc = "Opcode or Feature Identifier(OFI) "\
|
|
|
|
"specifies the command opcode or Set Features Feature Identifier "\
|
|
|
|
"identified by the Scope field.";
|
|
|
|
const char *ifc_desc = "[0-3] Interface (INF) field identifies the "\
|
|
|
|
"interfaces affected by this command.";
|
|
|
|
const char *prhbt_desc = "[0-1]Prohibit(PRHBT) bit specifies whether "\
|
|
|
|
"to prohibit or allow the command opcode or Set Features Feature "\
|
|
|
|
"Identifier specified by this command.";
|
|
|
|
const char *scp_desc = "[0-15]Scope(SCP) field specifies the contents "\
|
|
|
|
"of the Opcode or Feature Identifier field.";
|
|
|
|
const char *uuid_desc = "UUID Index - If this field is set to a non-zero "\
|
|
|
|
"value, then the value of this field is the index of a UUID in the UUID "\
|
|
|
|
"List that is used by the command.If this field is cleared to 0h,"\
|
|
|
|
"then no UUID index is specified";
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
struct nvme_dev *dev;
|
|
|
|
int err = -1;
|
2025-02-16 12:13:36 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 ofi;
|
|
|
|
__u8 ifc;
|
|
|
|
__u8 prhbt;
|
|
|
|
__u8 scp;
|
|
|
|
__u8 uuid;
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.ofi = 0,
|
|
|
|
.ifc = 0,
|
|
|
|
.prhbt = 0,
|
|
|
|
.scp = 0,
|
|
|
|
.uuid = 0,
|
2025-02-16 12:13:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_BYTE("ofi", 'o', &cfg.ofi, ofi_desc),
|
2025-02-16 12:13:36 +01:00
|
|
|
OPT_BYTE("ifc", 'f', &cfg.ifc, ifc_desc),
|
|
|
|
OPT_BYTE("prhbt", 'p', &cfg.prhbt, prhbt_desc),
|
|
|
|
OPT_BYTE("scp", 's', &cfg.scp, scp_desc),
|
|
|
|
OPT_BYTE("uuid", 'U', &cfg.uuid, uuid_desc),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 12:13:36 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
/* check for input argument limit */
|
2025-02-16 12:13:36 +01:00
|
|
|
if (cfg.ifc > 3) {
|
|
|
|
fprintf(stderr, "invalid interface settings:%d\n", cfg.ifc);
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
if (cfg.prhbt > 1) {
|
|
|
|
fprintf(stderr, "invalid prohibit settings:%d\n", cfg.prhbt);
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
if (cfg.scp > 15) {
|
|
|
|
fprintf(stderr, "invalid scope settings:%d\n", cfg.scp);
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
if (cfg.uuid > 127) {
|
|
|
|
fprintf(stderr, "invalid UUID index settings:%d\n", cfg.uuid);
|
|
|
|
err = -1;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_lockdown_args args = {
|
|
|
|
.args_size = sizeof(args),
|
2025-02-16 12:17:56 +01:00
|
|
|
.fd = dev_fd(dev),
|
2025-02-16 12:15:30 +01:00
|
|
|
.scp = cfg.scp,
|
|
|
|
.prhbt = cfg.prhbt,
|
|
|
|
.ifc = cfg.ifc,
|
|
|
|
.ofi = cfg.ofi,
|
|
|
|
.uuidx = cfg.uuid,
|
|
|
|
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
|
|
|
|
.result = NULL,
|
|
|
|
};
|
|
|
|
err = nvme_lockdown(&args);
|
2025-02-16 12:13:36 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "lockdown: %s\n", nvme_strerror(errno));
|
2025-02-16 12:13:36 +01:00
|
|
|
else if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
printf("Lockdown Command is Successful\n");
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 12:13:36 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 12:13:36 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static int passthru(int argc, char **argv, bool admin,
|
|
|
|
const char *desc, struct command *cmd)
|
2025-02-16 11:30:13 +01:00
|
|
|
{
|
|
|
|
const char *opcode = "opcode (required)";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *cflags = "command flags";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *rsvd = "value for reserved field";
|
|
|
|
const char *namespace_id = "desired namespace";
|
|
|
|
const char *data_len = "data I/O length (bytes)";
|
|
|
|
const char *metadata_len = "metadata seg. length (bytes)";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *metadata = "metadata input or output file";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *timeout = "timeout value, in milliseconds";
|
|
|
|
const char *cdw2 = "command dword 2 value";
|
|
|
|
const char *cdw3 = "command dword 3 value";
|
|
|
|
const char *cdw10 = "command dword 10 value";
|
|
|
|
const char *cdw11 = "command dword 11 value";
|
|
|
|
const char *cdw12 = "command dword 12 value";
|
|
|
|
const char *cdw13 = "command dword 13 value";
|
|
|
|
const char *cdw14 = "command dword 14 value";
|
|
|
|
const char *cdw15 = "command dword 15 value";
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *input = "data input or output file";
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *raw_binary = "dump output in binary format";
|
|
|
|
const char *show = "print command before sending";
|
|
|
|
const char *dry = "show command instead of sending";
|
|
|
|
const char *re = "set dataflow direction to receive";
|
|
|
|
const char *wr = "set dataflow direction to send";
|
|
|
|
const char *prefill = "prefill buffers with known byte-value, default 0";
|
2025-02-16 12:10:50 +01:00
|
|
|
const char *latency = "output latency statistics";
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
int flags;
|
|
|
|
int mode = S_IRUSR | S_IWUSR |S_IRGRP | S_IWGRP| S_IROTH;
|
|
|
|
void *data = NULL, *mdata = NULL;
|
2025-02-16 12:17:56 +01:00
|
|
|
int err = 0, dfd, mfd;
|
|
|
|
struct nvme_dev *dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
__u32 result;
|
2025-02-16 12:15:30 +01:00
|
|
|
bool huge = false;
|
2025-02-16 11:30:13 +01:00
|
|
|
const char *cmd_name = NULL;
|
2025-02-16 12:10:50 +01:00
|
|
|
struct timeval start_time, end_time;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
struct config {
|
2025-02-16 12:15:30 +01:00
|
|
|
__u8 opcode;
|
|
|
|
__u8 flags;
|
|
|
|
__u16 rsvd;
|
|
|
|
__u32 namespace_id;
|
|
|
|
__u32 data_len;
|
|
|
|
__u32 metadata_len;
|
|
|
|
__u32 timeout;
|
|
|
|
__u32 cdw2;
|
|
|
|
__u32 cdw3;
|
|
|
|
__u32 cdw10;
|
|
|
|
__u32 cdw11;
|
|
|
|
__u32 cdw12;
|
|
|
|
__u32 cdw13;
|
|
|
|
__u32 cdw14;
|
|
|
|
__u32 cdw15;
|
|
|
|
char *input_file;
|
|
|
|
char *metadata;
|
|
|
|
bool raw_binary;
|
|
|
|
bool show_command;
|
|
|
|
bool dry_run;
|
|
|
|
bool read;
|
|
|
|
bool write;
|
|
|
|
__u8 prefill;
|
|
|
|
bool latency;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
2025-02-16 12:15:30 +01:00
|
|
|
.opcode = 0,
|
|
|
|
.flags = 0,
|
|
|
|
.prefill = 0,
|
|
|
|
.rsvd = 0,
|
|
|
|
.namespace_id = 0,
|
|
|
|
.data_len = 0,
|
|
|
|
.metadata_len = 0,
|
|
|
|
.timeout = 0,
|
|
|
|
.cdw2 = 0,
|
|
|
|
.cdw3 = 0,
|
|
|
|
.cdw10 = 0,
|
|
|
|
.cdw11 = 0,
|
|
|
|
.cdw12 = 0,
|
|
|
|
.cdw13 = 0,
|
|
|
|
.cdw14 = 0,
|
|
|
|
.cdw15 = 0,
|
|
|
|
.input_file = "",
|
|
|
|
.metadata = "",
|
|
|
|
.raw_binary = false,
|
|
|
|
.show_command = false,
|
|
|
|
.dry_run = false,
|
|
|
|
.read = false,
|
|
|
|
.write = false,
|
|
|
|
.latency = false,
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_BYTE("opcode", 'o', &cfg.opcode, opcode),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_BYTE("flags", 'f', &cfg.flags, cflags),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_BYTE("prefill", 'p', &cfg.prefill, prefill),
|
|
|
|
OPT_SHRT("rsvd", 'R', &cfg.rsvd, rsvd),
|
|
|
|
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
|
|
|
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
|
|
|
|
OPT_UINT("metadata-len", 'm', &cfg.metadata_len, metadata_len),
|
|
|
|
OPT_UINT("timeout", 't', &cfg.timeout, timeout),
|
|
|
|
OPT_UINT("cdw2", '2', &cfg.cdw2, cdw2),
|
|
|
|
OPT_UINT("cdw3", '3', &cfg.cdw3, cdw3),
|
|
|
|
OPT_UINT("cdw10", '4', &cfg.cdw10, cdw10),
|
|
|
|
OPT_UINT("cdw11", '5', &cfg.cdw11, cdw11),
|
|
|
|
OPT_UINT("cdw12", '6', &cfg.cdw12, cdw12),
|
|
|
|
OPT_UINT("cdw13", '7', &cfg.cdw13, cdw13),
|
|
|
|
OPT_UINT("cdw14", '8', &cfg.cdw14, cdw14),
|
|
|
|
OPT_UINT("cdw15", '9', &cfg.cdw15, cdw15),
|
|
|
|
OPT_FILE("input-file", 'i', &cfg.input_file, input),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_FILE("metadata", 'M', &cfg.metadata, metadata),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_binary),
|
|
|
|
OPT_FLAG("show-command", 's', &cfg.show_command, show),
|
|
|
|
OPT_FLAG("dry-run", 'd', &cfg.dry_run, dry),
|
|
|
|
OPT_FLAG("read", 'r', &cfg.read, re),
|
|
|
|
OPT_FLAG("write", 'w', &cfg.write, wr),
|
2025-02-16 12:10:50 +01:00
|
|
|
OPT_FLAG("latency", 'T', &cfg.latency, latency),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
|
|
|
if (err)
|
2025-02-16 11:09:01 +01:00
|
|
|
goto ret;
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
if (cfg.opcode & 0x01)
|
|
|
|
cfg.write = true;
|
|
|
|
|
|
|
|
if (cfg.opcode & 0x02)
|
|
|
|
cfg.read = true;
|
|
|
|
|
|
|
|
if (cfg.write) {
|
|
|
|
flags = O_RDONLY;
|
|
|
|
dfd = mfd = STDIN_FILENO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.read) {
|
|
|
|
flags = O_WRONLY | O_CREAT;
|
|
|
|
dfd = mfd = STDOUT_FILENO;
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
if (strlen(cfg.input_file)) {
|
2025-02-16 12:15:30 +01:00
|
|
|
dfd = open(cfg.input_file, flags, mode);
|
|
|
|
if (dfd < 0) {
|
2025-02-16 11:09:01 +01:00
|
|
|
perror(cfg.input_file);
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 12:17:56 +01:00
|
|
|
goto close_dev;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.metadata && strlen(cfg.metadata)) {
|
|
|
|
mfd = open(cfg.metadata, flags, mode);
|
|
|
|
if (mfd < 0) {
|
|
|
|
perror(cfg.metadata);
|
|
|
|
err = -EINVAL;
|
|
|
|
goto close_dfd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.metadata_len) {
|
2025-02-16 12:15:30 +01:00
|
|
|
mdata = malloc(cfg.metadata_len);
|
|
|
|
if (!mdata) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto close_mfd;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (cfg.write) {
|
|
|
|
if (read(mfd, mdata, cfg.metadata_len) < 0) {
|
|
|
|
err = -errno;
|
|
|
|
perror("failed to read metadata write buffer");
|
|
|
|
goto free_metadata;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
memset(mdata, cfg.prefill, cfg.metadata_len);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (cfg.data_len) {
|
|
|
|
data = nvme_alloc(cfg.data_len, &huge);
|
|
|
|
if (!data) {
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -ENOMEM;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto free_metadata;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(data, cfg.prefill, cfg.data_len);
|
|
|
|
if (!cfg.read && !cfg.write) {
|
|
|
|
fprintf(stderr, "data direction not given\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
err = -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
goto free_data;
|
|
|
|
} else if (cfg.write) {
|
2025-02-16 12:15:30 +01:00
|
|
|
if (read(dfd, data, cfg.data_len) < 0) {
|
2025-02-16 11:09:01 +01:00
|
|
|
err = -errno;
|
|
|
|
fprintf(stderr, "failed to read write buffer "
|
|
|
|
"%s\n", strerror(errno));
|
|
|
|
goto free_data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.show_command || cfg.dry_run) {
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("opcode : %02x\n", cfg.opcode);
|
|
|
|
printf("flags : %02x\n", cfg.flags);
|
|
|
|
printf("rsvd1 : %04x\n", cfg.rsvd);
|
|
|
|
printf("nsid : %08x\n", cfg.namespace_id);
|
|
|
|
printf("cdw2 : %08x\n", cfg.cdw2);
|
|
|
|
printf("cdw3 : %08x\n", cfg.cdw3);
|
|
|
|
printf("data_len : %08x\n", cfg.data_len);
|
|
|
|
printf("metadata_len : %08x\n", cfg.metadata_len);
|
|
|
|
printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)data);
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)mdata);
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("cdw10 : %08x\n", cfg.cdw10);
|
|
|
|
printf("cdw11 : %08x\n", cfg.cdw11);
|
|
|
|
printf("cdw12 : %08x\n", cfg.cdw12);
|
|
|
|
printf("cdw13 : %08x\n", cfg.cdw13);
|
|
|
|
printf("cdw14 : %08x\n", cfg.cdw14);
|
|
|
|
printf("cdw15 : %08x\n", cfg.cdw15);
|
|
|
|
printf("timeout_ms : %08x\n", cfg.timeout);
|
|
|
|
}
|
|
|
|
if (cfg.dry_run)
|
|
|
|
goto free_data;
|
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
gettimeofday(&start_time, NULL);
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (admin)
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_admin_passthru(dev_fd(dev), cfg.opcode, cfg.flags,
|
|
|
|
cfg.rsvd,
|
|
|
|
cfg.namespace_id, cfg.cdw2,
|
|
|
|
cfg.cdw3, cfg.cdw10,
|
|
|
|
cfg.cdw11, cfg.cdw12, cfg.cdw13,
|
|
|
|
cfg.cdw14,
|
|
|
|
cfg.cdw15, cfg.data_len, data,
|
|
|
|
cfg.metadata_len,
|
|
|
|
mdata, cfg.timeout, &result);
|
2025-02-16 12:15:30 +01:00
|
|
|
else
|
2025-02-16 12:17:56 +01:00
|
|
|
err = nvme_io_passthru(dev_fd(dev), cfg.opcode, cfg.flags,
|
|
|
|
cfg.rsvd,
|
|
|
|
cfg.namespace_id, cfg.cdw2, cfg.cdw3,
|
|
|
|
cfg.cdw10,
|
|
|
|
cfg.cdw11, cfg.cdw12, cfg.cdw13,
|
|
|
|
cfg.cdw14,
|
|
|
|
cfg.cdw15, cfg.data_len, data,
|
|
|
|
cfg.metadata_len,
|
|
|
|
mdata, cfg.timeout, &result);
|
2025-02-16 12:10:50 +01:00
|
|
|
|
|
|
|
gettimeofday(&end_time, NULL);
|
2025-02-16 12:15:30 +01:00
|
|
|
cmd_name = nvme_cmd_to_string(admin, cfg.opcode);
|
2025-02-16 12:10:50 +01:00
|
|
|
if (cfg.latency)
|
|
|
|
printf("%s Command %s latency: %llu us\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
admin ? "Admin": "IO",
|
2025-02-16 12:10:50 +01:00
|
|
|
strcmp(cmd_name, "Unknown") ? cmd_name: "Vendor Specific",
|
|
|
|
elapsed_utime(start_time, end_time));
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (err < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
fprintf(stderr, "passthru: %s\n", nvme_strerror(errno));
|
2025-02-16 11:09:01 +01:00
|
|
|
else if (err)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else {
|
2025-02-16 11:30:13 +01:00
|
|
|
fprintf(stderr, "%s Command %s is Success and result: 0x%08x\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
admin ? "Admin": "IO",
|
2025-02-16 11:30:13 +01:00
|
|
|
strcmp(cmd_name, "Unknown") ? cmd_name: "Vendor Specific",
|
|
|
|
result);
|
2025-02-16 12:16:06 +01:00
|
|
|
if (cfg.read && strlen(cfg.input_file)) {
|
2025-02-16 12:15:30 +01:00
|
|
|
if (write(dfd, (void *)data, cfg.data_len) < 0)
|
|
|
|
perror("failed to write data buffer");
|
|
|
|
if (cfg.metadata_len && cfg.metadata)
|
|
|
|
if (write(mfd, (void *)mdata, cfg.metadata_len) < 0)
|
|
|
|
perror("failed to write metadata buffer");
|
|
|
|
} else if (!cfg.raw_binary) {
|
2025-02-16 11:09:01 +01:00
|
|
|
if (data && cfg.read && !err)
|
|
|
|
d((unsigned char *)data, cfg.data_len, 16, 1);
|
|
|
|
} else if (data && cfg.read)
|
|
|
|
d_raw((unsigned char *)data, cfg.data_len);
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
free_metadata:
|
|
|
|
free(mdata);
|
2025-02-16 11:09:01 +01:00
|
|
|
free_data:
|
2025-02-16 11:30:13 +01:00
|
|
|
nvme_free(data, huge);
|
2025-02-16 12:15:30 +01:00
|
|
|
close_dfd:
|
2025-02-16 11:09:01 +01:00
|
|
|
if (strlen(cfg.input_file))
|
2025-02-16 12:15:30 +01:00
|
|
|
close(dfd);
|
|
|
|
close_mfd:
|
2025-02-16 12:17:56 +01:00
|
|
|
if (cfg.metadata && strlen(cfg.metadata))
|
2025-02-16 12:15:30 +01:00
|
|
|
close(mfd);
|
2025-02-16 12:17:56 +01:00
|
|
|
close_dev:
|
|
|
|
dev_close(dev);
|
2025-02-16 11:09:01 +01:00
|
|
|
ret:
|
2025-02-16 12:15:30 +01:00
|
|
|
return err;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int io_passthru(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send a user-defined IO command to the specified "\
|
|
|
|
"device via IOCTL passthrough, return results.";
|
2025-02-16 12:15:30 +01:00
|
|
|
return passthru(argc, argv, false, desc, cmd);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int admin_passthru(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send a user-defined Admin command to the specified "\
|
|
|
|
"device via IOCTL passthrough, return results.";
|
2025-02-16 12:15:30 +01:00
|
|
|
return passthru(argc, argv, true, desc, cmd);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gen_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
char *hostnqn;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
hostnqn = nvmf_hostnqn_generate();
|
|
|
|
if (!hostnqn) {
|
2025-02-16 11:30:13 +01:00
|
|
|
fprintf(stderr, "\"%s\" not supported. Install lib uuid and rebuild.\n",
|
|
|
|
command->name);
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("%s\n", hostnqn);
|
|
|
|
free(hostnqn);
|
2025-02-16 11:09:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int show_hostnqn_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
char *hostnqn;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
hostnqn = nvmf_hostnqn_from_file();
|
|
|
|
if (!hostnqn)
|
|
|
|
hostnqn = nvmf_hostnqn_generate();
|
|
|
|
|
|
|
|
if (!hostnqn) {
|
2025-02-16 11:09:01 +01:00
|
|
|
fprintf(stderr, "hostnqn is not available -- use nvme gen-hostnqn\n");
|
2025-02-16 12:15:30 +01:00
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stdout, "%s\n", hostnqn);
|
|
|
|
free(hostnqn);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int gen_dhchap_key(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Generate a DH-HMAC-CHAP host key usable "\
|
|
|
|
"for NVMe In-Band Authentication.";
|
|
|
|
const char *secret = "Optional secret (in hexadecimal characters) "\
|
|
|
|
"to be used to initialize the host key.";
|
|
|
|
const char *key_len = "Length of the resulting key "\
|
|
|
|
"(32, 48, or 64 bytes).";
|
|
|
|
const char *hmac = "HMAC function to use for key transformation "\
|
|
|
|
"(0 = none, 1 = SHA-256, 2 = SHA-384, 3 = SHA-512).";
|
|
|
|
const char *nqn = "Host NQN to use for key transformation.";
|
|
|
|
|
|
|
|
unsigned char *raw_secret;
|
|
|
|
unsigned char key[68];
|
|
|
|
char encoded_key[128];
|
|
|
|
unsigned long crc = crc32(0L, NULL, 0);
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
struct config {
|
|
|
|
char *secret;
|
|
|
|
unsigned int key_len;
|
|
|
|
char *nqn;
|
|
|
|
unsigned int hmac;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.secret = NULL,
|
|
|
|
.key_len = 0,
|
|
|
|
.nqn = NULL,
|
|
|
|
.hmac = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_STR("secret", 's', &cfg.secret, secret),
|
|
|
|
OPT_UINT("key-length", 'l', &cfg.key_len, key_len),
|
|
|
|
OPT_STR("nqn", 'n', &cfg.nqn, nqn),
|
|
|
|
OPT_UINT("hmac", 'm', &cfg.hmac, hmac),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (cfg.hmac > 3) {
|
|
|
|
fprintf(stderr, "Invalid HMAC identifier %u\n", cfg.hmac);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (cfg.hmac > 0) {
|
|
|
|
switch (cfg.hmac) {
|
|
|
|
case 1:
|
|
|
|
if (!cfg.key_len)
|
|
|
|
cfg.key_len = 32;
|
|
|
|
else if (cfg.key_len != 32) {
|
|
|
|
fprintf(stderr, "Invalid key length %d for SHA(256)\n",
|
|
|
|
cfg.key_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (!cfg.key_len)
|
|
|
|
cfg.key_len = 48;
|
|
|
|
else if (cfg.key_len != 48) {
|
|
|
|
fprintf(stderr, "Invalid key length %d for SHA(384)\n",
|
|
|
|
cfg.key_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (!cfg.key_len)
|
|
|
|
cfg.key_len = 64;
|
|
|
|
else if (cfg.key_len != 64) {
|
|
|
|
fprintf(stderr, "Invalid key length %d for SHA(512)\n",
|
|
|
|
cfg.key_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (!cfg.key_len)
|
|
|
|
cfg.key_len = 32;
|
|
|
|
|
|
|
|
if (cfg.key_len != 32 && cfg.key_len != 48 && cfg.key_len != 64) {
|
|
|
|
fprintf(stderr, "Invalid key length %u\n", cfg.key_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
raw_secret = malloc(cfg.key_len);
|
|
|
|
if (!raw_secret)
|
|
|
|
return -ENOMEM;
|
|
|
|
if (!cfg.secret) {
|
|
|
|
if (getrandom_bytes(raw_secret, cfg.key_len) < 0)
|
|
|
|
return -errno;
|
|
|
|
} else {
|
|
|
|
int secret_len = 0, i;
|
|
|
|
unsigned int c;
|
|
|
|
|
|
|
|
for (i = 0; i < strlen(cfg.secret); i+=2) {
|
|
|
|
if (sscanf(&cfg.secret[i], "%02x", &c) != 1) {
|
|
|
|
fprintf(stderr, "Invalid secret '%s'\n",
|
|
|
|
cfg.secret);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
raw_secret[secret_len++] = (unsigned char)c;
|
|
|
|
}
|
|
|
|
if (secret_len != cfg.key_len) {
|
|
|
|
fprintf(stderr, "Invalid key length (%d bytes)\n",
|
|
|
|
secret_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg.nqn) {
|
|
|
|
cfg.nqn = nvmf_hostnqn_from_file();
|
|
|
|
if (!cfg.nqn) {
|
|
|
|
fprintf(stderr, "Could not read host NQN\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nvme_gen_dhchap_key(cfg.nqn, cfg.hmac, cfg.key_len,
|
|
|
|
raw_secret, key) < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
crc = crc32(crc, key, cfg.key_len);
|
|
|
|
key[cfg.key_len++] = crc & 0xff;
|
|
|
|
key[cfg.key_len++] = (crc >> 8) & 0xff;
|
|
|
|
key[cfg.key_len++] = (crc >> 16) & 0xff;
|
|
|
|
key[cfg.key_len++] = (crc >> 24) & 0xff;
|
|
|
|
|
|
|
|
memset(encoded_key, 0, sizeof(encoded_key));
|
|
|
|
base64_encode(key, cfg.key_len, encoded_key);
|
|
|
|
|
|
|
|
printf("DHHC-1:%02x:%s:\n", cfg.hmac, encoded_key);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_dhchap_key(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Check a DH-HMAC-CHAP host key for usability "\
|
|
|
|
"for NVMe In-Band Authentication.";
|
|
|
|
const char *key = "DH-HMAC-CHAP key (in hexadecimal characters) "\
|
|
|
|
"to be validated.";
|
|
|
|
|
|
|
|
unsigned char decoded_key[128];
|
|
|
|
unsigned int decoded_len;
|
|
|
|
u_int32_t crc = crc32(0L, NULL, 0);
|
|
|
|
u_int32_t key_crc;
|
|
|
|
int err = 0, hmac;
|
|
|
|
struct config {
|
|
|
|
char *key;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.key = NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_STR("key", 'k', &cfg.key, key),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (!cfg.key) {
|
|
|
|
fprintf(stderr, "Key not specified\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sscanf(cfg.key, "DHHC-1:%02x:*s", &hmac) != 1) {
|
|
|
|
fprintf(stderr, "Invalid key header '%s'\n", cfg.key);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
switch (hmac) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (strlen(cfg.key) != 59) {
|
|
|
|
fprintf(stderr, "Invalid key length for SHA(256)\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (strlen(cfg.key) != 83) {
|
|
|
|
fprintf(stderr, "Invalid key length for SHA(384)\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (strlen(cfg.key) != 103) {
|
|
|
|
fprintf(stderr, "Invalid key length for SHA(512)\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Invalid HMAC identifier %d\n", hmac);
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = base64_decode(cfg.key + 10, strlen(cfg.key) - 11,
|
|
|
|
decoded_key);
|
|
|
|
if (err < 0) {
|
|
|
|
fprintf(stderr, "Base64 decoding failed, error %d\n",
|
|
|
|
err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
decoded_len = err;
|
|
|
|
if (decoded_len < 32) {
|
|
|
|
fprintf(stderr, "Base64 decoding failed (%s, size %u)\n",
|
|
|
|
cfg.key + 10, decoded_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
decoded_len -= 4;
|
|
|
|
if (decoded_len != 32 && decoded_len != 48 && decoded_len != 64) {
|
|
|
|
fprintf(stderr, "Invalid key length %d\n", decoded_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
crc = crc32(crc, decoded_key, decoded_len);
|
|
|
|
key_crc = ((u_int32_t)decoded_key[decoded_len]) |
|
|
|
|
((u_int32_t)decoded_key[decoded_len + 1] << 8) |
|
|
|
|
((u_int32_t)decoded_key[decoded_len + 2] << 16) |
|
|
|
|
((u_int32_t)decoded_key[decoded_len + 3] << 24);
|
|
|
|
if (key_crc != crc) {
|
|
|
|
fprintf(stderr, "CRC mismatch (key %08x, crc %08x)\n",
|
|
|
|
key_crc, crc);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
printf("Key is valid (HMAC %d, length %d, CRC %08x)\n",
|
|
|
|
hmac, decoded_len, crc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gen_tls_key(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Generate a TLS key in NVMe PSK Interchange format.";
|
|
|
|
const char *secret = "Optional secret (in hexadecimal characters) "\
|
|
|
|
"to be used for the TLS key.";
|
|
|
|
const char *hmac = "HMAC function to use for the retained key "\
|
|
|
|
"(1 = SHA-256, 2 = SHA-384).";
|
|
|
|
|
|
|
|
unsigned char *raw_secret;
|
|
|
|
char encoded_key[128];
|
|
|
|
int key_len = 32;
|
|
|
|
unsigned long crc = crc32(0L, NULL, 0);
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
struct config {
|
|
|
|
char *secret;
|
|
|
|
unsigned int hmac;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.secret = NULL,
|
|
|
|
.hmac = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_STR("secret", 's', &cfg.secret, secret),
|
|
|
|
OPT_UINT("hmac", 'm', &cfg.hmac, hmac),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
if (cfg.hmac < 1 || cfg.hmac > 3) {
|
|
|
|
fprintf(stderr, "Invalid HMAC identifier %u\n", cfg.hmac);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.hmac == 2)
|
|
|
|
key_len = 48;
|
|
|
|
|
|
|
|
raw_secret = malloc(key_len + 4);
|
|
|
|
if (!raw_secret)
|
|
|
|
return -ENOMEM;
|
|
|
|
if (!cfg.secret) {
|
|
|
|
if (getrandom_bytes(raw_secret, key_len) < 0)
|
|
|
|
return -errno;
|
|
|
|
} else {
|
|
|
|
int secret_len = 0, i;
|
|
|
|
unsigned int c;
|
|
|
|
|
|
|
|
for (i = 0; i < strlen(cfg.secret); i+=2) {
|
|
|
|
if (sscanf(&cfg.secret[i], "%02x", &c) != 1) {
|
|
|
|
fprintf(stderr, "Invalid secret '%s'\n",
|
|
|
|
cfg.secret);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (i >= key_len) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Skipping excess secret bytes\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
raw_secret[secret_len++] = (unsigned char)c;
|
|
|
|
}
|
|
|
|
if (secret_len != key_len) {
|
|
|
|
fprintf(stderr, "Invalid key length (%d bytes)\n",
|
|
|
|
secret_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
crc = crc32(crc, raw_secret, key_len);
|
|
|
|
raw_secret[key_len++] = crc & 0xff;
|
|
|
|
raw_secret[key_len++] = (crc >> 8) & 0xff;
|
|
|
|
raw_secret[key_len++] = (crc >> 16) & 0xff;
|
|
|
|
raw_secret[key_len++] = (crc >> 24) & 0xff;
|
|
|
|
|
|
|
|
memset(encoded_key, 0, sizeof(encoded_key));
|
|
|
|
base64_encode(raw_secret, key_len, encoded_key);
|
|
|
|
|
|
|
|
printf("NVMeTLSkey-1:%02x:%s:\n", cfg.hmac, encoded_key);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_tls_key(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Check a TLS key for NVMe PSK Interchange format.\n";
|
|
|
|
const char *key = "TLS key (in PSK Interchange format) "\
|
|
|
|
"to be validated.";
|
|
|
|
|
|
|
|
unsigned char decoded_key[128];
|
|
|
|
unsigned int decoded_len;
|
|
|
|
u_int32_t crc = crc32(0L, NULL, 0);
|
|
|
|
u_int32_t key_crc;
|
|
|
|
int err = 0, hmac;
|
|
|
|
struct config {
|
|
|
|
char *key;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.key = NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_STR("key", 'k', &cfg.key, key),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (!cfg.key) {
|
|
|
|
fprintf(stderr, "Key not specified\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sscanf(cfg.key, "NVMeTLSkey-1:%02x:*s", &hmac) != 1) {
|
|
|
|
fprintf(stderr, "Invalid key header '%s'\n", cfg.key);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
switch (hmac) {
|
|
|
|
case 1:
|
|
|
|
if (strlen(cfg.key) != 65) {
|
2025-02-16 12:17:56 +01:00
|
|
|
fprintf(stderr, "Invalid key length %zu for SHA(256)\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
strlen(cfg.key));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (strlen(cfg.key) != 89) {
|
2025-02-16 12:17:56 +01:00
|
|
|
fprintf(stderr, "Invalid key length %zu for SHA(384)\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
strlen(cfg.key));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Invalid HMAC identifier %d\n", hmac);
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = base64_decode(cfg.key + 16, strlen(cfg.key) - 17,
|
|
|
|
decoded_key);
|
|
|
|
if (err < 0) {
|
|
|
|
fprintf(stderr, "Base64 decoding failed (%s, error %d)\n",
|
|
|
|
cfg.key + 16, err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
decoded_len = err;
|
|
|
|
decoded_len -= 4;
|
|
|
|
if (decoded_len != 32 && decoded_len != 48) {
|
|
|
|
fprintf(stderr, "Invalid key length %d\n", decoded_len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
crc = crc32(crc, decoded_key, decoded_len);
|
|
|
|
key_crc = ((u_int32_t)decoded_key[decoded_len]) |
|
|
|
|
((u_int32_t)decoded_key[decoded_len + 1] << 8) |
|
|
|
|
((u_int32_t)decoded_key[decoded_len + 2] << 16) |
|
|
|
|
((u_int32_t)decoded_key[decoded_len + 3] << 24);
|
|
|
|
if (key_crc != crc) {
|
|
|
|
fprintf(stderr, "CRC mismatch (key %08x, crc %08x)\n",
|
|
|
|
key_crc, crc);
|
|
|
|
return -EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("Key is valid (HMAC %d, length %d, CRC %08x)\n",
|
|
|
|
hmac, decoded_len, crc);
|
|
|
|
return 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int show_topology_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Show the topolog\n";
|
|
|
|
const char *verbose = "Increase output verbosity";
|
|
|
|
const char *ranking = "Ranking order: namespace|ctrl";
|
|
|
|
enum nvme_print_flags flags;
|
|
|
|
nvme_root_t r;
|
|
|
|
enum nvme_cli_topo_ranking rank;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
struct config {
|
|
|
|
char *output_format;
|
|
|
|
int verbose;
|
|
|
|
char *ranking;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct config cfg = {
|
|
|
|
.output_format = "normal",
|
|
|
|
.verbose = 0,
|
|
|
|
.ranking = "namespace",
|
|
|
|
};
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
|
|
|
|
OPT_INCR("verbose", 'v', &cfg.verbose, verbose),
|
|
|
|
OPT_FMT("ranking", 'r', &cfg.ranking, ranking),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
err = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = flags = validate_output_format(cfg.output_format);
|
|
|
|
if (flags < 0)
|
|
|
|
return err;
|
|
|
|
if (cfg.verbose)
|
|
|
|
flags |= VERBOSE;
|
|
|
|
|
|
|
|
if (!strcmp(cfg.ranking, "namespace"))
|
|
|
|
rank = NVME_CLI_TOPO_NAMESPACE;
|
|
|
|
else if (!strcmp(cfg.ranking, "ctrl"))
|
|
|
|
rank = NVME_CLI_TOPO_CTRL;
|
|
|
|
else {
|
|
|
|
fprintf(stderr, "Invalid ranking argument: %s\n",
|
|
|
|
cfg.ranking);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
|
|
|
|
if (!r) {
|
|
|
|
fprintf(stderr, "Failed to create topology root: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nvme_scan_topology(r, NULL, NULL);
|
|
|
|
if (err < 0) {
|
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
nvme_show_topology(r, flags, rank);
|
|
|
|
nvme_free_tree(r);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
static int discover_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send Get Log Page request to Discovery Controller.";
|
2025-02-16 12:15:30 +01:00
|
|
|
return nvmf_discover(desc, argc, argv, false);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int connect_all_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Discover NVMeoF subsystems and connect to them";
|
2025-02-16 12:15:30 +01:00
|
|
|
return nvmf_discover(desc, argc, argv, true);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int connect_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Connect to NVMeoF subsystem";
|
2025-02-16 12:15:30 +01:00
|
|
|
return nvmf_connect(desc, argc, argv);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int disconnect_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Disconnect from NVMeoF subsystem";
|
2025-02-16 12:15:30 +01:00
|
|
|
return nvmf_disconnect(desc, argc, argv);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
int disconnect_all_cmd(int argc, char **argv, struct command *command,
|
|
|
|
struct plugin *plugin)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
const char *desc = "Disconnect from all connected NVMeoF subsystems";
|
2025-02-16 12:15:30 +01:00
|
|
|
return nvmf_disconnect_all(desc, argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Configuration of NVMeoF subsystems";
|
|
|
|
return nvmf_config(desc, argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dim_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
|
|
|
|
{
|
|
|
|
const char *desc = "Send Discovery Information Management command to a Discovery Controller (DC)";
|
|
|
|
return nvmf_dim(desc, argc, argv);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void register_extension(struct plugin *plugin)
|
|
|
|
{
|
|
|
|
plugin->parent = &nvme;
|
|
|
|
nvme.extensions->tail->next = plugin;
|
|
|
|
nvme.extensions->tail = plugin;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
nvme.extensions->parent = &nvme;
|
|
|
|
if (argc < 2) {
|
|
|
|
general_help(&builtin);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
|
|
|
|
err = handle_plugin(argc - 1, &argv[1], nvme.extensions);
|
|
|
|
if (err == -ENOTTY)
|
|
|
|
general_help(&builtin);
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
return err ? 1 : 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|