blob: 9d32682113dac11374bdd9830d98eb67eb1d99b6 [file] [log] [blame]
#include "MCTPReactor.hpp"
#include "MCTPDeviceRepository.hpp"
#include "MCTPEndpoint.hpp"
#include "Utils.hpp"
#include <boost/system/detail/error_code.hpp>
#include <phosphor-logging/lg2.hpp>
#include <cstdlib>
#include <memory>
#include <optional>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
PHOSPHOR_LOG2_USING;
void MCTPReactor::deferSetup(const std::shared_ptr<MCTPDevice>& dev)
{
debug("Deferring setup for MCTP device at [ {MCTP_DEVICE} ]", "MCTP_DEVICE",
dev->describe());
deferred.emplace(dev);
}
void MCTPReactor::untrackEndpoint(const std::shared_ptr<MCTPEndpoint>& ep)
{
server.disassociate(MCTPDEndpoint::path(ep));
}
void MCTPReactor::trackEndpoint(const std::shared_ptr<MCTPEndpoint>& ep)
{
info("Added MCTP endpoint to device: [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT",
ep->describe());
ep->subscribe(
// Degraded
[](const std::shared_ptr<MCTPEndpoint>& ep) {
debug("Endpoint entered degraded state: [ {MCTP_ENDPOINT} ]",
"MCTP_ENDPOINT", ep->describe());
},
// Available
[](const std::shared_ptr<MCTPEndpoint>& ep) {
debug("Endpoint entered available state: [ {MCTP_ENDPOINT} ]",
"MCTP_ENDPOINT", ep->describe());
},
// Removed
[weak{weak_from_this()}](const std::shared_ptr<MCTPEndpoint>& 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<std::string> item = devices.inventoryFor(ep->device());
if (!item)
{
error("Inventory missing for endpoint: [ {MCTP_ENDPOINT} ]",
"MCTP_ENDPOINT", ep->describe());
return;
}
std::vector<Association> associations{
{"configured_by", "configures", *item}};
server.associate(MCTPDEndpoint::path(ep), associations);
}
void MCTPReactor::setupEndpoint(const std::shared_ptr<MCTPDevice>& 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<MCTPEndpoint>& 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<MCTPDevice>& 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();
}