Skip to content

Commit d8b5bb8

Browse files
committed
mi: Distinguish MI status from NVMe (CDW3) status
We curerently have some overloading in the status values returned from the nvme_* API, as the NVMe CDW3 values overlap with the recently-introduced NVMe-MI response header status. This change introduces a new encoding for the return values of MI functions, where we use a set of bits in the return value to encode whether the value is either a MI status value or a NVMe status value. We leave room for future expansion too, by defining three bits of possible type values. This has minimal change to the current API, as we're using 0 for the current NVMe status codes, so they are all unchanged. Since the MI values alised those, they will have high bits set now, but we couldn't previously distinguish them from the NVMe values anyway. Fixes: #456 Signed-off-by: Jeremy Kerr <[email protected]>
1 parent b483bbd commit d8b5bb8

3 files changed

Lines changed: 77 additions & 7 deletions

File tree

src/nvme/mi.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -315,13 +315,12 @@ static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result)
315315
resp_hdr = (struct nvme_mi_msg_resp *)resp->hdr;
316316

317317
/* If we have a MI error, we can't be sure there's an admin header
318-
* following; return just the MI status
319-
*
320-
* TODO: this may alias the cdw3 result values, see
321-
* https://github.com/linux-nvme/libnvme/issues/456
318+
* following; return just the MI status, with the status type
319+
* indicator of MI.
322320
*/
323321
if (resp_hdr->status)
324-
return resp_hdr->status;
322+
return resp_hdr->status |
323+
(NVME_STATUS_TYPE_MI << NVME_STATUS_TYPE_SHIFT);
325324

326325
/* We shouldn't hit this, as we'd have an error reported earlier.
327326
* However, for pointer safety, ensure we have a full admin header

src/nvme/types.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6141,6 +6141,75 @@ static inline __u16 nvme_status_code(__u16 status_field)
61416141
return NVME_GET(status_field, SC);
61426142
}
61436143

6144+
/**
6145+
* enum nvme_status_type - type encoding for NVMe return values, when
6146+
* represented as an int.
6147+
*
6148+
* The nvme_* api returns an int, with negative values indicating an internal
6149+
* or syscall error, zero signifying success, positive values representing
6150+
* the NVMe status.
6151+
*
6152+
* That latter case (the NVMe status) may represent status values from
6153+
* different parts of the transport/controller/etc, and are at most 16 bits of
6154+
* data. So, we use the most-significant 3 bits of the signed int to indicate
6155+
* which type of status this is.
6156+
*
6157+
* @NVME_STATUS_TYPE_SHIFT: shift value for status bits
6158+
* @NVME_STATUS_TYPE_MASK: mask value for status bits
6159+
*
6160+
* @NVME_STATUS_TYPE_NVME: NVMe command status value, typically from CDW3
6161+
* @NVME_STATUS_TYPE_MI: NVMe-MI header status
6162+
*/
6163+
enum nvme_status_type {
6164+
NVME_STATUS_TYPE_SHIFT = 27,
6165+
NVME_STATUS_TYPE_MASK = 0x7,
6166+
6167+
NVME_STATUS_TYPE_NVME = 0,
6168+
NVME_STATUS_TYPE_MI = 1,
6169+
};
6170+
6171+
/**
6172+
* nvme_status_get_type() - extract the type from a nvme_* return value
6173+
* @status: the (non-negative) return value from the NVMe API
6174+
*
6175+
* Returns: the type component of the status.
6176+
*/
6177+
static inline __u32 nvme_status_get_type(int status)
6178+
{
6179+
return NVME_GET(status, STATUS_TYPE);
6180+
}
6181+
6182+
/**
6183+
* nvme_status_get_value() - extract the status value from a nvme_* return
6184+
* value
6185+
* @status: the (non-negative) return value from the NVMe API
6186+
*
6187+
* Returns: the value component of the status; the set of values will depend
6188+
* on the status type.
6189+
*/
6190+
static inline __u32 nvme_status_get_value(int status)
6191+
{
6192+
return status & ~(NVME_STATUS_TYPE_MASK << NVME_STATUS_TYPE_SHIFT);
6193+
}
6194+
6195+
/**
6196+
* nvme_status_equals() - helper to check a status against a type and value
6197+
* @status: the (non-negative) return value from the NVMe API
6198+
* @type: the status type
6199+
* @value: the status value
6200+
*
6201+
* Returns: true if @status is of the specified type and value
6202+
*/
6203+
static inline __u32 nvme_status_equals(int status, enum nvme_status_type type,
6204+
unsigned int value)
6205+
{
6206+
if (status < 0)
6207+
return false;
6208+
6209+
return nvme_status_get_type(status) == type &&
6210+
nvme_status_get_value(status) == value;
6211+
}
6212+
61446213
/**
61456214
* enum nvme_admin_opcode - Known NVMe admin opcodes
61466215
* @nvme_admin_delete_sq: Delete I/O Submission Queue

test/mi-mctp.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ static void test_admin_resp_err(nvme_mi_ep_t ep, struct test_peer *peer)
308308
peer->tx_buf_len = 8;
309309

310310
rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
311-
assert(rc == 0x2);
311+
assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI);
312+
assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR);
312313
}
313314

314315
/* test: all 4-byte aligned response sizes - should be decoded into the
@@ -332,7 +333,8 @@ static void test_admin_resp_sizes(nvme_mi_ep_t ep, struct test_peer *peer)
332333
for (i = 8; i <= 4096 + 8; i+=4) {
333334
peer->tx_buf_len = i;
334335
rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
335-
assert(rc == 2);
336+
assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI);
337+
assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR);
336338
}
337339

338340
nvme_mi_close_ctrl(ctrl);

0 commit comments

Comments
 (0)