Skip to content

Commit deb48a7

Browse files
authored
Merge pull request #406 from CodeConstruct/mi-ep-scan
mi: track controller lifetime under endpoint and provide endpoint scan
2 parents b578ba9 + 7e9f3cc commit deb48a7

5 files changed

Lines changed: 261 additions & 1 deletion

File tree

src/libnvme-mi.map

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ LIBNVME_MI_1_1 {
1919
nvme_mi_root_close;
2020
nvme_mi_first_endpoint;
2121
nvme_mi_next_endpoint;
22+
nvme_mi_first_ctrl;
23+
nvme_mi_next_ctrl;
2224
nvme_mi_open_mctp;
2325
nvme_mi_scan_mctp;
26+
nvme_mi_scan_ep;
2427
local:
2528
*;
2629
};

src/nvme/mi.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ struct nvme_mi_ep *nvme_mi_init_ep(nvme_root_t root)
5454
ep = calloc(1, sizeof(*ep));
5555
list_node_init(&ep->root_entry);
5656
ep->root = root;
57+
ep->controllers_scanned = false;
58+
list_head_init(&ep->controllers);
5759

5860
list_add(&root->endpoints, &ep->root_entry);
5961

@@ -71,9 +73,52 @@ struct nvme_mi_ctrl *nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id)
7173
ctrl->ep = ep;
7274
ctrl->id = ctrl_id;
7375

76+
list_add_tail(&ep->controllers, &ctrl->ep_entry);
77+
7478
return ctrl;
7579
}
7680

81+
int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan)
82+
{
83+
struct nvme_ctrl_list list;
84+
unsigned int i, n_ctrl;
85+
int rc;
86+
87+
if (ep->controllers_scanned) {
88+
if (force_rescan) {
89+
struct nvme_mi_ctrl *ctrl, *tmp;
90+
nvme_mi_for_each_ctrl_safe(ep, ctrl, tmp)
91+
nvme_mi_close_ctrl(ctrl);
92+
} else {
93+
return 0;
94+
}
95+
}
96+
97+
rc = nvme_mi_mi_read_mi_data_ctrl_list(ep, 0, &list);
98+
if (rc)
99+
return -1;
100+
101+
n_ctrl = le16_to_cpu(list.num);
102+
if (n_ctrl > NVME_ID_CTRL_LIST_MAX)
103+
return -1;
104+
105+
for (i = 0; i < n_ctrl; i++) {
106+
struct nvme_mi_ctrl *ctrl;
107+
__u16 id;
108+
109+
id = le32_to_cpu(list.identifier[i]);
110+
if (!id)
111+
continue;
112+
113+
ctrl = nvme_mi_init_ctrl(ep, id);
114+
if (!ctrl)
115+
break;
116+
}
117+
118+
ep->controllers_scanned = true;
119+
return 0;
120+
}
121+
77122
__u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len)
78123
{
79124
int i;
@@ -618,6 +663,14 @@ int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
618663

619664
void nvme_mi_close(nvme_mi_ep_t ep)
620665
{
666+
struct nvme_mi_ctrl *ctrl, *tmp;
667+
668+
/* don't look for controllers during destruction */
669+
ep->controllers_scanned = true;
670+
671+
nvme_mi_for_each_ctrl_safe(ep, ctrl, tmp)
672+
nvme_mi_close_ctrl(ctrl);
673+
621674
if (ep->transport->close)
622675
ep->transport->close(ep);
623676
list_del(&ep->root_entry);
@@ -626,6 +679,7 @@ void nvme_mi_close(nvme_mi_ep_t ep)
626679

627680
void nvme_mi_close_ctrl(nvme_mi_ctrl_t ctrl)
628681
{
682+
list_del(&ctrl->ep_entry);
629683
free(ctrl);
630684
}
631685

@@ -668,3 +722,13 @@ nvme_mi_ep_t nvme_mi_next_endpoint(nvme_root_t m, nvme_mi_ep_t ep)
668722
{
669723
return ep ? list_next(&m->endpoints, ep, root_entry) : NULL;
670724
}
725+
726+
nvme_mi_ctrl_t nvme_mi_first_ctrl(nvme_mi_ep_t ep)
727+
{
728+
return list_top(&ep->controllers, struct nvme_mi_ctrl, ep_entry);
729+
}
730+
731+
nvme_mi_ctrl_t nvme_mi_next_ctrl(nvme_mi_ep_t ep, nvme_mi_ctrl_t c)
732+
{
733+
return c ? list_next(&ep->controllers, c, ep_entry) : NULL;
734+
}

src/nvme/mi.h

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,63 @@ struct nvme_mi_ctrl;
338338
*/
339339
typedef struct nvme_mi_ctrl * nvme_mi_ctrl_t;
340340

341+
/**
342+
* nvme_mi_first_ctrl - Start controller iterator
343+
* @ep: &nvme_mi_ep_t object
344+
*
345+
* Return: first MI controller object under this root, or NULL if no controllers
346+
* are present.
347+
*
348+
* See: &nvme_mi_next_ctrl, &nvme_mi_for_each_ctrl
349+
*/
350+
nvme_mi_ctrl_t nvme_mi_first_ctrl(nvme_mi_ep_t ep);
351+
352+
/**
353+
* nvme_mi_next_ctrl - Continue ctrl iterator
354+
* @ep: &nvme_mi_ep_t object
355+
* @c: &nvme_mi_ctrl_t current position of iterator
356+
*
357+
* Return: next MI controller object after @c under this endpoint, or NULL
358+
* if no further controllers are present.
359+
*
360+
* See: &nvme_mi_first_ctrl, &nvme_mi_for_each_ctrl
361+
*/
362+
nvme_mi_ctrl_t nvme_mi_next_ctrl(nvme_mi_ep_t ep, nvme_mi_ctrl_t c);
363+
364+
/**
365+
* nvme_mi_for_each_ctrl - Iterator for NVMe-MI controllers.
366+
* @ep: &nvme_mi_ep_t containing endpoints
367+
* @c: &nvme_mi_ctrl_t object, set on each iteration
368+
*
369+
* Allows iteration of the list of controllers behind an endpoint. Unless the
370+
* controllers have already been created explicitly, you'll probably want to
371+
* call &nvme_mi_scan_ep() to scan for the controllers first.
372+
*
373+
* See: &nvme_mi_scan_ep()
374+
*/
375+
#define nvme_mi_for_each_ctrl(ep, c) \
376+
for (c = nvme_mi_first_ctrl(ep); c != NULL; \
377+
c = nvme_mi_next_ctrl(ep, c))
378+
379+
/**
380+
* nvme_mi_for_each_ctrl_safe - Iterator for NVMe-MI controllers, allowing
381+
* deletion during traversal
382+
* @ep: &nvme_mi_ep_t containing controllers
383+
* @c: &nvme_mi_ctrl_t object, set on each iteration
384+
* @_c: &nvme_mi_ctrl_t object used as temporary storage
385+
*
386+
* Allows iteration of the list of controllers behind an endpoint, safe against
387+
* deletion during iteration. Unless the controllers have already been created
388+
* explicitly (or you're just iterating to destroy controllers) you'll probably
389+
* want to call &nvme_mi_scan_ep() to scan for the controllers first.
390+
*
391+
* See: &nvme_mi_scan_ep()
392+
*/
393+
#define nvme_mi_for_each_ctrl_safe(ep, c, _c) \
394+
for (c = nvme_mi_first_ctrl(ep), _c = nvme_mi_next_ctrl(ep, c); \
395+
c != NULL; \
396+
c = _c, _c = nvme_mi_next_ctrl(ep, c))
397+
341398
/**
342399
* nvme_mi_open_mctp() - Create an endpoint using a MCTP connection.
343400
* @root: root object to create under
@@ -354,7 +411,8 @@ typedef struct nvme_mi_ctrl * nvme_mi_ctrl_t;
354411
nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, uint8_t eid);
355412

356413
/**
357-
* nvme_mi_close() - Close an endpoint connection and release resources
414+
* nvme_mi_close() - Close an endpoint connection and release resources,
415+
* including controller objects.
358416
*
359417
* @ep: Endpoint object to close
360418
*/
@@ -374,6 +432,26 @@ void nvme_mi_close(nvme_mi_ep_t ep);
374432
*/
375433
nvme_root_t nvme_mi_scan_mctp(void);
376434

435+
/**
436+
* nvme_mi_scan_ep - query an endpoint for its NVMe controllers.
437+
* @ep: Endpoint to scan
438+
* @force_rescan: close existing controllers and rescan
439+
*
440+
* This function queries an MI endpoint for the controllers available, by
441+
* performing an MI Read MI Data Structure command (requesting the
442+
* controller list). The controllers are stored in the endpoint's internal
443+
* list, and can be iterated with nvme_mi_for_each_ctrl.
444+
*
445+
* This will only scan the endpoint once, unless @force_rescan is set. If
446+
* so, all existing controller objects will be freed - the caller must not
447+
* hold a reference to those across this call.
448+
*
449+
* Return: 0 on success, non-zero on failure
450+
*
451+
* See: &nvme_mi_for_each_ctrl
452+
*/
453+
int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan);
454+
377455
/**
378456
* nvme_mi_init_ctrl() - initialise a NVMe controller.
379457
* @ep: Endpoint to create under

src/nvme/private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,14 @@ struct nvme_mi_ep {
189189
const struct nvme_mi_transport *transport;
190190
void *transport_data;
191191
struct list_node root_entry;
192+
struct list_head controllers;
193+
bool controllers_scanned;
192194
};
193195

194196
struct nvme_mi_ctrl {
195197
struct nvme_mi_ep *ep;
196198
__u16 id;
199+
struct list_node ep_entry;
197200
};
198201

199202
struct nvme_mi_ep *nvme_mi_init_ep(struct nvme_root *root);

test/mi.c

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,47 @@ static void test_endpoint_lifetime(nvme_mi_ep_t ep)
152152
assert(count == 1);
153153
}
154154

155+
unsigned int count_ep_controllers(nvme_mi_ep_t ep)
156+
{
157+
unsigned int i = 0;
158+
nvme_mi_ctrl_t ctrl;
159+
160+
nvme_mi_for_each_ctrl(ep, ctrl)
161+
i++;
162+
163+
return i;
164+
}
165+
166+
/* test that the ep->controllers list is updated on controller
167+
* creation/destruction */
168+
static void test_ctrl_lifetime(nvme_mi_ep_t ep)
169+
{
170+
nvme_mi_ctrl_t c1, c2;
171+
int count;
172+
173+
ep->controllers_scanned = true;
174+
175+
count = count_ep_controllers(ep);
176+
assert(count == 0);
177+
178+
c1 = nvme_mi_init_ctrl(ep, 1);
179+
count = count_ep_controllers(ep);
180+
assert(count == 1);
181+
182+
c2 = nvme_mi_init_ctrl(ep, 2);
183+
count = count_ep_controllers(ep);
184+
assert(count == 2);
185+
186+
nvme_mi_close_ctrl(c1);
187+
count = count_ep_controllers(ep);
188+
assert(count == 1);
189+
190+
nvme_mi_close_ctrl(c2);
191+
count = count_ep_controllers(ep);
192+
assert(count == 0);
193+
}
194+
195+
155196
/* test: basic read MI datastructure command */
156197
static int test_read_mi_data_cb(struct nvme_mi_ep *ep,
157198
struct nvme_mi_req *req,
@@ -262,6 +303,75 @@ static void test_invalid_crc(nvme_mi_ep_t ep)
262303
assert(rc != 0);
263304
}
264305

306+
/* test: test that the controller list populates the endpoint's list of
307+
* controllers */
308+
static int test_scan_ctrl_list_cb(struct nvme_mi_ep *ep,
309+
struct nvme_mi_req *req,
310+
struct nvme_mi_resp *resp,
311+
void *data)
312+
{
313+
__u8 ror, mt, *hdr, *buf;
314+
315+
assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
316+
317+
ror = req->hdr->nmp >> 7;
318+
mt = req->hdr->nmp >> 3 & 0x7;
319+
assert(ror == NVME_MI_ROR_REQ);
320+
assert(mt == NVME_MI_MT_MI);
321+
322+
/* do we have enough for a mi header? */
323+
assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
324+
325+
/* inspect response as raw bytes */
326+
hdr = (__u8 *)req->hdr;
327+
assert(hdr[4] == nvme_mi_mi_opcode_mi_data_read);
328+
assert(hdr[11] == nvme_mi_dtyp_ctrl_list);
329+
330+
/* create basic response */
331+
assert(resp->hdr_len >= sizeof(struct nvme_mi_mi_resp_hdr));
332+
assert(resp->data_len >= 4);
333+
334+
hdr = (__u8 *)resp->hdr;
335+
hdr[4] = 0; /* status */
336+
337+
buf = (__u8 *)resp->data;
338+
memset(buf, 0, resp->data_len);
339+
buf[0] = 3; buf[1] = 0; /* num controllers */
340+
buf[2] = 1; buf[3] = 0; /* id 1 */
341+
buf[4] = 4; buf[5] = 0; /* id 4 */
342+
buf[6] = 5; buf[7] = 0; /* id 5 */
343+
344+
test_transport_resp_calc_mic(resp);
345+
346+
return 0;
347+
}
348+
349+
static void test_scan_ctrl_list(nvme_mi_ep_t ep)
350+
{
351+
struct nvme_mi_ctrl *ctrl;
352+
353+
ep->controllers_scanned = false;
354+
355+
test_set_transport_callback(ep, test_scan_ctrl_list_cb, NULL);
356+
357+
nvme_mi_scan_ep(ep, false);
358+
359+
ctrl = nvme_mi_first_ctrl(ep);
360+
assert(ctrl);
361+
assert(ctrl->id == 1);
362+
363+
ctrl = nvme_mi_next_ctrl(ep, ctrl);
364+
assert(ctrl);
365+
assert(ctrl->id == 4);
366+
367+
ctrl = nvme_mi_next_ctrl(ep, ctrl);
368+
assert(ctrl);
369+
assert(ctrl->id == 5);
370+
371+
ctrl = nvme_mi_next_ctrl(ep, ctrl);
372+
assert(ctrl == NULL);
373+
}
374+
265375
/* test: simple NVMe admin request/response */
266376
static int test_admin_id_cb(struct nvme_mi_ep *ep,
267377
struct nvme_mi_req *req,
@@ -389,9 +499,11 @@ struct test {
389499
void (*fn)(nvme_mi_ep_t);
390500
} tests[] = {
391501
DEFINE_TEST(endpoint_lifetime),
502+
DEFINE_TEST(ctrl_lifetime),
392503
DEFINE_TEST(read_mi_data),
393504
DEFINE_TEST(transport_fail),
394505
DEFINE_TEST(transport_describe),
506+
DEFINE_TEST(scan_ctrl_list),
395507
DEFINE_TEST(invalid_crc),
396508
DEFINE_TEST(admin_id),
397509
DEFINE_TEST(admin_err_resp),

0 commit comments

Comments
 (0)