Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mi_xfer: Added nvme_mi_mi_xfer API #939

Merged
merged 1 commit into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/libnvme-mi.map
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
LIBNVME_MI_1_12 {
global:
nvme_mi_mi_xfer;
};

LIBNVME_MI_1_11 {
global:
nvme_mi_control;
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 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). Note that the Management
* Request Doublewords are considered part of the header data.
*
* 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
Loading