1
0
Fork 0

Merging upstream version 2.12.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-03-20 08:10:44 +01:00
parent 078c0dbcc0
commit 635faa7346
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
571 changed files with 10718 additions and 2738 deletions

669
plugins/lm/lm-nvme.c Normal file
View file

@ -0,0 +1,669 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2024 Samsung Electronics Co., LTD.
*
* Authors: Nate Thornton <n.thornton@samsung.com>
*/
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/fs.h>
#include <inttypes.h>
#include <asm/byteorder.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/sysinfo.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
#include "common.h"
#include "nvme.h"
#include "nvme-print.h"
#include "libnvme.h"
#include "plugin.h"
#include "linux/types.h"
#include "nvme-wrap.h"
#include "util/cleanup.h"
#define CREATE_CMD
#include "lm-nvme.h"
#include "lm-print.h"
static inline const char *arg_str(const char * const *strings, size_t array_size, size_t idx)
{
if (idx < array_size && strings[idx])
return strings[idx];
return "unrecognized";
}
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define ARGSTR(s, i) arg_str(s, ARRAY_SIZE(s), i)
static int lm_create_cdq(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "Create Controller Data Queue for controller of specific type and size";
const char *sz = "CDQ Size (in dwords)";
const char *cntlid = "Controller ID";
const char *qt = "Queue Type (default: 0 = User Data Migration Queue)";
const char *consent = "I consent this will not work and understand a CDQ cannot be mapped "
"to user space. If I proceed with the creation of a CDQ, the device "
"will write to invalid memory, inevitably leading to MMU faults or "
"worse.";
_cleanup_huge_ struct nvme_mem_huge mh = { 0, };
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
struct lba_migration_queue_entry_type_0 *queue = NULL;
int err = -1;
struct config {
__u32 sz;
__u16 cntlid;
__u8 qt;
bool consent;
char *file;
};
struct config cfg = {
.sz = 0,
.cntlid = 0,
.qt = 0,
.consent = false,
.file = NULL,
};
OPT_ARGS(opts) = {
OPT_UINT("size", 's', &cfg.sz, sz),
OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid),
OPT_BYTE("queue-type", 'q', &cfg.qt, qt),
OPT_FLAG("consent", 0, &cfg.consent, consent),
OPT_END()
};
err = parse_and_open(&dev, argc, argv, desc, opts);
if (err)
return err;
if (!consent) {
nvme_show_error("ERROR: consent required");
return -EINVAL;
}
// Not that it really matters, but we setup memory as if the CDQ can be held
// in user space regardless.
queue = nvme_alloc_huge(cfg.sz << 2, &mh);
if (!queue) {
nvme_show_error("ERROR: nvme_alloc of size %dB failed %s", cfg.sz << 2,
strerror(errno));
return -ENOMEM;
}
struct nvme_lm_cdq_args args = {
.args_size = sizeof(args),
.fd = dev_fd(dev),
.sel = NVME_LM_SEL_CREATE_CDQ,
.mos = NVME_SET(cfg.qt, LM_QT),
.cntlid = cfg.cntlid,
.sz = cfg.sz,
.data = queue
};
err = nvme_lm_cdq(&args);
if (err < 0)
nvme_show_error("ERROR: nvme_lm_cdq() failed: %s", nvme_strerror(errno));
else if (err)
nvme_show_status(err);
else
printf("Create CDQ Successful: CDQID=0x%04x\n", args.cdqid);
return err;
}
static int lm_delete_cdq(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "Delete Controller Data Queue";
const char *cdqid = "Controller Data Queue ID";
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
int err = -1;
struct config {
__u16 cdqid;
};
struct config cfg = {
.cdqid = 0
};
OPT_ARGS(opts) = {
OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid),
OPT_END()
};
err = parse_and_open(&dev, argc, argv, desc, opts);
if (err)
return err;
struct nvme_lm_cdq_args args = {
.args_size = sizeof(args),
.fd = dev_fd(dev),
.sel = NVME_LM_SEL_DELETE_CDQ,
.cdqid = cfg.cdqid,
};
err = nvme_lm_cdq(&args);
if (err < 0)
nvme_show_error("ERROR: nvme_lm_cdq() failed: %s", nvme_strerror(errno));
else if (err > 0)
nvme_show_status(err);
else
printf("Delete CDQ Successful: CDQID=0x%04x\n", cfg.cdqid);
return err;
}
static const char * const lm_track_send_select_argstr[] = {
[NVME_LM_SEL_LOG_USER_DATA_CHANGES] = "Log User Data Changes",
[NVME_LM_SEL_TRACK_MEMORY_CHANGES] = "Track Memory Changes"
};
static int lm_track_send(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "Track Send command used to manage the tracking of information by a "
"controller";
const char *sel = "Type of management operation to perform\n"
" 0h = Log User Data Changes\n"
" 1h = Track Memory Changes";
const char *mos = "Management operation specific";
const char *cdqid = "Controller Data Queue ID";
const char *start = "Equivalent to start tracking with defaults";
const char *stop = "Equivalent to stop tracking with defaults";
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
int err = -1;
struct config {
__s8 sel;
__u8 mos;
__u16 cdqid;
bool start;
bool stop;
};
struct config cfg = {
.sel = -1,
.mos = 0,
.cdqid = 0,
.start = false,
.stop = false,
};
OPT_ARGS(opts) = {
OPT_BYTE("sel", 's', &cfg.sel, sel),
OPT_BYTE("mos", 'm', &cfg.mos, mos),
OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid),
OPT_FLAG("start", 0, &cfg.start, start),
OPT_FLAG("stop", 0, &cfg.stop, stop),
OPT_END()
};
err = parse_and_open(&dev, argc, argv, desc, opts);
if (err)
return err;
if (cfg.sel == -1) {
nvme_show_error("Select field required");
return -EINVAL;
}
if (cfg.sel != NVME_LM_SEL_LOG_USER_DATA_CHANGES) {
nvme_show_error("Unsupported select option %d (%s)", cfg.sel,
ARGSTR(lm_track_send_select_argstr, cfg.sel));
return -EINVAL;
}
if (cfg.start && cfg.stop) {
nvme_show_error("Must select one of start & stop, not both");
return -EINVAL;
} else if (cfg.sel == NVME_LM_SEL_LOG_USER_DATA_CHANGES) {
if (cfg.start)
cfg.mos = NVME_SET(NVME_LM_LACT_START_LOGGING, LM_LACT);
else if (cfg.stop)
cfg.mos = NVME_SET(NVME_LM_LACT_STOP_LOGGING, LM_LACT);
}
struct nvme_lm_track_send_args args = {
.args_size = sizeof(args),
.fd = dev_fd(dev),
.cdqid = cfg.cdqid,
.sel = cfg.sel,
.mos = cfg.mos,
};
err = nvme_lm_track_send(&args);
if (err < 0)
nvme_show_error("ERROR: nvme_lm_track_send() failed %s", strerror(errno));
else if (err)
nvme_show_status(err);
else
printf("Track Send (%s) Successful\n",
ARGSTR(lm_track_send_select_argstr, cfg.sel));
return err;
}
static const char * const lm_migration_send_select_argstr[] = {
[NVME_LM_SEL_SUSPEND] = "Suspend",
[NVME_LM_SEL_RESUME] = "Resume",
[NVME_LM_SEL_SET_CONTROLLER_STATE] = "Set Controller State"
};
static int lm_migration_send(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "Migration Send command is used to manage the migration of a controller";
const char *sel = "Select (SEL) the type of management operation to perform "
"(CDW10[07:00])\n"
" 0h = Suspend\n"
" 1h = Resume\n"
" 2h = Set Controller State";
const char *cntlid = "Controller Identifier (CDW11[15:00])";
const char *stype = "Type of suspend (STYPE) (CDW11[23:16]\n"
" 0h = Suspend Notification\n"
" 1h = Suspend";
const char *dudmq = "Delete user data migration queue (DUDMQ) as part of suspend operation "
"(CDW11[31])";
const char *seqind = "Sequence Indicator (CDW11[17:16])\n"
" 0h = Not first not last\n"
" 1h = First in two or more\n"
" 2h = Last in two or more\n"
" 3h = Entire state info";
const char *csuuidi = "Controller State UUID Index (CSUUIDI) (CDW11[31:24])";
const char *csvi = "Controller State Version Index (CSVI) (CDW11[23:16])";
const char *uidx = "UUID Index (UIDX) (CDW14[16:00])";
const char *offset = "Controller State Offset";
const char *numd = "Number of Dwords (NUMD)";
const char *input = "Controller State Data input file";
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
_cleanup_file_ FILE *file = NULL;
_cleanup_huge_ struct nvme_mem_huge mh = { 0, };
void *data = NULL;
int err = -1;
struct config {
__s8 sel;
__u16 cntlid;
__u8 stype;
__u8 seqind;
__u8 csuuidi;
__u8 csvi;
__u8 uidx;
__u64 offset;
__u32 numd;
char *input;
bool dudmq;
};
struct config cfg = {
.sel = -1,
.cntlid = 0,
.stype = 0,
.seqind = 0,
.csuuidi = 0,
.csvi = 0,
.uidx = 0,
.offset = 0,
.numd = 0,
.input = NULL,
.dudmq = false
};
OPT_ARGS(opts) = {
OPT_BYTE("sel", 's', &cfg.sel, sel),
OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid),
OPT_BYTE("stype", 't', &cfg.stype, stype),
OPT_FLAG("dudmq", 'd', &cfg.dudmq, dudmq),
OPT_BYTE("seq-ind", 'S', &cfg.seqind, seqind),
OPT_BYTE("csuuidi", 'U', &cfg.csuuidi, csuuidi),
OPT_BYTE("csvi", 'V', &cfg.csvi, csvi),
OPT_BYTE("uidx", 'u', &cfg.uidx, uidx),
OPT_LONG("offset", 'o', &cfg.offset, offset),
OPT_UINT("numd", 'n', &cfg.numd, numd),
OPT_FILE("input-file", 'f', &cfg.input, input),
OPT_END()
};
err = parse_and_open(&dev, argc, argv, desc, opts);
if (err)
return err;
if (cfg.sel == -1) {
nvme_show_error("Select field required");
return -EINVAL;
}
// Sanity check input parameters
if (cfg.sel == NVME_LM_SEL_SUSPEND || cfg.sel == NVME_LM_SEL_RESUME) {
if (cfg.csuuidi != 0 || cfg.csvi != 0) {
nvme_show_error("Unexpected fields for %s",
ARGSTR(lm_migration_send_select_argstr, cfg.sel));
return -EINVAL;
}
} else if (cfg.sel == NVME_LM_SEL_SET_CONTROLLER_STATE) {
if (cfg.dudmq || cfg.stype != 0) {
nvme_show_error("Unexpected fields for %s",
ARGSTR(lm_migration_send_select_argstr, cfg.sel));
return -EINVAL;
} else if (!strlen(cfg.input)) {
nvme_show_error("Expected file for %s",
ARGSTR(lm_migration_send_select_argstr, cfg.sel));
return -EINVAL;
}
}
if (cfg.input && strlen(cfg.input)) {
file = fopen(cfg.input, "r");
if (file == NULL) {
nvme_show_perror(cfg.input);
return -EINVAL;
}
data = nvme_alloc_huge(cfg.numd << 2, &mh);
if (!data)
return -ENOMEM;
size_t n_data = fread(data, 1, cfg.numd << 2, file);
fclose(file);
if (n_data != (size_t)(cfg.numd << 2)) {
nvme_show_error("failed to read controller state data %s", strerror(errno));
return -errno;
}
}
struct nvme_lm_migration_send_args args = {
.args_size = sizeof(args),
.fd = dev_fd(dev),
.sel = cfg.sel,
.mos = NVME_SET(cfg.seqind, LM_MIGRATION_SEND_MOS),
.cntlid = cfg.cntlid,
.csuuidi = cfg.csuuidi,
.uidx = cfg.uidx,
.stype = cfg.stype,
.offset = cfg.offset,
.dudmq = cfg.dudmq,
.numd = cfg.numd,
.data = data,
};
err = nvme_lm_migration_send(&args);
if (err < 0)
nvme_show_error("ERROR: nvme_lm_migration_send() failed %s", strerror(errno));
else if (err > 0)
nvme_show_status(err);
else
printf("Migration Send (%s) Successful\n",
ARGSTR(lm_migration_send_select_argstr, cfg.sel));
return err;
}
static int lm_migration_recv(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "Migration Receive command is used to obtain information used to manage "
" a migratable controller";
const char *sel = "Select (SEL) the type of management operation to perform "
"(CDW10[07:00])\n"
" 0h = Get Controller State";
const char *cntlid = "Controller Identifier (CDW10[31:16])";
const char *csuuidi = "Controller State UUID Index (CSUUIDI) (CDW11[23:16])";
const char *csvi = "Controller State Version Index (CSVI) (CDW11[7:0])";
const char *uidx = "UUID Index (UIDX) (CDW14[16:00])";
const char *offset = "Controller State Offset";
const char *numd = "Number of Dwords (NUMD)";
const char *output = "Controller State Data output file";
const char *human_readable_info = "show info in readable format";
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
_cleanup_file_ FILE *fd = NULL;
_cleanup_huge_ struct nvme_mem_huge mh = { 0, };
nvme_print_flags_t flags;
void *data = NULL;
int err = -1;
struct config {
__u8 sel;
__u16 cntlid;
__u8 csuuidi;
__u8 csvi;
__u8 uidx;
__u64 offset;
__u32 numd;
char *output;
char *output_format;
bool human_readable;
};
struct config cfg = {
.sel = -1,
.cntlid = 0,
.csuuidi = 0,
.csvi = 0,
.uidx = 0,
.offset = 0,
.numd = 0,
.output = NULL,
.output_format = "normal",
.human_readable = false
};
OPT_ARGS(opts) = {
OPT_BYTE("sel", 's', &cfg.sel, sel),
OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid),
OPT_BYTE("csuuidi", 'U', &cfg.csuuidi, csuuidi),
OPT_BYTE("csvi", 'V', &cfg.csvi, csvi),
OPT_BYTE("uidx", 'u', &cfg.uidx, uidx),
OPT_LONG("offset", 'o', &cfg.offset, offset),
OPT_UINT("numd", 'n', &cfg.numd, numd),
OPT_FILE("output-file", 'f', &cfg.output, output),
OPT_FMT("output-format", 0, &cfg.output_format, output_format),
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable_info),
OPT_END()
};
err = parse_and_open(&dev, argc, argv, desc, opts);
if (err)
return err;
err = validate_output_format(cfg.output_format, &flags);
if (err < 0) {
nvme_show_error("Invalid output format");
return err;
}
if (cfg.output_format && cfg.offset != 0 && !(flags & BINARY)) {
nvme_show_error("cannot parse non-zero offset");
return -EINVAL;
}
if (cfg.human_readable)
flags |= VERBOSE;
if (cfg.output && strlen(cfg.output)) {
fd = fopen(cfg.output, "w");
if (fd < 0) {
nvme_show_perror(cfg.output);
return -errno;
}
}
data = nvme_alloc_huge((cfg.numd + 1) << 2, &mh);
if (!data)
return -ENOMEM;
__u32 result = 0;
struct nvme_lm_migration_recv_args args = {
.args_size = sizeof(args),
.fd = dev_fd(dev),
.sel = cfg.sel,
.mos = NVME_SET(cfg.csvi, LM_GET_CONTROLLER_STATE_CSVI),
.uidx = cfg.uidx,
.csuuidi = cfg.csuuidi,
.offset = cfg.offset,
.cntlid = cfg.cntlid,
.data = data,
.result = &result,
};
err = nvme_lm_migration_recv(&args);
if (err < 0)
nvme_show_error("ERROR: nvme_lm_migration_recv() failed %s", strerror(errno));
else if (err)
nvme_show_status(err);
else if (cfg.sel == NVME_LM_SEL_GET_CONTROLLER_STATE) {
if (flags == NORMAL)
printf("CDW0: 0x%x: Controller %sSuspended\n", result,
(result & NVME_LM_GET_CONTROLLER_STATE_CSUP) ? "" : "NOT ");
if (cfg.output && strlen(cfg.output)) {
if (fwrite(data, 1, cfg.numd << 2, fd) != (cfg.numd << 2)) {
nvme_show_error("ERROR: %s: failed to write buffer to output file",
strerror(errno));
err = -errno;
}
} else {
lm_show_controller_state_data((struct nvme_lm_controller_state_data *)data,
(cfg.numd + 1) << 2, cfg.offset, flags);
}
}
return 0;
}
enum lm_controller_data_queue_feature_id {
lm_cdq_feature_id = 0x21
};
static int lm_set_cdq(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "This Feature allows a host to update the status of the head pointer "
"of a CDQ and specify the configuration of a CDQ Tail event.";
const char *cdqid = "Controller Data Queue ID";
const char *hp = "The slot of the head pointer for the specified CDQ";
const char *tpt = "If specified, the slot that causes the controller "
" to issue a CDQ Tail Pointer event";
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
int err = -1;
struct config {
__u16 cdqid;
__u32 hp;
__s32 tpt;
};
struct config cfg = {
.cdqid = 0,
.hp = 0,
.tpt = -1,
};
OPT_ARGS(opts) = {
OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid),
OPT_UINT("hp", 'H', &cfg.hp, hp),
OPT_UINT("tpt", 'T', &cfg.tpt, tpt),
OPT_END()
};
err = parse_and_open(&dev, argc, argv, desc, opts);
if (err)
return err;
struct nvme_set_features_args args = {
.args_size = sizeof(args),
.fd = dev_fd(dev),
.fid = lm_cdq_feature_id,
.cdw11 = cfg.cdqid |
((cfg.tpt >= 0) ? NVME_SET(1, LM_CTRL_DATA_QUEUE_ETPT) : 0),
.cdw12 = cfg.hp,
.cdw13 = cfg.tpt
};
err = nvme_set_features(&args);
if (err < 0)
nvme_show_error("ERROR: nvme_set_features() failed %s", nvme_strerror(errno));
else if (err)
nvme_show_status(err);
else
printf("Success. Head Pointer: %d\n", cfg.hp);
return err;
}
static int lm_get_cdq(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "This Feature allows a host to retrieve the status of the head pointer "
"of a CDQ and specify the configuration of a CDQ Tail event.";
const char *cdqid = "Controller Data Queue ID";
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
nvme_print_flags_t flags;
int err = -1;
struct config {
__u16 cdqid;
char *output_format;
};
struct config cfg = {
.cdqid = 0,
.output_format = "normal",
};
OPT_ARGS(opts) = {
OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid),
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
OPT_END()
};
err = parse_and_open(&dev, argc, argv, desc, opts);
if (err)
return err;
err = validate_output_format(cfg.output_format, &flags);
if (err < 0) {
nvme_show_error("Invalid output format");
return err;
}
struct nvme_lm_ctrl_data_queue_fid_data data;
struct nvme_get_features_args args = {
.args_size = sizeof(args),
.fd = dev_fd(dev),
.fid = lm_cdq_feature_id,
.cdw11 = cfg.cdqid,
.data = &data,
.data_len = sizeof(data)
};
err = nvme_get_features(&args);
if (err < 0)
nvme_show_error("ERROR: nvme_get_features() failed %s", nvme_strerror(errno));
else if (err)
nvme_show_status(err);
else
lm_show_controller_data_queue(&data, flags);
return err;
}

30
plugins/lm/lm-nvme.h Normal file
View file

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2024 Samsung Electronics Co., LTD.
*
* Authors: Nate Thornton <n.thornton@samsung.com>
*/
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/lm/lm-nvme
#if !defined(LIVE_MIGRATION_NVME) || defined(CMD_HEADER_MULTI_READ)
#define LIVE_MIGRATION_NVME
#include "cmd.h"
PLUGIN(NAME("lm", "Live Migration NVMe extensions", NVME_VERSION),
COMMAND_LIST(
ENTRY("create-cdq", "Create Controller Data Queue", lm_create_cdq)
ENTRY("delete-cdq", "Delete Controller Data Queue", lm_delete_cdq)
ENTRY("track-send", "Track Send Command", lm_track_send)
ENTRY("migration-send", "Migration Send", lm_migration_send)
ENTRY("migration-recv", "Migration Receive", lm_migration_recv)
ENTRY("set-cdq", "Set Feature - Controller Data Queue (FID 21h)", lm_set_cdq)
ENTRY("get-cdq", "Get Feature - Controller Data Queue (FID 21h)", lm_get_cdq)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "lm-print.h"
static void binary_controller_state_data(struct nvme_lm_controller_state_data *data, size_t len,
__u32 offset)
{
d_raw((unsigned char *)data, len);
}
static void binary_controller_data_queue(struct nvme_lm_ctrl_data_queue_fid_data *data)
{
d_raw((unsigned char *)data, sizeof(*data));
}
static struct lm_print_ops binary_print_ops = {
.controller_state_data = binary_controller_state_data,
.controller_data_queue = binary_controller_data_queue,
};
struct lm_print_ops *lm_get_binary_print_ops(nvme_print_flags_t flags)
{
binary_print_ops.flags = flags;
return &binary_print_ops;
}

109
plugins/lm/lm-print-json.c Normal file
View file

@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "lm-print.h"
#include "common.h"
static void json_controller_state_data(struct nvme_lm_controller_state_data *data, size_t len,
__u32 offset)
{
if (offset) {
fprintf(stderr, "cannot understand non-zero offset\n");
return;
}
struct json_object *root = json_create_object();
struct json_object *nvmecs = json_create_object();
struct json_object *iosqs = json_create_array();
struct json_object *iocqs = json_create_array();
json_object_add_value_uint(root, "version",
le16_to_cpu(data->hdr.ver));
json_object_add_value_uint(root, "controller state attributes",
data->hdr.csattr);
json_object_add_value_uint128(root, "nvme controller state size",
le128_to_cpu(data->hdr.nvmecss));
json_object_add_value_uint128(root, "vendor specific size",
le128_to_cpu(data->hdr.vss));
json_object_add_value_object(root, "nvme controller state", nvmecs);
json_object_add_value_uint(nvmecs, "version",
le16_to_cpu(data->data.hdr.ver));
json_object_add_value_uint(nvmecs, "number of io submission queues",
le16_to_cpu(data->data.hdr.niosq));
json_object_add_value_uint(nvmecs, "number of io completion queues",
le16_to_cpu(data->data.hdr.niocq));
json_object_add_value_array(nvmecs, "io submission queue list", iosqs);
for (int i = 0; i < data->data.hdr.niosq; i++) {
struct nvme_lm_io_submission_queue_data *sq = &data->data.sqs[i];
struct json_object *sq_obj = json_create_object();
json_object_add_value_uint64(sq_obj, "io submission prp entry 1",
le64_to_cpu(sq->iosqprp1));
json_object_add_value_uint(sq_obj, "io submission queue size",
le16_to_cpu(sq->iosqqsize));
json_object_add_value_uint(sq_obj, "io submission queue identifier",
le16_to_cpu(sq->iosqqid));
json_object_add_value_uint(sq_obj, "io completion queue identifier",
le16_to_cpu(sq->iosqcqid));
json_object_add_value_uint(sq_obj, "io submission queue attributes",
le16_to_cpu(sq->iosqa));
json_object_add_value_uint(sq_obj, "io submission queue head pointer",
le16_to_cpu(sq->iosqhp));
json_object_add_value_uint(sq_obj, "io submission queue tail pointer",
le16_to_cpu(sq->iosqtp));
json_array_add_value_object(iosqs, sq_obj);
}
json_object_add_value_array(nvmecs, "io completion queue list", iocqs);
for (int i = 0; i < data->data.hdr.niocq; i++) {
struct nvme_lm_io_completion_queue_data *cq = &data->data.cqs[i];
struct json_object *cq_obj = json_create_object();
json_object_add_value_uint64(cq_obj, "io completion prp entry 1",
le64_to_cpu(cq->iocqprp1));
json_object_add_value_uint(cq_obj, "io completion queue size",
le16_to_cpu(cq->iocqqsize));
json_object_add_value_uint(cq_obj, "io completion queue identifier",
le16_to_cpu(cq->iocqqid));
json_object_add_value_uint(cq_obj, "io completion queue head pointer",
le16_to_cpu(cq->iocqhp));
json_object_add_value_uint(cq_obj, "io completion queue tail pointer",
le16_to_cpu(cq->iocqtp));
json_object_add_value_uint(cq_obj, "io completion queue attributes",
le32_to_cpu(cq->iocqa));
json_array_add_value_object(iocqs, cq_obj);
}
json_print_object(root, NULL);
printf("\n");
json_free_object(root);
}
static void json_controller_data_queue(struct nvme_lm_ctrl_data_queue_fid_data *data)
{
struct json_object *root = json_create_object();
json_object_add_value_uint(root, "head_pointer", le32_to_cpu(data->hp));
json_object_add_value_uint(root, "tail_pointer_trigger", le32_to_cpu(data->tpt));
json_print_object(root, NULL);
printf("\n");
json_free_object(root);
}
static struct lm_print_ops json_print_ops = {
.controller_state_data = json_controller_state_data,
.controller_data_queue = json_controller_data_queue
};
struct lm_print_ops *lm_get_json_print_ops(nvme_print_flags_t flags)
{
json_print_ops.flags = flags;
return &json_print_ops;
}

View file

@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "lm-print.h"
#include <inttypes.h>
#include "common.h"
#include "util/types.h"
static struct lm_print_ops stdout_print_ops;
static void stdout_controller_state_data(struct nvme_lm_controller_state_data *data, size_t len,
__u32 offset)
{
if (offset) {
fprintf(stderr, "cannot understand non-zero offset\n");
return;
}
int human = stdout_print_ops.flags & VERBOSE;
if (sizeof(struct nvme_lm_controller_state_data_header) <= len) {
printf("Header:\n");
printf("%-45s: 0x%x\n", "Version (VER)", data->hdr.ver);
printf("%-45s: 0x%x\n", "Controller State Attributes (CSATTR)", data->hdr.csattr);
if (human)
printf(" [0:0] : 0x%x Controller %sSuspended\n",
data->hdr.csattr & 1, data->hdr.csattr & 1 ? "" : "NOT ");
printf("%-45s: %s\n", "NVMe Controller State Size (NVMECSS)",
uint128_t_to_string(le128_to_cpu(data->hdr.nvmecss)));
printf("%-45s: %s\n", "Vendor Specific Size (VSS)",
uint128_t_to_string(le128_to_cpu(data->hdr.vss)));
len -= sizeof(struct nvme_lm_controller_state_data_header);
} else {
fprintf(stderr, "WARNING: Header truncated\n");
len = 0;
}
if (!len)
return;
if (sizeof(struct nvme_lm_nvme_controller_state_data_header) <= len) {
int niosq = data->data.hdr.niosq;
int niocq = data->data.hdr.niocq;
printf("\nNVMe Controller State Data Structure:\n");
printf("%-45s: 0x%x\n", "Version (VER)",
le16_to_cpu(data->data.hdr.ver));
printf("%-45s: %d\n", "Number of I/O Submission Queues (NIOSQ)",
le16_to_cpu(niosq));
printf("%-45s: %d\n", "Number of I/O Completion Queues (NIOCQ)",
le16_to_cpu(niocq));
len -= sizeof(struct nvme_lm_nvme_controller_state_data_header);
if (len < niosq * sizeof(struct nvme_lm_io_submission_queue_data)) {
fprintf(stderr, "WARNING: I/O Submission Queues truncated\n");
niosq = len / sizeof(struct nvme_lm_io_submission_queue_data);
}
for (int i = 0; i < niosq; ++i) {
struct nvme_lm_io_submission_queue_data *sq = &(data->data.sqs[i]);
__u16 iosqa = le16_to_cpu(sq->iosqa);
printf("\nNVMe I/O Submission Queue Data [%d]:\n", i);
printf("%-45s: 0x%"PRIu64"\n", "PRP Entry 1 (IOSQPRP1)",
le64_to_cpu(sq->iosqprp1));
printf("%-45s: 0x%x\n", "Queue Size (IOSQQSIZE)",
le16_to_cpu(sq->iosqqsize));
printf("%-45s: 0x%x\n", "Identifier (IOSQQID)",
le16_to_cpu(sq->iosqqid));
printf("%-45s: 0x%x\n", "Completion Queue Identifier (IOSQCQID)",
le16_to_cpu(sq->iosqcqid));
printf("%-45s: 0x%x\n", "Attributes (IOSQA)", iosqa);
if (human) {
printf(" [2:1] : 0x%x Queue Priority (IOSQQPRIO)\n",
NVME_GET(iosqa, LM_IOSQPRIO));
printf(" [0:0] : 0x%x Queue %sPhysically Contiguous (IOSQPC)\n",
NVME_GET(iosqa, LM_IOSQPC),
NVME_GET(iosqa, LM_IOSQPC) ? "" : "NOT ");
}
printf("%-45s: 0x%x\n", "I/O Submission Queue Head Pointer (IOSQHP)",
le16_to_cpu(sq->iosqhp));
printf("%-45s: 0x%x\n", "I/O Submission Queue Tail Pointer (IOSQTP)",
le16_to_cpu(sq->iosqtp));
}
len -= niosq * sizeof(struct nvme_lm_io_submission_queue_data);
if (len < niocq * sizeof(struct nvme_lm_io_completion_queue_data)) {
fprintf(stderr, "WARNING: I/O Completion Queues truncated\n");
niocq = len / sizeof(struct nvme_lm_io_completion_queue_data);
}
for (int i = 0; i < niocq; ++i) {
struct nvme_lm_io_completion_queue_data *cq = &data->data.cqs[niosq + i];
__u32 iocqa = le32_to_cpu(cq->iocqa);
printf("\nNVMe I/O Completion Queue Data [%d]:\n", i);
printf("%-45s: 0x%"PRIu64"\n", "I/O Completion PRP Entry 1 (IOCQPRP1)",
le64_to_cpu(cq->iocqprp1));
printf("%-45s: 0x%x\n", "I/O Completion Queue Size (IOCQQSIZE)",
le16_to_cpu(cq->iocqqsize));
printf("%-45s: 0x%x\n", "I/O Completion Queue Identifier (IOCQQID)",
le16_to_cpu(cq->iocqqid));
printf("%-45s: 0x%x\n", "I/O Completion Queue Head Pointer (IOSQHP)",
le16_to_cpu(cq->iocqhp));
printf("%-45s: 0x%x\n", "I/O Completion Queue Tail Pointer (IOSQTP)",
le16_to_cpu(cq->iocqtp));
printf("%-45s: 0x%x\n", "I/O Completion Queue Attributes (IOCQA)", iocqa);
if (human) {
printf(" [31:16] : 0x%x I/O Completion Queue Interrupt Vector "
"(IOCQIV)\n",
NVME_GET(iocqa, LM_IOCQIEN));
printf(" [2:2] : 0x%x Slot 0 Phase Tag (S0PT)\n",
NVME_GET(iocqa, LM_S0PT));
printf(" [1:1] : 0x%x Interrupts %sEnabled (IOCQIEN)\n",
NVME_GET(iocqa, LM_IOCQIEN),
NVME_GET(iocqa, LM_IOCQIEN) ? "" : "NOT ");
printf(" [0:0] : 0x%x Queue %sPhysically Contiguous (IOCQPC)\n",
NVME_GET(iocqa, LM_IOCQPC),
NVME_GET(iocqa, LM_IOCQPC) ? "" : "NOT ");
}
}
} else
fprintf(stderr, "WARNING: NVMe Controller State Data Structure truncated\n");
}
static void stdout_show_controller_data_queue(struct nvme_lm_ctrl_data_queue_fid_data *data)
{
printf("Head Pointer: 0x%x\n", le32_to_cpu(data->hp));
printf("Tail Pointer Trigger: 0x%x\n", le32_to_cpu(data->tpt));
}
static struct lm_print_ops stdout_print_ops = {
.controller_state_data = stdout_controller_state_data,
.controller_data_queue = stdout_show_controller_data_queue
};
struct lm_print_ops *lm_get_stdout_print_ops(nvme_print_flags_t flags)
{
stdout_print_ops.flags = flags;
return &stdout_print_ops;
}

38
plugins/lm/lm-print.c Normal file
View file

@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "lm-print.h"
#define lm_print(name, flags, ...) \
do { \
struct lm_print_ops *ops = lm_print_ops(flags); \
if (ops && ops->name) \
ops->name(__VA_ARGS__); \
else \
fprintf(stderr, "unhandled output format\n"); \
} while (false)
static struct lm_print_ops *lm_print_ops(nvme_print_flags_t flags)
{
struct lm_print_ops *ops = NULL;
if (flags & JSON || nvme_is_output_format_json())
ops = lm_get_json_print_ops(flags);
else if (flags & BINARY)
ops = lm_get_binary_print_ops(flags);
else
ops = lm_get_stdout_print_ops(flags);
return ops;
}
void lm_show_controller_state_data(struct nvme_lm_controller_state_data *data, size_t len,
__u32 offset, nvme_print_flags_t flags)
{
lm_print(controller_state_data, flags, data, len, offset);
}
void lm_show_controller_data_queue(struct nvme_lm_ctrl_data_queue_fid_data *data,
nvme_print_flags_t flags)
{
lm_print(controller_data_queue, flags, data);
}

31
plugins/lm/lm-print.h Normal file
View file

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef LM_PRINT_H
#define LM_PRINT_H
#include "nvme.h"
#include "libnvme.h"
struct lm_print_ops {
void (*controller_state_data)(struct nvme_lm_controller_state_data *data, size_t len,
__u32 offset);
void (*controller_data_queue)(struct nvme_lm_ctrl_data_queue_fid_data *data);
nvme_print_flags_t flags;
};
struct lm_print_ops *lm_get_stdout_print_ops(nvme_print_flags_t flags);
struct lm_print_ops *lm_get_binary_print_ops(nvme_print_flags_t flags);
#ifdef CONFIG_JSONC
struct lm_print_ops *lm_get_json_print_ops(nvme_print_flags_t flags);
#else
static inline struct lm_print_ops *lm_get_json_print_ops(nvme_print_flags_t flags)
{
return NULL;
}
#endif
void lm_show_controller_state_data(struct nvme_lm_controller_state_data *data, size_t len,
__u32 offset, nvme_print_flags_t flags);
void lm_show_controller_data_queue(struct nvme_lm_ctrl_data_queue_fid_data *data,
nvme_print_flags_t flags);
#endif /* LM_PRINT_H */

12
plugins/lm/meson.build Normal file
View file

@ -0,0 +1,12 @@
sources += [
'plugins/lm/lm-nvme.c',
'plugins/lm/lm-print.c',
'plugins/lm/lm-print-stdout.c',
'plugins/lm/lm-print-binary.c',
]
if json_c_dep.found()
sources += [
'plugins/lm/lm-print-json.c',
]
endif