Skip to content

Commit f7bd771

Browse files
committed
libnvme-mi: Introduce NVMe Managament Interface library
This change adds support for the NVMe-MI protocol, as a secondary library: libnvme-mi.{a,so,h}. This allows management of NVMe devices, typically through NVMe Admin Channel commands, transmitted over a side-band channel - typically MCTP over SMBus. There's a fair amount of shared structure between the direct-ioctl and MI channel implementations; for example, the Admin command set is used mostly as-is over the MI transport. The library is built as separate .so/.a objects as client applications would typically either use one or the other. MI-specific functions are added with a 'nvme_mi' prefix. This change introduces a small set of MI commands using this channel. We'll extend this to further MI commands in subsequent changes, as well as implement Admin Channel commands too. We currently assume a MCTP transport for NVMe-commands, using the new AF_MCTP socket support in Linux. However, the transport-specific code is kept somewhat separate, through the internal struct nvme_mi_transport interface. This allows potential alternative transports in future - for example in-band PCIe. Finally, we start a new example application, mi-mctp, which provides a reference application using the new library. Signed-off-by: Jeremy Kerr <[email protected]>
1 parent aacaeea commit f7bd771

10 files changed

Lines changed: 862 additions & 0 deletions

File tree

examples/meson.build

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,10 @@ executable(
3232
dependencies: libnvme_dep,
3333
include_directories: [incdir, internal_incdir]
3434
)
35+
36+
executable(
37+
'mi-mctp',
38+
['mi-mctp.c'],
39+
dependencies: libnvme_mi_dep,
40+
include_directories: [incdir, internal_incdir]
41+
)

examples/mi-mctp.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
/**
3+
* This file is part of libnvme.
4+
* Copyright (c) 2021 Code Construct Pty Ltd.
5+
*
6+
* Authors: Jeremy Kerr <[email protected]>
7+
*/
8+
9+
/**
10+
* mi-mctp: open a MI connection over MCTP, and query controller info
11+
*/
12+
13+
#include <err.h>
14+
#include <stdio.h>
15+
#include <stdlib.h>
16+
17+
#include <libnvme-mi.h>
18+
19+
#include <ccan/array_size/array_size.h>
20+
#include <ccan/endian/endian.h>
21+
22+
static void show_port_pcie(struct nvme_mi_read_port_info *port)
23+
{
24+
printf(" PCIe max payload: 0x%x\n", 0x80 << port->pcie.mps);
25+
printf(" PCIe link speeds: 0x%02x\n", port->pcie.sls);
26+
printf(" PCIe current speed: 0x%02x\n", port->pcie.cls);
27+
printf(" PCIe max link width: 0x%02x\n", port->pcie.mlw);
28+
printf(" PCIe neg link width: 0x%02x\n", port->pcie.nlw);
29+
printf(" PCIe port: 0x%02x\n", port->pcie.pn);
30+
}
31+
32+
static void show_port_smbus(struct nvme_mi_read_port_info *port)
33+
{
34+
printf(" SMBus address: 0x%02x\n", port->smb.vpd_addr);
35+
printf(" VPD access freq: 0x%02x\n", port->smb.mvpd_freq);
36+
printf(" MCTP address: 0x%02x\n", port->smb.mme_addr);
37+
printf(" MCTP access freq: 0x%02x\n", port->smb.mme_freq);
38+
printf(" NVMe basic management: %s\n",
39+
(port->smb.nvmebm & 0x1) ? "enabled" : "disabled");
40+
}
41+
42+
static struct {
43+
int typeid;
44+
const char *name;
45+
void (*fn)(struct nvme_mi_read_port_info *);
46+
} port_types[] = {
47+
{ 0x00, "inactive", NULL },
48+
{ 0x01, "PCIe", show_port_pcie },
49+
{ 0x02, "SMBus", show_port_smbus },
50+
};
51+
52+
static int show_port(nvme_mi_ep_t ep, int portid)
53+
{
54+
void (*show_fn)(struct nvme_mi_read_port_info *);
55+
struct nvme_mi_read_port_info port;
56+
const char *typestr;
57+
int rc;
58+
59+
rc = nvme_mi_mi_read_mi_data_port(ep, portid, &port);
60+
if (rc)
61+
return rc;
62+
63+
if (port.portt < ARRAY_SIZE(port_types)) {
64+
show_fn = port_types[port.portt].fn;
65+
typestr = port_types[port.portt].name;
66+
} else {
67+
show_fn = NULL;
68+
typestr = "INVALID";
69+
}
70+
71+
printf(" port %d\n", portid);
72+
printf(" type %s[%d]\n", typestr, port.portt);
73+
printf(" MCTP MTU: %d\n", port.mmctptus);
74+
printf(" MEB size: %d\n", port.meb);
75+
76+
if (show_fn)
77+
show_fn(&port);
78+
79+
return 0;
80+
}
81+
82+
int main(int argc, char **argv)
83+
{
84+
struct nvme_mi_nvm_ss_health_status ss_health;
85+
struct nvme_mi_read_nvm_ss_info ss_info;
86+
nvme_root_t root;
87+
nvme_mi_ep_t ep;
88+
uint8_t eid;
89+
int net;
90+
int rc;
91+
int i;
92+
93+
if (argc != 3) {
94+
fprintf(stderr, "usage: %s <net> <eid>\n", argv[0]);
95+
return EXIT_FAILURE;
96+
}
97+
98+
net = atoi(argv[1]);
99+
eid = atoi(argv[2]) & 0xff;
100+
101+
root = nvme_mi_create_root(stderr, DEFAULT_LOGLEVEL);
102+
if (!root)
103+
err(EXIT_FAILURE, "can't create NVMe root");
104+
105+
ep = nvme_mi_open_mctp(root, net, eid);
106+
if (!ep)
107+
err(EXIT_FAILURE, "can't open MCTP endpoint %d:%d", net, eid);
108+
109+
rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
110+
if (rc)
111+
err(EXIT_FAILURE, "can't perform Read MI Data operation");
112+
113+
printf("NVMe MI subsys info:\n");
114+
printf(" num ports: %d\n", ss_info.nump + 1);
115+
printf(" major ver: %d\n", ss_info.mjr);
116+
printf(" minor ver: %d\n", ss_info.mnr);
117+
118+
printf("NVMe MI port info:\n");
119+
for (i = 0; i <= ss_info.nump; i++)
120+
show_port(ep, i);
121+
122+
rc = nvme_mi_mi_subsystem_health_status_poll(ep, true, &ss_health);
123+
if (rc)
124+
err(EXIT_FAILURE, "can't perform Health Status Poll operation");
125+
126+
printf("NVMe MI subsys health:\n");
127+
printf(" subsystem status: 0x%x\n", ss_health.nss);
128+
printf(" smart warnings: 0x%x\n", ss_health.sw);
129+
printf(" composite temp: %d\n", ss_health.ctemp);
130+
printf(" drive life used: %d%%\n", ss_health.pdlu);
131+
printf(" controller status: 0x%04x\n", le16_to_cpu(ss_health.pdlu));
132+
133+
nvme_mi_close(ep);
134+
135+
nvme_mi_free_root(root);
136+
137+
return EXIT_SUCCESS;
138+
}
139+
140+

meson.build

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ conf.set10(
155155
),
156156
description: 'Is isblank() available?'
157157
)
158+
conf.set10(
159+
'HAVE_LINUX_MCTP_H',
160+
cc.compiles(
161+
'''#include <linux/mctp.h>''',
162+
name: 'linux/mctp.h'
163+
),
164+
description: 'Is linux/mctp.h include-able?'
165+
)
158166

159167
################################################################################
160168
substs = configuration_data()

src/libnvme-mi.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
/*
3+
* This file is part of libnvme.
4+
* Copyright (c) 2021 Code Construct Pty Ltd
5+
*
6+
* Authors: Jeremy Kerr <[email protected]>
7+
*/
8+
9+
#ifndef _LIBNVME_MI_H
10+
#define _LIBNVME_MI_H
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
#include "nvme/types.h"
17+
#include "nvme/mi.h"
18+
#include "nvme/log.h"
19+
20+
#ifdef __cplusplus
21+
}
22+
#endif
23+
24+
#endif /* _LIBNVME_MI_H */

src/libnvme-mi.map

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
LIBNVME_MI_1_1 {
2+
global:
3+
nvme_mi_create_root;
4+
nvme_mi_free_root;
5+
nvme_mi_init_ctrl;
6+
nvme_mi_close_ctrl;
7+
nvme_mi_close;
8+
nvme_mi_mi_read_mi_data_subsys;
9+
nvme_mi_mi_read_mi_data_port;
10+
nvme_mi_mi_subsystem_health_status_poll;
11+
nvme_mi_open_mctp;
12+
local:
13+
*;
14+
};

src/meson.build

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ sources = [
1616
'nvme/util.c',
1717
]
1818

19+
mi_sources = [
20+
'nvme/cleanup.c',
21+
'nvme/log.c',
22+
'nvme/mi.c',
23+
'nvme/mi-mctp.c',
24+
]
25+
1926
if conf.get('CONFIG_JSONC')
2027
sources += 'nvme/json.c'
2128
endif
@@ -26,9 +33,15 @@ deps = [
2633
openssl_dep,
2734
]
2835

36+
mi_deps = [
37+
libuuid_dep,
38+
]
39+
2940
source_dir = meson.current_source_dir()
3041
mapfile = 'libnvme.map'
3142
version_script_arg = join_paths(source_dir, mapfile)
43+
mi_mapfile = 'libnvme-mi.map'
44+
mi_version_script_arg = join_paths(source_dir, mi_mapfile)
3245

3346
libnvme = library(
3447
'nvme', # produces libnvme.so
@@ -60,6 +73,26 @@ libnvme_dep = declare_dependency(
6073
link_with: libnvme,
6174
)
6275

76+
libnvme_mi = library(
77+
'nvme-mi', # produces libnvme-mi.so
78+
mi_sources,
79+
version: library_version,
80+
link_args: ['-Wl,--version-script=' + mi_version_script_arg],
81+
dependencies: mi_deps,
82+
link_depends: mi_mapfile,
83+
include_directories: [incdir, internal_incdir],
84+
install: true,
85+
link_with: libccan,
86+
)
87+
88+
libnvme_mi_dep = declare_dependency(
89+
include_directories: ['.'],
90+
dependencies: [
91+
libuuid_dep.partial_dependency(compile_args: true, includes: true),
92+
],
93+
link_with: libnvme_mi,
94+
)
95+
6396
mode = ['rw-r--r--', 0, 0]
6497
install_headers('libnvme.h', install_mode: mode)
6598
install_headers([
@@ -71,6 +104,7 @@ install_headers([
71104
'nvme/tree.h',
72105
'nvme/types.h',
73106
'nvme/util.h',
107+
'nvme/mi.h',
74108
],
75109
subdir: 'nvme',
76110
install_mode: mode,

0 commit comments

Comments
 (0)