#include "MCTPReactor.hpp" #include "MCTPDeviceRepository.hpp" #include "MCTPEndpoint.hpp" #include "Utils.hpp" #include #include #include #include #include #include #include #include #include PHOSPHOR_LOG2_USING; void MCTPReactor::deferSetup(const std::shared_ptr& dev) { debug("Deferring setup for MCTP device at [ {MCTP_DEVICE} ]", "MCTP_DEVICE", dev->describe()); deferred.emplace(dev); } void MCTPReactor::untrackEndpoint(const std::shared_ptr& ep) { server.disassociate(MCTPDEndpoint::path(ep)); } void MCTPReactor::trackEndpoint(const std::shared_ptr& ep) { info("Added MCTP endpoint to device: [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT", ep->describe()); ep->subscribe( // Degraded [](const std::shared_ptr& ep) { debug("Endpoint entered degraded state: [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT", ep->describe()); }, // Available [](const std::shared_ptr& ep) { debug("Endpoint entered available state: [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT", ep->describe()); }, // Removed [weak{weak_from_this()}](const std::shared_ptr& ep) { info("Removed MCTP endpoint from device: [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT", ep->describe()); if (auto self = weak.lock()) { self->untrackEndpoint(ep); // Only defer the setup if we know inventory is still present if (self->devices.contains(ep->device())) { self->deferSetup(ep->device()); } } else { info( "The reactor object was destroyed concurrent to the removal of the remove match for the endpoint '{MCTP_ENDPOINT}'", "MCTP_ENDPOINT", ep->describe()); } }); // Proxy-host the association back to the inventory at the same path as the // endpoint in mctpd. // // clang-format off // ``` // # busctl call xyz.openbmc_project.ObjectMapper /xyz/openbmc_project/object_mapper xyz.openbmc_project.ObjectMapper GetAssociatedSubTree ooias /xyz/openbmc_project/mctp/1/9/configured_by / 0 1 xyz.openbmc_project.Configuration.MCTPDevice // a{sa{sas}} 1 "/xyz/openbmc_project/inventory/system/nvme/NVMe_1/NVMe_1_Temp" 1 "xyz.openbmc_project.EntityManager" 1 "xyz.openbmc_project.Configuration.MCTPDevice" // ``` // clang-format on std::optional item = devices.inventoryFor(ep->device()); if (!item) { error("Inventory missing for endpoint: [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT", ep->describe()); return; } std::vector associations{ {"configured_by", "configures", *item}}; server.associate(MCTPDEndpoint::path(ep), associations); } void MCTPReactor::setupEndpoint(const std::shared_ptr& dev) { debug( "Attempting to setup up MCTP endpoint for device at [ {MCTP_DEVICE} ]", "MCTP_DEVICE", dev->describe()); dev->setup([weak{weak_from_this()}, dev](const std::error_code& ec, const std::shared_ptr& ep) mutable { auto self = weak.lock(); if (!self) { info( "The reactor object was destroyed concurrent to the completion of the endpoint setup for '{MCTP_ENDPOINT}'", "MCTP_ENDPOINT", ep->describe()); return; } if (ec) { debug( "Setup failed for MCTP device at [ {MCTP_DEVICE} ]: {ERROR_MESSAGE}", "MCTP_DEVICE", dev->describe(), "ERROR_MESSAGE", ec.message()); self->deferSetup(dev); return; } try { self->trackEndpoint(ep); } catch (const MCTPException& e) { error("Failed to track endpoint '{MCTP_ENDPOINT}': {EXCEPTION}", "MCTP_ENDPOINT", ep->describe(), "EXCEPTION", e); self->deferSetup(dev); } }); } void MCTPReactor::tick() { auto toSetup = std::exchange(deferred, {}); for (const auto& entry : toSetup) { setupEndpoint(entry); } } void MCTPReactor::manageMCTPDevice(const std::string& path, const std::shared_ptr& device) { if (!device) { return; } try { devices.add(path, device); debug("MCTP device inventory added at '{INVENTORY_PATH}'", "INVENTORY_PATH", path); setupEndpoint(device); } catch (const std::system_error& e) { if (e.code() != std::errc::device_or_resource_busy) { throw e; } auto current = devices.deviceFor(path); if (!current) { warning( "Invalid state: Failed to manage device for inventory at '{INVENTORY_PATH}', but the inventory item is unrecognised", "INVENTORY_PATH", path); return; } // TODO: Ensure remove completion happens-before add. For now this // happens unsynchronised. Make some noise about it. warning( "Unsynchronised endpoint reinitialsation due to configuration change at '{INVENTORY_PATH}': Removing '{MCTP_DEVICE}'", "INVENTORY_PATH", path, "MCTP_DEVICE", current->describe()); unmanageMCTPDevice(path); devices.add(path, device); // Pray (this is the unsynchronised bit) deferSetup(device); } } void MCTPReactor::unmanageMCTPDevice(const std::string& path) { auto device = devices.deviceFor(path); if (!device) { debug("Unrecognised inventory item: {INVENTORY_PATH}", "INVENTORY_PATH", path); return; } debug("MCTP device inventory removed at '{INVENTORY_PATH}'", "INVENTORY_PATH", path); deferred.erase(device); // Remove the device from the repository before notifying the device itself // of removal so we don't defer its setup devices.remove(device); debug("Stopping management of MCTP device at [ {MCTP_DEVICE} ]", "MCTP_DEVICE", device->describe()); device->remove(); }