summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2022-08-15 12:26:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2022-08-15 12:26:29 +0000
commit33b8fe1a294d670d67a2e9802ca0c753826b1562 (patch)
treec7909b9865f0ee1b4f517d16283a0420ce08566b /src
parentReleasing debian version 1.1~rc0-1. (diff)
downloadlibnvme-33b8fe1a294d670d67a2e9802ca0c753826b1562.tar.xz
libnvme-33b8fe1a294d670d67a2e9802ca0c753826b1562.zip
Merging upstream version 1.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r--src/libnvme-mi.map2
-rw-r--r--src/nvme/fabrics.c9
-rw-r--r--src/nvme/ioctl.h2
-rw-r--r--src/nvme/json.c11
-rw-r--r--src/nvme/mi-mctp.c70
-rw-r--r--src/nvme/mi.c41
-rw-r--r--src/nvme/mi.h38
-rw-r--r--src/nvme/private.h5
-rw-r--r--src/nvme/tree.c1
9 files changed, 151 insertions, 28 deletions
diff --git a/src/libnvme-mi.map b/src/libnvme-mi.map
index 33447e9..e16b400 100644
--- a/src/libnvme-mi.map
+++ b/src/libnvme-mi.map
@@ -13,7 +13,7 @@ LIBNVME_MI_1_1 {
nvme_mi_mi_read_mi_data_ctrl;
nvme_mi_mi_subsystem_health_status_poll;
nvme_mi_admin_identify_partial;
- nvme_mi_admin_get_log_page;
+ nvme_mi_admin_get_log;
nvme_mi_admin_xfer;
nvme_mi_admin_security_send;
nvme_mi_admin_security_recv;
diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c
index ee20da2..b68b7b9 100644
--- a/src/nvme/fabrics.c
+++ b/src/nvme/fabrics.c
@@ -438,20 +438,23 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr)
if (!transport) {
nvme_msg(h->r, LOG_ERR, "need a transport (-t) argument\n");
- return -ENVME_CONNECT_TARG;
+ errno = ENVME_CONNECT_TARG;
+ return -1;
}
if (strncmp(transport, "loop", 4)) {
if (!nvme_ctrl_get_traddr(c)) {
nvme_msg(h->r, LOG_ERR, "need a address (-a) argument\n");
- return -ENVME_CONNECT_AARG;
+ errno = ENVME_CONNECT_AARG;
+ return -1;
}
}
/* always specify nqn as first arg - this will init the string */
if (asprintf(argstr, "nqn=%s",
nvme_ctrl_get_subsysnqn(c)) < 0) {
- return -ENOMEM;
+ errno = ENOMEM;
+ return -1;
}
if (!strcmp(nvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME)) {
nvme_ctrl_set_discovery_ctrl(c, true);
diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h
index 379c43a..d559b12 100644
--- a/src/nvme/ioctl.h
+++ b/src/nvme/ioctl.h
@@ -1244,7 +1244,7 @@ static inline int nvme_get_nsid_log(int fd, bool rae,
.lsi = NVME_LOG_LSI_NONE,
.lsp = NVME_LOG_LSP_NONE,
.uuidx = NVME_UUID_NONE,
- .rae = false,
+ .rae = rae,
.ot = false,
};
diff --git a/src/nvme/json.c b/src/nvme/json.c
index 6f0b81c..b42cd51 100644
--- a/src/nvme/json.c
+++ b/src/nvme/json.c
@@ -95,7 +95,7 @@ static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj)
if (!c)
return;
json_update_attributes(c, port_obj);
- attr_obj = json_object_object_get(port_obj, "dhchap_key");
+ attr_obj = json_object_object_get(port_obj, "dhchap_ctrl_key");
if (attr_obj)
nvme_ctrl_set_dhchap_key(c, json_object_get_string(attr_obj));
}
@@ -224,7 +224,7 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c)
json_object_new_string(value));
value = nvme_ctrl_get_dhchap_key(c);
if (value)
- json_object_object_add(port_obj, "dhchap_key",
+ json_object_object_add(port_obj, "dhchap_ctrl_key",
json_object_new_string(value));
JSON_INT_OPTION(cfg, port_obj, nr_io_queues, 0);
JSON_INT_OPTION(cfg, port_obj, nr_write_queues, 0);
@@ -318,9 +318,10 @@ int json_update_config(nvme_root_t r, const char *config_file)
json_object_put(subsys_array);
json_object_array_add(json_root, host_obj);
}
- if (!config_file)
+ if (!config_file) {
ret = json_object_to_fd(1, json_root, JSON_C_TO_STRING_PRETTY);
- else
+ printf("\n");
+ } else
ret = json_object_to_file_ext(config_file, json_root,
JSON_C_TO_STRING_PRETTY);
if (ret < 0) {
@@ -366,7 +367,7 @@ static void json_dump_ctrl(struct json_object *ctrl_array, nvme_ctrl_t c)
json_object_new_string(value));
value = nvme_ctrl_get_dhchap_key(c);
if (value)
- json_object_object_add(ctrl_obj, "dhchap_key",
+ json_object_object_add(ctrl_obj, "dhchap_ctrl_key",
json_object_new_string(value));
JSON_INT_OPTION(cfg, ctrl_obj, nr_io_queues, 0);
JSON_INT_OPTION(cfg, ctrl_obj, nr_write_queues, 0);
diff --git a/src/nvme/mi-mctp.c b/src/nvme/mi-mctp.c
index 81704aa..ae604f2 100644
--- a/src/nvme/mi-mctp.c
+++ b/src/nvme/mi-mctp.c
@@ -12,6 +12,7 @@
#include <stdlib.h>
#include <unistd.h>
+#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
@@ -94,6 +95,7 @@ static struct __mi_mctp_socket_ops ops = {
socket,
sendmsg,
recvmsg,
+ poll,
ioctl_tag,
};
@@ -166,16 +168,27 @@ static void nvme_mi_mctp_tag_drop(struct nvme_mi_ep *ep, __u8 tag)
#endif /* !defined SIOMCTPTAGALLOC */
-static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len)
+struct nvme_mi_msg_resp_mpr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 rsvd0;
+ __u16 mprt;
+};
+
+/* Check if this response was a More Processing Required response; if so,
+ * populate the worst-case expected processing time, given in milliseconds.
+ */
+static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len,
+ unsigned int *mpr_time)
{
- struct nvme_mi_msg_resp *msg;
+ struct nvme_mi_msg_resp_mpr *msg;
__le32 mic;
__u32 crc;
if (len != sizeof(*msg) + sizeof(mic))
return false;
- msg = (struct nvme_mi_msg_resp *)resp->hdr;
+ msg = (struct nvme_mi_msg_resp_mpr *)resp->hdr;
if (msg->status != NVME_MI_RESP_MPR)
return false;
@@ -199,6 +212,9 @@ static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len)
if (le32_to_cpu(mic) != crc)
return false;
+ if (mpr_time)
+ *mpr_time = cpu_to_le16(msg->mprt) * 100;
+
return true;
}
@@ -209,8 +225,10 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
struct nvme_mi_transport_mctp *mctp;
struct iovec req_iov[3], resp_iov[3];
struct msghdr req_msg, resp_msg;
+ int i, rc, errno_save, timeout;
struct sockaddr_mctp addr;
- int i, rc, errno_save;
+ struct pollfd pollfds[1];
+ unsigned int mpr_time;
ssize_t len;
__le32 mic;
__u8 tag;
@@ -283,9 +301,29 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
resp_msg.msg_iov = resp_iov;
resp_msg.msg_iovlen = 3;
+ pollfds[0].fd = mctp->sd;
+ pollfds[0].events = POLLIN;
+ timeout = ep->timeout ?: -1;
retry:
+ rc = ops.poll(pollfds, 1, timeout);
+ if (rc < 0) {
+ if (errno == EINTR)
+ goto retry;
+ errno_save = errno;
+ nvme_msg(ep->root, LOG_ERR,
+ "Failed polling on MCTP socket: %m");
+ errno = errno_save;
+ return -1;
+ }
+
+ if (rc == 0) {
+ nvme_msg(ep->root, LOG_DEBUG, "Timeout on MCTP socket");
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
rc = -1;
- len = ops.recvmsg(mctp->sd, &resp_msg, 0);
+ len = ops.recvmsg(mctp->sd, &resp_msg, MSG_DONTWAIT);
if (len < 0) {
errno_save = errno;
@@ -331,11 +369,20 @@ retry:
* header fields. However, we need to do this in the transport in order
* to keep the tag allocated and retry the recvmsg
*/
- if (nvme_mi_mctp_resp_is_mpr(resp, len)) {
+ if (nvme_mi_mctp_resp_is_mpr(resp, len, &mpr_time)) {
nvme_msg(ep->root, LOG_DEBUG,
"Received More Processing Required, waiting for response\n");
- /* TODO: when we implement timeouts, inspect the MPR response
- * for the estimated completion time. */
+
+ /* if the controller hasn't set MPRT, fall back to our command/
+ * response timeout, or the largest possible MPRT if none set */
+ if (!mpr_time)
+ mpr_time = ep->timeout ?: 0xffff;
+
+ /* clamp to the endpoint max */
+ if (ep->mprt_max && mpr_time > ep->mprt_max)
+ mpr_time = ep->mprt_max;
+
+ timeout = mpr_time;
goto retry;
}
@@ -434,6 +481,13 @@ nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, __u8 eid)
ep->transport = &nvme_mi_transport_mctp;
ep->transport_data = mctp;
+ /* Assuming an i2c transport at 100kHz, smallest MTU (64+4). Given
+ * a worst-case clock stretch, and largest-sized packets, we can
+ * expect up to 1.6s per command/response pair. Allowing for a
+ * retry or two (handled by lower layers), 5s is a reasonable timeout.
+ */
+ ep->timeout = 5000;
+
return ep;
err_free_ep:
diff --git a/src/nvme/mi.c b/src/nvme/mi.c
index 8e868d1..181a16c 100644
--- a/src/nvme/mi.c
+++ b/src/nvme/mi.c
@@ -17,6 +17,9 @@
#include "mi.h"
#include "private.h"
+static const int default_timeout = 1000; /* milliseconds; endpoints may
+ override */
+
/* MI-equivalent of nvme_create_root, but avoids clashing symbol names
* when linking against both libnvme and libnvme-mi.
*/
@@ -57,6 +60,8 @@ struct nvme_mi_ep *nvme_mi_init_ep(nvme_root_t root)
list_node_init(&ep->root_entry);
ep->root = root;
ep->controllers_scanned = false;
+ ep->timeout = default_timeout;
+ ep->mprt_max = 0;
list_head_init(&ep->controllers);
list_add(&root->endpoints, &ep->root_entry);
@@ -64,6 +69,29 @@ struct nvme_mi_ep *nvme_mi_init_ep(nvme_root_t root)
return ep;
}
+int nvme_mi_ep_set_timeout(nvme_mi_ep_t ep, unsigned int timeout_ms)
+{
+ if (ep->transport->check_timeout) {
+ int rc;
+ rc = ep->transport->check_timeout(ep, timeout_ms);
+ if (rc)
+ return rc;
+ }
+
+ ep->timeout = timeout_ms;
+ return 0;
+}
+
+void nvme_mi_ep_set_mprt_max(nvme_mi_ep_t ep, unsigned int mprt_max_ms)
+{
+ ep->mprt_max = mprt_max_ms;
+}
+
+unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep)
+{
+ return ep->timeout;
+}
+
struct nvme_mi_ctrl *nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id)
{
struct nvme_mi_ctrl *ctrl;
@@ -404,9 +432,9 @@ int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
/* retrieves a MCTP-messsage-sized chunk of log page data. offset and len are
* specified within the args->data area */
-static int __nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
- const struct nvme_get_log_args *args,
- off_t offset, size_t *lenp, bool final)
+static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl,
+ const struct nvme_get_log_args *args,
+ off_t offset, size_t *lenp, bool final)
{
struct nvme_mi_admin_resp_hdr resp_hdr;
struct nvme_mi_admin_req_hdr req_hdr;
@@ -469,8 +497,7 @@ static int __nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
return 0;
}
-int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
- struct nvme_get_log_args *args)
+int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args)
{
const size_t xfer_size = 4096;
off_t xfer_offset;
@@ -492,8 +519,8 @@ int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
final = xfer_offset + cur_xfer_size >= args->len;
- rc = __nvme_mi_admin_get_log_page(ctrl, args, xfer_offset,
- &tmp, final);
+ rc = __nvme_mi_admin_get_log(ctrl, args, xfer_offset,
+ &tmp, final);
if (rc)
break;
diff --git a/src/nvme/mi.h b/src/nvme/mi.h
index 81cb00f..a7ed240 100644
--- a/src/nvme/mi.h
+++ b/src/nvme/mi.h
@@ -451,6 +451,39 @@ nvme_mi_ep_t nvme_mi_next_endpoint(nvme_root_t m, nvme_mi_ep_t e);
e != NULL; \
e = _e, _e = nvme_mi_next_endpoint(m, e))
+/**
+ * nvme_mi_ep_set_timeout - set a timeout for NVMe-MI responses
+ * @ep: MI endpoint object
+ * @timeout_ms: Timeout for MI responses, given in milliseconds
+ */
+int nvme_mi_ep_set_timeout(nvme_mi_ep_t ep, unsigned int timeout_ms);
+
+/**
+ * nvme_mi_ep_set_mprt_max - set the maximum wait time for a More Processing
+ * Required response
+ * @ep: MI endpoint object
+ * @mprt_max_ms: Maximum more processing required wait time
+ *
+ * NVMe-MI endpoints may respond to a request with a "More Processing Required"
+ * response; this also includes a hint on the worst-case processing time for
+ * the eventual response data, with a specification-defined maximum of 65.535
+ * seconds.
+ *
+ * This function provides a way to limit the maximum time we're prepared to
+ * wait for the final response. Specify zero in @mprt_max_ms for no limit.
+ * This should be larger than the command/response timeout set in
+ * &nvme_mi_ep_set_timeout().
+ */
+void nvme_mi_ep_set_mprt_max(nvme_mi_ep_t ep, unsigned int mprt_max_ms);
+
+/**
+ * nvme_mi_ep_get_timeout - get the current timeout value for NVMe-MI responses
+ * @ep: MI endpoint object
+ *
+ * Returns the current timeout value, in milliseconds, for this endpoint.
+ */
+unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep);
+
struct nvme_mi_ctrl;
/**
@@ -1046,7 +1079,7 @@ static inline int nvme_mi_admin_identify_ctrl_list(nvme_mi_ctrl_t ctrl,
}
/**
- * nvme_mi_admin_get_log_page() - Retrieve log page data from controller
+ * nvme_mi_admin_get_log() - Retrieve log page data from controller
* @ctrl: Controller to query
* @args: Get Log Page command arguments
*
@@ -1062,8 +1095,7 @@ static inline int nvme_mi_admin_identify_ctrl_list(nvme_mi_ctrl_t ctrl,
*
* See: &struct nvme_get_log_args
*/
-int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
- struct nvme_get_log_args *args);
+int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args);
/**
* nvme_mi_admin_security_send() - Perform a Security Send command on a
diff --git a/src/nvme/private.h b/src/nvme/private.h
index da699ba..b5610f5 100644
--- a/src/nvme/private.h
+++ b/src/nvme/private.h
@@ -10,6 +10,7 @@
#define _LIBNVME_PRIVATE_H
#include <ccan/list/list.h>
+#include <poll.h>
#include <sys/socket.h>
#include "fabrics.h"
@@ -183,6 +184,7 @@ struct nvme_mi_transport {
struct nvme_mi_resp *resp);
void (*close)(struct nvme_mi_ep *ep);
int (*desc_ep)(struct nvme_mi_ep *ep, char *buf, size_t len);
+ int (*check_timeout)(struct nvme_mi_ep *ep, unsigned int timeout);
};
struct nvme_mi_ep {
@@ -192,6 +194,8 @@ struct nvme_mi_ep {
struct list_node root_entry;
struct list_head controllers;
bool controllers_scanned;
+ unsigned int timeout;
+ unsigned int mprt_max;
};
struct nvme_mi_ctrl {
@@ -213,6 +217,7 @@ struct __mi_mctp_socket_ops {
int (*socket)(int, int, int);
ssize_t (*sendmsg)(int, const struct msghdr *, int);
ssize_t (*recvmsg)(int, struct msghdr *, int);
+ int (*poll)(struct pollfd *, nfds_t, int);
int (*ioctl_tag)(int, unsigned long, struct mctp_ioc_tag_ctl *);
};
void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops);
diff --git a/src/nvme/tree.c b/src/nvme/tree.c
index 9161791..e3b41e7 100644
--- a/src/nvme/tree.c
+++ b/src/nvme/tree.c
@@ -897,6 +897,7 @@ void nvme_deconfigure_ctrl(nvme_ctrl_t c)
FREE_CTRL_ATTR(c->queue_count);
FREE_CTRL_ATTR(c->serial);
FREE_CTRL_ATTR(c->sqsize);
+ FREE_CTRL_ATTR(c->dhchap_key);
FREE_CTRL_ATTR(c->address);
FREE_CTRL_ATTR(c->dctype);
FREE_CTRL_ATTR(c->cntrltype);