Skip to content

Commit 3868498

Browse files
committed
mi_xfer: Added nvme_mi_mi_xfer API
This is added to be analogous to nvme_mi_admin_xfer, providing a way for the application to implement vendor specific mi commands. Signed-off-by: Chuck Horkin <chorkin@microsoft.com>
1 parent 79ec876 commit 3868498

File tree

4 files changed

+143
-3
lines changed

4 files changed

+143
-3
lines changed

src/libnvme-mi.map

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
LIBNVME_MI_1_12 {
2+
global:
3+
nvme_mi_mi_xfer;
4+
};
5+
16
LIBNVME_MI_1_11 {
27
global:
38
nvme_mi_control;

src/nvme/mi.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,72 @@ static int nvme_mi_read_data(nvme_mi_ep_t ep, __u32 cdw0,
15991599
return 0;
16001600
}
16011601

1602+
int nvme_mi_mi_xfer(nvme_mi_ep_t ep,
1603+
struct nvme_mi_mi_req_hdr *mi_req,
1604+
size_t req_data_size,
1605+
struct nvme_mi_mi_resp_hdr *mi_resp,
1606+
size_t *resp_data_size)
1607+
{
1608+
int rc;
1609+
struct nvme_mi_req req;
1610+
struct nvme_mi_resp resp;
1611+
1612+
/* There is nothing in the spec to define this limit but going with the limits
1613+
* from the admin message types for DLEN seems like a reasonable starting point
1614+
* to check for coding errors
1615+
*/
1616+
const size_t mi_data_xfer_size_limit = 4096;
1617+
1618+
/* length/offset checks. The common _submit() API will do further
1619+
* checking on the message lengths too, so these are kept specific
1620+
* to the requirements of the particular command set
1621+
*/
1622+
1623+
if (*resp_data_size > mi_data_xfer_size_limit) {
1624+
errno = EINVAL;
1625+
return -1;
1626+
}
1627+
1628+
/* request and response lengths & offset must be aligned */
1629+
if ((req_data_size & 0x3) ||
1630+
(*resp_data_size & 0x3)) {
1631+
errno = EINVAL;
1632+
return -1;
1633+
}
1634+
1635+
/* bidirectional not permitted */
1636+
if (req_data_size && *resp_data_size) {
1637+
errno = EINVAL;
1638+
return -1;
1639+
}
1640+
1641+
mi_req->hdr.type = NVME_MI_MSGTYPE_NVME;
1642+
mi_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) |
1643+
(NVME_MI_MT_MI << 3);
1644+
1645+
memset(&req, 0, sizeof(req));
1646+
req.hdr = &mi_req->hdr;
1647+
req.hdr_len = sizeof(*mi_req);
1648+
req.data = mi_req + 1;
1649+
req.data_len = req_data_size;
1650+
1651+
nvme_mi_calc_req_mic(&req);
1652+
1653+
memset(&resp, 0, sizeof(resp));
1654+
resp.hdr = &mi_resp->hdr;
1655+
resp.hdr_len = sizeof(*mi_resp);
1656+
resp.data = mi_resp + 1;
1657+
resp.data_len = *resp_data_size;
1658+
1659+
rc = nvme_mi_submit(ep, &req, &resp);
1660+
if (rc)
1661+
return rc;
1662+
1663+
*resp_data_size = resp.data_len;
1664+
1665+
return 0;
1666+
}
1667+
16021668
int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep,
16031669
struct nvme_mi_read_nvm_ss_info *s)
16041670
{

src/nvme/mi.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,36 @@ char *nvme_mi_endpoint_desc(nvme_mi_ep_t ep);
729729

730730
/* MI Command API: nvme_mi_mi_ prefix */
731731

732+
/**
733+
* nvme_mi_mi_xfer() - Raw mi transfer interface.
734+
* @ep: endpoint to send the MI command to
735+
* @mi_req: request data
736+
* @req_data_size: size of request data payload
737+
* @mi_resp: buffer for response data
738+
* @resp_data_size: size of response data buffer, updated to received size
739+
*
740+
* Performs an arbitrary NVMe MI command, using the provided request data,
741+
* in @mi_req. The size of the request data *payload* is specified in
742+
* @req_data_size - this does not include the standard header length (so a
743+
* header-only request would have a size of 0). Note that the Management
744+
* Request Doublewords are considered part of the header data.
745+
*
746+
* On success, response data is stored in @mi_resp, which has an optional
747+
* appended payload buffer of @resp_data_size bytes. The actual payload
748+
* size transferred will be stored in @resp_data_size. This size does not
749+
* include the MI response header, so 0 represents no payload.
750+
*
751+
* See: &struct nvme_mi_mi_req_hdr and &struct nvme_mi_mi_resp_hdr.
752+
*
753+
* Return: The nvme command status if a response was received (see
754+
* &enum nvme_status_field) or -1 with errno set otherwise..
755+
*/
756+
int nvme_mi_mi_xfer(nvme_mi_ep_t ep,
757+
struct nvme_mi_mi_req_hdr *mi_req,
758+
size_t req_data_size,
759+
struct nvme_mi_mi_resp_hdr *mi_resp,
760+
size_t *resp_data_size);
761+
732762
/**
733763
* nvme_mi_mi_read_mi_data_subsys() - Perform a Read MI Data Structure command,
734764
* retrieving subsystem data.

test/mi.c

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -566,8 +566,8 @@ static void test_admin_err_nvme_resp(nvme_mi_ep_t ep)
566566
| NVME_SC_DNR));
567567
}
568568

569-
/* invalid Admin command transfers */
570-
static int test_admin_invalid_formats_cb(struct nvme_mi_ep *ep,
569+
/* invalid command transfers */
570+
static int test_rejected_command_cb(struct nvme_mi_ep *ep,
571571
struct nvme_mi_req *req,
572572
struct nvme_mi_resp *resp,
573573
void *data)
@@ -588,7 +588,7 @@ static void test_admin_invalid_formats(nvme_mi_ep_t ep)
588588
size_t len;
589589
int rc;
590590

591-
test_set_transport_callback(ep, test_admin_invalid_formats_cb, NULL);
591+
test_set_transport_callback(ep, test_rejected_command_cb, NULL);
592592

593593
ctrl = nvme_mi_init_ctrl(ep, 1);
594594
assert(ctrl);
@@ -629,6 +629,44 @@ static void test_admin_invalid_formats(nvme_mi_ep_t ep)
629629
assert(rc != 0);
630630
}
631631

632+
static void test_mi_invalid_formats(nvme_mi_ep_t ep)
633+
{
634+
struct {
635+
struct nvme_mi_mi_req_hdr hdr;
636+
uint8_t data[4];
637+
} req = { 0 };
638+
struct nvme_mi_mi_resp_hdr resp = { 0 };
639+
nvme_mi_ctrl_t ctrl;
640+
size_t len;
641+
int rc;
642+
643+
test_set_transport_callback(ep, test_rejected_command_cb, NULL);
644+
645+
ctrl = nvme_mi_init_ctrl(ep, 1);
646+
assert(ctrl);
647+
648+
/* unaligned req size */
649+
len = 0;
650+
651+
rc = nvme_mi_mi_xfer(ep, &req.hdr, 1, &resp, &len);
652+
assert(rc != 0);
653+
654+
/* unaligned resp size */
655+
len = 1;
656+
rc = nvme_mi_mi_xfer(ep, &req.hdr, 0, &resp, &len);
657+
assert(rc != 0);
658+
659+
/* resp too large */
660+
len = 4096 + 4;
661+
rc = nvme_mi_mi_xfer(ep, &req.hdr, 0, &resp, &len);
662+
assert(rc != 0);
663+
664+
/* req and resp payloads */
665+
len = 4;
666+
rc = nvme_mi_mi_xfer(ep, &req.hdr, 4, &resp, &len);
667+
assert(rc != 0);
668+
}
669+
632670
/* test: header length too small */
633671
static int test_resp_hdr_small_cb(struct nvme_mi_ep *ep,
634672
struct nvme_mi_req *req,
@@ -2049,6 +2087,7 @@ struct test {
20492087
DEFINE_TEST(endpoint_quirk_probe),
20502088
DEFINE_TEST(admin_dlen_doff_req),
20512089
DEFINE_TEST(admin_dlen_doff_resp),
2090+
DEFINE_TEST(mi_invalid_formats),
20522091
};
20532092

20542093
static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep)

0 commit comments

Comments
 (0)