Skip to content

Commit

Permalink
plugin/lm: Introduce Live Migration plugin
Browse files Browse the repository at this point in the history
Implementation of TP 4159 PCIe Infrastructure for Live Migration plugin.
Includes command support for Track Send, Migration Receive, Migration
Send, and Controller Data Queue; Identify Controller LM related fields;
Bash and ZSH completions.

Changes are isolated to the User Data Migration subset, with Track
Memory functionality deferred to a future commit.

Signed-off-by: Nate Thornton <[email protected]>
  • Loading branch information
NateThornton authored and igaw committed Dec 20, 2024
1 parent 6a73c83 commit 6d2a869
Show file tree
Hide file tree
Showing 9 changed files with 1,060 additions and 0 deletions.
669 changes: 669 additions & 0 deletions plugins/lm/lm-nvme.c

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions plugins/lm/lm-nvme.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2024 Samsung Electronics Co., LTD.
*
* Authors: Nate Thornton <[email protected]>
*/

#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"
25 changes: 25 additions & 0 deletions plugins/lm/lm-print-binary.c
Original file line number Diff line number Diff line change
@@ -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 changes: 109 additions & 0 deletions plugins/lm/lm-print-json.c
Original file line number Diff line number Diff line change
@@ -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;
}
145 changes: 145 additions & 0 deletions plugins/lm/lm-print-stdout.c
Original file line number Diff line number Diff line change
@@ -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 changes: 38 additions & 0 deletions plugins/lm/lm-print.c
Original file line number Diff line number Diff line change
@@ -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 changes: 31 additions & 0 deletions plugins/lm/lm-print.h
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions plugins/lm/meson.build
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 6d2a869

Please sign in to comment.