Skip to content

Commit bbee294

Browse files
authored
Merge pull request #76 from jk-ozlabs/mi
Initial NVMe-MI protocol support
2 parents f603510 + f87fa1c commit bbee294

12 files changed

Lines changed: 1498 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: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
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 <assert.h>
14+
#include <err.h>
15+
#include <stdio.h>
16+
#include <stdlib.h>
17+
#include <stddef.h>
18+
#include <string.h>
19+
20+
#include <libnvme-mi.h>
21+
22+
#include <ccan/array_size/array_size.h>
23+
#include <ccan/endian/endian.h>
24+
25+
static void show_port_pcie(struct nvme_mi_read_port_info *port)
26+
{
27+
printf(" PCIe max payload: 0x%x\n", 0x80 << port->pcie.mps);
28+
printf(" PCIe link speeds: 0x%02x\n", port->pcie.sls);
29+
printf(" PCIe current speed: 0x%02x\n", port->pcie.cls);
30+
printf(" PCIe max link width: 0x%02x\n", port->pcie.mlw);
31+
printf(" PCIe neg link width: 0x%02x\n", port->pcie.nlw);
32+
printf(" PCIe port: 0x%02x\n", port->pcie.pn);
33+
}
34+
35+
static void show_port_smbus(struct nvme_mi_read_port_info *port)
36+
{
37+
printf(" SMBus address: 0x%02x\n", port->smb.vpd_addr);
38+
printf(" VPD access freq: 0x%02x\n", port->smb.mvpd_freq);
39+
printf(" MCTP address: 0x%02x\n", port->smb.mme_addr);
40+
printf(" MCTP access freq: 0x%02x\n", port->smb.mme_freq);
41+
printf(" NVMe basic management: %s\n",
42+
(port->smb.nvmebm & 0x1) ? "enabled" : "disabled");
43+
}
44+
45+
static struct {
46+
int typeid;
47+
const char *name;
48+
void (*fn)(struct nvme_mi_read_port_info *);
49+
} port_types[] = {
50+
{ 0x00, "inactive", NULL },
51+
{ 0x01, "PCIe", show_port_pcie },
52+
{ 0x02, "SMBus", show_port_smbus },
53+
};
54+
55+
static int show_port(nvme_mi_ep_t ep, int portid)
56+
{
57+
void (*show_fn)(struct nvme_mi_read_port_info *);
58+
struct nvme_mi_read_port_info port;
59+
const char *typestr;
60+
int rc;
61+
62+
rc = nvme_mi_mi_read_mi_data_port(ep, portid, &port);
63+
if (rc)
64+
return rc;
65+
66+
if (port.portt < ARRAY_SIZE(port_types)) {
67+
show_fn = port_types[port.portt].fn;
68+
typestr = port_types[port.portt].name;
69+
} else {
70+
show_fn = NULL;
71+
typestr = "INVALID";
72+
}
73+
74+
printf(" port %d\n", portid);
75+
printf(" type %s[%d]\n", typestr, port.portt);
76+
printf(" MCTP MTU: %d\n", port.mmctptus);
77+
printf(" MEB size: %d\n", port.meb);
78+
79+
if (show_fn)
80+
show_fn(&port);
81+
82+
return 0;
83+
}
84+
85+
int do_info(nvme_mi_ep_t ep)
86+
{
87+
struct nvme_mi_nvm_ss_health_status ss_health;
88+
struct nvme_mi_read_nvm_ss_info ss_info;
89+
int i, rc;
90+
91+
rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
92+
if (rc) {
93+
warn("can't perform Read MI Data operation");
94+
return -1;
95+
}
96+
97+
printf("NVMe MI subsys info:\n");
98+
printf(" num ports: %d\n", ss_info.nump + 1);
99+
printf(" major ver: %d\n", ss_info.mjr);
100+
printf(" minor ver: %d\n", ss_info.mnr);
101+
102+
printf("NVMe MI port info:\n");
103+
for (i = 0; i <= ss_info.nump; i++)
104+
show_port(ep, i);
105+
106+
rc = nvme_mi_mi_subsystem_health_status_poll(ep, true, &ss_health);
107+
if (rc)
108+
err(EXIT_FAILURE, "can't perform Health Status Poll operation");
109+
110+
printf("NVMe MI subsys health:\n");
111+
printf(" subsystem status: 0x%x\n", ss_health.nss);
112+
printf(" smart warnings: 0x%x\n", ss_health.sw);
113+
printf(" composite temp: %d\n", ss_health.ctemp);
114+
printf(" drive life used: %d%%\n", ss_health.pdlu);
115+
printf(" controller status: 0x%04x\n", le16_to_cpu(ss_health.pdlu));
116+
117+
return 0;
118+
}
119+
120+
static int show_ctrl(nvme_mi_ep_t ep, uint16_t ctrl_id)
121+
{
122+
struct nvme_mi_read_ctrl_info ctrl;
123+
int rc;
124+
125+
rc = nvme_mi_mi_read_mi_data_ctrl(ep, ctrl_id, &ctrl);
126+
if (rc)
127+
return rc;
128+
129+
printf(" Controller id: %d\n", ctrl_id);
130+
printf(" port id: %d\n", ctrl.portid);
131+
if (ctrl.prii & 0x1) {
132+
uint16_t bdfn = le16_to_cpu(ctrl.pri);
133+
printf(" PCIe routing valid\n");
134+
printf(" PCIe bus: 0x%02x\n", bdfn >> 8);
135+
printf(" PCIe dev: 0x%02x\n", bdfn >> 3 & 0x1f);
136+
printf(" PCIe fn : 0x%02x\n", bdfn & 0x7);
137+
} else {
138+
printf(" PCIe routing invalid\n");
139+
}
140+
printf(" PCI vendor: %04x\n", le16_to_cpu(ctrl.vid));
141+
printf(" PCI device: %04x\n", le16_to_cpu(ctrl.did));
142+
printf(" PCI subsys vendor: %04x\n", le16_to_cpu(ctrl.ssvid));
143+
printf(" PCI subsys device: %04x\n", le16_to_cpu(ctrl.ssvid));
144+
145+
return 0;
146+
}
147+
148+
static int do_controllers(nvme_mi_ep_t ep)
149+
{
150+
struct nvme_ctrl_list ctrl_list;
151+
int rc, i;
152+
153+
rc = nvme_mi_mi_read_mi_data_ctrl_list(ep, 0, &ctrl_list);
154+
if (rc) {
155+
warnx("Can't perform Controller List operation");
156+
return rc;
157+
}
158+
159+
printf("NVMe controller list:\n");
160+
for (i = 0; i < le16_to_cpu(ctrl_list.num); i++) {
161+
uint16_t id = le16_to_cpu(ctrl_list.identifier[i]);
162+
show_ctrl(ep, id);
163+
}
164+
return 0;
165+
}
166+
167+
static const char *__copy_id_str(const void *field, size_t size,
168+
char *buf, size_t buf_size)
169+
{
170+
assert(size < buf_size);
171+
strncpy(buf, field, size);
172+
buf[size] = '\0';
173+
return buf;
174+
}
175+
176+
#define copy_id_str(f,b) __copy_id_str(f, sizeof(f), b, sizeof(b))
177+
178+
int do_identify(nvme_mi_ep_t ep, int argc, char **argv)
179+
{
180+
struct nvme_mi_ctrl *ctrl;
181+
struct nvme_id_ctrl id;
182+
uint16_t ctrl_id;
183+
char buf[41];
184+
int rc, tmp;
185+
186+
if (argc != 2) {
187+
fprintf(stderr, "no controller ID specified\n");
188+
return -1;
189+
}
190+
191+
tmp = atoi(argv[1]);
192+
if (tmp < 0 || tmp > 0xffff) {
193+
fprintf(stderr, "invalid controller ID\n");
194+
return -1;
195+
}
196+
197+
ctrl_id = tmp & 0xffff;
198+
199+
ctrl = nvme_mi_init_ctrl(ep, tmp);
200+
if (!ctrl) {
201+
warn("can't create controller");
202+
return -1;
203+
}
204+
205+
/* we only use the fields before rab; just request partial ID data */
206+
rc = nvme_mi_admin_identify_ctrl_partial(ctrl, &id, 0,
207+
offsetof(struct nvme_id_ctrl, rab));
208+
if (rc) {
209+
warn("can't perform Admin Identify command");
210+
return -1;
211+
}
212+
213+
printf("NVMe Controller %d identify\n", ctrl_id);
214+
printf(" PCI vendor: %04x\n", le16_to_cpu(id.vid));
215+
printf(" PCI subsys vendor: %04x\n", le16_to_cpu(id.ssvid));
216+
printf(" Serial number: %s\n", copy_id_str(id.sn, buf));
217+
printf(" Model number: %s\n", copy_id_str(id.mn, buf));
218+
printf(" Firmware rev: %s\n", copy_id_str(id.fr, buf));
219+
220+
return 0;
221+
}
222+
223+
enum action {
224+
ACTION_INFO,
225+
ACTION_CONTROLLERS,
226+
ACTION_IDENTIFY,
227+
};
228+
229+
int main(int argc, char **argv)
230+
{
231+
enum action action;
232+
nvme_root_t root;
233+
nvme_mi_ep_t ep;
234+
uint8_t eid;
235+
int rc, net;
236+
237+
if (argc < 3) {
238+
fprintf(stderr,
239+
"usage: %s <net> <eid> [action] [action args]\n",
240+
argv[0]);
241+
fprintf(stderr, "where action is:\n"
242+
" info\n"
243+
" controllers\n"
244+
" identify <controller-id>\n");
245+
return EXIT_FAILURE;
246+
}
247+
248+
net = atoi(argv[1]);
249+
eid = atoi(argv[2]) & 0xff;
250+
argv += 2;
251+
argc -= 2;
252+
253+
if (argc == 1) {
254+
action = ACTION_INFO;
255+
} else {
256+
char *action_str = argv[1];
257+
argc--;
258+
argv++;
259+
260+
if (!strcmp(action_str, "info")) {
261+
action = ACTION_INFO;
262+
} else if (!strcmp(action_str, "controllers")) {
263+
action = ACTION_CONTROLLERS;
264+
} else if (!strcmp(action_str, "identify")) {
265+
action = ACTION_IDENTIFY;
266+
} else {
267+
fprintf(stderr, "invalid action '%s'\n", action_str);
268+
return EXIT_FAILURE;
269+
}
270+
}
271+
272+
root = nvme_mi_create_root(stderr, DEFAULT_LOGLEVEL);
273+
if (!root)
274+
err(EXIT_FAILURE, "can't create NVMe root");
275+
276+
ep = nvme_mi_open_mctp(root, net, eid);
277+
if (!ep)
278+
err(EXIT_FAILURE, "can't open MCTP endpoint %d:%d", net, eid);
279+
280+
switch (action) {
281+
case ACTION_INFO:
282+
rc = do_info(ep);
283+
break;
284+
case ACTION_CONTROLLERS:
285+
rc = do_controllers(ep);
286+
break;
287+
case ACTION_IDENTIFY:
288+
rc = do_identify(ep, argc, argv);
289+
break;
290+
}
291+
292+
nvme_mi_close(ep);
293+
294+
nvme_mi_free_root(root);
295+
296+
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
297+
}
298+
299+

meson.build

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

160168
################################################################################
161169
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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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_read_mi_data_ctrl_list;
11+
nvme_mi_mi_read_mi_data_ctrl;
12+
nvme_mi_mi_subsystem_health_status_poll;
13+
nvme_mi_admin_identify_ctrl;
14+
nvme_mi_admin_identify_ctrl_partial;
15+
nvme_mi_admin_identify_ctrl_list;
16+
nvme_mi_open_mctp;
17+
local:
18+
*;
19+
};

0 commit comments

Comments
 (0)