From 33b8fe1a294d670d67a2e9802ca0c753826b1562 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Aug 2022 14:26:29 +0200 Subject: Merging upstream version 1.1. Signed-off-by: Daniel Baumann --- src/nvme/fabrics.c | 9 ++++--- src/nvme/ioctl.h | 2 +- src/nvme/json.c | 11 +++++---- src/nvme/mi-mctp.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++------- src/nvme/mi.c | 41 ++++++++++++++++++++++++++------ src/nvme/mi.h | 38 ++++++++++++++++++++++++++--- src/nvme/private.h | 5 ++++ src/nvme/tree.c | 1 + 8 files changed, 150 insertions(+), 27 deletions(-) (limited to 'src/nvme') 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 #include +#include #include #include #include @@ -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 +#include #include #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); -- cgit v1.2.3