summaryrefslogtreecommitdiffstats
path: root/examples/mi-conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/mi-conf.c')
-rw-r--r--examples/mi-conf.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/examples/mi-conf.c b/examples/mi-conf.c
new file mode 100644
index 0000000..4fdd405
--- /dev/null
+++ b/examples/mi-conf.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2022 Code Construct Pty Ltd.
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+
+/**
+ * mi-conf: query a device for optimal MTU and set for both the local MCTP
+ * route (through dbus to mctpd) and the device itself (through NVMe-MI
+ * configuration commands)
+ */
+
+#include <err.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libnvme-mi.h>
+
+#include <ccan/array_size/array_size.h>
+#include <ccan/endian/endian.h>
+
+#include <dbus/dbus.h>
+
+#define MCTP_DBUS_NAME "xyz.openbmc_project.MCTP"
+#define MCTP_DBUS_PATH "/xyz/openbmc_project/mctp"
+#define MCTP_DBUS_EP_IFACE "au.com.CodeConstruct.MCTP.Endpoint"
+
+static int parse_mctp(const char *devstr, unsigned int *net, uint8_t *eid)
+{
+ int rc;
+
+ rc = sscanf(devstr, "mctp:%u,%hhu", net, eid);
+ if (rc != 2)
+ return -1;
+
+ return 0;
+}
+
+int find_port(nvme_mi_ep_t ep, uint8_t *portp, uint16_t *mtup)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ struct nvme_mi_read_port_info port_info;
+ uint8_t port;
+ bool found;
+ int rc;
+
+ /* query number of ports */
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ if (rc) {
+ warn("Failed reading subsystem info");
+ return -1;
+ }
+
+ found = false;
+ for (port = 0; port <= ss_info.nump; port++) {
+ rc = nvme_mi_mi_read_mi_data_port(ep, port, &port_info);
+ if (rc) {
+ warn("Failed reading port info for port %ud", port);
+ return -1;
+ }
+
+ /* skip non-SMBus ports */
+ if (port_info.portt != 0x2)
+ continue;
+
+ if (found) {
+ warn("Mutliple SMBus ports; skipping duplicate");
+ } else {
+ *portp = port;
+ *mtup = port_info.mmctptus;
+ found = true;
+ }
+ }
+
+ return found ? 0 : 1;
+}
+
+int set_local_mtu(DBusConnection *bus, unsigned int net, uint8_t eid,
+ uint32_t mtu)
+{
+ DBusMessage *msg, *resp;
+ DBusError berr;
+ char *ep_path;
+ int rc;
+
+ rc = asprintf(&ep_path, "%s/%u/%hhu", MCTP_DBUS_PATH, net, eid);
+ if (rc < 0) {
+ warn("Failed to create dbus path");
+ return -1;
+ }
+
+ /* The NVMe-MI interfaces refer to their MTU as *not* including the
+ * 4-byte MCTP header, whereas the MCTP specs *do* include it. When
+ * we're setting the route MTU, we're using to the MCTP-style MTU,
+ * which needs the extra four bytes included
+ */
+ mtu += 4;
+
+ rc = -1;
+ dbus_error_init(&berr);
+ msg = dbus_message_new_method_call(MCTP_DBUS_NAME, ep_path,
+ MCTP_DBUS_EP_IFACE, "SetMTU");
+ if (!msg) {
+ warnx("Can't create D-Bus message");
+ goto out;
+ }
+
+ rc = dbus_message_append_args(msg,
+ DBUS_TYPE_UINT32, &mtu,
+ DBUS_TYPE_INVALID);
+ if (!rc) {
+ warnx("Can't construct D-Bus message arguments");
+ goto out_free_msg;
+ }
+
+ resp = dbus_connection_send_with_reply_and_block(bus, msg,
+ 2000, &berr);
+ if (!resp) {
+ warnx("Failed to set local MTU: %s (%s)", berr.message,
+ berr.name);
+ } else {
+ dbus_message_unref(resp);
+ rc = 0;
+ }
+
+out_free_msg:
+ dbus_message_unref(msg);
+out:
+ dbus_error_free(&berr);
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ uint16_t cur_mtu, mtu;
+ DBusConnection *bus;
+ const char *devstr;
+ uint8_t eid, port;
+ nvme_root_t root;
+ unsigned int net;
+ nvme_mi_ep_t ep;
+ DBusError berr;
+ int rc;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s mctp:<net>,<eid>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ devstr = argv[1];
+ rc = parse_mctp(devstr, &net, &eid);
+ if (rc)
+ errx(EXIT_FAILURE, "can't parse MI device string '%s'", devstr);
+
+ root = nvme_mi_create_root(stderr, DEFAULT_LOGLEVEL);
+ if (!root)
+ err(EXIT_FAILURE, "can't create NVMe root");
+
+ ep = nvme_mi_open_mctp(root, net, eid);
+ if (!ep) {
+ warnx("can't open MCTP endpoint %d:%d", net, eid);
+ goto out_free_root;
+ }
+
+ dbus_error_init(&berr);
+ bus = dbus_bus_get(DBUS_BUS_SYSTEM, &berr);
+ if (!bus) {
+ warnx("Failed opening D-Bus: %s (%s)\n",
+ berr.message, berr.name);
+ rc = -1;
+ goto out_close_ep;
+ }
+
+ rc = find_port(ep, &port, &mtu);
+ if (rc) {
+ warnx("Can't find SMBus port information");
+ goto out_close_bus;
+ }
+
+ rc = nvme_mi_mi_config_get_mctp_mtu(ep, port, &cur_mtu);
+ if (rc) {
+ cur_mtu = 0;
+ warn("Can't query current MTU; no way to revert on failure");
+ }
+
+ if (mtu == cur_mtu) {
+ printf("Current MTU (%d) is already at max\n", cur_mtu);
+ goto out_close_bus;
+ }
+
+ rc = nvme_mi_mi_config_set_mctp_mtu(ep, port, mtu);
+ if (rc) {
+ warn("Can't set MCTP MTU");
+ goto out_close_bus;
+ }
+
+ rc = set_local_mtu(bus, net, eid, mtu);
+ if (rc) {
+ /* revert if we have an old setting */
+ if (cur_mtu) {
+ rc = nvme_mi_mi_config_set_mctp_mtu(ep, port, cur_mtu);
+ if (rc)
+ warn("Failed to restore previous MTU!");
+ rc = -1;
+ }
+ } else {
+ printf("MTU for port %u set to %d (was %d)\n",
+ port, mtu, cur_mtu);
+ }
+
+out_close_bus:
+ dbus_connection_unref(bus);
+out_close_ep:
+ dbus_error_free(&berr);
+ nvme_mi_close(ep);
+out_free_root:
+ nvme_mi_free_root(root);
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+