Skip to content

Commit 3db872e

Browse files
committed
mi: Add MI configuration commands
This change adds an API to send MI Get / Set Configuration commands to an endpoint, allowing control of endpoint SMBus frequenct, MCTP MTU and clearing the subsystem health status bits. We do this with a new couple of functions: int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1, __u32 *nmresp) int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1) The dw0, dw1 and nmresp formats depend on the type of configuration accessed, which can be a little opaque. Se we add a bunch of helpers too, to get/set the three configuration params: nvme_mi_mi_config_get_smbus_freq(...); nvme_mi_mi_config_set_smbus_freq(...); nvme_mi_mi_config_get_mctp_mtu(...); nvme_mi_mi_config_set_mctp_mtu(...); nvme_mi_mi_config_set_health_status_change(...); [there's no getter for the latter, as this just clears polled bits] Signed-off-by: Jeremy Kerr <[email protected]>
1 parent 13b8bbc commit 3db872e

4 files changed

Lines changed: 349 additions & 0 deletions

File tree

src/libnvme-mi.map

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ LIBNVME_MI_1_1 {
55
nvme_mi_init_ctrl;
66
nvme_mi_close_ctrl;
77
nvme_mi_close;
8+
nvme_mi_mi_config_get;
9+
nvme_mi_mi_config_set;
810
nvme_mi_mi_read_mi_data_subsys;
911
nvme_mi_mi_read_mi_data_port;
1012
nvme_mi_mi_read_mi_data_ctrl_list;

src/nvme/mi.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,77 @@ int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
732732
return 0;
733733
}
734734

735+
int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1,
736+
__u32 *nmresp)
737+
{
738+
struct nvme_mi_mi_resp_hdr resp_hdr;
739+
struct nvme_mi_mi_req_hdr req_hdr;
740+
struct nvme_mi_resp resp;
741+
struct nvme_mi_req req;
742+
int rc;
743+
744+
memset(&req_hdr, 0, sizeof(req_hdr));
745+
req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;
746+
req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3);
747+
req_hdr.opcode = nvme_mi_mi_opcode_configuration_get;
748+
req_hdr.cdw0 = cpu_to_le32(dw0);
749+
req_hdr.cdw1 = cpu_to_le32(dw1);
750+
751+
memset(&req, 0, sizeof(req));
752+
req.hdr = &req_hdr.hdr;
753+
req.hdr_len = sizeof(req_hdr);
754+
755+
memset(&resp, 0, sizeof(resp));
756+
resp.hdr = &resp_hdr.hdr;
757+
resp.hdr_len = sizeof(resp_hdr);
758+
759+
rc = nvme_mi_submit(ep, &req, &resp);
760+
if (rc)
761+
return rc;
762+
763+
if (resp_hdr.status)
764+
return resp_hdr.status;
765+
766+
*nmresp = resp_hdr.nmresp[0] |
767+
resp_hdr.nmresp[1] << 8 |
768+
resp_hdr.nmresp[2] << 16;
769+
770+
return 0;
771+
}
772+
773+
int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1)
774+
{
775+
struct nvme_mi_mi_resp_hdr resp_hdr;
776+
struct nvme_mi_mi_req_hdr req_hdr;
777+
struct nvme_mi_resp resp;
778+
struct nvme_mi_req req;
779+
int rc;
780+
781+
memset(&req_hdr, 0, sizeof(req_hdr));
782+
req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;
783+
req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3);
784+
req_hdr.opcode = nvme_mi_mi_opcode_configuration_set;
785+
req_hdr.cdw0 = cpu_to_le32(dw0);
786+
req_hdr.cdw1 = cpu_to_le32(dw1);
787+
788+
memset(&req, 0, sizeof(req));
789+
req.hdr = &req_hdr.hdr;
790+
req.hdr_len = sizeof(req_hdr);
791+
792+
memset(&resp, 0, sizeof(resp));
793+
resp.hdr = &resp_hdr.hdr;
794+
resp.hdr_len = sizeof(resp_hdr);
795+
796+
rc = nvme_mi_submit(ep, &req, &resp);
797+
if (rc)
798+
return rc;
799+
800+
if (resp_hdr.status)
801+
return resp_hdr.status;
802+
803+
return 0;
804+
}
805+
735806
void nvme_mi_close(nvme_mi_ep_t ep)
736807
{
737808
struct nvme_mi_ctrl *ctrl, *tmp;

src/nvme/mi.h

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,14 @@ struct nvme_mi_msg_resp {
164164
* enum nvme_mi_mi_opcode - Operation code for supported NVMe-MI commands.
165165
* @nvme_mi_mi_opcode_mi_data_read: Read NVMe-MI Data Structure
166166
* @nvme_mi_mi_opcode_subsys_health_status_poll: Subsystem Health Status Poll
167+
* @nvme_mi_mi_opcode_configuration_set: MI Configuration Set
168+
* @nvme_mi_mi_opcode_configuration_get: MI Configuration Get
167169
*/
168170
enum nvme_mi_mi_opcode {
169171
nvme_mi_mi_opcode_mi_data_read = 0x00,
170172
nvme_mi_mi_opcode_subsys_health_status_poll = 0x01,
173+
nvme_mi_mi_opcode_configuration_set = 0x03,
174+
nvme_mi_mi_opcode_configuration_get = 0x04,
171175
};
172176

173177
/**
@@ -643,6 +647,171 @@ int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id,
643647
int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
644648
struct nvme_mi_nvm_ss_health_status *nshds);
645649

650+
/**
651+
* nvme_mi_mi_config_get - query a configuration parameter
652+
* @ep: endpoint for MI communication
653+
* @dw0: management doubleword 0, containing configuration identifier, plus
654+
* config-specific fields
655+
* @dw1: management doubleword 0, config-specific.
656+
* @nmresp: set to queried configuration data in NMRESP field of response.
657+
*
658+
* Performs a MI Configuration Get command, with the configuration identifier
659+
* as the LSB of @dw0. Other @dw0 and @dw1 data is configuration-identifier
660+
* specific.
661+
*
662+
* On a sucessful Configuration Get, the @nmresp pointer will be populated with
663+
* the bytes from the 3-byte NMRESP field, converted to native endian.
664+
*
665+
* See &enum nvme_mi_config_id for identifiers.
666+
*
667+
* Return: 0 on success, non-zero on failure.
668+
*/
669+
int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1,
670+
__u32 *nmresp);
671+
672+
/**
673+
* nvme_mi_mi_config_set - set a configuration parameter
674+
* @ep: endpoint for MI communication
675+
* @dw0: management doubleword 0, containing configuration identifier, plus
676+
* config-specific fields
677+
* @dw1: management doubleword 0, config-specific.
678+
*
679+
* Performs a MI Configuration Set command, with the command as the LSB of
680+
* @dw0. Other @dw0 and @dw1 data is configuration-identifier specific.
681+
*
682+
* See &enum nvme_mi_config_id for identifiers.
683+
*
684+
* Return: 0 on success, non-zero on failure.
685+
*/
686+
int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1);
687+
688+
/**
689+
* nvme_mi_mi_config_get_smbus_freq - get configuraton: SMBus port frequency
690+
* @ep: endpoint for MI communication
691+
* @port: port ID to query
692+
* @freq: output value for current frequency configuration
693+
*
694+
* Performs a MI Configuration Get, to query the current SMBus frequency of
695+
* the port specified in @port. On success, populates @freq with the port
696+
* frequency
697+
*
698+
* Return: 0 on success, non-zero on failure.
699+
*/
700+
static inline int nvme_mi_mi_config_get_smbus_freq(nvme_mi_ep_t ep, __u8 port,
701+
enum nvme_mi_config_smbus_freq *freq)
702+
{
703+
__u32 tmp, dw0;
704+
int rc;
705+
706+
dw0 = port << 24 | NVME_MI_CONFIG_SMBUS_FREQ;
707+
708+
rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp);
709+
if (!rc)
710+
*freq = tmp & 0x3;
711+
return rc;
712+
}
713+
714+
/**
715+
* nvme_mi_mi_config_set_smbus_freq - set configuraton: SMBus port frequency
716+
* @ep: endpoint for MI communication
717+
* @port: port ID to set
718+
* @freq: new frequency configuration
719+
*
720+
* Performs a MI Configuration Set, to update the current SMBus frequency of
721+
* the port specified in @port.
722+
*
723+
* See &struct nvme_mi_read_port_info for the maximum supported SMBus frequency
724+
* for the port.
725+
*
726+
* Return: 0 on success, non-zero on failure.
727+
*/
728+
static inline int nvme_mi_mi_config_set_smbus_freq(nvme_mi_ep_t ep, __u8 port,
729+
enum nvme_mi_config_smbus_freq freq)
730+
{
731+
__u32 dw0 = port << 24 |
732+
(freq & 0x3) << 8 |
733+
NVME_MI_CONFIG_SMBUS_FREQ;
734+
735+
return nvme_mi_mi_config_set(ep, dw0, 0);
736+
}
737+
738+
/**
739+
* nvme_mi_mi_config_set_health_status_change - clear CCS bits in health status
740+
* @ep: endpoint for MI communication
741+
* @mask: bitmask to clear
742+
*
743+
* Performs a MI Configuration Set, to update the current health status poll
744+
* values of the Composite Controller Status bits. Bits set in @mask will
745+
* be cleared from future health status poll data, and may be re-triggered by
746+
* a future health change event.
747+
*
748+
* See &nvme_mi_mi_subsystem_health_status_poll(), &enum nvme_mi_ccs for
749+
* values in @mask.
750+
*
751+
* Return: 0 on success, non-zero on failure.
752+
*/
753+
static inline int nvme_mi_mi_config_set_health_status_change(nvme_mi_ep_t ep,
754+
__u32 mask)
755+
{
756+
return nvme_mi_mi_config_set(ep, NVME_MI_CONFIG_HEALTH_STATUS_CHANGE,
757+
mask);
758+
}
759+
760+
/**
761+
* nvme_mi_mi_config_get_mctp_mtu - get configuraton: MCTP MTU
762+
* @ep: endpoint for MI communication
763+
* @port: port ID to query
764+
* @mtu: output value for current MCTP MTU configuration
765+
*
766+
* Performs a MI Configuration Get, to query the current MCTP Maximum
767+
* Transmission Unit size (MTU) of the port specified in @port. On success,
768+
* populates @mtu with the MTU.
769+
*
770+
* The default reset value is 64, corresponding to the MCTP baseline MTU.
771+
*
772+
* Some controllers may also use this as the maximum receive unit size, and
773+
* may not accept MCTP messages larger than the configured MTU.
774+
*
775+
* Return: 0 on success, non-zero on failure.
776+
*/
777+
static inline int nvme_mi_mi_config_get_mctp_mtu(nvme_mi_ep_t ep, __u8 port,
778+
__u16 *mtu)
779+
{
780+
__u32 tmp, dw0;
781+
int rc;
782+
783+
dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU;
784+
785+
rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp);
786+
if (!rc)
787+
*mtu = tmp & 0xffff;
788+
return rc;
789+
}
790+
791+
/**
792+
* nvme_mi_mi_config_set_mctp_mtu - set configuraton: MCTP MTU
793+
* @ep: endpoint for MI communication
794+
* @port: port ID to set
795+
* @mtu: new MTU configuration
796+
*
797+
* Performs a MI Configuration Set, to update the current MCTP MTU value for
798+
* the port specified in @port.
799+
*
800+
* Some controllers may also use this as the maximum receive unit size, and
801+
* may not accept MCTP messages larger than the configured MTU. When setting
802+
* this value, you will likely need to change the MTU of the local MCTP
803+
* interface(s) to match.
804+
*
805+
* Return: 0 on success, non-zero on failure.
806+
*/
807+
static inline int nvme_mi_mi_config_set_mctp_mtu(nvme_mi_ep_t ep, __u8 port,
808+
__u16 mtu)
809+
{
810+
__u32 dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU;
811+
812+
return nvme_mi_mi_config_set(ep, dw0, mtu);
813+
}
814+
646815
/* Admin channel functions */
647816

648817
/**

test/mi.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,110 @@ static void test_resp_csi(nvme_mi_ep_t ep)
643643
assert(rc != 0);
644644
}
645645

646+
/* test: config get MTU request & response layout, ensure we're handling
647+
* endianness in the 3-byte NMRESP field correctly */
648+
static int test_mi_config_get_mtu_cb(struct nvme_mi_ep *ep,
649+
struct nvme_mi_req *req,
650+
struct nvme_mi_resp *resp,
651+
void *data)
652+
{
653+
struct nvme_mi_mi_resp_hdr *mi_resp;
654+
uint8_t *buf;
655+
656+
assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
657+
assert(req->data_len == 0);
658+
659+
/* validate req as raw bytes */
660+
buf = (void *)req->hdr;
661+
assert(buf[4] == nvme_mi_mi_opcode_configuration_get);
662+
/* dword 0: port and config id */
663+
assert(buf[11] == 0x5);
664+
assert(buf[8] == NVME_MI_CONFIG_MCTP_MTU);
665+
666+
/* set MTU in response */
667+
mi_resp = (void *)resp->hdr;
668+
mi_resp->nmresp[1] = 0x12;
669+
mi_resp->nmresp[0] = 0x34;
670+
resp->hdr_len = sizeof(*mi_resp);
671+
resp->data_len = 0;
672+
673+
test_transport_resp_calc_mic(resp);
674+
return 0;
675+
}
676+
677+
static void test_mi_config_get_mtu(nvme_mi_ep_t ep)
678+
{
679+
uint16_t mtu;
680+
int rc;
681+
682+
test_set_transport_callback(ep, test_mi_config_get_mtu_cb, NULL);
683+
684+
rc = nvme_mi_mi_config_get_mctp_mtu(ep, 5, &mtu);
685+
assert(rc == 0);
686+
assert(mtu == 0x1234);
687+
}
688+
689+
/* test: config set SMBus freq, both valid and invalid */
690+
static int test_mi_config_set_freq_cb(struct nvme_mi_ep *ep,
691+
struct nvme_mi_req *req,
692+
struct nvme_mi_resp *resp,
693+
void *data)
694+
{
695+
struct nvme_mi_mi_resp_hdr *mi_resp;
696+
uint8_t *buf;
697+
698+
assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
699+
assert(req->data_len == 0);
700+
701+
/* validate req as raw bytes */
702+
buf = (void *)req->hdr;
703+
assert(buf[4] == nvme_mi_mi_opcode_configuration_set);
704+
/* dword 0: port and config id */
705+
assert(buf[11] == 0x5);
706+
assert(buf[8] == NVME_MI_CONFIG_SMBUS_FREQ);
707+
708+
mi_resp = (void *)resp->hdr;
709+
resp->hdr_len = sizeof(*mi_resp);
710+
resp->data_len = 0;
711+
712+
/* accept 100 & 400, reject others */
713+
switch (buf[9]) {
714+
case NVME_MI_CONFIG_SMBUS_FREQ_100kHz:
715+
case NVME_MI_CONFIG_SMBUS_FREQ_400kHz:
716+
mi_resp->status = 0;
717+
break;
718+
case NVME_MI_CONFIG_SMBUS_FREQ_1MHz:
719+
default:
720+
mi_resp->status = 0x4;
721+
break;
722+
}
723+
724+
test_transport_resp_calc_mic(resp);
725+
return 0;
726+
}
727+
728+
static void test_mi_config_set_freq(nvme_mi_ep_t ep)
729+
{
730+
int rc;
731+
732+
test_set_transport_callback(ep, test_mi_config_set_freq_cb, NULL);
733+
734+
rc = nvme_mi_mi_config_set_smbus_freq(ep, 5,
735+
NVME_MI_CONFIG_SMBUS_FREQ_100kHz);
736+
assert(rc == 0);
737+
}
738+
739+
static void test_mi_config_set_freq_invalid(nvme_mi_ep_t ep)
740+
{
741+
int rc;
742+
743+
test_set_transport_callback(ep, test_mi_config_set_freq_cb, NULL);
744+
745+
rc = nvme_mi_mi_config_set_smbus_freq(ep, 5,
746+
NVME_MI_CONFIG_SMBUS_FREQ_1MHz);
747+
assert(rc == 4);
748+
}
749+
646750
#define DEFINE_TEST(name) { #name, test_ ## name }
647751
struct test {
648752
const char *name;
@@ -662,6 +766,9 @@ struct test {
662766
DEFINE_TEST(resp_hdr_small),
663767
DEFINE_TEST(resp_invalid_type),
664768
DEFINE_TEST(resp_csi),
769+
DEFINE_TEST(mi_config_get_mtu),
770+
DEFINE_TEST(mi_config_set_freq),
771+
DEFINE_TEST(mi_config_set_freq_invalid),
665772
};
666773

667774
static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep)

0 commit comments

Comments
 (0)