]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
mi: Add MI configuration commands
authorJeremy Kerr <jk@codeconstruct.com.au>
Tue, 12 Oct 2021 03:13:58 +0000 (11:13 +0800)
committerJeremy Kerr <jk@codeconstruct.com.au>
Wed, 6 Jul 2022 05:37:18 +0000 (13:37 +0800)
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 <jk@codeconstruct.com.au>
src/libnvme-mi.map
src/nvme/mi.c
src/nvme/mi.h
test/mi.c

index 2ad3478ca9a15716409d5a6f889a2327f52e66e7..33447e903b7ae03dbeb9ed4aae7ce4ce194ee37f 100644 (file)
@@ -5,6 +5,8 @@ LIBNVME_MI_1_1 {
                nvme_mi_init_ctrl;
                nvme_mi_close_ctrl;
                nvme_mi_close;
+               nvme_mi_mi_config_get;
+               nvme_mi_mi_config_set;
                nvme_mi_mi_read_mi_data_subsys;
                nvme_mi_mi_read_mi_data_port;
                nvme_mi_mi_read_mi_data_ctrl_list;
index cbed80ba6a039848e7ab10c2f44f96dacaf14708..b804f22bc6b3a3374ff257a5f6819d154110071c 100644 (file)
@@ -732,6 +732,77 @@ int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
        return 0;
 }
 
+int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1,
+                         __u32 *nmresp)
+{
+       struct nvme_mi_mi_resp_hdr resp_hdr;
+       struct nvme_mi_mi_req_hdr req_hdr;
+       struct nvme_mi_resp resp;
+       struct nvme_mi_req req;
+       int rc;
+
+       memset(&req_hdr, 0, sizeof(req_hdr));
+       req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;
+       req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3);
+       req_hdr.opcode = nvme_mi_mi_opcode_configuration_get;
+       req_hdr.cdw0 = cpu_to_le32(dw0);
+       req_hdr.cdw1 = cpu_to_le32(dw1);
+
+       memset(&req, 0, sizeof(req));
+       req.hdr = &req_hdr.hdr;
+       req.hdr_len = sizeof(req_hdr);
+
+       memset(&resp, 0, sizeof(resp));
+       resp.hdr = &resp_hdr.hdr;
+       resp.hdr_len = sizeof(resp_hdr);
+
+       rc = nvme_mi_submit(ep, &req, &resp);
+       if (rc)
+               return rc;
+
+       if (resp_hdr.status)
+               return resp_hdr.status;
+
+       *nmresp = resp_hdr.nmresp[0] |
+                 resp_hdr.nmresp[1] << 8 |
+                 resp_hdr.nmresp[2] << 16;
+
+       return 0;
+}
+
+int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1)
+{
+       struct nvme_mi_mi_resp_hdr resp_hdr;
+       struct nvme_mi_mi_req_hdr req_hdr;
+       struct nvme_mi_resp resp;
+       struct nvme_mi_req req;
+       int rc;
+
+       memset(&req_hdr, 0, sizeof(req_hdr));
+       req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;
+       req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3);
+       req_hdr.opcode = nvme_mi_mi_opcode_configuration_set;
+       req_hdr.cdw0 = cpu_to_le32(dw0);
+       req_hdr.cdw1 = cpu_to_le32(dw1);
+
+       memset(&req, 0, sizeof(req));
+       req.hdr = &req_hdr.hdr;
+       req.hdr_len = sizeof(req_hdr);
+
+       memset(&resp, 0, sizeof(resp));
+       resp.hdr = &resp_hdr.hdr;
+       resp.hdr_len = sizeof(resp_hdr);
+
+       rc = nvme_mi_submit(ep, &req, &resp);
+       if (rc)
+               return rc;
+
+       if (resp_hdr.status)
+               return resp_hdr.status;
+
+       return 0;
+}
+
 void nvme_mi_close(nvme_mi_ep_t ep)
 {
        struct nvme_mi_ctrl *ctrl, *tmp;
index 9e71e863c589992574b3dc1116c04d842249c520..17444ddb94798e742cf3f37b4f81bd4d32c88bd2 100644 (file)
@@ -164,10 +164,14 @@ struct nvme_mi_msg_resp {
  * enum nvme_mi_mi_opcode - Operation code for supported NVMe-MI commands.
  * @nvme_mi_mi_opcode_mi_data_read: Read NVMe-MI Data Structure
  * @nvme_mi_mi_opcode_subsys_health_status_poll: Subsystem Health Status Poll
+ * @nvme_mi_mi_opcode_configuration_set: MI Configuration Set
+ * @nvme_mi_mi_opcode_configuration_get: MI Configuration Get
  */
 enum nvme_mi_mi_opcode {
        nvme_mi_mi_opcode_mi_data_read = 0x00,
        nvme_mi_mi_opcode_subsys_health_status_poll = 0x01,
+       nvme_mi_mi_opcode_configuration_set = 0x03,
+       nvme_mi_mi_opcode_configuration_get = 0x04,
 };
 
 /**
@@ -643,6 +647,171 @@ int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id,
 int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
                                            struct nvme_mi_nvm_ss_health_status *nshds);
 
+/**
+ * nvme_mi_mi_config_get - query a configuration parameter
+ * @ep: endpoint for MI communication
+ * @dw0: management doubleword 0, containing configuration identifier, plus
+ *       config-specific fields
+ * @dw1: management doubleword 0, config-specific.
+ * @nmresp: set to queried configuration data in NMRESP field of response.
+ *
+ * Performs a MI Configuration Get command, with the configuration identifier
+ * as the LSB of @dw0. Other @dw0 and @dw1 data is configuration-identifier
+ * specific.
+ *
+ * On a sucessful Configuration Get, the @nmresp pointer will be populated with
+ * the bytes from the 3-byte NMRESP field, converted to native endian.
+ *
+ * See &enum nvme_mi_config_id for identifiers.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1,
+                         __u32 *nmresp);
+
+/**
+ * nvme_mi_mi_config_set - set a configuration parameter
+ * @ep: endpoint for MI communication
+ * @dw0: management doubleword 0, containing configuration identifier, plus
+ *       config-specific fields
+ * @dw1: management doubleword 0, config-specific.
+ *
+ * Performs a MI Configuration Set command, with the command as the LSB of
+ * @dw0. Other @dw0 and @dw1 data is configuration-identifier specific.
+ *
+ * See &enum nvme_mi_config_id for identifiers.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1);
+
+/**
+ * nvme_mi_mi_config_get_smbus_freq - get configuraton: SMBus port frequency
+ * @ep: endpoint for MI communication
+ * @port: port ID to query
+ * @freq: output value for current frequency configuration
+ *
+ * Performs a MI Configuration Get, to query the current SMBus frequency of
+ * the port specified in @port. On success, populates @freq with the port
+ * frequency
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_get_smbus_freq(nvme_mi_ep_t ep, __u8 port,
+                                                  enum nvme_mi_config_smbus_freq *freq)
+{
+       __u32 tmp, dw0;
+       int rc;
+
+       dw0 = port << 24 | NVME_MI_CONFIG_SMBUS_FREQ;
+
+       rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp);
+       if (!rc)
+               *freq = tmp & 0x3;
+       return rc;
+}
+
+/**
+ * nvme_mi_mi_config_set_smbus_freq - set configuraton: SMBus port frequency
+ * @ep: endpoint for MI communication
+ * @port: port ID to set
+ * @freq: new frequency configuration
+ *
+ * Performs a MI Configuration Set, to update the current SMBus frequency of
+ * the port specified in @port.
+ *
+ * See &struct nvme_mi_read_port_info for the maximum supported SMBus frequency
+ * for the port.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_set_smbus_freq(nvme_mi_ep_t ep, __u8 port,
+                                                  enum nvme_mi_config_smbus_freq freq)
+{
+       __u32 dw0 = port << 24 |
+               (freq & 0x3) << 8 |
+               NVME_MI_CONFIG_SMBUS_FREQ;
+
+       return nvme_mi_mi_config_set(ep, dw0, 0);
+}
+
+/**
+ * nvme_mi_mi_config_set_health_status_change - clear CCS bits in health status
+ * @ep: endpoint for MI communication
+ * @mask: bitmask to clear
+ *
+ * Performs a MI Configuration Set, to update the current health status poll
+ * values of the Composite Controller Status bits. Bits set in @mask will
+ * be cleared from future health status poll data, and may be re-triggered by
+ * a future health change event.
+ *
+ * See &nvme_mi_mi_subsystem_health_status_poll(), &enum nvme_mi_ccs for
+ * values in @mask.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_set_health_status_change(nvme_mi_ep_t ep,
+                                                            __u32 mask)
+{
+       return nvme_mi_mi_config_set(ep, NVME_MI_CONFIG_HEALTH_STATUS_CHANGE,
+                                    mask);
+}
+
+/**
+ * nvme_mi_mi_config_get_mctp_mtu - get configuraton: MCTP MTU
+ * @ep: endpoint for MI communication
+ * @port: port ID to query
+ * @mtu: output value for current MCTP MTU configuration
+ *
+ * Performs a MI Configuration Get, to query the current MCTP Maximum
+ * Transmission Unit size (MTU) of the port specified in @port. On success,
+ * populates @mtu with the MTU.
+ *
+ * The default reset value is 64, corresponding to the MCTP baseline MTU.
+ *
+ * Some controllers may also use this as the maximum receive unit size, and
+ * may not accept MCTP messages larger than the configured MTU.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_get_mctp_mtu(nvme_mi_ep_t ep, __u8 port,
+                                                __u16 *mtu)
+{
+       __u32 tmp, dw0;
+       int rc;
+
+       dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU;
+
+       rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp);
+       if (!rc)
+               *mtu = tmp & 0xffff;
+       return rc;
+}
+
+/**
+ * nvme_mi_mi_config_set_mctp_mtu - set configuraton: MCTP MTU
+ * @ep: endpoint for MI communication
+ * @port: port ID to set
+ * @mtu: new MTU configuration
+ *
+ * Performs a MI Configuration Set, to update the current MCTP MTU value for
+ * the port specified in @port.
+ *
+ * Some controllers may also use this as the maximum receive unit size, and
+ * may not accept MCTP messages larger than the configured MTU. When setting
+ * this value, you will likely need to change the MTU of the local MCTP
+ * interface(s) to match.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_set_mctp_mtu(nvme_mi_ep_t ep, __u8 port,
+                                                __u16 mtu)
+{
+       __u32 dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU;
+
+       return nvme_mi_mi_config_set(ep, dw0, mtu);
+}
+
 /* Admin channel functions */
 
 /**
index 2cc7dde2a1904d09e58fc13547d73e0eb18fdd63..d269060ff256673e249d6e439b23f094d0f2ae19 100644 (file)
--- a/test/mi.c
+++ b/test/mi.c
@@ -643,6 +643,110 @@ static void test_resp_csi(nvme_mi_ep_t ep)
        assert(rc != 0);
 }
 
+/* test: config get MTU request & response layout, ensure we're handling
+ * endianness in the 3-byte NMRESP field correctly */
+static int test_mi_config_get_mtu_cb(struct nvme_mi_ep *ep,
+                           struct nvme_mi_req *req,
+                           struct nvme_mi_resp *resp,
+                           void *data)
+{
+       struct nvme_mi_mi_resp_hdr *mi_resp;
+       uint8_t *buf;
+
+       assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
+       assert(req->data_len == 0);
+
+       /* validate req as raw bytes */
+       buf = (void *)req->hdr;
+       assert(buf[4] == nvme_mi_mi_opcode_configuration_get);
+       /* dword 0: port and config id */
+       assert(buf[11] == 0x5);
+       assert(buf[8] == NVME_MI_CONFIG_MCTP_MTU);
+
+       /* set MTU in response */
+       mi_resp = (void *)resp->hdr;
+       mi_resp->nmresp[1] = 0x12;
+       mi_resp->nmresp[0] = 0x34;
+       resp->hdr_len = sizeof(*mi_resp);
+       resp->data_len = 0;
+
+       test_transport_resp_calc_mic(resp);
+       return 0;
+}
+
+static void test_mi_config_get_mtu(nvme_mi_ep_t ep)
+{
+       uint16_t mtu;
+       int rc;
+
+       test_set_transport_callback(ep, test_mi_config_get_mtu_cb, NULL);
+
+       rc = nvme_mi_mi_config_get_mctp_mtu(ep, 5, &mtu);
+       assert(rc == 0);
+       assert(mtu == 0x1234);
+}
+
+/* test: config set SMBus freq, both valid and invalid */
+static int test_mi_config_set_freq_cb(struct nvme_mi_ep *ep,
+                           struct nvme_mi_req *req,
+                           struct nvme_mi_resp *resp,
+                           void *data)
+{
+       struct nvme_mi_mi_resp_hdr *mi_resp;
+       uint8_t *buf;
+
+       assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
+       assert(req->data_len == 0);
+
+       /* validate req as raw bytes */
+       buf = (void *)req->hdr;
+       assert(buf[4] == nvme_mi_mi_opcode_configuration_set);
+       /* dword 0: port and config id */
+       assert(buf[11] == 0x5);
+       assert(buf[8] == NVME_MI_CONFIG_SMBUS_FREQ);
+
+       mi_resp = (void *)resp->hdr;
+       resp->hdr_len = sizeof(*mi_resp);
+       resp->data_len = 0;
+
+       /* accept 100 & 400, reject others */
+       switch (buf[9]) {
+       case NVME_MI_CONFIG_SMBUS_FREQ_100kHz:
+       case NVME_MI_CONFIG_SMBUS_FREQ_400kHz:
+               mi_resp->status = 0;
+               break;
+       case NVME_MI_CONFIG_SMBUS_FREQ_1MHz:
+       default:
+               mi_resp->status = 0x4;
+               break;
+       }
+
+       test_transport_resp_calc_mic(resp);
+       return 0;
+}
+
+static void test_mi_config_set_freq(nvme_mi_ep_t ep)
+{
+       int rc;
+
+       test_set_transport_callback(ep, test_mi_config_set_freq_cb, NULL);
+
+       rc = nvme_mi_mi_config_set_smbus_freq(ep, 5,
+                                             NVME_MI_CONFIG_SMBUS_FREQ_100kHz);
+       assert(rc == 0);
+}
+
+static void test_mi_config_set_freq_invalid(nvme_mi_ep_t ep)
+{
+       int rc;
+
+       test_set_transport_callback(ep, test_mi_config_set_freq_cb, NULL);
+
+       rc = nvme_mi_mi_config_set_smbus_freq(ep, 5,
+                                             NVME_MI_CONFIG_SMBUS_FREQ_1MHz);
+       assert(rc == 4);
+}
+
 #define DEFINE_TEST(name) { #name, test_ ## name }
 struct test {
        const char *name;
@@ -662,6 +766,9 @@ struct test {
        DEFINE_TEST(resp_hdr_small),
        DEFINE_TEST(resp_invalid_type),
        DEFINE_TEST(resp_csi),
+       DEFINE_TEST(mi_config_get_mtu),
+       DEFINE_TEST(mi_config_set_freq),
+       DEFINE_TEST(mi_config_set_freq_invalid),
 };
 
 static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep)