From c3ded48453712e92a85fae8b0cca3a1ce413c2a3 Mon Sep 17 00:00:00 2001 From: Jinliang Wang Date: Fri, 28 Oct 2022 08:42:10 -0700 Subject: [PATCH] mi: add nvme_mi_admin_admin_passthru Similar to nvme_admin_passthru, send a customized NVMe Admin command request message and get the corresponding response message. Currently, it only supports data xfer size <= 4096. Signed-off-by: Jinliang Wang [dwagner: refactored has_*_data checks] Signed-off-by: Daniel Wagner --- src/libnvme-mi.map | 1 + src/nvme/mi.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ src/nvme/mi.h | 40 +++++++++++++++++++++ 3 files changed, 128 insertions(+) diff --git a/src/libnvme-mi.map b/src/libnvme-mi.map index fad10c9c..739f135e 100644 --- a/src/libnvme-mi.map +++ b/src/libnvme-mi.map @@ -1,6 +1,7 @@ LIBNVME_MI_1_3 { global: nvme_mi_set_probe_enabled; + nvme_mi_admin_admin_passthru; }; LIBNVME_MI_1_2 { diff --git a/src/nvme/mi.c b/src/nvme/mi.c index 34bfa42c..20f5117c 100644 --- a/src/nvme/mi.c +++ b/src/nvme/mi.c @@ -624,6 +624,93 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, return 0; } +int nvme_mi_admin_admin_passthru(nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags, + __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, + __u32 cdw10, __u32 cdw11, __u32 cdw12, + __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u32 *result) +{ + /* Input parameters flags, rsvd, metadata, metadata_len are not used */ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + int direction = opcode & 0x3; + bool has_write_data = false; + bool has_read_data = false; + + if (direction == NVME_DATA_TFR_BIDIRECTIONAL) { + nvme_msg(ctrl->ep->root, LOG_ERR, + "nvme_mi_admin_admin_passthru doesn't support bidirectional commands\n"); + errno = EINVAL; + return -1; + } + + if (data_len > 4096) { + nvme_msg(ctrl->ep->root, LOG_ERR, + "nvme_mi_admin_admin_passthru doesn't support data_len over 4096 bytes.\n"); + errno = EINVAL; + return -1; + } + + if (data != NULL && data_len != 0) { + if (direction == NVME_DATA_TFR_HOST_TO_CTRL) + has_write_data = true; + if (direction == NVME_DATA_TFR_CTRL_TO_HOST) + has_read_data = true; + } + + if (timeout_ms > nvme_mi_ep_get_timeout(ctrl->ep)) { + /* Set timeout if user needs a bigger timeout */ + nvme_mi_ep_set_timeout(ctrl->ep, timeout_ms); + } + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, opcode); + req_hdr.cdw1 = cpu_to_le32(nsid); + req_hdr.cdw2 = cpu_to_le32(cdw2); + req_hdr.cdw3 = cpu_to_le32(cdw3); + req_hdr.cdw10 = cpu_to_le32(cdw10); + req_hdr.cdw11 = cpu_to_le32(cdw11); + req_hdr.cdw12 = cpu_to_le32(cdw12); + req_hdr.cdw13 = cpu_to_le32(cdw13); + req_hdr.cdw14 = cpu_to_le32(cdw14); + req_hdr.cdw15 = cpu_to_le32(cdw15); + req_hdr.doff = 0; + if (data_len != 0) { + req_hdr.dlen = cpu_to_le32(data_len); + /* Bit 0 set to 1 means DLEN contains a value */ + req_hdr.flags = 0x1; + } + + if (has_write_data) { + req.data = data; + req.data_len = data_len; + } + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + if (has_read_data) { + resp.data = data; + resp.data_len = data_len; + } + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + rc = nvme_mi_admin_parse_status(&resp, result); + if (has_read_data && (resp.data_len != data_len)) { + errno = EPROTO; + return -1; + } + + return 0; +} + int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, struct nvme_identify_args *args, off_t offset, size_t size) diff --git a/src/nvme/mi.h b/src/nvme/mi.h index be37d67f..56591595 100644 --- a/src/nvme/mi.h +++ b/src/nvme/mi.h @@ -966,6 +966,46 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, off_t resp_data_offset, size_t *resp_data_size); +/** + * nvme_mi_admin_admin_passthru() - Submit an nvme admin passthrough command + * @ctrl: Controller to send command to + * @opcode: The nvme admin command to send + * @flags: NVMe command flags (not used) + * @rsvd: Reserved for future use + * @nsid: Namespace identifier + * @cdw2: Command dword 2 + * @cdw3: Command dword 3 + * @cdw10: Command dword 10 + * @cdw11: Command dword 11 + * @cdw12: Command dword 12 + * @cdw13: Command dword 13 + * @cdw14: Command dword 14 + * @cdw15: Command dword 15 + * @data_len: Length of the data transferred in this command in bytes + * @data: Pointer to user address of the data buffer + * @metadata_len:Length of metadata transferred in this command(not used) + * @metadata: Pointer to user address of the metadata buffer(not used) + * @timeout_ms: How long to wait for the command to complete + * @result: Optional field to return the result from the CQE dword 0 + * + * Send a customized NVMe Admin command request message and get the corresponding + * response message. + * + * This interface supports no data, host to controller and controller to + * host but it doesn't support bidirectional data transfer. + * Also this interface only supports data transfer size range [0, 4096] (bytes) + * so the & data_len parameter must be less than 4097. + * + * Return: The nvme command status if a response was received (see + * &enum nvme_status_field) or -1 with errno set otherwise. + */ +int nvme_mi_admin_admin_passthru(nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags, + __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, + __u32 cdw10, __u32 cdw11, __u32 cdw12, + __u32 cdw13, __u32 cdw14, __u32 cdw15, + __u32 data_len, void *data, __u32 metadata_len, + void *metadata, __u32 timeout_ms, __u32 *result); + /** * nvme_mi_admin_identify_partial() - Perform an Admin identify command, * and retrieve partial response data. -- 2.50.1