Skip to content

Commit 9bd757c

Browse files
committed
mi: Add basic tests
This change adds a couple of basic tests for the mi infrastrcucture. To do this, we define a test "transport", which just allows inserting a custom function as the transport's ->submit callback. Each test is then a pair of: the test itself, and a callback to implement a specific response from the test "device". Signed-off-by: Jeremy Kerr <[email protected]>
1 parent f7bd771 commit 9bd757c

4 files changed

Lines changed: 233 additions & 1 deletion

File tree

src/nvme/mi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ struct nvme_mi_ctrl *nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id)
6363
return ctrl;
6464
}
6565

66-
static __u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len)
66+
__u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len)
6767
{
6868
int i;
6969

src/nvme/private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,7 @@ struct nvme_mi_ctrl {
195195

196196
struct nvme_mi_ep *nvme_mi_init_ep(struct nvme_root *root);
197197

198+
/* for tests, we need to calculate the correct MICs */
199+
__u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len);
200+
198201
#endif /* _LIBNVME_PRIVATE_H */

test/meson.build

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,20 @@ zns = executable(
3636
dependencies: libnvme_dep,
3737
include_directories: [incdir, internal_incdir]
3838
)
39+
40+
# The management interface tests don't require hardware, we have a small
41+
# test-mi endpoint instead.
42+
mi_sources = [
43+
'../src/nvme/mi.c',
44+
'../src/nvme/log.c',
45+
'../src/nvme/cleanup.c',
46+
]
47+
48+
mi = executable(
49+
'test-mi',
50+
['mi.c'] + mi_sources,
51+
dependencies: libnvme_mi_dep,
52+
include_directories: [incdir, internal_incdir]
53+
)
54+
55+
test('mi', mi)

test/mi.c

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
/**
3+
* This file is part of libnvme.
4+
* Copyright (c) 2022 Code Construct
5+
*/
6+
7+
#undef NDEBUG
8+
#include <assert.h>
9+
#include <stdlib.h>
10+
11+
/* we define a custom transport, so need the internal headers */
12+
#include "nvme/private.h"
13+
14+
#include "libnvme-mi.h"
15+
16+
typedef int (*test_submit_cb)(struct nvme_mi_ep *ep,
17+
struct nvme_mi_req *req,
18+
struct nvme_mi_resp *resp,
19+
void *data);
20+
21+
struct test_transport_data {
22+
unsigned int magic;
23+
test_submit_cb submit_cb;
24+
void *submit_cb_data;
25+
};
26+
27+
static const int test_transport_magic = 0x74657374;
28+
29+
static int test_transport_submit(struct nvme_mi_ep *ep,
30+
struct nvme_mi_req *req,
31+
struct nvme_mi_resp *resp)
32+
{
33+
struct test_transport_data *tpd = ep->transport_data;
34+
35+
assert(tpd->magic == test_transport_magic);
36+
37+
/* start from a zeroed response */
38+
memset(resp->hdr, 0, resp->hdr_len);
39+
memset(resp->data, 0, resp->data_len);
40+
41+
if (tpd->submit_cb)
42+
return tpd->submit_cb(ep, req, resp, tpd->submit_cb_data);
43+
44+
return 0;
45+
}
46+
47+
static void test_transport_close(struct nvme_mi_ep *ep)
48+
{
49+
struct test_transport_data *tpd = ep->transport_data;
50+
assert(tpd->magic == test_transport_magic);
51+
free(tpd);
52+
}
53+
54+
/* internal test helper to generate correct response crc */
55+
static void test_transport_resp_calc_mic(struct nvme_mi_resp *resp)
56+
{
57+
extern __u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len);
58+
__u32 crc = 0xffffffff;
59+
60+
crc = nvme_mi_crc32_update(crc, resp->hdr, resp->hdr_len);
61+
crc = nvme_mi_crc32_update(crc, resp->data, resp->data_len);
62+
63+
resp->mic = ~crc;
64+
}
65+
66+
static const struct nvme_mi_transport test_transport = {
67+
.name = "test-mi",
68+
.mic_enabled = true,
69+
.submit = test_transport_submit,
70+
.close = test_transport_close,
71+
};
72+
73+
static void test_set_transport_callback(nvme_mi_ep_t ep, test_submit_cb cb,
74+
void *data)
75+
{
76+
struct test_transport_data *tpd = ep->transport_data;
77+
assert(tpd->magic == test_transport_magic);
78+
79+
tpd->submit_cb = cb;
80+
tpd->submit_cb_data = data;
81+
}
82+
83+
nvme_mi_ep_t nvme_mi_open_test(nvme_root_t root)
84+
{
85+
struct test_transport_data *tpd;
86+
struct nvme_mi_ep *ep;
87+
88+
ep = nvme_mi_init_ep(root);
89+
assert(ep);
90+
91+
tpd = malloc(sizeof(*tpd));
92+
assert(tpd);
93+
94+
tpd->magic = test_transport_magic;
95+
96+
ep->transport = &test_transport;
97+
ep->transport_data = tpd;
98+
99+
return ep;
100+
}
101+
102+
/* test: basic read MI datastructure command */
103+
static int test_read_mi_data_cb(struct nvme_mi_ep *ep,
104+
struct nvme_mi_req *req,
105+
struct nvme_mi_resp *resp,
106+
void *data)
107+
{
108+
__u8 ror, mt, *hdr, *buf;
109+
110+
assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
111+
112+
ror = req->hdr->nmp >> 7;
113+
mt = req->hdr->nmp >> 3 & 0x7;
114+
assert(ror == NVME_MI_ROR_REQ);
115+
assert(mt == NVME_MI_MT_MI);
116+
117+
/* do we have enough for a mi header? */
118+
assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
119+
120+
/* inspect response as raw bytes */
121+
hdr = (__u8 *)req->hdr;
122+
assert(hdr[4] == nvme_mi_mi_opcode_mi_data_read);
123+
124+
/* create basic response */
125+
assert(resp->hdr_len >= sizeof(struct nvme_mi_mi_resp_hdr));
126+
assert(resp->data_len >= 4);
127+
128+
hdr = (__u8 *)resp->hdr;
129+
hdr[4] = 0; /* status */
130+
131+
buf = (__u8 *)resp->data;
132+
memset(buf, 0, resp->data_len);
133+
buf[0] = 1; /* NUMP */
134+
buf[1] = 1; /* MJR */
135+
buf[2] = 2; /* MNR */
136+
137+
test_transport_resp_calc_mic(resp);
138+
139+
return 0;
140+
}
141+
142+
static void test_read_mi_data(nvme_mi_ep_t ep)
143+
{
144+
struct nvme_mi_read_nvm_ss_info ss_info;
145+
int rc;
146+
147+
test_set_transport_callback(ep, test_read_mi_data_cb, NULL);
148+
149+
rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
150+
assert(rc == 0);
151+
}
152+
153+
/* test: failed transport */
154+
static int test_transport_fail_cb(struct nvme_mi_ep *ep,
155+
struct nvme_mi_req *req,
156+
struct nvme_mi_resp *resp,
157+
void *data)
158+
{
159+
return -1;
160+
}
161+
162+
static void test_transport_fail(nvme_mi_ep_t ep)
163+
{
164+
struct nvme_mi_read_nvm_ss_info ss_info;
165+
int rc;
166+
167+
test_set_transport_callback(ep, test_transport_fail_cb, NULL);
168+
rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
169+
assert(rc != 0);
170+
}
171+
172+
/* test: invalid crc */
173+
static int test_invalid_crc_cb(struct nvme_mi_ep *ep,
174+
struct nvme_mi_req *req,
175+
struct nvme_mi_resp *resp,
176+
void *data)
177+
{
178+
resp->mic = 0;
179+
return 0;
180+
}
181+
182+
static void test_invalid_crc(nvme_mi_ep_t ep)
183+
{
184+
struct nvme_mi_read_nvm_ss_info ss_info;
185+
int rc;
186+
187+
test_set_transport_callback(ep, test_invalid_crc_cb, NULL);
188+
rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
189+
assert(rc != 0);
190+
}
191+
192+
193+
int main(void)
194+
{
195+
nvme_root_t root;
196+
nvme_mi_ep_t ep;
197+
198+
root = nvme_mi_create_root(NULL, DEFAULT_LOGLEVEL);
199+
assert(root);
200+
201+
ep = nvme_mi_open_test(root);
202+
assert(ep);
203+
204+
test_read_mi_data(ep);
205+
test_transport_fail(ep);
206+
test_invalid_crc(ep);
207+
208+
nvme_mi_close(ep);
209+
nvme_mi_free_root(root);
210+
211+
return EXIT_SUCCESS;
212+
}

0 commit comments

Comments
 (0)