Skip to content

Commit 1133776

Browse files
authored
Merge pull request #432 from CodeConstruct/mi-timeout
MI: add command timeouts
2 parents 6c5aedd + 6a08780 commit 1133776

5 files changed

Lines changed: 350 additions & 8 deletions

File tree

src/nvme/mi-mctp.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <stdlib.h>
1313
#include <unistd.h>
1414

15+
#include <sys/poll.h>
1516
#include <sys/socket.h>
1617
#include <sys/types.h>
1718
#include <sys/uio.h>
@@ -94,6 +95,7 @@ static struct __mi_mctp_socket_ops ops = {
9495
socket,
9596
sendmsg,
9697
recvmsg,
98+
poll,
9799
ioctl_tag,
98100
};
99101

@@ -166,16 +168,27 @@ static void nvme_mi_mctp_tag_drop(struct nvme_mi_ep *ep, __u8 tag)
166168

167169
#endif /* !defined SIOMCTPTAGALLOC */
168170

169-
static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len)
171+
struct nvme_mi_msg_resp_mpr {
172+
struct nvme_mi_msg_hdr hdr;
173+
__u8 status;
174+
__u8 rsvd0;
175+
__u16 mprt;
176+
};
177+
178+
/* Check if this response was a More Processing Required response; if so,
179+
* populate the worst-case expected processing time, given in milliseconds.
180+
*/
181+
static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len,
182+
unsigned int *mpr_time)
170183
{
171-
struct nvme_mi_msg_resp *msg;
184+
struct nvme_mi_msg_resp_mpr *msg;
172185
__le32 mic;
173186
__u32 crc;
174187

175188
if (len != sizeof(*msg) + sizeof(mic))
176189
return false;
177190

178-
msg = (struct nvme_mi_msg_resp *)resp->hdr;
191+
msg = (struct nvme_mi_msg_resp_mpr *)resp->hdr;
179192

180193
if (msg->status != NVME_MI_RESP_MPR)
181194
return false;
@@ -199,6 +212,9 @@ static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len)
199212
if (le32_to_cpu(mic) != crc)
200213
return false;
201214

215+
if (mpr_time)
216+
*mpr_time = cpu_to_le16(msg->mprt) * 100;
217+
202218
return true;
203219
}
204220

@@ -209,8 +225,10 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
209225
struct nvme_mi_transport_mctp *mctp;
210226
struct iovec req_iov[3], resp_iov[3];
211227
struct msghdr req_msg, resp_msg;
228+
int i, rc, errno_save, timeout;
212229
struct sockaddr_mctp addr;
213-
int i, rc, errno_save;
230+
struct pollfd pollfds[1];
231+
unsigned int mpr_time;
214232
ssize_t len;
215233
__le32 mic;
216234
__u8 tag;
@@ -283,9 +301,29 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
283301
resp_msg.msg_iov = resp_iov;
284302
resp_msg.msg_iovlen = 3;
285303

304+
pollfds[0].fd = mctp->sd;
305+
pollfds[0].events = POLLIN;
306+
timeout = ep->timeout ?: -1;
286307
retry:
308+
rc = ops.poll(pollfds, 1, timeout);
309+
if (rc < 0) {
310+
if (errno == EINTR)
311+
goto retry;
312+
errno_save = errno;
313+
nvme_msg(ep->root, LOG_ERR,
314+
"Failed polling on MCTP socket: %m");
315+
errno = errno_save;
316+
return -1;
317+
}
318+
319+
if (rc == 0) {
320+
nvme_msg(ep->root, LOG_DEBUG, "Timeout on MCTP socket");
321+
errno = ETIMEDOUT;
322+
return -1;
323+
}
324+
287325
rc = -1;
288-
len = ops.recvmsg(mctp->sd, &resp_msg, 0);
326+
len = ops.recvmsg(mctp->sd, &resp_msg, MSG_DONTWAIT);
289327

290328
if (len < 0) {
291329
errno_save = errno;
@@ -331,11 +369,20 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
331369
* header fields. However, we need to do this in the transport in order
332370
* to keep the tag allocated and retry the recvmsg
333371
*/
334-
if (nvme_mi_mctp_resp_is_mpr(resp, len)) {
372+
if (nvme_mi_mctp_resp_is_mpr(resp, len, &mpr_time)) {
335373
nvme_msg(ep->root, LOG_DEBUG,
336374
"Received More Processing Required, waiting for response\n");
337-
/* TODO: when we implement timeouts, inspect the MPR response
338-
* for the estimated completion time. */
375+
376+
/* if the controller hasn't set MPRT, fall back to our command/
377+
* response timeout, or the largest possible MPRT if none set */
378+
if (!mpr_time)
379+
mpr_time = ep->timeout ?: 0xffff;
380+
381+
/* clamp to the endpoint max */
382+
if (ep->mprt_max && mpr_time > ep->mprt_max)
383+
mpr_time = ep->mprt_max;
384+
385+
timeout = mpr_time;
339386
goto retry;
340387
}
341388

@@ -434,6 +481,13 @@ nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, __u8 eid)
434481
ep->transport = &nvme_mi_transport_mctp;
435482
ep->transport_data = mctp;
436483

484+
/* Assuming an i2c transport at 100kHz, smallest MTU (64+4). Given
485+
* a worst-case clock stretch, and largest-sized packets, we can
486+
* expect up to 1.6s per command/response pair. Allowing for a
487+
* retry or two (handled by lower layers), 5s is a reasonable timeout.
488+
*/
489+
ep->timeout = 5000;
490+
437491
return ep;
438492

439493
err_free_ep:

src/nvme/mi.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#include "mi.h"
1818
#include "private.h"
1919

20+
static const int default_timeout = 1000; /* milliseconds; endpoints may
21+
override */
22+
2023
/* MI-equivalent of nvme_create_root, but avoids clashing symbol names
2124
* when linking against both libnvme and libnvme-mi.
2225
*/
@@ -57,13 +60,38 @@ struct nvme_mi_ep *nvme_mi_init_ep(nvme_root_t root)
5760
list_node_init(&ep->root_entry);
5861
ep->root = root;
5962
ep->controllers_scanned = false;
63+
ep->timeout = default_timeout;
64+
ep->mprt_max = 0;
6065
list_head_init(&ep->controllers);
6166

6267
list_add(&root->endpoints, &ep->root_entry);
6368

6469
return ep;
6570
}
6671

72+
int nvme_mi_ep_set_timeout(nvme_mi_ep_t ep, unsigned int timeout_ms)
73+
{
74+
if (ep->transport->check_timeout) {
75+
int rc;
76+
rc = ep->transport->check_timeout(ep, timeout_ms);
77+
if (rc)
78+
return rc;
79+
}
80+
81+
ep->timeout = timeout_ms;
82+
return 0;
83+
}
84+
85+
void nvme_mi_ep_set_mprt_max(nvme_mi_ep_t ep, unsigned int mprt_max_ms)
86+
{
87+
ep->mprt_max = mprt_max_ms;
88+
}
89+
90+
unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep)
91+
{
92+
return ep->timeout;
93+
}
94+
6795
struct nvme_mi_ctrl *nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id)
6896
{
6997
struct nvme_mi_ctrl *ctrl;

src/nvme/mi.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,39 @@ nvme_mi_ep_t nvme_mi_next_endpoint(nvme_root_t m, nvme_mi_ep_t e);
451451
e != NULL; \
452452
e = _e, _e = nvme_mi_next_endpoint(m, e))
453453

454+
/**
455+
* nvme_mi_ep_set_timeout - set a timeout for NVMe-MI responses
456+
* @ep: MI endpoint object
457+
* @timeout_ms: Timeout for MI responses, given in milliseconds
458+
*/
459+
int nvme_mi_ep_set_timeout(nvme_mi_ep_t ep, unsigned int timeout_ms);
460+
461+
/**
462+
* nvme_mi_ep_set_mprt_max - set the maximum wait time for a More Processing
463+
* Required response
464+
* @ep: MI endpoint object
465+
* @mprt_max_ms: Maximum more processing required wait time
466+
*
467+
* NVMe-MI endpoints may respond to a request with a "More Processing Required"
468+
* response; this also includes a hint on the worst-case processing time for
469+
* the eventual response data, with a specification-defined maximum of 65.535
470+
* seconds.
471+
*
472+
* This function provides a way to limit the maximum time we're prepared to
473+
* wait for the final response. Specify zero in @mprt_max_ms for no limit.
474+
* This should be larger than the command/response timeout set in
475+
* &nvme_mi_ep_set_timeout().
476+
*/
477+
void nvme_mi_ep_set_mprt_max(nvme_mi_ep_t ep, unsigned int mprt_max_ms);
478+
479+
/**
480+
* nvme_mi_ep_get_timeout - get the current timeout value for NVMe-MI responses
481+
* @ep: MI endpoint object
482+
*
483+
* Returns the current timeout value, in milliseconds, for this endpoint.
484+
*/
485+
unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep);
486+
454487
struct nvme_mi_ctrl;
455488

456489
/**

src/nvme/private.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define _LIBNVME_PRIVATE_H
1111

1212
#include <ccan/list/list.h>
13+
#include <sys/poll.h>
1314
#include <sys/socket.h>
1415

1516
#include "fabrics.h"
@@ -183,6 +184,7 @@ struct nvme_mi_transport {
183184
struct nvme_mi_resp *resp);
184185
void (*close)(struct nvme_mi_ep *ep);
185186
int (*desc_ep)(struct nvme_mi_ep *ep, char *buf, size_t len);
187+
int (*check_timeout)(struct nvme_mi_ep *ep, unsigned int timeout);
186188
};
187189

188190
struct nvme_mi_ep {
@@ -192,6 +194,8 @@ struct nvme_mi_ep {
192194
struct list_node root_entry;
193195
struct list_head controllers;
194196
bool controllers_scanned;
197+
unsigned int timeout;
198+
unsigned int mprt_max;
195199
};
196200

197201
struct nvme_mi_ctrl {
@@ -213,6 +217,7 @@ struct __mi_mctp_socket_ops {
213217
int (*socket)(int, int, int);
214218
ssize_t (*sendmsg)(int, const struct msghdr *, int);
215219
ssize_t (*recvmsg)(int, struct msghdr *, int);
220+
int (*poll)(struct pollfd *, nfds_t, int);
216221
int (*ioctl_tag)(int, unsigned long, struct mctp_ioc_tag_ctl *);
217222
};
218223
void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops);

0 commit comments

Comments
 (0)