Skip to content

Commit

Permalink
mi_xfer: Added nvme_mi_mi_xfer API
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
chorkin committed Jan 8, 2025
1 parent 79ec876 commit d7dd341
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/libnvme-mi.map
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
LIBNVME_MI_1_11 {
global:
nvme_mi_control;
nvme_mi_mi_xfer;
};

LIBNVME_MI_1_10 {
Expand Down
66 changes: 66 additions & 0 deletions src/nvme/mi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1599,6 +1599,72 @@ static int nvme_mi_read_data(nvme_mi_ep_t ep, __u32 cdw0,
return 0;
}

int nvme_mi_mi_xfer(nvme_mi_ep_t ep,
struct nvme_mi_mi_req_hdr *mi_req,
size_t req_data_size,
struct nvme_mi_mi_resp_hdr *mi_resp,
size_t *resp_data_size)
{
int rc;
struct nvme_mi_req req;
struct nvme_mi_resp resp;

/* There is nothing in the spec to define this limit but going with the limits
* from the admin message types for DLEN seems like a reasonable starting point
* to check for erronious coding errors
*/
const size_t mi_data_xfer_size_limit = 4096;

/* length/offset checks. The common _submit() API will do further
* checking on the message lengths too, so these are kept specific
* to the requirements of the particular command set
*/

if (*resp_data_size > mi_data_xfer_size_limit) {
errno = EINVAL;
return -1;
}

/* request and response lengths & offset must be aligned */
if ((req_data_size & 0x3) ||
(*resp_data_size & 0x3)) {
errno = EINVAL;
return -1;
}

/* bidirectional not permitted */
if (req_data_size && *resp_data_size) {
errno = EINVAL;
return -1;
}

mi_req->hdr.type = NVME_MI_MSGTYPE_NVME;
mi_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) |
(NVME_MI_MT_MI << 3);

memset(&req, 0, sizeof(req));
req.hdr = &mi_req->hdr;
req.hdr_len = sizeof(*mi_req);
req.data = mi_req + 1;
req.data_len = req_data_size;

nvme_mi_calc_req_mic(&req);

memset(&resp, 0, sizeof(resp));
resp.hdr = &mi_resp->hdr;
resp.hdr_len = sizeof(*mi_resp);
resp.data = mi_resp + 1;
resp.data_len = *resp_data_size;

rc = nvme_mi_submit(ep, &req, &resp);
if (rc)
return rc;

*resp_data_size = resp.data_len;

return 0;
}

int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep,
struct nvme_mi_read_nvm_ss_info *s)
{
Expand Down
30 changes: 30 additions & 0 deletions src/nvme/mi.h
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,36 @@ char *nvme_mi_endpoint_desc(nvme_mi_ep_t ep);

/* MI Command API: nvme_mi_mi_ prefix */

/**
* nvme_mi_mi_xfer() - Raw mi transfer interface.
* @ep: endpoint to send the MI command to
* @mi_req: request data
* @req_data_size: size of request data payload
* @mi_resp: buffer for response data
* @resp_data_size: size of response data buffer, updated to received size
*
* Performs an arbitrary NVMe MI command, using the provided request data,
* in @mi_req. The size of the request data *payload* is specified in
* @req_data_size - this does not include the standard header length (so a
* header-only request would have a size of 0).
*
* On success, response data is stored in @mi_resp, which has an optional
* appended payload buffer of @resp_data_size bytes. The actual payload
* size transferred will be stored in @resp_data_size. This size does not
* include the MI response header, so 0 represents no payload.
*
*
* See: &struct nvme_mi_mi_req_hdr and &struct nvme_mi_mi_resp_hdr.
*
* Return: The nvme command status if a response was received (see
* &enum nvme_status_field) or -1 with errno set otherwise..
*/
int nvme_mi_mi_xfer(nvme_mi_ep_t ep,
struct nvme_mi_mi_req_hdr *mi_req,
size_t req_data_size,
struct nvme_mi_mi_resp_hdr *mi_resp,
size_t *resp_data_size);

/**
* nvme_mi_mi_read_mi_data_subsys() - Perform a Read MI Data Structure command,
* retrieving subsystem data.
Expand Down
45 changes: 42 additions & 3 deletions test/mi.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,8 @@ static void test_admin_err_nvme_resp(nvme_mi_ep_t ep)
| NVME_SC_DNR));
}

/* invalid Admin command transfers */
static int test_admin_invalid_formats_cb(struct nvme_mi_ep *ep,
/* invalid command transfers */
static int test_rejected_command_cb(struct nvme_mi_ep *ep,
struct nvme_mi_req *req,
struct nvme_mi_resp *resp,
void *data)
Expand All @@ -588,7 +588,7 @@ static void test_admin_invalid_formats(nvme_mi_ep_t ep)
size_t len;
int rc;

test_set_transport_callback(ep, test_admin_invalid_formats_cb, NULL);
test_set_transport_callback(ep, test_rejected_command_cb, NULL);

ctrl = nvme_mi_init_ctrl(ep, 1);
assert(ctrl);
Expand Down Expand Up @@ -629,6 +629,44 @@ static void test_admin_invalid_formats(nvme_mi_ep_t ep)
assert(rc != 0);
}

static void test_mi_invalid_formats(nvme_mi_ep_t ep)
{
struct {
struct nvme_mi_mi_req_hdr hdr;
uint8_t data[4];
} req = { 0 };
struct nvme_mi_mi_resp_hdr resp = { 0 };
nvme_mi_ctrl_t ctrl;
size_t len;
int rc;

test_set_transport_callback(ep, test_rejected_command_cb, NULL);

ctrl = nvme_mi_init_ctrl(ep, 1);
assert(ctrl);

/* unaligned req size */
len = 0;

rc = nvme_mi_mi_xfer(ep, &req.hdr, 1, &resp, &len);
assert(rc != 0);

/* unaligned resp size */
len = 1;
rc = nvme_mi_mi_xfer(ep, &req.hdr, 0, &resp, &len);
assert(rc != 0);

/* resp too large */
len = 4096 + 4;
rc = nvme_mi_mi_xfer(ep, &req.hdr, 0, &resp, &len);
assert(rc != 0);

/* req and resp payloads */
len = 4;
rc = nvme_mi_mi_xfer(ep, &req.hdr, 4, &resp, &len);
assert(rc != 0);
}

/* test: header length too small */
static int test_resp_hdr_small_cb(struct nvme_mi_ep *ep,
struct nvme_mi_req *req,
Expand Down Expand Up @@ -2049,6 +2087,7 @@ struct test {
DEFINE_TEST(endpoint_quirk_probe),
DEFINE_TEST(admin_dlen_doff_req),
DEFINE_TEST(admin_dlen_doff_resp),
DEFINE_TEST(mi_invalid_formats),
};

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

0 comments on commit d7dd341

Please sign in to comment.