Skip to content

Commit de534bc

Browse files
committed
mi: Add direct admin transfer API
Callers may want to invoke arbitrary admin commands; so implement a direct admin API, passing just the Admin request/response header pointers: int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, struct nvme_mi_admin_req_hdr *admin_req, size_t req_data_size, struct nvme_mi_admin_resp_hdr *admin_resp, off_t resp_data_offset, size_t *resp_data_size); Signed-off-by: Jeremy Kerr <[email protected]>
1 parent a707319 commit de534bc

4 files changed

Lines changed: 159 additions & 0 deletions

File tree

examples/mi-mctp.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,104 @@ int do_get_log_page(nvme_mi_ep_t ep, int argc, char **argv)
323323
return 0;
324324
}
325325

326+
int do_admin_raw(nvme_mi_ep_t ep, int argc, char **argv)
327+
{
328+
struct nvme_mi_admin_req_hdr req;
329+
struct nvme_mi_admin_resp_hdr *resp;
330+
struct nvme_mi_ctrl *ctrl;
331+
size_t resp_data_len;
332+
unsigned long tmp;
333+
uint8_t buf[512];
334+
uint16_t ctrl_id;
335+
uint8_t opcode;
336+
__le32 *cdw;
337+
int i, rc;
338+
339+
if (argc < 2) {
340+
fprintf(stderr, "no controller ID specified\n");
341+
return -1;
342+
}
343+
344+
if (argc < 3) {
345+
fprintf(stderr, "no opcode specified\n");
346+
return -1;
347+
}
348+
349+
tmp = atoi(argv[1]);
350+
if (tmp < 0 || tmp > 0xffff) {
351+
fprintf(stderr, "invalid controller ID\n");
352+
return -1;
353+
}
354+
ctrl_id = tmp & 0xffff;
355+
356+
tmp = atoi(argv[2]);
357+
if (tmp < 0 || tmp > 0xff) {
358+
fprintf(stderr, "invalid opcode\n");
359+
return -1;
360+
}
361+
opcode = tmp & 0xff;
362+
363+
memset(&req, 0, sizeof(req));
364+
req.opcode = opcode;
365+
req.ctrl_id = cpu_to_le16(ctrl_id);
366+
367+
/* The cdw10 - cdw16 fields are contiguous in req; set from argv. */
368+
cdw = (void *)&req + offsetof(typeof(req), cdw10);
369+
for (i = 0; i < 6; i++) {
370+
if (argc >= 4 + i)
371+
tmp = strtoul(argv[3 + i], NULL, 0);
372+
else
373+
tmp = 0;
374+
*cdw = cpu_to_le32(tmp & 0xffffffff);
375+
cdw++;
376+
}
377+
378+
printf("Admin request:\n");
379+
printf(" opcode: 0x%02x\n", req.opcode);
380+
printf(" ctrl: 0x%04x\n", le16_to_cpu(req.ctrl_id));
381+
printf(" cdw10: 0x%08x\n", le32_to_cpu(req.cdw10));
382+
printf(" cdw11: 0x%08x\n", le32_to_cpu(req.cdw11));
383+
printf(" cdw12: 0x%08x\n", le32_to_cpu(req.cdw12));
384+
printf(" cdw13: 0x%08x\n", le32_to_cpu(req.cdw13));
385+
printf(" cdw14: 0x%08x\n", le32_to_cpu(req.cdw14));
386+
printf(" cdw15: 0x%08x\n", le32_to_cpu(req.cdw15));
387+
printf(" raw:\n");
388+
hexdump((void *)&req, sizeof(req));
389+
390+
memset(buf, 0, sizeof(buf));
391+
resp = (void *)buf;
392+
393+
ctrl = nvme_mi_init_ctrl(ep, ctrl_id);
394+
if (!ctrl) {
395+
warn("can't create controller");
396+
return -1;
397+
}
398+
399+
resp_data_len = sizeof(buf) - sizeof(*resp);
400+
401+
rc = nvme_mi_admin_xfer(ctrl, &req, 0, resp, 0, &resp_data_len);
402+
if (rc) {
403+
warn("nvme_admin_xfer failed: %d", rc);
404+
return -1;
405+
}
406+
407+
printf("Admin response:\n");
408+
printf(" Status: 0x%02x\n", resp->status);
409+
printf(" cdw0: 0x%08x\n", le32_to_cpu(resp->cdw0));
410+
printf(" cdw1: 0x%08x\n", le32_to_cpu(resp->cdw1));
411+
printf(" cdw3: 0x%08x\n", le32_to_cpu(resp->cdw3));
412+
printf(" data [%zd bytes]\n", resp_data_len);
413+
414+
hexdump(buf + sizeof(*resp), resp_data_len);
415+
return 0;
416+
}
417+
326418
enum action {
327419
ACTION_INFO,
328420
ACTION_CONTROLLERS,
329421
ACTION_IDENTIFY,
330422
ACTION_GET_LOG_PAGE,
423+
ACTION_ADMIN_RAW,
331424
};
332425

333426
int main(int argc, char **argv)
@@ -347,6 +440,7 @@ int main(int argc, char **argv)
347440
" controllers\n"
348441
" identify <controller-id> [--partial]\n"
349442
" get-log-page <controller-id> [<log-id>]\n"
443+
" admin <controller-id> <opcode> [<cdw10>, <cdw11>, ...]\n"
350444
);
351445
return EXIT_FAILURE;
352446
}
@@ -371,6 +465,8 @@ int main(int argc, char **argv)
371465
action = ACTION_IDENTIFY;
372466
} else if (!strcmp(action_str, "get-log-page")) {
373467
action = ACTION_GET_LOG_PAGE;
468+
} else if (!strcmp(action_str, "admin")) {
469+
action = ACTION_ADMIN_RAW;
374470
} else {
375471
fprintf(stderr, "invalid action '%s'\n", action_str);
376472
return EXIT_FAILURE;
@@ -398,6 +494,9 @@ int main(int argc, char **argv)
398494
case ACTION_GET_LOG_PAGE:
399495
rc = do_get_log_page(ep, argc, argv);
400496
break;
497+
case ACTION_ADMIN_RAW:
498+
rc = do_admin_raw(ep, argc, argv);
499+
break;
401500
}
402501

403502
nvme_mi_close(ep);

src/libnvme-mi.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ LIBNVME_MI_1_1 {
1212
nvme_mi_mi_subsystem_health_status_poll;
1313
nvme_mi_admin_identify_partial;
1414
nvme_mi_admin_get_log_page;
15+
nvme_mi_admin_xfer;
1516
nvme_mi_open_mctp;
1617
local:
1718
*;

src/nvme/mi.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,53 @@ static void nvme_mi_admin_init_resp(struct nvme_mi_resp *resp,
146146
resp->hdr_len = sizeof(*hdr);
147147
}
148148

149+
int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
150+
struct nvme_mi_admin_req_hdr *admin_req,
151+
size_t req_data_size,
152+
struct nvme_mi_admin_resp_hdr *admin_resp,
153+
off_t resp_data_offset,
154+
size_t *resp_data_size)
155+
{
156+
struct nvme_mi_resp resp;
157+
struct nvme_mi_req req;
158+
int rc;
159+
160+
if (*resp_data_size > 0xffffffff)
161+
return -EINVAL;
162+
if (resp_data_offset > 0xffffffff)
163+
return -EINVAL;
164+
165+
admin_req->hdr.type = NVME_MI_MSGTYPE_NVME;
166+
admin_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) |
167+
(NVME_MI_MT_ADMIN << 3);
168+
memset(&req, 0, sizeof(req));
169+
req.hdr = &admin_req->hdr;
170+
req.hdr_len = sizeof(*admin_req);
171+
req.data = admin_req + 1;
172+
req.data_len = req_data_size;
173+
174+
nvme_mi_calc_req_mic(&req);
175+
176+
memset(&resp, 0, sizeof(resp));
177+
resp.hdr = &admin_resp->hdr;
178+
resp.hdr_len = sizeof(*admin_resp);
179+
resp.data = admin_resp + 1;
180+
resp.data_len = *resp_data_size;
181+
182+
/* limit the response size, specify offset */
183+
admin_req->flags = 0x3;
184+
admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff);
185+
admin_req->doff = cpu_to_le32(resp_data_offset & 0xffffffff);
186+
187+
rc = nvme_mi_submit(ctrl->ep, &req, &resp);
188+
if (rc)
189+
return rc;
190+
191+
*resp_data_size = resp.data_len;
192+
193+
return 0;
194+
}
195+
149196
int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
150197
struct nvme_identify_args *args,
151198
off_t offset, size_t size)

src/nvme/mi.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,18 @@ int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
124124
struct nvme_mi_nvm_ss_health_status *nshds);
125125

126126
/* Admin channel functions */
127+
128+
/* "raw" admin transfer. req_data_size and resp_data_size are the sizes of
129+
* the data portion of the payload, so do not include the length of
130+
* the header, and start at 0 for no payload.
131+
*/
132+
int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
133+
struct nvme_mi_admin_req_hdr *admin_req,
134+
size_t req_data_size,
135+
struct nvme_mi_admin_resp_hdr *admin_resp,
136+
off_t resp_data_offset,
137+
size_t *resp_data_size);
138+
127139
int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
128140
struct nvme_identify_args *args,
129141
off_t offset, size_t size);

0 commit comments

Comments
 (0)