Skip to content

Commit 6a08780

Browse files
committed
mi-mctp: Add timeout support to MCTP transport
Now that we have timeout values in the endpoint, implement a per-command timeout using poll(), and clamp the maximum MPR time. Add tests to suit. Signed-off-by: Jeremy Kerr <[email protected]>
1 parent 2ce9608 commit 6a08780

3 files changed

Lines changed: 286 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/private.h

Lines changed: 2 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"
@@ -216,6 +217,7 @@ struct __mi_mctp_socket_ops {
216217
int (*socket)(int, int, int);
217218
ssize_t (*sendmsg)(int, const struct msghdr *, int);
218219
ssize_t (*recvmsg)(int, struct msghdr *, int);
220+
int (*poll)(struct pollfd *, nfds_t, int);
219221
int (*ioctl_tag)(int, unsigned long, struct mctp_ioc_tag_ctl *);
220222
};
221223
void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops);

0 commit comments

Comments
 (0)