Skip to content

Commit fec443b

Browse files
authored
Merge pull request #460 from CodeConstruct/mpr
mi: Allow Admin-message sized More Processing Required responses
2 parents 3d214c0 + c75a043 commit fec443b

2 files changed

Lines changed: 71 additions & 17 deletions

File tree

src/nvme/mi-mctp.c

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -179,36 +179,58 @@ struct nvme_mi_msg_resp_mpr {
179179
* populate the worst-case expected processing time, given in milliseconds.
180180
*/
181181
static bool nvme_mi_mctp_resp_is_mpr(struct nvme_mi_resp *resp, size_t len,
182-
unsigned int *mpr_time)
182+
__le32 mic, unsigned int *mpr_time)
183183
{
184+
struct nvme_mi_admin_resp_hdr *admin_msg;
184185
struct nvme_mi_msg_resp_mpr *msg;
185-
__le32 mic;
186+
size_t clen;
186187
__u32 crc;
187188

188-
if (len != sizeof(*msg) + sizeof(mic))
189+
/* We need at least the minimal header plus checksum */
190+
if (len < sizeof(*msg) + sizeof(mic))
189191
return false;
190192

191193
msg = (struct nvme_mi_msg_resp_mpr *)resp->hdr;
192194

193195
if (msg->status != NVME_MI_RESP_MPR)
194196
return false;
195197

196-
/* We can't use verify_resp_mic here, as the response structure has
197-
* not been laid-out properly in resp yet (this is deferred until
198-
* we have the actual response).
198+
/* Find and verify the MIC from the response, which may not be laid out
199+
* in resp as we expect. We have to preserve resp->hdr_len and
200+
* resp->data_len, as we will need them for the eventual reply message.
201+
* Because of that, we can't use verify_resp_mic here.
199202
*
200-
* We know the data is a fixed size, and linear in the hdr buf, so
201-
* calculation is fairly simple. We do need to find the MIC data
202-
* though, which could either be in the header buf (if the original
203-
* header was larger than the minimal header message), or the start of
204-
* the data buf (otherwise).
203+
* If the packet was at the expected response size, then mic will
204+
* be set already; if not, find it within the header/data buffers.
205+
*/
206+
207+
/* Devices may send a MPR response as a full-sized Admin response,
208+
* rather than the minimal MI-only header. Allow this, but only if the
209+
* type indicates admin, and the allocated response header is the
210+
* correct size for an Admin response.
211+
*/
212+
if (((msg->hdr.nmp >> 3) & 0xf) == NVME_MI_MT_ADMIN &&
213+
len == sizeof(*admin_msg) + sizeof(mic) &&
214+
resp->hdr_len == sizeof(*admin_msg)) {
215+
if (resp->data_len)
216+
mic = *(__le32 *)resp->data;
217+
} else if (len == sizeof(*msg) + sizeof(mic)) {
218+
if (resp->hdr_len > sizeof(*msg))
219+
mic = *(__le32 *)(msg + 1);
220+
else if (resp->data_len)
221+
mic = *(__le32 *)(resp->data);
222+
} else {
223+
return false;
224+
}
225+
226+
/* Since our response is just a header, we're guaranteed to have
227+
* all data in resp->hdr. The response may be shorter than the expected
228+
* header though, so clamp to len.
205229
*/
206-
if (resp->hdr_len > sizeof(*msg))
207-
mic = *(__le32 *)(msg + 1);
208-
else
209-
mic = *(__le32 *)(resp->data);
230+
len -= sizeof(mic);
231+
clen = len < resp->hdr_len ? len : resp->hdr_len;
210232

211-
crc = ~nvme_mi_crc32_update(0xffffffff, msg, sizeof(*msg));
233+
crc = ~nvme_mi_crc32_update(0xffffffff, resp->hdr, clen);
212234
if (le32_to_cpu(mic) != crc)
213235
return false;
214236

@@ -369,7 +391,7 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
369391
* header fields. However, we need to do this in the transport in order
370392
* to keep the tag allocated and retry the recvmsg
371393
*/
372-
if (nvme_mi_mctp_resp_is_mpr(resp, len, &mpr_time)) {
394+
if (nvme_mi_mctp_resp_is_mpr(resp, len, mic, &mpr_time)) {
373395
nvme_msg(ep->root, LOG_DEBUG,
374396
"Received More Processing Required, waiting for response\n");
375397

test/mi-mctp.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ static void test_poll_timeout(nvme_mi_ep_t ep, struct test_peer *peer)
407407
/* test: send a More Processing Required response, then the actual response */
408408
struct mpr_tx_info {
409409
int msg_no;
410+
bool admin_quirk;
410411
size_t final_len;
411412
};
412413

@@ -422,6 +423,9 @@ static int tx_mpr(struct test_peer *peer, void *buf, size_t len)
422423
case 1:
423424
peer->tx_buf[4] = NVME_MI_RESP_MPR;
424425
peer->tx_buf_len = 8;
426+
if (tx_info->admin_quirk) {
427+
peer->tx_buf_len = 20;
428+
}
425429
break;
426430
case 2:
427431
peer->tx_buf[4] = NVME_MI_RESP_SUCCESS;
@@ -446,6 +450,7 @@ static void test_mpr_mi(nvme_mi_ep_t ep, struct test_peer *peer)
446450

447451
tx_info.msg_no = 1;
448452
tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
453+
tx_info.admin_quirk = false;
449454

450455
peer->tx_fn = tx_mpr;
451456
peer->tx_data = &tx_info;
@@ -463,6 +468,32 @@ static void test_mpr_admin(nvme_mi_ep_t ep, struct test_peer *peer)
463468

464469
tx_info.msg_no = 1;
465470
tx_info.final_len = sizeof(struct nvme_mi_admin_resp_hdr) + sizeof(id);
471+
tx_info.admin_quirk = false;
472+
473+
peer->tx_fn = tx_mpr;
474+
peer->tx_data = &tx_info;
475+
476+
ctrl = nvme_mi_init_ctrl(ep, 1);
477+
478+
rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
479+
assert(rc == 0);
480+
481+
nvme_mi_close_ctrl(ctrl);
482+
}
483+
484+
/* We have seen drives that send a MPR response as a full Admin message,
485+
* rather than a MI message; these have a larger message body
486+
*/
487+
static void test_mpr_admin_quirked(nvme_mi_ep_t ep, struct test_peer *peer)
488+
{
489+
struct mpr_tx_info tx_info;
490+
struct nvme_id_ctrl id;
491+
nvme_mi_ctrl_t ctrl;
492+
int rc;
493+
494+
tx_info.msg_no = 1;
495+
tx_info.final_len = sizeof(struct nvme_mi_admin_resp_hdr) + sizeof(id);
496+
tx_info.admin_quirk = true;
466497

467498
peer->tx_fn = tx_mpr;
468499
peer->tx_data = &tx_info;
@@ -638,6 +669,7 @@ struct test {
638669
DEFINE_TEST(poll_timeout),
639670
DEFINE_TEST(mpr_mi),
640671
DEFINE_TEST(mpr_admin),
672+
DEFINE_TEST(mpr_admin_quirked),
641673
DEFINE_TEST(mpr_timeouts),
642674
DEFINE_TEST(mpr_timeout_clamp),
643675
DEFINE_TEST(mpr_mprt_zero),

0 commit comments

Comments
 (0)