blob: 62358ee9b3ab2b998bc25f7acdc00f77208249ab [file] [log] [blame]
From 812761bcba5136f20a1e03f5f1c944cffa46da07 Mon Sep 17 00:00:00 2001
From: Hao Jiang <jianghao@google.com>
Date: Wed, 30 Nov 2022 18:00:18 -0800
Subject: [PATCH 1/4] mi_admin_xfer: set data fields based on opcode
The opcode indicated the data transfer direction. Previously, the xfer
function didn't check the opcode and force using flags = 03h for all
data transaction. It created issues on some NVMe controller
implementation who explicitly checks the flags and doff/dlen with the
opcode.
Adding the same condition check on host code (libnvme-mi) and setting
the corresponding fields as well.
Patch Tracking Bug: b/274912759
Upstream info / review: https://github.com/linux-nvme/libnvme/pull/549
Upstream-Status: Submitted
Justification: Some device are unable to handle the command request
without this patch.
---
src/nvme/mi.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 66 insertions(+), 6 deletions(-)
diff --git a/src/nvme/mi.c b/src/nvme/mi.c
index adf1753..693bbb4 100644
--- a/src/nvme/mi.c
+++ b/src/nvme/mi.c
@@ -582,12 +582,39 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
}
/* bidirectional not permitted (see DLEN definition) */
- if (req_data_size && *resp_data_size) {
+ int direction = admin_req->opcode & 0x3;
+ if (direction == NVME_DATA_TFR_BIDIRECTIONAL || (req_data_size && *resp_data_size))
+ {
+ nvme_msg(ctrl->ep->root, LOG_ERR,
+ "nvme_mi_admin_xfer doesn't support bidirectional commands\n");
errno = EINVAL;
return -1;
}
-
- if (!*resp_data_size && resp_data_offset) {
+ else if (direction == NVME_DATA_TFR_HOST_TO_CTRL && *resp_data_size)
+ {
+ nvme_msg(ctrl->ep->root, LOG_ERR,
+ "nvme_mi_admin_xfer doesn't support response data on WRITE commands\n");
+ errno = EINVAL;
+ return -1;
+ }
+ else if (direction == NVME_DATA_TFR_CTRL_TO_HOST && req_data_size)
+ {
+ nvme_msg(ctrl->ep->root, LOG_ERR,
+ "nvme_mi_admin_xfer doesn't support request data on READ commands\n");
+ errno = EINVAL;
+ return -1;
+ }
+ else if (resp_data_offset && (direction != NVME_DATA_TFR_CTRL_TO_HOST || !*resp_data_size))
+ {
+ nvme_msg(ctrl->ep->root, LOG_ERR,
+ "nvme_mi_admin_xfer invalid offset value\n");
+ errno = EINVAL;
+ return -1;
+ }
+ else if (direction == NVME_DATA_TFR_NO_DATA_TFR && req_data_size && *resp_data_size)
+ {
+ nvme_msg(ctrl->ep->root, LOG_ERR,
+ "nvme_mi_admin_xfer doesn't support data on NONE_DATA_TRANSFER commands\n");
errno = EINVAL;
return -1;
}
@@ -611,9 +638,42 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
resp.data_len = *resp_data_size;
/* limit the response size, specify offset */
- admin_req->flags = 0x3;
- admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff);
- admin_req->doff = cpu_to_le32(resp_data_offset & 0xffffffff);
+ if (direction == NVME_DATA_TFR_HOST_TO_CTRL)
+ {
+ if (req.data_len)
+ {
+ admin_req->flags = 0x1;
+ }
+ else
+ {
+ admin_req->flags = 0;
+ }
+ admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff);
+ admin_req->doff = 0;
+ }
+ else if (direction == NVME_DATA_TFR_CTRL_TO_HOST)
+ {
+ if (resp.data_len && resp_data_offset)
+ {
+ admin_req->flags = 0x3;
+ }
+ else if (resp.data_len)
+ {
+ admin_req->flags = 0x1;
+ }
+ else
+ {
+ admin_req->flags = 0x0;
+ }
+ admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff);
+ admin_req->doff = cpu_to_le32(resp_data_offset & 0xffffffff);
+ }
+ else
+ {
+ admin_req->flags = 0;
+ admin_req->dlen = 0;
+ admin_req->doff = 0;
+ }
rc = nvme_mi_submit(ctrl->ep, &req, &resp);
if (rc)
--
2.39.0.314.g84b9a713c41-goog